From 81fc0fe6b76aa8de4bb9026b8b238e0717144fa6 Mon Sep 17 00:00:00 2001 From: Miller Puckette <msp@ucsd.edu> Date: Fri, 3 Aug 2007 08:39:03 -0700 Subject: [PATCH] oops, see next --- portaudio/LICENSE.txt | 65 - portaudio/README.txt | 81 - portaudio/V19-devel-readme.txt | 222 - portaudio/pa_asio/ASIO-README.txt | 137 - portaudio/pa_asio/CVS/Entries | 8 - portaudio/pa_asio/CVS/Repository | 1 - portaudio/pa_asio/CVS/Root | 1 - portaudio/pa_asio/CVS/Tag | 1 - portaudio/pa_asio/Callback_adaptation_.pdf | Bin 50527 -> 0 bytes portaudio/pa_asio/Pa_ASIO.pdf | Bin 50778 -> 0 bytes portaudio/pa_asio/iasiothiscallresolver.cpp | 563 --- portaudio/pa_asio/iasiothiscallresolver.h | 197 - portaudio/pa_asio/pa_asio.cpp | 2958 ------------- portaudio/pa_asio/pa_asio.h | 122 - portaudio/pa_common/CVS/Entries | 22 - portaudio/pa_common/CVS/Repository | 1 - portaudio/pa_common/CVS/Root | 1 - portaudio/pa_common/CVS/Tag | 1 - portaudio/pa_common/pa_allocation.c | 234 -- portaudio/pa_common/pa_allocation.h | 95 - portaudio/pa_common/pa_converters.c | 1926 --------- portaudio/pa_common/pa_converters.h | 254 -- portaudio/pa_common/pa_cpuload.c | 96 - portaudio/pa_common/pa_cpuload.h | 63 - portaudio/pa_common/pa_dither.c | 204 - portaudio/pa_common/pa_dither.h | 91 - portaudio/pa_common/pa_endianness.h | 113 - portaudio/pa_common/pa_front.c | 1981 --------- portaudio/pa_common/pa_hostapi.h | 244 -- portaudio/pa_common/pa_process.c | 1763 -------- portaudio/pa_common/pa_process.h | 741 ---- portaudio/pa_common/pa_skeleton.c | 807 ---- portaudio/pa_common/pa_stream.c | 141 - portaudio/pa_common/pa_stream.h | 196 - portaudio/pa_common/pa_trace.c | 88 - portaudio/pa_common/pa_trace.h | 70 - portaudio/pa_common/pa_types.h | 65 - portaudio/pa_common/pa_util.h | 167 - portaudio/pa_common/portaudio.h | 1124 ----- portaudio/pa_dll_switch/CVS/Entries | 6 - portaudio/pa_dll_switch/CVS/Repository | 1 - portaudio/pa_dll_switch/CVS/Root | 1 - portaudio/pa_dll_switch/CVS/Tag | 1 - portaudio/pa_dll_switch/PaDllEntry.h | 184 - .../pa_dll_switch/letter_from_tim_010817.txt | Bin 1176 -> 0 bytes portaudio/pa_dll_switch/loadPA_DLL.cpp | 203 - portaudio/pa_dll_switch/pa_lib.c | 827 ---- portaudio/pa_dll_switch/portaudio.h | 439 -- portaudio/pa_jack/CVS/Entries | 2 - portaudio/pa_jack/CVS/Repository | 1 - portaudio/pa_jack/CVS/Root | 1 - portaudio/pa_jack/CVS/Tag | 1 - portaudio/pa_jack/pa_jack.c | 1714 -------- portaudio/pa_linux_alsa/CVS/Entries | 3 - portaudio/pa_linux_alsa/CVS/Repository | 1 - portaudio/pa_linux_alsa/CVS/Root | 1 - portaudio/pa_linux_alsa/CVS/Tag | 1 - portaudio/pa_linux_alsa/pa_linux_alsa.c | 3682 ----------------- portaudio/pa_linux_alsa/pa_linux_alsa.h | 64 - portaudio/pa_mac/CVS/Entries | 2 - portaudio/pa_mac/CVS/Repository | 1 - portaudio/pa_mac/CVS/Root | 1 - portaudio/pa_mac/CVS/Tag | 1 - portaudio/pa_mac/pa_mac_hostapis.c | 79 - portaudio/pa_mac_core/CVS/Entries | 6 - portaudio/pa_mac_core/CVS/Repository | 1 - portaudio/pa_mac_core/CVS/Root | 1 - portaudio/pa_mac_core/CVS/Tag | 1 - portaudio/pa_mac_core/notes.txt | 145 - portaudio/pa_mac_core/pa_mac_core.c | 2105 ---------- portaudio/pa_mac_core/pa_mac_core.h | 69 - portaudio/pa_mac_core/pa_mac_core_old.c | 907 ---- portaudio/pa_mac_core/pa_mac_core_utilities.c | 466 --- portaudio/pa_unix/CVS/Entries | 4 - portaudio/pa_unix/CVS/Repository | 1 - portaudio/pa_unix/CVS/Root | 1 - portaudio/pa_unix/CVS/Tag | 1 - portaudio/pa_unix/pa_unix_hostapis.c | 64 - portaudio/pa_unix/pa_unix_util.c | 192 - portaudio/pa_unix/pa_unix_util.h | 73 - portaudio/pa_unix_oss/CVS/Entries | 4 - portaudio/pa_unix_oss/CVS/Repository | 1 - portaudio/pa_unix_oss/CVS/Root | 1 - portaudio/pa_unix_oss/CVS/Tag | 1 - portaudio/pa_unix_oss/low_latency_tip.txt | Bin 3111 -> 0 bytes portaudio/pa_unix_oss/pa_unix_oss.c | 1924 --------- portaudio/pa_unix_oss/recplay.c | 114 - portaudio/pa_win/CVS/Entries | 5 - portaudio/pa_win/CVS/Entries.Log | 2 - portaudio/pa_win/CVS/Repository | 1 - portaudio/pa_win/CVS/Root | 1 - portaudio/pa_win/CVS/Tag | 1 - portaudio/pa_win/dev-cpp/CVS/Entries | 6 - portaudio/pa_win/dev-cpp/CVS/Repository | 1 - portaudio/pa_win/dev-cpp/CVS/Root | 1 - portaudio/pa_win/dev-cpp/CVS/Tag | 1 - portaudio/pa_win/dev-cpp/Makefile-dll | 78 - portaudio/pa_win/dev-cpp/Makefile-static | 75 - portaudio/pa_win/dev-cpp/portaudio-dll.dev | 209 - portaudio/pa_win/dev-cpp/portaudio-static.dev | 209 - portaudio/pa_win/dev-cpp/readme.txt | 23 - portaudio/pa_win/msvc/CVS/Entries | 7 - portaudio/pa_win/msvc/CVS/Repository | 1 - portaudio/pa_win/msvc/CVS/Root | 1 - portaudio/pa_win/msvc/CVS/Tag | 1 - portaudio/pa_win/msvc/Makefile.msvc | 159 - portaudio/pa_win/msvc/clean.bat | 7 - portaudio/pa_win/msvc/make.bat | 8 - portaudio/pa_win/msvc/portaudio.def | 43 - portaudio/pa_win/msvc/readme.txt | 56 - portaudio/pa_win/msvc/setenv.bat | 1 - portaudio/pa_win/pa_win_hostapis.c | 86 - portaudio/pa_win/pa_win_util.c | 134 - portaudio/pa_win/pa_x86_plain_converters.c | 1167 ------ portaudio/pa_win/pa_x86_plain_converters.h | 19 - portaudio/pa_win_ds/CVS/Entries | 4 - portaudio/pa_win_ds/CVS/Repository | 1 - portaudio/pa_win_ds/CVS/Root | 1 - portaudio/pa_win_ds/CVS/Tag | 1 - portaudio/pa_win_ds/dsound_wrapper.c | 616 --- portaudio/pa_win_ds/dsound_wrapper.h | 130 - portaudio/pa_win_ds/pa_win_ds.c | 1864 --------- portaudio/pa_win_wdmks/CVS/Entries | 3 - portaudio/pa_win_wdmks/CVS/Repository | 1 - portaudio/pa_win_wdmks/CVS/Root | 1 - portaudio/pa_win_wdmks/CVS/Tag | 1 - portaudio/pa_win_wdmks/pa_win_wdmks.c | 3269 --------------- portaudio/pa_win_wdmks/readme.txt | 82 - portaudio/pa_win_wmme/CVS/Entries | 3 - portaudio/pa_win_wmme/CVS/Repository | 1 - portaudio/pa_win_wmme/CVS/Root | 1 - portaudio/pa_win_wmme/CVS/Tag | 1 - portaudio/pa_win_wmme/pa_win_wmme.c | 3634 ---------------- portaudio/pa_win_wmme/pa_win_wmme.h | 160 - portaudio/pablio/CVS/Entries | 11 - portaudio/pablio/CVS/Repository | 1 - portaudio/pablio/CVS/Root | 1 - portaudio/pablio/CVS/Tag | 1 - portaudio/pablio/README.txt | 39 - portaudio/pablio/pablio.c | 307 -- portaudio/pablio/pablio.def | 35 - portaudio/pablio/pablio.h | 109 - portaudio/pablio/ringbuffer.c | 199 - portaudio/pablio/ringbuffer.h | 101 - portaudio/pablio/test_rw.c | 99 - portaudio/pablio/test_rw_echo.c | 123 - portaudio/pablio/test_w_saw.c | 108 - portaudio/pablio/test_w_saw8.c | 106 - portmidi/Makefile | 77 - portmidi/README.txt | 62 - portmidi/pm_common/pminternal.h | 173 - portmidi/pm_common/pmutil.c | 132 - portmidi/pm_common/pmutil.h | 56 - portmidi/pm_common/portmidi.c | 980 ----- portmidi/pm_common/portmidi.h | 692 ---- portmidi/pm_linux/README_LINUX.txt | 32 - portmidi/pm_linux/pmlinux.c | 51 - portmidi/pm_linux/pmlinux.h | 5 - portmidi/pm_linux/pmlinuxalsa.c | 724 ---- portmidi/pm_linux/pmlinuxalsa.h | 6 - portmidi/pm_mac/pmmac.c | 42 - portmidi/pm_mac/pmmac.h | 4 - portmidi/pm_mac/pmmacosxcm.c | 709 ---- portmidi/pm_mac/pmmacosxcm.h | 4 - portmidi/pm_test/latency.c | 278 -- portmidi/pm_test/latency.dsp | 102 - portmidi/pm_test/midithread.c | 327 -- portmidi/pm_test/midithread.dsp | 102 - portmidi/pm_test/midithru.c | 364 -- portmidi/pm_test/midithru.dsp | 102 - portmidi/pm_test/midithru.dsw | 29 - portmidi/pm_test/sysex.c | 319 -- portmidi/pm_test/sysex.dsp | 102 - portmidi/pm_test/test.c | 469 --- portmidi/pm_test/test.dsp | 102 - portmidi/pm_test/txdata.syx | 257 -- portmidi/pm_win/README_WIN.txt | 183 - portmidi/pm_win/copy-dll.bat | 13 - portmidi/pm_win/debugging_dlls.txt | 145 - portmidi/pm_win/pm_dll.dsp | 107 - portmidi/pm_win/pmdll.c | 49 - portmidi/pm_win/pmdll.h | 5 - portmidi/pm_win/pmwin.c | 113 - portmidi/pm_win/pmwinmm.c | 1547 ------- portmidi/pm_win/pmwinmm.h | 5 - portmidi/portmidi.dsp | 124 - portmidi/portmidi.dsw | 158 - portmidi/porttime/porttime.c | 3 - portmidi/porttime/porttime.dsp | 104 - portmidi/porttime/porttime.h | 36 - portmidi/porttime/ptlinux.c | 120 - portmidi/porttime/ptmacosx_cf.c | 132 - portmidi/porttime/ptmacosx_mach.c | 123 - portmidi/porttime/ptwinmm.c | 65 - src/s_midi_sgi.c | 203 - 195 files changed, 50997 deletions(-) delete mode 100644 portaudio/LICENSE.txt delete mode 100644 portaudio/README.txt delete mode 100644 portaudio/V19-devel-readme.txt delete mode 100644 portaudio/pa_asio/ASIO-README.txt delete mode 100644 portaudio/pa_asio/CVS/Entries delete mode 100644 portaudio/pa_asio/CVS/Repository delete mode 100644 portaudio/pa_asio/CVS/Root delete mode 100644 portaudio/pa_asio/CVS/Tag delete mode 100644 portaudio/pa_asio/Callback_adaptation_.pdf delete mode 100644 portaudio/pa_asio/Pa_ASIO.pdf delete mode 100644 portaudio/pa_asio/iasiothiscallresolver.cpp delete mode 100644 portaudio/pa_asio/iasiothiscallresolver.h delete mode 100644 portaudio/pa_asio/pa_asio.cpp delete mode 100644 portaudio/pa_asio/pa_asio.h delete mode 100644 portaudio/pa_common/CVS/Entries delete mode 100644 portaudio/pa_common/CVS/Repository delete mode 100644 portaudio/pa_common/CVS/Root delete mode 100644 portaudio/pa_common/CVS/Tag delete mode 100644 portaudio/pa_common/pa_allocation.c delete mode 100644 portaudio/pa_common/pa_allocation.h delete mode 100644 portaudio/pa_common/pa_converters.c delete mode 100644 portaudio/pa_common/pa_converters.h delete mode 100644 portaudio/pa_common/pa_cpuload.c delete mode 100644 portaudio/pa_common/pa_cpuload.h delete mode 100644 portaudio/pa_common/pa_dither.c delete mode 100644 portaudio/pa_common/pa_dither.h delete mode 100644 portaudio/pa_common/pa_endianness.h delete mode 100644 portaudio/pa_common/pa_front.c delete mode 100644 portaudio/pa_common/pa_hostapi.h delete mode 100644 portaudio/pa_common/pa_process.c delete mode 100644 portaudio/pa_common/pa_process.h delete mode 100644 portaudio/pa_common/pa_skeleton.c delete mode 100644 portaudio/pa_common/pa_stream.c delete mode 100644 portaudio/pa_common/pa_stream.h delete mode 100644 portaudio/pa_common/pa_trace.c delete mode 100644 portaudio/pa_common/pa_trace.h delete mode 100644 portaudio/pa_common/pa_types.h delete mode 100644 portaudio/pa_common/pa_util.h delete mode 100644 portaudio/pa_common/portaudio.h delete mode 100644 portaudio/pa_dll_switch/CVS/Entries delete mode 100644 portaudio/pa_dll_switch/CVS/Repository delete mode 100644 portaudio/pa_dll_switch/CVS/Root delete mode 100644 portaudio/pa_dll_switch/CVS/Tag delete mode 100644 portaudio/pa_dll_switch/PaDllEntry.h delete mode 100644 portaudio/pa_dll_switch/letter_from_tim_010817.txt delete mode 100644 portaudio/pa_dll_switch/loadPA_DLL.cpp delete mode 100644 portaudio/pa_dll_switch/pa_lib.c delete mode 100644 portaudio/pa_dll_switch/portaudio.h delete mode 100644 portaudio/pa_jack/CVS/Entries delete mode 100644 portaudio/pa_jack/CVS/Repository delete mode 100644 portaudio/pa_jack/CVS/Root delete mode 100644 portaudio/pa_jack/CVS/Tag delete mode 100644 portaudio/pa_jack/pa_jack.c delete mode 100644 portaudio/pa_linux_alsa/CVS/Entries delete mode 100644 portaudio/pa_linux_alsa/CVS/Repository delete mode 100644 portaudio/pa_linux_alsa/CVS/Root delete mode 100644 portaudio/pa_linux_alsa/CVS/Tag delete mode 100644 portaudio/pa_linux_alsa/pa_linux_alsa.c delete mode 100644 portaudio/pa_linux_alsa/pa_linux_alsa.h delete mode 100644 portaudio/pa_mac/CVS/Entries delete mode 100644 portaudio/pa_mac/CVS/Repository delete mode 100644 portaudio/pa_mac/CVS/Root delete mode 100644 portaudio/pa_mac/CVS/Tag delete mode 100644 portaudio/pa_mac/pa_mac_hostapis.c delete mode 100644 portaudio/pa_mac_core/CVS/Entries delete mode 100644 portaudio/pa_mac_core/CVS/Repository delete mode 100644 portaudio/pa_mac_core/CVS/Root delete mode 100644 portaudio/pa_mac_core/CVS/Tag delete mode 100644 portaudio/pa_mac_core/notes.txt delete mode 100644 portaudio/pa_mac_core/pa_mac_core.c delete mode 100644 portaudio/pa_mac_core/pa_mac_core.h delete mode 100644 portaudio/pa_mac_core/pa_mac_core_old.c delete mode 100644 portaudio/pa_mac_core/pa_mac_core_utilities.c delete mode 100644 portaudio/pa_unix/CVS/Entries delete mode 100644 portaudio/pa_unix/CVS/Repository delete mode 100644 portaudio/pa_unix/CVS/Root delete mode 100644 portaudio/pa_unix/CVS/Tag delete mode 100644 portaudio/pa_unix/pa_unix_hostapis.c delete mode 100644 portaudio/pa_unix/pa_unix_util.c delete mode 100644 portaudio/pa_unix/pa_unix_util.h delete mode 100644 portaudio/pa_unix_oss/CVS/Entries delete mode 100644 portaudio/pa_unix_oss/CVS/Repository delete mode 100644 portaudio/pa_unix_oss/CVS/Root delete mode 100644 portaudio/pa_unix_oss/CVS/Tag delete mode 100644 portaudio/pa_unix_oss/low_latency_tip.txt delete mode 100644 portaudio/pa_unix_oss/pa_unix_oss.c delete mode 100644 portaudio/pa_unix_oss/recplay.c delete mode 100644 portaudio/pa_win/CVS/Entries delete mode 100644 portaudio/pa_win/CVS/Entries.Log delete mode 100644 portaudio/pa_win/CVS/Repository delete mode 100644 portaudio/pa_win/CVS/Root delete mode 100644 portaudio/pa_win/CVS/Tag delete mode 100644 portaudio/pa_win/dev-cpp/CVS/Entries delete mode 100644 portaudio/pa_win/dev-cpp/CVS/Repository delete mode 100644 portaudio/pa_win/dev-cpp/CVS/Root delete mode 100644 portaudio/pa_win/dev-cpp/CVS/Tag delete mode 100644 portaudio/pa_win/dev-cpp/Makefile-dll delete mode 100644 portaudio/pa_win/dev-cpp/Makefile-static delete mode 100644 portaudio/pa_win/dev-cpp/portaudio-dll.dev delete mode 100644 portaudio/pa_win/dev-cpp/portaudio-static.dev delete mode 100644 portaudio/pa_win/dev-cpp/readme.txt delete mode 100644 portaudio/pa_win/msvc/CVS/Entries delete mode 100644 portaudio/pa_win/msvc/CVS/Repository delete mode 100644 portaudio/pa_win/msvc/CVS/Root delete mode 100644 portaudio/pa_win/msvc/CVS/Tag delete mode 100644 portaudio/pa_win/msvc/Makefile.msvc delete mode 100755 portaudio/pa_win/msvc/clean.bat delete mode 100755 portaudio/pa_win/msvc/make.bat delete mode 100644 portaudio/pa_win/msvc/portaudio.def delete mode 100644 portaudio/pa_win/msvc/readme.txt delete mode 100755 portaudio/pa_win/msvc/setenv.bat delete mode 100644 portaudio/pa_win/pa_win_hostapis.c delete mode 100644 portaudio/pa_win/pa_win_util.c delete mode 100644 portaudio/pa_win/pa_x86_plain_converters.c delete mode 100644 portaudio/pa_win/pa_x86_plain_converters.h delete mode 100644 portaudio/pa_win_ds/CVS/Entries delete mode 100644 portaudio/pa_win_ds/CVS/Repository delete mode 100644 portaudio/pa_win_ds/CVS/Root delete mode 100644 portaudio/pa_win_ds/CVS/Tag delete mode 100644 portaudio/pa_win_ds/dsound_wrapper.c delete mode 100644 portaudio/pa_win_ds/dsound_wrapper.h delete mode 100644 portaudio/pa_win_ds/pa_win_ds.c delete mode 100644 portaudio/pa_win_wdmks/CVS/Entries delete mode 100644 portaudio/pa_win_wdmks/CVS/Repository delete mode 100644 portaudio/pa_win_wdmks/CVS/Root delete mode 100644 portaudio/pa_win_wdmks/CVS/Tag delete mode 100644 portaudio/pa_win_wdmks/pa_win_wdmks.c delete mode 100644 portaudio/pa_win_wdmks/readme.txt delete mode 100644 portaudio/pa_win_wmme/CVS/Entries delete mode 100644 portaudio/pa_win_wmme/CVS/Repository delete mode 100644 portaudio/pa_win_wmme/CVS/Root delete mode 100644 portaudio/pa_win_wmme/CVS/Tag delete mode 100644 portaudio/pa_win_wmme/pa_win_wmme.c delete mode 100644 portaudio/pa_win_wmme/pa_win_wmme.h delete mode 100644 portaudio/pablio/CVS/Entries delete mode 100644 portaudio/pablio/CVS/Repository delete mode 100644 portaudio/pablio/CVS/Root delete mode 100644 portaudio/pablio/CVS/Tag delete mode 100644 portaudio/pablio/README.txt delete mode 100644 portaudio/pablio/pablio.c delete mode 100644 portaudio/pablio/pablio.def delete mode 100644 portaudio/pablio/pablio.h delete mode 100644 portaudio/pablio/ringbuffer.c delete mode 100644 portaudio/pablio/ringbuffer.h delete mode 100644 portaudio/pablio/test_rw.c delete mode 100644 portaudio/pablio/test_rw_echo.c delete mode 100644 portaudio/pablio/test_w_saw.c delete mode 100644 portaudio/pablio/test_w_saw8.c delete mode 100644 portmidi/Makefile delete mode 100644 portmidi/README.txt delete mode 100644 portmidi/pm_common/pminternal.h delete mode 100644 portmidi/pm_common/pmutil.c delete mode 100644 portmidi/pm_common/pmutil.h delete mode 100644 portmidi/pm_common/portmidi.c delete mode 100644 portmidi/pm_common/portmidi.h delete mode 100644 portmidi/pm_linux/README_LINUX.txt delete mode 100644 portmidi/pm_linux/pmlinux.c delete mode 100644 portmidi/pm_linux/pmlinux.h delete mode 100644 portmidi/pm_linux/pmlinuxalsa.c delete mode 100644 portmidi/pm_linux/pmlinuxalsa.h delete mode 100644 portmidi/pm_mac/pmmac.c delete mode 100644 portmidi/pm_mac/pmmac.h delete mode 100644 portmidi/pm_mac/pmmacosxcm.c delete mode 100644 portmidi/pm_mac/pmmacosxcm.h delete mode 100644 portmidi/pm_test/latency.c delete mode 100644 portmidi/pm_test/latency.dsp delete mode 100644 portmidi/pm_test/midithread.c delete mode 100644 portmidi/pm_test/midithread.dsp delete mode 100644 portmidi/pm_test/midithru.c delete mode 100644 portmidi/pm_test/midithru.dsp delete mode 100644 portmidi/pm_test/midithru.dsw delete mode 100644 portmidi/pm_test/sysex.c delete mode 100644 portmidi/pm_test/sysex.dsp delete mode 100644 portmidi/pm_test/test.c delete mode 100644 portmidi/pm_test/test.dsp delete mode 100644 portmidi/pm_test/txdata.syx delete mode 100644 portmidi/pm_win/README_WIN.txt delete mode 100644 portmidi/pm_win/copy-dll.bat delete mode 100644 portmidi/pm_win/debugging_dlls.txt delete mode 100644 portmidi/pm_win/pm_dll.dsp delete mode 100644 portmidi/pm_win/pmdll.c delete mode 100644 portmidi/pm_win/pmdll.h delete mode 100644 portmidi/pm_win/pmwin.c delete mode 100644 portmidi/pm_win/pmwinmm.c delete mode 100644 portmidi/pm_win/pmwinmm.h delete mode 100644 portmidi/portmidi.dsp delete mode 100644 portmidi/portmidi.dsw delete mode 100644 portmidi/porttime/porttime.c delete mode 100644 portmidi/porttime/porttime.dsp delete mode 100644 portmidi/porttime/porttime.h delete mode 100644 portmidi/porttime/ptlinux.c delete mode 100644 portmidi/porttime/ptmacosx_cf.c delete mode 100644 portmidi/porttime/ptmacosx_mach.c delete mode 100644 portmidi/porttime/ptwinmm.c delete mode 100644 src/s_midi_sgi.c diff --git a/portaudio/LICENSE.txt b/portaudio/LICENSE.txt deleted file mode 100644 index 105da3f75..000000000 --- a/portaudio/LICENSE.txt +++ /dev/null @@ -1,65 +0,0 @@ -Portable header file to contain: -/* - * PortAudio Portable Real-Time Audio Library - * PortAudio API Header File - * Latest version available at: http://www.audiomulch.com/portaudio/ - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - - -Implementation files to contain: -/* - * PortAudio Portable Real-Time Audio Library - * Latest version at: http://www.audiomulch.com/portaudio/ - * <platform> Implementation - * Copyright (c) 1999-2000 <author(s)> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ \ No newline at end of file diff --git a/portaudio/README.txt b/portaudio/README.txt deleted file mode 100644 index 4cfc6166e..000000000 --- a/portaudio/README.txt +++ /dev/null @@ -1,81 +0,0 @@ -README for PortAudio -Implementations for PC DirectSound and Mac SoundManager - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com// - * - * Copyright (c) 1999-2000 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -PortAudio is a portable audio I/O library designed for cross-platform -support of audio. It uses a callback mechanism to request audio processing. -Audio can be generated in various formats, including 32 bit floating point, -and will be converted to the native format internally. - -Documentation: - See "pa_common/portaudio.h" for API spec. - See docs folder for a tutorial. - Also see http://www.portaudio.com/docs/ - And see "pa_tests/patest_saw.c" for an example. - -For information on compiling programs with PortAudio, please see the -tutorial at: - - http://www.portaudio.com/docs/pa_tutorial.html - -Important Files and Folders: - pa_common/ = platform independant code - pa_common/portaudio.h = header file for PortAudio API. Specifies API. - pa_common/pa_lib.c = host independant code for all implementations. - - pablio = simple blocking read/write interface - -Platform Implementations - pa_asio = ASIO for Windows and Macintosh - pa_beos = BeOS - pa_mac_sm = Macintosh Sound Manager for OS 8,9 and Carbon - pa_mac_core = Macintosh Core Audio for OS X - pa_sgi = Silicon Graphics AL - pa_unix_oss = OSS implementation for various Unixes - pa_win_ds = Windows Direct Sound - pa_win_wmme = Windows MME (most widely supported) - -Test Programs - pa_tests/pa_fuzz.c = guitar fuzz box - pa_tests/pa_devs.c = print a list of available devices - pa_tests/pa_minlat.c = determine minimum latency for your machine - pa_tests/paqa_devs.c = self test that opens all devices - pa_tests/paqa_errs.c = test error detection and reporting - pa_tests/patest_clip.c = hear a sine wave clipped and unclipped - pa_tests/patest_dither.c = hear effects of dithering (extremely subtle) - pa_tests/patest_pink.c = fun with pink noise - pa_tests/patest_record.c = record and playback some audio - pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad(). - pa_tests/patest_sine.c = output a sine wave in a simple PA app - pa_tests/patest_sync.c = test syncronization of audio and video - pa_tests/patest_wire.c = pass input to output, wire simulator diff --git a/portaudio/V19-devel-readme.txt b/portaudio/V19-devel-readme.txt deleted file mode 100644 index ae5570eb6..000000000 --- a/portaudio/V19-devel-readme.txt +++ /dev/null @@ -1,222 +0,0 @@ -STATUS: - -MME, DirectSound and ASIO versions are more-or-less working. See FIXMEs @todos -and the proposals matrix at portaudio.com for further status. - -The pa_tests directory contains tests. pa_tests/README.txt notes which tests -currently build. - -The PaUtil support code is finished enough for other implementations to be -ported. No changes are expected to be made to the definition of the PaUtil -functions. - -Note that it's not yet 100% clear how the current support functions -will interact with blocking read/write streams. - -BUILD INSTRUCTIONS - -to build tests/patest_sine.c you will need to compile and link the following -files (MME) -pa_common\pa_process.c -pa_common\pa_skeleton.c -pa_common\pa_stream.c -pa_common\pa_trace.c -pa_common\pa_converters.c -pa_common\pa_cpuload.c -pa_common\pa_dither.c -pa_common\pa_front.c -pa_common\pa_allocation.h -pa_win\pa_win_util.c -pa_win\pa_win_hostapis.c -pa_win_wmme\pa_win_wmme.c - -see below for a description of these files. - - -FILES: - -portaudio.h - public api header file - -pa_front.c - implements the interface defined in portaudio.h. manages multiple host apis. - validates function parameters before calling through to host apis. tracks - open streams and closes them at Pa_Terminate(). - -pa_util.h - declares utility functions for use my implementations. including utility - functions which must be implemented separately for each platform. - -pa_hostapi.h - hostapi representation structure used to interface between pa_front.c - and implementations - -pa_stream.c/h - stream interface and representation structures and helper functions - used to interface between pa_front.c and implementations - -pa_cpuload.c/h - source and header for cpu load calculation facility - -pa_trace.c/h - source and header for debug trace log facility - -pa_converters.c/h - sample buffer conversion facility - -pa_dither.c/h - dither noise generator - -pa_process.c/h - callback buffer processing facility including interleave and block adaption - -pa_allocation.c/h - allocation context for tracking groups of allocations - -pa_skeleton.c - an skeleton implementation showing how the common code can be used. - -pa_win_util.c - Win32 implementation of platform specific PaUtil functions (memory allocation, - usec clock, Pa_Sleep().) The file will be used with all Win32 host APIs. - -pa_win_hostapis.c - contains the paHostApiInitializers array and an implementation of - Pa_GetDefaultHostApi() for win32 builds. - -pa_win_wmme.c - Win32 host api implementation for the windows multimedia extensions audio API. - -pa_win_wmme.h - public header file containing interfaces to mme-specific functions and the - deviceInfo data structure. - - -CODING GUIDELINES: - -naming conventions: - #defines begin with PA_ - #defines local to a file end with _ - global utility variables begin with paUtil - global utility types begin with PaUtil (including function types) - global utility functions begin with PaUtil_ - static variables end with _ - static constants begin with const and end with _ - static funtions have no special prefix/suffix - -In general, implementations should declare all of their members static, -except for their initializer which should be exported. All exported names -should be preceeded by Pa<MN>_ where MN is the module name, for example -the windows mme initializer should be named PaWinWmme_Initialize(). - -Every host api should define an initializer which returns an error code -and a PaHostApiInterface*. The initializer should only return an error other -than paNoError if it encounters an unexpected and fatal error (memory allocation -error for example). In general, there may be conditions under which it returns -a NULL interface pointer and also returns paNoError. For example, if the ASIO -implementation detects that ASIO is not installed, it should return a -NULL interface, and paNoError. - -Platform-specific shared functions should begin with Pa<PN>_ where PN is the -platform name. eg. PaWin_ for windows, PaUnix_ for unix. - -The above two conventions should also be followed whenever it is necessary to -share functions accross multiple source files. - -Two utilities for debug messages are provided. The PA_DEBUG macro defined in -pa_implementation.h provides a simple way to print debug messages to stderr. -Due to real-time performance issues, PA_DEBUG may not be suitable for use -within the portaudio processing callback, or in other threads. In such cases -the event tracing facility provided in pa_trace.h may be more appropriate. - -If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API -will be logged to stderr along with parameter and return values. - - -TODO: - (this list is totally out of date) - - finish coding converter functions in pa_converters.c (anyone?) - - implement block adaption in pa_process.c (phil?) - - fix all current tests to work with new code. this should mostly involve - changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc. - - write some new tests to exercise the multi-api functions - - write (doxygen) documentation for pa_trace (phil?) - - remove unused typeids from PaHostAPITypeID - - create a global configuration file which documents which PA_ defines can be - used for configuration - - need a coding standard for comment formatting - - migrate directx (phil) - - migrate asio (ross?, stephane?) - - see top of pa_win_wmme.c for MME todo items (ross) - - write style guide document (ross) - - -DESIGN ISSUES: - (this list is totally out of date) - - consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API - - switch to new latency parameter mechanism now (?) - - question: if input or outputDriverInfo structures are passed for a different - hostApi from the one being called, do we return an error or just ignore - them? (i think return error) - - consider renaming PortAudioCallback to PaStreamCallback - - consider renaming PaError, PaResult - - -ASSORTED DISORGANISED NOTES: - - NOTE: - pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do: - - checks the device info to make sure that the device supports the requested sample rate, - it may also change the sample rate to the "closest available" sample rate if it - is within a particular error margin - - rationale for breaking up internalPortAudioStream: - each implementation has its own requirements and behavior, and should be - able to choose the best way to operate without being limited by the - constraints imposed by a common infrastructure. in other words the - implementations should be able to pick and choose services from the - common infrastructure. currently identified services include: - - - cpu load tracking - - buffering and conversion service (same code works for input and output) - - should support buffer multiplexing (non-integer length input and output buffers) - - in-place conversion where possible (only for callback, read/write always copies) - - should manage allocation of temporary buffers if necessary - - instrumentation (should be able to be disabled): callback count, framesProcessed - - common data: magic, streamInterface, callback, userdata - - -- conversion functions: - - should handle temp buffer allocation - - dithering (random number state per-stream) - - buffer size mismatches - - with new buffer slip rules, temp buffers may always be needed - - we should aim for in-place conversion wherever possible - - does phil's code support in-place conversion? (yes) - -- dicuss relationship between user and host buffer sizes - - completely independent.. individual implementations may constrain - host buffer sizes if necessary - - -- discuss device capabilities: - - i'd like to be able to request certain information: - - channel count for example - diff --git a/portaudio/pa_asio/ASIO-README.txt b/portaudio/pa_asio/ASIO-README.txt deleted file mode 100644 index 9fb74ae16..000000000 --- a/portaudio/pa_asio/ASIO-README.txt +++ /dev/null @@ -1,137 +0,0 @@ -ASIO-README.txt - -This document contains information to help you compile PortAudio with -ASIO support. If you find any omissions or errors in this document -please notify Ross Bencina <rossb@audiomulch.com>. - - -Building PortAudio with ASIO support ------------------------------------- - -To build PortAudio with ASIO support you need to compile and link with -pa_asio.c, and files from the ASIO SDK (see below), along with the common -files from pa_common/ and platform specific files from pa_win/ (for Win32) -or pa_mac/ (for Macintosh). - -If you are compiling with a non-Microsoft compiler on windows, also -compile and link with iasiothiscallresolver.cpp (see below for -an explanation). - -For some platforms (MingW, possibly Mac), you may simply -be able to type: - -./configure --with-host_os=mingw --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2] -make - -./configure --with-host_os=darwin --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2] -make - -and life will be good. - - -Obtaining the ASIO SDK ----------------------- - -In order to build PortAudio with ASIO support, you need to download -the ASIO SDK (version 2.0) from Steinberg. Steinberg makes the ASIO -SDK available to anyone free of charge, however they do not permit its -source code to be distributed. - -NOTE: In some cases the ASIO SDK may require patching, see below -for further details. - -http://www.steinberg.net/en/ps/support/3rdparty/asio_sdk/ - -If the above link is broken search Google for: -"download steinberg ASIO SDK" - - - -Building the ASIO SDK on Macintosh ----------------------------------- - -To build the ASIO SDK on Macintosh you need to compile and link with the -following files from the ASIO SDK: - -host/asiodrivers.cpp -host/mac/asioshlib.cpp -host/mac/codefragements.cpp - - - -Building the ASIO SDK on Windows --------------------------------- - -To build the ASIO SDK on Windows you need to compile and link with the -following files from the ASIO SDK: - -asio_sdk\common\asio.cpp -asio_sdk\host\asiodrivers.cpp -asio_sdk\host\pc\asiolist.cpp - -You may also need to adjust your include paths to support inclusion of -header files from the above directories. - -The ASIO SDK depends on the following COM API functions: -CoInitialize, CoUninitialize, CoCreateInstance, CLSIDFromString -For compilation with MinGW you will need to link with -lole32, for -Borland link with Import32.lib. - - - -Non-Microsoft (MSVC) Compilers on Windows including Borland and GCC -------------------------------------------------------------------- - -Steinberg did not specify a calling convention in the IASIO interface -definition. This causes the Microsoft compiler to use the proprietary -thiscall convention which is not compatible with other compilers, such -as compilers from Borland (BCC and C++Builder) and GNU (gcc). -Steinberg's ASIO SDK will compile but crash on initialization if -compiled with a non-Microsoft compiler on Windows. - -PortAudio solves this problem using the iasiothiscallresolver library -which is included in the distribution. When building ASIO support for -non-Microsoft compilers, be sure to compile and link with -iasiothiscallresolver.cpp. Note that iasiothiscallresolver includes -conditional directives which cause it to have no effect if it is -compiled with a Microsoft compiler, or on the Macintosh. - -If you use configure and make (see above), this should be handled -automatically for you. - -For further information about the IASIO thiscall problem see this page: -http://www.audiomulch.com/~rossb/code/calliasio - - - -Macintosh ASIO SDK Bug Patch ----------------------------- - -There is a bug in the ASIO SDK that causes the Macintosh version to -often fail during initialization. Below is a patch that you can apply. - -In codefragments.cpp replace getFrontProcessDirectory function with -the following one (GetFrontProcess replaced by GetCurrentProcess). - - -bool CodeFragments::getFrontProcessDirectory(void *specs) -{ - FSSpec *fss = (FSSpec *)specs; - ProcessInfoRec pif; - ProcessSerialNumber psn; - - memset(&psn,0,(long)sizeof(ProcessSerialNumber)); - // if(GetFrontProcess(&psn) == noErr) // wrong !!! - if(GetCurrentProcess(&psn) == noErr) // correct !!! - { - pif.processName = 0; - pif.processAppSpec = fss; - pif.processInfoLength = sizeof(ProcessInfoRec); - if(GetProcessInformation(&psn, &pif) == noErr) - return true; - } - return false; -} - - ---- diff --git a/portaudio/pa_asio/CVS/Entries b/portaudio/pa_asio/CVS/Entries deleted file mode 100644 index c89cf4e65..000000000 --- a/portaudio/pa_asio/CVS/Entries +++ /dev/null @@ -1,8 +0,0 @@ -/ASIO-README.txt/1.1.2.4/Fri Sep 19 08:46:15 2003//Tv19-devel -/Callback_adaptation_.pdf/1.1.1.1/Tue Jan 22 00:51:53 2002/-kb/Tv19-devel -/Pa_ASIO.pdf/1.1.1.1/Tue Jan 22 00:51:56 2002/-kb/Tv19-devel -/iasiothiscallresolver.cpp/1.1.2.4/Sat Jul 10 03:27:41 2004//Tv19-devel -/iasiothiscallresolver.h/1.1.2.3/Mon Jul 12 13:35:05 2004//Tv19-devel -/pa_asio.cpp/1.7.2.68/Mon Dec 5 04:55:28 2005//Tv19-devel -/pa_asio.h/1.1.2.7/Sat Jan 1 19:35:33 2005//Tv19-devel -D diff --git a/portaudio/pa_asio/CVS/Repository b/portaudio/pa_asio/CVS/Repository deleted file mode 100644 index 7683a07f5..000000000 --- a/portaudio/pa_asio/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_asio diff --git a/portaudio/pa_asio/CVS/Root b/portaudio/pa_asio/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_asio/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_asio/CVS/Tag b/portaudio/pa_asio/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_asio/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_asio/Callback_adaptation_.pdf b/portaudio/pa_asio/Callback_adaptation_.pdf deleted file mode 100644 index 76bf67863524626aa079d74629c9dd9f720b0b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50527 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 F{{U7nKEVJ0 diff --git a/portaudio/pa_asio/Pa_ASIO.pdf b/portaudio/pa_asio/Pa_ASIO.pdf deleted file mode 100644 index ac5ecadbc9c861e9fae4b77f5493951504f36f4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50778 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 Vb#+HAUpR1x2_s}-kylk9{6Fg**QEde diff --git a/portaudio/pa_asio/iasiothiscallresolver.cpp b/portaudio/pa_asio/iasiothiscallresolver.cpp deleted file mode 100644 index 8dfefbd67..000000000 --- a/portaudio/pa_asio/iasiothiscallresolver.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/* - IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for - the top level description - this comment describes the technical details of - the implementation. - - The latest version of this file is available from: - http://www.audiomulch.com/~rossb/code/calliasio - - please email comments to Ross Bencina <rossb@audiomulch.com> - - BACKGROUND - - The IASIO interface declared in the Steinberg ASIO 2 SDK declares - functions with no explicit calling convention. This causes MSVC++ to default - to using the thiscall convention, which is a proprietary convention not - implemented by some non-microsoft compilers - notably borland BCC, - C++Builder, and gcc. MSVC++ is the defacto standard compiler used by - Steinberg. As a result of this situation, the ASIO sdk will compile with - any compiler, however attempting to execute the compiled code will cause a - crash due to different default calling conventions on non-Microsoft - compilers. - - IASIOThiscallResolver solves the problem by providing an adapter class that - delegates to the IASIO interface using the correct calling convention - (thiscall). Due to the lack of support for thiscall in the Borland and GCC - compilers, the calls have been implemented in assembly language. - - A number of macros are defined for thiscall function calls with different - numbers of parameters, with and without return values - it may be possible - to modify the format of these macros to make them work with other inline - assemblers. - - - THISCALL DEFINITION - - A number of definitions of the thiscall calling convention are floating - around the internet. The following definition has been validated against - output from the MSVC++ compiler: - - For non-vararg functions, thiscall works as follows: the object (this) - pointer is passed in ECX. All arguments are passed on the stack in - right to left order. The return value is placed in EAX. The callee - clears the passed arguments from the stack. - - - FINDING FUNCTION POINTERS FROM AN IASIO POINTER - - The first field of a COM object is a pointer to its vtble. Thus a pointer - to an object implementing the IASIO interface also points to a pointer to - that object's vtbl. The vtble is a table of function pointers for all of - the virtual functions exposed by the implemented interfaces. - - If we consider a variable declared as a pointer to IASO: - - IASIO *theAsioDriver - - theAsioDriver points to: - - object implementing IASIO - { - IASIOvtbl *vtbl - other data - } - - in other words, theAsioDriver points to a pointer to an IASIOvtbl - - vtbl points to a table of function pointers: - - IASIOvtbl ( interface IASIO : public IUnknown ) - { - (IUnknown functions) - 0 virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0; - 4 virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0; - 8 virtual ULONG STDMETHODCALLTYPE (*Release)() = 0; - - (IASIO functions) - 12 virtual ASIOBool (*init)(void *sysHandle) = 0; - 16 virtual void (*getDriverName)(char *name) = 0; - 20 virtual long (*getDriverVersion)() = 0; - 24 virtual void (*getErrorMessage)(char *string) = 0; - 28 virtual ASIOError (*start)() = 0; - 32 virtual ASIOError (*stop)() = 0; - 36 virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0; - 40 virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0; - 44 virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize, - long *preferredSize, long *granularity) = 0; - 48 virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0; - 52 virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0; - 56 virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0; - 60 virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0; - 64 virtual ASIOError (*setClockSource)(long reference) = 0; - 68 virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; - 72 virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0; - 76 virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels, - long bufferSize, ASIOCallbacks *callbacks) = 0; - 80 virtual ASIOError (*disposeBuffers)() = 0; - 84 virtual ASIOError (*controlPanel)() = 0; - 88 virtual ASIOError (*future)(long selector,void *opt) = 0; - 92 virtual ASIOError (*outputReady)() = 0; - }; - - The numbers in the left column show the byte offset of each function ptr - from the beginning of the vtbl. These numbers are used in the code below - to select different functions. - - In order to find the address of a particular function, theAsioDriver - must first be dereferenced to find the value of the vtbl pointer: - - mov eax, theAsioDriver - mov edx, [theAsioDriver] // edx now points to vtbl[0] - - Then an offset must be added to the vtbl pointer to select a - particular function, for example vtbl+44 points to the slot containing - a pointer to the getBufferSize function. - - Finally vtbl+x must be dereferenced to obtain the value of the function - pointer stored in that address: - - call [edx+44] // call the function pointed to by - // the value in the getBufferSize field of the vtbl - - - SEE ALSO - - Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same - problem by providing a new COM interface which wraps IASIO with an - interface that uses portable calling conventions. OpenASIO must be compiled - with MSVC, and requires that you ship the OpenASIO DLL with your - application. - - - ACKNOWLEDGEMENTS - - Ross Bencina: worked out the thiscall details above, wrote the original - Borland asm macros, and a patch for asio.cpp (which is no longer needed). - Thanks to Martin Fay for introducing me to the issues discussed here, - and to Rene G. Ceballos for assisting with asm dumps from MSVC++. - - Antti Silvast: converted the original calliasio to work with gcc and NASM - by implementing the asm code in a separate file. - - Fraser Adams: modified the original calliasio containing the Borland inline - asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax - for gcc. This seems a neater approach for gcc than to have a separate .asm - file and it means that we only need one version of the thiscall patch. - - Fraser Adams: rewrote the original calliasio patch in the form of the - IASIOThiscallResolver class in order to avoid modifications to files from - the Steinberg SDK, which may have had potential licence issues. - - Andrew Baldwin: contributed fixes for compatibility problems with more - recent versions of the gcc assembler. -*/ - - -// We only need IASIOThiscallResolver at all if we are on Win32. For other -// platforms we simply bypass the IASIOThiscallResolver definition to allow us -// to be safely #include'd whatever the platform to keep client code portable -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) - - -// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver -// is not used. -#if !defined(_MSC_VER) - - -#include <new> -#include <assert.h> - -// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is -// #include'd before it in client code, we do NOT want to do this test here. -#define iasiothiscallresolver_sourcefile 1 -#include "iasiothiscallresolver.h" -#undef iasiothiscallresolver_sourcefile - -// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want -// this macro defined in this translation unit. -#undef ASIOInit - - -// theAsioDriver is a global pointer to the current IASIO instance which the -// ASIO SDK uses to perform all actions on the IASIO interface. We substitute -// our own forwarding interface into this pointer. -extern IASIO* theAsioDriver; - - -// The following macros define the inline assembler for BORLAND first then gcc - -#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__) - - -#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\ - void *this_ = (thisPtr); \ - __asm { \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - mov resultName, eax ; \ - } - - -#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\ - void *this_ = (thisPtr); \ - __asm { \ - mov eax, param1 ; \ - push eax ; \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - } - - -#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\ - void *this_ = (thisPtr); \ - __asm { \ - mov eax, param1 ; \ - push eax ; \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - mov resultName, eax ; \ - } - - -#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\ - void *this_ = (thisPtr); \ - void *doubleParamPtr_ (¶m1); \ - __asm { \ - mov eax, doubleParamPtr_ ; \ - push [eax+4] ; \ - push [eax] ; \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - mov resultName, eax ; \ - } - - -#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\ - void *this_ = (thisPtr); \ - __asm { \ - mov eax, param2 ; \ - push eax ; \ - mov eax, param1 ; \ - push eax ; \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - mov resultName, eax ; \ - } - - -#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ - void *this_ = (thisPtr); \ - __asm { \ - mov eax, param4 ; \ - push eax ; \ - mov eax, param3 ; \ - push eax ; \ - mov eax, param2 ; \ - push eax ; \ - mov eax, param1 ; \ - push eax ; \ - mov ecx, this_ ; \ - mov eax, [ecx] ; \ - call [eax+funcOffset] ; \ - mov resultName, eax ; \ - } - - -#elif defined(__GNUC__) - - -#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \ - __asm__ __volatile__ ("movl (%1), %%edx\n\t" \ - "call *"#funcOffset"(%%edx)\n\t" \ - :"=a"(resultName) /* Output Operands */ \ - :"c"(thisPtr) /* Input Operands */ \ - ); \ - - -#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \ - __asm__ __volatile__ ("pushl %0\n\t" \ - "movl (%1), %%edx\n\t" \ - "call *"#funcOffset"(%%edx)\n\t" \ - : /* Output Operands */ \ - :"r"(param1), /* Input Operands */ \ - "c"(thisPtr) \ - ); \ - - -#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \ - __asm__ __volatile__ ("pushl %1\n\t" \ - "movl (%2), %%edx\n\t" \ - "call *"#funcOffset"(%%edx)\n\t" \ - :"=a"(resultName) /* Output Operands */ \ - :"r"(param1), /* Input Operands */ \ - "c"(thisPtr) \ - ); \ - - -#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \ - __asm__ __volatile__ ("pushl 4(%1)\n\t" \ - "pushl (%1)\n\t" \ - "movl (%2), %%edx\n\t" \ - "call *"#funcOffset"(%%edx);\n\t" \ - :"=a"(resultName) /* Output Operands */ \ - :"a"(¶m1), /* Input Operands */ \ - /* Note: Using "r" above instead of "a" fails */ \ - /* when using GCC 3.3.3, and maybe later versions*/\ - "c"(thisPtr) \ - ); \ - - -#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \ - __asm__ __volatile__ ("pushl %1\n\t" \ - "pushl %2\n\t" \ - "movl (%3), %%edx\n\t" \ - "call *"#funcOffset"(%%edx)\n\t" \ - :"=a"(resultName) /* Output Operands */ \ - :"r"(param2), /* Input Operands */ \ - "r"(param1), \ - "c"(thisPtr) \ - ); \ - - -#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ - __asm__ __volatile__ ("pushl %1\n\t" \ - "pushl %2\n\t" \ - "pushl %3\n\t" \ - "pushl %4\n\t" \ - "movl (%5), %%edx\n\t" \ - "call *"#funcOffset"(%%edx)\n\t" \ - :"=a"(resultName) /* Output Operands */ \ - :"r"(param4), /* Input Operands */ \ - "r"(param3), \ - "r"(param2), \ - "r"(param1), \ - "c"(thisPtr) \ - ); \ - -#endif - - - -// Our static singleton instance. -IASIOThiscallResolver IASIOThiscallResolver::instance; - -// Constructor called to initialize static Singleton instance above. Note that -// it is important not to clear that_ incase it has already been set by the call -// to placement new in ASIOInit(). -IASIOThiscallResolver::IASIOThiscallResolver() -{ -} - -// Constructor called from ASIOInit() below -IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that) -: that_( that ) -{ -} - -// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not -// really a COM object, just a wrapper which will work with the ASIO SDK. -// If you wanted to use ASIO without the SDK you might want to implement COM -// aggregation in these methods. -HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv) -{ - (void)riid; // suppress unused variable warning - - assert( false ); // this function should never be called by the ASIO SDK. - - *ppv = NULL; - return E_NOINTERFACE; -} - -ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef() -{ - assert( false ); // this function should never be called by the ASIO SDK. - - return 1; -} - -ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release() -{ - assert( false ); // this function should never be called by the ASIO SDK. - - return 1; -} - - -// Implement the IASIO interface methods by performing the vptr manipulation -// described above then delegating to the real implementation. -ASIOBool IASIOThiscallResolver::init(void *sysHandle) -{ - ASIOBool result; - CALL_THISCALL_1( result, that_, 12, sysHandle ); - return result; -} - -void IASIOThiscallResolver::getDriverName(char *name) -{ - CALL_VOID_THISCALL_1( that_, 16, name ); -} - -long IASIOThiscallResolver::getDriverVersion() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 20 ); - return result; -} - -void IASIOThiscallResolver::getErrorMessage(char *string) -{ - CALL_VOID_THISCALL_1( that_, 24, string ); -} - -ASIOError IASIOThiscallResolver::start() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 28 ); - return result; -} - -ASIOError IASIOThiscallResolver::stop() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 32 ); - return result; -} - -ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels) -{ - ASIOBool result; - CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels ); - return result; -} - -ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency) -{ - ASIOBool result; - CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency ); - return result; -} - -ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize, - long *preferredSize, long *granularity) -{ - ASIOBool result; - CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity ); - return result; -} - -ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate) -{ - ASIOBool result; - CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate ); - return result; -} - -ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate) -{ - ASIOBool result; - CALL_THISCALL_1( result, that_, 52, sampleRate ); - return result; -} - -ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate) -{ - ASIOBool result; - CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate ); - return result; -} - -ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources) -{ - ASIOBool result; - CALL_THISCALL_2( result, that_, 60, clocks, numSources ); - return result; -} - -ASIOError IASIOThiscallResolver::setClockSource(long reference) -{ - ASIOBool result; - CALL_THISCALL_1( result, that_, 64, reference ); - return result; -} - -ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) -{ - ASIOBool result; - CALL_THISCALL_2( result, that_, 68, sPos, tStamp ); - return result; -} - -ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info) -{ - ASIOBool result; - CALL_THISCALL_1( result, that_, 72, info ); - return result; -} - -ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos, - long numChannels, long bufferSize, ASIOCallbacks *callbacks) -{ - ASIOBool result; - CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks ); - return result; -} - -ASIOError IASIOThiscallResolver::disposeBuffers() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 80 ); - return result; -} - -ASIOError IASIOThiscallResolver::controlPanel() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 84 ); - return result; -} - -ASIOError IASIOThiscallResolver::future(long selector,void *opt) -{ - ASIOBool result; - CALL_THISCALL_2( result, that_, 88, selector, opt ); - return result; -} - -ASIOError IASIOThiscallResolver::outputReady() -{ - ASIOBool result; - CALL_THISCALL_0( result, that_, 92 ); - return result; -} - - -// Implement our substitute ASIOInit() method -ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info) -{ - // To ensure that our instance's vptr is correctly constructed, even if - // ASIOInit is called prior to main(), we explicitly call its constructor - // (potentially over the top of an existing instance). Note that this is - // pretty ugly, and is only safe because IASIOThiscallResolver has no - // destructor and contains no objects with destructors. - new((void*)&instance) IASIOThiscallResolver( theAsioDriver ); - - // Interpose between ASIO client code and the real driver. - theAsioDriver = &instance; - - // Note that we never need to switch theAsioDriver back to point to the - // real driver because theAsioDriver is reset to zero in ASIOExit(). - - // Delegate to the real ASIOInit - return ::ASIOInit(info); -} - - -#endif /* !defined(_MSC_VER) */ - -#endif /* Win32 */ - diff --git a/portaudio/pa_asio/iasiothiscallresolver.h b/portaudio/pa_asio/iasiothiscallresolver.h deleted file mode 100644 index 2ecfed799..000000000 --- a/portaudio/pa_asio/iasiothiscallresolver.h +++ /dev/null @@ -1,197 +0,0 @@ -// **************************************************************************** -// File: IASIOThiscallResolver.h -// Description: The IASIOThiscallResolver class implements the IASIO -// interface and acts as a proxy to the real IASIO interface by -// calling through its vptr table using the thiscall calling -// convention. To put it another way, we interpose -// IASIOThiscallResolver between ASIO SDK code and the driver. -// This is necessary because most non-Microsoft compilers don't -// implement the thiscall calling convention used by IASIO. -// -// iasiothiscallresolver.cpp contains the background of this -// problem plus a technical description of the vptr -// manipulations. -// -// In order to use this mechanism one simply has to add -// iasiothiscallresolver.cpp to the list of files to compile -// and #include <iasiothiscallresolver.h> -// -// Note that this #include must come after the other ASIO SDK -// #includes, for example: -// -// #include <windows.h> -// #include <asiosys.h> -// #include <asio.h> -// #include <asiodrivers.h> -// #include <iasiothiscallresolver.h> -// -// Actually the important thing is to #include -// <iasiothiscallresolver.h> after <asio.h>. We have -// incorporated a test to enforce this ordering. -// -// The code transparently takes care of the interposition by -// using macro substitution to intercept calls to ASIOInit() -// and ASIOExit(). We save the original ASIO global -// "theAsioDriver" in our "that" variable, and then set -// "theAsioDriver" to equal our IASIOThiscallResolver instance. -// -// Whilst this method of resolving the thiscall problem requires -// the addition of #include <iasiothiscallresolver.h> to client -// code it has the advantage that it does not break the terms -// of the ASIO licence by publishing it. We are NOT modifying -// any Steinberg code here, we are merely implementing the IASIO -// interface in the same way that we would need to do if we -// wished to provide an open source ASIO driver. -// -// For compilation with MinGW -lole32 needs to be added to the -// linker options. For BORLAND, linking with Import32.lib is -// sufficient. -// -// The dependencies are with: CoInitialize, CoUninitialize, -// CoCreateInstance, CLSIDFromString - used by asiolist.cpp -// and are required on Windows whether ThiscallResolver is used -// or not. -// -// Searching for the above strings in the root library path -// of your compiler should enable the correct libraries to be -// identified if they aren't immediately obvious. -// -// Note that the current implementation of IASIOThiscallResolver -// is not COM compliant - it does not correctly implement the -// IUnknown interface. Implementing it is not necessary because -// it is not called by parts of the ASIO SDK which call through -// theAsioDriver ptr. The IUnknown methods are implemented as -// assert(false) to ensure that the code fails if they are -// ever called. -// Restrictions: None. Public Domain & Open Source distribute freely -// You may use IASIOThiscallResolver commercially as well as -// privately. -// You the user assume the responsibility for the use of the -// files, binary or text, and there is no guarantee or warranty, -// expressed or implied, including but not limited to the -// implied warranties of merchantability and fitness for a -// particular purpose. You assume all responsibility and agree -// to hold no entity, copyright holder or distributors liable -// for any loss of data or inaccurate representations of data -// as a result of using IASIOThiscallResolver. -// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from -// Andrew Baldwin, and volatile for whole gcc asm blocks, -// both for compatibility with newer gcc versions. Cleaned up -// Borland asm to use one less register. -// 1.3 Switched to including assert.h for better compatibility. -// Wrapped entire .h and .cpp contents with a check for -// _MSC_VER to provide better compatibility with MS compilers. -// Changed Singleton implementation to use static instance -// instead of freestore allocated instance. Removed ASIOExit -// macro as it is no longer needed. -// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to -// allow them to be embedded in expressions (if statements). -// Cleaned up some comments. Removed combase.c dependency (it -// doesn't compile with BCB anyway) by stubbing IUnknown. -// 1.1 Incorporated comments from Ross Bencina including things -// such as changing name from ThiscallResolver to -// IASIOThiscallResolver, tidying up the constructor, fixing -// a bug in IASIOThiscallResolver::ASIOExit() and improving -// portability through the use of conditional compilation -// 1.0 Initial working version. -// Created: 6/09/2003 -// Authors: Fraser Adams -// Ross Bencina -// Rene G. Ceballos -// Martin Fay -// Antti Silvast -// Andrew Baldwin -// -// **************************************************************************** - - -#ifndef included_iasiothiscallresolver_h -#define included_iasiothiscallresolver_h - -// We only need IASIOThiscallResolver at all if we are on Win32. For other -// platforms we simply bypass the IASIOThiscallResolver definition to allow us -// to be safely #include'd whatever the platform to keep client code portable -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) - - -// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver -// is not used. -#if !defined(_MSC_VER) - - -// The following is in order to ensure that this header is only included after -// the other ASIO headers (except for the case of iasiothiscallresolver.cpp). -// We need to do this because IASIOThiscallResolver works by eclipsing the -// original definition of ASIOInit() with a macro (see below). -#if !defined(iasiothiscallresolver_sourcefile) - #if !defined(__ASIO_H) - #error iasiothiscallresolver.h must be included AFTER asio.h - #endif -#endif - -#include <windows.h> -#include <asiodrvr.h> /* From ASIO SDK */ - - -class IASIOThiscallResolver : public IASIO { -private: - IASIO* that_; // Points to the real IASIO - - static IASIOThiscallResolver instance; // Singleton instance - - // Constructors - declared private so construction is limited to - // our Singleton instance - IASIOThiscallResolver(); - IASIOThiscallResolver(IASIO* that); -public: - - // Methods from the IUnknown interface. We don't fully implement IUnknown - // because the ASIO SDK never calls these methods through theAsioDriver ptr. - // These methods are implemented as assert(false). - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv); - virtual ULONG STDMETHODCALLTYPE AddRef(); - virtual ULONG STDMETHODCALLTYPE Release(); - - // Methods from the IASIO interface, implemented as forwarning calls to that. - virtual ASIOBool init(void *sysHandle); - virtual void getDriverName(char *name); - virtual long getDriverVersion(); - virtual void getErrorMessage(char *string); - virtual ASIOError start(); - virtual ASIOError stop(); - virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels); - virtual ASIOError getLatencies(long *inputLatency, long *outputLatency); - virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); - virtual ASIOError canSampleRate(ASIOSampleRate sampleRate); - virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate); - virtual ASIOError setSampleRate(ASIOSampleRate sampleRate); - virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources); - virtual ASIOError setClockSource(long reference); - virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp); - virtual ASIOError getChannelInfo(ASIOChannelInfo *info); - virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks); - virtual ASIOError disposeBuffers(); - virtual ASIOError controlPanel(); - virtual ASIOError future(long selector,void *opt); - virtual ASIOError outputReady(); - - // Class method, see ASIOInit() macro below. - static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit -}; - - -// Replace calls to ASIOInit with our interposing version. -// This macro enables us to perform thiscall resolution simply by #including -// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be -// included _after_ the asio #includes) - -#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name)) - - -#endif /* !defined(_MSC_VER) */ - -#endif /* Win32 */ - -#endif /* included_iasiothiscallresolver_h */ - - diff --git a/portaudio/pa_asio/pa_asio.cpp b/portaudio/pa_asio/pa_asio.cpp deleted file mode 100644 index 21987c714..000000000 --- a/portaudio/pa_asio/pa_asio.cpp +++ /dev/null @@ -1,2958 +0,0 @@ -/* - * $Id: pa_asio.cpp,v 1.7.2.68 2005/12/05 04:55:28 rossbencina Exp $ - * Portable Audio I/O Library for ASIO Drivers - * - * Author: Stephane Letz - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* Modification History - - 08-03-01 First version : Stephane Letz - 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk - 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz - 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz - 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil - 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when - the stream is stopped : Stephane Letz - 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when - the stream is stopped : Stephane Letz - 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that - respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz - 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz - 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil - 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk - 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz - 11-06-01 Rename functions : Stephane Letz - 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz - 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk - 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz - 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz - 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz - 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz - 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk - 13-04-02 Removes another compiler warning : Stephane Letz - 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz - 12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina - 18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B. - 21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina - ** NOTE maintanance history is now stored in CVS ** -*/ - -/** @file - - Note that specific support for paInputUnderflow, paOutputOverflow and - paNeverDropInput is not necessary or possible with this driver due to the - synchronous full duplex double-buffered architecture of ASIO. - - @todo check that CoInitialize()/CoUninitialize() are always correctly - paired, even in error cases. - - @todo implement host api specific extension to set i/o buffer sizes in frames - - @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable - - @todo implement IsFormatSupported - - @todo work out how to implement stream stoppage from callback and - implement IsStreamActive properly. Stream stoppage could be implemented - using a high-priority thread blocked on an Event which is signalled - by the callback. Or, we could just call ASIO stop from the callback - and see what happens. - - @todo rigorously check asio return codes and convert to pa error codes - - @todo Different channels of a multichannel stream can have different sample - formats, but we assume that all are the same as the first channel for now. - Fixing this will require the block processor to maintain per-channel - conversion functions - could get nasty. - - @todo investigate whether the asio processNow flag needs to be honoured - - @todo handle asioMessages() callbacks in a useful way, or at least document - what cases we don't handle. - - @todo miscellaneous other FIXMEs - - @todo provide an asio-specific method for setting the systems specific - value (aka main window handle) - check that this matches the value - passed to PaAsio_ShowControlPanel, or remove it entirely from - PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel - to be called for the currently open stream (at present all streams - must be closed). -*/ - - - -#include <stdio.h> -#include <assert.h> -#include <string.h> -//#include <values.h> - -#include <windows.h> -#include <mmsystem.h> - -#include "portaudio.h" -#include "pa_asio.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "asiosys.h" -#include "asio.h" -#include "asiodrivers.h" -#include "iasiothiscallresolver.h" - -/* -#if MAC -#include <Devices.h> -#include <Timer.h> -#include <Math64.h> -#else -*/ -/* -#include <math.h> -#include <windows.h> -#include <mmsystem.h> -*/ -/* -#endif -*/ - -/* external references */ -extern AsioDrivers* asioDrivers ; -bool loadAsioDriver(char *name); - - -/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */ -/* not tested at all since new code was introduced. */ -#define CARBON_COMPATIBLE (0) - - - - -/* prototypes for functions declared in this file */ - -extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - -/* our ASIO callback functions */ - -static void bufferSwitch(long index, ASIOBool processNow); -static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow); -static void sampleRateChanged(ASIOSampleRate sRate); -static long asioMessages(long selector, long value, void* message, double* opt); - -static ASIOCallbacks asioCallbacks_ = - { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo }; - - -#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText ) - - -static void PaAsio_SetLastSystemError( DWORD errorCode ) -{ - LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); - PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf ); - LocalFree( lpMsgBuf ); -} - -#define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \ - PaAsio_SetLastSystemError( errorCode ) - - -static const char* PaAsio_GetAsioErrorText( ASIOError asioError ) -{ - const char *result; - - switch( asioError ){ - case ASE_OK: - case ASE_SUCCESS: result = "Success"; break; - case ASE_NotPresent: result = "Hardware input or output is not present or available"; break; - case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break; - case ASE_InvalidParameter: result = "Input parameter invalid"; break; - case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break; - case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break; - case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break; - case ASE_NoMemory: result = "Not enough memory for completing the request"; break; - default: result = "Unknown ASIO error"; break; - } - - return result; -} - - -#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \ - PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) ) - - - - -// Atomic increment and decrement operations -#if MAC - /* need to be implemented on Mac */ - inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));} -#elif WINDOWS - inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));} -#endif - - - -typedef struct PaAsioDriverInfo -{ - ASIODriverInfo asioDriverInfo; - long inputChannelCount, outputChannelCount; - long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity; - bool postOutput; -} -PaAsioDriverInfo; - - -/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - void *systemSpecific; - - /* the ASIO C API only allows one ASIO driver to be open at a time, - so we keep track of whether we have the driver open here, and - use this information to return errors from OpenStream if the - driver is already open. - - openAsioDeviceIndex will be PaNoDevice if there is no device open - and a valid pa_asio (not global) device index otherwise. - - openAsioDriverInfo is populated with the driver info for the - currently open device (if any) - */ - PaDeviceIndex openAsioDeviceIndex; - PaAsioDriverInfo openAsioDriverInfo; -} -PaAsioHostApiRepresentation; - - -/* - Retrieve <driverCount> driver names from ASIO, returned in a char** - allocated in <group>. -*/ -static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount ) -{ - char **result = 0; - int i; - - result =(char**)PaUtil_GroupAllocateMemory( - group, sizeof(char*) * driverCount ); - if( !result ) - goto error; - - result[0] = (char*)PaUtil_GroupAllocateMemory( - group, 32 * driverCount ); - if( !result[0] ) - goto error; - - for( i=0; i<driverCount; ++i ) - result[i] = result[0] + (32 * i); - - asioDrivers->getDriverNames( result, driverCount ); - -error: - return result; -} - - -static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type) -{ - switch (type) { - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - return paInt16; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - return paFloat32; - - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - return paInt32; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - return paInt24; - - default: - return paCustomFormat; - } -} - -void AsioSampleTypeLOG(ASIOSampleType type) -{ - switch (type) { - case ASIOSTInt16MSB: PA_DEBUG(("ASIOSTInt16MSB\n")); break; - case ASIOSTInt16LSB: PA_DEBUG(("ASIOSTInt16LSB\n")); break; - case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break; - case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break; - case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break; - case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break; - case ASIOSTInt32MSB: PA_DEBUG(("ASIOSTInt32MSB\n")); break; - case ASIOSTInt32LSB: PA_DEBUG(("ASIOSTInt32LSB\n")); break; - case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break; - case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break; - case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break; - case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break; - case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break; - case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break; - case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break; - case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break; - case ASIOSTInt24MSB: PA_DEBUG(("ASIOSTInt24MSB\n")); break; - case ASIOSTInt24LSB: PA_DEBUG(("ASIOSTInt24LSB\n")); break; - default: PA_DEBUG(("Custom Format%d\n",type));break; - - } -} - -static int BytesPerAsioSample( ASIOSampleType sampleType ) -{ - switch (sampleType) { - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - return 2; - - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - return 8; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - return 4; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - return 3; - - default: - return 0; - } -} - - -static void Swap16( void *buffer, long shift, long count ) -{ - unsigned short *p = (unsigned short*)buffer; - unsigned short temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p++ = (unsigned short)((temp<<8) | (temp>>8)); - } -} - -static void Swap24( void *buffer, long shift, long count ) -{ - unsigned char *p = (unsigned char*)buffer; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p = *(p+2); - *(p+2) = temp; - p += 3; - } -} - -#define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24)); - -static void Swap32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p++ = PA_SWAP32_( temp); - } -} - -static void SwapShiftLeft32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - temp = PA_SWAP32_( temp); - *p++ = temp << shift; - } -} - -static void ShiftRightSwap32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p >> shift; - *p++ = PA_SWAP32_( temp); - } -} - -static void ShiftLeft32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - *p++ = temp << shift; - } -} - -static void ShiftRight32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - *p++ = temp >> shift; - } -} - -#define PA_SWAP_( x, y ) temp=x; x = y; y = temp; - -static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count ) -{ - double *in = (double*)buffer; - float *out = (float*)buffer; - unsigned char *p; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - p = (unsigned char*)in; - PA_SWAP_( p[0], p[7] ); - PA_SWAP_( p[1], p[6] ); - PA_SWAP_( p[2], p[5] ); - PA_SWAP_( p[3], p[4] ); - - *out++ = (float) (*in++); - } -} - -static void ConvertFloat64ToFloat32( void *buffer, long shift, long count ) -{ - double *in = (double*)buffer; - float *out = (float*)buffer; - (void) shift; /* unused parameter */ - - while( count-- ) - *out++ = (float) (*in++); -} - -static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count ) -{ - float *in = ((float*)buffer) + (count-1); - double *out = ((double*)buffer) + (count-1); - unsigned char *p; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - *out = *in--; - - p = (unsigned char*)out; - PA_SWAP_( p[0], p[7] ); - PA_SWAP_( p[1], p[6] ); - PA_SWAP_( p[2], p[5] ); - PA_SWAP_( p[3], p[4] ); - - out--; - } -} - -static void ConvertFloat32ToFloat64( void *buffer, long shift, long count ) -{ - float *in = ((float*)buffer) + (count-1); - double *out = ((double*)buffer) + (count-1); - (void) shift; /* unused parameter */ - - while( count-- ) - *out-- = *in--; -} - -#ifdef MAC -#define PA_MSB_IS_NATIVE_ -#undef PA_LSB_IS_NATIVE_ -#endif - -#ifdef WINDOWS -#undef PA_MSB_IS_NATIVE_ -#define PA_LSB_IS_NATIVE_ -#endif - -typedef void PaAsioBufferConverter( void *, long, long ); - -static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift ) -{ - *shift = 0; - *converter = 0; - - switch (type) { - case ASIOSTInt16MSB: - /* dest: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTInt16LSB: - /* dest: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTFloat32MSB: - /* dest: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat32LSB: - /* dest: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat64MSB: - /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap64ConvertFloat64ToFloat32; - #else - *converter = ConvertFloat64ToFloat32; - #endif - break; - case ASIOSTFloat64LSB: - /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap64ConvertFloat64ToFloat32; - #else - *converter = ConvertFloat64ToFloat32; - #endif - break; - case ASIOSTInt32MSB: - /* dest: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32LSB: - /* dest: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32MSB16: - /* dest: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 16; - break; - case ASIOSTInt32MSB18: - /* dest: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 14; - break; - case ASIOSTInt32MSB20: - /* dest: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 12; - break; - case ASIOSTInt32MSB24: - /* dest: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 8; - break; - case ASIOSTInt32LSB16: - /* dest: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 16; - break; - case ASIOSTInt32LSB18: - /* dest: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 14; - break; - case ASIOSTInt32LSB20: - /* dest: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 12; - break; - case ASIOSTInt32LSB24: - /* dest: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 8; - break; - case ASIOSTInt24MSB: - /* dest: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - case ASIOSTInt24LSB: - /* dest: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - } -} - - -static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift ) -{ - *shift = 0; - *converter = 0; - - switch (type) { - case ASIOSTInt16MSB: - /* src: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTInt16LSB: - /* src: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTFloat32MSB: - /* src: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat32LSB: - /* src: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat64MSB: - /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ConvertFloat32ToFloat64Swap64; - #else - *converter = ConvertFloat32ToFloat64; - #endif - break; - case ASIOSTFloat64LSB: - /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ConvertFloat32ToFloat64Swap64; - #else - *converter = ConvertFloat32ToFloat64; - #endif - break; - case ASIOSTInt32MSB: - /* src: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32LSB: - /* src: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32MSB16: - /* src: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 16; - break; - case ASIOSTInt32MSB18: - /* src: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 14; - break; - case ASIOSTInt32MSB20: - /* src: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 12; - break; - case ASIOSTInt32MSB24: - /* src: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 8; - break; - case ASIOSTInt32LSB16: - /* src: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 16; - break; - case ASIOSTInt32LSB18: - /* src: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 14; - break; - case ASIOSTInt32LSB20: - /* src: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 12; - break; - case ASIOSTInt32LSB24: - /* src: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 8; - break; - case ASIOSTInt24MSB: - /* src: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - case ASIOSTInt24LSB: - /* src: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - } -} - - -typedef struct PaAsioDeviceInfo -{ - PaDeviceInfo commonDeviceInfo; - long minBufferSize; - long maxBufferSize; - long preferredBufferSize; - long bufferGranularity; - - ASIOChannelInfo *asioChannelInfos; -} -PaAsioDeviceInfo; - - -PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, - long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - - if( result == paNoError ) - { - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - - if( result == paNoError ) - { - PaAsioDeviceInfo *asioDeviceInfo = - (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - *minLatency = asioDeviceInfo->minBufferSize; - *maxLatency = asioDeviceInfo->maxBufferSize; - *preferredLatency = asioDeviceInfo->preferredBufferSize; - *granularity = asioDeviceInfo->bufferGranularity; - } - } - - return result; -} - - - -/* - load the asio driver named by <driverName> and return statistics about - the driver in info. If no error occurred, the driver will remain open - and must be closed by the called by calling ASIOExit() - if an error - is returned the driver will already be closed. -*/ -static PaError LoadAsioDriver( const char *driverName, - PaAsioDriverInfo *driverInfo, void *systemSpecific ) -{ - PaError result = paNoError; - ASIOError asioError; - int asioIsInitialized = 0; - - if( !loadAsioDriver( const_cast<char*>(driverName) ) ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" ); - goto error; - } - - memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) ); - driverInfo->asioDriverInfo.asioVersion = 2; - driverInfo->asioDriverInfo.sysRef = systemSpecific; - if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - else - { - asioIsInitialized = 1; - } - - if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount, - &driverInfo->outputChannelCount)) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - - if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize, - &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize, - &driverInfo->bufferGranularity)) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - - if( ASIOOutputReady() == ASE_OK ) - driverInfo->postOutput = true; - else - driverInfo->postOutput = false; - - return result; - -error: - if( asioIsInitialized ) - ASIOExit(); - - return result; -} - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */ -static ASIOSampleRate defaultSampleRateSearchOrder_[] - = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, - 192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - - -PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, driverCount; - PaAsioHostApiRepresentation *asioHostApi; - PaAsioDeviceInfo *deviceInfoArray; - char **names; - PaAsioDriverInfo paAsioDriverInfo; - - asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) ); - if( !asioHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - asioHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !asioHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - asioHostApi->systemSpecific = 0; - asioHostApi->openAsioDeviceIndex = paNoDevice; - - *hostApi = &asioHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - - (*hostApi)->info.type = paASIO; - (*hostApi)->info.name = "ASIO"; - (*hostApi)->info.deviceCount = 0; - - #ifdef WINDOWS - /* use desktop window as system specific ptr */ - asioHostApi->systemSpecific = GetDesktopWindow(); - CoInitialize(NULL); - #endif - - /* MUST BE CHECKED : to force fragments loading on Mac */ - loadAsioDriver( "dummy" ); - - /* driverCount is the number of installed drivers - not necessarily - the number of installed physical devices. */ - #if MAC - driverCount = asioDrivers->getNumFragments(); - #elif WINDOWS - driverCount = asioDrivers->asioGetNumDev(); - #endif - - if( driverCount > 0 ) - { - names = GetAsioDriverNames( asioHostApi->allocations, driverCount ); - if( !names ) - { - result = paInsufficientMemory; - goto error; - } - - - /* allocate enough space for all drivers, even if some aren't installed */ - - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < driverCount; ++i ) - { - - PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i])); - - // Since portaudio opens ALL ASIO drivers, and no one else does that, - // we face fact that some drivers were not meant for it, drivers which act - // like shells on top of REAL drivers, for instance. - // so we get duplicate handles, locks and other problems. - // so lets NOT try to load any such wrappers. - // The ones i [davidv] know of so far are: - - if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0 - || strcmp (names[i],"ASIO Multimedia Driver") == 0 - || strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0" - || strncmp(names[i],"Adobe",5) == 0 ) //"Adobe Default Windows Sound 1.5" - { - PA_DEBUG(("BLACKLISTED!!!\n")); - continue; - } - - - /* Attempt to load the asio driver... */ - if( LoadAsioDriver( names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError ) - { - PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo; - - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->name = names[i]; - PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", i,deviceInfo->name)); - PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriverInfo.inputChannelCount)); - PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriverInfo.outputChannelCount)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", i, paAsioDriverInfo.bufferMinSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", i, paAsioDriverInfo.bufferMaxSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriverInfo.bufferPreferredSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", i, paAsioDriverInfo.bufferGranularity)); - - deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount; - deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount; - - deviceInfo->defaultSampleRate = 0.; - bool foundDefaultSampleRate = false; - for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j ) - { - ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] ); - if( asioError != ASE_NoClock && asioError != ASE_NotPresent ) - { - deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j]; - foundDefaultSampleRate = true; - break; - } - } - - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate)); - - if( foundDefaultSampleRate ){ - - /* calculate default latency values from bufferPreferredSize - for default low latency, and bufferPreferredSize * 3 - for default high latency. - use the default sample rate to convert from samples to - seconds. Without knowing what sample rate the user will - use this is the best we can do. - */ - - double defaultLowLatency = - paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - - long defaultHighLatencyBufferSize = - paAsioDriverInfo.bufferPreferredSize * 3; - - if( defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize ) - defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize; - - double defaultHighLatency = - defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate; - - if( defaultHighLatency < defaultLowLatency ) - defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */ - - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - }else{ - - deviceInfo->defaultLowInputLatency = 0.; - deviceInfo->defaultLowOutputLatency = 0.; - deviceInfo->defaultHighInputLatency = 0.; - deviceInfo->defaultHighOutputLatency = 0.; - } - - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency)); - - asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize; - asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize; - asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize; - asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity; - - - asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, - sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels - + deviceInfo->maxOutputChannels) ); - if( !asioDeviceInfo->asioChannelInfos ) - { - result = paInsufficientMemory; - goto error; - } - - int a; - - for( a=0; a < deviceInfo->maxInputChannels; ++a ){ - asioDeviceInfo->asioChannelInfos[a].channel = a; - asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue; - ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - } - - for( a=0; a < deviceInfo->maxOutputChannels; ++a ){ - int b = deviceInfo->maxInputChannels + a; - asioDeviceInfo->asioChannelInfos[b].channel = a; - asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse; - ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - } - - - /* unload the driver */ - ASIOExit(); - - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - } - } - - if( (*hostApi)->info.deviceCount > 0 ) - { - (*hostApi)->info.defaultInputDevice = 0; - (*hostApi)->info.defaultOutputDevice = 0; - } - else - { - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - } - - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( asioHostApi ) - { - if( asioHostApi->allocations ) - { - PaUtil_FreeAllAllocations( asioHostApi->allocations ); - PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); - } - - PaUtil_FreeMemory( asioHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ - - if( asioHostApi->allocations ) - { - PaUtil_FreeAllAllocations( asioHostApi->allocations ); - PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); - } - - PaUtil_FreeMemory( asioHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaError result = paNoError; - PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaDeviceIndex asioDeviceIndex; - ASIOError asioError; - - if( inputParameters && outputParameters ) - { - /* full duplex ASIO stream must use the same device for input and output */ - - if( inputParameters->device != outputParameters->device ) - return paBadIODeviceCombination; - } - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = inputParameters->device; - - /* validate inputStreamInfo */ - /** @todo do more validation here */ - // if( inputParameters->hostApiSpecificStreamInfo ) - // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = outputParameters->device; - - /* validate outputStreamInfo */ - /** @todo do more validation here */ - // if( outputParameters->hostApiSpecificStreamInfo ) - // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - - - /* if an ASIO device is open we can only get format information for the currently open device */ - - if( asioHostApi->openAsioDeviceIndex != paNoDevice - && asioHostApi->openAsioDeviceIndex != asioDeviceIndex ) - { - return paDeviceUnavailable; - } - - - /* NOTE: we load the driver and use its current settings - rather than the ones in our device info structure which may be stale */ - - /* open the device if it's not already open */ - if( asioHostApi->openAsioDeviceIndex == paNoDevice ) - { - result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name, - driverInfo, asioHostApi->systemSpecific ); - if( result != paNoError ) - return result; - } - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > 0 ) - { - if( inputChannelCount > driverInfo->inputChannelCount ) - { - result = paInvalidChannelCount; - goto done; - } - } - - /* check that output device can support outputChannelCount */ - if( outputChannelCount ) - { - if( outputChannelCount > driverInfo->outputChannelCount ) - { - result = paInvalidChannelCount; - goto done; - } - } - - /* query for sample rate support */ - asioError = ASIOCanSampleRate( sampleRate ); - if( asioError == ASE_NoClock || asioError == ASE_NotPresent ) - { - result = paInvalidSampleRate; - goto done; - } - -done: - /* close the device if it wasn't already open */ - if( asioHostApi->openAsioDeviceIndex == paNoDevice ) - { - ASIOExit(); /* not sure if we should check for errors here */ - } - - if( result == paNoError ) - return paFormatIsSupported; - else - return result; -} - - - -/* PaAsioStream - a stream data structure specifically for this implementation */ - -typedef struct PaAsioStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaAsioHostApiRepresentation *asioHostApi; - unsigned long framesPerHostCallback; - - /* ASIO driver info - these may not be needed for the life of the stream, - but store them here until we work out how format conversion is going - to work. */ - - ASIOBufferInfo *asioBufferInfos; - ASIOChannelInfo *asioChannelInfos; - long inputLatency, outputLatency; // actual latencies returned by asio - - long inputChannelCount, outputChannelCount; - bool postOutput; - - void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */ - void **inputBufferPtrs[2]; - void **outputBufferPtrs[2]; - - PaAsioBufferConverter *inputBufferConverter; - long inputShift; - PaAsioBufferConverter *outputBufferConverter; - long outputShift; - - volatile bool stopProcessing; - int stopPlayoutCount; - HANDLE completedBuffersPlayedEvent; - - bool streamFinishedCallbackCalled; - volatile int isActive; - volatile bool zeroOutput; /* all future calls to the callback will output silence */ - - volatile long reenterCount; - volatile long reenterError; - - PaStreamCallbackFlags callbackFlags; -} -PaAsioStream; - -static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */ - - -static void ZeroOutputBuffers( PaAsioStream *stream, long index ) -{ - int i; - - for( i=0; i < stream->outputChannelCount; ++i ) - { - void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index]; - - int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type ); - - memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample ); - } -} - - -static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames, - PaAsioDriverInfo *driverInfo ) -{ - unsigned long result; - - if( suggestedLatencyFrames == 0 ) - { - result = driverInfo->bufferPreferredSize; - } - else{ - if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize ) - { - result = driverInfo->bufferMinSize; - } - else if( suggestedLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize ) - { - result = driverInfo->bufferMaxSize; - } - else - { - if( driverInfo->bufferGranularity == -1 ) - { - /* power-of-two */ - result = 2; - - while( result < suggestedLatencyFrames ) - result *= 2; - - if( result < (unsigned long)driverInfo->bufferMinSize ) - result = driverInfo->bufferMinSize; - - if( result > (unsigned long)driverInfo->bufferMaxSize ) - result = driverInfo->bufferMaxSize; - } - else if( driverInfo->bufferGranularity == 0 ) - { - /* the documentation states that bufferGranularity should be - zero when bufferMinSize, bufferMaxSize and - bufferPreferredSize are the same. We assume that is the case. - */ - - result = driverInfo->bufferPreferredSize; - } - else - { - /* modulo granularity */ - - unsigned long remainder = - suggestedLatencyFrames % driverInfo->bufferGranularity; - - if( remainder == 0 ) - { - result = suggestedLatencyFrames; - } - else - { - result = suggestedLatencyFrames - + (driverInfo->bufferGranularity - remainder); - - if( result > (unsigned long)driverInfo->bufferMaxSize ) - result = driverInfo->bufferMaxSize; - } - } - } - } - - return result; -} - - -/* returns channelSelectors if present */ - -static PaError ValidateAsioSpecificStreamInfo( - const PaStreamParameters *streamParameters, - const PaAsioStreamInfo *streamInfo, - int deviceChannelCount, - int **channelSelectors ) -{ - if( streamInfo ) - { - if( streamInfo->size != sizeof( PaAsioStreamInfo ) - || streamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( streamInfo->flags & paAsioUseChannelSelectors ) - *channelSelectors = streamInfo->channelSelectors; - - if( !(*channelSelectors) ) - return paIncompatibleHostApiSpecificStreamInfo; - - for( int i=0; i < streamParameters->channelCount; ++i ){ - if( (*channelSelectors)[i] < 0 - || (*channelSelectors)[i] >= deviceChannelCount ){ - return paInvalidChannelCount; - } - } - } - - return paNoError; -} - - -/* 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; - PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo; - unsigned long framesPerHostBuffer; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - unsigned long suggestedInputLatencyFrames; - unsigned long suggestedOutputLatencyFrames; - PaDeviceIndex asioDeviceIndex; - ASIOError asioError; - int asioIsInitialized = 0; - int asioBuffersCreated = 0; - int completedBuffersPlayedEventInited = 0; - int i; - PaAsioDriverInfo *driverInfo; - int *inputChannelSelectors = 0; - int *outputChannelSelectors = 0; - - /* unless we move to using lower level ASIO calls, we can only have - one device open at a time */ - if( asioHostApi->openAsioDeviceIndex != paNoDevice ){ - PA_DEBUG(("OpenStream paDeviceUnavailable\n")); - return paDeviceUnavailable; - } - - if( inputParameters && outputParameters ) - { - /* full duplex ASIO stream must use the same device for input and output */ - - if( inputParameters->device != outputParameters->device ){ - PA_DEBUG(("OpenStream paBadIODeviceCombination\n")); - return paBadIODeviceCombination; - } - } - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f); - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = inputParameters->device; - - PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex]; - - /* validate hostApiSpecificStreamInfo */ - inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo; - result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo, - asioDeviceInfo->commonDeviceInfo.maxInputChannels, - &inputChannelSelectors - ); - if( result != paNoError ) return result; - } - else - { - inputChannelCount = 0; - inputSampleFormat = 0; - suggestedInputLatencyFrames = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f); - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = outputParameters->device; - - PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex]; - - /* validate hostApiSpecificStreamInfo */ - outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo; - result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo, - asioDeviceInfo->commonDeviceInfo.maxOutputChannels, - &outputChannelSelectors - ); - if( result != paNoError ) return result; - } - else - { - outputChannelCount = 0; - outputSampleFormat = 0; - suggestedOutputLatencyFrames = 0; - } - - driverInfo = &asioHostApi->openAsioDriverInfo; - - /* NOTE: we load the driver and use its current settings - rather than the ones in our device info structure which may be stale */ - - result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name, - driverInfo, asioHostApi->systemSpecific ); - if( result == paNoError ) - asioIsInitialized = 1; - else{ - PA_DEBUG(("OpenStream ERROR1\n")); - goto error; - } - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > 0 ) - { - if( inputChannelCount > driverInfo->inputChannelCount ) - { - result = paInvalidChannelCount; - PA_DEBUG(("OpenStream ERROR2\n")); - goto error; - } - } - - /* check that output device can support outputChannelCount */ - if( outputChannelCount ) - { - if( outputChannelCount > driverInfo->outputChannelCount ) - { - result = paInvalidChannelCount; - PA_DEBUG(("OpenStream ERROR3\n")); - goto error; - } - } - - - // check that the device supports the requested sample rate - - asioError = ASIOCanSampleRate( sampleRate ); - PA_DEBUG(("ASIOCanSampleRate(%f):%d\n",sampleRate, asioError )); - - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - - - // retrieve the current sample rate, we only change to the requested - // sample rate if the device is not already in that rate. - - ASIOSampleRate oldRate; - asioError = ASIOGetSampleRate(&oldRate); - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate)); - - if (oldRate != sampleRate){ - - PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate)); - asioError = ASIOSetSampleRate( sampleRate ); - /* Set sample rate */ - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate)); - } - else - { - PA_DEBUG(("No Need to change SR\n")); - } - - - /* - IMPLEMENT ME: - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - */ - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ){ - PA_DEBUG(("OpenStream invalid flags!!\n")); - return paInvalidFlag; /* unexpected platform specific flag */ - } - - - stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) ); - if( !stream ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR5\n")); - goto error; - } - - stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); - if( stream->completedBuffersPlayedEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - PA_DEBUG(("OpenStream ERROR6\n")); - goto error; - } - completedBuffersPlayedEventInited = 1; - - - stream->asioBufferInfos = 0; /* for deallocation in error */ - stream->asioChannelInfos = 0; /* for deallocation in error */ - stream->bufferPtrs = 0; /* for deallocation in error */ - - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &asioHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &asioHostApi->blockingStreamInterface, streamCallback, userData ); - } - - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory( - sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) ); - if( !stream->asioBufferInfos ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR7\n")); - goto error; - } - - - for( i=0; i < inputChannelCount; ++i ) - { - ASIOBufferInfo *info = &stream->asioBufferInfos[i]; - - info->isInput = ASIOTrue; - - if( inputChannelSelectors ){ - // inputChannelSelectors values have already been validated in - // ValidateAsioSpecificStreamInfo() above - info->channelNum = inputChannelSelectors[i]; - }else{ - info->channelNum = i; - } - - info->buffers[0] = info->buffers[1] = 0; - } - - for( i=0; i < outputChannelCount; ++i ){ - ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i]; - - info->isInput = ASIOFalse; - - if( outputChannelSelectors ){ - // outputChannelSelectors values have already been validated in - // ValidateAsioSpecificStreamInfo() above - info->channelNum = outputChannelSelectors[i]; - }else{ - info->channelNum = i; - } - - info->buffers[0] = info->buffers[1] = 0; - } - - - framesPerHostBuffer = SelectHostBufferSize( - (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames ) - ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), - driverInfo ); - - - PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer)); - - asioError = ASIOCreateBuffers( stream->asioBufferInfos, - inputChannelCount+outputChannelCount, - framesPerHostBuffer, &asioCallbacks_ ); - - if( asioError != ASE_OK - && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize ) - { - PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) )); - /* - Some buggy drivers (like the Hoontech DSP24) give incorrect - [min, preferred, max] values They should work with the preferred size - value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize - computed in SelectHostBufferSize, we try again with the preferred size. - */ - - framesPerHostBuffer = driverInfo->bufferPreferredSize; - - PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n", framesPerHostBuffer)); - - ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos, - inputChannelCount+outputChannelCount, - framesPerHostBuffer, &asioCallbacks_ ); - if( asioError2 == ASE_OK ) - asioError = ASE_OK; - } - - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - PA_DEBUG(("OpenStream ERROR9\n")); - goto error; - } - - asioBuffersCreated = 1; - - stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory( - sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) ); - if( !stream->asioChannelInfos ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR10\n")); - goto error; - } - - for( i=0; i < inputChannelCount + outputChannelCount; ++i ) - { - stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum; - stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput; - asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - PA_DEBUG(("OpenStream ERROR11\n")); - goto error; - } - } - - stream->bufferPtrs = (void**)PaUtil_AllocateMemory( - 2 * sizeof(void*) * (inputChannelCount + outputChannelCount) ); - if( !stream->bufferPtrs ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR12\n")); - goto error; - } - - if( inputChannelCount > 0 ) - { - stream->inputBufferPtrs[0] = stream-> bufferPtrs; - stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount]; - - for( i=0; i<inputChannelCount; ++i ) - { - stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0]; - stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1]; - } - } - else - { - stream->inputBufferPtrs[0] = 0; - stream->inputBufferPtrs[1] = 0; - } - - if( outputChannelCount > 0 ) - { - stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2]; - stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount]; - - for( i=0; i<outputChannelCount; ++i ) - { - stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0]; - stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1]; - } - } - else - { - stream->outputBufferPtrs[0] = 0; - stream->outputBufferPtrs[1] = 0; - } - - if( inputChannelCount > 0 ) - { - /* FIXME: assume all channels use the same type for now */ - ASIOSampleType inputType = stream->asioChannelInfos[0].type; - - PA_DEBUG(("ASIO Input type:%d",inputType)); - AsioSampleTypeLOG(inputType); - hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType ); - - SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift ); - } - else - { - hostInputSampleFormat = 0; - stream->inputBufferConverter = 0; - } - - if( outputChannelCount > 0 ) - { - /* FIXME: assume all channels use the same type for now */ - ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type; - - PA_DEBUG(("ASIO Output type:%d",outputType)); - AsioSampleTypeLOG(outputType); - hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType ); - - SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift ); - } - else - { - hostOutputSampleFormat = 0; - stream->outputBufferConverter = 0; - } - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerHostBuffer, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ){ - PA_DEBUG(("OpenStream ERROR13\n")); - goto error; - } - - - ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency ); - - stream->streamRepresentation.streamInfo.inputLatency = - (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) - + stream->inputLatency) / sampleRate; // seconds - stream->streamRepresentation.streamInfo.outputLatency = - (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - + stream->outputLatency) / sampleRate; // seconds - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - // the code below prints the ASIO latency which doesn't include the - // buffer processor latency. it reports the added latency separately - PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->inputLatency, - (long)((stream->inputLatency*1000)/ sampleRate), - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate) - )); - - PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->outputLatency, - (long)((stream->outputLatency*1000)/ sampleRate), - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate) - )); - - stream->asioHostApi = asioHostApi; - stream->framesPerHostCallback = framesPerHostBuffer; - - stream->inputChannelCount = inputChannelCount; - stream->outputChannelCount = outputChannelCount; - stream->postOutput = driverInfo->postOutput; - stream->isActive = 0; - - asioHostApi->openAsioDeviceIndex = asioDeviceIndex; - - *s = (PaStream*)stream; - - return result; - -error: - PA_DEBUG(("goto errored\n")); - if( stream ) - { - if( completedBuffersPlayedEventInited ) - CloseHandle( stream->completedBuffersPlayedEvent ); - - if( stream->asioBufferInfos ) - PaUtil_FreeMemory( stream->asioBufferInfos ); - - if( stream->asioChannelInfos ) - PaUtil_FreeMemory( stream->asioChannelInfos ); - - if( stream->bufferPtrs ) - PaUtil_FreeMemory( stream->bufferPtrs ); - - PaUtil_FreeMemory( stream ); - } - - if( asioBuffersCreated ) - ASIODisposeBuffers(); - - if( asioIsInitialized ) - ASIOExit(); - - return result; -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - - /* - IMPLEMENT ME: - - additional stream closing + cleanup - */ - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - stream->asioHostApi->openAsioDeviceIndex = paNoDevice; - - CloseHandle( stream->completedBuffersPlayedEvent ); - - PaUtil_FreeMemory( stream->asioBufferInfos ); - PaUtil_FreeMemory( stream->asioChannelInfos ); - PaUtil_FreeMemory( stream->bufferPtrs ); - PaUtil_FreeMemory( stream ); - - ASIODisposeBuffers(); - ASIOExit(); - - return result; -} - - -static void bufferSwitch(long index, ASIOBool directProcess) -{ -//TAKEN FROM THE ASIO SDK - - // the actual processing callback. - // Beware that this is normally in a seperate thread, hence be sure that - // you take care about thread synchronization. This is omitted here for - // simplicity. - - // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs - // to be created though it will only set the timeInfo.samplePosition and - // timeInfo.systemTime fields and the according flags - - ASIOTime timeInfo; - memset( &timeInfo, 0, sizeof (timeInfo) ); - - // get the time stamp of the buffer, not necessary if no - // synchronization to other media is required - if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK) - timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid; - - // Call the real callback - bufferSwitchTimeInfo( &timeInfo, index, directProcess ); -} - - -// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float -#if NATIVE_INT64 - #define ASIO64toDouble(a) (a) -#else - const double twoRaisedTo32 = 4294967296.; - #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32) -#endif - -static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess ) -{ - // the actual processing callback. - // Beware that this is normally in a seperate thread, hence be sure that - // you take care about thread synchronization. - - - /* The SDK says the following about the directProcess flag: - suggests to the host whether it should immediately start processing - (directProcess == ASIOTrue), or whether its process should be deferred - because the call comes from a very low level (for instance, a high level - priority interrupt), and direct processing would cause timing instabilities for - the rest of the system. If in doubt, directProcess should be set to ASIOFalse. - - We just ignore directProcess. This could cause incompatibilities with - drivers which really don't want the audio processing to occur in this - callback, but none have been identified yet. - */ - - (void) directProcess; /* suppress unused parameter warning */ - -#if 0 - // store the timeInfo for later use - asioDriverInfo.tInfo = *timeInfo; - - // get the time stamp of the buffer, not necessary if no - // synchronization to other media is required - - if (timeInfo->timeInfo.flags & kSystemTimeValid) - asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime); - else - asioDriverInfo.nanoSeconds = 0; - - if (timeInfo->timeInfo.flags & kSamplePositionValid) - asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); - else - asioDriverInfo.samples = 0; - - if (timeInfo->timeCode.flags & kTcValid) - asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples); - else - asioDriverInfo.tcSamples = 0; - - // get the system reference time - asioDriverInfo.sysRefTime = get_sys_reference_time(); -#endif - -#if 0 - // a few debug messages for the Windows device driver developer - // tells you the time when driver got its interrupt and the delay until the app receives - // the event notification. - static double last_samples = 0; - char tmp[128]; - sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples)); - OutputDebugString (tmp); - last_samples = asioDriverInfo.samples; -#endif - - - if( !theAsioStream ) - return 0L; - - // Keep sample position - // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo; - - - // protect against reentrancy - if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) ) - { - theAsioStream->reenterError++; - //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError)); - return 0L; - } - - int buffersDone = 0; - - do - { - if( buffersDone > 0 ) - { - // this is a reentered buffer, we missed processing it on time - // set the input overflow and output underflow flags as appropriate - - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; - } - else - { - if( theAsioStream->zeroOutput ) - { - ZeroOutputBuffers( theAsioStream, index ); - - // Finally if the driver supports the ASIOOutputReady() optimization, - // do it here, all data are in place - if( theAsioStream->postOutput ) - ASIOOutputReady(); - - if( theAsioStream->stopProcessing ) - { - if( theAsioStream->stopPlayoutCount < 2 ) - { - ++theAsioStream->stopPlayoutCount; - if( theAsioStream->stopPlayoutCount == 2 ) - { - theAsioStream->isActive = 0; - if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 ) - theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData ); - theAsioStream->streamFinishedCallbackCalled = true; - SetEvent( theAsioStream->completedBuffersPlayedEvent ); - } - } - } - } - else - { - -#if 0 -// test code to try to detect slip conditions... these may work on some systems -// but neither of them work on the RME Digi96 - -// check that sample delta matches buffer size (otherwise we must have skipped -// a buffer. -static double last_samples = -512; -double samples; -//if( timeInfo->timeCode.flags & kTcValid ) -// samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples); -//else - samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); -int delta = samples - last_samples; -//printf( "%d\n", delta); -last_samples = samples; - -if( delta > theAsioStream->framesPerHostCallback ) -{ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; -} - -// check that the buffer index is not the previous index (which would indicate -// that a buffer was skipped. -static int previousIndex = 1; -if( index == previousIndex ) -{ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; -} -previousIndex = index; -#endif - - int i; - - PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer ); - - PaStreamCallbackTimeInfo paTimeInfo; - - // asio systemTime is supposed to be measured according to the same - // clock as timeGetTime - paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001); - - /* patch from Paul Boege */ - paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - - ((double)theAsioStream->inputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate); - - paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + - ((double)theAsioStream->outputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate); - - /* old version is buggy because the buffer processor also adds in its latency to the time parameters - paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency; - paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency; - */ -#if 1 -// detect underflows by checking inter-callback time > 2 buffer period -static double previousTime = -1; -if( previousTime > 0 ){ - - double delta = paTimeInfo.currentTime - previousTime; - - if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; - } -} -previousTime = paTimeInfo.currentTime; -#endif - - // note that the above input and output times do not need to be - // adjusted for the latency of the buffer processor -- the buffer - // processor handles that. - - if( theAsioStream->inputBufferConverter ) - { - for( i=0; i<theAsioStream->inputChannelCount; i++ ) - { - theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i], - theAsioStream->inputShift, theAsioStream->framesPerHostCallback ); - } - } - - PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags ); - - /* reset status flags once they've been passed to the callback */ - theAsioStream->callbackFlags = 0; - - PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); - for( i=0; i<theAsioStream->inputChannelCount; ++i ) - PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] ); - - PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); - for( i=0; i<theAsioStream->outputChannelCount; ++i ) - PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] ); - - int callbackResult; - if( theAsioStream->stopProcessing ) - callbackResult = paComplete; - else - callbackResult = paContinue; - unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult ); - - if( theAsioStream->outputBufferConverter ) - { - for( i=0; i<theAsioStream->outputChannelCount; i++ ) - { - theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i], - theAsioStream->outputShift, theAsioStream->framesPerHostCallback ); - } - } - - PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed ); - - // Finally if the driver supports the ASIOOutputReady() optimization, - // do it here, all data are in place - if( theAsioStream->postOutput ) - ASIOOutputReady(); - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - /* finish playback immediately */ - theAsioStream->isActive = 0; - if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 ) - theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData ); - theAsioStream->streamFinishedCallbackCalled = true; - SetEvent( theAsioStream->completedBuffersPlayedEvent ); - theAsioStream->zeroOutput = true; - } - else /* paComplete or other non-zero value indicating complete */ - { - /* Finish playback once currently queued audio has completed. */ - theAsioStream->stopProcessing = true; - - if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) ) - { - theAsioStream->zeroOutput = true; - theAsioStream->stopPlayoutCount = 0; - } - } - } - } - - ++buffersDone; - }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 ); - - return 0L; -} - - -static void sampleRateChanged(ASIOSampleRate sRate) -{ - // TAKEN FROM THE ASIO SDK - // do whatever you need to do if the sample rate changed - // usually this only happens during external sync. - // Audio processing is not stopped by the driver, actual sample rate - // might not have even changed, maybe only the sample rate status of an - // AES/EBU or S/PDIF digital input at the audio device. - // You might have to update time/sample related conversion routines, etc. - - (void) sRate; /* unused parameter */ - PA_DEBUG( ("sampleRateChanged : %d \n", sRate)); -} - -static long asioMessages(long selector, long value, void* message, double* opt) -{ -// TAKEN FROM THE ASIO SDK - // currently the parameters "value", "message" and "opt" are not used. - long ret = 0; - - (void) message; /* unused parameters */ - (void) opt; - - PA_DEBUG( ("asioMessages : %d , %d \n", selector, value)); - - switch(selector) - { - case kAsioSelectorSupported: - if(value == kAsioResetRequest - || value == kAsioEngineVersion - || value == kAsioResyncRequest - || value == kAsioLatenciesChanged - // the following three were added for ASIO 2.0, you don't necessarily have to support them - || value == kAsioSupportsTimeInfo - || value == kAsioSupportsTimeCode - || value == kAsioSupportsInputMonitor) - ret = 1L; - break; - - case kAsioBufferSizeChange: - //printf("kAsioBufferSizeChange \n"); - break; - - case kAsioResetRequest: - // defer the task and perform the reset of the driver during the next "safe" situation - // You cannot reset the driver right now, as this code is called from the driver. - // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction - // Afterwards you initialize the driver again. - - /*FIXME: commented the next line out */ - //asioDriverInfo.stopped; // In this sample the processing will just stop - ret = 1L; - break; - - case kAsioResyncRequest: - // This informs the application, that the driver encountered some non fatal data loss. - // It is used for synchronization purposes of different media. - // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the - // Windows Multimedia system, which could loose data because the Mutex was hold too long - // by another thread. - // However a driver can issue it in other situations, too. - ret = 1L; - break; - - case kAsioLatenciesChanged: - // This will inform the host application that the drivers were latencies changed. - // Beware, it this does not mean that the buffer sizes have changed! - // You might need to update internal delay data. - ret = 1L; - //printf("kAsioLatenciesChanged \n"); - break; - - case kAsioEngineVersion: - // return the supported ASIO version of the host application - // If a host applications does not implement this selector, ASIO 1.0 is assumed - // by the driver - ret = 2L; - break; - - case kAsioSupportsTimeInfo: - // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback - // is supported. - // For compatibility with ASIO 1.0 drivers the host application should always support - // the "old" bufferSwitch method, too. - ret = 1; - break; - - case kAsioSupportsTimeCode: - // informs the driver wether application is interested in time code info. - // If an application does not need to know about time code, the driver has less work - // to do. - ret = 0; - break; - } - return ret; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - ASIOError asioError; - - if( stream->outputChannelCount > 0 ) - { - ZeroOutputBuffers( stream, 0 ); - ZeroOutputBuffers( stream, 1 ); - } - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - stream->stopProcessing = false; - stream->zeroOutput = false; - - /* Reentrancy counter initialisation */ - stream->reenterCount = -1; - stream->reenterError = 0; - - stream->callbackFlags = 0; - - if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - if( result == paNoError ) - { - theAsioStream = stream; - asioError = ASIOStart(); - if( asioError == ASE_OK ) - { - stream->isActive = 1; - stream->streamFinishedCallbackCalled = false; - } - else - { - theAsioStream = 0; - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - } - - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - ASIOError asioError; - - if( stream->isActive ) - { - stream->stopProcessing = true; - - /* wait for the stream to finish playing out enqueued buffers. - timeout after four times the stream latency. - - @todo should use a better time out value - if the user buffer - length is longer than the asio buffer size then that should - be taken into account. - */ - if( WaitForSingleObject( theAsioStream->completedBuffersPlayedEvent, - (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) ) - == WAIT_TIMEOUT ) - { - PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" )); - } - } - - asioError = ASIOStop(); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - - theAsioStream = 0; - stream->isActive = 0; - - if( !stream->streamFinishedCallbackCalled ) - { - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - ASIOError asioError; - - stream->zeroOutput = true; - - asioError = ASIOStop(); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - else - { - // make sure that the callback is not still in-flight when ASIOStop() - // returns. This has been observed to happen on the Hoontech DSP24 for - // example. - int count = 2000; // only wait for 2 seconds, rather than hanging. - while( theAsioStream->reenterCount != -1 && count > 0 ) - { - Sleep(1); - --count; - } - } - - /* it is questionable whether we should zero theAsioStream if ASIOStop() - returns an error, because the callback could still be active. We assume - not - this is based on the fact that ASIOStop is unlikely to fail - if the callback is running - it's more likely to fail because the - callback is not running. */ - - theAsioStream = 0; - stream->isActive = 0; - - if( !stream->streamFinishedCallbackCalled ) - { - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - //PaAsioStream *stream = (PaAsioStream*)s; - (void) s; /* unused parameter */ - return theAsioStream == 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - (void) s; /* unused parameter */ - return (double)timeGetTime() * .001; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameters */ - (void) buffer; - (void) frames; - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameters */ - (void) buffer; - (void) frames; - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameter */ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameter */ - - return 0; -} - - -PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - ASIODriverInfo asioDriverInfo; - ASIOError asioError; - int asioIsInitialized = 0; - PaAsioHostApiRepresentation *asioHostApi; - PaAsioDeviceInfo *asioDeviceInfo; - - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - /* - In theory we could proceed if the currently open device was the same - one for which the control panel was requested, however because the - window pointer is not available until this function is called we - currently need to call ASIOInit() again here, which of course can't be - done safely while a stream is open. - */ - - asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - if( asioHostApi->openAsioDeviceIndex != paNoDevice ) - { - result = paDeviceUnavailable; - goto error; - } - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( !loadAsioDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) ) - { - result = paUnanticipatedHostError; - goto error; - } - - /* CRUCIAL!!! */ - memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) ); - asioDriverInfo.asioVersion = 2; - asioDriverInfo.sysRef = systemSpecific; - asioError = ASIOInit( &asioDriverInfo ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - else - { - asioIsInitialized = 1; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); -PA_DEBUG(("asioVersion: ASIOInit(): %ld\n", asioDriverInfo.asioVersion )); -PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion )); -PA_DEBUG(("Name: ASIOInit(): %s\n", asioDriverInfo.name )); -PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n", asioDriverInfo.errorMessage )); - - asioError = ASIOControlPanel(); - if( asioError != ASE_OK ) - { - PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - - asioError = ASIOExit(); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - asioIsInitialized = 0; - goto error; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - - return result; - -error: - if( asioIsInitialized ) - ASIOExit(); - - return result; -} - - -PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - PaAsioDeviceInfo *asioDeviceInfo; - - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxInputChannels ){ - result = paInvalidChannelCount; - goto error; - } - - *channelName = asioDeviceInfo->asioChannelInfos[channelIndex].name; - - return paNoError; - -error: - return result; -} - - -PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - PaAsioDeviceInfo *asioDeviceInfo; - - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxOutputChannels ){ - result = paInvalidChannelCount; - goto error; - } - - *channelName = asioDeviceInfo->asioChannelInfos[ - asioDeviceInfo->commonDeviceInfo.maxInputChannels + channelIndex].name; - - return paNoError; - -error: - return result; -} diff --git a/portaudio/pa_asio/pa_asio.h b/portaudio/pa_asio/pa_asio.h deleted file mode 100644 index 230fb2d85..000000000 --- a/portaudio/pa_asio/pa_asio.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef PA_ASIO_H -#define PA_ASIO_H -/* - * $Id: pa_asio.h,v 1.1.2.7 2005/01/01 19:35:33 rossbencina Exp $ - * PortAudio Portable Real-Time Audio Library - * ASIO specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/** @file - @brief ASIO-specific PortAudio API extension header file. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** Retrieve legal latency settings for the specificed device, in samples. - - @param device The global index of the device about which the query is being made. - @param minLatency A pointer to the location which will recieve the minimum latency value. - @param maxLatency A pointer to the location which will recieve the maximum latency value. - @param preferredLatency A pointer to the location which will recieve the preferred latency value. - @param granularity A pointer to the location which will recieve the granularity. This value - determines which values between minLatency and maxLatency are available. ie the step size, - if granularity is -1 then available latency settings are powers of two. - - @see ASIOGetBufferSize in the ASIO SDK. - - @todo This function should have a better name, any suggestions? -*/ -PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, - long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ); - - -/** Display the ASIO control panel for the specified device. - - @param device The global index of the device whose control panel is to be displayed. - @param systemSpecific On Windows, the calling application's main window handle, - on Macintosh this value should be zero. -*/ -PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ); - - - - -/** Retrieve a pointer to a string containing the name of the specified - input channel. The string is valid until Pa_Terminate is called. - - The string will be no longer than 32 characters including the null terminator. -*/ -PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ); - - -/** Retrieve a pointer to a string containing the name of the specified - input channel. The string is valid until Pa_Terminate is called. - - The string will be no longer than 32 characters including the null terminator. -*/ -PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ); - - -#define paAsioUseChannelSelectors (0x01) - -typedef struct PaAsioStreamInfo{ - unsigned long size; /**< sizeof(PaAsioStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paASIO */ - unsigned long version; /**< 1 */ - - unsigned long flags; - - /* Support for opening only specific channels of an ASIO device. - If the paAsioUseChannelSelectors flag is set, channelSelectors is a - pointer to an array of integers specifying the device channels to use. - When used, the length of the channelSelectors array must match the - corresponding channelCount parameter to Pa_OpenStream() otherwise a - crash may result. - The values in the selectors array must specify channels within the - range of supported channels for the device or paInvalidChannelCount will - result. - */ - int *channelSelectors; -}PaAsioStreamInfo; - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_ASIO_H */ diff --git a/portaudio/pa_common/CVS/Entries b/portaudio/pa_common/CVS/Entries deleted file mode 100644 index 98e8ca0c5..000000000 --- a/portaudio/pa_common/CVS/Entries +++ /dev/null @@ -1,22 +0,0 @@ -/pa_allocation.c/1.1.2.6/Mon Dec 20 12:07:51 2004//Tv19-devel -/pa_allocation.h/1.1.2.4/Sat Sep 20 21:04:44 2003//Tv19-devel -/pa_converters.c/1.1.2.27/Wed Nov 2 12:14:07 2005//Tv19-devel -/pa_converters.h/1.1.2.9/Sat Sep 20 21:05:14 2003//Tv19-devel -/pa_cpuload.c/1.1.2.14/Thu Jan 8 22:01:12 2004//Tv19-devel -/pa_cpuload.h/1.1.2.10/Thu Jan 8 22:01:12 2004//Tv19-devel -/pa_dither.c/1.1.2.6/Sat May 28 22:49:02 2005//Tv19-devel -/pa_dither.h/1.1.2.4/Sat Sep 20 21:06:19 2003//Tv19-devel -/pa_endianness.h/1.1.2.5/Thu Feb 16 16:26:07 2006//Tv19-devel -/pa_front.c/1.1.2.53/Mon Mar 20 18:11:09 2006//Tv19-devel -/pa_hostapi.h/1.1.2.14/Thu Jan 8 22:01:12 2004//Tv19-devel -/pa_process.c/1.1.2.51/Thu Oct 27 23:28:48 2005//Tv19-devel -/pa_process.h/1.1.2.30/Mon Dec 13 09:48:44 2004//Tv19-devel -/pa_skeleton.c/1.1.2.39/Wed Nov 26 14:56:09 2003//Tv19-devel -/pa_stream.c/1.1.2.12/Sat Sep 20 21:31:00 2003//Tv19-devel -/pa_stream.h/1.1.2.13/Sat Nov 1 06:37:28 2003//Tv19-devel -/pa_trace.c/1.1.1.1.2.4/Wed Nov 2 12:06:44 2005//Tv19-devel -/pa_trace.h/1.1.1.1.2.3/Sat Sep 20 21:09:15 2003//Tv19-devel -/pa_types.h/1.1.2.2/Sat Feb 5 15:52:05 2005//Tv19-devel -/pa_util.h/1.1.2.13/Wed Nov 9 06:31:42 2005//Tv19-devel -/portaudio.h/1.5.2.53/Mon Mar 20 17:49:38 2006//Tv19-devel -D diff --git a/portaudio/pa_common/CVS/Repository b/portaudio/pa_common/CVS/Repository deleted file mode 100644 index 6c0ffe7aa..000000000 --- a/portaudio/pa_common/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_common diff --git a/portaudio/pa_common/CVS/Root b/portaudio/pa_common/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_common/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_common/CVS/Tag b/portaudio/pa_common/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_common/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_common/pa_allocation.c b/portaudio/pa_common/pa_allocation.c deleted file mode 100644 index 035b4d0b5..000000000 --- a/portaudio/pa_common/pa_allocation.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $ - * Portable Audio I/O Library allocation group implementation - * memory allocation group for tracking allocation groups - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Allocation Group implementation. -*/ - - -#include "pa_allocation.h" -#include "pa_util.h" - - -/* - Maintain 3 singly linked lists... - linkBlocks: the buffers used to allocate the links - spareLinks: links available for use in the allocations list - allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory() - - Link block size is doubled every time new links are allocated. -*/ - - -#define PA_INITIAL_LINK_COUNT_ 16 - -struct PaUtilAllocationGroupLink -{ - struct PaUtilAllocationGroupLink *next; - void *buffer; -}; - -/* - Allocate a block of links. The first link will have it's buffer member - pointing to the block, and it's next member set to <nextBlock>. The remaining - links will have NULL buffer members, and each link will point to - the next link except the last, which will point to <nextSpare> -*/ -static struct PaUtilAllocationGroupLink *AllocateLinks( long count, - struct PaUtilAllocationGroupLink *nextBlock, - struct PaUtilAllocationGroupLink *nextSpare ) -{ - struct PaUtilAllocationGroupLink *result; - int i; - - result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory( - sizeof(struct PaUtilAllocationGroupLink) * count ); - if( result ) - { - /* the block link */ - result[0].buffer = result; - result[0].next = nextBlock; - - /* the spare links */ - for( i=1; i<count; ++i ) - { - result[i].buffer = 0; - result[i].next = &result[i+1]; - } - result[count-1].next = nextSpare; - } - - return result; -} - - -PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void ) -{ - PaUtilAllocationGroup* result = 0; - struct PaUtilAllocationGroupLink *links; - - - links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 ); - if( links != 0 ) - { - result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) ); - if( result ) - { - result->linkCount = PA_INITIAL_LINK_COUNT_; - result->linkBlocks = &links[0]; - result->spareLinks = &links[1]; - result->allocations = 0; - } - else - { - PaUtil_FreeMemory( links ); - } - } - - return result; -} - - -void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ) -{ - struct PaUtilAllocationGroupLink *current = group->linkBlocks; - struct PaUtilAllocationGroupLink *next; - - while( current ) - { - next = current->next; - PaUtil_FreeMemory( current->buffer ); - current = next; - } - - PaUtil_FreeMemory( group ); -} - - -void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ) -{ - struct PaUtilAllocationGroupLink *links, *link; - void *result = 0; - - /* allocate more links if necessary */ - if( !group->spareLinks ) - { - /* double the link count on each block allocation */ - links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks ); - if( links ) - { - group->linkCount += group->linkCount; - group->linkBlocks = &links[0]; - group->spareLinks = &links[1]; - } - } - - if( group->spareLinks ) - { - result = PaUtil_AllocateMemory( size ); - if( result ) - { - link = group->spareLinks; - group->spareLinks = link->next; - - link->buffer = result; - link->next = group->allocations; - - group->allocations = link; - } - } - - return result; -} - - -void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ) -{ - struct PaUtilAllocationGroupLink *current = group->allocations; - struct PaUtilAllocationGroupLink *previous = 0; - - if( buffer == 0 ) - return; - - /* find the right link and remove it */ - while( current ) - { - if( current->buffer == buffer ) - { - if( previous ) - { - previous->next = current->next; - } - else - { - group->allocations = current->next; - } - - current->buffer = 0; - current->next = group->spareLinks; - group->spareLinks = current; - - break; - } - - previous = current; - current = current->next; - } - - PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */ -} - - -void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ) -{ - struct PaUtilAllocationGroupLink *current = group->allocations; - struct PaUtilAllocationGroupLink *previous = 0; - - /* free all buffers in the allocations list */ - while( current ) - { - PaUtil_FreeMemory( current->buffer ); - current->buffer = 0; - - previous = current; - current = current->next; - } - - /* link the former allocations list onto the front of the spareLinks list */ - if( previous ) - { - previous->next = group->spareLinks; - group->spareLinks = group->allocations; - group->allocations = 0; - } -} - diff --git a/portaudio/pa_common/pa_allocation.h b/portaudio/pa_common/pa_allocation.h deleted file mode 100644 index fb9321a01..000000000 --- a/portaudio/pa_common/pa_allocation.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef PA_ALLOCATION_H -#define PA_ALLOCATION_H -/* - * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $ - * Portable Audio I/O Library allocation context header - * memory allocation context for tracking allocation groups - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Allocation Group prototypes. An Allocation Group makes it easy to - allocate multiple blocks of memory and free them all simultanously. - - An allocation group is useful for keeping track of multiple blocks - of memory which are allocated at the same time (such as during initialization) - and need to be deallocated at the same time. The allocation group maintains - a list of allocated blocks, and can deallocate them all simultaneously which - can be usefull for cleaning up after a partially initialized object fails. - - The allocation group implementation is built on top of the lower - level allocation functions defined in pa_util.h -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -typedef struct -{ - long linkCount; - struct PaUtilAllocationGroupLink *linkBlocks; - struct PaUtilAllocationGroupLink *spareLinks; - struct PaUtilAllocationGroupLink *allocations; -}PaUtilAllocationGroup; - - - -/** Create an allocation group. -*/ -PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void ); - -/** Destroy an allocation group, but not the memory allocated through the group. -*/ -void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ); - -/** Allocate a block of memory though an allocation group. -*/ -void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ); - -/** Free a block of memory that was previously allocated though an allocation - group. Calling this function is a relatively time consuming operation. - Under normal circumstances clients should call PaUtil_FreeAllAllocations to - free all allocated blocks simultaneously. - @see PaUtil_FreeAllAllocations -*/ -void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ); - -/** Free all blocks of memory which have been allocated through the allocation - group. This function doesn't destroy the group itself. -*/ -void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_ALLOCATION_H */ diff --git a/portaudio/pa_common/pa_converters.c b/portaudio/pa_common/pa_converters.c deleted file mode 100644 index a7e3a06cb..000000000 --- a/portaudio/pa_common/pa_converters.c +++ /dev/null @@ -1,1926 +0,0 @@ -/* - * $Id: pa_converters.c,v 1.1.2.27 2005/11/02 12:14:07 rossbencina Exp $ - * Portable Audio I/O Library sample conversion mechanism - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Conversion functions implementations. - - If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it - - @todo Consider whether functions which dither but don't clip should exist, - V18 automatically enabled clipping whenever dithering was selected. Perhaps - we should do the same. - - @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither, - Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither, - Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, - Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither, - - @todo review the converters marked REVIEW: Float32_To_Int32, - Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip, - Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32 -*/ - - -#include "pa_converters.h" -#include "pa_dither.h" -#include "pa_endianness.h" -#include "pa_types.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; - PaInt32 *dest = (PaInt32*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float scaled = *src * 0x7FFFFFFF; - *dest = lrintf(scaled-0.5f); -#else - double scaled = *src * 0x7FFFFFFF; - *dest = (PaInt32) scaled; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - PaInt32 *dest = (PaInt32*)destinationBuffer; - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = ((float)*src * (2147483646.0f)) + dither; - *dest = lrintf(dithered - 0.5f); -#else - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - *dest = (PaInt32) dithered; -#endif - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - PaInt32 *dest = (PaInt32*)destinationBuffer; - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648.f, 2147483647.f ); - *dest = lrintf(scaled-0.5f); -#else - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - *dest = (PaInt32) scaled; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - PaInt32 *dest = (PaInt32*)destinationBuffer; - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = ((float)*src * (2147483646.0f)) + dither; - PA_CLIP_( dithered, -2147483648.f, 2147483647.f ); - *dest = lrintf(dithered-0.5f); -#else - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - *dest = (PaInt32) dithered; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - PaInt32 temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* convert to 32 bit and drop the low 8 bits */ - double scaled = *src * 0x7FFFFFFF; - temp = (PaInt32) 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; - PaInt32 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 = (PaInt32) 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; - PaInt32 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 = (PaInt32) 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; - PaInt32 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 = (PaInt32) 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; - PaInt16 *dest = (PaInt16*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { -#ifdef PA_USE_C99_LRINTF - float tempf = (*src * (32767.0f)) ; - *dest = lrintf(tempf-0.5f); -#else - short samp = (short) (*src * (32767.0f)); - *dest = samp; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - - while( count-- ) - { - - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (32766.0f)) + dither; - -#ifdef PA_USE_C99_LRINTF - *dest = lrintf(dithered-0.5f); -#else - *dest = (PaInt16) dithered; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { -#ifdef PA_USE_C99_LRINTF - long samp = lrintf((*src * (32767.0f)) -0.5f); -#else - long samp = (PaInt32) (*src * (32767.0f)); -#endif - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (PaInt16) 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; - PaInt16 *dest = (PaInt16*)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; - PaInt32 samp = (PaInt32) dithered; - PA_CLIP_( samp, -0x8000, 0x7FFF ); -#ifdef PA_USE_C99_LRINTF - *dest = lrintf(samp-0.5f); -#else - *dest = (PaInt16) samp; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - signed char samp = (signed char) (*src * (127.0f)); - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (126.0f)) + dither; - PaInt32 samp = (PaInt32) 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-- ) - { - PaInt32 samp = (PaInt32)(*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; - PaInt32 samp = (PaInt32) 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 ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (float) ((double)*src * const_1_div_2147483648_); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(*src >> 8); - dest[1] = (unsigned char)(*src >> 16); - dest[2] = (unsigned char)(*src >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(*src >> 24); - dest[1] = (unsigned char)(*src >> 16); - dest[2] = (unsigned char)(*src >> 8); -#endif - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int24_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (PaInt16) ((*src) >> 16); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - PaInt32 dither; - - while( count-- ) - { - /* REVIEW */ - dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); - *dest = (PaInt16) ((((*src)>>1) + dither) >> 15); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (signed char) ((*src) >> 24); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - PaInt32 dither; - - while( count-- ) - { - /* REVIEW */ - dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); - *dest = (signed char) ((((*src)>>1) + dither) >> 23); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(((*src) >> 24) + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt32 *src = (PaInt32*)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; - PaInt32 temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - temp = (((long)src[0]) << 8); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 24); -#elif defined(PA_BIG_ENDIAN) - temp = (((long)src[0]) << 24); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 8); -#endif - - *dest = (float) ((double)temp * const_1_div_2147483648_); - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - PaInt32 *dest = (PaInt32*) destinationBuffer; - PaInt32 temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - temp = (((long)src[0]) << 8); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 24); -#elif defined(PA_BIG_ENDIAN) - temp = (((long)src[0]) << 24); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 8); -#endif - - *dest = temp; - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - - PaInt16 temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - temp = (((PaInt16)src[1])); - temp = temp | (PaInt16)(((PaInt16)src[2]) << 8); -#elif defined(PA_BIG_ENDIAN) - /* src[2] is discarded */ - temp = (PaInt16)(((PaInt16)src[0]) << 8); - temp = temp | (((PaInt16)src[1])); -#endif - - *dest = temp; - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - /* src[1] is discarded */ - *dest = src[2]; -#elif defined(PA_BIG_ENDIAN) - /* src[2] is discarded */ - /* src[1] is discarded */ - *dest = src[0]; -#endif - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - /* src[1] is discarded */ - *dest = (unsigned char)(src[2] + 128); -#elif defined(PA_BIG_ENDIAN) - *dest = (unsigned char)(src[0] + 128); - /* src[1] is discarded */ - /* src[2] is discarded */ -#endif - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt16 *src = (PaInt16*)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 ) -{ - PaInt16 *src = (PaInt16*)sourceBuffer; - PaInt32 *dest = (PaInt32*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW: we should consider something like - (*src << 16) | (*src & 0xFFFF) - */ - - *dest = *src << 16; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt16 *src = (PaInt16*) sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - PaInt16 temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - temp = *src; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = (unsigned char)(temp); - dest[2] = (unsigned char)(temp >> 8); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp); - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt16 *src = (PaInt16*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed char)((*src) >> 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt16 *src = (PaInt16*)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 ) -{ - PaInt16 *src = (PaInt16*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(((*src) >> 8) + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaInt16 *src = (PaInt16*)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; - PaInt32 *dest = (PaInt32*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (*src) << 24; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = 0; - dest[2] = (*src); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (*src); - dest[1] = 0; - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (PaInt16)((*src) << 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(*src + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float samp = (*src - 128) * const_1_div_128_; - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - PaInt32 *dest = (PaInt32*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (*src - 128) << 24; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void) ditherGenerator; /* unused parameters */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = 0; - dest[2] = (unsigned char)(*src - 128); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(*src - 128); - dest[1] = 0; - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - PaInt16 *dest = (PaInt16*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (PaInt16)((*src - 128) << 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed char)(*src - 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_8_To_8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = *src; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_16_To_16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaUint16 *src = (PaUint16 *)sourceBuffer; - PaUint16 *dest = (PaUint16 *)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 ) -{ - PaUint32 *dest = (PaUint32 *)destinationBuffer; - PaUint32 *src = (PaUint32 *)sourceBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = *src; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -PaUtilConverterTable paConverters = { - Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */ - Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */ - Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */ - Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ - - Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */ - Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */ - Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */ - Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ - - Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */ - Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */ - Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */ - Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ - - Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */ - Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */ - Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */ - Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ - - Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */ - Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */ - Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */ - Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ - - Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */ - Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */ - Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */ - Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */ - Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */ - Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */ - Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */ - Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */ - Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */ - - Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */ - Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */ - Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */ - Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */ - Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */ - Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */ - Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */ - Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */ - - Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */ - Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */ - Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */ - Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */ - Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */ - Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */ - Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */ - - Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */ - Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */ - Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */ - Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */ - Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */ - - UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */ - UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */ - UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */ - UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */ - UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */ - - Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */ - Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */ - Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */ - Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#endif /* PA_NO_STANDARD_CONVERTERS */ - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ) -{ - switch( destinationFormat & ~paNonInterleaved ){ - case paFloat32: - return paZeroers.Zero32; - case paInt32: - return paZeroers.Zero32; - case paInt24: - return paZeroers.Zero24; - case paInt16: - return paZeroers.Zero16; - case paInt8: - return paZeroers.Zero8; - case paUInt8: - return paZeroers.ZeroU8; - default: return 0; - } -} - -/* -------------------------------------------------------------------------- */ - -#ifdef PA_NO_STANDARD_ZEROERS - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroerTable paZeroers = { - 0, /* PaUtilZeroer *ZeroU8; */ - 0, /* PaUtilZeroer *Zero8; */ - 0, /* PaUtilZeroer *Zero16; */ - 0, /* PaUtilZeroer *Zero24; */ - 0, /* PaUtilZeroer *Zero32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#else /* PA_NO_STANDARD_ZEROERS is not defined */ - -/* -------------------------------------------------------------------------- */ - -static void ZeroU8( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - *dest = 128; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero8( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero16( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - PaUint16 *dest = (PaUint16 *)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero24( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - dest[0] = 0; - dest[1] = 0; - dest[2] = 0; - - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero32( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - PaUint32 *dest = (PaUint32 *)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroerTable paZeroers = { - ZeroU8, /* PaUtilZeroer *ZeroU8; */ - Zero8, /* PaUtilZeroer *Zero8; */ - Zero16, /* PaUtilZeroer *Zero16; */ - Zero24, /* PaUtilZeroer *Zero24; */ - Zero32, /* PaUtilZeroer *Zero32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#endif /* PA_NO_STANDARD_ZEROERS */ diff --git a/portaudio/pa_common/pa_converters.h b/portaudio/pa_common/pa_converters.h deleted file mode 100644 index 831c9c641..000000000 --- a/portaudio/pa_common/pa_converters.h +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef PA_CONVERTERS_H -#define PA_CONVERTERS_H -/* - * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $ - * Portable Audio I/O Library sample conversion mechanism - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Conversion functions used to convert buffers of samples from one - format to another. -*/ - - -#include "portaudio.h" /* for PaSampleFormat */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -struct PaUtilTriangularDitherGenerator; - - -/** Choose an available sample format which is most appropriate for - representing the requested format. If the requested format is not available - higher quality formats are considered before lower quality formates. - @param availableFormats A variable containing the logical OR of all available - formats. - @param format The desired format. - @return The most appropriate available format for representing the requested - format. -*/ -PaSampleFormat PaUtil_SelectClosestAvailableFormat( - PaSampleFormat availableFormats, PaSampleFormat format ); - - -/* high level conversions functions for use by implementations */ - - -/** The generic sample converter prototype. Sample converters convert count - samples from sourceBuffer to destinationBuffer. The actual type of the data - pointed to by these parameters varys for different converter functions. - @param destinationBuffer A pointer to the first sample of the destination. - @param destinationStride An offset between successive destination samples - expressed in samples (not bytes.) It may be negative. - @param sourceBuffer A pointer to the first sample of the source. - @param sourceStride An offset between successive source samples - expressed in samples (not bytes.) It may be negative. - @param count The number of samples to convert. - @param ditherState State information used to calculate dither. Converters - that do not perform dithering will ignore this parameter, in which case - NULL or invalid dither state may be passed. -*/ -typedef void PaUtilConverter( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ); - - -/** Find a sample converter function for the given source and destinations - formats and flags (clip and dither.) - @return - A pointer to a PaUtilConverter which will perform the requested - conversion, or NULL if the given format conversion is not supported. - For conversions where clipping or dithering is not necessary, the - clip and dither flags are ignored and a non-clipping or dithering - version is returned. - If the source and destination formats are the same, a function which - copies data of the appropriate size will be returned. -*/ -PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat, - PaSampleFormat destinationFormat, PaStreamFlags flags ); - - -/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to - destinationBuffer. The actual type of the data pointed to varys for - different zeroer functions. - @param destinationBuffer A pointer to the first sample of the destination. - @param destinationStride An offset between successive destination samples - expressed in samples (not bytes.) It may be negative. - @param count The number of samples to zero. -*/ -typedef void PaUtilZeroer( - void *destinationBuffer, signed int destinationStride, unsigned int count ); - - -/** Find a buffer zeroer function for the given destination format. - @return - A pointer to a PaUtilZeroer which will perform the requested - zeroing. -*/ -PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ); - -/*----------------------------------------------------------------------------*/ -/* low level functions and data structures which may be used for - substituting conversion functions */ - - -/** The type used to store all sample conversion functions. - @see paConverters; -*/ -typedef struct{ - PaUtilConverter *Float32_To_Int32; - PaUtilConverter *Float32_To_Int32_Dither; - PaUtilConverter *Float32_To_Int32_Clip; - PaUtilConverter *Float32_To_Int32_DitherClip; - - PaUtilConverter *Float32_To_Int24; - PaUtilConverter *Float32_To_Int24_Dither; - PaUtilConverter *Float32_To_Int24_Clip; - PaUtilConverter *Float32_To_Int24_DitherClip; - - PaUtilConverter *Float32_To_Int16; - PaUtilConverter *Float32_To_Int16_Dither; - PaUtilConverter *Float32_To_Int16_Clip; - PaUtilConverter *Float32_To_Int16_DitherClip; - - PaUtilConverter *Float32_To_Int8; - PaUtilConverter *Float32_To_Int8_Dither; - PaUtilConverter *Float32_To_Int8_Clip; - PaUtilConverter *Float32_To_Int8_DitherClip; - - PaUtilConverter *Float32_To_UInt8; - PaUtilConverter *Float32_To_UInt8_Dither; - PaUtilConverter *Float32_To_UInt8_Clip; - PaUtilConverter *Float32_To_UInt8_DitherClip; - - PaUtilConverter *Int32_To_Float32; - PaUtilConverter *Int32_To_Int24; - PaUtilConverter *Int32_To_Int24_Dither; - PaUtilConverter *Int32_To_Int16; - PaUtilConverter *Int32_To_Int16_Dither; - PaUtilConverter *Int32_To_Int8; - PaUtilConverter *Int32_To_Int8_Dither; - PaUtilConverter *Int32_To_UInt8; - PaUtilConverter *Int32_To_UInt8_Dither; - - PaUtilConverter *Int24_To_Float32; - PaUtilConverter *Int24_To_Int32; - PaUtilConverter *Int24_To_Int16; - PaUtilConverter *Int24_To_Int16_Dither; - PaUtilConverter *Int24_To_Int8; - PaUtilConverter *Int24_To_Int8_Dither; - PaUtilConverter *Int24_To_UInt8; - PaUtilConverter *Int24_To_UInt8_Dither; - - PaUtilConverter *Int16_To_Float32; - PaUtilConverter *Int16_To_Int32; - PaUtilConverter *Int16_To_Int24; - PaUtilConverter *Int16_To_Int8; - PaUtilConverter *Int16_To_Int8_Dither; - PaUtilConverter *Int16_To_UInt8; - PaUtilConverter *Int16_To_UInt8_Dither; - - PaUtilConverter *Int8_To_Float32; - PaUtilConverter *Int8_To_Int32; - PaUtilConverter *Int8_To_Int24; - PaUtilConverter *Int8_To_Int16; - PaUtilConverter *Int8_To_UInt8; - - PaUtilConverter *UInt8_To_Float32; - PaUtilConverter *UInt8_To_Int32; - PaUtilConverter *UInt8_To_Int24; - PaUtilConverter *UInt8_To_Int16; - PaUtilConverter *UInt8_To_Int8; - - PaUtilConverter *Copy_8_To_8; /* copy without any conversion */ - PaUtilConverter *Copy_16_To_16; /* copy without any conversion */ - PaUtilConverter *Copy_24_To_24; /* copy without any conversion */ - PaUtilConverter *Copy_32_To_32; /* copy without any conversion */ -} PaUtilConverterTable; - - -/** A table of pointers to all required converter functions. - PaUtil_SelectConverter() uses this table to lookup the appropriate - conversion functions. The fields of this structure are initialized - with default conversion functions. Fields may be NULL, indicating that - no conversion function is available. User code may substitue optimised - conversion functions by assigning different function pointers to - these fields. - - @note - If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined, - PortAudio's standard converters will not be compiled, and all fields - of this structure will be initialized to NULL. In such cases, users - should supply their own conversion functions if the require PortAudio - to open a stream that requires sample conversion. - - @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter -*/ -extern PaUtilConverterTable paConverters; - - -/** The type used to store all buffer zeroing functions. - @see paZeroers; -*/ -typedef struct{ - PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */ - PaUtilZeroer *Zero8; - PaUtilZeroer *Zero16; - PaUtilZeroer *Zero24; - PaUtilZeroer *Zero32; -} PaUtilZeroerTable; - - -/** A table of pointers to all required zeroer functions. - PaUtil_SelectZeroer() uses this table to lookup the appropriate - conversion functions. The fields of this structure are initialized - with default conversion functions. User code may substitue optimised - conversion functions by assigning different function pointers to - these fields. - - @note - If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined, - PortAudio's standard zeroers will not be compiled, and all fields - of this structure will be initialized to NULL. In such cases, users - should supply their own zeroing functions for the sample sizes which - they intend to use. - - @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer -*/ -extern PaUtilZeroerTable paZeroers; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_CONVERTERS_H */ diff --git a/portaudio/pa_common/pa_cpuload.c b/portaudio/pa_common/pa_cpuload.c deleted file mode 100644 index e70fbf4ec..000000000 --- a/portaudio/pa_common/pa_cpuload.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 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. - */ - -/** @file - @brief Functions to assist in measuring the CPU utilization of a callback - stream. Used to implement the Pa_GetStreamCpuLoad() function. - - @todo Dynamically calculate the coefficients used to smooth the CPU Load - Measurements over time to provide a uniform characterisation of CPU Load - independent of rate at which PaUtil_BeginCpuLoadMeasurement / - PaUtil_EndCpuLoadMeasurement are called. -*/ - - -#include "pa_cpuload.h" - -#include <assert.h> - -#include "pa_util.h" /* for PaUtil_GetTime() */ - - -void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ) -{ - assert( sampleRate > 0 ); - - measurer->samplingPeriod = 1. / sampleRate; - measurer->averageLoad = 0.; -} - -void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ) -{ - measurer->averageLoad = 0.; -} - -void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ) -{ - measurer->measurementStartTime = PaUtil_GetTime(); -} - - -void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ) -{ - double measurementEndTime, secondsFor100Percent, measuredLoad; - - if( framesProcessed > 0 ){ - measurementEndTime = PaUtil_GetTime(); - - assert( framesProcessed > 0 ); - secondsFor100Percent = framesProcessed * measurer->samplingPeriod; - - measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent; - - /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */ - /** FIXME @todo these coefficients shouldn't be hardwired */ -#define LOWPASS_COEFFICIENT_0 (0.9) -#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) - - measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) + - (LOWPASS_COEFFICIENT_1 * measuredLoad); - } -} - - -double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ) -{ - return measurer->averageLoad; -} diff --git a/portaudio/pa_common/pa_cpuload.h b/portaudio/pa_common/pa_cpuload.h deleted file mode 100644 index f77d91995..000000000 --- a/portaudio/pa_common/pa_cpuload.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef PA_CPULOAD_H -#define PA_CPULOAD_H -/* - * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 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. - */ - -/** @file - @brief Functions to assist in measuring the CPU utilization of a callback - stream. Used to implement the Pa_GetStreamCpuLoad() function. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -typedef struct { - double samplingPeriod; - double measurementStartTime; - double averageLoad; -} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */ - -void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ); -void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ); -void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ); -void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ); -double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_CPULOAD_H */ diff --git a/portaudio/pa_common/pa_dither.c b/portaudio/pa_common/pa_dither.c deleted file mode 100644 index 0600db625..000000000 --- a/portaudio/pa_common/pa_dither.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49: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. - */ - -/** @file - @brief Functions for generating dither noise -*/ - - -#include "pa_dither.h" -#include "pa_types.h" - -#define PA_DITHER_BITS_ (15) - - -void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state ) -{ - state->previous = 0; - state->randSeed1 = 22222; - state->randSeed2 = 5555555; -} - - -signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state ) -{ - signed long current, highPass; - - /* Generate two random numbers. */ - state->randSeed1 = (state->randSeed1 * 196314165) + 907633515; - state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; - - /* Generate triangular distribution about 0. - * Shift before adding to prevent overflow which would skew the distribution. - * Also shift an extra bit for the high pass filter. - */ -#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) - current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + - (((signed long)state->randSeed2)>>DITHER_SHIFT_); - - /* High pass filter to reduce audibility. */ - highPass = current - state->previous; - state->previous = current; - return highPass; -} - - -/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ -#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1)) -static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_; - -float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state ) -{ - signed long current, highPass; - - /* Generate two random numbers. */ - state->randSeed1 = (state->randSeed1 * 196314165) + 907633515; - state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; - - /* Generate triangular distribution about 0. - * Shift before adding to prevent overflow which would skew the distribution. - * Also shift an extra bit for the high pass filter. - */ -#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) - current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + - (((signed long)state->randSeed2)>>DITHER_SHIFT_); - - /* High pass filter to reduce audibility. */ - highPass = current - state->previous; - state->previous = current; - return ((float)highPass) * const_float_dither_scale_; -} - - -/* -The following alternate dither algorithms (from musicdsp.org) could be -considered -*/ - -/*Noise shaped dither (March 2000) -------------------- - -This is a simple implementation of highpass triangular-PDF dither with -2nd-order noise shaping, for use when truncating floating point audio -data to fixed point. - -The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz -sample rate) compared to triangular-PDF dither. The code below assumes -input data is in the range +1 to -1 and doesn't check for overloads! - -To save time when generating dither for multiple channels you can do -things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again. - - - - int r1, r2; //rectangular-PDF random numbers - float s1, s2; //error feedback buffers - float s = 0.5f; //set to 0.0f for no noise shaping - float w = pow(2.0,bits-1); //word length (usually bits=16) - float wi= 1.0f/w; - float d = wi / RAND_MAX; //dither amplitude (2 lsb) - float o = wi * 0.5f; //remove dc offset - float in, tmp; - int out; - - -//for each sample... - - r2=r1; //can make HP-TRI dither by - r1=rand(); //subtracting previous rand() - - in += s * (s1 + s1 - s2); //error feedback - tmp = in + o + d * (float)(r1 - r2); //dc offset and dither - - out = (int)(w * tmp); //truncate downwards - if(tmp<0.0f) out--; //this is faster than floor() - - s2 = s1; - s1 = in - wi * (float)out; //error - - - --- -paul.kellett@maxim.abel.co.uk -http://www.maxim.abel.co.uk -*/ - - -/* -16-to-8-bit first-order dither - -Type : First order error feedforward dithering code -References : Posted by Jon Watte - -Notes : -This is about as simple a dithering algorithm as you can implement, but it's -likely to sound better than just truncating to N bits. - -Note that you might not want to carry forward the full difference for infinity. -It's probably likely that the worst performance hit comes from the saturation -conditionals, which can be avoided with appropriate instructions on many DSPs -and integer SIMD type instructions, or CMOV. - -Last, if sound quality is paramount (such as when going from > 16 bits to 16 -bits) you probably want to use a higher-order dither function found elsewhere -on this site. - - -Code : -// This code will down-convert and dither a 16-bit signed short -// mono signal into an 8-bit unsigned char signal, using a first -// order forward-feeding error term dither. - -#define uchar unsigned char - -void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) -{ - int m = *memory; - while( count-- > 0 ) { - int i = *input++; - i += m; - int j = i + 32768 - 128; - uchar o; - if( j < 0 ) { - o = 0; - } - else if( j > 65535 ) { - o = 255; - } - else { - o = (uchar)((j>>8)&0xff); - } - m = ((j-32768+128)-i); - *output++ = o; - } - *memory = m; -} -*/ diff --git a/portaudio/pa_common/pa_dither.h b/portaudio/pa_common/pa_dither.h deleted file mode 100644 index 70369e187..000000000 --- a/portaudio/pa_common/pa_dither.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef PA_DITHER_H -#define PA_DITHER_H -/* - * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 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. - */ - -/** @file - @brief Functions for generating dither noise -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** @brief State needed to generate a dither signal */ -typedef struct PaUtilTriangularDitherGenerator{ - unsigned long previous; - unsigned long randSeed1; - unsigned long randSeed2; -} PaUtilTriangularDitherGenerator; - - -/** @brief Initialize dither state */ -void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState ); - - -/** - @brief Calculate 2 LSB dither signal with a triangular distribution. - Ranged for adding to a 1 bit right-shifted 32 bit integer - prior to >>15. eg: -<pre> - signed long in = * - signed long dither = PaUtil_Generate16BitTriangularDither( ditherState ); - signed short out = (signed short)(((in>>1) + dither) >> 15); -</pre> - @return - A signed long with a range of +32767 to -32768 -*/ -signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - -/** - @brief Calculate 2 LSB dither signal with a triangular distribution. - Ranged for adding to a pre-scaled float. -<pre> - float in = * - float dither = PaUtil_GenerateFloatTriangularDither( ditherState ); - // use smaller scaler to prevent overflow when we add the dither - signed short out = (signed short)(in*(32766.0f) + dither ); -</pre> - @return - A float with a range of -2.0 to +1.99999. -*/ -float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_DITHER_H */ diff --git a/portaudio/pa_common/pa_endianness.h b/portaudio/pa_common/pa_endianness.h deleted file mode 100644 index cb6f8ad5e..000000000 --- a/portaudio/pa_common/pa_endianness.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef PA_ENDIANNESS_H -#define PA_ENDIANNESS_H -/* - * $Id: pa_endianness.h,v 1.1.2.5 2006/02/16 16:26:07 bjornroche 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. - */ - -/** @file - @brief Configure endianness symbols for the target processor. - - Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols - to be defined. The one that is defined reflects the endianness of the target - platform and may be used to implement conditional compilation of byte-order - dependent code. - - If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt - is made to override that setting. This may be useful if you have a better way - of determining the platform's endianness. The autoconf mechanism uses this for - example. - - A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time - and runtime endiannes and raise an assertion if they don't match. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#if 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 */ - - #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(LITTLE_ENDIAN) || defined(__i386) || defined(_M_IX86) - - #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */ - - #else - - #define PA_BIG_ENDIAN - -#endif - - #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN) - /* - If the following error is raised, you either need to modify the code above - to automatically determine the endianness from other symbols defined on your - platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally. - */ - #error pa_endianness.h was unable to automatically determine the endianness of the target platform - #endif - -#endif - -/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness, - and raises an assertion if they don't match. <assert.h> must be included in - the context in which this macro is used. -*/ -#if defined(PA_LITTLE_ENDIAN) - #define PA_VALIDATE_ENDIANNESS \ - { \ - const long nativeOne = 1; \ - assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \ - } -#elif defined(PA_BIG_ENDIAN) - #define PA_VALIDATE_ENDIANNESS \ - { \ - const long nativeOne = 1; \ - assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \ - } -#endif - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_ENDIANNESS_H */ diff --git a/portaudio/pa_common/pa_front.c b/portaudio/pa_common/pa_front.c deleted file mode 100644 index 696df8b2d..000000000 --- a/portaudio/pa_common/pa_front.c +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * $Id: pa_front.c,v 1.1.2.53 2006/03/20 18:11:09 aknudsen 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, as documented in portaudio.h - -- The <a href="todo.html">TODO List</a> - -Feel free to pick an item off TODO list and fix/implement it. You may want to -enquire about status on the PortAudio mailing list first. -*/ - - -/** @file - @brief Implements public PortAudio API, checks some errors, forwards to - host API implementations. - - Implements the functions defined in the PortAudio API, checks for - some parameter and state inconsistencies and forwards API requests to - specific Host API implementations (via the interface declared in - pa_hostapi.h), and Streams (via the interface declared in pa_stream.h). - - This file handles initialization and termination of Host API - implementations via initializers stored in the paHostApiInitializers - global variable. - - Some utility functions declared in pa_util.h are implemented in this file. - - All PortAudio API functions can be conditionally compiled with logging code. - To compile with logging, define the PA_LOG_API_CALLS precompiler symbol. - - @todo Consider adding host API specific error text in Pa_GetErrorText() for - paUnanticipatedHostError - - @todo Consider adding a new error code for when (inputParameters == NULL) - && (outputParameters == NULL) - - @todo review whether Pa_CloseStream() should call the interface's - CloseStream function if aborting the stream returns an error code. - - @todo Create new error codes if a NULL buffer pointer, or a - zero frame count is passed to Pa_ReadStream or Pa_WriteStream. -*/ - - -#include <stdio.h> -#include <stdarg.h> -#include <memory.h> -#include <string.h> -#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */ - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_endianness.h" -#include "pa_types.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 described below. If you need to - add any log messages, please follow this format. - - Function entry (void function): - - "FunctionName called.\n" - - Function entry (non void function): - - "FunctionName called:\n" - "\tParam1Type param1: param1Value\n" - "\tParam2Type param2: param2Value\n" (etc...) - - - Function exit (no return value): - - "FunctionName returned.\n" - - Function 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 below for 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_ = { (PaHostApiTypeId)-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_ = (PaUtilHostApiRepresentation**)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_] ) - { - PaUtilHostApiRepresentation* hostApi = hostApis_[hostApisCount_]; - assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount ); - assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount ); - - 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 - <device> belongs and returns it. if <hostSpecificDeviceIndex> is - non-null, the host specific device index is returned in it. - returns -1 if <device> 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_TYPE_SIZES; - 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 errorCode ) -{ - const char *result; - - switch( errorCode ) - { - 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; - case paInputOverflowed: result = "Input overflowed"; break; - case paOutputUnderflowed: result = "Output underflowed"; break; - case paHostApiNotFound: result = "Host API not found"; break; - case paInvalidHostApi: result = "Invalid host API"; break; - case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break; - case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break; - case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break; - case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; 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("\tPaHostApiTypeId type: %d\n", type ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = paHostApiNotFound; - - 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" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - 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 = paHostApiNotFound; - - 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_GetHostApiCount( void ) -{ - int result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = hostApisCount_; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); -#endif - - return result; -} - - -PaHostApiIndex Pa_GetDefaultHostApi( void ) -{ - int result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - 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" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - 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 = paNotInitialized; - } - else - { - if( hostApi < 0 || hostApi >= hostApisCount_ ) - { - result = paInvalidHostApi; - } - else - { - if( hostApiDeviceIndex < 0 || - hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount ) - { - result = paInvalidDevice; - } - else - { - result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex; - } - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); -#endif - - return result; -} - - -PaDeviceIndex Pa_GetDeviceCount( void ) -{ - PaDeviceIndex result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = deviceCount_; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - 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{\n" ); - - 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 & outputParameters are both valid, that - inputParameters->device & outputParameters->device both use the same host api - - PaDeviceIndex inputParameters->device - - is within range (0 to Pa_GetDeviceCount-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_GetDeviceCount-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 - - paNeverDropInput is only used for full-duplex callback streams with - variable buffer size (paFramesPerBufferUnspecified) -*/ -static PaError ValidateOpenStreamParameters( - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - PaUtilHostApiRepresentation **hostApi, - PaDeviceIndex *hostApiInputDevice, - PaDeviceIndex *hostApiOutputDevice ) -{ - int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */ - outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */ - /* ters are both nonzero, these indices are set. */ - - 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*)outputParameters->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; - - if( streamFlags & paNeverDropInput ) - { - /* must be a callback stream */ - if( !streamCallback ) - return paInvalidFlag; - - /* must be a full duplex stream */ - if( (inputParameters == NULL) || (outputParameters == NULL) ) - return paInvalidFlag; - - /* must use paFramesPerBufferUnspecified */ - if( framesPerBuffer != paFramesPerBufferUnspecified ) - 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("\tPaStreamParameters *inputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *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("\tPaStreamParameters *outputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *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, 0, paNoFlag, 0, - &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("\tPaStreamParameters *inputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *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("\tPaStreamParameters *outputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *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. - NOTE: make sure this validation list is kept syncronised with the one - in pa_hostapi.h - */ - - 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, framesPerBuffer, - streamFlags, streamCallback, - &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; - /* defaultHighInputLatency is used below instead of - defaultLowInputLatency because it is more important for the default - stream to work reliably than it is for it to work with the lowest - latency. - */ - hostApiInputParameters.suggestedLatency = - 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; - /* defaultHighOutputLatency is used below instead of - defaultLowOutputLatency because it is more important for the default - stream to work reliably than it is for it to work with the lowest - latency. - */ - hostApiOutputParameters.suggestedLatency = - 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; -} - - -PaError PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( 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); - - /* abort the stream if it isn't stopped */ - result = interface->IsStopped( stream ); - if( result == 1 ) - result = paNoError; - else if( result == 0 ) - result = interface->Abort( stream ); - - if( result == paNoError ) /** @todo 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 = PaUtil_ValidateStreamPointer( 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 ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = paStreamIsNotStopped ; - } - if( result == 1 ) - { - PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback; - result = paNoError; - } - } - -#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 = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StartStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = paStreamIsNotStopped ; - } - else if( result == 1 ) - { - 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 = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StopStream called\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Stop( stream ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - -#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 = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_AbortStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Abort( stream ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - -#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 = PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( 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("\tconst 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 = PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_ReadStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - if( frames == 0 ) - { - /* XXX: Should we not allow the implementation to signal any overflow condition? */ - result = paNoError; - } - else if( buffer == 0 ) - { - result = paBadBufferPtr; - } - else - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - } - -#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, - const void *buffer, - unsigned long frames ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_WriteStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - if( frames == 0 ) - { - /* XXX: Should we not allow the implementation to signal any underflow condition? */ - result = paNoError; - } - else if( buffer == 0 ) - { - result = paBadBufferPtr; - } - else - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - } - -#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 = PaUtil_ValidateStreamPointer( 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 = PaUtil_ValidateStreamPointer( 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/portaudio/pa_common/pa_hostapi.h b/portaudio/pa_common/pa_hostapi.h deleted file mode 100644 index d05507067..000000000 --- a/portaudio/pa_common/pa_hostapi.h +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef PA_HOSTAPI_H -#define PA_HOSTAPI_H -/* - * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $ - * Portable Audio I/O Library - * host api representation - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Interface used by pa_front to virtualize functions which operate on - host APIs. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** **FOR THE USE OF pa_front.c ONLY** - Do NOT use fields in this structure, they my change at any time. - Use functions defined in pa_util.h if you think you need functionality - which can be derived from here. -*/ -typedef struct PaUtilPrivatePaFrontHostApiInfo { - - - unsigned long baseDeviceIndex; -}PaUtilPrivatePaFrontHostApiInfo; - - -/** The common header for all data structures whose pointers are passed through - the hostApiSpecificStreamInfo field of the PaStreamParameters structure. - Note that in order to keep the public PortAudio interface clean, this structure - is not used explicitly when declaring hostApiSpecificStreamInfo data structures. - However, some code in pa_front depends on the first 3 members being equivalent - with this structure. - @see PaStreamParameters -*/ -typedef struct PaUtilHostApiSpecificStreamInfoHeader -{ - unsigned long size; /**< size of whole structure including this header */ - PaHostApiTypeId hostApiType; /**< host API for which this data is intended */ - unsigned long version; /**< structure version */ -} PaUtilHostApiSpecificStreamInfoHeader; - - - -/** A structure representing the interface to a host API. Contains both - concrete data and pointers to functions which implement the interface. -*/ -typedef struct PaUtilHostApiRepresentation { - PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo; - - /** The host api implementation should populate the info field. In the - case of info.defaultInputDevice and info.defaultOutputDevice the - values stored should be 0 based indices within the host api's own - device index range (0 to deviceCount). These values will be converted - to global device indices by pa_front after PaUtilHostApiInitializer() - returns. - */ - PaHostApiInfo info; - - PaDeviceInfo** deviceInfos; - - /** - (*Terminate)() is guaranteed to be called with a valid <hostApi> - parameter, which was previously returned from the same implementation's - initializer. - */ - void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi ); - - /** - The inputParameters and outputParameters pointers should not be saved - as they will not remain valid after OpenStream is called. - - - The following guarantees are made about parameters to (*OpenStream)(): - - [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be - kept in sync with the one for ValidateOpenStreamParameters and - Pa_OpenStream in pa_front.c] - - PaHostApiRepresentation *hostApi - - is valid for this implementation - - PaStream** stream - - is non-null - - - at least one of inputParameters & outputParmeters is valid (not NULL) - - - if inputParameters & outputParmeters are both valid, that - inputParameters->device & outputParmeters->device both use the same host api - - PaDeviceIndex inputParameters->device - - is within range (0 to Pa_CountDevices-1) Or: - - is paUseHostApiSpecificDeviceSpecification and - inputParameters->hostApiSpecificStreamInfo is non-NULL and refers - to a valid host api - - int inputParameters->numChannels - - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat inputParameters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *inputParameters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the input device's host Api - - PaDeviceIndex outputParmeters->device - - is within range (0 to Pa_CountDevices-1) - - int outputParmeters->numChannels - - if inputDevice is valid, numInputChannels is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat outputParmeters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *outputParmeters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the output device's host Api - - double sampleRate - - is not an 'absurd' rate (less than 1000. or greater than 200000.) - - sampleRate is NOT validated against device capabilities - - PaStreamFlags streamFlags - - unused platform neutral flags are zero - - paNeverDropInput is only used for full-duplex callback streams - with variable buffer size (paFramesPerBufferUnspecified) - - [*END PA FRONT VALIDATIONS*] - - - The following validations MUST be performed by (*OpenStream)(): - - - check that input device can support numInputChannels - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if inputStreamInfo is supplied, validate its contents, - or return an error if no inputStreamInfo is expected - - - check that output device can support numOutputChannels - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if outputStreamInfo is supplied, validate its contents, - or return an error if no outputStreamInfo is expected - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - - alter sampleRate to a close allowable rate if necessary - - - validate inputLatency and outputLatency - - - validate any platform specific flags, if flags are supplied they - must be valid. - */ - PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi, - PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerCallback, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); - - - PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -} PaUtilHostApiRepresentation; - - -/** Prototype for the initialization function which must be implemented by every - host API. - - @see paHostApiInitializers -*/ -typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex ); - - -/** paHostApiInitializers is a NULL-terminated array of host API initialization - functions. These functions are called by pa_front to initialize the host APIs - when the client calls Pa_Initialize(). - - There is a platform specific file which defines paHostApiInitializers for that - platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example. -*/ -extern PaUtilHostApiInitializer *paHostApiInitializers[]; - - -/** The index of the default host API in the paHostApiInitializers array. - - There is a platform specific file which defines paDefaultHostApiIndex for that - platform, see pa_win/pa_win_hostapis.c for example. -*/ -extern int paDefaultHostApiIndex; - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_HOSTAPI_H */ diff --git a/portaudio/pa_common/pa_process.c b/portaudio/pa_common/pa_process.c deleted file mode 100644 index 4a52165b2..000000000 --- a/portaudio/pa_common/pa_process.c +++ /dev/null @@ -1,1763 +0,0 @@ -/* - * $Id: pa_process.c,v 1.1.2.51 2005/10/27 23:28:48 aknudsen 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. - */ - -/** @file - @brief Buffer Processor implementation. - - The code in this file is not optimised yet - although it's not clear that - it needs to be. 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. - - @todo Consider cache tilings for intereave<->deinterleave. - - @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing() - - @todo specify and implement some kind of logical policy for handling the - underflow and overflow stream flags when the underflow/overflow overlaps - multiple user buffers/callbacks. - - @todo provide support for priming the buffers with data from the callback. - The client interface is now implemented through PaUtil_SetNoInput() - which sets bp->hostInputChannels[0][0].data to zero. However this is - currently only implemented in NonAdaptingProcess(). It shouldn't be - needed for AdaptingInputOnlyProcess() (no priming should ever be - requested for AdaptingInputOnlyProcess()). - Not sure if additional work should be required to make it work with - AdaptingOutputOnlyProcess, but it definitely is required for - AdaptingProcess. - - @todo implement PaUtil_SetNoOutput for AdaptingProcess - - @todo don't allocate temp buffers for blocking streams unless they are - needed. At the moment they are needed, but perhaps for host APIs - where the implementation passes a buffer to the host they could be - used. -*/ - - -#include <assert.h> -#include <string.h> /* memset() */ - -#include "pa_process.h" -#include "pa_util.h" - - -#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024 - -#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) ) - - -/* 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; - - assert( M > 0 ); - assert( N > 0 ); - - 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; - - if( streamFlags & paNeverDropInput ) - { - /* paNeverDropInput is only valid for full-duplex callback streams, with an unspecified number of frames per buffer. */ - if( !streamCallback || !(inputChannelCount > 0 && outputChannelCount > 0) || - framesPerUserBuffer != paFramesPerBufferUnspecified ) - return paInvalidFlag; - } - - /* 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] = bp->hostInputChannels[1] = 0; - bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0; - - if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */ - { - bp->useNonAdaptingProcess = 1; - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 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->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 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->initialFramesInTempInputBuffer = frameShift; - bp->initialFramesInTempOutputBuffer = 0; - } - else - { - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = frameShift; - } - } - else /* variable host buffer size, add framesPerUserBuffer latency */ - { - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = framesPerUserBuffer; - } - } - else - { - /* half duplex */ - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 0; - } - } - } - - - bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; - bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; - - - 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->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat ); - - 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 = - (void **)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->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat ); - - 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 = - (void **)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_ResetBufferProcessor( PaUtilBufferProcessor* bp ) -{ - unsigned long tempInputBufferSize, tempOutputBufferSize; - - bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; - bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; - - if( bp->framesInTempInputBuffer > 0 ) - { - tempInputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount; - memset( bp->tempInputBuffer, 0, tempInputBufferSize ); - } - - if( bp->framesInTempOutputBuffer > 0 ) - { - tempOutputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount; - memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); - } -} - - -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp ) -{ - return bp->initialFramesInTempInputBuffer; -} - - -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp ) -{ - return bp->initialFramesInTempOutputBuffer; -} - - -void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp, - unsigned long frameCount ) -{ - if( frameCount == 0 ) - bp->hostInputFrameCount[0] = bp->framesPerHostBuffer; - else - bp->hostInputFrameCount[0] = frameCount; -} - - -void PaUtil_SetNoInput( PaUtilBufferProcessor* bp ) -{ - assert( bp->inputChannelCount > 0 ); - - bp->hostInputChannels[0][0].data = 0; -} - - -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_SetNoOutput( PaUtilBufferProcessor* bp ) -{ - assert( bp->outputChannelCount > 0 ); - - bp->hostOutputChannels[0][0].data = 0; -} - - -void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data, unsigned int stride ) -{ - assert( channel < bp->outputChannelCount ); - assert( data != NULL ); - - 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 ) - { - PaUtil_SetOutputChannel( bp, channel + i, p, channelCount ); - p += bp->bytesPerHostOutputSample; - } -} - - -void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->outputChannelCount ); - - PaUtil_SetOutputChannel( bp, channel, data, 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 ); - assert( data != NULL ); - - 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 ) - { - PaUtil_Set2ndOutputChannel( bp, channel + i, p, channelCount ); - p += bp->bytesPerHostOutputSample; - } -} - - -void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->outputChannelCount ); - - PaUtil_Set2ndOutputChannel( bp, channel, data, 1 ); -} - - -void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp, - PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ) -{ - 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; - - bp->timeInfo->currentTime = 0; /** 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->callbackStatusFlags = callbackStatusFlags; - - bp->hostInputFrameCount[1] = 0; - bp->hostOutputFrameCount[1] = 0; -} - - -/* - 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 srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - - if( *streamCallbackResult == paContinue ) - { - do - { - frameCount = PA_MIN_( 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 = (unsigned char *)bp->tempInputBuffer; - - if( bp->userInputIsInterleaved ) - { - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - userInput = bp->tempInputBuffer; - } - else /* user input is not interleaved */ - { - destSampleStrideSamples = 1; - destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample; - - /* setup non-interleaved ptrs */ - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->bytesPerUserInputSample * frameCount; - } - - userInput = bp->tempInputBufferPtrs; - } - - if( !bp->hostInputChannels[0][0].data ) - { - /* no input was supplied (see PaUtil_SetNoInput), so - zero the input buffer */ - - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount ); - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - } - } - else - { - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* 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, bp->callbackStatusFlags, bp->userData ); - - if( *streamCallbackResult == paAbort ) - { - /* callback returned paAbort, don't advance framesProcessed - and framesToGo, they will be handled below */ - } - else - { - bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; - bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod; - - /* convert output data (user -> host) */ - - if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) - { - /* - could use more elaborate logic here and sometimes process - buffers in-place. - */ - - srcBytePtr = (unsigned char *)bp->tempOutputBuffer; - - if( bp->userOutputIsInterleaved ) - { - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcSampleStrideSamples = 1; - srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample; - } - - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* 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 && *streamCallbackResult == paContinue ); - } - - if( framesToGo > 0 ) - { - /* zero any remaining frames output. There will only be remaining frames - if the callback has returned paComplete or paAbort */ - - frameCount = framesToGo; - - if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) - { - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - frameCount ); - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - framesProcessed += frameCount; - } - - 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 destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - 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; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - - userInput = bp->tempInputBuffer; - } - else /* user input is not interleaved */ - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; - - destSampleStrideSamples = 1; - destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; - - /* setup non-interleaved ptrs */ - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; - } - - userInput = bp->tempInputBufferPtrs; - } - - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* 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 ) - { - /** - @todo (non-critical optimisation) - The conditional below implements the continue/complete/abort mechanism - simply by continuing on iterating through the input buffer, but not - passing the data to the callback. With care, the outer loop could be - terminated earlier, thus some unneeded conversion cycles would be - saved. - */ - if( *streamCallbackResult == paContinue ) - { - bp->timeInfo->outputBufferDacTime = 0; - - *streamCallbackResult = bp->streamCallback( userInput, userOutput, - bp->framesPerUserBuffer, bp->timeInfo, - bp->callbackStatusFlags, bp->userData ); - - bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; - } - - 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 srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - do - { - if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue ) - { - 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, - bp->callbackStatusFlags, bp->userData ); - - if( *streamCallbackResult == paAbort ) - { - /* if the callback returned paAbort, we disregard its output */ - } - else - { - bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; - - bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; - } - } - - if( bp->framesInTempOutputBuffer > 0 ) - { - /* convert frameCount frames from user buffer to host buffer */ - - frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo ); - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * bp->outputChannelCount * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = 1; - srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* 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; - } - else - { - /* no more user data is available because the callback has returned - paComplete or paAbort. Fill the remainder of the host buffer - with zeros. - */ - - frameCount = framesToGo; - - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - frameCount ); - - /* 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; -} - -/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from - tempOutputBuffer to hostOutputChannels. This includes data conversion - and interleaving. -*/ -static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp) -{ - unsigned long maxFramesToCopy; - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int frameCount; - unsigned char *srcBytePtr; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - /* 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 = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy ); - } - else - { - hostOutputChannels = bp->hostOutputChannels[1]; - frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy ); - } - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * bp->outputChannelCount * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = 1; - srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - for( i=0; i<bp->outputChannelCount; ++i ) - { - assert( hostOutputChannels[i].data != NULL ); - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* 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; - } -} - -/* - 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 *destBytePtr; - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i, j; - - - framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */ - - if( processPartialUserBuffers ) - endProcessingMinFrameCount = 0; - else - endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1); - - /* Fill host output with remaining frames in user output (tempOutputBuffer) */ - CopyTempOutputBuffersToHostOutputBuffers( bp ); - - while( framesAvailable > endProcessingMinFrameCount ) - { - - if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue ) - { - /* the callback will not be called any more, so zero what remains - of the host output buffers */ - - for( i=0; i<2; ++i ) - { - frameCount = bp->hostOutputFrameCount[i]; - if( frameCount > 0 ) - { - hostOutputChannels = bp->hostOutputChannels[i]; - - for( j=0; j<bp->outputChannelCount; ++j ) - { - bp->outputZeroer( hostOutputChannels[j].data, - hostOutputChannels[j].stride, - frameCount ); - - /* advance dest ptr for next iteration */ - hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) + - frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample; - } - bp->hostOutputFrameCount[i] = 0; - } - } - } - - - /* 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 = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy ); - } - else - { - hostInputChannels = bp->hostInputChannels[1]; - frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy ); - } - - /* configure conversion destination pointers */ - if( bp->userInputIsInterleaved ) - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->inputChannelCount * - bp->framesInTempInputBuffer; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - } - else /* user input is not interleaved */ - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; - - destSampleStrideSamples = 1; - destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; - } - - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* 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 ) - { - if( *streamCallbackResult == paContinue ) - { - /* 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, - bp->callbackStatusFlags, bp->userData ); - - bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; - bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; - - bp->framesInTempInputBuffer = 0; - - if( *streamCallbackResult == paAbort ) - bp->framesInTempOutputBuffer = 0; - else - bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; - } - else - { - /* paComplete or paAbort has already been called. */ - - bp->framesInTempInputBuffer = 0; - } - } - - /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) - Means to process the user output provided by the callback. Has to be called after - each callback. */ - CopyTempOutputBuffersToHostOutputBuffers( bp ); - - } - - return framesProcessed; -} - - -unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult ) -{ - unsigned long framesToProcess, framesToGo; - unsigned long framesProcessed = 0; - - if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 - && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */ - && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ ) - { - assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) == - (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) ); - } - - assert( *streamCallbackResult == paContinue - || *streamCallbackResult == paComplete - || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */ - - if( bp->useNonAdaptingProcess ) - { - if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) - { - /* full duplex non-adapting process, splice buffers if they are - different lengths */ - - framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */ - - do{ - unsigned long noInputInputFrameCount; - unsigned long *hostInputFrameCount; - PaUtilChannelDescriptor *hostInputChannels; - unsigned long noOutputOutputFrameCount; - unsigned long *hostOutputFrameCount; - PaUtilChannelDescriptor *hostOutputChannels; - unsigned long framesProcessedThisIteration; - - if( !bp->hostInputChannels[0][0].data ) - { - /* no input was supplied (see PaUtil_SetNoInput) - NonAdaptingProcess knows how to deal with this - */ - noInputInputFrameCount = framesToGo; - hostInputFrameCount = &noInputInputFrameCount; - hostInputChannels = 0; - } - else if( bp->hostInputFrameCount[0] != 0 ) - { - hostInputFrameCount = &bp->hostInputFrameCount[0]; - hostInputChannels = bp->hostInputChannels[0]; - } - else - { - hostInputFrameCount = &bp->hostInputFrameCount[1]; - hostInputChannels = bp->hostInputChannels[1]; - } - - if( !bp->hostOutputChannels[0][0].data ) - { - /* no output was supplied (see PaUtil_SetNoOutput) - NonAdaptingProcess knows how to deal with this - */ - noOutputOutputFrameCount = framesToGo; - hostOutputFrameCount = &noOutputOutputFrameCount; - hostOutputChannels = 0; - } - 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; -} - - -int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp ) -{ - return (bp->framesInTempOutputBuffer) ? 0 : 1; -} - - -unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp, - void **buffer, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostInputChannels; - unsigned int framesToCopy; - unsigned char *destBytePtr; - void **nonInterleavedDestPtrs; - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - hostInputChannels = bp->hostInputChannels[0]; - framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount ); - - if( bp->userInputIsInterleaved ) - { - destBytePtr = (unsigned char*)*buffer; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - - for( i=0; i<bp->inputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - framesToCopy, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - - /* advance callers dest pointer (buffer) */ - *buffer = ((unsigned char *)*buffer) + - framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample; - } - else - { - /* user input is not interleaved */ - - nonInterleavedDestPtrs = (void**)*buffer; - - destSampleStrideSamples = 1; - - for( i=0; i<bp->inputChannelCount; ++i ) - { - destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i]; - - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - framesToCopy, &bp->ditherGenerator ); - - /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */ - destBytePtr += bp->bytesPerUserInputSample * framesToCopy; - nonInterleavedDestPtrs[i] = destBytePtr; - - /* advance dest ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - } - - bp->hostInputFrameCount[0] -= framesToCopy; - - return framesToCopy; -} - -unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp, - const void ** buffer, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int framesToCopy; - unsigned char *srcBytePtr; - void **nonInterleavedSrcPtrs; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - hostOutputChannels = bp->hostOutputChannels[0]; - framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = (unsigned char*)*buffer; - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - framesToCopy, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - /* advance callers source pointer (buffer) */ - *buffer = ((unsigned char *)*buffer) + - framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample; - - } - else - { - /* user output is not interleaved */ - - nonInterleavedSrcPtrs = (void**)*buffer; - - srcSampleStrideSamples = 1; - - for( i=0; i<bp->outputChannelCount; ++i ) - { - srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i]; - - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - framesToCopy, &bp->ditherGenerator ); - - - /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */ - srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy; - nonInterleavedSrcPtrs[i] = srcBytePtr; - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - bp->hostOutputFrameCount[0] += framesToCopy; - - return framesToCopy; -} - - -unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int framesToZero; - unsigned int i; - - hostOutputChannels = bp->hostOutputChannels[0]; - framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); - - for( i=0; i<bp->outputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - framesToZero ); - - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - bp->hostOutputFrameCount[0] += framesToZero; - - return framesToZero; -} diff --git a/portaudio/pa_common/pa_process.h b/portaudio/pa_common/pa_process.h deleted file mode 100644 index c52e9ea0a..000000000 --- a/portaudio/pa_common/pa_process.h +++ /dev/null @@ -1,741 +0,0 @@ -#ifndef PA_PROCESS_H -#define PA_PROCESS_H -/* - * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 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. - */ - -/** @file - @brief Buffer Processor prototypes. A Buffer Processor performs buffer length - adaption, coordinates sample format conversion, and interleaves/deinterleaves - channels. - - <h3>Overview</h3> - - The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio - data from host buffers to user buffers and back again. Where required, the - buffer processor takes care of converting between host and user sample formats, - interleaving and deinterleaving multichannel buffers, and adapting between host - and user buffers with different lengths. The buffer processor may be used with - full and half duplex streams, for both callback streams and blocking read/write - streams. - - One of the important capabilities provided by the buffer processor is - the ability to adapt between user and host buffer sizes of different lengths - with minimum latency. Although this task is relatively easy to perform when - the host buffer size is an integer multiple of the user buffer size, the - problem is more complicated when this is not the case - especially for - full-duplex callback streams. Where necessary the adaption is implemented by - internally buffering some input and/or output data. The buffer adation - algorithm used by the buffer processor was originally implemented by - Stephan Letz for the ASIO version of PortAudio, and is described in his - Callback_adaption_.pdf which is included in the distribution. - - The buffer processor performs sample conversion using the functions provided - by pa_converters.c. - - The following sections provide an overview of how to use the buffer processor. - Interested readers are advised to consult the host API implementations for - examples of buffer processor usage. - - - <h4>Initialization, resetting and termination</h4> - - When a stream is opened, the buffer processor should be initialized using - PaUtil_InitializeBufferProcessor. This function initializes internal state - and allocates temporary buffers as neccesary according to the supplied - configuration parameters. Some of the parameters correspond to those requested - by the user in their call to Pa_OpenStream(), others reflect the requirements - of the host API implementation - they indicate host buffer sizes, formats, - and the type of buffering which the Host API uses. The buffer processor should - be initialized for callback streams and blocking read/write streams. - - Call PaUtil_ResetBufferProcessor to clear any sample data which is present - in the buffer processor before starting to use it (for example when - Pa_StartStream is called). - - When the buffer processor is no longer used call - PaUtil_TerminateBufferProcessor. - - - <h4>Using the buffer processor for a callback stream</h4> - - The buffer processor's role in a callback stream is to take host input buffers - process them with the stream callback, and fill host output buffers. For a - full duplex stream, the buffer processor handles input and output simultaneously - due to the requirements of the minimum-latency buffer adation algorithm. - - When a host buffer becomes available, the implementation should call - the buffer processor to process the buffer. The buffer processor calls the - stream callback to consume and/or produce audio data as necessary. The buffer - processor will convert sample formats, interleave/deinterleave channels, - and slice or chunk the data to the appropriate buffer lengths according to - the requirements of the stream callback and the host API. - - To process a host buffer (or a pair of host buffers for a full-duplex stream) - use the following calling sequence: - - -# Call PaUtil_BeginBufferProcessing - -# For a stream which takes input: - - Call PaUtil_SetInputFrameCount with the number of frames in the host input - buffer. - - Call one of the following functions one or more times to tell the - buffer processor about the host input buffer(s): PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - - If the available host data is split accross two buffers (for example a - data range at the end of a circular buffer and another range at the - beginning of the circular buffer), also call - PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel, - PaUtil_Set2ndInterleavedInputChannels, - PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer - processor about the second buffer. - -# For a stream which generates output: - - Call PaUtil_SetOutputFrameCount with the number of frames in the host - output buffer. - - Call one of the following functions one or more times to tell the - buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, - PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - - If the available host output buffer space is split accross two buffers - (for example a data range at the end of a circular buffer and another - range at the beginning of the circular buffer), call - PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel, - PaUtil_Set2ndInterleavedOutputChannels, - PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer - processor about the second buffer. - -# Call PaUtil_EndBufferProcessing, this function performs the actual data - conversion and processing. - - - <h4>Using the buffer processor for a blocking read/write stream</h4> - - Blocking read/write streams use the buffer processor to convert and copy user - output data to a host buffer, and to convert and copy host input data to - the user's buffer. The buffer processor does not perform any buffer adaption. - When using the buffer processor in a blocking read/write stream the input and - output conversion are performed separately by the PaUtil_CopyInput and - PaUtil_CopyOutput functions. - - To copy data from a host input buffer to the buffer(s) which the user supplies - to Pa_ReadStream, use the following calling sequence. - - - Repeat the following three steps until the user buffer(s) have been filled - with samples from the host input buffers: - -# Call PaUtil_SetInputFrameCount with the number of frames in the host - input buffer. - -# Call one of the following functions one or more times to tell the - buffer processor about the host input buffer(s): PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the - array of buffer pointers for a non-interleaved stream) passed to - Pa_ReadStream, along with the number of frames in the user buffer(s). - Be careful to pass a <i>copy</i> of the user buffer pointers to - PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to - the start of the next region to copy. - - PaUtil_CopyInput will not copy more data than is available in the - host buffer(s), so the above steps need to be repeated until the user - buffer(s) are full. - - - To copy data to the host output buffer from the user buffers(s) supplied - to Pa_WriteStream use the following calling sequence. - - - Repeat the following three steps until all frames from the user buffer(s) - have been copied to the host API: - -# Call PaUtil_SetOutputFrameCount with the number of frames in the host - output buffer. - -# Call one of the following functions one or more times to tell the - buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, - PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the - array of buffer pointers for a non-interleaved stream) passed to - Pa_WriteStream, along with the number of frames in the user buffer(s). - Be careful to pass a <i>copy</i> of the user buffer pointers to - PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to - the start of the next region to copy. - - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s), - so the above steps need to be repeated until all user data is copied. -*/ - - -#include "portaudio.h" -#include "pa_converters.h" -#include "pa_dither.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type - of buffering that the host API uses. - - The mode used depends on whether the host API or the implementation manages - the buffers, and how these buffers are used (scatter gather, circular buffer). -*/ -typedef enum { -/** The host buffer size is a fixed known size. */ - paUtilFixedHostBufferSize, - -/** The host buffer size may vary, but has a known maximum size. */ - paUtilBoundedHostBufferSize, - -/** Nothing is known about the host buffer size. */ - paUtilUnknownHostBufferSize, - -/** The host buffer size varies, and the client does not require the buffer - processor to consume all of the input and fill all of the output buffer. This - is useful when the implementation has access to the host API's circular buffer - and only needs to consume/fill some of it, not necessarily all of it, with each - call to the buffer processor. This is the only mode where - PaUtil_EndBufferProcessing() may not consume the whole buffer. -*/ - paUtilVariableHostBufferSizePartialUsageAllowed -}PaUtilHostBufferSizeMode; - - -/** @brief An auxilliary data structure used internally by the buffer processor - to represent host input and output buffers. */ -typedef struct PaUtilChannelDescriptor{ - void *data; - unsigned int stride; /**< stride in samples, not bytes */ -}PaUtilChannelDescriptor; - - -/** @brief The main buffer processor data structure. - - Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor - and terminate it with PaUtil_TerminateBufferProcessor. -*/ -typedef struct { - unsigned long framesPerUserBuffer; - unsigned long framesPerHostBuffer; - - PaUtilHostBufferSizeMode hostBufferSizeMode; - int useNonAdaptingProcess; - unsigned long framesPerTempBuffer; - - unsigned int inputChannelCount; - unsigned int bytesPerHostInputSample; - unsigned int bytesPerUserInputSample; - int userInputIsInterleaved; - PaUtilConverter *inputConverter; - PaUtilZeroer *inputZeroer; - - unsigned int outputChannelCount; - unsigned int bytesPerHostOutputSample; - unsigned int bytesPerUserOutputSample; - int userOutputIsInterleaved; - PaUtilConverter *outputConverter; - PaUtilZeroer *outputZeroer; - - unsigned long initialFramesInTempInputBuffer; - unsigned long initialFramesInTempOutputBuffer; - - void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */ - void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */ - unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ - - void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */ - void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */ - unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ - - PaStreamCallbackTimeInfo *timeInfo; - - PaStreamCallbackFlags callbackStatusFlags; - - unsigned long hostInputFrameCount[2]; - PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors. - pointers are NULL for half-duplex output processing. - hostInputChannels[i].data is NULL when the caller - calls PaUtil_SetNoInput() - */ - unsigned long hostOutputFrameCount[2]; - PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors. - pointers are NULL for half-duplex input processing. - hostOutputChannels[i].data is NULL when the caller - calls PaUtil_SetNoOutput() - */ - - PaUtilTriangularDitherGenerator ditherGenerator; - - double samplePeriod; - - PaStreamCallback *streamCallback; - void *userData; -} PaUtilBufferProcessor; - - -/** @name Initialization, termination, resetting and info */ -/*@{*/ - -/** Initialize a buffer processor's representation stored in a - PaUtilBufferProcessor structure. Be sure to call - PaUtil_TerminateBufferProcessor after finishing with a buffer processor. - - @param bufferProcessor The buffer processor structure to initialize. - - @param inputChannelCount The number of input channels as passed to - Pa_OpenStream or 0 for an output-only stream. - - @param userInputSampleFormat Format of user input samples, as passed to - Pa_OpenStream. This parameter is ignored for ouput-only streams. - - @param hostInputSampleFormat Format of host input samples. This parameter is - ignored for output-only streams. See note about host buffer interleave below. - - @param outputChannelCount The number of output channels as passed to - Pa_OpenStream or 0 for an input-only stream. - - @param userOutputSampleFormat Format of user output samples, as passed to - Pa_OpenStream. This parameter is ignored for input-only streams. - - @param hostOutputSampleFormat Format of host output samples. This parameter is - ignored for input-only streams. See note about host buffer interleave below. - - @param sampleRate Sample rate of the stream. The more accurate this is the - better - it is used for updating time stamps when adapting buffers. - - @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is - used for selecting special sample conversion options such as clipping and - dithering. - - @param framesPerUserBuffer Number of frames per user buffer, as requested - by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be - zero to indicate that the user will accept any (and varying) buffer sizes. - - @param framesPerHostBuffer Specifies the number of frames per host buffer - for the fixed buffer size mode, and the maximum number of frames - per host buffer for the bounded host buffer size mode. It is ignored for - the other modes. - - @param hostBufferSizeMode A mode flag indicating the size variability of - host buffers that will be passed to the buffer processor. See - PaUtilHostBufferSizeMode for further details. - - @param streamCallback The user stream callback passed to Pa_OpenStream. - - @param userData The user data field passed to Pa_OpenStream. - - @note The interleave flag is ignored for host buffer formats. Host - interleave is determined by the use of different SetInput and SetOutput - functions. - - @return An error code indicating whether the initialization was successful. - If the error code is not PaNoError, the buffer processor was not initialized - and should not be used. - - @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor -*/ -PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor, - int inputChannelCount, PaSampleFormat userInputSampleFormat, - PaSampleFormat hostInputSampleFormat, - int outputChannelCount, PaSampleFormat userOutputSampleFormat, - PaSampleFormat hostOutputSampleFormat, - double sampleRate, - PaStreamFlags streamFlags, - unsigned long framesPerUserBuffer, /* 0 indicates don't care */ - unsigned long framesPerHostBuffer, - PaUtilHostBufferSizeMode hostBufferSizeMode, - PaStreamCallback *streamCallback, void *userData ); - - -/** Terminate a buffer processor's representation. Deallocates any temporary - buffers allocated by PaUtil_InitializeBufferProcessor. - - @param bufferProcessor The buffer processor structure to terminate. - - @see PaUtil_InitializeBufferProcessor. -*/ -void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); - - -/** Clear any internally buffered data. If you call - PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you - call PaUtil_ResetBufferProcessor in your StartStream call. - - @param bufferProcessor The buffer processor to reset. -*/ -void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); - - -/** Retrieve the input latency of a buffer processor. - - @param bufferProcessor The buffer processor examine. - - @return The input latency introduced by the buffer processor, in frames. - - @see PaUtil_GetBufferProcessorOutputLatency -*/ -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor ); - -/** Retrieve the output latency of a buffer processor. - - @param bufferProcessor The buffer processor examine. - - @return The output latency introduced by the buffer processor, in frames. - - @see PaUtil_GetBufferProcessorInputLatency -*/ -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor ); - -/*@}*/ - - -/** @name Host buffer pointer configuration - - Functions to set host input and output buffers, used by both callback streams - and blocking read/write streams. -*/ -/*@{*/ - - -/** Set the number of frames in the input host buffer(s) specified by the - PaUtil_Set*InputChannel functions. - - @param bufferProcessor The buffer processor. - - @param frameCount The number of host input frames. A 0 frameCount indicates to - use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. - - @see PaUtil_SetNoInput, PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel -*/ -void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/** Indicate that no input is avalable. This function should be used when - priming the output of a full-duplex stream opened with the - paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary - to call this or any othe PaUtil_Set*Input* functions for ouput-only streams. - - @param bufferProcessor The buffer processor. -*/ -void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor ); - - -/** Provide the buffer processor with a pointer to a host input channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. - @param stride The stride from one sample to the next, in samples. For - interleaved host buffers, the stride will usually be the same as the number of - channels in the buffer. -*/ -void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - - -/** Provide the buffer processor with a pointer to an number of interleaved - host input channels. - - @param bufferProcessor The buffer processor. - @param firstChannel The first channel number. - @param data The buffer. - @param channelCount The number of interleaved channels in the buffer. If - channelCount is zero, the number of channels specified to - PaUtil_InitializeBufferProcessor will be used. -*/ -void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - - -/** Provide the buffer processor with a pointer to one non-interleaved host - output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. -*/ -void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInputFrameCount -*/ -void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInputChannel -*/ -void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInterleavedInputChannels -*/ -void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetNonInterleavedInputChannel -*/ -void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Set the number of frames in the output host buffer(s) specified by the - PaUtil_Set*OutputChannel functions. - - @param bufferProcessor The buffer processor. - - @param frameCount The number of host output frames. A 0 frameCount indicates to - use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. - - @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels, - PaUtil_SetNonInterleavedOutputChannel -*/ -void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/** Indicate that the output will be discarded. This function should be used - when implementing the paNeverDropInput mode for full duplex streams. - - @param bufferProcessor The buffer processor. -*/ -void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor ); - - -/** Provide the buffer processor with a pointer to a host output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. - @param stride The stride from one sample to the next, in samples. For - interleaved host buffers, the stride will usually be the same as the number of - channels in the buffer. -*/ -void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - - -/** Provide the buffer processor with a pointer to a number of interleaved - host output channels. - - @param bufferProcessor The buffer processor. - @param firstChannel The first channel number. - @param data The buffer. - @param channelCount The number of interleaved channels in the buffer. If - channelCount is zero, the number of channels specified to - PaUtil_InitializeBufferProcessor will be used. -*/ -void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - - -/** Provide the buffer processor with a pointer to one non-interleaved host - output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. -*/ -void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetOutputFrameCount -*/ -void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetOutputChannel -*/ -void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetInterleavedOutputChannels -*/ -void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetNonInterleavedOutputChannel -*/ -void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - -/*@}*/ - - -/** @name Buffer processing functions for callback streams -*/ -/*@{*/ - -/** Commence processing a host buffer (or a pair of host buffers in the - full-duplex case) for a callback stream. - - @param bufferProcessor The buffer processor. - - @param timeInfo Timing information for the first sample of the host - buffer(s). This information may be adjusted when buffer adaption is being - performed. - - @param callbackStatusFlags Flags indicating whether underruns and overruns - have occurred since the last time the buffer processor was called. -*/ -void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor, - PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ); - - -/** Finish processing a host buffer (or a pair of host buffers in the - full-duplex case) for a callback stream. - - @param bufferProcessor The buffer processor. - - @param callbackResult On input, indicates a previous callback result, and on - exit, the result of the user stream callback, if it is called. - On entry callbackResult should contain one of { paContinue, paComplete, or - paAbort}. If paComplete is passed, the stream callback will not be called - but any audio that was generated by previous stream callbacks will be copied - to the output buffer(s). You can check whether the buffer processor's internal - buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty. - - If the stream callback is called its result is stored in *callbackResult. If - the stream callback returns paComplete or paAbort, all output buffers will be - full of valid data - some of which may be zeros to acount for data that - wasn't generated by the terminating callback. - - @return The number of frames processed. This usually corresponds to the - number of frames specified by the PaUtil_Set*FrameCount functions, exept in - the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a - smaller value may be returned. -*/ -unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor, - int *callbackResult ); - - -/** Determine whether any callback generated output remains in the bufffer - processor's internal buffers. This method may be used to determine when to - continue calling PaUtil_EndBufferProcessing() after the callback has returned - a callbackResult of paComplete. - - @param bufferProcessor The buffer processor. - - @return Returns non-zero when callback generated output remains in the internal - buffer and zero (0) when there internal buffer contains no callback generated - data. -*/ -int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor ); - -/*@}*/ - - -/** @name Buffer processing functions for blocking read/write streams -*/ -/*@{*/ - -/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels - functions to a user supplied buffer. This function is intended for use with - blocking read/write streams. Copies the minimum of the number of - user frames (specified by the frameCount parameter) and the number of available - host frames (specified in a previous call to SetInputFrameCount()). - - @param bufferProcessor The buffer processor. - - @param buffer A pointer to the user buffer pointer, or a pointer to a pointer - to an array of user buffer pointers for a non-interleaved stream. It is - important that this parameter points to a copy of the user buffer pointers, - not to the actual user buffer pointers, because this function updates the - pointers before returning. - - @param frameCount The number of frames of data in the buffer(s) pointed to by - the buffer parameter. - - @return The number of frames copied. The buffer pointer(s) pointed to by the - buffer parameter are advanced to point to the frame(s) following the last one - filled. -*/ -unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor, - void **buffer, unsigned long frameCount ); - - -/* Copy samples from a user supplied buffer to host output channels set up by - the PaUtil_Set*OutputChannels functions. This function is intended for use with - blocking read/write streams. Copies the minimum of the number of - user frames (specified by the frameCount parameter) and the number of - host frames (specified in a previous call to SetOutputFrameCount()). - - @param bufferProcessor The buffer processor. - - @param buffer A pointer to the user buffer pointer, or a pointer to a pointer - to an array of user buffer pointers for a non-interleaved stream. It is - important that this parameter points to a copy of the user buffer pointers, - not to the actual user buffer pointers, because this function updates the - pointers before returning. - - @param frameCount The number of frames of data in the buffer(s) pointed to by - the buffer parameter. - - @return The number of frames copied. The buffer pointer(s) pointed to by the - buffer parameter are advanced to point to the frame(s) following the last one - copied. -*/ -unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor, - const void ** buffer, unsigned long frameCount ); - - -/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels - functions. This function is useful for flushing streams. - Zeros the minimum of frameCount and the number of host frames specified in a - previous call to SetOutputFrameCount(). - - @param bufferProcessor The buffer processor. - - @param frameCount The maximum number of frames to zero. - - @return The number of frames zeroed. -*/ -unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/*@}*/ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_PROCESS_H */ diff --git a/portaudio/pa_common/pa_skeleton.c b/portaudio/pa_common/pa_skeleton.c deleted file mode 100644 index ebc1f5018..000000000 --- a/portaudio/pa_common/pa_skeleton.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 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. - */ - -/** @file - @brief Skeleton implementation of support for a host API. - - @note This file is provided as a starting point for implementing support for - a new host API. IMPLEMENT ME comments are used to indicate functionality - which much be customised for each implementation. -*/ - - -#include <string.h> /* strlen() */ - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* IMPLEMENT ME: a macro like the following one should be used for reporting - host errors */ -#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) - -/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ -} -PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */ - - -PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, deviceCount; - PaSkeletonHostApiRepresentation *skeletonHostApi; - PaDeviceInfo *deviceInfoArray; - - skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) ); - if( !skeletonHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - skeletonHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !skeletonHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &skeletonHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */ - (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */ - - (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */ - (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */ - - (*hostApi)->info.deviceCount = 0; - - deviceCount = 0; /* IMPLEMENT ME */ - - if( deviceCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < deviceCount; ++i ) - { - PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg: - deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, srcName ); - deviceInfo->name = deviceName; - */ - - deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */ - deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */ - - deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */ - - deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */ - - (*hostApi)->deviceInfos[i] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - } - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( skeletonHostApi ) - { - if( skeletonHostApi->allocations ) - { - PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); - PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); - } - - PaUtil_FreeMemory( skeletonHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ - - if( skeletonHostApi->allocations ) - { - PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); - PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); - } - - PaUtil_FreeMemory( skeletonHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* - IMPLEMENT ME: - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary - - - check that the device supports sampleRate - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from inputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - - - /* suppress unused variable warnings */ - (void) sampleRate; - - return paFormatIsSupported; -} - -/* PaSkeletonStream - a stream data structure specifically for this implementation */ - -typedef struct PaSkeletonStream -{ /* IMPLEMENT ME: rename this */ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - /* IMPLEMENT ME: - - implementation specific data goes here - */ - unsigned long framesPerHostCallback; /* just an example */ -} -PaSkeletonStream; - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; - PaSkeletonStream *stream = 0; - unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* IMPLEMENT ME - establish which host formats are available */ - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* IMPLEMENT ME - establish which host formats are available */ - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ - } - - /* - IMPLEMENT ME: - - ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? ) - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - - alter sampleRate to a close allowable rate if possible / necessary - - - validate suggestedInputLatency and suggestedOutputLatency parameters, - use default values where necessary - */ - - - - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - - stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &skeletonHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &skeletonHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - /* we assume a fixed host buffer size in this example, but the buffer processor - can also support bounded and unknown host buffer sizes by passing - paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of - paUtilFixedHostBufferSize below. */ - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerHostBuffer, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - - - /* - IMPLEMENT ME: initialise the following fields with estimated or actual - values. - */ - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - - /* - IMPLEMENT ME: - - additional stream setup + opening - */ - - stream->framesPerHostCallback = framesPerHostBuffer; - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - PaUtil_FreeMemory( stream ); - - return result; -} - -/* - ExampleHostProcessingLoop() illustrates the kind of processing which may - occur in a host implementation. - -*/ -static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)userData; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ - int callbackResult; - unsigned long framesProcessed; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* - IMPLEMENT ME: - - generate timing information - - handle buffer slips - */ - - /* - If you need to byte swap or shift inputBuffer to convert it into a - portaudio format, do it here. - */ - - - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ ); - - /* - depending on whether the host buffers are interleaved, non-interleaved - or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), - PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. - */ - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - inputBuffer, - 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, - 0, /* first channel of outputBuffer is channel 0 */ - outputBuffer, - 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ - - /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing() - in general you would pass paContinue for normal operation, and - paComplete to drain the buffer processor's internal output buffer. - You can check whether the buffer processor's output buffer is empty - using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) - */ - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - - /* - If you need to byte swap or shift outputBuffer to convert it to - host format, do it here. - */ - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - /* IMPLEMENT ME - finish playback immediately */ - - /* once finished, call the finished callback */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - else - { - /* User callback has asked us to stop with paComplete or other non-zero value */ - - /* IMPLEMENT ME - finish playback once currently queued audio has completed */ - - /* once finished, call the finished callback */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* - IMPLEMENT ME: - - additional stream closing + cleanup - */ - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - /* suppress unused function warning. the code in ExampleHostProcessingLoop or - something similar should be implemented to feed samples to and from the - host after StartStream() is called. - */ - (void) ExampleHostProcessingLoop; - - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return 0; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - - - diff --git a/portaudio/pa_common/pa_stream.c b/portaudio/pa_common/pa_stream.c deleted file mode 100644 index 044dde29e..000000000 --- a/portaudio/pa_common/pa_stream.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 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. - */ - -/** @file - @brief Interface used by pa_front to virtualize functions which operate on - streams. -*/ - - -#include "pa_stream.h" - - -void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, - PaError (*Close)( PaStream* ), - PaError (*Start)( PaStream* ), - PaError (*Stop)( PaStream* ), - PaError (*Abort)( PaStream* ), - PaError (*IsStopped)( PaStream* ), - PaError (*IsActive)( PaStream* ), - PaTime (*GetTime)( PaStream* ), - double (*GetCpuLoad)( PaStream* ), - PaError (*Read)( PaStream*, void *, unsigned long ), - PaError (*Write)( PaStream*, const void *, unsigned long ), - signed long (*GetReadAvailable)( PaStream* ), - signed long (*GetWriteAvailable)( PaStream* ) ) -{ - streamInterface->Close = Close; - streamInterface->Start = Start; - streamInterface->Stop = Stop; - streamInterface->Abort = Abort; - streamInterface->IsStopped = IsStopped; - streamInterface->IsActive = IsActive; - streamInterface->GetTime = GetTime; - streamInterface->GetCpuLoad = GetCpuLoad; - streamInterface->Read = Read; - streamInterface->Write = Write; - streamInterface->GetReadAvailable = GetReadAvailable; - streamInterface->GetWriteAvailable = GetWriteAvailable; -} - - -void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation, - PaUtilStreamInterface *streamInterface, - PaStreamCallback *streamCallback, - void *userData ) -{ - streamRepresentation->magic = PA_STREAM_MAGIC; - streamRepresentation->nextOpenStream = 0; - streamRepresentation->streamInterface = streamInterface; - streamRepresentation->streamCallback = streamCallback; - streamRepresentation->streamFinishedCallback = 0; - - streamRepresentation->userData = userData; - - streamRepresentation->streamInfo.inputLatency = 0.; - streamRepresentation->streamInfo.outputLatency = 0.; - streamRepresentation->streamInfo.sampleRate = 0.; -} - - -void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ) -{ - streamRepresentation->magic = 0; -} - - -PaError PaUtil_DummyRead( PaStream* stream, - void *buffer, - unsigned long frames ) -{ - (void)stream; /* unused parameter */ - (void)buffer; /* unused parameter */ - (void)frames; /* unused parameter */ - - return paCanNotReadFromACallbackStream; -} - - -PaError PaUtil_DummyWrite( PaStream* stream, - const void *buffer, - unsigned long frames ) -{ - (void)stream; /* unused parameter */ - (void)buffer; /* unused parameter */ - (void)frames; /* unused parameter */ - - return paCanNotWriteToACallbackStream; -} - - -signed long PaUtil_DummyGetReadAvailable( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return paCanNotReadFromACallbackStream; -} - - -signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return paCanNotWriteToACallbackStream; -} - - -double PaUtil_DummyGetCpuLoad( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return 0.0; -} diff --git a/portaudio/pa_common/pa_stream.h b/portaudio/pa_common/pa_stream.h deleted file mode 100644 index 8b86943bf..000000000 --- a/portaudio/pa_common/pa_stream.h +++ /dev/null @@ -1,196 +0,0 @@ -#ifndef PA_STREAM_H -#define PA_STREAM_H -/* - * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 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 - @brief Interface used by pa_front to virtualize functions which operate on - streams. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#define PA_STREAM_MAGIC (0x18273645) - - -/** A structure representing an (abstract) interface to a host API. Contains - pointers to functions which implement the interface. - - All PaStreamInterface functions are guaranteed to be called with a non-null, - valid stream parameter. -*/ -typedef struct { - PaError (*Close)( PaStream* stream ); - PaError (*Start)( PaStream *stream ); - PaError (*Stop)( PaStream *stream ); - PaError (*Abort)( PaStream *stream ); - PaError (*IsStopped)( PaStream *stream ); - PaError (*IsActive)( PaStream *stream ); - PaTime (*GetTime)( PaStream *stream ); - double (*GetCpuLoad)( PaStream* stream ); - PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ); - PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ); - signed long (*GetReadAvailable)( PaStream* stream ); - signed long (*GetWriteAvailable)( PaStream* stream ); -} PaUtilStreamInterface; - - -/** Initialize the fields of a PaUtilStreamInterface structure. -*/ -void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, - PaError (*Close)( PaStream* ), - PaError (*Start)( PaStream* ), - PaError (*Stop)( PaStream* ), - PaError (*Abort)( PaStream* ), - PaError (*IsStopped)( PaStream* ), - PaError (*IsActive)( PaStream* ), - PaTime (*GetTime)( PaStream* ), - double (*GetCpuLoad)( PaStream* ), - PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ), - PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ), - signed long (*GetReadAvailable)( PaStream* stream ), - signed long (*GetWriteAvailable)( PaStream* stream ) ); - - -/** Dummy Read function for use in interfaces to a callback based streams. - Pass to the Read parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -PaError PaUtil_DummyRead( PaStream* stream, - void *buffer, - unsigned long frames ); - - -/** Dummy Write function for use in an interfaces to callback based streams. - Pass to the Write parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -PaError PaUtil_DummyWrite( PaStream* stream, - const void *buffer, - unsigned long frames ); - - -/** Dummy GetReadAvailable function for use in interfaces to callback based - streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -signed long PaUtil_DummyGetReadAvailable( PaStream* stream ); - - -/** Dummy GetWriteAvailable function for use in interfaces to callback based - streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ); - - - -/** Dummy GetCpuLoad function for use in an interface to a read/write stream. - Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface. - @return Returns 0. -*/ -double PaUtil_DummyGetCpuLoad( PaStream* stream ); - - -/** Non host specific data for a stream. This data is used by pa_front to - forward to the appropriate functions in the streamInterface structure. -*/ -typedef struct PaUtilStreamRepresentation { - unsigned long magic; /**< set to PA_STREAM_MAGIC */ - struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */ - PaUtilStreamInterface *streamInterface; - PaStreamCallback *streamCallback; - PaStreamFinishedCallback *streamFinishedCallback; - void *userData; - PaStreamInfo streamInfo; -} PaUtilStreamRepresentation; - - -/** Initialize a PaUtilStreamRepresentation structure. - - @see PaUtil_InitializeStreamRepresentation -*/ -void PaUtil_InitializeStreamRepresentation( - PaUtilStreamRepresentation *streamRepresentation, - PaUtilStreamInterface *streamInterface, - PaStreamCallback *streamCallback, - void *userData ); - - -/** Clean up a PaUtilStreamRepresentation structure previously initialized - by a call to PaUtil_InitializeStreamRepresentation. - - @see PaUtil_InitializeStreamRepresentation -*/ -void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ); - - -/** Check that the stream pointer is valid. - - @return Returns paNoError if the stream pointer appears to be OK, otherwise - returns an error indicating the cause of failure. -*/ -PaError PaUtil_ValidateStreamPointer( PaStream *stream ); - - -/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation. - - @see PaUtilStreamRepresentation -*/ -#define PA_STREAM_REP( stream )\ - ((PaUtilStreamRepresentation*) (stream) ) - - -/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface. - - @see PaUtilStreamRepresentation, PaUtilStreamInterface -*/ -#define PA_STREAM_INTERFACE( stream )\ - PA_STREAM_REP( (stream) )->streamInterface - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_STREAM_H */ diff --git a/portaudio/pa_common/pa_trace.c b/portaudio/pa_common/pa_trace.c deleted file mode 100644 index e36329fdf..000000000 --- a/portaudio/pa_common/pa_trace.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * $Id: pa_trace.c,v 1.1.1.1.2.4 2005/11/02 12:06:44 rossbencina Exp $ - * Portable Audio I/O Library Trace Facility - * Store trace information in real-time for later printing. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Event trace mechanism for debugging. -*/ - - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "pa_trace.h" - -#if PA_TRACE_REALTIME_EVENTS - -static char *traceTextArray[PA_MAX_TRACE_RECORDS]; -static int traceIntArray[PA_MAX_TRACE_RECORDS]; -static int traceIndex = 0; -static int traceBlock = 0; - -/*********************************************************************/ -void PaUtil_ResetTraceMessages() -{ - traceIndex = 0; -} - -/*********************************************************************/ -void PaUtil_DumpTraceMessages() -{ - int i; - int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS; - - printf("DumpTraceMessages: traceIndex = %d\n", traceIndex ); - for( i=0; i<messageCount; i++ ) - { - printf("%3d: %s = 0x%08X\n", - i, traceTextArray[i], traceIntArray[i] ); - } - PaUtil_ResetTraceMessages(); - fflush(stdout); -} - -/*********************************************************************/ -void PaUtil_AddTraceMessage( const char *msg, int data ) -{ - if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) ) - { - traceBlock = 1; - /* PaUtil_DumpTraceMessages(); */ - } - else if( traceIndex < PA_MAX_TRACE_RECORDS ) - { - traceTextArray[traceIndex] = msg; - traceIntArray[traceIndex] = data; - traceIndex++; - } -} - -#endif /* TRACE_REALTIME_EVENTS */ diff --git a/portaudio/pa_common/pa_trace.h b/portaudio/pa_common/pa_trace.h deleted file mode 100644 index f72a78b34..000000000 --- a/portaudio/pa_common/pa_trace.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef PA_TRACE_H -#define PA_TRACE_H -/* - * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $ - * Portable Audio I/O Library Trace Facility - * Store trace information in real-time for later printing. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Event trace mechanism for debugging. - - Allows data to be written to the buffer at interrupt time and dumped later. -*/ - - -#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */ -#define PA_MAX_TRACE_RECORDS (2048) - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#if PA_TRACE_REALTIME_EVENTS - -void PaUtil_ResetTraceMessages(); -void PaUtil_AddTraceMessage( const char *msg, int data ); -void PaUtil_DumpTraceMessages(); - -#else - -#define PaUtil_ResetTraceMessages() /* noop */ -#define PaUtil_AddTraceMessage(msg,data) /* noop */ -#define PaUtil_DumpTraceMessages() /* noop */ - -#endif - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_TRACE_H */ diff --git a/portaudio/pa_common/pa_types.h b/portaudio/pa_common/pa_types.h deleted file mode 100644 index 343dc8cf8..000000000 --- a/portaudio/pa_common/pa_types.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef PA_TYPES_H -#define PA_TYPES_H - -/* - SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script - when it is used. Otherwise we default to the common 32 bit values, if your - platform doesn't use configure, and doesn't use the default values below - you will need to explicitly define these symbols in your make file. - - A PA_VALIDATE_SIZES macro is provided to assert that the values set in this - file are correct. -*/ - -#ifndef SIZEOF_SHORT -#define SIZEOF_SHORT 2 -#endif - -#ifndef SIZEOF_INT -#define SIZEOF_INT 4 -#endif - -#ifndef SIZEOF_LONG -#define SIZEOF_LONG 4 -#endif - - -#if SIZEOF_SHORT == 2 -typedef signed short PaInt16; -typedef unsigned short PaUint16; -#elif SIZEOF_INT == 2 -typedef signed int PaInt16; -typedef unsigned int PaUint16; -#else -#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform -#endif - -#if SIZEOF_SHORT == 4 -typedef signed short PaInt32; -typedef unsigned short PaUint32; -#elif SIZEOF_INT == 4 -typedef signed int PaInt32; -typedef unsigned int PaUint32; -#elif SIZEOF_LONG == 4 -typedef signed long PaInt32; -typedef unsigned long PaUint32; -#else -#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform -#endif - - -/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to - ensure that PortAudio was configured correctly, and raises an assertion if - they don't match the expected values. <assert.h> must be included in the - context in which this macro is used. -*/ -#define PA_VALIDATE_TYPE_SIZES \ - { \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \ - } - - -#endif /* PA_TYPES_H */ diff --git a/portaudio/pa_common/pa_util.h b/portaudio/pa_common/pa_util.h deleted file mode 100644 index d20badd28..000000000 --- a/portaudio/pa_common/pa_util.h +++ /dev/null @@ -1,167 +0,0 @@ -#ifndef PA_UTIL_H -#define PA_UTIL_H -/* - * $Id: pa_util.h,v 1.1.2.13 2005/11/09 06:31:42 aknudsen Exp $ - * Portable Audio I/O Library implementation utilities header - * common implementation utilities and interfaces - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Prototypes for utility functions used by PortAudio implementations. - - @todo Document and adhere to the alignment guarantees provided by - PaUtil_AllocateMemory(). -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -struct PaUtilHostApiRepresentation; - - -/** Retrieve a specific host API representation. This function can be used - by implementations to retrieve a pointer to their representation in - host api specific extension functions which aren't passed a rep pointer - by pa_front.c. - - @param hostApi A pointer to a host API represenation pointer. Apon success - this will receive the requested representation pointer. - - @param type A valid host API type identifier. - - @returns An error code. If the result is PaNoError then a pointer to the - requested host API representation will be stored in *hostApi. If the host API - specified by type is not found, this function returns paHostApiNotFound. -*/ -PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, - PaHostApiTypeId type ); - - -/** Convert a PortAudio device index into a host API specific device index. - @param hostApiDevice Pointer to a device index, on success this will recieve the - converted device index value. - @param device The PortAudio device index to convert. - @param hostApi The host api which the index should be converted for. - - @returns On success returns PaNoError and places the converted index in the - hostApiDevice parameter. -*/ -PaError PaUtil_DeviceIndexToHostApiDeviceIndex( - PaDeviceIndex *hostApiDevice, PaDeviceIndex device, - struct PaUtilHostApiRepresentation *hostApi ); - - -/** Set the host error information returned by Pa_GetLastHostErrorInfo. This - function and the paUnanticipatedHostError error code should be used as a - last resort. Implementors should use existing PA error codes where possible, - or nominate new ones. Note that at it is always better to use - PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an - ambiguous or inaccurate PaError code. - - @param hostApiType The host API which encountered the error (ie of the caller) - - @param errorCode The error code returned by the native API function. - - @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo - makes a copy of the string, so it is not necessary for the pointer to remain - valid after the call to PaUtil_SetLastHostErrorInfo() returns. - -*/ -void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, - const char *errorText ); - - - -/** PA_DEBUG() provides a simple debug message printing facility. The macro - passes it's argument to a printf-like function called PaUtil_DebugPrint() - which prints to stderr and always flushes the stream after printing. - Because preprocessor macros cannot directly accept variable length argument - lists, calls to the macro must include an additional set of parenthesis, eg: - PA_DEBUG(("errorno: %d", 1001 )); -*/ - -void PaUtil_DebugPrint( const char *format, ... ); - -#ifdef PA_ENABLE_DEBUG_OUTPUT -#define PA_DEBUG(x) PaUtil_DebugPrint x ; -#else -#define PA_DEBUG(x) -#endif - - -/* the following functions are implemented in a platform platform specific - .c file -*/ - -/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */ -void *PaUtil_AllocateMemory( long size ); - - -/** Realease block if non-NULL. block may be NULL */ -void PaUtil_FreeMemory( void *block ); - - -/** Return the number of currently allocated blocks. This function can be - used for detecting memory leaks. - - @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If - it isn't, this function will always return 0. -*/ -int PaUtil_CountCurrentlyAllocatedBlocks( void ); - - -/** Initialize the clock used by PaUtil_GetTime(). Call this before calling - PaUtil_GetTime. - - @see PaUtil_GetTime -*/ -void PaUtil_InitializeClock( void ); - - -/** Return the system time in seconds. Used to implement CPU load functions - - @see PaUtil_InitializeClock -*/ -double PaUtil_GetTime( void ); - - -/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */ - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_UTIL_H */ diff --git a/portaudio/pa_common/portaudio.h b/portaudio/pa_common/portaudio.h deleted file mode 100644 index c5967b0bd..000000000 --- a/portaudio/pa_common/portaudio.h +++ /dev/null @@ -1,1124 +0,0 @@ - -#ifndef PORTAUDIO_H -#define PORTAUDIO_H -/* - * $Id: portaudio.h,v 1.5.2.53 2006/03/20 17:49:38 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * PortAudio API Header File - * Latest version available at: http://www.portaudio.com/ - * - * Copyright (c) 1999-2002 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief The PortAudio API. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** Retrieve the release number of the currently running PortAudio build, - eg 1900. -*/ -int Pa_GetVersion( void ); - - -/** Retrieve a textual description of the current PortAudio build, - eg "PortAudio V19-devel 13 October 2002". -*/ -const char* Pa_GetVersionText( void ); - - -/** Error codes returned by PortAudio functions. - Note that with the exception of paNoError, all PaErrorCodes are negative. -*/ - -typedef int PaError; -typedef enum PaErrorCode -{ - paNoError = 0, - - paNotInitialized = -10000, - paUnanticipatedHostError, - paInvalidChannelCount, - paInvalidSampleRate, - paInvalidDevice, - paInvalidFlag, - paSampleFormatNotSupported, - paBadIODeviceCombination, - paInsufficientMemory, - paBufferTooBig, - paBufferTooSmall, - paNullCallback, - paBadStreamPtr, - paTimedOut, - paInternalError, - paDeviceUnavailable, - paIncompatibleHostApiSpecificStreamInfo, - paStreamIsStopped, - paStreamIsNotStopped, - paInputOverflowed, - paOutputUnderflowed, - paHostApiNotFound, - paInvalidHostApi, - paCanNotReadFromACallbackStream, /**< @todo review error code name */ - paCanNotWriteToACallbackStream, /**< @todo review error code name */ - paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */ - paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */ - paIncompatibleStreamHostApi, - paBadBufferPtr -} PaErrorCode; - - -/** Translate the supplied PortAudio error code into a human readable - message. -*/ -const char *Pa_GetErrorText( PaError errorCode ); - - -/** Library initialization function - call this before using PortAudio. - This function initialises internal data structures and prepares underlying - host APIs for use. This function MUST be called before using any other - PortAudio API functions. - - If Pa_Initialize() is called multiple times, each successful - call must be matched with a corresponding call to Pa_Terminate(). - Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not - required to be fully nested. - - Note that if Pa_Initialize() returns an error code, Pa_Terminate() should - NOT be called. - - @return paNoError if successful, otherwise an error code indicating the cause - of failure. - - @see Pa_Terminate -*/ -PaError Pa_Initialize( void ); - - -/** Library termination function - call this when finished using PortAudio. - This function deallocates all resources allocated by PortAudio since it was - initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has - been called multiple times, each call must be matched with a corresponding call - to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically - close any PortAudio streams that are still open. - - Pa_Terminate() MUST be called before exiting a program which uses PortAudio. - Failure to do so may result in serious resource leaks, such as audio devices - not being available until the next reboot. - - @return paNoError if successful, otherwise an error code indicating the cause - of failure. - - @see Pa_Initialize -*/ -PaError Pa_Terminate( void ); - - - -/** The type used to refer to audio devices. Values of this type usually - range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice - and paUseHostApiSpecificDeviceSpecification values. - - @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification -*/ -typedef int PaDeviceIndex; - - -/** A special PaDeviceIndex value indicating that no device is available, - or should be used. - - @see PaDeviceIndex -*/ -#define paNoDevice ((PaDeviceIndex)-1) - - -/** A special PaDeviceIndex value indicating that the device(s) to be used - are specified in the host api specific stream info structure. - - @see PaDeviceIndex -*/ -#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2) - - -/* Host API enumeration mechanism */ - -/** The type used to enumerate to host APIs at runtime. Values of this type - range from 0 to (Pa_GetHostApiCount()-1). - - @see Pa_GetHostApiCount -*/ -typedef int PaHostApiIndex; - - -/** Retrieve the number of available host APIs. Even if a host API is - available it may have no devices available. - - @return A non-negative value indicating the number of available host APIs - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaHostApiIndex -*/ -PaHostApiIndex Pa_GetHostApiCount( void ); - - -/** Retrieve the index of the default host API. The default host API will be - the lowest common denominator host API on the current platform and is - unlikely to provide the best performance. - - @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1) - indicating the default host API index or, a PaErrorCode (which are always - negative) if PortAudio is not initialized or an error is encountered. -*/ -PaHostApiIndex Pa_GetDefaultHostApi( void ); - - -/** Unchanging unique identifiers for each supported host API. This type - is used in the PaHostApiInfo structure. The values are guaranteed to be - unique and to never change, thus allowing code to be written that - conditionally uses host API specific extensions. - - New type ids will be allocated when support for a host API reaches - "public alpha" status, prior to that developers should use the - paInDevelopment type id. - - @see PaHostApiInfo -*/ -typedef enum PaHostApiTypeId -{ - paInDevelopment=0, /* use while developing support for a new host API */ - paDirectSound=1, - paMME=2, - paASIO=3, - paSoundManager=4, - paCoreAudio=5, - paOSS=7, - paALSA=8, - paAL=9, - paBeOS=10, - paWDMKS=11, - paJACK=12 -} PaHostApiTypeId; - - -/** A structure containing information about a particular host API. */ - -typedef struct PaHostApiInfo -{ - /** this is struct version 1 */ - int structVersion; - /** The well known unique identifier of this host API @see PaHostApiTypeId */ - PaHostApiTypeId type; - /** A textual description of the host API for display on user interfaces. */ - const char *name; - - /** The number of devices belonging to this host API. This field may be - used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate - all devices for this host API. - @see Pa_HostApiDeviceIndexToDeviceIndex - */ - int deviceCount; - - /** The default input device for this host API. The value will be a - device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice - if no default input device is available. - */ - PaDeviceIndex defaultInputDevice; - - /** The default output device for this host API. The value will be a - device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice - if no default output device is available. - */ - PaDeviceIndex defaultOutputDevice; - -} PaHostApiInfo; - - -/** Retrieve a pointer to a structure containing information about a specific - host Api. - - @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) - - @return A pointer to an immutable PaHostApiInfo structure describing - a specific host API. If the hostApi parameter is out of range or an error - is encountered, the function returns NULL. - - The returned structure is owned by the PortAudio implementation and must not - be manipulated or freed. The pointer is only guaranteed to be valid between - calls to Pa_Initialize() and Pa_Terminate(). -*/ -const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi ); - - -/** Convert a static host API unique identifier, into a runtime - host API index. - - @param type A unique host API identifier belonging to the PaHostApiTypeId - enumeration. - - @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or, - a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - The paHostApiNotFound error code indicates that the host API specified by the - type parameter is not available. - - @see PaHostApiTypeId -*/ -PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ); - - -/** Convert a host-API-specific device index to standard PortAudio device index. - This function may be used in conjunction with the deviceCount field of - PaHostApiInfo to enumerate all devices for the specified host API. - - @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) - - @param hostApiDeviceIndex A valid per-host device index in the range - 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) - - @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1) - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - A paInvalidHostApi error code indicates that the host API index specified by - the hostApi parameter is out of range. - - A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter - is out of range. - - @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. The number of available devices - may be zero. - - @return A non-negative value indicating the number of available devices or, - a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. -*/ -PaDeviceIndex Pa_GetDeviceCount( 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 default 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. -<pre> - set PA_RECOMMENDED_OUTPUT_DEVICE=1 -</pre> - 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; - - -/** A type used to specify one or more sample formats. Each value indicates - a possible format for sound data passed to and from the stream callback, - Pa_ReadStream and Pa_WriteStream. - - The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 - and aUInt8 are usually implemented by all implementations. - - The floating point representation (paFloat32) uses +1.0 and -1.0 as the - maximum and minimum respectively. - - paUInt8 is an unsigned 8 bit format where 128 is considered "ground" - - 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; - - -#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; - - /* 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; - - 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_GetDeviceCount()-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 ); - - -/** Parameters for one direction (input or output) of a stream. -*/ -typedef struct PaStreamParameters -{ - /** A valid device index in the range 0 to (Pa_GetDeviceCount()-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. - */ - 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 - should round the suggestedLatency up to the next practial value - ie to - provide an equal or higher latency than suggestedLatency wherever 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 NULL. - */ - 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 - - @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 ); - - - -/* 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; - - -/** 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) - -/** Flag requests that where possible a full duplex stream will not discard - overflowed input samples without calling the stream callback. This flag is - only valid for full duplex callback streams and only when used in combination - with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using - this flag incorrectly results in a paInvalidFlag error being returned from - Pa_OpenStream and Pa_OpenDefaultStream. - - @see PaStreamFlags, paFramesPerBufferUnspecified -*/ -#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; - -/** In a stream opened with paFramesPerBufferUnspecified, indicates that - input data is all silence (zeros) because no real data is available. In a - stream opened without paFramesPerBufferUnspecified, it indicates that one or - more zero samples have been inserted into the input buffer to compensate - for an input underflow. - @see PaStreamCallbackFlags -*/ -#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) - -/** In a stream opened with paFramesPerBufferUnspecified, indicates that data - prior to the first sample of the input buffer was discarded due to an - overflow, possibly because the stream callback is using too much CPU time. - Otherwise indicates that data prior to one or more samples in the - input buffer was discarded. - @see PaStreamCallbackFlags -*/ -#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) - -/** Indicates that output data (or a gap) was inserted, possibly because the - stream callback is using too much CPU time. - @see PaStreamCallbackFlags -*/ -#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) - -/** Indicates that 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 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( - const 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. - - @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. - - @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. This parameter is ignored if streamCallback - is NULL. - - @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( PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); - - -/** 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. - - @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. - - @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 - - @see Pa_OpenStream, PaStreamCallback -*/ -PaError Pa_OpenDefaultStream( PaStream** stream, - int numInputChannels, - int numOutputChannels, - PaSampleFormat sampleFormat, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamCallback *streamCallback, - void *userData ); - - -/** 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 ); - - -/** 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 ); - - -/** Determine whether the stream is stopped. - 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. - - @return Returns one (1) when the stream is stopped, zero (0) when - the stream is running or, a PaErrorCode (which are always negative) if - PortAudio is not initialized or an error is encountered. - - @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive -*/ -PaError Pa_IsStreamStopped( PaStream *stream ); - - -/** Determine whether the stream is active. - 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. - - @return Returns one (1) when the stream is active (ie playing or recording - audio), zero (0) when not playing or, a PaErrorCode (which are always negative) - if PortAudio is not initialized or an error is encountered. - - @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped -*/ -PaError Pa_IsStreamActive( PaStream *stream ); - - - -/** A structure containing unchanging information about an open stream. - @see Pa_GetStreamInfo -*/ - -typedef struct PaStreamInfo -{ - /** this is struct version 1 */ - int structVersion; - - /** 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 ); - - -/** Determine the current time for the stream according to the same clock used - to generate buffer timestamps. This time may be used for syncronising other - events to the audio stream, for example synchronizing audio to MIDI. - - @return The stream's current time in seconds, or 0 if an error occurred. - - @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 - 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, or if an error occurrs. -*/ -double Pa_GetStreamCpuLoad( PaStream* stream ); - - -/** 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 stream A pointer to an open stream previously created with Pa_OpenStream. - - @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 stream A pointer to an open stream previously created with Pa_OpenStream. - - @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, - const void *buffer, - unsigned long frames ); - - -/** Retrieve the number of frames that can be read from the stream without - waiting. - - @return Returns a non-negative value representing the maximum number of frames - that can be read from the stream without blocking or busy waiting or, a - PaErrorCode (which are always negative) if PortAudio is not initialized or an - error is encountered. -*/ -signed long Pa_GetStreamReadAvailable( PaStream* stream ); - - -/** Retrieve the number of frames that can be written to the stream without - waiting. - - @return Returns a non-negative value representing the maximum number of frames - that can be written to the stream without blocking or busy waiting or, a - PaErrorCode (which are always negative) if PortAudio is not initialized or an - error is encountered. -*/ -signed long Pa_GetStreamWriteAvailable( PaStream* stream ); - - -/* Miscellaneous utilities */ - - -/** Retrieve the size of a given sample format in bytes. - - @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 ); - - -/** Put the caller to sleep for at least 'msec' milliseconds. This function is - provided only as a convenience for authors of portable code (such as the tests - and examples in the PortAudio distribution.) - - The function may sleep longer than requested so don't rely on this for accurate - musical timing. -*/ -void Pa_Sleep( long msec ); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PORTAUDIO_H */ diff --git a/portaudio/pa_dll_switch/CVS/Entries b/portaudio/pa_dll_switch/CVS/Entries deleted file mode 100644 index e971ac482..000000000 --- a/portaudio/pa_dll_switch/CVS/Entries +++ /dev/null @@ -1,6 +0,0 @@ -/PaDllEntry.h/1.1.1.1/Tue Jan 22 00:52:13 2002//Tv19-devel -/letter_from_tim_010817.txt/1.1.1.1/Tue Jan 22 00:52:12 2002//Tv19-devel -/loadPA_DLL.cpp/1.1.1.1/Tue Jan 22 00:52:16 2002//Tv19-devel -/pa_lib.c/1.1.1.1/Tue Jan 22 00:52:16 2002//Tv19-devel -/portaudio.h/1.1.1.1/Tue Jan 22 00:52:14 2002//Tv19-devel -D diff --git a/portaudio/pa_dll_switch/CVS/Repository b/portaudio/pa_dll_switch/CVS/Repository deleted file mode 100644 index c2647c860..000000000 --- a/portaudio/pa_dll_switch/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_dll_switch diff --git a/portaudio/pa_dll_switch/CVS/Root b/portaudio/pa_dll_switch/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_dll_switch/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_dll_switch/CVS/Tag b/portaudio/pa_dll_switch/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_dll_switch/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_dll_switch/PaDllEntry.h b/portaudio/pa_dll_switch/PaDllEntry.h deleted file mode 100644 index e070054b3..000000000 --- a/portaudio/pa_dll_switch/PaDllEntry.h +++ /dev/null @@ -1,184 +0,0 @@ - -/* - * 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/portaudio/pa_dll_switch/letter_from_tim_010817.txt b/portaudio/pa_dll_switch/letter_from_tim_010817.txt deleted file mode 100644 index a535cd1dae6afccc58d304b141166f7f7600dbd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1176 zcmZuxTT9$P6z=o<ii1#8>V{oit8R;E*@}X#()!Rhna!MR#+}TBxv1M;-|u9#rL+*p zkaPPkXL1okH>0gP+tGy!ZREH`ADhkT>}T(^=gRP@x(mp<ZLOnEC9aUu<GjG}EQKbg zwa;x;hwfB;FN+7R^Nhanj8a)HIb|C>ji@Uw+ukuPbiHD4lA7y`q>E^cUhmO)S(cP( za<oVL<MD)!X47Lj|Dx2TC7HaZRLhacCVFahrYkbm%#@l`IM%K9OE#2Sre5DWyQpf? z-cTQ6*2UORwPZboXgE?1)W`5>4U;D09O>SYBC-uVX`k6BwO>hkw??RUw$=lQD{8et zNgaw12@N&H*F&fe7KJg9;l_F(*-6Z#__^lTXL3QC+w=SR^>tPE{Vs5V!IHB8wvisS zD-2rf2L=KOG^I$e98{$7B_KCaV;!eWeMvl|--6@0a2&nDNdr(3<5tg|`|NXELjYxc z?aI*eiugH4U4!h<<8OczN)~$HpF)W>;{}!5&_n;cok$fAAybCLdI2d?=(r6}u%aO! zGBwPOng|J?wN9%_sk^~F4F|PDw?9yu5aA!!E9;kH!Q4h{&<{~SKWgh_fOxFCp|y$( zH2|#0GBiMsL_Cx6_${1?QoKhC5V(?jTMrB&H@5KW9i?^3+@T^vjzpbJN#=@HuaHny z(UJHsUkw!SrV>vE5gueq2fQ+UqXzN-=EKb{-E4|BUSTOkU5qh-li!Bjj5g~~2A)G> zfzG%axC@39q!9Ub=9?HcB+(1s1y*m^kk{-DNedzRK42B3?aGHIOw@U|QkUY^a7$z| zRF-|yehol=sT3%{L8Y?olp2lXO)ocJ{qqM<>i5s!kzMFJ_F1*TK}+jxLN7`=R&;PO un(nK~{^b3H795@RxK>T@GkP~YsSd`~@!^!lCp0;%#uCxb|A`!`H@^T5#E2*W diff --git a/portaudio/pa_dll_switch/loadPA_DLL.cpp b/portaudio/pa_dll_switch/loadPA_DLL.cpp deleted file mode 100644 index 043eda878..000000000 --- a/portaudio/pa_dll_switch/loadPA_DLL.cpp +++ /dev/null @@ -1,203 +0,0 @@ -////////////////////////////////////////////////////////////////////////// - - -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/portaudio/pa_dll_switch/pa_lib.c b/portaudio/pa_dll_switch/pa_lib.c deleted file mode 100644 index 86601592c..000000000 --- a/portaudio/pa_dll_switch/pa_lib.c +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Portable Audio I/O Library - * Host Independant Layer - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/* Modification History: - PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifdef _WIN32 -#ifndef __MWERKS__ -#include <memory.h> -#endif /* __MWERKS__ */ -#else /* !_WIN32 */ -#include <memory.h> -#endif /* _WIN32 */ - -#include "portaudio.h" -#include "pa_host.h" -#include "pa_trace.h" - -/* The reason we might NOT want to validate the rate before opening the stream - * is because many DirectSound drivers lie about the rates they actually support. - */ -#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */ - -/* -O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion -*/ - -#ifndef FALSE - #define FALSE (0) - #define TRUE (!FALSE) -#endif - -#define PRINT(x) { printf x; fflush(stdout); } -#define ERR_RPT(x) PRINT(x) -#define DBUG(x) /* PRINT(x) */ -#define DBUGX(x) /* PRINT(x) */ - -static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */ - -static PaError Pa_KillStream( PortAudioStream *stream, int abort ); - -/***********************************************************************/ -int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate ) -{ - double err, minErr = allowableError; - int i, bestFit = -1; - - for( i=0; i<numRates; i++ ) - { - err = fabs( frameRate - rateTable[i] ); - if( err < minErr ) - { - minErr = err; - bestFit = i; - } - } - return bestFit; -} - -/************************************************************************** -** Make sure sample rate is legal and also convert to enumeration for driver. -*/ -PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate, - double *closestFrameRatePtr ) -{ - long bestRateIndex; - const PaDeviceInfo *pdi; - pdi = Pa_GetDeviceInfo( id ); - if( pdi == NULL ) return paInvalidDeviceId; - - if( pdi->numSampleRates == -1 ) - { - /* Is it out of range? */ - if( (requestedFrameRate < pdi->sampleRates[0]) || - (requestedFrameRate > pdi->sampleRates[1]) ) - { - return paInvalidSampleRate; - } - - *closestFrameRatePtr = requestedFrameRate; - } - else - { - bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate ); - if( bestRateIndex < 0 ) return paInvalidSampleRate; - *closestFrameRatePtr = pdi->sampleRates[bestRateIndex]; - } - return paNoError; -} - -/*************************************************************************/ -DLL_API PaError Pa_OpenStream( - PortAudioStream** streamPtrPtr, - PaDeviceID inputDeviceID, - int numInputChannels, - PaSampleFormat inputSampleFormat, - void *inputDriverInfo, - PaDeviceID outputDeviceID, - int numOutputChannels, - PaSampleFormat outputSampleFormat, - void *outputDriverInfo, - double sampleRate, - unsigned long framesPerBuffer, - unsigned long numberOfBuffers, - unsigned long streamFlags, - PortAudioCallback *callback, - void *userData ) -{ - internalPortAudioStream *past = NULL; - PaError result = paNoError; - int bitsPerInputSample; - int bitsPerOutputSample; - /* Print passed parameters. */ - DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n", - streamPtrPtr, inputDeviceID, numInputChannels, - inputSampleFormat, inputDriverInfo )); - DBUG((" %d, %d, %d, %p, /* output */\n", - outputDeviceID, numOutputChannels, - outputSampleFormat, outputDriverInfo )); - DBUG((" %g, %d, %d, 0x%x, , %p )\n", - sampleRate, framesPerBuffer, numberOfBuffers, - streamFlags, userData )); - - /* Check for parameter errors. */ - if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag; - if( streamPtrPtr == NULL ) return paBadStreamPtr; - if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */ - if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */ - if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId; - if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId; - if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount; - -#if SUPPORT_AUDIO_CAPTURE - if( inputDeviceID >= 0 ) - { - PaError size = Pa_GetSampleSize( inputSampleFormat ); - if( size < 0 ) return size; - bitsPerInputSample = 8 * size; - if( (numInputChannels <= 0) ) return paInvalidChannelCount; - } -#else - if( inputDeviceID >= 0 ) - { - return paInvalidChannelCount; - } -#endif /* SUPPORT_AUDIO_CAPTURE */ - else - { - if( numInputChannels > 0 ) return paInvalidChannelCount; - bitsPerInputSample = 0; - } - - if( outputDeviceID >= 0 ) - { - PaError size = Pa_GetSampleSize( outputSampleFormat ); - if( size < 0 ) return size; - bitsPerOutputSample = 8 * size; - if( (numOutputChannels <= 0) ) return paInvalidChannelCount; - } - else - { - if( numOutputChannels > 0 ) return paInvalidChannelCount; - bitsPerOutputSample = 0; - } - - if( callback == NULL ) return paNullCallback; - - /* Allocate and clear stream structure. */ - past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) ); - if( past == NULL ) return paInsufficientMemory; - memset( past, 0, sizeof(internalPortAudioStream) ); - AddTraceMessage("Pa_OpenStream: past", (long) past ); - - past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */ - past->past_FramesPerUserBuffer = framesPerBuffer; - past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */ - past->past_Callback = callback; - past->past_UserData = userData; - past->past_OutputSampleFormat = outputSampleFormat; - past->past_InputSampleFormat = inputSampleFormat; - past->past_OutputDeviceID = outputDeviceID; - past->past_InputDeviceID = inputDeviceID; - past->past_NumInputChannels = numInputChannels; - past->past_NumOutputChannels = numOutputChannels; - past->past_Flags = streamFlags; - - /* Check for absurd sample rates. */ - if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) - { - result = paInvalidSampleRate; - goto cleanup; - } - - /* Allocate buffers that may be used for format conversion from user to native buffers. */ - if( numInputChannels > 0 ) - { - -#if PA_VALIDATE_RATE - result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate ); - if( result < 0 ) - { - goto cleanup; - } -#else - past->past_SampleRate = sampleRate; -#endif - /* Allocate single Input buffer. */ - past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8); - past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize); - if( past->past_InputBuffer == NULL ) - { - result = paInsufficientMemory; - goto cleanup; - } - } - else - { - past->past_InputBuffer = NULL; - } - - /* Allocate single Output buffer. */ - if( numOutputChannels > 0 ) - { -#if PA_VALIDATE_RATE - result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate ); - if( result < 0 ) - { - goto cleanup; - } -#else - past->past_SampleRate = sampleRate; -#endif - past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8); - past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize); - if( past->past_OutputBuffer == NULL ) - { - result = paInsufficientMemory; - goto cleanup; - } - } - else - { - past->past_OutputBuffer = NULL; - } - - result = PaHost_OpenStream( past ); - if( result < 0 ) goto cleanup; - - *streamPtrPtr = (void *) past; - - return result; - -cleanup: - if( past != NULL ) Pa_CloseStream( past ); - *streamPtrPtr = NULL; - return result; -} - - -/*************************************************************************/ -DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream, - int numInputChannels, - int numOutputChannels, - PaSampleFormat sampleFormat, - double sampleRate, - unsigned long framesPerBuffer, - unsigned long numberOfBuffers, - PortAudioCallback *callback, - void *userData ) -{ - return Pa_OpenStream( - stream, - ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice), - numInputChannels, sampleFormat, NULL, - ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), - numOutputChannels, sampleFormat, NULL, - sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData ); -} - -/*************************************************************************/ -DLL_API PaError Pa_CloseStream( PortAudioStream* stream) -{ - PaError result; - internalPortAudioStream *past; - - DBUG(("Pa_CloseStream()\n")); - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - Pa_AbortStream( past ); - result = PaHost_CloseStream( past ); - - if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize ); - if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize ); - PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) ); - - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StartStream( PortAudioStream *stream ) -{ - PaError result = paHostError; - internalPortAudioStream *past; - - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - past->past_FrameCount = 0.0; - - if( past->past_NumInputChannels > 0 ) - { - result = PaHost_StartInput( past ); - DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - if( past->past_NumOutputChannels > 0 ) - { - result = PaHost_StartOutput( past ); - DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - result = PaHost_StartEngine( past ); - DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - - return paNoError; - -error: - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StopStream( PortAudioStream *stream ) -{ - return Pa_KillStream( stream, 0 ); -} - -/*************************************************************************/ -DLL_API PaError Pa_AbortStream( PortAudioStream *stream ) -{ - return Pa_KillStream( stream, 1 ); -} - -/*************************************************************************/ -static PaError Pa_KillStream( PortAudioStream *stream, int abort ) -{ - PaError result = paNoError; - internalPortAudioStream *past; - - DBUG(("Pa_StopStream().\n")); - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) ) - { - result = PaHost_StopEngine( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - if( past->past_NumInputChannels > 0 ) - { - result = PaHost_StopInput( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result)); - if( result != paNoError ) goto error; - } - - if( past->past_NumOutputChannels > 0 ) - { - result = PaHost_StopOutput( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result)); - if( result != paNoError ) goto error; - } - -error: - past->past_Usage = 0; - past->past_IfLastExitValid = 0; - - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StreamActive( PortAudioStream *stream ) -{ - internalPortAudioStream *past; - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - return PaHost_StreamActive( past ); -} - -/*************************************************************************/ -DLL_API const char *Pa_GetErrorText( PaError errnum ) -{ - const char *msg; - - switch(errnum) - { - case paNoError: msg = "Success"; break; - case paHostError: msg = "Host error."; break; - case paInvalidChannelCount: msg = "Invalid number of channels."; break; - case paInvalidSampleRate: msg = "Invalid sample rate."; break; - case paInvalidDeviceId: msg = "Invalid device ID."; break; - case paInvalidFlag: msg = "Invalid flag."; break; - case paSampleFormatNotSupported: msg = "Sample format not supported"; break; - case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break; - case paInsufficientMemory: msg = "Insufficient memory."; break; - case paBufferTooBig: msg = "Buffer too big."; break; - case paBufferTooSmall: msg = "Buffer too small."; break; - case paNullCallback: msg = "No callback routine specified."; break; - case paBadStreamPtr: msg = "Invalid stream pointer."; break; - case paTimedOut : msg = "Wait Timed Out."; break; - case paInternalError: msg = "Internal PortAudio Error."; break; - default: msg = "Illegal error number."; break; - } - return msg; -} - -/* - Get CPU Load as a fraction of total CPU time. - A value of 0.5 would imply that PortAudio and the sound generating - callback was consuming roughly 50% of the available CPU time. - The amount may vary depending on CPU load. - This function may be called from the callback function. -*/ -DLL_API double Pa_GetCPULoad( PortAudioStream* stream) -{ - internalPortAudioStream *past; - if( stream == NULL ) return (double) paBadStreamPtr; - past = (internalPortAudioStream *) stream; - return past->past_Usage; -} - -/************************************************************* -** Calculate 2 LSB dither signal with a triangular distribution. -** Ranged properly for adding to a 32 bit integer prior to >>15. -*/ -#define DITHER_BITS (15) -#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1)) -static long Pa_TriangularDither( void ) -{ - static unsigned long previous = 0; - static unsigned long randSeed1 = 22222; - static unsigned long randSeed2 = 5555555; - long current, highPass; - /* Generate two random numbers. */ - randSeed1 = (randSeed1 * 196314165) + 907633515; - randSeed2 = (randSeed2 * 196314165) + 907633515; - /* Generate triangular distribution about 0. */ - current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); - /* High pass filter to reduce audibility. */ - highPass = current - previous; - previous = current; - return highPass; -} - -/************************************************************************* -** Called by host code. -** Convert input from Int16, call user code, then convert output -** to Int16 format for native use. -** Assumes host native format is paInt16. -** Returns result from user callback. -*/ -long Pa_CallConvertInt16( internalPortAudioStream *past, - short *nativeInputBuffer, - short *nativeOutputBuffer ) -{ - long temp; - long bytesEmpty = 0; - long bytesFilled = 0; - int userResult; - unsigned int i; - void *inputBuffer = NULL; - void *outputBuffer = NULL; - -#if SUPPORT_AUDIO_CAPTURE - /* Get native data from DirectSound. */ - if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) - { - /* Convert from native format to PA format. */ - unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels; - switch(past->past_InputSampleFormat) - { - - case paFloat32: - { - float *inBufPtr = (float *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - for( i=0; i<samplesPerBuffer; i++ ) - { - inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f); - } - break; - } - - case paInt32: - { - /* Convert 16 bit data to 32 bit integers */ - int *inBufPtr = (int *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - for( i=0; i<samplesPerBuffer; i++ ) - { - inBufPtr[i] = nativeInputBuffer[i] << 16; - } - break; - } - - case paInt16: - { - /* Already in correct format so don't copy. */ - inputBuffer = nativeInputBuffer; - break; - } - - case paInt8: - { - /* Convert 16 bit data to 8 bit chars */ - char *inBufPtr = (char *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i<samplesPerBuffer; i++ ) - { - inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8); - } - } - else - { - for( i=0; i<samplesPerBuffer; i++ ) - { - temp = nativeInputBuffer[i]; - temp += Pa_TriangularDither() >> 7; - temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - inBufPtr[i] = (char)(temp >> 8); - } - } - break; - } - - case paUInt8: - { - /* Convert 16 bit data to 8 bit unsigned chars */ - unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i<samplesPerBuffer; i++ ) - { - inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80; - } - } - else - { - /* If you dither then you have to clip because dithering could push the signal out of range! */ - for( i=0; i<samplesPerBuffer; i++ ) - { - temp = nativeInputBuffer[i]; - temp += Pa_TriangularDither() >> 7; - temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - inBufPtr[i] = (unsigned char)(temp + 0x80); - } - } - break; - } - - default: - break; - } - } -#endif /* SUPPORT_AUDIO_CAPTURE */ - - /* Are we doing output time? */ - if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) - { - /* May already be in native format so just write directly to native buffer. */ - outputBuffer = (past->past_OutputSampleFormat == paInt16) ? - nativeOutputBuffer : past->past_OutputBuffer; - } - /* - AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); - AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); - */ - /* Call user callback routine. */ - userResult = past->past_Callback( - inputBuffer, - outputBuffer, - past->past_FramesPerUserBuffer, - past->past_FrameCount, - past->past_UserData ); - - past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; - - /* Convert to native format if necessary. */ - if( outputBuffer != NULL ) - { - unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels; - switch(past->past_OutputSampleFormat) - { - case paFloat32: - { - float *outBufPtr = (float *) past->past_OutputBuffer; - if( past->past_Flags & paDitherOff ) - { - if( past->past_Flags & paClipOff ) /* NOTHING */ - { - for( i=0; i<samplesPerBuffer; i++ ) - { - *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f)); - } - } - else /* CLIP */ - { - for( i=0; i<samplesPerBuffer; i++ ) - { - temp = (long)(outBufPtr[i] * 32767.0f); - *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - } - } - } - else - { - /* If you dither then you have to clip because dithering could push the signal out of range! */ - for( i=0; i<samplesPerBuffer; i++ ) - { - float dither = Pa_TriangularDither()*DITHER_SCALE; - float dithered = (outBufPtr[i] * (32767.0f)) + dither; - temp = (long) (dithered); - *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - } - } - break; - } - - case paInt32: - { - int *outBufPtr = (int *) past->past_OutputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i<samplesPerBuffer; i++ ) - { - *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 ); - } - } - else - { - for( i=0; i<samplesPerBuffer; i++ ) - { - /* Shift one bit down before dithering so that we have room for overflow from add. */ - temp = (outBufPtr[i] >> 1) + Pa_TriangularDither(); - temp = temp >> 15; - *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - } - } - break; - } - - case paInt8: - { - char *outBufPtr = (char *) past->past_OutputBuffer; - for( i=0; i<samplesPerBuffer; i++ ) - { - *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8; - } - break; - } - - case paUInt8: - { - unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer; - for( i=0; i<samplesPerBuffer; i++ ) - { - *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8; - } - break; - } - - default: - break; - } - - } - - return userResult; -} - -/************************************************************************* -** Called by host code. -** Convert input from Float32, call user code, then convert output -** to Float32 format for native use. -** Assumes host native format is Float32. -** Returns result from user callback. -** FIXME - Unimplemented for formats other than paFloat32!!!! -*/ -long Pa_CallConvertFloat32( internalPortAudioStream *past, - float *nativeInputBuffer, - float *nativeOutputBuffer ) -{ - long bytesEmpty = 0; - long bytesFilled = 0; - int userResult; - void *inputBuffer = NULL; - void *outputBuffer = NULL; - - /* Get native data from DirectSound. */ - if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) - { - inputBuffer = nativeInputBuffer; // FIXME - } - - /* Are we doing output time? */ - if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) - { - /* May already be in native format so just write directly to native buffer. */ - outputBuffer = (past->past_OutputSampleFormat == paFloat32) ? - nativeOutputBuffer : past->past_OutputBuffer; - } - /* - AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); - AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); - */ - /* Call user callback routine. */ - userResult = past->past_Callback( - inputBuffer, - outputBuffer, - past->past_FramesPerUserBuffer, - past->past_FrameCount, - past->past_UserData ); - - past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; - - /* Convert to native format if necessary. */ // FIXME - return userResult; -} - -/*************************************************************************/ -DLL_API PaError Pa_Initialize( void ) -{ - if( gInitCount++ > 0 ) return paNoError; - ResetTraceMessages(); - return PaHost_Init(); -} - -DLL_API PaError Pa_Terminate( void ) -{ - PaError result = paNoError; - - if( gInitCount == 0 ) return paNoError; - else if( --gInitCount == 0 ) - { - result = PaHost_Term(); - DumpTraceMessages(); - } - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_GetSampleSize( PaSampleFormat format ) -{ - int size; - switch(format ) - { - - case paUInt8: - case paInt8: - size = 1; - break; - - case paInt16: - size = 2; - break; - - case paPackedInt24: - size = 3; - break; - - case paFloat32: - case paInt32: - case paInt24: - size = 4; - break; - - default: - size = paSampleFormatNotSupported; - break; - } - return (PaError) size; -} - - diff --git a/portaudio/pa_dll_switch/portaudio.h b/portaudio/pa_dll_switch/portaudio.h deleted file mode 100644 index 9632521eb..000000000 --- a/portaudio/pa_dll_switch/portaudio.h +++ /dev/null @@ -1,439 +0,0 @@ -#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/portaudio/pa_jack/CVS/Entries b/portaudio/pa_jack/CVS/Entries deleted file mode 100644 index 69d827fc4..000000000 --- a/portaudio/pa_jack/CVS/Entries +++ /dev/null @@ -1,2 +0,0 @@ -/pa_jack.c/1.1.2.20/Sun Oct 2 22:02:26 2005//Tv19-devel -D diff --git a/portaudio/pa_jack/CVS/Repository b/portaudio/pa_jack/CVS/Repository deleted file mode 100644 index 51f4f90a7..000000000 --- a/portaudio/pa_jack/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_jack diff --git a/portaudio/pa_jack/CVS/Root b/portaudio/pa_jack/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_jack/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_jack/CVS/Tag b/portaudio/pa_jack/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_jack/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_jack/pa_jack.c b/portaudio/pa_jack/pa_jack.c deleted file mode 100644 index 2199365f4..000000000 --- a/portaudio/pa_jack/pa_jack.c +++ /dev/null @@ -1,1714 +0,0 @@ -/* - * $Id: pa_jack.c,v 1.1.2.20 2005/10/02 22:02:26 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * JACK Implementation by Joshua Haberman - * - * Copyright (c) 2004 Stefan Westerfeld <stefan@space.twc.de> - * Copyright (c) 2004 Arve Knudsen <aknuds-1@broadpark.no> - * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com> - * - * 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 <string.h> -#include <regex.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> /* EBUSY */ -#include <signal.h> /* sig_atomic_t */ -#include <math.h> -#include <semaphore.h> - -#include <jack/types.h> -#include <jack/jack.h> - -#include "pa_util.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_process.h" -#include "pa_allocation.h" -#include "pa_cpuload.h" -#include "../pablio/ringbuffer.c" - -static int aErr_; -static PaError paErr_; /* For use with ENSURE_PA */ -static pthread_t mainThread_; -static char *jackErr_ = NULL; - -#define STRINGIZE_HELPER(expr) #expr -#define STRINGIZE(expr) STRINGIZE_HELPER(expr) - -/* Check PaError */ -#define ENSURE_PA(expr) \ - do { \ - if( (paErr_ = (expr)) < paNoError ) \ - { \ - if( (paErr_) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - assert( jackErr_ ); \ - PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \ - } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = paErr_; \ - goto error; \ - } \ - } while( 0 ) - -#define UNLESS(expr, code) \ - do { \ - if( (expr) == 0 ) \ - { \ - if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - assert( jackErr_ ); \ - PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \ - } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ) - -#define ASSERT_CALL(expr, success) \ - aErr_ = (expr); \ - assert( aErr_ == success ); - -/* - * Functions that directly map to the PortAudio stream interface - */ - -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 ); - - -/* - * Data specific to this API - */ - -struct PaJackStream; - -typedef struct -{ - PaUtilHostApiRepresentation commonHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *deviceInfoMemory; - - jack_client_t *jack_client; - int jack_buffer_size; - PaHostApiIndex hostApiIndex; - - pthread_mutex_t mtx; - pthread_cond_t cond; - unsigned long inputBase, outputBase; - - /* For dealing with the process thread */ - volatile int xrun; /* Received xrun notification from JACK? */ - struct PaJackStream * volatile toAdd, * volatile toRemove; - struct PaJackStream *processQueue; - volatile sig_atomic_t jackIsDown; -} -PaJackHostApiRepresentation; - -/* PaJackStream - a stream data structure specifically for this implementation */ - -typedef struct PaJackStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilBufferProcessor bufferProcessor; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaJackHostApiRepresentation *hostApi; - - /* 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. - */ - volatile sig_atomic_t is_running; - volatile sig_atomic_t is_active; - /* Used to signal processing thread that stream should start or stop, respectively */ - volatile sig_atomic_t doStart, doStop, doAbort; - - jack_nframes_t t0; - - PaUtilAllocationGroup *stream_memory; - - /* These are useful in the process callback */ - - int callbackResult; - int isSilenced; - int xrun; - - /* These are useful for the blocking API */ - - int isBlockingStream; - RingBuffer inFIFO; - RingBuffer outFIFO; - volatile sig_atomic_t data_available; - sem_t data_semaphore; - int bytesPerFrame; - int samplesPerFrame; - - struct PaJackStream *next; -} -PaJackStream; - -#define TRUE 1 -#define FALSE 0 - -/* - * Functions specific to this API - */ - -static int JackCallback( jack_nframes_t frames, void *userData ); - - -/* - * - * Implementation - * - */ - -/* ---- blocking emulation layer ---- */ - -/* Allocate buffer. */ -static PaError BlockingInitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ) -{ - long numBytes = numFrames * bytesPerFrame; - char *buffer = (char *) malloc( numBytes ); - if( buffer == NULL ) return paInsufficientMemory; - memset( buffer, 0, numBytes ); - return (PaError) RingBuffer_Init( rbuf, numBytes, buffer ); -} - -/* Free buffer. */ -static PaError BlockingTermFIFO( RingBuffer *rbuf ) -{ - if( rbuf->buffer ) free( rbuf->buffer ); - rbuf->buffer = NULL; - return paNoError; -} - -static int -BlockingCallback( const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - struct PaJackStream *stream = (PaJackStream *)userData; - long numBytes = stream->bytesPerFrame * framesPerBuffer; - - /* This may get called with NULL inputBuffer during initial setup. */ - if( inputBuffer != NULL ) - { - RingBuffer_Write( &stream->inFIFO, inputBuffer, numBytes ); - } - if( outputBuffer != NULL ) - { - int numRead = RingBuffer_Read( &stream->outFIFO, outputBuffer, numBytes ); - /* Zero out remainder of buffer if we run out of data. */ - memset( (char *)outputBuffer + numRead, 0, numBytes - numRead ); - } - - if( !stream->data_available ) - { - stream->data_available = 1; - sem_post( &stream->data_semaphore ); - } - return paContinue; -} - -static PaError -BlockingBegin( PaJackStream *stream, int minimum_buffer_size ) -{ - long doRead = 0; - long doWrite = 0; - PaError result = paNoError; - long numFrames; - - doRead = stream->local_input_ports != NULL; - doWrite = stream->local_output_ports != NULL; - /* <FIXME> */ - stream->samplesPerFrame = 2; - stream->bytesPerFrame = sizeof(float) * stream->samplesPerFrame; - /* </FIXME> */ - numFrames = 32; - while (numFrames < minimum_buffer_size) - numFrames *= 2; - - if( doRead ) - { - ENSURE_PA( BlockingInitFIFO( &stream->inFIFO, numFrames, stream->bytesPerFrame ) ); - } - if( doWrite ) - { - long numBytes; - - ENSURE_PA( BlockingInitFIFO( &stream->outFIFO, numFrames, stream->bytesPerFrame ) ); - - /* Make Write FIFO appear full initially. */ - numBytes = RingBuffer_GetWriteAvailable( &stream->outFIFO ); - RingBuffer_AdvanceWriteIndex( &stream->outFIFO, numBytes ); - } - - stream->data_available = 0; - sem_init( &stream->data_semaphore, 0, 0 ); - -error: - return result; -} - -static void -BlockingEnd( PaJackStream *stream ) -{ - BlockingTermFIFO( &stream->inFIFO ); - BlockingTermFIFO( &stream->outFIFO ); - - sem_destroy( &stream->data_semaphore ); -} - -static PaError BlockingReadStream( PaStream* s, void *data, unsigned long numFrames ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream *)s; - - long bytesRead; - char *p = (char *) data; - long numBytes = stream->bytesPerFrame * numFrames; - while( numBytes > 0 ) - { - bytesRead = RingBuffer_Read( &stream->inFIFO, p, numBytes ); - numBytes -= bytesRead; - p += bytesRead; - if( numBytes > 0 ) - { - /* see write for an explanation */ - if( stream->data_available ) - stream->data_available = 0; - else - sem_wait( &stream->data_semaphore ); - } - } - - return result; -} - -static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long numFrames ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream *)s; - long bytesWritten; - char *p = (char *) data; - long numBytes = stream->bytesPerFrame * numFrames; - while( numBytes > 0 ) - { - bytesWritten = RingBuffer_Write( &stream->outFIFO, p, numBytes ); - numBytes -= bytesWritten; - p += bytesWritten; - if( numBytes > 0 ) - { - /* we use the following algorithm: - * (1) write data - * (2) if some data didn't fit into the ringbuffer, set data_available to 0 - * to indicate to the audio that if space becomes available, we want to know - * (3) retry to write data (because it might be that between (1) and (2) - * new space in the buffer became available) - * (4) if this failed, we are sure that the buffer is really empty and - * we will definitely receive a notification when it becomes available - * thus we can safely sleep - * - * if the algorithm bailed out in step (3) before, it leaks a count of 1 - * on the semaphore; however, it doesn't matter, because if we block in (4), - * we also do it in a loop - */ - if( stream->data_available ) - stream->data_available = 0; - else - sem_wait( &stream->data_semaphore ); - } - } - - return result; -} - -static signed long -BlockingGetStreamReadAvailable( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - int bytesFull = RingBuffer_GetReadAvailable( &stream->inFIFO ); - return bytesFull / stream->bytesPerFrame; -} - -static signed long -BlockingGetStreamWriteAvailable( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - int bytesEmpty = RingBuffer_GetWriteAvailable( &stream->outFIFO ); - return bytesEmpty / stream->bytesPerFrame; -} - -static PaError -BlockingWaitEmpty( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - while( RingBuffer_GetReadAvailable( &stream->outFIFO ) > 0 ) - { - stream->data_available = 0; - sem_wait( &stream->data_semaphore ); - } - return 0; -} - -/* ---- jack driver ---- */ - -/* 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) )) - - /* 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 */ - - PaError result = paNoError; - PaUtilHostApiRepresentation *commonApi = &jackApi->commonHostApiRep; - - const char **jack_ports = NULL; - char **client_names = NULL; - char *regex_pattern = alloca( jack_client_name_size() + 3 ); - int port_index, client_index, i; - double globalSampleRate; - regex_t port_regex; - unsigned long numClients = 0, numPorts = 0; - char *tmp_client_name = alloca( jack_client_name_size() ); - - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - commonApi->info.deviceCount = 0; - - /* Parse the list of ports, using a regex to grab the client names */ - ASSERT_CALL( regcomp( &port_regex, "^[^:]*", REG_EXTENDED ), 0 ); - - /* 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) - * A: If jack_get_ports returns NULL, there's nothing for us to do */ - UNLESS( (jack_ports = jack_get_ports( jackApi->jack_client, "", "", 0 )) && jack_ports[0], paNoError ); - /* Find number of ports */ - while( jack_ports[numPorts] ) - ++numPorts; - /* At least there will be one port per client :) */ - UNLESS( client_names = alloca( numPorts * sizeof (char *) ), paInsufficientMemory ); - - /* Build a list of clients from the list of ports */ - for( numClients = 0, port_index = 0; jack_ports[port_index] != NULL; port_index++ ) - { - int client_seen = FALSE; - regmatch_t match_info; - const char *port = jack_ports[port_index]; - - /* extract the client name from the port name, using a regex - * that parses the clientname:portname syntax */ - UNLESS( !regexec( &port_regex, port, 1, &match_info, 0 ), paInternalError ); - assert(match_info.rm_eo - match_info.rm_so < jack_client_name_size()); - memcpy( tmp_client_name, port + 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? */ - for( i = 0; i < numClients; i++ ) - { - if( strcmp( tmp_client_name, client_names[i] ) == 0 ) - client_seen = TRUE; - } - - if (client_seen) - continue; /* A: Nothing to see here, move along */ - - UNLESS( client_names[numClients] = (char*)MALLOC(strlen(tmp_client_name) + 1), paInsufficientMemory ); - - /* 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 && numClients > 0 ) - { - /* alsa_pcm goes in spot 0 */ - strcpy( client_names[ numClients ], client_names[0] ); - strcpy( client_names[0], tmp_client_name ); - } - else - { - /* put the new client at the end of the client list */ - strcpy( client_names[ numClients ], tmp_client_name ); - } - ++numClients; - } - - /* Now we have a list of clients, which will become the list of - * PortAudio devices. */ - - /* there is one global sample rate all clients must conform to */ - - globalSampleRate = jack_get_sample_rate( jackApi->jack_client ); - UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)MALLOC( sizeof(PaDeviceInfo*) * - numClients ), paInsufficientMemory ); - - assert( commonApi->info.deviceCount == 0 ); - - /* Create a PaDeviceInfo structure for every client */ - for( client_index = 0; client_index < numClients; client_index++ ) - { - PaDeviceInfo *curDevInfo; - const char **clientPorts = NULL; - - UNLESS( curDevInfo = (PaDeviceInfo*)MALLOC( sizeof(PaDeviceInfo) ), paInsufficientMemory ); - UNLESS( curDevInfo->name = (char*)MALLOC( strlen(client_names[client_index]) + 1 ), paInsufficientMemory ); - 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->defaultSampleRate = globalSampleRate; - - /* 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 from)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, - NULL, JackPortIsOutput); - curDevInfo->maxInputChannels = 0; - curDevInfo->defaultLowInputLatency = 0.; - curDevInfo->defaultHighInputLatency = 0.; - if( clientPorts ) - { - jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); - curDevInfo->defaultLowInputLatency = curDevInfo->defaultHighInputLatency = - jack_port_get_latency( p ) / globalSampleRate; - - for( i = 0; clientPorts[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(clientPorts); - } - - /* ... what are your input ports (that we could output to)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, - NULL, JackPortIsInput); - curDevInfo->maxOutputChannels = 0; - curDevInfo->defaultLowOutputLatency = 0.; - curDevInfo->defaultHighOutputLatency = 0.; - if( clientPorts ) - { - jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); - curDevInfo->defaultLowOutputLatency = curDevInfo->defaultHighOutputLatency = - jack_port_get_latency( p ) / globalSampleRate; - - for( i = 0; clientPorts[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(clientPorts); - } - - /* Add this client to the list of devices */ - commonApi->deviceInfos[client_index] = curDevInfo; - ++commonApi->info.deviceCount; - if( commonApi->info.defaultInputDevice == paNoDevice && curDevInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = client_index; - if( commonApi->info.defaultOutputDevice == paNoDevice && curDevInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = client_index; - } - -error: - regfree( &port_regex ); - free( jack_ports ); - return result; -} -#undef MALLOC - -static void UpdateSampleRate( PaJackStream *stream, double sampleRate ) -{ - /* XXX: Maybe not the cleanest way of going about this? */ - stream->cpuLoadMeasurer.samplingPeriod = stream->bufferProcessor.samplePeriod = 1. / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; -} - -static void JackErrorCallback( const char *msg ) -{ - if( pthread_self() == mainThread_ ) - { - assert( msg ); - free( jackErr_ ); - jackErr_ = malloc( strlen( msg ) ); - sprintf( jackErr_, msg ); - } -} - -static void JackOnShutdown( void *arg ) -{ - PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; - PaJackStream *stream = jackApi->processQueue; - - PA_DEBUG(( "%s: JACK server is shutting down\n", __FUNCTION__ )); - for( ; stream; stream = stream->next ) - { - stream->is_active = 0; - } - - /* Make sure that the main thread doesn't get stuck waiting on the condition */ - ASSERT_CALL( pthread_mutex_lock( &jackApi->mtx ), 0 ); - jackApi->jackIsDown = 1; - ASSERT_CALL( pthread_cond_signal( &jackApi->cond ), 0 ); - ASSERT_CALL( pthread_mutex_unlock( &jackApi->mtx ), 0 ); - -} - -static int JackSrCb( jack_nframes_t nframes, void *arg ) -{ - PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; - double sampleRate = (double)nframes; - PaJackStream *stream = jackApi->processQueue; - - /* Update all streams in process queue */ - PA_DEBUG(( "%s: Acting on change in JACK samplerate: %f\n", __FUNCTION__, sampleRate )); - for( ; stream; stream = stream->next ) - { - if( stream->streamRepresentation.streamInfo.sampleRate != sampleRate ) - { - PA_DEBUG(( "%s: Updating samplerate\n", __FUNCTION__ )); - UpdateSampleRate( stream, sampleRate ); - } - } - - return 0; -} - -static int JackXRunCb(void *arg) { - PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)arg; - assert( hostApi ); - hostApi->xrun = TRUE; - PA_DEBUG(( "%s: JACK signalled xrun\n", __FUNCTION__ )); - return 0; -} - -PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, - PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *jackHostApi; - int activated = 0; - char *clientName; - int written; - *hostApi = NULL; /* Initialize to NULL */ - - UNLESS( jackHostApi = (PaJackHostApiRepresentation*) - PaUtil_AllocateMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory ); - jackHostApi->deviceInfoMemory = NULL; - - mainThread_ = pthread_self(); - ASSERT_CALL( pthread_mutex_init( &jackHostApi->mtx, NULL ), 0 ); - ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 ); - - /* Try to become a client of the JACK server. If we cannot do - * this, then this API cannot be used. */ - - clientName = alloca( jack_client_name_size() ); - written = snprintf( clientName, jack_client_name_size(), "PortAudio-%d", getpid() ); - assert( written < jack_client_name_size() ); - jackHostApi->jack_client = jack_client_new( clientName ); - if( jackHostApi->jack_client == NULL ) - { - /* 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; - goto error; - } - - UNLESS( jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - jackHostApi->hostApiIndex = hostApiIndex; - - *hostApi = &jackHostApi->commonHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paJACK; - (*hostApi)->info.name = "JACK Audio Connection Kit"; - - /* Build a device list by querying the JACK server */ - - ENSURE_PA( BuildDeviceList( jackHostApi ) ); - - /* Register functions */ - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &jackHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - BlockingReadStream, BlockingWriteStream, - BlockingGetStreamReadAvailable, BlockingGetStreamWriteAvailable ); - - jackHostApi->inputBase = jackHostApi->outputBase = 0; - jackHostApi->xrun = 0; - jackHostApi->toAdd = jackHostApi->toRemove = NULL; - jackHostApi->processQueue = NULL; - jackHostApi->jackIsDown = 0; - - jack_on_shutdown( jackHostApi->jack_client, JackOnShutdown, jackHostApi ); - jack_set_error_function( JackErrorCallback ); - jackHostApi->jack_buffer_size = jack_get_buffer_size ( jackHostApi->jack_client ); - UNLESS( !jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_set_xrun_callback( jackHostApi->jack_client, JackXRunCb, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_set_process_callback( jackHostApi->jack_client, JackCallback, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_activate( jackHostApi->jack_client ), paUnanticipatedHostError ); - activated = 1; - - return result; - -error: - if( activated ) - ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 ); - - if( jackHostApi ) - { - if( jackHostApi->jack_client ) - ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 ); - - 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; - - /* note: this automatically disconnects all ports, since a deactivated - * client is not allowed to have any ports connected */ - ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 ); - - ASSERT_CALL( pthread_mutex_destroy( &jackHostApi->mtx ), 0 ); - ASSERT_CALL( pthread_cond_destroy( &jackHostApi->cond ), 0 ); - - ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 ); - - if( jackHostApi->deviceInfoMemory ) - { - PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory ); - PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory ); - } - - PaUtil_FreeMemory( jackHostApi ); - - free( jackErr_ ); -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount = 0, outputChannelCount = 0; - 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; - } - - /* - The following check is not necessary for JACK. - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from 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 - */ - - /* check that the device supports sampleRate */ - -#define ABS(x) ( (x) > 0 ? (x) : -(x) ) - if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 ) - return paInvalidSampleRate; -#undef ABS - - return paFormatIsSupported; -} - -/* Basic stream initialization */ -static PaError InitializeStream( PaJackStream *stream, PaJackHostApiRepresentation *hostApi, int numInputChannels, - int numOutputChannels ) -{ - PaError result = paNoError; - assert( stream ); - - memset( stream, 0, sizeof (PaJackStream) ); - UNLESS( stream->stream_memory = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - stream->jack_client = hostApi->jack_client; - stream->hostApi = hostApi; - - if( numInputChannels > 0 ) - { - UNLESS( stream->local_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), - paInsufficientMemory ); - memset( stream->local_input_ports, 0, sizeof(jack_port_t*) * numInputChannels ); - UNLESS( stream->remote_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), - paInsufficientMemory ); - memset( stream->remote_output_ports, 0, sizeof(jack_port_t*) * numInputChannels ); - } - if( numOutputChannels > 0 ) - { - UNLESS( stream->local_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), - paInsufficientMemory ); - memset( stream->local_output_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); - UNLESS( stream->remote_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), - paInsufficientMemory ); - memset( stream->remote_input_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); - } - - stream->num_incoming_connections = numInputChannels; - stream->num_outgoing_connections = numOutputChannels; - -error: - return result; -} - -/*! - * Free resources associated with stream, and eventually stream itself. - * - * Frees allocated memory, and closes opened pcms. - */ -static void CleanUpStream( PaJackStream *stream, int terminateStreamRepresentation, int terminateBufferProcessor ) -{ - int i; - assert( stream ); - - if( stream->isBlockingStream ) - BlockingEnd( stream ); - - for( i = 0; i < stream->num_incoming_connections; ++i ) - { - if( stream->local_input_ports[i] ) - ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_input_ports[i] ), 0 ); - } - for( i = 0; i < stream->num_outgoing_connections; ++i ) - { - if( stream->local_output_ports[i] ) - ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_output_ports[i] ), 0 ); - } - - if( terminateStreamRepresentation ) - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - if( terminateBufferProcessor ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - - if( stream->stream_memory ) - { - PaUtil_FreeAllAllocations( stream->stream_memory ); - PaUtil_DestroyAllocationGroup( stream->stream_memory ); - } - PaUtil_FreeMemory( stream ); -} - -static PaError WaitCondition( PaJackHostApiRepresentation *hostApi ) -{ - PaError result = paNoError; - int err = 0; - PaTime pt = PaUtil_GetTime(); - struct timespec ts; - - ts.tv_sec = (time_t) floor( pt + 1 ); - ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); - /* XXX: Best enclose in loop, in case of spurious wakeups? */ - err = pthread_cond_timedwait( &hostApi->cond, &hostApi->mtx, &ts ); - - /* Make sure we didn't time out */ - UNLESS( err != ETIMEDOUT, paTimedOut ); - UNLESS( !err, paInternalError ); - -error: - return result; -} - -static PaError AddStream( PaJackStream *stream ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = stream->hostApi; - /* Add to queue of streams that should be processed */ - ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 ); - if( !hostApi->jackIsDown ) - { - hostApi->toAdd = stream; - /* Unlock mutex and await signal from processing thread */ - result = WaitCondition( stream->hostApi ); - } - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - ENSURE_PA( result ); - - UNLESS( !hostApi->jackIsDown, paDeviceUnavailable ); - -error: - return result; -} - -/* Remove stream from processing queue */ -static PaError RemoveStream( PaJackStream *stream ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = stream->hostApi; - - /* Add to queue over streams that should be processed */ - ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 ); - if( !hostApi->jackIsDown ) - { - hostApi->toRemove = stream; - /* Unlock mutex and await signal from processing thread */ - result = WaitCondition( stream->hostApi ); - } - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - ENSURE_PA( result ); - -error: - return result; -} - -/* Add stream to processing queue */ -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; - PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; - PaJackStream *stream = NULL; - char *port_string = alloca( jack_port_name_size() ); - unsigned long regexSz = jack_client_name_size() + 3; - char *regex_pattern = alloca( regexSz ); - const char **jack_ports = NULL; - /* int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); */ - int i; - int inputChannelCount, outputChannelCount; - const double jackSr = jack_get_sample_rate( jackHostApi->jack_client ); - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - int bpInitialized = 0, srInitialized = 0; /* Initialized buffer processor and stream representation? */ - unsigned long ofs; - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - if( (streamFlags & paPrimeOutputBuffersUsingStreamCallback) != 0 ) - { - streamFlags &= ~paPrimeOutputBuffersUsingStreamCallback; - /*return paInvalidFlag;*/ /* This implementation does not support buffer priming */ - } - - if( framesPerBuffer != paFramesPerBufferUnspecified ) - { - /* Jack operates with power of two buffers, and we don't support non-integer buffer adaption (yet) */ - /*UNLESS( !(framesPerBuffer & (framesPerBuffer - 1)), paBufferTooBig );*/ /* TODO: Add descriptive error code? */ - } - - /* Preliminary checks */ - - 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; - } - - /* ... check that the sample rate exactly matches the ONE acceptable rate - * A: This rate isn't necessarily constant though? */ - -#define ABS(x) ( (x) > 0 ? (x) : -(x) ) - if( ABS(sampleRate - jackSr) > 1 ) - return paInvalidSampleRate; -#undef ABS - - UNLESS( stream = (PaJackStream*)PaUtil_AllocateMemory( sizeof(PaJackStream) ), paInsufficientMemory ); - ENSURE_PA( InitializeStream( stream, jackHostApi, inputChannelCount, outputChannelCount ) ); - - /* the blocking emulation, if necessary */ - stream->isBlockingStream = !streamCallback; - if( stream->isBlockingStream ) - { - float latency = 0.001; /* 1ms is the absolute minimum we support */ - int minimum_buffer_frames = 0; - - if( inputParameters && inputParameters->suggestedLatency > latency ) - latency = inputParameters->suggestedLatency; - else if( outputParameters && outputParameters->suggestedLatency > latency ) - latency = outputParameters->suggestedLatency; - - /* the latency the user asked for indicates the minimum buffer size in frames */ - minimum_buffer_frames = (int) (latency * jack_get_sample_rate( jackHostApi->jack_client )); - - /* we also need to be able to store at least three full jack buffers to avoid dropouts */ - if( jackHostApi->jack_buffer_size * 3 > minimum_buffer_frames ) - minimum_buffer_frames = jackHostApi->jack_buffer_size * 3; - - /* setup blocking API data structures (FIXME: can fail) */ - BlockingBegin( stream, minimum_buffer_frames ); - - /* install our own callback for the blocking API */ - streamCallback = BlockingCallback; - userData = stream; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &jackHostApi->blockingStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &jackHostApi->callbackStreamInterface, streamCallback, userData ); - } - srInitialized = 1; - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, jackSr ); - - /* create the JACK ports. We cannot connect them until audio - * processing begins */ - - /* Register a unique set of ports for this stream - * TODO: Robust allocation of new port names */ - - ofs = jackHostApi->inputBase; - for( i = 0; i < inputChannelCount; i++ ) - { - snprintf( port_string, jack_port_name_size(), "in_%lu", ofs + i ); - UNLESS( stream->local_input_ports[i] = jack_port_register( - jackHostApi->jack_client, port_string, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ), paInsufficientMemory ); - } - jackHostApi->inputBase += inputChannelCount; - - ofs = jackHostApi->outputBase; - for( i = 0; i < outputChannelCount; i++ ) - { - snprintf( port_string, jack_port_name_size(), "out_%lu", ofs + i ); - UNLESS( stream->local_output_ports[i] = jack_port_register( - jackHostApi->jack_client, port_string, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ), paInsufficientMemory ); - } - jackHostApi->outputBase += outputChannelCount; - - /* 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( inputChannelCount > 0 ) - { - int err = 0; - - /* ... remote output ports (that we input from) */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name ); - UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, - NULL, JackPortIsOutput ), paUnanticipatedHostError ); - for( i = 0; i < inputChannelCount && jack_ports[i]; i++ ) - { - if( (stream->remote_output_ports[i] = jack_port_by_name( - jackHostApi->jack_client, jack_ports[i] )) == NULL ) - { - err = 1; - break; - } - } - free( jack_ports ); - UNLESS( !err, paInsufficientMemory ); - - /* Fewer ports than expected? */ - UNLESS( i == inputChannelCount, paInternalError ); - } - - if( outputChannelCount > 0 ) - { - int err = 0; - - /* ... remote input ports (that we output to) */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name ); - UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, - NULL, JackPortIsInput ), paUnanticipatedHostError ); - for( i = 0; i < outputChannelCount && jack_ports[i]; i++ ) - { - if( (stream->remote_input_ports[i] = jack_port_by_name( - jackHostApi->jack_client, jack_ports[i] )) == 0 ) - { - err = 1; - break; - } - } - free( jack_ports ); - UNLESS( !err , paInsufficientMemory ); - - /* Fewer ports than expected? */ - UNLESS( i == outputChannelCount, paInternalError ); - } - - ENSURE_PA( PaUtil_InitializeBufferProcessor( - &stream->bufferProcessor, - inputChannelCount, - inputSampleFormat, - paFloat32, /* hostInputSampleFormat */ - outputChannelCount, - outputSampleFormat, - paFloat32, /* hostOutputSampleFormat */ - jackSr, - streamFlags, - framesPerBuffer, - 0, /* Ignored */ - paUtilUnknownHostBufferSize, /* Buffer size may vary on JACK's discretion */ - streamCallback, - userData ) ); - bpInitialized = 1; - - if( stream->num_incoming_connections > 0 ) - stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[0] ) - - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor )) / sampleRate; - if( stream->num_outgoing_connections > 0 ) - stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[0] ) - - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor )) / sampleRate; - - stream->streamRepresentation.streamInfo.sampleRate = jackSr; - stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */ - - ENSURE_PA( AddStream( stream ) ); /* Add to queue over opened streams */ - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - CleanUpStream( stream, srInitialized, bpInitialized ); - - 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; - PaJackStream *stream = (PaJackStream*)s; - - /* Remove this stream from the processing queue */ - ENSURE_PA( RemoveStream( stream ) ); - -error: - CleanUpStream( stream, 1, 1 ); - return result; -} - -static PaError RealProcess( PaJackStream *stream, jack_nframes_t frames ) -{ - PaError result = paNoError; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; - int chn; - int framesProcessed; - const double sr = jack_get_sample_rate( stream->jack_client ); /* Shouldn't change during the process callback */ - PaStreamCallbackFlags cbFlags = 0; - - /* If the user has returned !paContinue from the callback we'll want to flush the internal buffers, - * when these are empty we can finally mark the stream as inactive */ - if( stream->callbackResult != paContinue && - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - { - stream->is_active = 0; - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - PA_DEBUG(( "%s: Callback finished\n", __FUNCTION__ )); - - goto end; - } - - timeInfo.currentTime = (jack_frame_time( stream->jack_client ) - stream->t0) / sr; - if( stream->num_incoming_connections > 0 ) - timeInfo.inputBufferAdcTime = timeInfo.currentTime - jack_port_get_latency( stream->remote_output_ports[0] ) - / sr; - if( stream->num_outgoing_connections > 0 ) - timeInfo.outputBufferDacTime = timeInfo.currentTime + jack_port_get_latency( stream->remote_input_ports[0] ) - / sr; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - if( stream->xrun ) - { - /* XXX: Any way to tell which of these occurred? */ - cbFlags = paOutputUnderflow | paInputOverflow; - stream->xrun = FALSE; - } - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - - if( stream->num_incoming_connections > 0 ) - PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames ); - if( stream->num_outgoing_connections > 0 ) - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames ); - - for( chn = 0; chn < stream->num_incoming_connections; chn++ ) - { - jack_default_audio_sample_t *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 = (jack_default_audio_sample_t*) - jack_port_get_buffer( stream->local_output_ports[chn], - frames ); - - PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, - chn, - channel_buf ); - } - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &stream->callbackResult ); - /* We've specified a host buffer size mode where every frame should be consumed by the buffer processor */ - assert( framesProcessed == frames ); - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - -end: - return result; -} - -/* Alter the processing queue if necessary */ -static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi ) -{ - PaError result = paNoError; - int queueModified = 0; - const double jackSr = jack_get_sample_rate( hostApi->jack_client ); - int err; - - if( (err = pthread_mutex_trylock( &hostApi->mtx )) != 0 ) - { - assert( err == EBUSY ); - return paNoError; - } - - if( hostApi->toAdd ) - { - if( hostApi->processQueue ) - { - PaJackStream *node = hostApi->processQueue; - /* Advance to end of queue */ - while( node->next ) - node = node->next; - - node->next = hostApi->toAdd; - } - else - hostApi->processQueue = (PaJackStream *)hostApi->toAdd; - - /* If necessary, update stream state */ - if( hostApi->toAdd->streamRepresentation.streamInfo.sampleRate != jackSr ) - UpdateSampleRate( hostApi->toAdd, jackSr ); - - hostApi->toAdd = NULL; - queueModified = 1; - } - if( hostApi->toRemove ) - { - int removed = 0; - PaJackStream *node = hostApi->processQueue, *prev = NULL; - assert( hostApi->processQueue ); - - while( node ) - { - if( node == hostApi->toRemove ) - { - if( prev ) - prev->next = node->next; - else - hostApi->processQueue = (PaJackStream *)node->next; - - removed = 1; - break; - } - - prev = node; - node = node->next; - } - UNLESS( removed, paInternalError ); - hostApi->toRemove = NULL; - PA_DEBUG(( "%s: Removed stream from processing queue\n", __FUNCTION__ )); - queueModified = 1; - } - - if( queueModified ) - { - /* Signal that we've done what was asked of us */ - ASSERT_CALL( pthread_cond_signal( &hostApi->cond ), 0 ); - } - -error: - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - - return result; -} - -static int JackCallback( jack_nframes_t frames, void *userData ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)userData; - PaJackStream *stream = NULL; - int xrun = hostApi->xrun; - hostApi->xrun = 0; - - assert( hostApi ); - - ENSURE_PA( UpdateQueue( hostApi ) ); - - /* Process each stream */ - stream = hostApi->processQueue; - for( ; stream; stream = stream->next ) - { - if( xrun ) /* Don't override if already set */ - stream->xrun = 1; - - /* See if this stream is to be started */ - if( stream->doStart ) - { - /* If we can't obtain a lock, we'll try next time */ - int err = pthread_mutex_trylock( &stream->hostApi->mtx ); - if( !err ) - { - if( stream->doStart ) /* Could potentially change before obtaining the lock */ - { - stream->is_active = 1; - stream->doStart = 0; - PA_DEBUG(( "%s: Starting stream\n", __FUNCTION__ )); - ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 ); - stream->callbackResult = paContinue; - stream->isSilenced = 0; - } - - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - } - else - assert( err == EBUSY ); - } - else if( stream->doStop || stream->doAbort ) /* Should we stop/abort stream? */ - { - if( stream->callbackResult == paContinue ) /* Ok, make it stop */ - { - PA_DEBUG(( "%s: Stopping stream\n", __FUNCTION__ )); - stream->callbackResult = stream->doStop ? paComplete : paAbort; - } - } - - if( stream->is_active ) - ENSURE_PA( RealProcess( stream, frames ) ); - /* If we have just entered inactive state, silence output */ - if( !stream->is_active && !stream->isSilenced ) - { - int i; - - /* Silence buffer after entering inactive state */ - PA_DEBUG(( "Silencing the output\n" )); - for( i = 0; i < stream->num_outgoing_connections; ++i ) - { - jack_default_audio_sample_t *buffer = jack_port_get_buffer( stream->local_output_ports[i], frames ); - memset( buffer, 0, sizeof (jack_default_audio_sample_t) * frames ); - } - - stream->isSilenced = 1; - } - - if( stream->doStop || stream->doAbort ) - { - /* See if RealProcess has acted on the request */ - if( !stream->is_active ) /* Ok, signal to the main thread that we've carried out the operation */ - { - /* If we can't obtain a lock, we'll try next time */ - int err = pthread_mutex_trylock( &stream->hostApi->mtx ); - if( !err ) - { - stream->doStop = stream->doAbort = 0; - ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 ); - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - } - else - assert( err == EBUSY ); - } - } - } - - return 0; -error: - return -1; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream*)s; - int i; - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* 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++ ) - UNLESS( jack_connect( stream->jack_client, - jack_port_name( stream->remote_output_ports[i] ), - jack_port_name( stream->local_input_ports[i] ) ) == 0, paUnanticipatedHostError ); - } - - if( stream->num_outgoing_connections > 0 ) - { - for( i = 0; i < stream->num_outgoing_connections; i++ ) - UNLESS( jack_connect( stream->jack_client, - jack_port_name( stream->local_output_ports[i] ), - jack_port_name( stream->remote_input_ports[i] ) ) == 0, paUnanticipatedHostError ); - } - - stream->xrun = FALSE; - - /* Enable processing */ - - ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 ); - stream->doStart = 1; - - /* Wait for stream to be started */ - result = WaitCondition( stream->hostApi ); - /* - do - { - err = pthread_cond_timedwait( &stream->hostApi->cond, &stream->hostApi->mtx, &ts ); - } while( !stream->is_active && !err ); - */ - if( result != paNoError ) /* Something went wrong, call off the stream start */ - { - stream->doStart = 0; - stream->is_active = 0; /* Cancel any processing */ - } - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - - ENSURE_PA( result ); - - stream->is_running = TRUE; - PA_DEBUG(( "%s: Stream started\n", __FUNCTION__ )); - -error: - return result; -} - -static PaError RealStop( PaJackStream *stream, int abort ) -{ - PaError result = paNoError; - int i; - - if( stream->isBlockingStream ) - BlockingWaitEmpty ( stream ); - - ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 ); - if( abort ) - stream->doAbort = 1; - else - stream->doStop = 1; - - /* Wait for stream to be stopped */ - result = WaitCondition( stream->hostApi ); - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - ENSURE_PA( result ); - - UNLESS( !stream->is_active, paInternalError ); - - PA_DEBUG(( "%s: Stream stopped\n", __FUNCTION__ )); - -error: - stream->is_running = FALSE; - - /* Disconnect ports belonging to this stream */ - - if( !stream->hostApi->jackIsDown ) /* XXX: Well? */ - { - if( stream->num_incoming_connections > 0 ) - { - for( i = 0; i < stream->num_incoming_connections; i++ ) - UNLESS( !jack_disconnect( stream->jack_client, - jack_port_name( stream->remote_output_ports[i] ), - jack_port_name( stream->local_input_ports[i] ) ), paUnanticipatedHostError ); - } - if( stream->num_outgoing_connections > 0 ) - { - for( i = 0; i < stream->num_outgoing_connections; i++ ) - UNLESS( !jack_disconnect( stream->jack_client, - jack_port_name( stream->local_output_ports[i] ), - jack_port_name( stream->remote_input_ports[i] ) ), paUnanticipatedHostError ); - } - } - - return result; -} - -static PaError StopStream( PaStream *s ) -{ - assert(s); - return RealStop( (PaJackStream *)s, 0 ); -} - -static PaError AbortStream( PaStream *s ) -{ - assert(s); - return RealStop( (PaJackStream *)s, 1 ); -} - -static PaError IsStreamStopped( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return !stream->is_running; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return stream->is_active; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - - /* A: Is this relevant?? --> TODO: what if we're recording-only? */ - return (jack_frame_time( stream->jack_client ) - stream->t0) / (PaTime)jack_get_sample_rate( stream->jack_client ); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} diff --git a/portaudio/pa_linux_alsa/CVS/Entries b/portaudio/pa_linux_alsa/CVS/Entries deleted file mode 100644 index 6bfbe173d..000000000 --- a/portaudio/pa_linux_alsa/CVS/Entries +++ /dev/null @@ -1,3 +0,0 @@ -/pa_linux_alsa.c/1.1.2.92/Wed Mar 22 19:36:04 2006//Tv19-devel -/pa_linux_alsa.h/1.1.2.12/Sat Sep 25 14:15:25 2004//Tv19-devel -D diff --git a/portaudio/pa_linux_alsa/CVS/Repository b/portaudio/pa_linux_alsa/CVS/Repository deleted file mode 100644 index 26c4919b4..000000000 --- a/portaudio/pa_linux_alsa/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_linux_alsa diff --git a/portaudio/pa_linux_alsa/CVS/Root b/portaudio/pa_linux_alsa/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_linux_alsa/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_linux_alsa/CVS/Tag b/portaudio/pa_linux_alsa/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_linux_alsa/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_linux_alsa/pa_linux_alsa.c b/portaudio/pa_linux_alsa/pa_linux_alsa.c deleted file mode 100644 index 2f88fc2fa..000000000 --- a/portaudio/pa_linux_alsa/pa_linux_alsa.c +++ /dev/null @@ -1,3682 +0,0 @@ -/* - * $Id: pa_linux_alsa.c,v 1.1.2.92 2006/03/22 19:36:04 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * ALSA implementation by Joshua Haberman and Arve Knudsen - * - * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com> - * Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no> - * - * 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. - */ - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include <alsa/asoundlib.h> -#undef ALSA_PCM_NEW_HW_PARAMS_API -#undef ALSA_PCM_NEW_SW_PARAMS_API - -#include <sys/poll.h> -#include <string.h> /* strlen() */ -#include <limits.h> -#include <math.h> -#include <pthread.h> -#include <signal.h> -#include <time.h> -#include <sys/mman.h> -#include <signal.h> /* For sig_atomic_t */ - -#include "portaudio.h" -#include "pa_util.h" -#include "../pa_unix/pa_unix_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" - -/* Check return value of ALSA function, and map it to PaError */ -#define ENSURE_(expr, code) \ - do { \ - if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ - } \ - PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \ - if( (code) == paUnanticipatedHostError ) \ - PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ); - -#define ENSURE_SYSTEM_(expr, success) \ - do { \ - if( UNLIKELY( (aErr_ = (expr)) != success ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( pthread_self() != callbackThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, strerror( aErr_ ) ); \ - } \ - PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \ - result = paUnanticipatedHostError; \ - goto error; \ - } \ - } while( 0 ); - -#define ASSERT_CALL_(expr, success) \ - aErr_ = (expr); \ - assert( success == aErr_ ); - -static int aErr_; /* Used with ENSURE_ */ -static pthread_t callbackThread_; - -typedef enum -{ - StreamDirection_In, - StreamDirection_Out -} StreamDirection; - -/* Threading utility struct */ -typedef struct PaAlsaThreading -{ - pthread_t watchdogThread; - pthread_t callbackThread; - int watchdogRunning; - int rtSched; - int rtPrio; - int useWatchdog; - unsigned long throttledSleepTime; - volatile PaTime callbackTime; - volatile PaTime callbackCpuTime; - PaUtilCpuLoadMeasurer *cpuLoadMeasurer; -} PaAlsaThreading; - -typedef struct -{ - PaSampleFormat hostSampleFormat; - unsigned long framesPerBuffer; - int numUserChannels, numHostChannels; - int userInterleaved, hostInterleaved; - - snd_pcm_t *pcm; - snd_pcm_uframes_t bufferSize; - snd_pcm_format_t nativeFormat; - unsigned int nfds; - int ready; /* Marked ready from poll */ - void **userBuffers; - snd_pcm_uframes_t offset; - StreamDirection streamDir; - - snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */ -} PaAlsaStreamComponent; - -/* Implementation specific stream structure */ -typedef struct PaAlsaStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - PaAlsaThreading threading; - - unsigned long framesPerUserBuffer, maxFramesPerHostBuffer; - - int primeBuffers; - int callbackMode; /* bool: are we running in callback mode? */ - int pcmsSynced; /* Have we successfully synced pcms */ - - /* the callback thread uses these to poll the sound device(s), waiting - * for data to be ready/available */ - struct pollfd* pfds; - int pollTimeout; - - /* Used in communication between threads */ - volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */ - volatile sig_atomic_t callbackAbort; /* Drop frames? */ - volatile sig_atomic_t callbackStop; /* Signal a stop */ - volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */ - pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */ - pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */ - pthread_cond_t startCond; /* Wait untill audio is started in callback thread */ - - int neverDropInput; - - PaTime underrun; - PaTime overrun; - - PaAlsaStreamComponent capture, playback; -} -PaAlsaStream; - -/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct PaAlsaHostApiRepresentation -{ - PaUtilHostApiRepresentation baseHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaHostApiIndex hostApiIndex; -} -PaAlsaHostApiRepresentation; - -typedef struct PaAlsaDeviceInfo -{ - PaDeviceInfo baseDeviceInfo; - char *alsaName; - int isPlug; - int minInputChannels; - int minOutputChannels; -} -PaAlsaDeviceInfo; - -/* Threading utilities */ - -static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm ) -{ - th->watchdogRunning = 0; - th->rtSched = 0; - th->callbackTime = 0; - th->callbackCpuTime = 0; - th->useWatchdog = 1; - th->throttledSleepTime = 0; - th->cpuLoadMeasurer = clm; - - th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2 - + sched_get_priority_min( SCHED_FIFO ); -} - -static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult ) -{ - PaError result = paNoError; - void *pret; - - if( exitResult ) - *exitResult = paNoError; - if( watchdogExitResult ) - *watchdogExitResult = paNoError; - - if( th->watchdogRunning ) - { - pthread_cancel( th->watchdogThread ); - ENSURE_SYSTEM_( pthread_join( th->watchdogThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( watchdogExitResult ) - *watchdogExitResult = *(PaError *) pret; - free( pret ); - } - } - - /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ - /* TODO: Make join time out */ - if( !wait ) - { - PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, th->callbackThread )); - pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ - } - PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, th->callbackThread )); - ENSURE_SYSTEM_( pthread_join( th->callbackThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( exitResult ) - *exitResult = *(PaError *) pret; - free( pret ); - } - -error: - return result; -} - -/** Lock a pthread_mutex_t. - * - * @concern ThreadCancellation We're disabling thread cancellation while the thread is holding a lock, so mutexes are - * properly unlocked at termination time. - */ -static PaError LockMutex( pthread_mutex_t *mtx ) -{ - PaError result = paNoError; - int oldState; - - ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 ); - ENSURE_SYSTEM_( pthread_mutex_lock( mtx ), 0 ); - -error: - return result; -} - -/** Unlock a pthread_mutex_t. - * - * @concern ThreadCancellation Thread cancellation is enabled again after the mutex is properly unlocked. - */ -static PaError UnlockMutex( pthread_mutex_t *mtx ) -{ - PaError result = paNoError; - int oldState; - - ENSURE_SYSTEM_( pthread_mutex_unlock( mtx ), 0 ); - ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 ); - -error: - return result; -} - -static void OnWatchdogExit( void *userData ) -{ - PaAlsaThreading *th = (PaAlsaThreading *) userData; - struct sched_param spm = { 0 }; - assert( th ); - - ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */ - PA_DEBUG(( "Watchdog exiting\n" )); -} - -static PaError BoostPriority( PaAlsaThreading *th ) -{ - PaError result = paNoError; - struct sched_param spm = { 0 }; - spm.sched_priority = th->rtPrio; - - assert( th ); - - if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) - { - PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */ - PA_DEBUG(( "Failed bumping priority\n" )); - result = 0; - } - else - result = 1; /* Success */ -error: - return result; -} - -static void *WatchdogFunc( void *userData ) -{ - PaError result = paNoError, *pres = NULL; - int err; - PaAlsaThreading *th = (PaAlsaThreading *) userData; - unsigned intervalMsec = 500; - const PaTime maxSeconds = 3.; /* Max seconds between callbacks */ - PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed; - double cpuLoad, avgCpuLoad = 0.; - int throttled = 0; - - assert( th ); - - /* Execute OnWatchdogExit when exiting */ - pthread_cleanup_push( &OnWatchdogExit, th ); - - /* Boost priority of callback thread */ - PA_ENSURE( result = BoostPriority( th ) ); - if( !result ) - { - /* Boost failed, might as well exit */ - pthread_exit( NULL ); - } - - cpuTimeThen = th->callbackCpuTime; - { - int policy; - struct sched_param spm = { 0 }; - pthread_getschedparam( pthread_self(), &policy, &spm ); - PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority )); - } - - while( 1 ) - { - double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff; - - /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */ - pthread_testcancel(); - Pa_Sleep( intervalMsec ); - pthread_testcancel(); - - if( PaUtil_GetTime() - th->callbackTime > maxSeconds ) - { - PA_DEBUG(( "Watchdog: Terminating callback thread\n" )); - /* Tell thread to terminate */ - err = pthread_kill( th->callbackThread, SIGKILL ); - pthread_exit( NULL ); - } - - PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) )); - - /* Check if we should throttle, or unthrottle :P */ - cpuTimeNow = th->callbackCpuTime; - cpuTimeElapsed = cpuTimeNow - cpuTimeThen; - cpuTimeThen = cpuTimeNow; - - timeNow = PaUtil_GetTime(); - timeElapsed = timeNow - timeThen; - timeThen = timeNow; - cpuLoad = cpuTimeElapsed / timeElapsed; - avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1; - /* - if( throttled ) - PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed )); - */ - if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 ) - { - static int policy; - static struct sched_param spm = { 0 }; - static const struct sched_param defaultSpm = { 0 }; - PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority )); - - pthread_getschedparam( th->callbackThread, &policy, &spm ); - if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) ) - { - throttled = 1; - } - else - PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) )); - - /* Give other processes a go, before raising priority again */ - PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime )); - Pa_Sleep( th->throttledSleepTime ); - - /* Reset callback priority */ - if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) - { - PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) )); - } - - if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 ) - intervalMsec = 50; - else - intervalMsec = 100; - - /* - lowpassCoeff = .97; - lowpassCoeff1 = .99999 - lowpassCoeff; - */ - } - else if( throttled && avgCpuLoad < .8 ) - { - intervalMsec = 500; - throttled = 0; - - /* - lowpassCoeff = .9; - lowpassCoeff1 = .99999 - lowpassCoeff; - */ - } - } - - pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */ - -error: - /* Shouldn't get here in the normal case */ - - /* Pass on error code */ - pres = malloc( sizeof (PaError) ); - *pres = result; - - pthread_exit( pres ); -} - -static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s ) -{ - PaError result = paNoError; - pthread_attr_t attr; - int started = 0; - -#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1) - if( th->rtSched ) - { - if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 ) - { - int savedErrno = errno; /* In case errno gets overwritten */ - assert( savedErrno != EINVAL ); /* Most likely a programmer error */ - PA_UNLESS( (savedErrno == EPERM), paInternalError ); - PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ )); - } - else - PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ )); - } -#endif - - PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); - /* Priority relative to other processes */ - PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); - - PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError ); - started = 1; - - if( th->rtSched ) - { - if( th->useWatchdog ) - { - int err; - struct sched_param wdSpm = { 0 }; - /* Launch watchdog, watchdog sets callback thread priority */ - int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) ); - wdSpm.sched_priority = prio; - - PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); - PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError ); - PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); - PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError ); - PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError ); - if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) ) - { - PA_UNLESS( err == EPERM, paInternalError ); - /* Permission error, go on without realtime privileges */ - PA_DEBUG(( "Failed bumping priority\n" )); - } - else - { - int policy; - th->watchdogRunning = 1; - ENSURE_SYSTEM_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 ); - /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */ - if( wdSpm.sched_priority != prio ) - { - PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority )); - PA_ENSURE( paInternalError ); - } - } - } - else - PA_ENSURE( BoostPriority( th ) ); - } - -end: - return result; -error: - if( started ) - KillCallbackThread( th, 0, NULL, NULL ); - - goto end; -} - -static void CallbackUpdate( PaAlsaThreading *th ) -{ - th->callbackTime = PaUtil_GetTime(); - th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer ); -} - -/* prototypes for functions declared in this file */ - -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 *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 ); -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); - -/* Callback prototypes */ -static void *CallbackThreadFunc( void *userData ); - -/* Blocking prototypes */ -static signed long GetStreamReadAvailable( PaStream* s ); -static signed long GetStreamWriteAvailable( PaStream* s ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); - - -static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device ) -{ - return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; -} - -static void AlsaErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...) -{ -} - -PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = NULL; - - PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( - sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); - PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - alsaHostApi->hostApiIndex = hostApiIndex; - - *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paALSA; - (*hostApi)->info.name = "ALSA"; - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError ); - - PA_ENSURE( BuildDeviceList( alsaHostApi ) ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, - GetStreamReadAvailable, - GetStreamWriteAvailable ); - - return result; - -error: - if( alsaHostApi ) - { - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - } - - return result; -} - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - - assert( hostApi ); - - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - snd_config_update_free_global(); -} - -/** Determine max channels and default latencies. - * - * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for - * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero, - * and a suitable result returned. The device is closed before returning. - */ -static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking, - PaAlsaDeviceInfo* devInfo, int* canMmap ) -{ - PaError result = paNoError; - snd_pcm_hw_params_t *hwParams; - snd_pcm_uframes_t lowLatency = 512, highLatency = 2048; - unsigned int minChans, maxChans; - int* minChannels, * maxChannels; - double * defaultLowLatency, * defaultHighLatency, * defaultSampleRate = - &devInfo->baseDeviceInfo.defaultSampleRate; - double defaultSr = *defaultSampleRate; - - assert( pcm ); - - if( StreamDirection_In == mode ) - { - minChannels = &devInfo->minInputChannels; - maxChannels = &devInfo->baseDeviceInfo.maxInputChannels; - defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowInputLatency; - defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighInputLatency; - } - else - { - minChannels = &devInfo->minOutputChannels; - maxChannels = &devInfo->baseDeviceInfo.maxOutputChannels; - defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowOutputLatency; - defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighOutputLatency; - } - - ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); - - snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_hw_params_any( pcm, hwParams ); - - *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 || - snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0; - - if( defaultSr >= 0 ) - { - /* Could be that the device opened in one mode supports samplerates that the other mode wont have, - * so try again .. */ - if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 ) - { - defaultSr = -1.; - PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ )); - } - } - - if( defaultSr < 0. ) /* Default sample rate not set */ - { - unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ - if( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0) - { - result = paUnanticipatedHostError; - goto error; - } - ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); - assert( maxChans <= INT_MAX ); - assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, - resulting in zeroed values */ - - /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */ - if( isPlug && maxChans > 128 ) - { - maxChans = 128; - PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans )); - } - - /* TWEAKME: - * - * Giving values for default min and max latency is not - * straightforward. Here are our objectives: - * - * * for low latency, we want to give the lowest value - * that will work reliably. This varies based on the - * sound card, kernel, CPU, etc. I think it is better - * to give sub-optimal latency than to give a number - * too low and cause dropouts. My conservative - * estimate at this point is to base it on 4096-sample - * latency at 44.1 kHz, which gives a latency of 23ms. - * * for high latency we want to give a large enough - * value that dropouts are basically impossible. This - * doesn't really require as much tweaking, since - * providing too large a number will just cause us to - * select the nearest setting that will work at stream - * config time. - */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); - - /* Have to reset hwParams, to set new buffer size */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); - - *minChannels = (int)minChans; - *maxChannels = (int)maxChans; - *defaultSampleRate = defaultSr; - *defaultLowLatency = (double) lowLatency / *defaultSampleRate; - *defaultHighLatency = (double) highLatency / *defaultSampleRate; - -end: - snd_pcm_close( pcm ); - return result; - -error: - goto end; -} - -/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate - * wether input/output is available) */ -static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ) -{ - deviceInfo->structVersion = -1; - deviceInfo->name = NULL; - deviceInfo->hostApi = -1; - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - deviceInfo->defaultLowInputLatency = -1.; - deviceInfo->defaultLowOutputLatency = -1.; - deviceInfo->defaultHighInputLatency = -1.; - deviceInfo->defaultHighOutputLatency = -1.; - deviceInfo->defaultSampleRate = -1.; -} - -/* Helper struct */ -typedef struct -{ - char *alsaName; - char *name; - int isPlug; - int hasPlayback; - int hasCapture; -} DeviceNames; - -static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi, - char **dst, - const char *src) -{ - PaError result = paNoError; - int len = strlen( src ) + 1; - - /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */ - - PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - strncpy( *dst, src, len ); - -error: - return result; -} - -/* Disregard some standard plugins - */ -static int IgnorePlugin( const char *pluginId ) -{ - /* XXX: dmix and default ignored because after opening and closing, they seem to keep hogging resources. - */ - static const char *ignoredPlugins[] = {"hw", "plughw", "plug", "dsnoop", "tee", - "file", "null", "shm", "cards", "dmix", "default", NULL}; - int i = 0; - while( ignoredPlugins[i] ) - { - if( !strcmp( pluginId, ignoredPlugins[i] ) ) - { - return 1; - } - ++i; - } - - return 0; -} - -/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) -{ - PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; - PaAlsaDeviceInfo *deviceInfoArray; - int cardIdx = -1, devIdx = 0; - snd_ctl_card_info_t *cardInfo; - PaError result = paNoError; - size_t numDeviceNames = 0, maxDeviceNames = 1, i; - DeviceNames *deviceNames = NULL; - snd_config_t *topNode = NULL; - snd_pcm_info_t *pcmInfo; - int res; - int blocking = SND_PCM_NONBLOCK; - char alsaCardName[50]; - if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) - blocking = 0; - - /* These two will be set to the first working input and output device, respectively */ - baseApi->info.defaultInputDevice = paNoDevice; - baseApi->info.defaultOutputDevice = paNoDevice; - - /* 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. */ - cardIdx = -1; - snd_ctl_card_info_alloca( &cardInfo ); - snd_pcm_info_alloca( &pcmInfo ); - while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) - { - char *cardName; - int devIdx = -1; - snd_ctl_t *ctl; - char buf[50]; - - snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); - - /* Acquire name of card */ - if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) - { - /* Unable to open card :( */ - continue; - } - snd_ctl_card_info( ctl, cardInfo ); - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); - - while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) - { - char *alsaDeviceName, *deviceName; - size_t len; - int hasPlayback = 0, hasCapture = 0; - snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx ); - - /* Obtain info about this particular device */ - snd_pcm_info_set_device( pcmInfo, devIdx ); - snd_pcm_info_set_subdevice( pcmInfo, 0 ); - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - { - hasCapture = 1; - } - - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - { - hasPlayback = 1; - } - - if( !hasPlayback && !hasCapture ) - { - continue; /* Error */ - } - - /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; - PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - snprintf( deviceName, len, "%s: %s (%s)", cardName, - snd_pcm_info_get_name( pcmInfo ), buf ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); - - deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName; - deviceNames[ numDeviceNames - 1 ].name = deviceName; - deviceNames[ numDeviceNames - 1 ].isPlug = 0; - deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback; - deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture; - } - snd_ctl_close( ctl ); - } - - /* Iterate over plugin devices */ - if( NULL == snd_config ) - { - /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ - ENSURE_( snd_config_update(), paUnanticipatedHostError ); - PA_DEBUG(( "Updating snd_config\n" )); - } - assert( snd_config ); - if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) - { - snd_config_iterator_t i, next; - - snd_config_for_each( i, next, topNode ) - { - const char *tpStr = "unknown", *idStr = NULL; - int err = 0; - - char *alsaDeviceName, *deviceName; - snd_config_t *n = snd_config_iterator_entry( i ), * tp = NULL;; - - if( (err = snd_config_search( n, "type", &tp )) < 0 ) - { - if( -ENOENT != err ) - { - ENSURE_(err, paUnanticipatedHostError); - } - } - else - { - ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); - } - ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); - if( IgnorePlugin( idStr ) ) - { - PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); - continue; - } - PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); - - PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 6 ), paInsufficientMemory ); - strcpy( alsaDeviceName, idStr ); - PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 1 ), paInsufficientMemory ); - strcpy( deviceName, idStr ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName; - deviceNames[numDeviceNames - 1].name = deviceName; - deviceNames[numDeviceNames - 1].isPlug = 1; - deviceNames[numDeviceNames - 1].hasPlayback = 1; - deviceNames[numDeviceNames - 1].hasCapture = 1; - } - } - else - PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); - - /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); - - /* allocate all device info structs in a contiguous block */ - PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); - - /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name), - * it's ignored. - */ - /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */ - for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) - { - snd_pcm_t *pcm; - PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx]; - PaDeviceInfo *baseDeviceInfo = &deviceInfo->baseDeviceInfo; - int canMmap = -1; - - /* Zero fields */ - InitializeDeviceInfo( baseDeviceInfo ); - - /* to determine device capabilities, we must open the device and query the - * hardware parameter configuration space */ - - /* Query capture */ - if( deviceNames[i].hasCapture && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 ) - { - if( GropeDevice( pcm, deviceNames[i].isPlug, StreamDirection_In, blocking, deviceInfo, - &canMmap ) != paNoError ) - { - /* Error */ - PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceNames[i].alsaName)); - continue; - } - } - - /* Query playback */ - if( deviceNames[i].hasPlayback && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 ) - { - if( GropeDevice( pcm, deviceNames[i].isPlug, StreamDirection_Out, blocking, deviceInfo, - &canMmap ) != paNoError ) - { - /* Error */ - PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceNames[i].alsaName)); - continue; - } - } - - if( 0 == canMmap ) - { - PA_DEBUG(("%s: Device %s doesn't support mmap\n", __FUNCTION__, deviceNames[i].alsaName)); - continue; - } - - baseDeviceInfo->structVersion = 2; - baseDeviceInfo->hostApi = alsaApi->hostApiIndex; - baseDeviceInfo->name = deviceNames[i].name; - deviceInfo->alsaName = deviceNames[i].alsaName; - deviceInfo->isPlug = deviceNames[i].isPlug; - - /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. - * Should now be safe to add device info, unless the device supports neither capture nor playback - */ - if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 ) - { - if( baseApi->info.defaultInputDevice == paNoDevice && baseDeviceInfo->maxInputChannels > 0 ) - baseApi->info.defaultInputDevice = devIdx; - if( baseApi->info.defaultOutputDevice == paNoDevice && baseDeviceInfo->maxOutputChannels > 0 ) - baseApi->info.defaultOutputDevice = devIdx; - PA_DEBUG(("%s: Adding device %s\n", __FUNCTION__, deviceNames[i].name)); - baseApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo; - } - } - free( deviceNames ); - - baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ - -end: - return result; - -error: - /* No particular action */ - goto end; -} - -/* Check against known device capabilities */ -static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode ) -{ - PaError result = paNoError; - int maxChans; - const PaAlsaDeviceInfo *deviceInfo = NULL; - assert( parameters ); - - if( parameters->device != paUseHostApiSpecificDeviceSpecification ) - { - assert( parameters->device < hostApi->info.deviceCount ); - PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination ); - deviceInfo = GetDeviceInfo( hostApi, parameters->device ); - } - else - { - const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo; - - PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice ); - PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1, - paIncompatibleHostApiSpecificStreamInfo ); - PA_UNLESS( streamInfo->deviceString != NULL, paInvalidDevice ); - - /* Skip further checking */ - return paNoError; - } - - assert( deviceInfo ); - assert( parameters->hostApiSpecificStreamInfo == NULL ); - maxChans = (StreamDirection_In == mode ? deviceInfo->baseDeviceInfo.maxInputChannels : - deviceInfo->baseDeviceInfo.maxOutputChannels); - PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount ); - -error: - return result; -} - -/* Given an open stream, what sample formats are available? */ -static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) -{ - PaSampleFormat available = 0; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) - available |= paFloat32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) - available |= paInt32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0) - available |= paInt24; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) - available |= paInt16; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) - available |= paUInt8; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) - available |= paInt8; - - return available; -} - -static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) -{ - switch( paFormat ) - { - case paFloat32: - return SND_PCM_FORMAT_FLOAT; - - case paInt16: - return SND_PCM_FORMAT_S16; - - case paInt24: - return SND_PCM_FORMAT_S24; - - case paInt32: - return SND_PCM_FORMAT_S32; - - case paInt8: - return SND_PCM_FORMAT_S8; - - case paUInt8: - return SND_PCM_FORMAT_U8; - - default: - return SND_PCM_FORMAT_UNKNOWN; - } -} - -/** Open an ALSA pcm handle. - * - * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a - * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin - * device. - */ -static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection - streamDir, snd_pcm_t **pcm ) -{ - PaError result = paNoError; - int ret; - const char *deviceName = alloca( 50 ); - const PaAlsaDeviceInfo *deviceInfo = NULL; - PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo; - - if( !streamInfo ) - { - int usePlug = 0; - deviceInfo = GetDeviceInfo( hostApi, params->device ); - - /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */ - if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) ) - usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) ); - if( usePlug ) - snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName ); - else - deviceName = deviceInfo->alsaName; - } - else - deviceName = streamInfo->deviceString; - - PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName )); - if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK )) < 0 ) - { - /* Not to be closed */ - *pcm = NULL; - ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination ); - } - ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); - -end: - return result; - -error: - goto end; -} - -static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters, - double sampleRate, StreamDirection streamDir ) -{ - PaError result = paNoError; - snd_pcm_t *pcm = NULL; - PaSampleFormat availableFormats; - /* We are able to adapt to a number of channels less than what the device supports */ - unsigned int numHostChannels; - PaSampleFormat hostFormat; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - if( !parameters->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device ); - numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ? - devInfo->minInputChannels : devInfo->minOutputChannels ); - } - else - numHostChannels = parameters->channelCount; - - PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 ) - { - result = paInvalidSampleRate; - goto error; - } - - if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) - { - result = paInvalidChannelCount; - goto error; - } - - /* See if we can find a best possible match */ - availableFormats = GetAvailableFormats( pcm ); - PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); - - { - /* It happens that this call fails because the device is busy */ - int ret = 0; - if( (ret = snd_pcm_hw_params( pcm, hwParams )) < 0) - { - ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paUnanticipatedHostError ); - } - } - -end: - if( pcm ) - { - snd_pcm_close( pcm ); - } - return result; - -error: - goto end; -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaError result = paFormatIsSupported; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - if( inputChannelCount ) - { - if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In )) - != paNoError ) - goto error; - } - if ( outputChannelCount ) - { - if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out )) - != paNoError ) - goto error; - } - - return paFormatIsSupported; - -error: - return result; -} - -static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, - const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) -{ - PaError result = paNoError; - PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat; - assert( params->channelCount > 0 ); - - /* Make sure things have an initial value */ - memset( self, 0, sizeof (PaAlsaStreamComponent) ); - - if( NULL == params->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device ); - self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels - : devInfo->minOutputChannels ); - } - else - { - /* We're blissfully unaware of the minimum channelCount */ - self->numHostChannels = params->channelCount; - } - - PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) ); - self->nfds = snd_pcm_poll_descriptors_count( self->pcm ); - hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ); - - self->hostSampleFormat = hostSampleFormat; - self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); - self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved); - self->numUserChannels = params->channelCount; - self->streamDir = streamDir; - - if( !callbackMode && !self->userInterleaved ) - { - /* Pre-allocate non-interleaved user provided buffers */ - PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ), - paInsufficientMemory ); - } - -error: - return result; -} - -static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self ) -{ - snd_pcm_close( self->pcm ); - if( self->userBuffers ) - PaUtil_FreeMemory( self->userBuffers ); -} - -int nearbyint_(float value) { - if( value - (int)value > .5 ) - return (int)ceil( value ); - return (int)floor( value ); -} - -/** Initiate configuration, preparing for determining a period size suitable for both capture and playback components. - * - */ -static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *self, const PaStreamParameters *params, - int primeBuffers, snd_pcm_hw_params_t *hwParams, double *sampleRate ) -{ - /* 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. - */ - - PaError result = paNoError; - snd_pcm_access_t accessMode, alternateAccessMode; - int dir = 0; - snd_pcm_t *pcm = self->pcm; - double sr = *sampleRate; - unsigned int minPeriods = 2; - - /* self->framesPerBuffer = framesPerHostBuffer; */ - - /* ... fill up the configuration space with all possibile - * combinations of parameters this device will accept */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); - /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */ - dir = 0; - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); - - if( self->userInterleaved ) - { - accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - } - else - { - accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - } - /* If requested access mode fails, try alternate mode */ - if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) - { - int err = 0; - if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0) - { - result = paUnanticipatedHostError; - if( -EINVAL == err ) - { - PaUtil_SetLastHostErrorInfo( paALSA, err, "PA ALSA requires that a device supports mmap access" ); - } - else - { - PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) ); - } - goto error; - } - /* Flip mode */ - self->hostInterleaved = !self->userInterleaved; - } - - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); - - ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate ); - ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError ); - /* reject if there's no sample rate within 1% of the one requested */ - if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 ) - { - PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); - PA_ENSURE( paInvalidSampleRate ); - } - - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); - - *sampleRate = sr; - -end: - return result; - -error: - /* No particular action */ - goto end; -} - -/** Finish the configuration of the component's ALSA device. - * - * As part of this method, the component's bufferSize attribute will be set. - * @param latency: The latency for this component. - */ -static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *self, snd_pcm_hw_params_t* hwParams, - const PaStreamParameters *params, int primeBuffers, double sampleRate, PaTime* latency ) -{ - PaError result = paNoError; - snd_pcm_sw_params_t* swParams; - snd_pcm_uframes_t bufSz = 0; - *latency = -1.; - - snd_pcm_sw_params_alloca( &swParams ); - - bufSz = (params->suggestedLatency * sampleRate) + self->framesPerBuffer; /* One period does not count as latency */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_hw_params( self->pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); - /* Latency in seconds, one period is not counted as latency */ - *latency = (self->bufferSize - self->framesPerBuffer) / sampleRate; - - /* Now software parameters... */ - ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); - - /* Silence buffer in the case of underrun */ - if( !primeBuffers ) /* XXX: Make sense? */ - { - snd_pcm_uframes_t boundary; - ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); - -error: - return result; -} - -static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams, - const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback, - PaStreamFlags streamFlags, void *userData ) -{ - PaError result = paNoError; - assert( self ); - - memset( self, 0, sizeof (PaAlsaStream) ); - - if( NULL != callback ) - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->callbackStreamInterface, - callback, userData ); - self->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->blockingStreamInterface, - NULL, userData ); - } - - self->framesPerUserBuffer = framesPerUserBuffer; - self->neverDropInput = streamFlags & paNeverDropInput; - /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */ - /* - if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback ) - self->primeBuffers = 1; - */ - memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) ); - memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) ); - if( inParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) ); - if( outParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) ); - - assert( self->capture.nfds || self->playback.nfds ); - - PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds + - self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory ); - - PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate ); - InitializeThreading( &self->threading, &self->cpuLoadMeasurer ); - ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 ); - -error: - return result; -} - -/** Free resources associated with stream, and eventually stream itself. - * - * Frees allocated memory, and terminates individual StreamComponents. - */ -static void PaAlsaStream_Terminate( PaAlsaStream *self ) -{ - assert( self ); - - if( self->capture.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->capture ); - } - if( self->playback.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->playback ); - } - - PaUtil_FreeMemory( self->pfds ); - ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 ); - ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 ); - ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 ); - - PaUtil_FreeMemory( self ); -} - -/** Calculate polling timeout - * - * @param frames Time to wait - * @return Polling timeout in milliseconds - */ -static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames ) -{ - assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 ); - /* Period in msecs, rounded up */ - return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate ); -} - -/** Determine size per host buffer. - * - * During this method call, the component's framesPerBuffer attribute gets computed, and the corresponding period size - * gets configured for the device. - * @param accurate: If the configured period size is non-integer, this will be set to 0. - */ -static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params, - unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams, int* accurate ) -{ - PaError result = paNoError; - unsigned long bufferSize = params->suggestedLatency * sampleRate, framesPerHostBuffer; - int dir = 0; - - { - snd_pcm_uframes_t tmp; - snd_pcm_hw_params_get_buffer_size_min( hwParams, &tmp ); - bufferSize = PA_MAX( bufferSize, tmp ); - snd_pcm_hw_params_get_buffer_size_max( hwParams, &tmp ); - bufferSize = PA_MIN( bufferSize, tmp ); - } - - assert( bufferSize > 0 ); - - if( framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - /* Preferably the host buffer size should be a multiple of the user buffer size */ - - if( bufferSize > framesPerUserBuffer ) - { - snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer; - if( remainder > framesPerUserBuffer / 2. ) - bufferSize += framesPerUserBuffer - remainder; - else - bufferSize -= remainder; - - assert( bufferSize % framesPerUserBuffer == 0 ); - } - else if( framesPerUserBuffer % bufferSize != 0 ) - { - /* Find a good compromise between user specified latency and buffer size */ - if( bufferSize > framesPerUserBuffer * .75 ) - { - bufferSize = framesPerUserBuffer; - } - else - { - snd_pcm_uframes_t newSz = framesPerUserBuffer; - while( newSz / 2 >= bufferSize ) - { - if( framesPerUserBuffer % (newSz / 2) != 0 ) - { - /* No use dividing any further */ - break; - } - newSz /= 2; - } - bufferSize = newSz; - } - - assert( framesPerUserBuffer % bufferSize == 0 ); - } - } - - /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period), - finding a combination of period/buffer size which best fits these constraints */ - { - unsigned numPeriods = 4, maxPeriods = 0; - /* It may be that the device only supports 2 periods for instance */ - dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError ); - assert( maxPeriods > 1 ); - /* One period is not counted as latency */ - maxPeriods -= 1; - numPeriods = PA_MIN( maxPeriods, numPeriods ); - - if( framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - framesPerHostBuffer = framesPerUserBuffer; - if( framesPerHostBuffer < bufferSize ) - { - while( bufferSize / framesPerHostBuffer > numPeriods ) - { - framesPerHostBuffer *= 2; - } - } - else - { - while( bufferSize / framesPerHostBuffer < numPeriods ) - { - if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 ) - { - /* Can't be divided any further */ - break; - } - framesPerHostBuffer /= 2; - } - } - - if( framesPerHostBuffer < framesPerUserBuffer ) - { - assert( framesPerUserBuffer % framesPerHostBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) - { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) - framesPerHostBuffer *= 2; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) - framesPerHostBuffer /= 2; - } - } - else - { - assert( framesPerHostBuffer % framesPerUserBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) - { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) - framesPerHostBuffer += framesPerUserBuffer; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) - framesPerHostBuffer -= framesPerUserBuffer; - } - } - } - else - { - framesPerHostBuffer = bufferSize / numPeriods; - } - } - - assert( framesPerHostBuffer > 0 ); - { - snd_pcm_uframes_t min = 0, max = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError ); - - if( framesPerHostBuffer < min ) - { - framesPerHostBuffer = min; - PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__, - framesPerHostBuffer, min )); - } - else if( framesPerHostBuffer > max ) - { - framesPerHostBuffer = max; - PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__, - framesPerHostBuffer, max )); - } - - assert( framesPerHostBuffer >= min && framesPerHostBuffer <= max ); - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), - paUnanticipatedHostError ); - if( dir != 0 ) - { - PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir )); - *accurate = 0; - } - } - self->framesPerBuffer = framesPerHostBuffer; - -error: - return result; -} - -/* We need to determine how many frames per host buffer (period) to use. Our - * goals are to provide the best possible performance, but also to - * honor the requested latency settings as closely as we can. Therefore this - * decision is based on: - * - * - the period sizes that playback and/or capture support. The - * host buffer size has to be one of these. - * - the number of periods that playback and/or capture support. - * - * We want to make period_size*(num_periods-1) to be as close as possible - * to latency*rate for both playback and capture. - * - * This method will determine suitable period sizes for capture and playback handles, and report the maximum number of - * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs - * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to - * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper - * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size, - * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size, - * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user - * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size. - * - * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding - * values determined here. Since these should be reported as - * - * This is one of those blocks of code that will just take a lot of - * refinement to be any good. - * - * In the full-duplex case it is possible that the routine was unable - * to find a number of frames per buffer acceptable to both devices - * TODO: Implement an algorithm to find the value closest to acceptance - * by both devices, to minimize difference between period sizes? - * - * @param determinedFramesPerHostBuffer: The determined host buffer size. - */ -static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters, - const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture, - snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode ) -{ - PaError result = paNoError; - unsigned long framesPerHostBuffer = 0; - int dir = 0; - int accurate = 1; - - if( self->capture.pcm && self->playback.pcm ) - { - if( framesPerUserBuffer == paFramesPerBufferUnspecified ) - { - snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize, - minCapture, minPlayback, maxCapture, maxPlayback; - - /* Come up with a common desired latency */ - - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); - minPeriodSize = PA_MAX( minPlayback, minCapture ); - maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); - PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination ); - - desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency ) - * sampleRate); - /* Clamp desiredLatency */ - { - snd_pcm_uframes_t maxBufferSize; - snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback; - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError ); - maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback ); - - desiredLatency = PA_MIN( desiredLatency, maxBufferSize ); - } - - /* Find the closest power of 2 */ - e = ilogb( minPeriodSize ); - if( minPeriodSize & (minPeriodSize - 1) ) - e += 1; - periodSize = (snd_pcm_uframes_t)pow( 2, e ); - - while( periodSize <= maxPeriodSize ) - { - if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && - snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) - break; /* Ok! */ - - periodSize *= 2; - } - - /* 4 periods considered optimal */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - /* Find the closest power of 2 */ - e = ilogb( optimalPeriodSize ); - if( optimalPeriodSize & (optimalPeriodSize - 1) ) - e += 1; - optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e ); - - while( optimalPeriodSize >= periodSize ) - { - if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 ) - continue; - if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 ) - break; - optimalPeriodSize /= 2; - } - if( optimalPeriodSize > periodSize ) - periodSize = optimalPeriodSize; - - if( periodSize <= maxPeriodSize ) - { - /* Looks good, the periodSize _should_ be acceptable by both devices */ - ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ), - paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ), - paUnanticipatedHostError ); - self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize; - framesPerHostBuffer = periodSize; - } - else - { - /* Unable to find a common period size, oh well */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - self->capture.framesPerBuffer = optimalPeriodSize; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ), - paUnanticipatedHostError ); - self->playback.framesPerBuffer = optimalPeriodSize; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ), - paUnanticipatedHostError ); - framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer ); - *hostBufferSizeMode = paUtilBoundedHostBufferSize; - } - } - else - { - /* We choose the simple route and determine a suitable number of frames per buffer for one component of - * the stream, then we hope that this will work for the other component too (it should!). - */ - - unsigned maxPeriods = 0; - PaAlsaStreamComponent* first = &self->capture, * second = &self->playback; - const PaStreamParameters* firstStreamParams = inputParameters; - snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback; - - dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError ); - if( maxPeriods < 4 ) - { - /* The playback component is trickier to get right, try that first */ - first = &self->playback; - second = &self->capture; - firstStreamParams = outputParameters; - firstHwParams = hwParamsPlayback; - secondHwParams = hwParamsCapture; - } - - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( first, firstStreamParams, framesPerUserBuffer, - sampleRate, firstHwParams, &accurate ) ); - - second->framesPerBuffer = first->framesPerBuffer; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ), - paUnanticipatedHostError ); - if( self->capture.framesPerBuffer == self->playback.framesPerBuffer ) - { - framesPerHostBuffer = self->capture.framesPerBuffer; - } - else - { - framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer ); - *hostBufferSizeMode = paUtilBoundedHostBufferSize; - } - } - } - else /* half-duplex is a slightly simpler case */ - { - if( self->capture.pcm ) - { - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->capture, inputParameters, framesPerUserBuffer, - sampleRate, hwParamsCapture, &accurate) ); - framesPerHostBuffer = self->capture.framesPerBuffer; - } - else - { - assert( self->playback.pcm ); - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->playback, outputParameters, framesPerUserBuffer, - sampleRate, hwParamsPlayback, &accurate ) ); - framesPerHostBuffer = self->playback.framesPerBuffer; - } - } - - PA_UNLESS( framesPerHostBuffer != 0, paInternalError ); - self->maxFramesPerHostBuffer = framesPerHostBuffer; - - if( !accurate ) - { - /* Don't know the exact size per host buffer */ - *hostBufferSizeMode = paUtilBoundedHostBufferSize; - /* Raise upper bound */ - ++self->maxFramesPerHostBuffer; - } - -error: - return result; -} - -/** Set up ALSA stream parameters. - * - */ -static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters* - outParams, double sampleRate, unsigned long framesPerUserBuffer, double* inputLatency, double* outputLatency, - PaUtilHostBufferSizeMode* hostBufferSizeMode ) -{ - PaError result = paNoError; - double realSr = sampleRate; - snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback; - - snd_pcm_hw_params_alloca( &hwParamsCapture ); - snd_pcm_hw_params_alloca( &hwParamsPlayback ); - - if( self->capture.pcm ) - PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture, - &realSr ) ); - if( self->playback.pcm ) - PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->playback, outParams, self->primeBuffers, hwParamsPlayback, - &realSr ) ); - - PA_ENSURE( PaAlsaStream_DetermineFramesPerBuffer( self, realSr, inParams, outParams, framesPerUserBuffer, - hwParamsCapture, hwParamsPlayback, hostBufferSizeMode ) ); - - if( self->capture.pcm ) - { - assert( self->capture.framesPerBuffer != 0 ); - PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->capture, hwParamsCapture, inParams, self->primeBuffers, realSr, - inputLatency ) ); - PA_DEBUG(( "%s: Capture period size: %lu, latency: %f\n", __FUNCTION__, self->capture.framesPerBuffer, *inputLatency )); - } - if( self->playback.pcm ) - { - assert( self->playback.framesPerBuffer != 0 ); - PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->playback, hwParamsPlayback, outParams, self->primeBuffers, realSr, - outputLatency ) ); - PA_DEBUG(( "%s: Playback period size: %lu, latency: %f\n", __FUNCTION__, self->playback.framesPerBuffer, *outputLatency )); - } - - /* Should be exact now */ - self->streamRepresentation.streamInfo.sampleRate = realSr; - - /* this will cause the two streams to automatically start/stop/prepare in sync. - * We only need to execute these operations on one of the pair. - * A: We don't want to do this on a blocking stream. - */ - if( self->callbackMode && self->capture.pcm && self->playback.pcm ) - { - int err = snd_pcm_link( self->capture.pcm, self->playback.pcm ); - if( err == 0 ) - self->pcmsSynced = 1; - else - PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) )); - } - - { - unsigned long minFramesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX, - self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX ); - self->pollTimeout = CalculatePollTimeout( self, minFramesPerHostBuffer ); /* Period in msecs, rounded up */ - - /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */ - self->threading.throttledSleepTime = (unsigned long) (minFramesPerHostBuffer / sampleRate / 4 * 1000); - } - - if( self->callbackMode ) - { - /* If the user expects a certain number of frames per callback we will either have to rely on block adaption - * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number - * of host buffer frames with what the user specified */ - if( self->framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - /* self->alignFrames = 1; */ - - /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely - * on block adaption */ - /* - if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm && - self->capture.framesPerBuffer != self->playback.framesPerBuffer) ) - self->useBlockAdaption = 1; - else - self->alignFrames = 1; - */ - } - } - -error: - return result; -} - -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 *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - PaAlsaStream *stream = NULL; - PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - int numInputChannels = 0, numOutputChannels = 0; - PaTime inputLatency, outputLatency; - /* Operate with fixed host buffer size by default, since other modes will invariably lead to block adaption */ - /* XXX: Use Bounded by default? Output tends to get stuttery with Fixed ... */ - PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilFixedHostBufferSize; - - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - numInputChannels = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - numOutputChannels = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - /* XXX: Why do we support this anyway? */ - if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL ) - { - PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ )); - framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") ); - } - - PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory ); - PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, - framesPerBuffer, callback, streamFlags, userData ) ); - - PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer, - &inputLatency, &outputLatency, &hostBufferSizeMode ) ); - hostInputSampleFormat = stream->capture.hostSampleFormat; - hostOutputSampleFormat = stream->playback.hostSampleFormat; - - PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - numInputChannels, inputSampleFormat, hostInputSampleFormat, - numOutputChannels, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer, - hostBufferSizeMode, callback, userData ) ); - - /* Ok, buffer processor is initialized, now we can deduce it's latency */ - if( numInputChannels > 0 ) - stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency( - &stream->bufferProcessor ); - if( numOutputChannels > 0 ) - stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency( - &stream->bufferProcessor ); - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - { - PA_DEBUG(( "%s: Stream in error, terminating\n", __FUNCTION__ )); - PaAlsaStream_Terminate( stream ); - } - - return result; -} - -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - PaAlsaStream_Terminate( stream ); - - return result; -} - -static void SilenceBuffer( PaAlsaStream *stream ) -{ - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset; - - snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); - snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); - snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); -} - -/** Start/prepare pcm(s) for streaming. - * - * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply - * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and - * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will - * be started automatically as the user writes to output. - * - * The capture pcm, however, will simply be prepared and started. - * - * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode) - */ -static PaError AlsaStart( PaAlsaStream *stream, int priming ) -{ - PaError result = paNoError; - - if( stream->playback.pcm ) - { - if( stream->callbackMode ) - { - if( !priming ) - { - /* Buffer isn't primed, so prepare and silence */ - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - SilenceBuffer( stream ); - } - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - else - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */ - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - -end: - return result; -error: - goto end; -} - -/** Utility function for determining if pcms are in running state. - * - */ -static int IsRunning( PaAlsaStream *stream ) -{ - int result = 0; - - LockMutex( &stream->stateMtx ); - if( stream->capture.pcm ) - { - snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm ); - - if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN - || capture_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - - if( stream->playback.pcm ) - { - snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm ); - - if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN - || playback_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - -end: - ASSERT_CALL_( UnlockMutex( &stream->stateMtx ), paNoError ); - - return result; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - int streamStarted = 0; /* So we can know wether we need to take the stream down */ - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* Set now, so we can test for activity further down */ - stream->isActive = 1; - - if( stream->callbackMode ) - { - int res = 0; - PaTime pt = PaUtil_GetTime(); - struct timespec ts; - - PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) ); - streamStarted = 1; - - /* Wait for stream to be started */ - ts.tv_sec = (time_t) floor( pt + 1 ); - ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); - - /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking - * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely - * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */ - PA_ENSURE( LockMutex( &stream->startMtx ) ); - - /* Due to possible spurious wakeups, we enclose in a loop */ - while( !IsRunning( stream ) && IsStreamActive( s ) && !res ) - { - res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts ); - } - PA_ENSURE( UnlockMutex( &stream->startMtx ) ); - - PA_UNLESS( !res || res == ETIMEDOUT, paInternalError ); - PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt )); - - if( res == ETIMEDOUT ) - { - PA_ENSURE( paTimedOut ); - } - } - else - { - PA_ENSURE( AlsaStart( stream, 0 ) ); - streamStarted = 1; - } - -end: - return result; -error: - if( streamStarted ) - AbortStream( stream ); - stream->isActive = 0; - - goto end; -} - -static PaError AlsaStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - if( abort ) - { - if( stream->playback.pcm ) - { - ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); - } - - PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ )); - } - else - { - if( stream->playback.pcm ) - { - ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError ); - if( snd_pcm_drain( stream->playback.pcm ) < 0 ) - { - PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ )); - } - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - /* We don't need to retrieve any remaining frames */ - if( snd_pcm_drop( stream->capture.pcm ) < 0 ) - { - PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ )); - } - } - } - -end: - return result; -error: - goto end; -} - -/** Stop or abort stream. - * - * If a stream is in callback mode we will have to inspect wether the background thread has - * finished, or we will have to take it out. In either case we join the thread before - * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish - * buffers (drain) - * - * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function - */ -static PaError RealStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - /* First deal with the callback thread, cancelling and/or joining - * it if necessary - */ - if( stream->callbackMode ) - { - PaError threadRes, watchdogRes; - stream->callbackAbort = abort; - - if( !abort ) - { - PA_DEBUG(( "Stopping callback\n" )); - stream->callbackStop = 1; - } - PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) ); - if( threadRes != paNoError ) - PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); - if( watchdogRes != paNoError ) - PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes )); - - stream->callbackStop = 0; /* The deed is done */ - stream->callback_finished = 0; - } - else - { - PA_ENSURE( AlsaStop( stream, abort ) ); - } - - stream->isActive = 0; - -end: - return result; - -error: - goto end; -} - -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream *) s, 0 ); -} - -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream * ) s, 1 ); -} - -/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback - * returning !paContinue is not considered) - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream *)s; - - /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */ - return !IsStreamActive( s ) && !stream->callback_finished; -} - -static PaError IsStreamActive( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - return stream->isActive; -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - 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 from multiple threads. - * need to verify that libasound is thread-safe. */ - - if( stream->capture.pcm ) - { - snd_pcm_status( stream->capture.pcm, status ); - } - else if( stream->playback.pcm ) - { - snd_pcm_status( stream->playback.pcm, status ); - } - - snd_pcm_status_get_tstamp( status, ×tamp ); - return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6; -} - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ) -{ - unsigned long approx = (unsigned long) sampleRate; - int dir = 0; - double fraction = sampleRate - approx; - - assert( pcm && hwParams ); - - if( fraction > 0.0 ) - { - if( fraction > 0.5 ) - { - ++approx; - dir = -1; - } - else - dir = 1; - } - - return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ); -} - -/* Return exact sample rate in param sampleRate */ -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ) -{ - unsigned int num, den; - int err; - - assert( hwParams ); - - err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); - *sampleRate = (double) num / den; - - return err; -} - -/* Utility functions for blocking/callback interfaces */ - -/* Atomic restart of stream (we don't want the intermediate state visible) */ -static PaError AlsaRestart( PaAlsaStream *stream ) -{ - PaError result = paNoError; - - PA_ENSURE( LockMutex( &stream->stateMtx ) ); - PA_ENSURE( AlsaStop( stream, 0 ) ); - PA_ENSURE( AlsaStart( stream, 0 ) ); - - PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ )); - -error: - PA_ENSURE( UnlockMutex( &stream->stateMtx ) ); - - return result; -} - -/** Recover from xrun state. - * - */ -static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) -{ - PaError result = paNoError; - snd_pcm_status_t *st; - PaTime now = PaUtil_GetTime(); - snd_timestamp_t t; - - snd_pcm_status_alloca( &st ); - - if( self->playback.pcm ) - { - snd_pcm_status( self->playback.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - if( self->capture.pcm ) - { - snd_pcm_status( self->capture.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - - PA_ENSURE( AlsaRestart( self ) ); - -end: - return result; -error: - goto end; -} - -/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout. - * - */ -static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll ) -{ - PaError result = paNoError; - snd_pcm_sframes_t delay, margin; - int err; - const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL; - - *continuePoll = 1; - - if( StreamDirection_In == streamDir ) - { - component = &stream->capture; - otherComponent = &stream->playback; - } - else - { - component = &stream->playback; - otherComponent = &stream->capture; - } - - /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */ - if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) - { - if( err == -EPIPE ) - { - /* Xrun */ - *continuePoll = 0; - goto error; - } - - ENSURE_( err, paUnanticipatedHostError ); - } - - if( StreamDirection_Out == streamDir ) - { - /* Number of eligible frames before capture overrun */ - delay = otherComponent->bufferSize - delay; - } - margin = delay - otherComponent->framesPerBuffer / 2; - - if( margin < 0 ) - { - PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" )); - *continuePoll = 0; - } - else if( margin < otherComponent->framesPerBuffer ) - { - *pollTimeout = CalculatePollTimeout( stream, margin ); - PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n", - __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout )); - } - -error: - return result; -} - -/* Callback interface */ - -static void OnExit( void *data ) -{ - PaAlsaStream *stream = (PaAlsaStream *) data; - - assert( data ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */ - PA_DEBUG(( "%s: Stopping ALSA handles\n", __FUNCTION__ )); - AlsaStop( stream, stream->callbackAbort ); - stream->callbackAbort = 0; /* Clear state */ - - PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ )); - - /* Eventually notify user all buffers have played */ - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - stream->isActive = 0; -} - -static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo ) -{ - snd_pcm_status_t *capture_status, *playback_status; - snd_timestamp_t capture_timestamp, playback_timestamp; - PaTime capture_time = 0., playback_time = 0.; - - snd_pcm_status_alloca( &capture_status ); - snd_pcm_status_alloca( &playback_status ); - - if( stream->capture.pcm ) - { - snd_pcm_sframes_t capture_delay; - - snd_pcm_status( stream->capture.pcm, capture_status ); - snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); - - capture_time = capture_timestamp.tv_sec + - ((PaTime)capture_timestamp.tv_usec / 1000000.0); - timeInfo->currentTime = capture_time; - - capture_delay = snd_pcm_status_get_delay( capture_status ); - timeInfo->inputBufferAdcTime = timeInfo->currentTime - - (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; - } - if( stream->playback.pcm ) - { - snd_pcm_sframes_t playback_delay; - - snd_pcm_status( stream->playback.pcm, playback_status ); - snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); - - playback_time = playback_timestamp.tv_sec + - ((PaTime)playback_timestamp.tv_usec / 1000000.0); - - if( stream->capture.pcm ) /* Full duplex */ - { - /* Hmm, we have both a playback and a capture timestamp. - * Hopefully they are the same... */ - if( fabs( capture_time - playback_time ) > 0.01 ) - PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time))); - } - else - timeInfo->currentTime = playback_time; - - playback_delay = snd_pcm_status_get_delay( playback_status ); - timeInfo->outputBufferDacTime = timeInfo->currentTime + - (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; - } -} - -/** Called after buffer processing is finished. - * - * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime. - * - * @param numFrames The number of frames that has been processed - * @param xrun Return whether an xrun has occurred - */ -static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun ) -{ - PaError result = paNoError; - int res; - - /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed - * afterwards - */ - if( !self->ready ) - goto end; - - res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); - if( res == -EPIPE || res == -ESTRPIPE ) - { - *xrun = 1; - } - else - { - ENSURE_( res, paUnanticipatedHostError ); - } - -end: -error: - return result; -} - -/* Extract buffer from channel area */ -static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset ) -{ - return (unsigned char *) area->addr + (area->first + offset * area->step) / 8; -} - -/** Do necessary adaption between user and host channels. - * - @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and - duplicating mono information if host outputs come in pairs. - */ -static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames ) -{ - PaError result = paNoError; - unsigned char *p; - int i; - int unusedChans = self->numHostChannels - self->numUserChannels; - unsigned char *src, *dst; - int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0; - - assert( StreamDirection_Out == self->streamDir ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset ); - - /* Start after the last user channel */ - p = buffer + self->numUserChannels * swidth; - - if( convertMono ) - { - /* Convert the last user channel into stereo pair */ - src = buffer + (self->numUserChannels - 1) * swidth; - for( i = 0; i < numFrames; ++i ) - { - dst = src + swidth; - memcpy( dst, src, swidth ); - src += self->numHostChannels * swidth; - } - - /* Don't touch the channel we just wrote to */ - p += swidth; - --unusedChans; - } - - if( unusedChans > 0 ) - { - /* Silence unused output channels */ - for( i = 0; i < numFrames; ++i ) - { - memset( p, 0, swidth * unusedChans ); - p += self->numHostChannels * swidth; - } - } - } - else - { - /* We extract the last user channel */ - if( convertMono ) - { - ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + - (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError ); - --unusedChans; - } - if( unusedChans > 0 ) - { - snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, - self->nativeFormat ); - } - } - -error: - return result; -} - -static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - int xrun = 0; - - if( self->capture.pcm ) - { - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) ); - } - if( self->playback.pcm ) - { - if( self->playback.numHostChannels > self->playback.numUserChannels ) - PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) ); - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) ); - } - -error: - *xrunOccurred = xrun; - return result; -} - -/** Update the number of available frames. - * - */ -static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); - *xrunOccurred = 0; - - if( -EPIPE == framesAvail ) - { - *xrunOccurred = 1; - framesAvail = 0; - } - else - ENSURE_( framesAvail, paUnanticipatedHostError ); - - *numFrames = framesAvail; - -error: - return result; -} - -/** Fill in pollfd objects. - */ -static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds ) -{ - PaError result = paNoError; - int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); - (void)ret; /* Prevent unused variable warning if asserts are turned off */ - assert( ret == self->nfds ); - - self->ready = 0; - - return result; -} - -/** Examine results from poll(). - * - * @param pfds pollfds to inspect - * @param shouldPoll Should we continue to poll - * @param xrun Has an xrun occurred - */ -static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, struct pollfd* pfds, int* shouldPoll, int* xrun ) -{ - PaError result = paNoError; - unsigned short revents; - - ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); - if( revents != 0 ) - { - if( revents & POLLERR ) - { - *xrun = 1; - } - else - self->ready = 1; - - *shouldPoll = 0; - } - -error: - return result; -} - -/** Return the number of available frames for this stream. - * - * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore - * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback. - * - * @param queryCapture Check available for capture - * @param queryPlayback Check available for playback - * @param available The returned number of frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long - *available, int *xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames, playbackFrames; - *xrunOccurred = 0; - - assert( queryCapture || queryPlayback ); - - if( queryCapture ) - { - assert( self->capture.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - if( queryPlayback ) - { - assert( self->playback.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - - if( queryCapture && queryPlayback ) - { - *available = PA_MIN( captureFrames, playbackFrames ); - /*PA_DEBUG(("capture: %lu, playback: %lu, combined: %lu\n", captureFrames, playbackFrames, *available));*/ - } - else if( queryCapture ) - { - *available = captureFrames; - } - else - { - *available = playbackFrames; - } - -end: -error: - return result; -} - -/** Wait for and report available buffer space from ALSA. - * - * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more. - * Both of these operations can uncover xrun conditions. - * - * @concern Xruns Both polling and querying available frames can report an xrun condition. - * - * @param framesAvail Return the number of available frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred ) -{ - PaError result = paNoError; - int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL; - int pollTimeout = self->pollTimeout; - int xrun = 0; - - assert( self ); - assert( framesAvail ); - - if( !self->callbackMode ) - { - /* In blocking mode we will only wait if necessary */ - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL, - framesAvail, &xrun ) ); - if( xrun ) - { - goto end; - } - - if( *framesAvail > 0 ) - { - /* Mark pcms ready from poll */ - if( self->capture.pcm ) - self->capture.ready = 1; - if( self->playback.pcm ) - self->playback.ready = 1; - - goto end; - } - } - - while( pollPlayback || pollCapture ) - { - int totalFds = 0; - struct pollfd *capturePfds = NULL, *playbackPfds = NULL; - - pthread_testcancel(); - - if( pollCapture ) - { - capturePfds = self->pfds; - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) ); - totalFds += self->capture.nfds; - } - if( pollPlayback ) - { - playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0); - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) ); - totalFds += self->playback.nfds; - } - - if( poll( self->pfds, totalFds, pollTimeout ) < 0 ) - { - /* XXX: Depend on preprocessor condition? */ - if( errno == EINTR ) - { - /* gdb */ - continue; - } - - /* TODO: Add macro for checking system calls */ - PA_ENSURE( paInternalError ); - } - - /* check the return status of our pfds */ - if( pollCapture ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) ); - } - if( pollPlayback ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) ); - } - if( xrun ) - { - break; - } - - /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two. - * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will - * stop polling. - */ - if( self->capture.pcm && self->playback.pcm ) - { - if( pollCapture && !pollPlayback ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) ); - } - else if( pollPlayback && !pollCapture ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) ); - } - } - } - - if( !xrun ) - { - /* Get the number of available frames for the pcms that are marked ready. - * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for - * the other direction is returned. Output is normally preferred over capture however, so capture frames may be - * discarded to avoid overrun unless paNeverDropInput is specified. - */ - int captureReady = self->capture.pcm ? self->capture.ready : 0, - playbackReady = self->playback.pcm ? self->playback.ready : 0; - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) ); - - if( self->capture.pcm && self->playback.pcm ) - { - if( !self->playback.ready && !self->neverDropInput ) - { - /* Drop input, a period's worth */ - assert( self->capture.ready ); - PaAlsaStreamComponent_EndProcessing( &self->capture, PA_MIN( self->capture.framesPerBuffer, - *framesAvail ), &xrun ); - *framesAvail = 0; - self->capture.ready = 0; - } - } - else if( self->capture.pcm ) - assert( self->capture.ready ); - else - assert( self->playback.ready ); - } - -end: -error: - if( xrun ) - { - /* Recover from the xrun state */ - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *framesAvail = 0; - } - else - { - if( 0 != *framesAvail ) - { - /* If we're reporting frames eligible for processing, one of the handles better be ready */ - PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError ); - } - } - *xrunOccurred = xrun; - - return result; -} - -/** Register per-channel ALSA buffer information with buffer processor. - * - * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the - * number of host and user channels is taken into account. - * - * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames. - */ -static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* self, PaUtilBufferProcessor* bp, - unsigned long* numFrames, int* xrun ) -{ - PaError result = paNoError; - const snd_pcm_channel_area_t *areas, *area; - void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) = - StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel; - unsigned char *buffer, *p; - int i; - unsigned long framesAvail; - - /* This _must_ be called before mmap_begin */ - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) ); - if( *xrun ) - { - *numFrames = 0; - goto end; - } - - ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - - p = buffer = ExtractAddress( areas, self->offset ); - for( i = 0; i < self->numUserChannels; ++i ) - { - /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */ - setChannel( bp, i, p, self->numHostChannels ); - p += swidth; - } - } - else - { - for( i = 0; i < self->numUserChannels; ++i ) - { - area = areas + i; - buffer = ExtractAddress( area, self->offset ); - setChannel( bp, i, buffer, 1 ); - } - } - - /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */ - self->channelAreas = (snd_pcm_channel_area_t *)areas; - -end: -error: - return result; -} - -/** Initiate buffer processing. - * - * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set. - * - * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is - * calculated. - * - * @param numFrames On entrance the number of available frames, on exit the number of received frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream* self, unsigned long* numFrames, int* xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0; - int xrun = 0; - - if( *xrunOccurred ) - { - *numFrames = 0; - return result; - } - /* If we got here at least one of the pcm's should be marked ready */ - PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError ); - - /* Extract per-channel ALSA buffer pointers and register them with the buffer processor. - * It is possible that a direction is not marked ready however, because it is out of sync with the other. - */ - if( self->capture.pcm && self->capture.ready ) - { - captureFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, - &xrun ) ); - } - if( self->playback.pcm && self->playback.ready ) - { - playbackFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, - &xrun ) ); - } - if( xrun ) - { - /* Nothing more to do */ - assert( 0 == commonFrames ); - goto end; - } - - commonFrames = PA_MIN( captureFrames, playbackFrames ); - /* assert( commonFrames <= *numFrames ); */ - if( commonFrames > *numFrames ) - { - /* Hmmm ... how come there are more frames available than we requested!? Blah. */ - PA_DEBUG(( "%s: Common available frames are reported to be more than number requested: %lu, %lu, callbackMode: %d\n", __FUNCTION__, - commonFrames, *numFrames, self->callbackMode )); - if( self->capture.pcm ) - { - PA_DEBUG(( "%s: captureFrames: %lu, capture.ready: %d\n", __FUNCTION__, captureFrames, self->capture.ready )); - } - if( self->playback.pcm ) - { - PA_DEBUG(( "%s: playbackFrames: %lu, playback.ready: %d\n", __FUNCTION__, playbackFrames, self->playback.ready )); - } - - commonFrames = 0; - goto end; - } - - /* Inform PortAudio of the number of frames we got. - * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on - * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply - * discard the excess input or call the callback with paOutputOverflow flagged. - */ - if( self->capture.pcm ) - { - if( self->capture.ready ) - { - PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have input underflow */ - PaUtil_SetNoInput( &self->bufferProcessor ); - } - } - if( self->playback.pcm ) - { - if( self->playback.ready ) - { - PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have output underflow, but keeping input data (paNeverDropInput) */ - assert( self->neverDropInput ); - assert( self->capture.pcm != NULL ); - PA_DEBUG(( "%s: Setting output buffers to NULL\n", __FUNCTION__ )); - PaUtil_SetNoOutput( &self->bufferProcessor ); - } - } - -end: - *numFrames = commonFrames; -error: - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *numFrames = 0; - } - *xrunOccurred = xrun; - - return result; -} - -/** Callback thread's function. - * - * Roughly, the workflow can be described in the following way: The number of available frames that can be processed - * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount - * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with - * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can - * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected). - */ -static void *CallbackThreadFunc( void *userData ) -{ - PaError result = paNoError, *pres = NULL; - PaAlsaStream *stream = (PaAlsaStream*) userData; - PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; - snd_pcm_sframes_t startThreshold = 0; - int callbackResult = paContinue; - PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ - int streamStarted = 0; - - assert( stream ); - - callbackThread_ = pthread_self(); - /* Execute OnExit when exiting */ - pthread_cleanup_push( &OnExit, stream ); - - /* Not implemented */ - assert( !stream->primeBuffers ); - - /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the - * stream is started immediately. The latter involves signaling the waiting main thread. - */ - if( stream->primeBuffers ) - { - snd_pcm_sframes_t avail; - - if( stream->playback.pcm ) - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - - /* We can't be certain that the whole ring buffer is available for priming, but there should be - * at least one period */ - avail = snd_pcm_avail_update( stream->playback.pcm ); - startThreshold = avail - (avail % stream->playback.framesPerBuffer); - assert( startThreshold >= stream->playback.framesPerBuffer ); - } - else - { - PA_ENSURE( LockMutex( &stream->startMtx ) ); - /* Buffer will be zeroed */ - PA_ENSURE( AlsaStart( stream, 0 ) ); - ENSURE_SYSTEM_( pthread_cond_signal( &stream->startCond ), 0 ); - PA_ENSURE( UnlockMutex( &stream->startMtx ) ); - - streamStarted = 1; - } - - while( 1 ) - { - unsigned long framesAvail, framesGot; - int xrun = 0; - - pthread_testcancel(); - - /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively - * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output). - */ - if( stream->callbackStop && paContinue == callbackResult ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - if( paContinue != callbackResult ) - { - stream->callbackAbort = (paAbort == callbackResult); - if( stream->callbackAbort || - /** @concern BlockAdaption: Go on if adaption buffers are empty */ - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - goto end; - - PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); - /* There is still buffered output that needs to be processed */ - } - - /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have - * a number of available frames. - */ - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - if( xrun ) - { - assert( 0 == framesAvail ); - continue; - - /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due - * to constant xruns, it might be desirable to notify the user of this. - */ - } - - /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the - * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller - * portions at a time than is available as a whole. Therefore we should be prepared to process several - * chunks successively. The buffers are passed to the PA buffer processor. - */ - while( framesAvail > 0 ) - { - xrun = 0; - - pthread_testcancel(); - - /** @concern Xruns Under/overflows are to be reported to the callback */ - if( stream->underrun > 0.0 ) - { - cbFlags |= paOutputUnderflow; - stream->underrun = 0.0; - } - if( stream->overrun > 0.0 ) - { - cbFlags |= paInputOverflow; - stream->overrun = 0.0; - } - if( stream->capture.pcm && stream->playback.pcm ) - { - /** @concern FullDuplex It's possible that only one direction is being processed to avoid an - * under- or overflow, this should be reported correspondingly */ - if( !stream->capture.ready ) - { - cbFlags |= paInputUnderflow; - PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ )); - } - else if( !stream->playback.ready ) - { - cbFlags |= paOutputOverflow; - PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ )); - } - } - - CallbackUpdate( &stream->threading ); - CalculateTimeInfo( stream, &timeInfo ); - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); - cbFlags = 0; - - /* CPU load measurement should include processing activivity external to the stream callback */ - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - framesGot = framesAvail; - if( paUtilFixedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode ) - { - /* We've committed to a fixed host buffer size, stick to that */ - framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0; - } - else - { - /* We've committed to an upper bound on the size of host buffers */ - assert( paUtilBoundedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode ); - framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer ); - } - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - /* Check the host buffer size against the buffer processor configuration */ - framesAvail -= framesGot; - - if( framesGot > 0 ) - { - assert( !xrun ); - PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - } - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); - - if( 0 == framesGot ) - { - /* Go back to polling for more frames */ - break; - - } - - if( paContinue != callbackResult ) - break; - } - } - - /* Match pthread_cleanup_push */ - pthread_cleanup_pop( 1 ); - -end: - PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() )); - pthread_exit( pres ); - -error: - /* Pass on error code */ - pres = malloc( sizeof (PaError) ); - *pres = result; - - goto end; -} - -/* Blocking interface */ - -static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long framesGot, framesAvail; - void *userBuffer; - snd_pcm_t *save = stream->playback.pcm; - - assert( stream ); - - PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream ); - - /* Disregard playback */ - stream->playback.pcm = NULL; - - if( stream->overrun > 0. ) - { - result = paInputOverflowed; - stream->overrun = 0.0; - } - - if( stream->capture.userInterleaved ) - { - userBuffer = buffer; - } - else - { - /* Copy channels into local array */ - userBuffer = stream->capture.userBuffers; - memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels ); - } - - /* Start stream if in prepared state */ - if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) - { - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - - while( frames > 0 ) - { - int xrun = 0; - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - } - -end: - stream->playback.pcm = save; - return result; -error: - goto end; -} - -static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - signed long err; - PaAlsaStream *stream = (PaAlsaStream*)s; - snd_pcm_uframes_t framesGot, framesAvail; - const void *userBuffer; - snd_pcm_t *save = stream->capture.pcm; - - assert( stream ); - - PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream ); - - /* Disregard capture */ - stream->capture.pcm = NULL; - - if( stream->underrun > 0. ) - { - result = paOutputUnderflowed; - stream->underrun = 0.0; - } - - if( stream->playback.userInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->playback.userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels ); - } - - while( frames > 0 ) - { - int xrun = 0; - snd_pcm_uframes_t hwAvail; - - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - - /* Start stream after one period of samples worth */ - - /* Frames residing in buffer */ - PA_ENSURE( err = GetStreamWriteAvailable( stream ) ); - framesAvail = err; - hwAvail = stream->playback.bufferSize - framesAvail; - - if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && - hwAvail >= stream->playback.framesPerBuffer ) - { - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - } - -end: - stream->capture.pcm = save; - return result; -error: - goto end; -} - -/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - PA_ENSURE( paInputOverflowed ); - } - - return (signed long)avail; - -error: - return result; -} - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) ); - if( xrun ) - { - snd_pcm_sframes_t savail; - - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - savail = snd_pcm_avail_update( stream->playback.pcm ); - - /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ - ENSURE_( savail, paUnanticipatedHostError ); - - avail = (unsigned long) savail; - } - - return (signed long)avail; - -error: - return result; -} - -/* Extensions */ - -/* Initialize host api specific structure */ -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ) -{ - info->size = sizeof (PaAlsaStreamInfo); - info->hostApiType = paALSA; - info->version = 1; - info->deviceString = NULL; -} - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.rtSched = enable; -} - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.useWatchdog = enable; -} diff --git a/portaudio/pa_linux_alsa/pa_linux_alsa.h b/portaudio/pa_linux_alsa/pa_linux_alsa.h deleted file mode 100644 index e6f44b16e..000000000 --- a/portaudio/pa_linux_alsa/pa_linux_alsa.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef PA_LINUX_ALSA_H -#define PA_LINUX_ALSA_H - -/* - * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * ALSA-specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/** @file - * ALSA-specific PortAudio API extension header file. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PaAlsaStreamInfo -{ - unsigned long size; - PaHostApiTypeId hostApiType; - unsigned long version; - - const char *deviceString; -} -PaAlsaStreamInfo; - -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ); - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ); - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/portaudio/pa_mac/CVS/Entries b/portaudio/pa_mac/CVS/Entries deleted file mode 100644 index 24943262a..000000000 --- a/portaudio/pa_mac/CVS/Entries +++ /dev/null @@ -1,2 +0,0 @@ -/pa_mac_hostapis.c/1.1.2.1/Thu May 27 22:39:58 2004//Tv19-devel -D diff --git a/portaudio/pa_mac/CVS/Repository b/portaudio/pa_mac/CVS/Repository deleted file mode 100644 index a8e170174..000000000 --- a/portaudio/pa_mac/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_mac diff --git a/portaudio/pa_mac/CVS/Root b/portaudio/pa_mac/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_mac/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_mac/CVS/Tag b/portaudio/pa_mac/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_mac/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_mac/pa_mac_hostapis.c b/portaudio/pa_mac/pa_mac_hostapis.c deleted file mode 100644 index 2e71577e7..000000000 --- a/portaudio/pa_mac/pa_mac_hostapis.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * $Id: pa_mac_hostapis.c,v 1.1.2.1 2004/05/27 22:39:58 gregpfeil 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. - */ - -/** @file -Mac OS host API initialization function table. -*/ - - -#include "pa_hostapi.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacSm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -PaUtilHostApiInitializer *paHostApiInitializers[] = -{ -#ifdef PA_USE_COREAUDIO - PaMacCore_Initialize, -#endif - -#ifdef PA_USE_SM - PaMacSm_Initialize, -#endif - -#ifdef PA_USE_JACK - PaJack_Initialize, -#endif - -#ifdef PA_USE_ASIO - PaMacAsio_Initialize, -#endif - - PaSkeleton_Initialize, /* just for testing */ - - 0 /* NULL terminated array */ -}; - - -int paDefaultHostApiIndex = 0; diff --git a/portaudio/pa_mac_core/CVS/Entries b/portaudio/pa_mac_core/CVS/Entries deleted file mode 100644 index 442a32691..000000000 --- a/portaudio/pa_mac_core/CVS/Entries +++ /dev/null @@ -1,6 +0,0 @@ -/notes.txt/1.3.2.9/Thu Feb 23 18:07:11 2006//Tv19-devel -/pa_mac_core.c/1.8.2.13/Thu Apr 6 13:19:23 2006//Tv19-devel -/pa_mac_core.h/1.1.2.3/Thu Feb 16 18:25:39 2006//Tv19-devel -/pa_mac_core_old.c/1.1.2.1/Sat Dec 24 01:22:52 2005//Tv19-devel -/pa_mac_core_utilities.c/1.1.2.2/Fri Dec 9 19:43:14 2005//Tv19-devel -D diff --git a/portaudio/pa_mac_core/CVS/Repository b/portaudio/pa_mac_core/CVS/Repository deleted file mode 100644 index 9203f53ab..000000000 --- a/portaudio/pa_mac_core/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_mac_core diff --git a/portaudio/pa_mac_core/CVS/Root b/portaudio/pa_mac_core/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_mac_core/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_mac_core/CVS/Tag b/portaudio/pa_mac_core/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_mac_core/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_mac_core/notes.txt b/portaudio/pa_mac_core/notes.txt deleted file mode 100644 index ad66f358d..000000000 --- a/portaudio/pa_mac_core/notes.txt +++ /dev/null @@ -1,145 +0,0 @@ -Notes on status of CoreAudio Implementation of PortAudio - -Document Last Updated December 9, 2005 - -There are currently two implementations of PortAudio for Mac Core Audio. - -The original is in pa_mac_core_old.c, and the newer, default implementation -is in pa_mac_core.c. -Only pa_mac_core.c is currently developed and supported as it uses apple's -current core audio technology. To select use the old implementation, replace -pa_mac_core.c with pa_mac_core_old.c (eg. "cp pa_mac_core_auhal.c -pa_mac_core.c"), then run configure and make as usual. - ----------------------------------------- - -Notes on Original implementation: - -by Phil Burk and Darren Gibbs - -Last updated March 20, 2002 - -WHAT WORKS - -Output with very low latency, <10 msec. -Half duplex input or output. -Full duplex on the same CoreAudio device. -The paFLoat32, paInt16, paInt8, paUInt8 sample formats. -Pa_GetCPULoad() -Pa_StreamTime() - -KNOWN BUGS OR LIMITATIONS - -We do not yet support simultaneous input and output on different -devices. Note that some CoreAudio devices like the Roland UH30 look -like one device but are actually two different CoreAudio devices. The -Built-In audio is typically one CoreAudio device. - -Mono doesn't work. - -DEVICE MAPPING - -CoreAudio devices can support both input and output. But the sample -rates supported may be different. So we have map one or two PortAudio -device to each CoreAudio device depending on whether it supports -input, output or both. - -When we query devices, we first get a list of CoreAudio devices. Then -we scan the list and add a PortAudio device for each CoreAudio device -that supports input. Then we make a scan for output devices. - -------------------------------------------- - -Notes on Newer/Default AUHAL implementation: - -by Bjorn Roche - -Last Updated December 9, 2005 - -Principle of Operation: - -This implementation uses AUHAL for audio I/O. To some extent, it also -operates at the "HAL" Layer, though this behavior can be limited by -platform specific flags (see pa_mac_core.h for details). The default -settings should be reasonable: they don't change the SR of the device and -don't cause interruptions if other devices are using the device. - -Major Software Elements Used: Apple's HAL AUs provide output SR -conversion transparently, however, only on output, so this -implementation uses AudioConverters to convert the sample rate on input. -A PortAudio ring buffer is used to buffer input when sample rate -conversion is required or when separate audio units are used for duplex -IO. Finally, a PortAudio buffer processor is used to convert formats and -provide additional buffers if needed. Internally, interleaved floating -point data streams are used exclusively - the audio unit converts from -the audio hardware's native format to interleaved float PCM and -PortAudio's Buffer processor is used for conversion to user formats. - -Simplex Input: Simplex input uses a single callback. If sample rate -conversion is required, a ring buffer and AudioConverter are used as -well. - -Simplex output: Simplex output uses a single callback. No ring buffer or -audio converter is used because AUHAL does its own output SR conversion. - -Duplex, one device (no SR conversion): When one device is used, a single -callback is used. This achieves very low latency. - -Duplex, separate devices or SR conversion: When SR conversion is -required, data must be buffered before it is converted and data is not -always available at the same times on input and output, so SR conversion -requires the same treatment as separate devices. The input callback -reads data and puts it in the ring buffer. The output callback reads the -data off the ring buffer, into an audio converter and finally to the -buffer processor. - -Platform Specific Options: - -By using the flags in pa_mac_core.h, the user may specify several options. -For example, the user can specify the sample-rate conversion quality, and -the extent to which PA will attempt to "play nice" and to what extent it -will interrupt other apps to improve performance. For example, if 44100 Hz -sample rate is requested but the device is set at 48000 Hz, PA can either -change the device for optimal playback ("Pro" mode), which may interrupt -other programs playing back audio, or simple use a sample-rate coversion, -which allows for friendlier sharing of the device ("Play Nice" mode). - - -Known issues: - -- Latency: Latency settings are ignored in most cases. Exceptions are when -doing I/O between different devices and as a hint for selecting a realtively -low or relatively high latency in conjunction with -paHostFramesPerBufferUnspecified. Latency settings are always automatically -bound to "safe" values, however, so setting extreme values here should not be -an issue. - -- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes -are supported. paHostFramesPerBufferUnspecified works best in "pro" mode, -where the buffer size and sample rate of the audio device is most likely -to match the expected values. - -- Timing info. It reports on stream time, but I'm probably doing something -wrong since patest_sine_time often reports negative latency numbers. - -- xrun detection: The only xrun detection performed is when reading -and writing the ring buffer. There is probably more that can be done. - -- abort/stop issues: stopping a stream is always a complete operation, -but latency should be low enough to make the lack of a separate abort -unnecessary. Apple clarifies its AudioOutputUnitStop() call here: -http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html - -- blocking interface: Not implemented. - -- multichannel: It has been tested successfully on multichannel hardware -from MOTU: traveler and 896HD. - -- sample rate conversion quality: By default, SR conversion is the maximum -available. This can be tweaked using flags pa_mac_core.h. Note that the AU -render quyality property is used to set the sample rat conversion quality -as "documented" here: -http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html - -- x86: I haven't tested it on an x86 Mac myself, but users have reported -being able to comiple and run it. diff --git a/portaudio/pa_mac_core/pa_mac_core.c b/portaudio/pa_mac_core/pa_mac_core.c deleted file mode 100644 index 16eb08249..000000000 --- a/portaudio/pa_mac_core/pa_mac_core.c +++ /dev/null @@ -1,2105 +0,0 @@ -/* - * This is the AUHAL implementation of portaudio. Hopefully this will - * one day replace pa_mac_core. - * - * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. - * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) - * - * Dominic's code was based on code by Phil Burk, Darren Gibbs, - * Gord Peters, Stephane Letz, and Greg Pfiel. - * - * Bjorn Roche and XO Audio LLC reserve no rights to this code. - * The maintainers of PortAudio may redistribute and modify the code and - * licenses as they deam appropriate. - * - * 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 pa_mac_core - @author Bjorn Roche - @brief AUHAL implementation of PortAudio -*/ - -#include <string.h> /* strlen(), memcmp() etc. */ - -#include <AudioUnit/AudioUnit.h> -#include <AudioToolbox/AudioToolbox.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 "../pablio/ringbuffer.h" -#include "pa_mac_core.h" - -#ifndef MIN -#define MIN(a, b) (((a)<(b))?(a):(b)) -#endif - -#ifndef MAX -#define MAX(a, b) (((a)<(b))?(b):(a)) -#endif - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define ERR(mac_error) PaMacCore_SetError(mac_error, __LINE__, 1 ) -#define WARNING(mac_error) PaMacCore_SetError(mac_error, __LINE__, 0 ) - -/* Help keep track of AUHAL element numbers */ -#define INPUT_ELEMENT (1) -#define OUTPUT_ELEMENT (0) - -/* Normal level of debugging: fine for most apps that don't mind the occational warning being printf'ed */ -/* - */ -#define MAC_CORE_DEBUG -#ifdef MAC_CORE_DEBUG -# define DBUG(MSG) do { printf("||PaMacCore (AUHAL)|| "); printf MSG ; fflush(stdout); } while(0) -#else -# define DBUG(MSG) -#endif - -/* Verbose Debugging: useful for developement */ -/* -#define MAC_CORE_VERBOSE_DEBUG - */ -#ifdef MAC_CORE_VERBOSE_DEBUG -# define VDBUG(MSG) do { printf("||PaMacCore (v )|| "); printf MSG ; fflush(stdout); } while(0) -#else -# define VDBUG(MSG) -#endif - -/* Very Verbose Debugging: Traces every call. */ -/* -#define MAC_CORE_VERY_VERBOSE_DEBUG - */ -#ifdef MAC_CORE_VERY_VERBOSE_DEBUG -# define VVDBUG(MSG) do { printf("||PaMacCore (vv)|| "); printf MSG ; fflush(stdout); } while(0) -#else -# define VVDBUG(MSG) -#endif - -#define RING_BUFFER_ADVANCE_DENOMINATOR (4) - -/* Some utilities that sort of belong here, but were getting too unweildy */ -#include "pa_mac_core_utilities.c" -/* Special purpose ring buffer just for pa_mac_core input processing. */ -/* #include "pa_mac_core_input_ring_buffer.c" */ -#include "../pablio/ringbuffer.c" - - -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 void setStreamStartTime( PaStream *stream ); -static OSStatus AudioIOProc( void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); -/* PaMacAUHAL - host api datastructure specific to this implementation */ -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - long devCount; - AudioDeviceID *devIds; /*array of all audio devices*/ - AudioDeviceID defaultIn; - AudioDeviceID defaultOut; -} -PaMacAUHAL; - -/* stream data structure specifically for this implementation */ -typedef struct PaMacCoreStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - /* implementation specific data goes here */ - bool bufferProcessorIsInitialized; - AudioUnit inputUnit; - AudioUnit outputUnit; - AudioDeviceID inputDevice; - AudioDeviceID outputDevice; - size_t userInChan; - size_t userOutChan; - size_t inputFramesPerBuffer; - size_t outputFramesPerBuffer; - /* We use this ring buffer when input and out devs are different. */ - RingBuffer inputRingBuffer; - /* We may need to do SR conversion on input. */ - AudioConverterRef inputSRConverter; - /* We need to preallocate an inputBuffer for reading data. */ - AudioBufferList inputAudioBufferList; - AudioTimeStamp startTime; - //volatile bool isTimeSet; - volatile PaStreamCallbackFlags xrunFlags; - volatile enum { - STOPPED = 0, /* playback is completely stopped, - and the user has called StopStream(). */ - CALLBACK_STOPPED = 1, /* callback has requested stop, - but user has not yet called StopStream(). */ - STOPPING = 2, /* The stream is in the process of closing. - This state is just used internally; - externally it is indistinguishable from - ACTIVE.*/ - ACTIVE = 3 /* The stream is active and running. */ - } state; - double sampleRate; -} -PaMacCoreStream; - -static PaError OpenAndSetupOneAudioUnit( - const PaStreamParameters *inStreamParams, - const PaStreamParameters *outStreamParams, - const unsigned long requestedFramesPerBuffer, - unsigned long *actualInputFramesPerBuffer, - unsigned long *actualOutputFramesPerBuffer, - const PaMacAUHAL *auhalHostApi, - AudioUnit *audioUnit, - AudioConverterRef *srConverter, - AudioDeviceID *audioDevice, - const double sampleRate, - void *refCon ); - -/* for setting errors. */ -#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) - - - - -/*currently, this is only used in initialization, but it might be modified - to be used when the list of devices changes.*/ -static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) -{ - UInt32 size; - UInt32 propsize; - VVDBUG(("gatherDeviceInfo()\n")); - /* -- free any previous allocations -- */ - if( auhalHostApi->devIds ) - PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); - auhalHostApi->devIds = NULL; - - /* -- figure out how many devices there are -- */ - AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, - &propsize, - NULL ); - auhalHostApi->devCount = propsize / sizeof( AudioDeviceID ); - - VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) ); - - /* -- copy the device IDs -- */ - auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, - propsize ); - if( !auhalHostApi->devIds ) - return paInsufficientMemory; - AudioHardwareGetProperty( kAudioHardwarePropertyDevices, - &propsize, - auhalHostApi->devIds ); -#ifdef MAC_CORE_VERBOSE_DEBUG - { - int i; - for( i=0; i<auhalHostApi->devCount; ++i ) - printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] ); - } -#endif - - size = sizeof(AudioDeviceID); - auhalHostApi->defaultIn = kAudioDeviceUnknown; - auhalHostApi->defaultOut = kAudioDeviceUnknown; - /* FEEDBACK: these calls could fail, in which case default in and out will - be unknown devices or could be undefined. Do I need to be more - rigorous here? */ - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, - &size, - &auhalHostApi->defaultIn); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, - &size, - &auhalHostApi->defaultOut); - VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) ); - VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) ); - - return paNoError; -} - -static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, - PaDeviceInfo *deviceInfo, - AudioDeviceID macCoreDeviceId, - int isInput) -{ - UInt32 propSize; - PaError err = paNoError; - UInt32 i; - int numChannels = 0; - AudioBufferList *buflist; - UInt32 frameLatency; - - VVDBUG(("GetChannelInfo()\n")); - - /* Get the number of channels from the stream configuration. - Fail if we can't get this. */ - - err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); - if (err) - return err; - - buflist = PaUtil_AllocateMemory(propSize); - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); - if (err) - return err; - - for (i = 0; i < buflist->mNumberBuffers; ++i) - numChannels += buflist->mBuffers[i].mNumberChannels; - - if (isInput) - deviceInfo->maxInputChannels = numChannels; - else - deviceInfo->maxOutputChannels = numChannels; - - if (numChannels > 0) // do not try to retrieve the latency if there is no channels. - { - /* Get the latency. Don't fail if we can't get this. */ - /* default to something reasonable */ - deviceInfo->defaultLowInputLatency = .01; - deviceInfo->defaultHighInputLatency = .01; - deviceInfo->defaultLowOutputLatency = .01; - deviceInfo->defaultHighOutputLatency = .01; - propSize = sizeof(UInt32); - err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); - if (!err) - { - double secondLatency = frameLatency / deviceInfo->defaultSampleRate; - if (isInput) - { - deviceInfo->defaultLowInputLatency = secondLatency; - deviceInfo->defaultHighInputLatency = secondLatency; - } - else - { - deviceInfo->defaultLowOutputLatency = secondLatency; - deviceInfo->defaultHighOutputLatency = secondLatency; - } - } - } - return paNoError; -} - -static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, - PaDeviceInfo *deviceInfo, - AudioDeviceID macCoreDeviceId, - PaHostApiIndex hostApiIndex ) -{ - Float64 sampleRate; - char *name; - PaError err = paNoError; - UInt32 propSize; - - VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId)); - - memset(deviceInfo, 0, sizeof(deviceInfo)); - - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - /* Get the device name. Fail if we can't get it. */ - err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); - if (err) - return err; - - name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize); - if ( !name ) - return paInsufficientMemory; - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); - if (err) - return err; - deviceInfo->name = name; - - /* Try to get the default sample rate. Don't fail if we can't get this. */ - propSize = sizeof(Float64); - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); - if (err) - deviceInfo->defaultSampleRate = 0.0; - else - deviceInfo->defaultSampleRate = sampleRate; - - /* Get the maximum number of input and output channels. Fail if we can't get this. */ - - err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1); - if (err) - return err; - - err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0); - if (err) - return err; - - return paNoError; -} - -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i; - PaMacAUHAL *auhalHostApi; - PaDeviceInfo *deviceInfoArray; - - VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex)); - - auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) ); - if( !auhalHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - auhalHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !auhalHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - auhalHostApi->devIds = NULL; - auhalHostApi->devCount = 0; - - /* get the info we need about the devices */ - result = gatherDeviceInfo( auhalHostApi ); - if( result != paNoError ) - goto error; - - *hostApi = &auhalHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paCoreAudio; - (*hostApi)->info.name = "Core Audio"; - - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - - (*hostApi)->info.deviceCount = 0; - - if( auhalHostApi->devCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < auhalHostApi->devCount; ++i ) - { - int err; - err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], - auhalHostApi->devIds[i], - hostApiIndex ); - if (err == paNoError) - { /* copy some info and set the defaults */ - (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i]; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - (*hostApi)->info.deviceCount++; - } - else - { /* there was an error. we need to shift the devices down, so we ignore this one */ - int j; - auhalHostApi->devCount--; - for( j=i; j<auhalHostApi->devCount; ++j ) - auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; - i--; - } - } - } - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, - IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, - IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, - GetStreamReadAvailable, - GetStreamWriteAvailable ); - - return result; - -error: - if( auhalHostApi ) - { - if( auhalHostApi->allocations ) - { - PaUtil_FreeAllAllocations( auhalHostApi->allocations ); - PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); - } - - PaUtil_FreeMemory( auhalHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; - - VVDBUG(("Terminate()\n")); - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - TODO: Double check that everything is handled by alloc group - */ - - if( auhalHostApi->allocations ) - { - PaUtil_FreeAllAllocations( auhalHostApi->allocations ); - PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); - } - - PaUtil_FreeMemory( auhalHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n", - inputParameters ? inputParameters->channelCount : -1, - inputParameters ? inputParameters->sampleFormat : -1, - outputParameters ? outputParameters->channelCount : -1, - outputParameters ? outputParameters->sampleFormat : -1, - (float) sampleRate )); - - /** These first checks are standard PA checks. We do some fancier checks - later. */ - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - } - else - { - outputChannelCount = 0; - } - - /* FEEDBACK */ - /* I think the only way to check a given format SR combo is */ - /* to try opening it. This could be disruptive, is that Okay? */ - /* The alternative is to just read off available sample rates, */ - /* but this will not work %100 of the time (eg, a device that */ - /* supports N output at one rate but only N/2 at a higher rate.)*/ - - /* The following code opens the device with the requested parameters to - see if it works. */ - { - PaError err; - PaStream *s; - err = OpenStream( hostApi, &s, inputParameters, outputParameters, - sampleRate, 1024, 0, (PaStreamCallback *)1, NULL ); - if( err != paNoError && err != paInvalidSampleRate ) - DBUG( ( "OpenStream @ %g returned: %d: %s\n", - (float) sampleRate, err, Pa_GetErrorText( err ) ) ); - if( err ) - return err; - err = CloseStream( s ); - if( err ) { - /* FEEDBACK: is this more serious? should we assert? */ - DBUG( ( "WARNING: could not close Stream. %d: %s\n", - err, Pa_GetErrorText( err ) ) ); - } - } - - return paFormatIsSupported; -} - -static PaError OpenAndSetupOneAudioUnit( - const PaStreamParameters *inStreamParams, - const PaStreamParameters *outStreamParams, - const unsigned long requestedFramesPerBuffer, - unsigned long *actualInputFramesPerBuffer, - unsigned long *actualOutputFramesPerBuffer, - const PaMacAUHAL *auhalHostApi, - AudioUnit *audioUnit, - AudioConverterRef *srConverter, - AudioDeviceID *audioDevice, - const double sampleRate, - void *refCon ) -{ - ComponentDescription desc; - Component comp; - /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/ - AudioStreamBasicDescription desiredFormat; - OSErr result = noErr; - PaError paResult = paNoError; - int line; - UInt32 callbackKey; - AURenderCallbackStruct rcbs; - unsigned long macInputStreamFlags = paMacCorePlayNice; - unsigned long macOutputStreamFlags = paMacCorePlayNice; - - VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n", - inStreamParams ? inStreamParams->channelCount : -1, - inStreamParams ? inStreamParams->sampleFormat : -1, - outStreamParams ? outStreamParams->channelCount : -1, - outStreamParams ? outStreamParams->sampleFormat : -1, - requestedFramesPerBuffer )); - - /* -- handle the degenerate case -- */ - if( !inStreamParams && !outStreamParams ) { - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return paNoError; - } - - /* -- get the user's api specific info, if they set any -- */ - if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo ) - macInputStreamFlags= - ((paMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo) - ->flags; - if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo ) - macOutputStreamFlags= - ((paMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo) - ->flags; - /* Override user's flags here, if desired for testing. */ - - /* - * The HAL AU is a Mac OS style "component". - * the first few steps deal with that. - * Later steps work on a combination of Mac OS - * components and the slightly lower level - * HAL. - */ - - /* -- describe the output type AudioUnit -- */ - /* Note: for the default AudioUnit, we could use the - * componentSubType value kAudioUnitSubType_DefaultOutput; - * but I don't think that's relevant here. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - /* -- find the component -- */ - comp = FindNextComponent( NULL, &desc ); - if( !comp ) - { - DBUG( ( "AUHAL component not found." ) ); - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return paUnanticipatedHostError; - } - /* -- open it -- */ - result = OpenAComponent( comp, audioUnit ); - if( result ) - { - DBUG( ( "Failed to open AUHAL component." ) ); - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return ERR( result ); - } - /* -- prepare a little error handling logic / hackery -- */ -#define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0) - - /* -- if there is input, we have to explicitly enable input -- */ - if( inStreamParams ) - { - UInt32 enableIO; - enableIO = 1; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Input, - INPUT_ELEMENT, - &enableIO, - sizeof(enableIO) ) ); - } - /* -- if there is no output, we must explicitly disable output -- */ - if( !outStreamParams ) - { - UInt32 enableIO; - enableIO = 0; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - OUTPUT_ELEMENT, - &enableIO, - sizeof(enableIO) ) ); - } - /* -- set the devices -- */ - /* make sure input and output are the same device if we are doing input and - output. */ - if( inStreamParams && outStreamParams ) - assert( outStreamParams->device == inStreamParams->device ); - if( inStreamParams ) - { - *audioDevice = auhalHostApi->devIds[inStreamParams->device] ; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - INPUT_ELEMENT, - audioDevice, - sizeof(AudioDeviceID) ) ); - } - if( outStreamParams ) - { - *audioDevice = auhalHostApi->devIds[outStreamParams->device] ; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - OUTPUT_ELEMENT, - audioDevice, - sizeof(AudioDeviceID) ) ); - } - - /* -- set format -- */ - bzero( &desiredFormat, sizeof(desiredFormat) ); - desiredFormat.mFormatID = kAudioFormatLinearPCM ; - desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - desiredFormat.mFramesPerPacket = 1; - desiredFormat.mBitsPerChannel = sizeof( float ) * 8; - - result = 0; - /* set device format first, but only touch the device if the user asked */ - if( inStreamParams ) { - /*The callback never calls back if we don't set the FPB */ - /*This seems wierd, because I would think setting anything on the device - would be disruptive.*/ - paResult = setBestFramesPerBuffer( *audioDevice, FALSE, - requestedFramesPerBuffer, - actualInputFramesPerBuffer ); - if( paResult ) goto error; - if( macInputStreamFlags & paMacCore_ChangeDeviceParameters ) { - bool requireExact; - requireExact=macInputStreamFlags&paMacCore_FailIfConversionRequired; - paResult = setBestSampleRateForDevice( *audioDevice, FALSE, - requireExact, sampleRate ); - if( paResult ) goto error; - } - if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer ) - *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ; - } - if( outStreamParams && !inStreamParams ) { - /*The callback never calls back if we don't set the FPB */ - /*This seems wierd, because I would think setting anything on the device - would be disruptive.*/ - paResult = setBestFramesPerBuffer( *audioDevice, TRUE, - requestedFramesPerBuffer, - actualOutputFramesPerBuffer ); - if( paResult ) goto error; - if( macOutputStreamFlags & paMacCore_ChangeDeviceParameters ) { - bool requireExact; - requireExact=macOutputStreamFlags&paMacCore_FailIfConversionRequired; - paResult = setBestSampleRateForDevice( *audioDevice, TRUE, - requireExact, sampleRate ); - if( paResult ) goto error; - } - } - - /* -- set the quality of the output converter -- */ - if( outStreamParams ) { - UInt32 value = kAudioConverterQuality_Max; - switch( macOutputStreamFlags & 0x0700 ) { - case 0x0100: /*paMacCore_ConversionQualityMin:*/ - value=kRenderQuality_Min; - break; - case 0x0200: /*paMacCore_ConversionQualityLow:*/ - value=kRenderQuality_Low; - break; - case 0x0300: /*paMacCore_ConversionQualityMedium:*/ - value=kRenderQuality_Medium; - break; - case 0x0400: /*paMacCore_ConversionQualityHigh:*/ - value=kRenderQuality_High; - break; - } - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_RenderQuality, - kAudioUnitScope_Global, - OUTPUT_ELEMENT, - &value, - sizeof(value) ) ); - } - /* now set the format on the Audio Units. */ - if( outStreamParams ) - { - desiredFormat.mSampleRate =sampleRate; - desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = outStreamParams->channelCount; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - OUTPUT_ELEMENT, - &desiredFormat, - sizeof(AudioStreamBasicDescription) ) ); - } - if( inStreamParams ) - { - AudioStreamBasicDescription sourceFormat; - UInt32 size = sizeof( AudioStreamBasicDescription ); - - /* keep the sample rate of the device, or we confuse AUHAL */ - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - INPUT_ELEMENT, - &sourceFormat, - &size ) ); - desiredFormat.mSampleRate = sourceFormat.mSampleRate; - desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &desiredFormat, - sizeof(AudioStreamBasicDescription) ) ); - } - /* set the maximumFramesPerSlice */ - /* not doing this causes real problems - (eg. the callback might not be called). The idea of setting both this - and the frames per buffer on the device is that we'll be most likely - to actually get the frame size we requested in the callback with the - minimum latency. */ - if( outStreamParams ) { - UInt32 size = sizeof( *actualOutputFramesPerBuffer ); - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Input, - OUTPUT_ELEMENT, - actualOutputFramesPerBuffer, - sizeof(unsigned long) ) ); - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - OUTPUT_ELEMENT, - actualOutputFramesPerBuffer, - &size ) ); - } - if( inStreamParams ) { - /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/ - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Output, - INPUT_ELEMENT, - actualInputFramesPerBuffer, - sizeof(unsigned long) ) ); -/* Don't know why this causes problems - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, //Output, - INPUT_ELEMENT, - actualInputFramesPerBuffer, - &size ) ); -*/ - } - - /* -- if we have input, we may need to setup an SR converter -- */ - /* even if we got the sample rate we asked for, we need to do - the conversion in case another program changes the underlying SR. */ - /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */ - if( inStreamParams ) { - AudioStreamBasicDescription desiredFormat; - AudioStreamBasicDescription sourceFormat; - UInt32 sourceSize = sizeof( sourceFormat ); - bzero( &desiredFormat, sizeof(desiredFormat) ); - desiredFormat.mSampleRate = sampleRate; - desiredFormat.mFormatID = kAudioFormatLinearPCM ; - desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - desiredFormat.mFramesPerPacket = 1; - desiredFormat.mBitsPerChannel = sizeof( float ) * 8; - desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; - - /* get the source format */ - ERR_WRAP( AudioUnitGetProperty( - *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &sourceFormat, - &sourceSize ) ); - - if( desiredFormat.mSampleRate != sourceFormat.mSampleRate ) - { - UInt32 value = kAudioConverterQuality_Max; - switch( macInputStreamFlags & 0x0700 ) { - case 0x0100: /*paMacCore_ConversionQualityMin:*/ - value=kAudioConverterQuality_Min; - break; - case 0x0200: /*paMacCore_ConversionQualityLow:*/ - value=kAudioConverterQuality_Low; - break; - case 0x0300: /*paMacCore_ConversionQualityMedium:*/ - value=kAudioConverterQuality_Medium; - break; - case 0x0400: /*paMacCore_ConversionQualityHigh:*/ - value=kAudioConverterQuality_High; - break; - } - VDBUG(( "Creating sample rate converter for input" - " to convert from %g to %g\n", - (float)sourceFormat.mSampleRate, - (float)desiredFormat.mSampleRate ) ); - /* create our converter */ - ERR_WRAP( AudioConverterNew( - &sourceFormat, - &desiredFormat, - srConverter ) ); - /* Set quality */ - ERR_WRAP( AudioConverterSetProperty( - *srConverter, - kAudioConverterSampleRateConverterQuality, - sizeof( value ), - &value ) ); - } - } - /* -- set IOProc (callback) -- */ - callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback - : kAudioOutputUnitProperty_SetInputCallback ; - rcbs.inputProc = AudioIOProc; - rcbs.inputProcRefCon = refCon; - ERR_WRAP( AudioUnitSetProperty( - *audioUnit, - callbackKey, - kAudioUnitScope_Output, - outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT, - &rcbs, - sizeof(rcbs)) ); - - if( inStreamParams && outStreamParams && *srConverter ) - ERR_WRAP( AudioUnitSetProperty( - *audioUnit, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &rcbs, - sizeof(rcbs)) ); - - /*IMPLEMENTME: may need to worry about channel mapping.*/ - - /* initialize the audio unit */ - ERR_WRAP( AudioUnitInitialize(*audioUnit) ); - - if( inStreamParams && outStreamParams ) - VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); - else if( inStreamParams ) - VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); - else if( outStreamParams ) - VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); - return paNoError; -#undef ERR_WRAP - - error: - CloseComponent( *audioUnit ); - *audioUnit = NULL; - if( result ) - return PaMacCore_SetError( result, line, 1 ); - return paResult; -} - -/* 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; - PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; - PaMacCoreStream *stream = 0; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n", - inputParameters ? inputParameters->channelCount : -1, - inputParameters ? inputParameters->sampleFormat : -1, - outputParameters ? outputParameters->channelCount : -1, - outputParameters ? outputParameters->sampleFormat : -1, - (float) sampleRate, - framesPerBuffer )); - VDBUG( ("Opening Stream.\n") ); - - /*These first few bits of code are from paSkeleton with few modifications.*/ - 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; - - /* Host supports interleaved float32 */ - hostInputSampleFormat = paFloat32; - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* Host supports interleaved float32 */ - hostOutputSampleFormat = paFloat32; - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */ - } - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - /* If we fail after this point, we my be left in a bad state, with - some data structures setup and others not. So, first thing we - do is initialize everything so that if we fail, we know what hasn't - been touched. - */ - - stream->inputAudioBufferList.mBuffers[0].mData = NULL; - stream->inputRingBuffer.buffer = NULL; - stream->inputSRConverter = NULL; - stream->inputUnit = NULL; - stream->outputUnit = NULL; - stream->inputFramesPerBuffer = 0; - stream->outputFramesPerBuffer = 0; - stream->bufferProcessorIsInitialized = FALSE; - - assert( streamCallback ) ; /* only callback mode is implemented */ - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &auhalHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &auhalHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - /* -- handle paFramesPerBufferUnspecified -- */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) { - long requested = 64; - if( inputParameters ) - requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 ); - if( outputParameters ) - requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 ); - VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", - requested ) ); - if( requested <= 64 ) { - /*requested a realtively low latency. make sure this is in range of devices */ - /*try to get the device's min natural buffer size and use that (but no smaller than 64).*/ - AudioValueRange audioRange; - size_t size = sizeof( audioRange ); - if( inputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MAX( requested, audioRange.mMinimum ); - } - if( outputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MAX( requested, audioRange.mMinimum ); - } - } else { - /* requested a realtively high latency. make sure this is in range of devices */ - /*try to get the device's max natural buffer size and use that (but no larger than 1024).*/ - AudioValueRange audioRange; - size_t size = sizeof( audioRange ); - requested = MIN( requested, 1024 ); - if( inputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MIN( requested, audioRange.mMaximum ); - } - if( outputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MIN( requested, audioRange.mMaximum ); - } - } - /* -- double check ranges -- */ - if( requested > 1024 ) requested = 1024; - if( requested < 64 ) requested = 64; - VDBUG(("After querying hardware, setting block size to %ld.\n", requested)); - framesPerBuffer = requested; - } - - /* -- Now we actually open and setup streams. -- */ - if( inputParameters && outputParameters && outputParameters->device == inputParameters->device ) - { /* full duplex. One device. */ - result = OpenAndSetupOneAudioUnit( inputParameters, - outputParameters, - framesPerBuffer, - &(stream->inputFramesPerBuffer), - &(stream->outputFramesPerBuffer), - auhalHostApi, - &(stream->inputUnit), - &(stream->inputSRConverter), - &(stream->inputDevice), - sampleRate, - stream ); - stream->outputUnit = stream->inputUnit; - stream->outputDevice = stream->inputDevice; - if( result != paNoError ) - goto error; - } - else - { /* full duplex, different devices OR simplex */ - result = OpenAndSetupOneAudioUnit( NULL, - outputParameters, - framesPerBuffer, - NULL, - &(stream->outputFramesPerBuffer), - auhalHostApi, - &(stream->outputUnit), - NULL, - &(stream->outputDevice), - sampleRate, - stream ); - if( result != paNoError ) - goto error; - result = OpenAndSetupOneAudioUnit( inputParameters, - NULL, - framesPerBuffer, - &(stream->inputFramesPerBuffer), - NULL, - auhalHostApi, - &(stream->inputUnit), - &(stream->inputSRConverter), - &(stream->inputDevice), - sampleRate, - stream ); - if( result != paNoError ) - goto error; - } - - if( stream->inputUnit ) { - const size_t szfl = sizeof(float); - /* setup the AudioBufferList used for input */ - bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) ); - stream->inputAudioBufferList.mNumberBuffers = 1; - stream->inputAudioBufferList.mBuffers[0].mNumberChannels - = inputChannelCount; - stream->inputAudioBufferList.mBuffers[0].mDataByteSize - = stream->inputFramesPerBuffer*inputChannelCount*szfl; - stream->inputAudioBufferList.mBuffers[0].mData - = (float *) calloc( - stream->inputFramesPerBuffer*inputChannelCount, - szfl ); - if( !stream->inputAudioBufferList.mBuffers[0].mData ) - { - result = paInsufficientMemory; - goto error; - } - - /* - * If input and output devs are different or we are doing SR conversion, - * we also need a - * ring buffer to store inpt data while waiting for output - * data. - */ - if( (stream->outputUnit && stream->inputUnit != stream->outputUnit) - || stream->inputSRConverter ) - { - /* May want the ringSize ot initial position in - ring buffer to depend somewhat on sample rate change */ - /* Calculate an appropriate ring buffer size. It must be at least - 3x framesPerBuffer and 2x suggested latency and it must be a - power of 2. FEEDBACK: too liberal/conservative/another way?*/ - double latency; - int index, i; - void *data; - long ringSize; - if( !outputParameters ) - latency = inputParameters->suggestedLatency; - else - latency = MAX( inputParameters->suggestedLatency, - outputParameters->suggestedLatency ); - ringSize = latency * sampleRate * 2 * inputChannelCount; - VDBUG( ( "suggested latency: %d\n", (int) (latency*sampleRate) ) ); - if( ringSize < stream->inputFramesPerBuffer * 3 ) - ringSize = stream->inputFramesPerBuffer * 3 * inputChannelCount; - if( outputParameters && ringSize < stream->outputFramesPerBuffer * 3 ) - ringSize = stream->outputFramesPerBuffer * 3 * inputChannelCount; - VDBUG(("inFramesPerBuffer:%d\n",(int)stream->inputFramesPerBuffer)); - if( outputParameters ) - VDBUG(("outFramesPerBuffer:%d\n", - (int)stream->outputFramesPerBuffer)); - VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize )); - - /* round up to the next power of 2 */ - index = -1; - for( i=0; i<sizeof(long)*8; ++i ) - if( ringSize >> i & 0x01 ) - index = i; - assert( index > 0 ); - if( ringSize <= ( 0x01 << index ) ) - ringSize = 0x01 << index ; - else - ringSize = 0x01 << ( index + 1 ); - - /*ringSize <<= 4; *//*16x bigger, for testing */ - - VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize )); - - /*now, we need to allocate memory for the ring buffer*/ - data = calloc( ringSize, szfl ); - if( !data ) - { - result = paInsufficientMemory; - goto error; - } - - /* now we can initialize the ring buffer */ - assert( 0 == - RingBuffer_Init( &stream->inputRingBuffer, - ringSize*szfl, data ) ); - /* advance the read point a little, so we are reading from the - middle of the buffer */ - if( stream->outputUnit ) - RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR ); - } - } - - /* -- initialize Buffer Processor -- */ - { - unsigned long maxHostFrames = stream->inputFramesPerBuffer; - if( stream->outputFramesPerBuffer > maxHostFrames ) - maxHostFrames = stream->outputFramesPerBuffer; - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, - hostInputSampleFormat, - outputChannelCount, outputSampleFormat, - hostOutputSampleFormat, - sampleRate, - streamFlags, - framesPerBuffer, - /* If sample rate conversion takes place, the buffer size - will not be known. */ - maxHostFrames, - stream->inputSRConverter - ? paUtilUnknownHostBufferSize - : paUtilBoundedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - } - stream->bufferProcessorIsInitialized = TRUE; - - /* - IMPLEMENT ME: initialise the following fields with estimated or actual - values. - I think this is okay the way it is br 12/1/05 - maybe need to change input latency estimate if IO devs differ - */ - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - stream->sampleRate = sampleRate; - stream->userInChan = inputChannelCount; - stream->userOutChan = outputChannelCount; - - //stream->isTimeSet = FALSE; - stream->state = STOPPED; - stream->xrunFlags = 0; - - *s = (PaStream*)stream; - - setStreamStartTime( stream ); - - return result; - -error: - CloseStream( stream ); - return result; -} - -PaTime GetStreamTime( PaStream *s ) -{ - /* FIXME: I am not at all sure this timing info stuff is right. - patest_sine_time reports negative latencies, which is wierd.*/ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - AudioTimeStamp timeStamp; - - VVDBUG(("GetStreamTime()\n")); - - //if ( !stream->isTimeSet ) - // return (PaTime)0; - - if ( stream->outputDevice ) - AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp); - else if ( stream->inputDevice ) - AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp); - else - return (PaTime)0; - - return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->sampleRate; -} - -static void setStreamStartTime( PaStream *stream ) -{ - /* FIXME: I am not at all sure this timing info stuff is right. - patest_sine_time reports negative latencies, which is wierd.*/ - VVDBUG(("setStreamStartTime()\n")); - PaMacCoreStream *s = (PaMacCoreStream *) stream; - if( s->inputDevice ) - AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime); - else - AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime); -} - - -static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp) -{ - VVDBUG(("TimeStampToSecs()\n")); - if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid) - return (timeStamp->mSampleTime / stream->sampleRate); - else - return 0; -} - -#define RING_BUFFER_EMPTY (1000) - -static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter, - UInt32*ioDataSize, - void** outData, - void*inUserData ) -{ - void *dummyData; - long dummySize; - RingBuffer *rb = (RingBuffer *) inUserData; - - VVDBUG(("ringBufferIOProc()\n")); - - assert( sizeof( UInt32 ) == sizeof( long ) ); - if( RingBuffer_GetReadAvailable( rb ) == 0 ) { - *outData = NULL; - *ioDataSize = 0; - return RING_BUFFER_EMPTY; - } - RingBuffer_GetReadRegions( rb, *ioDataSize, - outData, (long *)ioDataSize, - &dummyData, &dummySize ); - - assert( *ioDataSize ); - RingBuffer_AdvanceReadIndex( rb, *ioDataSize ); - - return noErr; -} - -/* - * Called by the AudioUnit API to process audio from the sound card. - * This is where the magic happens. - */ -/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */ -static OSStatus AudioIOProc( void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData ) -{ - unsigned long framesProcessed = 0; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; - PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon; - const bool isRender = inBusNumber == OUTPUT_ELEMENT; - int callbackResult = paContinue ; - - VVDBUG(("AudioIOProc()\n")); - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - //if( !stream->isTimeSet ) - // setStreamStartTime( stream ); - //stream->isTimeSet = TRUE; - - - /* -----------------------------------------------------------------*\ - This output may be useful for debugging, - But printing durring the callback is a bad enough idea that - this is not enabled by enableing the usual debugging calls. - \* -----------------------------------------------------------------*/ - /* - static int renderCount = 0; - static int inputCount = 0; - printf( "------------------- starting reder/input\n" ); - if( isRender ) - printf("Render callback (%d):\t", ++renderCount); - else - printf("Input callback (%d):\t", ++inputCount); - printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount ); - - printf( "--- inBusNumber: %lu\n", inBusNumber ); - printf( "--- inNumberFrames: %lu\n", inNumberFrames ); - printf( "--- %x ioData\n", (unsigned) ioData ); - if( ioData ) - { - int i=0; - printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers ); - for( i=0; i<ioData->mNumberBuffers; ++i ) - printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize ); - } - ----------------------------------------------------------------- */ - - if( isRender ) { - AudioTimeStamp currentTime; - timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp); - AudioDeviceGetCurrentTime(stream->outputDevice, ¤tTime); - timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); - } - if( isRender && stream->inputUnit == stream->outputUnit ) - timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); - if( !isRender ) { - AudioTimeStamp currentTime; - timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); - AudioDeviceGetCurrentTime(stream->inputDevice, ¤tTime); - timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); - } - - - if( isRender && stream->inputUnit == stream->outputUnit - && !stream->inputSRConverter ) - { - /* --------- Full Duplex, One Device, no SR Conversion ------- - * - * This is the lowest latency case, and also the simplest. - * Input data and output data are available at the same time. - * we do not use the input SR converter or the input ring buffer. - * - */ - OSErr err = 0; - unsigned long frames; - - /* -- start processing -- */ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - /* -- compute frames. do some checks -- */ - assert( ioData->mNumberBuffers == 1 ); - assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); - frames = ioData->mBuffers[0].mDataByteSize; - frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; - /* -- copy and process input data -- */ - err= AudioUnitRender(stream->inputUnit, - ioActionFlags, - inTimeStamp, - INPUT_ELEMENT, - inNumberFrames, - &stream->inputAudioBufferList ); - /* FEEDBACK: I'm not sure what to do when this call fails */ - assert( !err ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - stream->inputAudioBufferList.mBuffers[0].mData, - stream->inputAudioBufferList.mBuffers[0].mNumberChannels); - /* -- Copy and process output data -- */ - PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), - 0, - ioData->mBuffers[0].mData, - ioData->mBuffers[0].mNumberChannels); - /* -- complete processing -- */ - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - else if( isRender ) - { - /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion) - * -- OR Simplex Output - * - * This case handles output data as in the full duplex case, - * and, if there is input data, reads it off the ring buffer - * and into the PA buffer processor. If sample rate conversion - * is required on input, that is done here as well. - */ - unsigned long frames; - - /* Sometimes, when stopping a duplex stream we get erroneous - xrun flags, so if this is our last run, clear the flags. */ - int xrunFlags = stream->xrunFlags; - if( xrunFlags & paInputUnderflow ) - printf( "input underflow.\n" ); - if( xrunFlags & paInputOverflow ) - printf( "input overflow.\n" ); - if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED ) - xrunFlags = 0; - - /* -- start processing -- */ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - xrunFlags ); - stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */ - - /* -- Copy and process output data -- */ - assert( ioData->mNumberBuffers == 1 ); - frames = ioData->mBuffers[0].mDataByteSize; - frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; - assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); - PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), - 0, - ioData->mBuffers[0].mData, - ioData->mBuffers[0].mNumberChannels); - - /* -- copy and process input data, and complete processing -- */ - if( stream->inputUnit ) { - const int flsz = sizeof( float ); - /* Here, we read the data out of the ring buffer, through the - audio converter. */ - int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels; - if( stream->inputSRConverter ) - { - OSStatus err; - UInt32 size; - float data[ inChan * frames ]; - size = sizeof( data ); - err = AudioConverterFillBuffer( - stream->inputSRConverter, - ringBufferIOProc, - &stream->inputRingBuffer, - &size, - (void *)&data ); - if( err == RING_BUFFER_EMPTY ) - { /*the ring buffer callback underflowed */ - err = 0; - bzero( ((char *)data) + size, sizeof(data)-size ); - stream->xrunFlags |= paInputUnderflow; - } - ERR( err ); - assert( !err ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - else - { - /* Without the AudioConverter is actually a bit more complex - because we have to do a little buffer processing that the - AudioConverter would otherwise handle for us. */ - void *data1, *data2; - long size1, size2; - RingBuffer_GetReadRegions( &stream->inputRingBuffer, - inChan*frames*flsz, - &data1, &size1, - &data2, &size2 ); - if( size1 / ( flsz * inChan ) == frames ) { - /* simplest case: all in first buffer */ - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data1, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1 ); - } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) { - /*we underflowed. take what data we can, zero the rest.*/ - float data[frames*inChan]; - if( size1 ) - memcpy( data, data1, size1 ); - if( size2 ) - memcpy( data+size1, data2, size2 ); - bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex( &stream->inputRingBuffer, - size1+size2 ); - /* flag underflow */ - stream->xrunFlags |= paInputUnderflow; - } else { - /*we got all the data, but split between buffers*/ - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), - size1 / ( flsz * inChan ) ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data1, - inChan ); - PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor), - size2 / ( flsz * inChan ) ); - PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data2, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1+size2 ); - } - } - } else { - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - - } - else - { - /* ------------------ Input - * - * First, we read off the audio data and put it in the ring buffer. - * if this is an input-only stream, we need to process it more, - * otherwise, we let the output case deal with it. - */ - OSErr err = 0; - int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ; - /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */ - do { - err= AudioUnitRender(stream->inputUnit, - ioActionFlags, - inTimeStamp, - INPUT_ELEMENT, - inNumberFrames, - &stream->inputAudioBufferList ); - if( err == -10874 ) - inNumberFrames /= 2; - } while( err == -10874 && inNumberFrames > 1 ); - /* FEEDBACK: I'm not sure what to do when this call fails */ - ERR( err ); - assert( !err ); - if( stream->inputSRConverter || stream->outputUnit ) - { - /* If this is duplex or we use a converter, put the data - into the ring buffer. */ - long bytesIn, bytesOut; - bytesIn = sizeof( float ) * inNumberFrames * chan; - bytesOut = RingBuffer_Write( &stream->inputRingBuffer, - stream->inputAudioBufferList.mBuffers[0].mData, - bytesIn ); - if( bytesIn != bytesOut ) - stream->xrunFlags |= paInputOverflow ; - } - else - { - /* for simplex input w/o SR conversion, - just pop the data into the buffer processor.*/ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - stream->inputAudioBufferList.mBuffers[0].mData, - chan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - if( !stream->outputUnit && stream->inputSRConverter ) - { - /* ------------------ Simplex Input w/ SR Conversion - * - * if this is a simplex input stream, we need to read off the buffer, - * do our sample rate conversion and pass the results to the buffer - * processor. - * The logic here is complicated somewhat by the fact that we don't - * know how much data is available, so we loop on reasonably sized - * chunks, and let the BufferProcessor deal with the rest. - * - */ - /*This might be too big or small depending on SR conversion*/ - float data[ chan * inNumberFrames ]; - OSStatus err; - do - { /*Run the buffer processor until we are out of data*/ - UInt32 size; - long f; - - size = sizeof( data ); - err = AudioConverterFillBuffer( - stream->inputSRConverter, - ringBufferIOProc, - &stream->inputRingBuffer, - &size, - (void *)data ); - if( err != RING_BUFFER_EMPTY ) - ERR( err ); - assert( err == 0 || err == RING_BUFFER_EMPTY ); - - f = size / ( chan * sizeof(float) ); - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f ); - if( f ) - { - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - chan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - } while( callbackResult == paContinue && !err ); - } - } - - switch( callbackResult ) - { - case paContinue: break; - case paComplete: - case paAbort: - stream->state = CALLBACK_STOPPED ; - if( stream->outputUnit ) - AudioOutputUnitStop(stream->outputUnit); - if( stream->inputUnit ) - AudioOutputUnitStop(stream->inputUnit); - break; - } - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - return noErr; -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - /* This may be called from a failed OpenStream. - Therefore, each piece of info is treated seperately. */ - PaError result = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - VVDBUG(("CloseStream()\n")); - VDBUG( ( "Closing stream.\n" ) ); - - if( stream ) { - if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { - AudioUnitUninitialize( stream->outputUnit ); - CloseComponent( stream->outputUnit ); - } - stream->outputUnit = NULL; - if( stream->inputUnit ) - { - AudioUnitUninitialize( stream->inputUnit ); - CloseComponent( stream->inputUnit ); - stream->inputUnit = NULL; - } - if( stream->inputRingBuffer.buffer ) - free( stream->inputRingBuffer.buffer ); - stream->inputRingBuffer.buffer = NULL; - /*TODO: is there more that needs to be done on error - from AudioConverterDispose?*/ - if( stream->inputSRConverter ) - ERR( AudioConverterDispose( stream->inputSRConverter ) ); - stream->inputSRConverter = NULL; - if( stream->inputAudioBufferList.mBuffers[0].mData ) - free( stream->inputAudioBufferList.mBuffers[0].mData ); - stream->inputAudioBufferList.mBuffers[0].mData = NULL; - - if( stream->bufferProcessorIsInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - } - - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - OSErr result = noErr; - VVDBUG(("StartStream()\n")); - VDBUG( ( "Starting stream.\n" ) ); - -#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) - - /*FIXME: maybe want to do this on close/abort for faster start? */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - if( stream->inputSRConverter ) - ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) ); - - /* -- start -- */ - stream->state = ACTIVE; - if( stream->inputUnit ) { - ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) ); - } - if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { - ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) ); - } - - return paNoError; -#undef ERR_WRAP -} - - -static PaError StopStream( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - OSErr result = noErr; - VVDBUG(("StopStream()\n")); - VDBUG( ( "Stopping stream.\n" ) ); - - stream->state = STOPPING; - -#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) - /* -- stop and reset -- */ - if( stream->inputUnit == stream->outputUnit && stream->inputUnit ) - { - ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) ); - ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) ); - ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) ); - } - else - { - if( stream->inputUnit ) - { - ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) ); - ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1)); - } - if( stream->outputUnit ) - { - ERR_WRAP(AudioOutputUnitStop(stream->outputUnit)); - ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0)); - } - } - if( stream->inputRingBuffer.buffer ) { - RingBuffer_Flush( &stream->inputRingBuffer ); - bzero(stream->inputRingBuffer.buffer,stream->inputRingBuffer.bufferSize); - /* advance the write point a little, so we are reading from the - middle of the buffer. We'll need extra at the end because - testing has shown that this helps. */ - if( stream->outputUnit ) - RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, - stream->inputRingBuffer.bufferSize - / RING_BUFFER_ADVANCE_DENOMINATOR ); - } - - //stream->isTimeSet = FALSE; - stream->xrunFlags = 0; - stream->state = STOPPED; - - VDBUG( ( "Stream Stopped.\n" ) ); - return paNoError; -#undef ERR_WRAP -} - -static PaError AbortStream( PaStream *s ) -{ - VVDBUG(("AbortStream()->StopStream()\n")); - VDBUG( ( "Aborting stream.\n" ) ); - /* We have nothing faster than StopStream. */ - return StopStream(s); -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("IsStreamStopped()\n")); - - return stream->state == STOPPED ? 1 : 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("IsStreamActive()\n")); - return ( stream->state == ACTIVE || stream->state == STOPPING ); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("GetStreamCpuLoad()\n")); - - 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. IMPLEMENTME: no blocking interface yet! -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("ReadStream()\n")); - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("WriteStream()\n")); - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("GetStreamReadAvailable()\n")); - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VVDBUG(("GetStreamWriteAvailable()\n")); - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} diff --git a/portaudio/pa_mac_core/pa_mac_core.h b/portaudio/pa_mac_core/pa_mac_core.h deleted file mode 100644 index 5994294ad..000000000 --- a/portaudio/pa_mac_core/pa_mac_core.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Mac spcific flags for PA. - * portaudio.h should be included before this file. - */ - -/* - * A pointer to a paMacCoreStreamInfo may be passed as - * the hostApiSpecificStreamInfo in the PaStreamParameters struct - * when opening a stream. Use NULL, for the defaults. Note that for - * duplex streams, both infos should be the same or behaviour - * is undefined. - */ -typedef struct paMacCoreStreamInfo -{ - unsigned long size; /**size of whole structure including this header */ - PaHostApiTypeId hostApiType;/**host API for which this data is intended */ - unsigned long version; /**structure version */ - unsigned long flags; /* flags to modify behaviour */ -} paMacCoreStreamInfo; - -/* Use this function to initialize a paMacCoreStreamInfo struct - using the requested flags. */ -void paSetupMacCoreStreamInfo( paMacCoreStreamInfo *data, unsigned long flags ) -{ - bzero( data, sizeof( paMacCoreStreamInfo ) ); - data->size = sizeof( paMacCoreStreamInfo ); - data->hostApiType = paCoreAudio; - data->version = 0x01; - data->flags = flags; -} - -/* - * The following flags alter the behaviour of PA on the mac platform. - * they can be ORed together. These should work both for opening and - * checking a device. - */ -/* Allows PortAudio to change things like the device's frame size, - * which allows for much lower latency, but might disrupt the device - * if other programs are using it. */ -const unsigned long paMacCore_ChangeDeviceParameters = 0x01; - -/* In combination with the above flag, - * causes the stream opening to fail, unless the exact sample rates - * are supported by the device. */ -const unsigned long paMacCore_FailIfConversionRequired = 0x02; - -/* These flags set the SR conversion quality, if required. The wierd ordering - * allows Maximum Quality to be the default.*/ -const unsigned long paMacCore_ConversionQualityMin = 0x0100; -const unsigned long paMacCore_ConversionQualityMedium = 0x0200; -const unsigned long paMacCore_ConversionQualityLow = 0x0300; -const unsigned long paMacCore_ConversionQualityHigh = 0x0400; -const unsigned long paMacCore_ConversionQualityMax = 0x0000; - -/* - * Here are some "preset" combinations of flags (above) to get to some - * common configurations. THIS IS OVERKILL, but if more flags are added - * it won't be. - */ -/*This is the default setting: do as much sample rate conversion as possible - * and as little mucking with the device as possible. */ -const unsigned long paMacCorePlayNice = 0x00; -/*This setting is tuned for pro audio apps. It allows SR conversion on input - and output, but it tries to set the appropriate SR on the device.*/ -const unsigned long paMacCorePro = 0x01; -/*This is a setting to minimize CPU usage and still play nice.*/ -const unsigned long paMacCoreMinimizeCPUButPlayNice = 0x0100; -/*This is a setting to minimize CPU usage, even if that means interrupting the device. */ -const unsigned long paMacCoreMinimizeCPU = 0x0101; diff --git a/portaudio/pa_mac_core/pa_mac_core_old.c b/portaudio/pa_mac_core/pa_mac_core_old.c deleted file mode 100644 index b1a1f0e15..000000000 --- a/portaudio/pa_mac_core/pa_mac_core_old.c +++ /dev/null @@ -1,907 +0,0 @@ -/* - * $Id: pa_mac_core_old.c,v 1.1.2.1 2005/12/24 01:22:52 bjornroche Exp $ - * pa_mac_core.c - * Implementation of PortAudio for Mac OS X CoreAudio - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <CoreAudio/CoreAudio.h> -#include <AudioToolbox/AudioToolbox.h> -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include <assert.h> - -#include "portaudio.h" -#include "pa_trace.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -// ===== constants ===== - -// ===== structs ===== -#pragma mark structs - -// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation -typedef struct PaMacCore_HAR -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - AudioDeviceID *macCoreDeviceIds; -} -PaMacCoreHostApiRepresentation; - -typedef struct PaMacCore_DI -{ - PaDeviceInfo inheritedDeviceInfo; -} -PaMacCoreDeviceInfo; - -// PaMacCoreStream - a stream data structure specifically for this implementation -typedef struct PaMacCore_S -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - int primeStreamUsingCallback; - - AudioDeviceID inputDevice; - AudioDeviceID outputDevice; - - // Processing thread management -------------- -// HANDLE abortEvent; -// HANDLE processingThread; -// DWORD processingThreadId; - - char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle - int processingThreadPriority; - int highThreadPriority; - int throttledThreadPriority; - unsigned long throttledSleepMsecs; - - int isStopped; - volatile int isActive; - volatile int stopProcessing; // stop thread once existing buffers have been returned - volatile int abortProcessing; // stop thread immediately - -// DWORD allBuffersDurationMs; // used to calculate timeouts -} -PaMacCoreStream; - -// Data needed by the CoreAudio callback functions -typedef struct PaMacCore_CD -{ - PaMacCoreStream *stream; - PaStreamCallback *callback; - void *userData; - PaUtilConverter *inputConverter; - PaUtilConverter *outputConverter; - void *inputBuffer; - void *outputBuffer; - int inputChannelCount; - int outputChannelCount; - PaSampleFormat inputSampleFormat; - PaSampleFormat outputSampleFormat; - PaUtilTriangularDitherGenerator *ditherGenerator; -} -PaMacClientData; - -// ===== CoreAudio-PortAudio bridge functions ===== -#pragma mark CoreAudio-PortAudio bridge functions - -// Maps CoreAudio OSStatus codes to PortAudio PaError codes -static PaError conv_err(OSStatus error) -{ - PaError result; - - switch (error) { - case kAudioHardwareNoError: - result = paNoError; break; - case kAudioHardwareNotRunningError: - result = paInternalError; break; - case kAudioHardwareUnspecifiedError: - result = paInternalError; break; - case kAudioHardwareUnknownPropertyError: - result = paInternalError; break; - case kAudioHardwareBadPropertySizeError: - result = paInternalError; break; - case kAudioHardwareIllegalOperationError: - result = paInternalError; break; - case kAudioHardwareBadDeviceError: - result = paInvalidDevice; break; - case kAudioHardwareBadStreamError: - result = paBadStreamPtr; break; - case kAudioHardwareUnsupportedOperationError: - result = paInternalError; break; - case kAudioDeviceUnsupportedFormatError: - result = paSampleFormatNotSupported; break; - case kAudioDevicePermissionsError: - result = paDeviceUnavailable; break; - default: - result = paInternalError; - } - - return result; -} - -/* This function is unused -static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate) -{ - struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription)); - streamDescription->mSampleRate = sampleRate; - streamDescription->mFormatID = kAudioFormatLinearPCM; - streamDescription->mFormatFlags = 0; - streamDescription->mFramesPerPacket = 1; - - if (parameters->sampleFormat & paNonInterleaved) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved; - streamDescription->mChannelsPerFrame = 1; - streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat); - streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat); - } - else { - streamDescription->mChannelsPerFrame = parameters->channelCount; - } - - streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame; - streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket; - - if (parameters->sampleFormat & paFloat32) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat; - streamDescription->mBitsPerChannel = 32; - } - else if (parameters->sampleFormat & paInt32) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 32; - } - else if (parameters->sampleFormat & paInt24) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 24; - } - else if (parameters->sampleFormat & paInt16) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 16; - } - else if (parameters->sampleFormat & paInt8) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 8; - } - else if (parameters->sampleFormat & paInt32) { - streamDescription->mBitsPerChannel = 8; - } - - return streamDescription; -} -*/ - -static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime) -{ - PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo)); - - timeInfo->inputBufferAdcTime = inputTime->mSampleTime; - timeInfo->currentTime = now->mSampleTime; - timeInfo->outputBufferDacTime = outputTime->mSampleTime; - - return timeInfo; -} - -// ===== support functions ===== -#pragma mark support functions - -static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi) -{ - if( macCoreHostApi->allocations ) - { - PaUtil_FreeAllAllocations( macCoreHostApi->allocations ); - PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations ); - } - - PaUtil_FreeMemory( macCoreHostApi ); -} - -static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput) -{ - UInt32 propSize; - PaError err = paNoError; - UInt32 i; - int numChannels = 0; - AudioBufferList *buflist; - - err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); - buflist = PaUtil_AllocateMemory(propSize); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); - if (!err) { - for (i = 0; i < buflist->mNumberBuffers; ++i) { - numChannels += buflist->mBuffers[i].mNumberChannels; - } - - if (isInput) - deviceInfo->maxInputChannels = numChannels; - else - deviceInfo->maxOutputChannels = numChannels; - - int frameLatency; - propSize = sizeof(UInt32); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); - if (!err) { - double secondLatency = frameLatency / deviceInfo->defaultSampleRate; - if (isInput) { - deviceInfo->defaultLowInputLatency = secondLatency; - deviceInfo->defaultHighInputLatency = secondLatency; - } - else { - deviceInfo->defaultLowOutputLatency = secondLatency; - deviceInfo->defaultHighOutputLatency = secondLatency; - } - } - } - PaUtil_FreeMemory(buflist); - - return err; -} - -static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex ) -{ - PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - PaError err = paNoError; - UInt32 propSize; - - err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); - // FIXME: this allocation should be part of the allocations group - char *name = PaUtil_AllocateMemory(propSize); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); - if (!err) { - deviceInfo->name = name; - } - - Float64 sampleRate; - propSize = sizeof(Float64); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); - if (!err) { - deviceInfo->defaultSampleRate = sampleRate; - } - - - // Get channel info - err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1); - err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0); - - return err; -} - -static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaMacCoreDeviceInfo *deviceInfoArray; - - // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized. - hostApi = &macCoreHostApi->inheritedHostApiRep; - hostApi->info.deviceCount = 0; - hostApi->info.defaultInputDevice = paNoDevice; - hostApi->info.defaultOutputDevice = paNoDevice; - - UInt32 propsize; - AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL); - int numDevices = propsize / sizeof(AudioDeviceID); - hostApi->info.deviceCount = numDevices; - if (numDevices > 0) { - hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); - if( !hostApi->deviceInfos ) - { - return paInsufficientMemory; - } - - // allocate all device info structs in a contiguous block - deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory( - macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices ); - if( !deviceInfoArray ) - { - return paInsufficientMemory; - } - - macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize); - AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds); - - AudioDeviceID defaultInputDevice, defaultOutputDevice; - propsize = sizeof(AudioDeviceID); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice); - - UInt32 i; - for (i = 0; i < numDevices; ++i) { - if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) { - hostApi->info.defaultInputDevice = i; - } - if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) { - hostApi->info.defaultOutputDevice = i; - } - InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex); - hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo); - } - } - - return result; -} - -static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput) -{ - UInt32 propSize = sizeof(AudioStreamBasicDescription); - AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize); - - streamDescription->mSampleRate = sampleRate; - streamDescription->mFormatID = 0; - streamDescription->mFormatFlags = 0; - streamDescription->mBytesPerPacket = 0; - streamDescription->mFramesPerPacket = 0; - streamDescription->mBytesPerFrame = 0; - streamDescription->mChannelsPerFrame = 0; - streamDescription->mBitsPerChannel = 0; - streamDescription->mReserved = 0; - - OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription); - PaUtil_FreeMemory(streamDescription); - return result; -} - -static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount) -{ - int frameSpacing, channelSpacing; - if (destination->inputSampleFormat & paNonInterleaved) { - frameSpacing = 1; - channelSpacing = destination->inputChannelCount; - } - else { - frameSpacing = destination->inputChannelCount; - channelSpacing = 1; - } - - AudioBuffer const *inputBuffer = &source->mBuffers[0]; - void *coreAudioBuffer = inputBuffer->mData; - void *portAudioBuffer = destination->inputBuffer; - UInt32 i, streamNumber, streamChannel; - for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) { - if (streamChannel >= inputBuffer->mNumberChannels) { - ++streamNumber; - inputBuffer = &source->mBuffers[streamNumber]; - coreAudioBuffer = inputBuffer->mData; - streamChannel = 0; - } - destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator); - coreAudioBuffer += sizeof(Float32); - portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing; - } - return noErr; -} - -static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount) -{ - int frameSpacing, channelSpacing; - if (source->outputSampleFormat & paNonInterleaved) { - frameSpacing = 1; - channelSpacing = source->outputChannelCount; - } - else { - frameSpacing = source->outputChannelCount; - channelSpacing = 1; - } - - AudioBuffer *outputBuffer = &destination->mBuffers[0]; - void *coreAudioBuffer = outputBuffer->mData; - void *portAudioBuffer = source->outputBuffer; - UInt32 i, streamNumber, streamChannel; - for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) { - if (streamChannel >= outputBuffer->mNumberChannels) { - ++streamNumber; - outputBuffer = &destination->mBuffers[streamNumber]; - coreAudioBuffer = outputBuffer->mData; - streamChannel = 0; - } - source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL); - coreAudioBuffer += sizeof(Float32); - portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing; - } - return noErr; -} - -static OSStatus AudioIOProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; - unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); - - if (clientData->inputBuffer) { - CopyInputData(clientData, inInputData, frameCount); - } - PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - if (clientData->outputBuffer) { - CopyOutputData(outOutputData, clientData, frameCount); - } - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - - if (result == paComplete || result == paAbort) { - Pa_StopStream(clientData->stream); - } - - PaUtil_FreeMemory( timeInfo ); - return noErr; -} - -// This is not for input-only streams, this is for streams where the input device is different from the output device -static OSStatus AudioInputProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer const *inputBuffer = &inInputData->mBuffers[0]; - unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32)); - - CopyInputData(clientData, inInputData, frameCount); - PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - if( result == paComplete || result == paAbort ) - Pa_StopStream(clientData->stream); - PaUtil_FreeMemory( timeInfo ); - return noErr; -} - -// This is not for output-only streams, this is for streams where the input device is different from the output device -static OSStatus AudioOutputProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - //PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; - unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); - - //clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - - CopyOutputData(outOutputData, clientData, frameCount); - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - return noErr; -} - -static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput) -{ - PaError result = paNoError; - - double actualSampleRate; - UInt32 propSize = sizeof(double); - result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate)); - - result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate)); - - if (result == paNoError && actualSampleRate != sampleRate) { - result = paInvalidSampleRate; - } - - return result; -} - -static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput) -{ - PaError result = paNoError; - UInt32 preferredFramesPerBuffer = framesPerBuffer; - // while (preferredFramesPerBuffer > UINT32_MAX) { - // preferredFramesPerBuffer /= 2; - // } - - UInt32 actualFramesPerBuffer; - UInt32 propSize = sizeof(UInt32); - result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer)); - - result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer)); - - if (result != paNoError) { - // do nothing - } - else if (actualFramesPerBuffer > framesPerBuffer) { - result = paBufferTooSmall; - } - else if (actualFramesPerBuffer < framesPerBuffer) { - result = paBufferTooBig; - } - - return result; -} - -static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput) -{ - PaError err = paNoError; - err = SetSampleRate(device, sampleRate, isInput); - if( err == paNoError ) - err = SetFramesPerBuffer(device, framesPerBuffer, isInput); - return err; -} - -// ===== PortAudio functions ===== -#pragma mark PortAudio functions - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif // __cplusplus - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; - - CleanUp(macCoreHostApi); -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; - PaDeviceInfo *deviceInfo; - - PaError result = paNoError; - if (inputParameters) { - deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; - if (inputParameters->channelCount > deviceInfo->maxInputChannels) - result = paInvalidChannelCount; - else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) { - result = paInvalidSampleRate; - } - } - if (outputParameters && result == paNoError) { - deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; - if (outputParameters->channelCount > deviceInfo->maxOutputChannels) - result = paInvalidChannelCount; - else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) { - result = paInvalidSampleRate; - } - } - - return result; -} - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError err = paNoError; - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi; - PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream)); - stream->isActive = 0; - stream->isStopped = 1; - stream->inputDevice = kAudioDeviceUnknown; - stream->outputDevice = kAudioDeviceUnknown; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - ( (streamCallback) - ? &macCoreHostApi->callbackStreamInterface - : &macCoreHostApi->blockingStreamInterface ), - streamCallback, userData ); - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - *s = (PaStream*)stream; - PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData)); - clientData->stream = stream; - clientData->callback = streamCallback; - clientData->userData = userData; - clientData->inputBuffer = 0; - clientData->outputBuffer = 0; - clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator)); - PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator); - - if (inputParameters != NULL) { - stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device]; - clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags); - clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount); - clientData->inputChannelCount = inputParameters->channelCount; - clientData->inputSampleFormat = inputParameters->sampleFormat; - err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1); - } - - if (err == paNoError && outputParameters != NULL) { - stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device]; - clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags); - clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount); - clientData->outputChannelCount = outputParameters->channelCount; - clientData->outputSampleFormat = outputParameters->sampleFormat; - err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0); - } - - if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) { - AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice; - - AudioDeviceAddIOProc(device, AudioIOProc, clientData); - } - else { - // using different devices for input and output - AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData); - AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData); - } - - return err; -} - -// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. -static PaError CloseStream( PaStream* s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc)); - } - - return err; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc)); - } - - stream->isActive = 1; - stream->isStopped = 0; - return err; -} - -static PaError AbortStream( PaStream *s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc)); - } - - stream->isActive = 0; - stream->isStopped = 1; - return err; -} - -static PaError StopStream( PaStream *s ) -{ - // TODO: this should be nicer than abort - return AbortStream(s); -} - -static PaError IsStreamStopped( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return stream->isStopped; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - OSStatus err; - PaTime result; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp)); - if (stream->inputDevice != kAudioDeviceUnknown) { - err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp); - } - else { - err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp); - } - - result = err ? 0 : timeStamp->mSampleTime; - PaUtil_FreeMemory(timeStamp); - - return result; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams. - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - return paInternalError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - return paInternalError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - return paInternalError; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - return paInternalError; -} - -// HostAPI-specific initialization function -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) ); - if( !macCoreHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - macCoreHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !macCoreHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &macCoreHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paCoreAudio; - (*hostApi)->info.name = "CoreAudio"; - - result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex); - if (result != paNoError) { - goto error; - } - - // Set up the proper callbacks to this HostApi's functions - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( macCoreHostApi ) { - CleanUp(macCoreHostApi); - } - - return result; -} diff --git a/portaudio/pa_mac_core/pa_mac_core_utilities.c b/portaudio/pa_mac_core/pa_mac_core_utilities.c deleted file mode 100644 index f2cbd29cb..000000000 --- a/portaudio/pa_mac_core/pa_mac_core_utilities.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * - * pa_mac_core_utilities.c - * - * utilitiy functions for pa_mac_core.c - * - * This functions are more like helper functions than part of the core, - * so I moved them to a separate file so the core code would be cleaner. - * - * by Bjorn Roche. - */ - -/* - * Translates MacOS generated errors into PaErrors - */ -static PaError PaMacCore_SetError(OSStatus error, int line, int isError) -{ - /*FIXME: still need to handle possible ComponentResult values.*/ - /* unfortunately, they don't seem to be documented anywhere.*/ - PaError result; - const char *errorType; - const char *errorText; - - switch (error) { - case kAudioHardwareNoError: - return paNoError; - case kAudioHardwareNotRunningError: - errorText = "Audio Hardware Not Running"; - result = paInternalError; break; - case kAudioHardwareUnspecifiedError: - errorText = "Unspecified Audio Hardware Error"; - result = paInternalError; break; - case kAudioHardwareUnknownPropertyError: - errorText = "Audio Hardware: Unknown Property"; - result = paInternalError; break; - case kAudioHardwareBadPropertySizeError: - errorText = "Audio Hardware: Bad Property Size"; - result = paInternalError; break; - case kAudioHardwareIllegalOperationError: - errorText = "Audio Hardware: Illegal Operation"; - result = paInternalError; break; - case kAudioHardwareBadDeviceError: - errorText = "Audio Hardware: Bad Device"; - result = paInvalidDevice; break; - case kAudioHardwareBadStreamError: - errorText = "Audio Hardware: BadStream"; - result = paBadStreamPtr; break; - case kAudioHardwareUnsupportedOperationError: - errorText = "Audio Hardware: Unsupported Operation"; - result = paInternalError; break; - case kAudioDeviceUnsupportedFormatError: - errorText = "Audio Device: Unsupported Format"; - result = paSampleFormatNotSupported; break; - case kAudioDevicePermissionsError: - errorText = "Audio Device: Permissions Error"; - result = paDeviceUnavailable; break; - /* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */ - case kAudioUnitErr_InvalidProperty: - errorText = "Audio Unit: Invalid Property"; - result = paInternalError; break; - case kAudioUnitErr_InvalidParameter: - errorText = "Audio Unit: Invalid Parameter"; - result = paInternalError; break; - case kAudioUnitErr_NoConnection: - errorText = "Audio Unit: No Connection"; - result = paInternalError; break; - case kAudioUnitErr_FailedInitialization: - errorText = "Audio Unit: Initialization Failed"; - result = paInternalError; break; - case kAudioUnitErr_TooManyFramesToProcess: - errorText = "Audio Unit: Too Many Frames"; - result = paInternalError; break; - case kAudioUnitErr_IllegalInstrument: - errorText = "Audio Unit: Illegal Instrument"; - result = paInternalError; break; - case kAudioUnitErr_InstrumentTypeNotFound: - errorText = "Audio Unit: Instrument Type Not Found"; - result = paInternalError; break; - case kAudioUnitErr_InvalidFile: - errorText = "Audio Unit: Invalid File"; - result = paInternalError; break; - case kAudioUnitErr_UnknownFileType: - errorText = "Audio Unit: Unknown File Type"; - result = paInternalError; break; - case kAudioUnitErr_FileNotSpecified: - errorText = "Audio Unit: File Not Specified"; - result = paInternalError; break; - case kAudioUnitErr_FormatNotSupported: - errorText = "Audio Unit: Format Not Supported"; - result = paInternalError; break; - case kAudioUnitErr_Uninitialized: - errorText = "Audio Unit: Unitialized"; - result = paInternalError; break; - case kAudioUnitErr_InvalidScope: - errorText = "Audio Unit: Invalid Scope"; - result = paInternalError; break; - case kAudioUnitErr_PropertyNotWritable: - errorText = "Audio Unit: PropertyNotWritable"; - result = paInternalError; break; - case kAudioUnitErr_InvalidPropertyValue: - errorText = "Audio Unit: Invalid Property Value"; - result = paInternalError; break; - case kAudioUnitErr_PropertyNotInUse: - errorText = "Audio Unit: Property Not In Use"; - result = paInternalError; break; - case kAudioUnitErr_Initialized: - errorText = "Audio Unit: Initialized"; - result = paInternalError; break; - case kAudioUnitErr_InvalidOfflineRender: - errorText = "Audio Unit: Invalid Offline Render"; - result = paInternalError; break; - case kAudioUnitErr_Unauthorized: - errorText = "Audio Unit: Unauthorized"; - result = paInternalError; break; - case kAudioUnitErr_CannotDoInCurrentContext: - errorText = "Audio Unit: cannot do in current context"; - result = paInternalError; break; - default: - errorText = "Unknown Error"; - result = paInternalError; - } - - if (isError) - errorType = "Error"; - else - errorType = "Warning"; - - if ((int)error < -99999 || (int)error > 99999) - DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText)); - else - DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText)); - - PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText ); - - return result; -} - - - - -/* - * Durring testing of core audio, I found that serious crashes could occur - * if properties such as sample rate were changed multiple times in rapid - * succession. The function below has some fancy logic to make sure that changes - * are acknowledged before another is requested. That seems to help a lot. - */ - -#include <pthread.h> -typedef struct { - bool once; /* I didn't end up using this. bdr */ - pthread_mutex_t mutex; -} MutexAndBool ; - -static OSStatus propertyProc( - AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData ) -{ - MutexAndBool *mab = (MutexAndBool *) inClientData; - mab->once = TRUE; - pthread_mutex_unlock( &(mab->mutex) ); - return noErr; -} - -/* sets the value of the given property and waits for the change to - be acknowledged, and returns the final value, which is not guaranteed - by this function to be the same as the desired value. Obviously, this - function can only be used for data whose input and output are the - same size and format, and their size and format are known in advance.*/ -PaError AudioDeviceSetPropertyNowAndWaitForChange( - AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - UInt32 inPropertyDataSize, - const void *inPropertyData, - void *outPropertyData ) -{ - OSStatus macErr; - int unixErr; - MutexAndBool mab; - UInt32 outPropertyDataSize = inPropertyDataSize; - - /* First, see if it already has that value. If so, return. */ - macErr = AudioDeviceGetProperty( inDevice, inChannel, - isInput, inPropertyID, - &outPropertyDataSize, outPropertyData ); - if( macErr ) - goto failMac2; - if( inPropertyDataSize!=outPropertyDataSize ) - return paInternalError; - if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) - return paNoError; - - /* setup and lock mutex */ - mab.once = FALSE; - unixErr = pthread_mutex_init( &mab.mutex, NULL ); - if( unixErr ) - goto failUnix2; - unixErr = pthread_mutex_lock( &mab.mutex ); - if( unixErr ) - goto failUnix; - - /* add property listener */ - macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc, - &mab ); - if( macErr ) - goto failMac; - /* set property */ - macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel, - isInput, inPropertyID, - inPropertyDataSize, inPropertyData ); - if( macErr ) { - /* we couldn't set the property, so we'll just unlock the mutex - and move on. */ - pthread_mutex_unlock( &mab.mutex ); - } - - /* wait for property to change */ - unixErr = pthread_mutex_lock( &mab.mutex ); - if( unixErr ) - goto failUnix; - - /* now read the property back out */ - macErr = AudioDeviceGetProperty( inDevice, inChannel, - isInput, inPropertyID, - &outPropertyDataSize, outPropertyData ); - if( macErr ) - goto failMac; - /* cleanup */ - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - unixErr = pthread_mutex_unlock( &mab.mutex ); - if( unixErr ) - goto failUnix2; - unixErr = pthread_mutex_destroy( &mab.mutex ); - if( unixErr ) - goto failUnix2; - - return paNoError; - - failUnix: - pthread_mutex_destroy( &mab.mutex ); - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - - failUnix2: - DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) ); - return paUnanticipatedHostError; - - failMac: - pthread_mutex_destroy( &mab.mutex ); - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - failMac2: - return ERR( macErr ); -} - -/* - * Sets the sample rate the HAL device. - * if requireExact: set the sample rate or fail. - * - * otherwise : set the exact sample rate. - * If that fails, check for available sample rates, and choose one - * higher than the requested rate. If there isn't a higher one, - * just use the highest available. - */ -static PaError setBestSampleRateForDevice( const AudioDeviceID device, - const bool isOutput, - const bool requireExact, - const Float64 desiredSrate ) -{ - /*FIXME: changing the sample rate is disruptive to other programs using the - device, so it might be good to offer a custom flag to not change the - sample rate and just do conversion. (in my casual tests, there is - no disruption unless the sample rate really does need to change) */ - const bool isInput = isOutput ? 0 : 1; - Float64 srate; - UInt32 propsize = sizeof( Float64 ); - OSErr err; - AudioValueRange *ranges; - int i=0; - Float64 max = -1; /*the maximum rate available*/ - Float64 best = -1; /*the lowest sample rate still greater than desired rate*/ - VDBUG(("Setting sample rate for device %ld to %g.\n",device,(float)desiredSrate)); - - /* -- try setting the sample rate -- */ - err = AudioDeviceSetPropertyNowAndWaitForChange( - device, 0, isInput, - kAudioDevicePropertyNominalSampleRate, - propsize, &desiredSrate, &srate ); - if( err ) - return err; - - /* -- if the rate agrees, and we got no errors, we are done -- */ - if( !err && srate == desiredSrate ) - return paNoError; - /* -- we've failed if the rates disagree and we are setting input -- */ - if( requireExact ) - return paInvalidSampleRate; - - /* -- generate a list of available sample rates -- */ - err = AudioDeviceGetPropertyInfo( device, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &propsize, NULL ); - if( err ) - return ERR( err ); - ranges = (AudioValueRange *)calloc( 1, propsize ); - if( !ranges ) - return paInsufficientMemory; - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &propsize, ranges ); - if( err ) - { - free( ranges ); - return ERR( err ); - } - VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate)); - VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange))); -#ifdef MAC_CORE_VERBOSE_DEBUG - for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) - VDBUG( ("\t%g-%g\n", - (float) ranges[i].mMinimum, - (float) ranges[i].mMaximum ) ); -#endif - VDBUG(("-----\n")); - - /* -- now pick the best available sample rate -- */ - for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) - { - if( ranges[i].mMaximum > max ) max = ranges[i].mMaximum; - if( ranges[i].mMinimum > desiredSrate ) { - if( best < 0 ) - best = ranges[i].mMinimum; - else if( ranges[i].mMinimum < best ) - best = ranges[i].mMinimum; - } - } - if( best < 0 ) - best = max; - VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) ); - free( ranges ); - - /* -- set the sample rate -- */ - propsize = sizeof( best ); - err = AudioDeviceSetPropertyNowAndWaitForChange( - device, 0, isInput, - kAudioDevicePropertyNominalSampleRate, - propsize, &best, &srate ); - if( err ) - return err; - - if( err ) - return ERR( err ); - /* -- if the set rate matches, we are done -- */ - if( srate == best ) - return paNoError; - - /* -- otherwise, something wierd happened: we didn't set the rate, and we got no errors. Just bail. */ - return paInternalError; -} - - -/* - Attempts to set the requestedFramesPerBuffer. If it can't set the exact - value, it settles for something smaller if available. If nothing smaller - is available, it uses the smallest available size. - actualFramesPerBuffer will be set to the actual value on successful return. - OK to pass NULL to actualFramesPerBuffer. - The logic is very simmilar too setBestSampleRate only failure here is - not usually catastrophic. -*/ -static PaError setBestFramesPerBuffer( const AudioDeviceID device, - const bool isOutput, - unsigned long requestedFramesPerBuffer, - unsigned long *actualFramesPerBuffer ) -{ - UInt32 afpb; - const bool isInput = !isOutput; - UInt32 propsize = sizeof(UInt32); - OSErr err; - Float64 min = -1; /*the min blocksize*/ - Float64 best = -1; /*the best blocksize*/ - int i=0; - AudioValueRange *ranges; - - if( actualFramesPerBuffer == NULL ) - actualFramesPerBuffer = &afpb; - - - /* -- try and set exact FPB -- */ - err = AudioDeviceSetProperty( device, NULL, 0, isInput, - kAudioDevicePropertyBufferFrameSize, - propsize, &requestedFramesPerBuffer); - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferFrameSize, - &propsize, actualFramesPerBuffer); - if( err ) - return ERR( err ); - if( *actualFramesPerBuffer == requestedFramesPerBuffer ) - return paNoError; /* we are done */ - - /* -- fetch available block sizes -- */ - err = AudioDeviceGetPropertyInfo( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, NULL ); - if( err ) - return ERR( err ); - ranges = (AudioValueRange *)calloc( 1, propsize ); - if( !ranges ) - return paInsufficientMemory; - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, ranges ); - if( err ) - { - free( ranges ); - return ERR( err ); - } - VDBUG(("Requested block size of %lu was not available.\n", - requestedFramesPerBuffer )); - VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange))); -#ifdef MAC_CORE_VERBOSE_DEBUG - for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) - VDBUG( ("\t%g-%g\n", - (float) ranges[i].mMinimum, - (float) ranges[i].mMaximum ) ); -#endif - VDBUG(("-----\n")); - - /* --- now pick the best available framesPerBuffer -- */ - for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) - { - if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum; - if( ranges[i].mMaximum < requestedFramesPerBuffer ) { - if( best < 0 ) - best = ranges[i].mMaximum; - else if( ranges[i].mMaximum > best ) - best = ranges[i].mMaximum; - } - } - if( best == -1 ) - best = min; - VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) ); - free( ranges ); - - /* --- set the buffer size (ignore errors) -- */ - requestedFramesPerBuffer = (UInt32) best ; - propsize = sizeof( UInt32 ); - err = AudioDeviceSetProperty( device, NULL, 0, isInput, - kAudioDevicePropertyBufferSize, - propsize, &requestedFramesPerBuffer ); - /* --- read the property to check that it was set -- */ - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSize, - &propsize, actualFramesPerBuffer ); - - if( err ) - return ERR( err ); - - return paNoError; -} diff --git a/portaudio/pa_unix/CVS/Entries b/portaudio/pa_unix/CVS/Entries deleted file mode 100644 index 7f24f7229..000000000 --- a/portaudio/pa_unix/CVS/Entries +++ /dev/null @@ -1,4 +0,0 @@ -/pa_unix_hostapis.c/1.1.2.5/Thu Oct 2 12:35:46 2003//Tv19-devel -/pa_unix_util.c/1.1.2.8/Sun Nov 20 13:46:13 2005//Tv19-devel -/pa_unix_util.h/1.1.2.3/Fri Feb 25 02:19:40 2005//Tv19-devel -D diff --git a/portaudio/pa_unix/CVS/Repository b/portaudio/pa_unix/CVS/Repository deleted file mode 100644 index 610e5b880..000000000 --- a/portaudio/pa_unix/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_unix diff --git a/portaudio/pa_unix/CVS/Root b/portaudio/pa_unix/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_unix/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_unix/CVS/Tag b/portaudio/pa_unix/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_unix/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_unix/pa_unix_hostapis.c b/portaudio/pa_unix/pa_unix_hostapis.c deleted file mode 100644 index 9bddc2e0d..000000000 --- a/portaudio/pa_unix/pa_unix_hostapis.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter 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 ); -/* Added for IRIX, Pieter, oct 2, 2003: */ -PaError PaSGI_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 - /* Added for IRIX, Pieter, oct 2, 2003: */ -#ifdef PA_USE_SGI - PaSGI_Initialize, -#endif - 0 /* NULL terminated array */ - }; - -int paDefaultHostApiIndex = 0; - - diff --git a/portaudio/pa_unix/pa_unix_util.c b/portaudio/pa_unix/pa_unix_util.c deleted file mode 100644 index 88ca60922..000000000 --- a/portaudio/pa_unix/pa_unix_util.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * $Id: pa_unix_util.c,v 1.1.2.8 2005/11/20 13:46:13 aknudsen 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 <pthread.h> -#include <unistd.h> -#include <stdlib.h> -#include <time.h> -#include <sys/time.h> -#include <assert.h> -#include <string.h> /* For memset */ - -#include "pa_util.h" -#include "pa_unix_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 ) -{ -#ifdef HAVE_NANOSLEEP - struct timespec req = {0}, rem = {0}; - PaTime time = msec / 1.e3; - req.tv_sec = (time_t)time; - assert(time - req.tv_sec < 1.0); - req.tv_nsec = (long)((time - req.tv_sec) * 1.e9); - nanosleep(&req, &rem); - /* XXX: Try sleeping the remaining time (contained in rem) if interrupted by a signal? */ -#else - while( msec > 999 ) /* For OpenBSD and IRIX, argument */ - { /* to usleep must be < 1000000. */ - usleep( 999000 ); - msec -= 999; - } - usleep( msec * 1000 ); -#endif -} - -/* *** NOT USED YET: *** -static int usePerformanceCounter_; -static double microsecondsPerTick_; -*/ - -void PaUtil_InitializeClock( void ) -{ - /* TODO */ -} - - -PaTime PaUtil_GetTime( void ) -{ -#ifdef HAVE_CLOCK_GETTIME - struct timespec tp; - clock_gettime(CLOCK_REALTIME, &tp); - return (PaTime)(tp.tv_sec + tp.tv_nsec / 1.e9); -#else - struct timeval tv; - gettimeofday( &tv, NULL ); - return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec; -#endif -} - -PaError PaUtil_InitializeThreading( PaUtilThreading *threading ) -{ - (void) paUtilErr_; - return paNoError; -} - -void PaUtil_TerminateThreading( PaUtilThreading *threading ) -{ -} - -PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ) -{ - pthread_create( &threading->callbackThread, NULL, threadRoutine, data ); - return paNoError; -} - -PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ) -{ - PaError result = paNoError; - void *pret; - - if( exitResult ) - *exitResult = paNoError; - - /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ - if( !wait ) - pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ - pthread_join( threading->callbackThread, &pret ); - -#ifdef PTHREAD_CANCELED - if( pret && PTHREAD_CANCELED != pret ) -#else - /* !wait means the thread may have been canceled */ - if( pret && wait ) -#endif - { - if( exitResult ) - *exitResult = *(PaError *) pret; - free( pret ); - } - - return result; -} - -/* -static void *CanaryFunc( void *userData ) -{ - const unsigned intervalMsec = 1000; - PaUtilThreading *th = (PaUtilThreading *) userData; - - while( 1 ) - { - th->canaryTime = PaUtil_GetTime(); - - pthread_testcancel(); - Pa_Sleep( intervalMsec ); - } - - pthread_exit( NULL ); -} -*/ diff --git a/portaudio/pa_unix/pa_unix_util.h b/portaudio/pa_unix/pa_unix_util.h deleted file mode 100644 index 01dda01e5..000000000 --- a/portaudio/pa_unix/pa_unix_util.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef PA_UNIX_UTIL_H -#define PA_UNIX_UTIL_H - -#include "pa_cpuload.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) ) -#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) ) - -/* Utilize GCC branch prediction for error tests */ -#if defined __GNUC__ && __GNUC__ >= 3 -#define UNLIKELY(expr) __builtin_expect( (expr), 0 ) -#else -#define UNLIKELY(expr) (expr) -#endif - -#define STRINGIZE_HELPER(expr) #expr -#define STRINGIZE(expr) STRINGIZE_HELPER(expr) - -#define PA_UNLESS(expr, code) \ - do { \ - if( UNLIKELY( (expr) == 0 ) ) \ - { \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while (0); - -static PaError paUtilErr_; /* Used with PA_ENSURE */ - -/* Check PaError */ -#define PA_ENSURE(expr) \ - do { \ - if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \ - { \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = paUtilErr_; \ - goto error; \ - } \ - } while (0); - -typedef struct { - pthread_t callbackThread; -} PaUtilThreading; - -PaError PaUtil_InitializeThreading( PaUtilThreading *threading ); -void PaUtil_TerminateThreading( PaUtilThreading *threading ); -PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ); -PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ); - -/* State accessed by utility functions */ - -/* -void PaUnix_SetRealtimeScheduling( int rt ); - -void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm ); - -PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s ); - -PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult ); - -void PaUtil_CallbackUpdate( PaUtilThreading *th ); -*/ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif diff --git a/portaudio/pa_unix_oss/CVS/Entries b/portaudio/pa_unix_oss/CVS/Entries deleted file mode 100644 index d949129d7..000000000 --- a/portaudio/pa_unix_oss/CVS/Entries +++ /dev/null @@ -1,4 +0,0 @@ -/low_latency_tip.txt/1.1.1.1/Tue Jan 22 00:52:44 2002//Tv19-devel -/pa_unix_oss.c/1.6.2.28/Mon Mar 20 18:22:06 2006//Tv19-devel -/recplay.c/1.1.1.1/Tue Jan 22 00:52:44 2002//Tv19-devel -D diff --git a/portaudio/pa_unix_oss/CVS/Repository b/portaudio/pa_unix_oss/CVS/Repository deleted file mode 100644 index 274189e5c..000000000 --- a/portaudio/pa_unix_oss/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_unix_oss diff --git a/portaudio/pa_unix_oss/CVS/Root b/portaudio/pa_unix_oss/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_unix_oss/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_unix_oss/CVS/Tag b/portaudio/pa_unix_oss/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_unix_oss/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_unix_oss/low_latency_tip.txt b/portaudio/pa_unix_oss/low_latency_tip.txt deleted file mode 100644 index 2d982b79baa6d943c1fae0c7122d190ebf46c6f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3111 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 diff --git a/portaudio/pa_unix_oss/pa_unix_oss.c b/portaudio/pa_unix_oss/pa_unix_oss.c deleted file mode 100644 index 125fb8ce6..000000000 --- a/portaudio/pa_unix_oss/pa_unix_oss.c +++ /dev/null @@ -1,1924 +0,0 @@ -/* - * $Id: pa_unix_oss.c,v 1.6.2.28 2006/03/20 18:22:06 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * OSS implementation by: - * Douglas Repetto - * Phil Burk - * Dominic Mazzoni - * Arve Knudsen - * - * 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 <stdio.h> -#include <string.h> -#include <math.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <unistd.h> -#include <pthread.h> -#include <alloca.h> -#include <malloc.h> -#include <assert.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/poll.h> -#include <limits.h> -#include <semaphore.h> - -#ifdef __FreeBSD__ -# include <sys/soundcard.h> -# define DEVICE_NAME_BASE "/dev/dsp" -#elif defined __linux__ -# include <linux/soundcard.h> -# define DEVICE_NAME_BASE "/dev/dsp" -#else -# include <machine/soundcard.h> /* 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" -#include "../pa_unix/pa_unix_util.h" - -static int sysErr_; -static pthread_t mainThread_; - -/* Check return value of system call, and map it to PaError */ -#define ENSURE_(expr, code) \ - do { \ - if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \ - } \ - \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ); - -#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 - -/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaHostApiIndex hostApiIndex; -} -PaOSSHostApiRepresentation; - -/** Per-direction structure for PaOssStream. - * - * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback, - * but with different number of channels we will have to adapt between the number of user and host - * channels for at least one direction, since the configuration space is the same for both directions - * of an OSS device. - */ -typedef struct -{ - int fd; - const char *devName; - int userChannelCount, hostChannelCount; - int userInterleaved; - void *buffer; - PaSampleFormat userFormat, hostFormat; - double latency; - unsigned long hostFrames, numBufs; - void **userBuffers; /* For non-interleaved blocking */ -} PaOssStreamComponent; - -/** Implementation specific representation of a PaStream. - * - */ -typedef struct PaOssStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaUtilThreading threading; - - int sharedDevice; - unsigned long framesPerHostBuffer; - int triggered; /* Have the devices been triggered yet (first start) */ - - int isActive; - int isStopped; - - int lastPosPtr; - double lastStreamBytes; - - int framesProcessed; - - double sampleRate; - - int callbackMode; - int callbackStop, callbackAbort; - - PaOssStreamComponent *capture, *playback; - unsigned long pollTimeout; - sem_t semaphore; -} -PaOssStream; - -typedef enum { - StreamMode_In, - StreamMode_Out -} StreamMode; - -/* prototypes for functions declared in this file */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); -static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi ); - - -/** Initialize the OSS API implementation. - * - * This function will initialize host API datastructures and query host devices for information. - * - * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here - * - * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function, - * this happens with the usual "error" label. - */ -PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaOSSHostApiRepresentation *ossHostApi = NULL; - - PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ), - paInsufficientMemory ); - PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - ossHostApi->hostApiIndex = hostApiIndex; - - /* Initialize host API structure */ - *hostApi = &ossHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paOSS; - (*hostApi)->info.name = "OSS"; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PA_ENSURE( BuildDeviceList( ossHostApi ) ); - - PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - mainThread_ = pthread_self(); - - return result; - -error: - if( ossHostApi ) - { - if( ossHostApi->allocations ) - { - PaUtil_FreeAllAllocations( ossHostApi->allocations ); - PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); - } - - PaUtil_FreeMemory( ossHostApi ); - } - return result; -} - -PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels, - int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency, - PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations ) -{ - PaError result = paNoError; - - deviceInfo->structVersion = 2; - if( allocations ) - { - size_t len = strlen( name ) + 1; - PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory ); - strncpy( (char *)deviceInfo->name, name, len ); - } - else - deviceInfo->name = name; - - deviceInfo->hostApi = hostApiIndex; - deviceInfo->maxInputChannels = maxInputChannels; - deviceInfo->maxOutputChannels = maxOutputChannels; - deviceInfo->defaultLowInputLatency = defaultLowInputLatency; - deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency; - deviceInfo->defaultHighInputLatency = defaultHighInputLatency; - deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency; - deviceInfo->defaultSampleRate = defaultSampleRate; - -error: - return result; -} - -static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount, - double *defaultLowLatency, double *defaultHighLatency ) -{ - PaError result = paNoError; - int numChannels, maxNumChannels; - int busy = 0; - int devHandle = -1; - int sr; - *maxChannelCount = 0; /* Default value in case this fails */ - - if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 ) - { - if( errno == EBUSY || errno == EAGAIN ) - { - PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName )); - } - else - { - PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) )); - } - - return paDeviceUnavailable; - } - - /* 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; - if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 ) - { - busy = EAGAIN == errno || EBUSY == errno; - /* ioctl() failed so bail out if we already have stereo */ - if( maxNumChannels >= 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; - if( temp > maxNumChannels ) - maxNumChannels = temp; /* Save maximum. */ - } - } - /* A: We're able to open a device for capture if it's busy playing back and vice versa, - * but we can't configure anything */ - if( 0 == maxNumChannels && busy ) - { - result = paDeviceUnavailable; - goto error; - } - - /* The above negotiation may fail for an old driver so try this older technique. */ - if( maxNumChannels < 1 ) - { - int stereo = 1; - if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 ) - { - maxNumChannels = 1; - } - else - { - maxNumChannels = (stereo) ? 2 : 1; - } - PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels )) - } - - /* During channel negotiation, the last ioctl() may have failed. This can - * also cause sample rate negotiation to fail. Hence the following, to return - * to a supported number of channels. SG20011005 */ - { - /* use most reasonable default value */ - int temp = PA_MIN( maxNumChannels, 2 ); - ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError ); - } - - /* Get supported sample rate closest to 44100 Hz */ - if( *defaultSampleRate < 0 ) - { - sr = 44100; - if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 ) - { - result = paUnanticipatedHostError; - goto error; - } - - *defaultSampleRate = sr; - } - - *maxChannelCount = maxNumChannels; - /* TODO */ - *defaultLowLatency = 512. / *defaultSampleRate; - *defaultHighLatency = 2048. / *defaultSampleRate; - -error: - if( devHandle >= 0 ) - close( devHandle ); - - return result; -} - -/** Query OSS device. - * - * This is where PaDeviceInfo objects are constructed and filled in with relevant information. - * - * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed - * in place. - */ -static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo ) -{ - PaError result = paNoError; - double sampleRate = -1.; - int maxInputChannels, maxOutputChannels; - PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency; - PaError tmpRes = paNoError; - int busy = 0; - *deviceInfo = NULL; - - /* 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 - */ - - /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is - * opened in, it may have more channels available for capture than playback and vice versa. Therefore - * we will open the device in both read- and write-only mode to determine the supported number. - */ - if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency, - &defaultHighInputLatency )) != paNoError ) - { - if( tmpRes != paDeviceUnavailable ) - { - PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName )); - /* PA_ENSURE( tmpRes ); */ - } - ++busy; - } - if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency, - &defaultHighOutputLatency )) != paNoError ) - { - if( tmpRes != paDeviceUnavailable ) - { - PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName )); - /* PA_ENSURE( tmpRes ); */ - } - ++busy; - } - assert( 0 <= busy && busy <= 2 ); - if( 2 == busy ) /* Both directions are unavailable to us */ - { - result = paDeviceUnavailable; - goto error; - } - - PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory ); - PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels, - defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate, - ossApi->allocations ) ); - -error: - return result; -} - -/** Query host devices. - * - * Loop over host devices and query their capabilitiesu - * - * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object - * per device, these are placed in the host api representation's deviceInfos array. - */ -static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep; - int i; - int numDevices = 0, maxDeviceInfos = 1; - PaDeviceInfo **deviceInfos = NULL; - - /* These two will be set to the first working input and output device, respectively */ - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - - /* Find devices by calling QueryDevice on each - * potential device names. When we find a valid one, - * add it to a linked list. - * A: Can there only be 10 devices? */ - - for( i = 0; i < 10; i++ ) - { - char deviceName[32]; - PaDeviceInfo *deviceInfo; - int testResult; - struct stat stbuf; - - if( i == 0 ) - snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE); - else - snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i); - - /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */ - if( stat( deviceName, &stbuf ) < 0 ) - { - if( ENOENT != errno ) - PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) )); - continue; - } - if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError ) - { - if( testResult != paDeviceUnavailable ) - PA_ENSURE( testResult ); - - continue; - } - - ++numDevices; - if( !deviceInfos || numDevices > maxDeviceInfos ) - { - maxDeviceInfos *= 2; - PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ), - paInsufficientMemory ); - } - { - int devIdx = numDevices - 1; - deviceInfos[devIdx] = deviceInfo; - - if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = devIdx; - if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = devIdx; - } - } - - /* Make an array of PaDeviceInfo pointers out of the linked list */ - - PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices)); - - commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); - memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) ); - - commonApi->info.deviceCount = numDevices; - -error: - free( deviceInfos ); - - return result; -} - -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 ) -{ - PaError result = paNoError; - PaDeviceIndex device; - PaDeviceInfo *deviceInfo; - char *deviceName; - int inputChannelCount, outputChannelCount; - int tempDevHandle = -1; - 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; - - ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable ); - - /* PaOssStream_Configure will do the rest of the checking for us */ - /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */ - - /* everything succeeded! */ - - error: - if( tempDevHandle >= 0 ) - close( tempDevHandle ); - - return result; -} - -/** Validate stream parameters. - * - * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device - */ -static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode ) -{ - int maxChans; - - assert( parameters ); - - if( parameters->device == paUseHostApiSpecificDeviceSpecification ) - { - return paInvalidDevice; - } - - maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels : - deviceInfo->maxOutputChannels); - if( parameters->channelCount > maxChans ) - { - return paInvalidChannelCount; - } - - return paNoError; -} - -static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters, - int callbackMode, int fd, const char *deviceName ) -{ - PaError result = paNoError; - assert( component ); - - memset( component, 0, sizeof (PaOssStreamComponent) ); - - component->fd = fd; - component->devName = deviceName; - component->userChannelCount = parameters->channelCount; - component->userFormat = parameters->sampleFormat; - component->latency = parameters->suggestedLatency; - component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved); - - if( !callbackMode && !component->userInterleaved ) - { - /* Pre-allocate non-interleaved user provided buffers */ - PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ), - paInsufficientMemory ); - } - -error: - return result; -} - -static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component ) -{ - assert( component ); - - if( component->fd >= 0 ) - close( component->fd ); - if( component->buffer ) - PaUtil_FreeMemory( component->buffer ); - - if( component->userBuffers ) - PaUtil_FreeMemory( component->userBuffers ); - - PaUtil_FreeMemory( component ); -} - -static PaError ModifyBlocking( int fd, int blocking ) -{ - PaError result = paNoError; - int fflags; - - ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError ); - - if( blocking ) - fflags &= ~O_NONBLOCK; - else - fflags |= O_NONBLOCK; - - ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError ); - -error: - return result; -} - -static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev ) -{ - PaError result = paNoError; - int flags = O_NONBLOCK, duplex = 0; - int enableBits = 0; - *idev = *odev = -1; - - if( idevName && odevName ) - { - duplex = 1; - flags |= O_RDWR; - } - else if( idevName ) - flags |= O_RDONLY; - else - flags |= O_WRONLY; - - /* open first in nonblocking mode, in case it's busy... - * A: then unset the non-blocking attribute */ - assert( flags & O_NONBLOCK ); - if( idevName ) - { - ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable ); - PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */ - - /* Initially disable */ - enableBits = ~PCM_ENABLE_INPUT; - ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - if( odevName ) - { - if( !idevName ) - { - ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable ); - PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */ - - /* Initially disable */ - enableBits = ~PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - else - { - ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError ); - } - } - -error: - return result; -} - -static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, - PaStreamCallback callback, void *userData, PaStreamFlags streamFlags, - PaOSSHostApiRepresentation *ossApi ) -{ - PaError result = paNoError; - int idev, odev; - PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep; - const char *idevName = NULL, *odevName = NULL; - - assert( stream ); - - memset( stream, 0, sizeof (PaOssStream) ); - stream->isStopped = 1; - - PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) ); - - if( inputParameters && outputParameters ) - { - if( inputParameters->device == outputParameters->device ) - stream->sharedDevice = 1; - } - - if( inputParameters ) - idevName = hostApi->deviceInfos[inputParameters->device]->name; - if( outputParameters ) - odevName = hostApi->deviceInfos[outputParameters->device]->name; - PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) ); - if( inputParameters ) - { - PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); - PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) ); - } - if( outputParameters ) - { - PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); - PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) ); - } - - if( callback != NULL ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &ossApi->callbackStreamInterface, callback, userData ); - stream->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &ossApi->blockingStreamInterface, callback, userData ); - } - - ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError ); - -error: - return result; -} - -static void PaOssStream_Terminate( PaOssStream *stream ) -{ - assert( stream ); - - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_TerminateThreading( &stream->threading ); - - if( stream->capture ) - PaOssStreamComponent_Terminate( stream->capture ); - if( stream->playback ) - PaOssStreamComponent_Terminate( stream->playback ); - - sem_destroy( &stream->semaphore ); - - PaUtil_FreeMemory( stream ); -} - -/** Translate from PA format to OSS native. - * - */ -static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat ) -{ - switch( paFormat ) - { - case paUInt8: - *ossFormat = AFMT_U8; - break; - case paInt8: - *ossFormat = AFMT_S8; - break; - case paInt16: - *ossFormat = AFMT_S16_NE; - break; - default: - return paInternalError; /* This shouldn't happen */ - } - - return paNoError; -} - -/** Return the PA-compatible formats that this device can support. - * - */ -static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats ) -{ - PaError result = paNoError; - int mask = 0; - PaSampleFormat frmts = 0; - - ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError ); - if( mask & AFMT_U8 ) - frmts |= paUInt8; - if( mask & AFMT_S8 ) - frmts |= paInt8; - if( mask & AFMT_S16_NE ) - frmts |= paInt16; - else - result = paSampleFormatNotSupported; - - *availableFormats = frmts; - -error: - return result; -} - -static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component ) -{ - return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount; -} - -/** Buffer size in bytes. - * - */ -static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component ) -{ - return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs; -} - -static int CalcHigherLogTwo( int n ) -{ - int log2 = 0; - while( (1<<log2) < n ) log2++; - return log2; -} - -static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer, - StreamMode streamMode, PaOssStreamComponent *master ) -{ - PaError result = paNoError; - int temp, nativeFormat; - int sr = (int)sampleRate; - PaSampleFormat availableFormats, hostFormat; - int chans = component->userChannelCount; - int frgmt; - int numBufs; - int bytesPerBuf; - double bufSz; - unsigned long fragSz; - audio_buf_info bufInfo; - - /* We may have a situation where only one component (the master) is configured, if both point to the same device. - * In that case, the second component will copy settings from the other */ - if( !master ) - { - /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size. - * The hardware need not respect the requested fragment size, so we may have to adapt. - */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - bufSz = component->latency * sampleRate; - fragSz = bufSz / 4; - } - else - { - fragSz = framesPerBuffer; - bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */ - } - - PA_ENSURE( GetAvailableFormats( component, &availableFormats ) ); - hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat ); - - /* OSS demands at least 2 buffers, and 16 bytes per buffer */ - numBufs = PA_MAX( bufSz / fragSz, 2 ); - bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 ); - - /* The fragment parameters are encoded like this: - * Most significant byte: number of fragments - * Least significant byte: exponent of fragment size (i.e., for 256, 8) - */ - frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff); - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError ); - - /* A: according to the OSS programmer's guide parameters should be set in this order: - * format, channels, rate */ - - /* This format should be deemed good before we get this far */ - PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) ); - nativeFormat = temp; - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError ); - PA_UNLESS( temp == nativeFormat, paInternalError ); - - /* try to set the number of channels */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */ - /* It's possible that the minimum number of host channels is greater than what the user requested */ - PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount ); - - /* try to set the sample rate */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate ); - - /* reject if there's no sample rate within 1% of the one requested */ - if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 ) - { - PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); - PA_ENSURE( paInvalidSampleRate ); - } - - ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ), - paUnanticipatedHostError ); - component->numBufs = bufInfo.fragstotal; - - /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError ); - - component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans; - component->hostChannelCount = chans; - component->hostFormat = hostFormat; - } - else - { - component->hostFormat = master->hostFormat; - component->hostFrames = master->hostFrames; - component->hostChannelCount = master->hostChannelCount; - component->numBufs = master->numBufs; - } - - PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ), - paInsufficientMemory ); - -error: - return result; -} - -static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames ) -{ - PaError result = paNoError; - size_t len = *frames * PaOssStreamComponent_FrameSize( component ); - ssize_t bytesRead; - - ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError ); - *frames = bytesRead / PaOssStreamComponent_FrameSize( component ); - /* TODO: Handle condition where number of frames read doesn't equal number of frames requested */ - -error: - return result; -} - -static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames ) -{ - PaError result = paNoError; - size_t len = *frames * PaOssStreamComponent_FrameSize( component ); - ssize_t bytesWritten; - - ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError ); - *frames = bytesWritten / PaOssStreamComponent_FrameSize( component ); - /* TODO: Handle condition where number of frames written doesn't equal number of frames requested */ - -error: - return result; -} - -/** Configure the stream according to input/output parameters. - * - * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by - * the user, if so we'll record the actual number of host channels and adapt later. - */ -static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer, - double *inputLatency, double *outputLatency ) -{ - PaError result = paNoError; - int duplex = stream->capture && stream->playback; - unsigned long framesPerHostBuffer = 0; - - /* We should request full duplex first thing after opening the device */ - if( duplex && stream->sharedDevice ) - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError ); - - if( stream->capture ) - { - PaOssStreamComponent *component = stream->capture; - PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL ); - - assert( component->hostChannelCount > 0 ); - assert( component->hostFrames > 0 ); - - *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; - } - if( stream->playback ) - { - PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL; - PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out, - master ) ); - - assert( component->hostChannelCount > 0 ); - assert( component->hostFrames > 0 ); - - *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; - } - - if( duplex ) - framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames ); - else if( stream->capture ) - framesPerHostBuffer = stream->capture->hostFrames; - else if( stream->playback ) - framesPerHostBuffer = stream->playback->hostFrames; - - stream->framesPerHostBuffer = framesPerHostBuffer; - stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */ - - stream->sampleRate = stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - -error: - return result; -} - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -/** Open a PA OSS stream. - * - * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the - * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both - * directions are the same device we will demand the same number of channels. The number of channels can range - * from 1 to the maximum supported by the device. - * - * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback - * must reflect this, in addition the host latency per device should approximate the corresponding - * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that - * both capture and playback can agree on (they can be different devices), the buffer processor can adapt - * between host and user buffer size, but the ratio should preferably be integral. - */ -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 = NULL; - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0; - const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0; - int bpInitialized = 0; - double inLatency, outLatency; - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - if( inputParameters ) - { - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - inputDeviceInfo = hostApi->deviceInfos[inputParameters->device]; - PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) ); - - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - if( outputParameters ) - { - outputDeviceInfo = hostApi->deviceInfos[outputParameters->device]; - PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) ); - - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same - * device is opened for both directions - */ - if( inputChannelCount > 0 && outputChannelCount > 0 ) - { - if( inputParameters->device == outputParameters->device ) - { - if( inputParameters->channelCount != outputParameters->channelCount ) - return paInvalidChannelCount; - } - } - - /* allocate and do basic initialization of the stream structure */ - PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory ); - PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) ); - - PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) ); - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - if( inputParameters ) - { - inputHostFormat = stream->capture->hostFormat; - stream->streamRepresentation.streamInfo.inputLatency = inLatency + - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate; - } - if( outputParameters ) - { - outputHostFormat = stream->playback->hostFormat; - stream->streamRepresentation.streamInfo.outputLatency = outLatency + - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate; - } - - /* Initialize buffer processor with fixed host buffer size. - * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will - * convert between the two. - */ - PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat, - outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer, - paUtilFixedHostBufferSize, streamCallback, userData ) ); - bpInitialized = 1; - - *s = (PaStream*)stream; - - return result; - -error: - if( bpInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - if( stream ) - PaOssStream_Terminate( stream ); - - return result; -} - -/*! Poll on I/O filedescriptors. - - Poll till we've determined there's data for read or write. In the full-duplex case, - we don't want to hang around forever waiting for either input or output frames, so - whenever we have a timed out filedescriptor we check if we're nearing under/overrun - for the other direction (critical limit set at one buffer). If so, we exit the waiting - state, and go on with what we got. We align the number of frames on a host buffer - boundary because it is possible that the buffer size differs for the two directions and - the host buffer size is a compromise between the two. - */ -static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames ) -{ - PaError result = paNoError; - int pollPlayback = 0, pollCapture = 0; - int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail; - audio_buf_info bufInfo; - /* int ofs = 0, nfds = stream->nfds; */ - fd_set readFds, writeFds; - int nfds = 0; - struct timeval selectTimeval = {0, 0}; - unsigned long timeout = stream->pollTimeout; /* In usecs */ - int captureFd = -1, playbackFd = -1; - - assert( stream ); - assert( frames ); - - if( stream->capture ) - { - pollCapture = 1; - captureFd = stream->capture->fd; - /* stream->capture->pfd->events = POLLIN; */ - } - if( stream->playback ) - { - pollPlayback = 1; - playbackFd = stream->playback->fd; - /* stream->playback->pfd->events = POLLOUT; */ - } - - FD_ZERO( &readFds ); - FD_ZERO( &writeFds ); - - while( pollPlayback || pollCapture ) - { - pthread_testcancel(); - - /* select may modify the timeout parameter */ - selectTimeval.tv_usec = timeout; - nfds = 0; - - if( pollCapture ) - { - FD_SET( captureFd, &readFds ); - nfds = captureFd + 1; - } - if( pollPlayback ) - { - FD_SET( playbackFd, &writeFds ); - nfds = PA_MAX( nfds, playbackFd + 1 ); - } - ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError ); - /* - if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 ) - { - - ENSURE_( -1, paUnanticipatedHostError ); - } - */ - pthread_testcancel(); - - if( pollCapture ) - { - if( FD_ISSET( captureFd, &readFds ) ) - { - FD_CLR( captureFd, &readFds ); - pollCapture = 0; - } - /* - if( stream->capture->pfd->revents & POLLIN ) - { - --nfds; - ++ofs; - pollCapture = 0; - } - */ - else if( stream->playback ) /* Timed out, go on with playback? */ - { - /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n", - __FUNCTION__, stream->pollTimeout ));*/ - } - } - if( pollPlayback ) - { - if( FD_ISSET( playbackFd, &writeFds ) ) - { - FD_CLR( playbackFd, &writeFds ); - pollPlayback = 0; - } - /* - if( stream->playback->pfd->revents & POLLOUT ) - { - --nfds; - pollPlayback = 0; - } - */ - else if( stream->capture ) /* Timed out, go on with capture? */ - { - /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n", - __FUNCTION__, stream->pollTimeout ));*/ - } - } - } - - if( stream->capture ) - { - ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError ); - captureAvail = bufInfo.fragments * stream->capture->hostFrames; - if( !captureAvail ) - PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ )); - - captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */ - } - if( stream->playback ) - { - ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError ); - playbackAvail = bufInfo.fragments * stream->playback->hostFrames; - if( !playbackAvail ) - { - PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ )); - } - - playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */ - } - - commonAvail = PA_MIN( captureAvail, playbackAvail ); - if( commonAvail == INT_MAX ) - commonAvail = 0; - commonAvail -= commonAvail % stream->framesPerHostBuffer; - - assert( commonAvail != INT_MAX ); - assert( commonAvail >= 0 ); - *frames = commonAvail; - -error: - return result; -} - -/** Prepare stream for capture/playback. - * - * In order to synchronize capture and playback properly we use the SETTRIGGER command. - */ -static PaError PaOssStream_Prepare( PaOssStream *stream ) -{ - PaError result = paNoError; - int enableBits = 0; - - if( stream->triggered ) - return result; - - if( stream->playback ) - { - size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback ); - memset( stream->playback->buffer, 0, bufSz ); - - /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer - * OSS will complain. */ - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - while (1) - { - if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 ) - break; - } - PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) ); - } - - if( stream->sharedDevice ) - { - enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - else - { - if( stream->capture ) - { - enableBits = PCM_ENABLE_INPUT; - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - if( stream->playback ) - { - enableBits = PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - } - - /* Ok, we have triggered the stream */ - stream->triggered = 1; - -error: - return result; -} - -/** Stop audio processing - * - */ -static PaError PaOssStream_Stop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST. - * Also disable capture/playback till the stream is started again */ - if( stream->capture ) - { - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); - } - if( stream->playback && !stream->sharedDevice ) - { - ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); - } - -error: - return result; -} - -/** Clean up after thread exit. - * - * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here - */ -static void OnExit( void *data ) -{ - PaOssStream *stream = (PaOssStream *) data; - assert( data ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - PaOssStream_Stop( stream, stream->callbackAbort ); - - PA_DEBUG(( "OnExit: Stoppage\n" )); - - /* Eventually notify user all buffers have played */ - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - - stream->callbackAbort = 0; /* Clear state */ - stream->isActive = 0; -} - -static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail ) -{ - PaError result = paNoError; - - if( stream->capture ) - { - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, - stream->capture->hostChannelCount ); - PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail ); - } - if( stream->playback ) - { - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, - stream->playback->hostChannelCount ); - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail ); - } - - return result; -} - -/** Thread procedure for callback processing. - * - * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the - * callback should be used for buffer priming. When the stream is cancelled a separate function will - * take care of the transition to the Callback Finished state (the stream isn't considered Stopped - * before StopStream() or AbortStream() are called). - */ -static void *PaOSS_AudioThreadProc( void *userData ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)userData; - unsigned long framesAvail, framesProcessed; - int callbackResult = paContinue; - int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */ - int initiateProcessing = triggered; /* Already triggered? */ - PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ - - /* -#if ( SOUND_VERSION > 0x030904 ) - audio_errinfo errinfo; -#endif -*/ - - assert( stream ); - - pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */ - - /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and - * playback in sync, when the stream is restarted after being stopped we simply start by reading/ - * writing. - */ - PA_ENSURE( PaOssStream_Prepare( stream ) ); - - /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */ - if( initiateProcessing ) - { - /* Make sure devices are in blocking mode */ - if( stream->capture ) - ModifyBlocking( stream->capture->fd, 1 ); - if( stream->playback ) - ModifyBlocking( stream->playback->fd, 1 ); - } - - while( 1 ) - { - pthread_testcancel(); - - if( stream->callbackStop && callbackResult == paContinue ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless - * the stream has been recently started, we will have to go right ahead and read/write in blocking - * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch - * to non-blocking mode. - */ - if( !initiateProcessing ) - { - PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */ - assert( framesAvail % stream->framesPerHostBuffer == 0 ); - } - else - { - framesAvail = stream->framesPerHostBuffer; - } - - while( framesAvail > 0 ) - { - unsigned long frames = framesAvail; - - pthread_testcancel(); - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* Read data */ - if ( stream->capture ) - { - PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) ); - assert( frames == framesAvail ); - } - -#if ( SOUND_VERSION >= 0x030904 ) - /* - Check with OSS to see if there have been any under/overruns - since last time we checked. - */ - /* - if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 ) - { - if( errinfo.play_underruns ) - cbFlags |= paOutputUnderflow ; - if( errinfo.record_underruns ) - cbFlags |= paInputUnderflow ; - } - else - PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) )); - */ -#endif - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - cbFlags = 0; - PA_ENSURE( SetUpBuffers( stream, framesAvail ) ); - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &callbackResult ); - assert( framesProcessed == framesAvail ); - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if ( stream->playback ) - { - frames = framesAvail; - - PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) ); - assert( frames == framesAvail ); - - /* TODO: handle bytesWritten != bytesRequested (slippage?) */ - } - - framesAvail -= framesProcessed; - stream->framesProcessed += framesProcessed; - - if( callbackResult != paContinue ) - break; - } - - if( initiateProcessing || !triggered ) - { - /* Non-blocking */ - if( stream->capture ) - PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) ); - if( stream->playback && !stream->sharedDevice ) - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - - initiateProcessing = 0; - sem_post( &stream->semaphore ); - } - - if( callbackResult != paContinue ) - { - stream->callbackAbort = callbackResult == paAbort; - if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - break; - } - } - - pthread_cleanup_pop( 1 ); - -error: - pthread_exit( NULL ); -} - -/** Close the stream. - * - */ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - assert( stream ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaOssStream_Terminate( stream ); - - return result; -} - -/** Start the stream. - * - * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual - * callback will be repeatedly called in a separate thread. If a separate thread is started this function - * will block untill it has started processing audio, otherwise audio processing is started directly. - */ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - stream->isActive = 1; - stream->isStopped = 0; - stream->lastPosPtr = 0; - stream->lastStreamBytes = 0; - stream->framesProcessed = 0; - - /* only use the thread for callback streams */ - if( stream->bufferProcessor.streamCallback ) - { - PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) ); - sem_wait( &stream->semaphore ); - } - else - PA_ENSURE( PaOssStream_Prepare( stream ) ); - -error: - return result; -} - -static PaError RealStop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - if( stream->callbackMode ) - { - if( abort ) - stream->callbackAbort = 1; - else - stream->callbackStop = 1; - - PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) ); - - stream->callbackStop = stream->callbackAbort = 0; - } - else - PA_ENSURE( PaOssStream_Stop( stream, abort ) ); - - stream->isStopped = 1; - -error: - return result; -} - -/** Stop the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued - * buffers. - */ -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 0 ); -} - -/** Abort the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued - * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing - * the OSS device. - */ -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 1 ); -} - -/** Is the stream in the Stopped state. - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isStopped); -} - -/** Is the stream in the Active state. - * - */ -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->playback ) { - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) { - delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate; - } - } - else { - if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) { - delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / 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; - unsigned long framesRequested; - void *userBuffer; - - /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userInputIsInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->capture->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount ); - } - - while( frames ) - { - framesRequested = PA_MIN( frames, stream->capture->hostFrames ); - - bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture ); - bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested ); - if ( bytesRequested != bytesRead ) - return paUnanticipatedHostError; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount ); - PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested ); - frames -= framesRequested; - } - return paNoError; -} - - -static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesWritten; - unsigned long framesConverted; - const void *userBuffer; - - /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - userBuffer = buffer; - else - { - /* Copy channels into local array */ - userBuffer = stream->playback->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount ); - } - - while( frames ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount ); - - framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames ); - frames -= framesConverted; - - bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback ); - bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested ); - - if ( bytesRequested != bytesWritten ) - return paUnanticipatedHostError; - } - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - audio_buf_info info; - - if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 ) - return paUnanticipatedHostError; - return info.fragments * stream->capture->hostFrames; -} - - -/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - int delay = 0; - - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 ) - return paUnanticipatedHostError; - - return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback ); -} - diff --git a/portaudio/pa_unix_oss/recplay.c b/portaudio/pa_unix_oss/recplay.c deleted file mode 100644 index 9d4c78cfa..000000000 --- a/portaudio/pa_unix_oss/recplay.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * recplay.c - * Phil Burk - * Minimal record and playback test. - * - */ -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#ifndef __STDC__ -/* #include <getopt.h> */ -#endif /* __STDC__ */ -#include <fcntl.h> -#ifdef __STDC__ -#include <string.h> -#else /* __STDC__ */ -#include <strings.h> -#endif /* __STDC__ */ -#include <sys/soundcard.h> - -#define NUM_BYTES (64*1024) -#define BLOCK_SIZE (4*1024) - -#define AUDIO "/dev/dsp" - -char buffer[NUM_BYTES]; - -int audioDev = 0; - -main (int argc, char *argv[]) -{ - int numLeft; - char *ptr; - int num; - int samplesize; - - /********** RECORD ********************/ - /* Open audio device. */ - audioDev = open (AUDIO, O_RDONLY, 0); - if (audioDev == -1) - { - perror (AUDIO); - exit (-1); - } - - /* Set to 16 bit samples. */ - samplesize = 16; - ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); - if (samplesize != 16) - { - perror("Unable to set the sample size."); - exit(-1); - } - - /* Record in blocks */ - printf("Begin recording.\n"); - numLeft = NUM_BYTES; - ptr = buffer; - while( numLeft >= BLOCK_SIZE ) - { - if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 ) - { - perror (AUDIO); - exit (-1); - } - else - { - printf("Read %d bytes\n", num); - ptr += num; - numLeft -= num; - } - } - - close( audioDev ); - - /********** PLAYBACK ********************/ - /* Open audio device for writing. */ - audioDev = open (AUDIO, O_WRONLY, 0); - if (audioDev == -1) - { - perror (AUDIO); - exit (-1); - } - - /* Set to 16 bit samples. */ - samplesize = 16; - ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); - if (samplesize != 16) - { - perror("Unable to set the sample size."); - exit(-1); - } - - /* Play in blocks */ - printf("Begin playing.\n"); - numLeft = NUM_BYTES; - ptr = buffer; - while( numLeft >= BLOCK_SIZE ) - { - if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 ) - { - perror (AUDIO); - exit (-1); - } - else - { - printf("Wrote %d bytes\n", num); - ptr += num; - numLeft -= num; - } - } - - close( audioDev ); -} diff --git a/portaudio/pa_win/CVS/Entries b/portaudio/pa_win/CVS/Entries deleted file mode 100644 index 857a6055a..000000000 --- a/portaudio/pa_win/CVS/Entries +++ /dev/null @@ -1,5 +0,0 @@ -/pa_win_hostapis.c/1.1.2.10/Wed Sep 8 17:31:37 2004//Tv19-devel -/pa_win_util.c/1.1.2.7/Mon Sep 15 18:30:26 2003//Tv19-devel -/pa_x86_plain_converters.c/1.1.2.2/Fri Feb 28 01:49:59 2003//Tv19-devel -/pa_x86_plain_converters.h/1.1.2.2/Sat Sep 20 21:09:55 2003//Tv19-devel -D diff --git a/portaudio/pa_win/CVS/Entries.Log b/portaudio/pa_win/CVS/Entries.Log deleted file mode 100644 index 0d56e1d46..000000000 --- a/portaudio/pa_win/CVS/Entries.Log +++ /dev/null @@ -1,2 +0,0 @@ -A D/dev-cpp//// -A D/msvc//// diff --git a/portaudio/pa_win/CVS/Repository b/portaudio/pa_win/CVS/Repository deleted file mode 100644 index 064cf91c2..000000000 --- a/portaudio/pa_win/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win diff --git a/portaudio/pa_win/CVS/Root b/portaudio/pa_win/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win/CVS/Tag b/portaudio/pa_win/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win/dev-cpp/CVS/Entries b/portaudio/pa_win/dev-cpp/CVS/Entries deleted file mode 100644 index 87893783b..000000000 --- a/portaudio/pa_win/dev-cpp/CVS/Entries +++ /dev/null @@ -1,6 +0,0 @@ -/Makefile-dll/1.1.2.1/Wed Sep 17 19:52:09 2003//Tv19-devel -/Makefile-static/1.1.2.1/Wed Sep 17 19:52:09 2003//Tv19-devel -/portaudio-dll.dev/1.1.2.1/Wed Sep 17 19:52:09 2003//Tv19-devel -/portaudio-static.dev/1.1.2.1/Wed Sep 17 19:52:09 2003//Tv19-devel -/readme.txt/1.1.2.1/Wed Sep 17 19:52:09 2003//Tv19-devel -D diff --git a/portaudio/pa_win/dev-cpp/CVS/Repository b/portaudio/pa_win/dev-cpp/CVS/Repository deleted file mode 100644 index ebf18943a..000000000 --- a/portaudio/pa_win/dev-cpp/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win/dev-cpp diff --git a/portaudio/pa_win/dev-cpp/CVS/Root b/portaudio/pa_win/dev-cpp/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win/dev-cpp/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win/dev-cpp/CVS/Tag b/portaudio/pa_win/dev-cpp/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win/dev-cpp/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win/dev-cpp/Makefile-dll b/portaudio/pa_win/dev-cpp/Makefile-dll deleted file mode 100644 index 856af638e..000000000 --- a/portaudio/pa_win/dev-cpp/Makefile-dll +++ /dev/null @@ -1,78 +0,0 @@ -# Project: portaudio-dll -# Makefile created by Dev-C++ 4.9.8.2 - -CPP = g++.exe -CC = gcc.exe -WINDRES = windres.exe -RES = -OBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LINKOBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LIBS = -L"C:/Dev-CPP/lib" -fmessage-length=0 --no-export-all-symbols --add-stdcall-alias ../../../asiosdk2/asiosdk2.a -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm -O3 -s -INCS = -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -CXXINCS = -I"C:/Dev-CPP/include/c++" -I"C:/Dev-CPP/include/c++/mingw32" -I"C:/Dev-CPP/include/c++/backward" -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -BIN = portaudio-dll.dll -CXXFLAGS = $(CXXINCS)-O3 -fmessage-length=0 -Wall -CFLAGS = $(INCS)-DBUILDING_DLL=1 -O3 -fmessage-length=0 -Wall - -.PHONY: all all-before all-after clean clean-custom - -all: all-before portaudio-dll.dll all-after - - -clean: clean-custom - rm -f $(OBJ) $(BIN) - -DLLWRAP=dllwrap.exe -DEFFILE=libportaudio-dll.def -STATICLIB=libportaudio-dll.a - -$(BIN): $(LINKOBJ) - $(DLLWRAP) --output-def $(DEFFILE) --driver-name c++ --implib $(STATICLIB) $(LINKOBJ) $(LIBS) -o $(BIN) - -./pa_skeleton.o: ../../pa_common/pa_skeleton.c - $(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS) - -./pa_stream.o: ../../pa_common/pa_stream.c - $(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS) - -./pa_trace.o: ../../pa_common/pa_trace.c - $(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS) - -./pa_allocation.o: ../../pa_common/pa_allocation.c - $(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS) - -./pa_converters.o: ../../pa_common/pa_converters.c - $(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS) - -./pa_cpuload.o: ../../pa_common/pa_cpuload.c - $(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS) - -./pa_dither.o: ../../pa_common/pa_dither.c - $(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS) - -./pa_front.o: ../../pa_common/pa_front.c - $(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS) - -./pa_process.o: ../../pa_common/pa_process.c - $(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS) - -./pa_asio.o: ../../pa_asio/pa_asio.cpp - $(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -./pa_win_util.o: ../pa_win_util.c - $(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS) - -./pa_win_hostapis.o: ../pa_win_hostapis.c - $(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS) - -./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c - $(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS) - -./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c - $(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS) - -./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c - $(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS) - -./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp - $(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS) diff --git a/portaudio/pa_win/dev-cpp/Makefile-static b/portaudio/pa_win/dev-cpp/Makefile-static deleted file mode 100644 index 2a1647ddc..000000000 --- a/portaudio/pa_win/dev-cpp/Makefile-static +++ /dev/null @@ -1,75 +0,0 @@ -# Project: portaudio-static -# Makefile created by Dev-C++ 4.9.8.2 - -CPP = g++.exe -CC = gcc.exe -WINDRES = windres.exe -RES = -OBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LINKOBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LIBS = -L"C:/Dev-CPP/lib" -fmessage-length=0 -O3 -s -INCS = -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -CXXINCS = -I"C:/Dev-CPP/include/c++" -I"C:/Dev-CPP/include/c++/mingw32" -I"C:/Dev-CPP/include/c++/backward" -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -BIN = portaudio-static.a -CXXFLAGS = $(CXXINCS)-O3 -fmessage-length=0 -Wall -CFLAGS = $(INCS)-O3 -fmessage-length=0 -Wall - -.PHONY: all all-before all-after clean clean-custom - -all: all-before portaudio-static.a all-after - - -clean: clean-custom - rm -f $(OBJ) $(BIN) - -$(BIN): $(LINKOBJ) - ar r $(BIN) $(LINKOBJ) - ranlib $(BIN) - -./pa_skeleton.o: ../../pa_common/pa_skeleton.c - $(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS) - -./pa_stream.o: ../../pa_common/pa_stream.c - $(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS) - -./pa_trace.o: ../../pa_common/pa_trace.c - $(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS) - -./pa_allocation.o: ../../pa_common/pa_allocation.c - $(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS) - -./pa_converters.o: ../../pa_common/pa_converters.c - $(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS) - -./pa_cpuload.o: ../../pa_common/pa_cpuload.c - $(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS) - -./pa_dither.o: ../../pa_common/pa_dither.c - $(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS) - -./pa_front.o: ../../pa_common/pa_front.c - $(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS) - -./pa_process.o: ../../pa_common/pa_process.c - $(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS) - -./pa_asio.o: ../../pa_asio/pa_asio.cpp - $(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -./pa_win_util.o: ../pa_win_util.c - $(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS) - -./pa_win_hostapis.o: ../pa_win_hostapis.c - $(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS) - -./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c - $(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS) - -./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c - $(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS) - -./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c - $(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS) - -./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp - $(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS) diff --git a/portaudio/pa_win/dev-cpp/portaudio-dll.dev b/portaudio/pa_win/dev-cpp/portaudio-dll.dev deleted file mode 100644 index 086e109ee..000000000 --- a/portaudio/pa_win/dev-cpp/portaudio-dll.dev +++ /dev/null @@ -1,209 +0,0 @@ -[Project] -FileName=portaudio-dll.dev -Name=portaudio-dll -UnitCount=16 -Type=3 -Ver=1 -ObjFiles= -Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common -Libs= -PrivateResource= -ResourceIncludes= -MakeIncludes= -Compiler=-DBUILDING_DLL=1_@@_-O3_@@_ -CppCompiler=-O3_@@_ -Linker=--no-export-all-symbols --add-stdcall-alias_@@_../../../asiosdk2/asiosdk2.a_@@_-lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm_@@_-O3 -s_@@_ -IsCpp=1 -Icon= -ExeOutput=. -ObjectOutput=. -OverrideOutput=0 -OverrideOutputName=portaudio.a -HostApplication= -Folders= -CommandLine= -IncludeVersionInfo=0 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000000000000000 -UseCustomMakefile=0 -CustomMakefile= - -[Unit1] -FileName=..\..\pa_common\pa_skeleton.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS) - -[Unit2] -FileName=..\..\pa_common\pa_stream.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS) - -[Unit3] -FileName=..\..\pa_common\pa_trace.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS) - -[Unit4] -FileName=..\..\pa_common\pa_allocation.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS) - -[Unit5] -FileName=..\..\pa_common\pa_converters.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS) - -[Unit6] -FileName=..\..\pa_common\pa_cpuload.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS) - -[Unit7] -FileName=..\..\pa_common\pa_dither.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS) - -[Unit8] -FileName=..\..\pa_common\pa_front.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS) - -[Unit9] -FileName=..\..\pa_common\pa_process.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS) - -[VersionInfo] -Major=0 -Minor=1 -Release=1 -Build=1 -LanguageID=1033 -CharsetID=1252 -CompanyName= -FileVersion= -FileDescription=Developed using the Dev-C++ IDE -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion= -AutoIncBuildNr=0 - -[Unit10] -FileName=..\..\pa_asio\pa_asio.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -[Unit11] -FileName=..\pa_win_util.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS) - -[Unit12] -FileName=..\pa_win_hostapis.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS) - -[Unit13] -FileName=..\..\pa_win_ds\pa_win_ds.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS) - -[Unit14] -FileName=..\..\pa_win_ds\dsound_wrapper.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS) - -[Unit15] -FileName=..\..\pa_win_wmme\pa_win_wmme.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS) - -[Unit16] -FileName=..\..\pa_asio\iasiothiscallresolver.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - diff --git a/portaudio/pa_win/dev-cpp/portaudio-static.dev b/portaudio/pa_win/dev-cpp/portaudio-static.dev deleted file mode 100644 index 2aae584df..000000000 --- a/portaudio/pa_win/dev-cpp/portaudio-static.dev +++ /dev/null @@ -1,209 +0,0 @@ -[Project] -FileName=portaudio-static.dev -Name=portaudio-static -UnitCount=16 -Type=2 -Ver=1 -ObjFiles= -Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common -Libs= -PrivateResource= -ResourceIncludes= -MakeIncludes= -Compiler=-O3_@@_ -CppCompiler=-O3_@@_ -Linker=-O3 -s_@@_ -IsCpp=1 -Icon= -ExeOutput=. -ObjectOutput=. -OverrideOutput=0 -OverrideOutputName=portaudio.a -HostApplication= -Folders= -CommandLine= -IncludeVersionInfo=0 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000000000000000 -UseCustomMakefile=0 -CustomMakefile= - -[Unit1] -FileName=..\..\pa_common\pa_skeleton.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS) - -[Unit2] -FileName=..\..\pa_common\pa_stream.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS) - -[Unit3] -FileName=..\..\pa_common\pa_trace.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS) - -[Unit4] -FileName=..\..\pa_common\pa_allocation.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS) - -[Unit5] -FileName=..\..\pa_common\pa_converters.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS) - -[Unit6] -FileName=..\..\pa_common\pa_cpuload.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS) - -[Unit7] -FileName=..\..\pa_common\pa_dither.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS) - -[Unit8] -FileName=..\..\pa_common\pa_front.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS) - -[Unit9] -FileName=..\..\pa_common\pa_process.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS) - -[VersionInfo] -Major=0 -Minor=1 -Release=1 -Build=1 -LanguageID=1033 -CharsetID=1252 -CompanyName= -FileVersion= -FileDescription=Developed using the Dev-C++ IDE -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion= -AutoIncBuildNr=0 - -[Unit10] -FileName=..\..\pa_asio\pa_asio.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -[Unit11] -FileName=..\..\pa_win\pa_win_util.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS) - -[Unit12] -FileName=..\..\pa_win\pa_win_hostapis.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS) - -[Unit13] -FileName=..\..\pa_win_ds\pa_win_ds.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS) - -[Unit14] -FileName=..\..\pa_win_ds\dsound_wrapper.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS) - -[Unit15] -FileName=..\..\pa_win_wmme\pa_win_wmme.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS) - -[Unit16] -FileName=..\..\pa_asio\iasiothiscallresolver.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - diff --git a/portaudio/pa_win/dev-cpp/readme.txt b/portaudio/pa_win/dev-cpp/readme.txt deleted file mode 100644 index 07108a7dd..000000000 --- a/portaudio/pa_win/dev-cpp/readme.txt +++ /dev/null @@ -1,23 +0,0 @@ -From: "Peter L Jones" -Sent: Wednesday, September 17, 2003 5:18 AM -Subject: Dev-C++ project files - -I attach two project files intended for portaudio/pa_win/dev-cpp (i.e. in -parallel with the msvc directory), if you want them. One is for a static -library build and one for a DLL. I've used the static library (in building -a single monolithic DLL) but I can't guarantee the DLL version will build a -working library (I think it's mostly there, though!). - -I also attach the resulting makefiles, which may be of use to other MinGW -users. - -They're rooted in the directory given above and drop their object and -library files in the same place. They assume the asiosdk2 files are in the -same directory as portaudio/ in a sub-directory called asiosdk2/. Oh! The -DLL is built against a static asiosdk2.a library... maybe not the best way -to do it... I ought to figure out how to link against a "home made" dll in -Dev-C++, I guess ;-) - -Cheers, - --- Peter diff --git a/portaudio/pa_win/msvc/CVS/Entries b/portaudio/pa_win/msvc/CVS/Entries deleted file mode 100644 index a6ddafc01..000000000 --- a/portaudio/pa_win/msvc/CVS/Entries +++ /dev/null @@ -1,7 +0,0 @@ -/Makefile.msvc/1.1.2.3/Wed Apr 16 16:16:47 2003//Tv19-devel -/clean.bat/1.1.2.1/Wed Mar 26 21:49:35 2003//Tv19-devel -/make.bat/1.1.2.1/Tue Apr 8 19:32:22 2003//Tv19-devel -/portaudio.def/1.1.2.3/Sun May 15 11:39:26 2005//Tv19-devel -/readme.txt/1.1.2.3/Wed Apr 16 16:16:47 2003//Tv19-devel -/setenv.bat/1.1.2.1/Wed Mar 26 21:49:35 2003//Tv19-devel -D diff --git a/portaudio/pa_win/msvc/CVS/Repository b/portaudio/pa_win/msvc/CVS/Repository deleted file mode 100644 index 07681ffee..000000000 --- a/portaudio/pa_win/msvc/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win/msvc diff --git a/portaudio/pa_win/msvc/CVS/Root b/portaudio/pa_win/msvc/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win/msvc/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win/msvc/CVS/Tag b/portaudio/pa_win/msvc/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win/msvc/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win/msvc/Makefile.msvc b/portaudio/pa_win/msvc/Makefile.msvc deleted file mode 100644 index 2f1a104e7..000000000 --- a/portaudio/pa_win/msvc/Makefile.msvc +++ /dev/null @@ -1,159 +0,0 @@ -# Portaudio v1.9-devel VC6 DLL makefile 1.0 -# -# David Viens, davidv@plogue.com -# (im far from a VC6 makefile expert, so please bear with me :) -# -# For more info, look at readme.txt -# -#if you keep the ASIODIR as ".", it will use the SDK files that direclty in "pa_win/msvc" dir -ASIODIR=. -ASIOINC=/I ".\host" /I ".\host\pc" /I ".\common" -# - -LIBZ=kernel32.lib user32.lib gdi32.lib wininet.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib winmm.lib dsound.lib dxguid.lib - -CPP=cl.exe -LINK32=link.exe - -#release -CFLAGS=/nologo /MD /W3 /GX /O2 /Ob2 /I "src" /I "Win32" /I "$(MSVCDir)\Include" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /YX /FD -DLL_LINK_FLAGS= /nologo /dll /incremental:no /libpath:"$(MSVCDir)\Lib" $(LIBZ) /pdb:"portaudio.pdb" /implib:".\portaudio.lib" /machine:I386 /out:"portaudio.dll" - - -COMMONINC=/I "..\..\pa_common" /I "." - -#==================================================================== -# Targets - -ALL : portaudio.dll - -CLEAN : - -@erase "*.obj" - -#==================================================================== - - -LINK32_OBJS= \ - ".\pa_allocation.obj" \ - ".\pa_converters.obj" \ - ".\pa_x86_plain_converters.obj" \ -# ".\pa_cppHelp.obj" \ - ".\pa_cpuload.obj" \ - ".\pa_dither.obj" \ - ".\pa_front.obj" \ - ".\pa_process.obj" \ - ".\pa_skeleton.obj" \ - ".\pa_stream.obj" \ - ".\pa_trace.obj" \ -# - ".\pa_win_hostapis.obj" \ - ".\pa_win_util.obj" \ -# - ".\pa_win_wmme.obj" \ -# - ".\pa_win_ds.obj" \ - ".\dsound_wrapper.obj" \ -# - ".\pa_asio.obj" \ -# - ".\asio.obj" \ - ".\ASIOConvertSamples.obj" \ - ".\asiodrivers.obj" \ - ".\asiolist.obj" \ - ".\combase.obj" \ - ".\debugmessage.obj" \ -# ".\dllentry.obj" \ - ".\register.obj" - - -portaudio.dll : $(LINK32_OBJS) ".\portaudio.def" - $(LINK32) $(DLL_LINK_FLAGS) /def:".\portaudio.def" $(LINK32_OBJS) - -#==================================================================== -# asio files (need to agree to steinberg agreement) -# this makefile assumes all files have being copied in the pa_win/msvc dir (for now) -# see readme.txt for details - -".\asio.obj" : ".\common\asio.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asio.obj" /c ".\common\asio.cpp" - -".\ASIOConvertSamples.obj" : ".\host\ASIOConvertSamples.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\ASIOConvertSamples.obj" /c ".\host\ASIOConvertSamples.cpp" - -".\asiodrivers.obj" : ".\host\asiodrivers.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiodrivers.obj" /c ".\host\asiodrivers.cpp" - -".\asiolist.obj" : ".\host\pc\asiolist.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiolist.obj" /c ".\host\pc\asiolist.cpp" - -".\combase.obj" : ".\common\combase.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\combase.obj" /c ".\common\combase.cpp" - -".\debugmessage.obj" : ".\common\debugmessage.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\debugmessage.obj" /c ".\common\debugmessage.cpp" - -".\register.obj" : ".\common\register.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\register.obj" /c ".\common\register.cpp" - -#==================================================================== -# Portaudio Common -# -".\pa_allocation.obj" : "..\..\pa_common\pa_allocation.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_allocation.obj" /c "..\..\pa_common\pa_allocation.c" - -".\pa_converters.obj" : "..\..\pa_common\pa_converters.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_converters.obj" /c "..\..\pa_common\pa_converters.c" - -".\pa_cppHelp.obj" : "..\..\pa_common\pa_cppHelp.cpp" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cppHelp.obj" /c "..\..\pa_common\pa_cppHelp.cpp" - -".\pa_cpuload.obj" : "..\..\pa_common\pa_cpuload.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cpuload.obj" /c "..\..\pa_common\pa_cpuload.c" - -".\pa_dither.obj" : "..\..\pa_common\pa_dither.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_dither.obj" /c "..\..\pa_common\pa_dither.c" - -".\pa_front.obj" : "..\..\pa_common\pa_front.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_front.obj" /c "..\..\pa_common\pa_front.c" - -".\pa_process.obj" : "..\..\pa_common\pa_process.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_process.obj" /c "..\..\pa_common\pa_process.c" - -".\pa_skeleton.obj" : "..\..\pa_common\pa_skeleton.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_skeleton.obj" /c "..\..\pa_common\pa_skeleton.c" - -".\pa_stream.obj" : "..\..\pa_common\pa_stream.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_stream.obj" /c "..\..\pa_common\pa_stream.c" - -".\pa_trace.obj" : "..\..\pa_common\pa_trace.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_trace.obj" /c "..\..\pa_common\pa_trace.c" - -#==================================================================== -# Portaudio implementations -# - -".\pa_win_hostapis.obj" : "..\..\pa_win\pa_win_hostapis.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_hostapis.obj" /c "..\..\pa_win\pa_win_hostapis.c" - -".\pa_win_util.obj" : "..\..\pa_win\pa_win_util.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_util.obj" /c "..\..\pa_win\pa_win_util.c" - -".\pa_x86_plain_converters.obj" : "..\..\pa_win\pa_x86_plain_converters.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_x86_plain_converters.obj" /c "..\..\pa_win\pa_x86_plain_converters.c" - -".\pa_asio.obj" : "..\..\pa_asio\pa_asio.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) $(COMMONINC) /Fo".\pa_asio.obj" /c "..\..\pa_asio\pa_asio.cpp" - -".\pa_win_wmme.obj" : "..\..\pa_win_wmme\pa_win_wmme.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_wmme.obj" /c "..\..\pa_win_wmme\pa_win_wmme.c" - -".\pa_win_ds.obj" : "..\..\pa_win_ds\pa_win_ds.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_ds.obj" /c "..\..\pa_win_ds\pa_win_ds.c" - -".\dsound_wrapper.obj" : "..\..\pa_win_ds\dsound_wrapper.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\dsound_wrapper.obj" /c "..\..\pa_win_ds\dsound_wrapper.c" - - -# End of Makefile -#==================================================================== -# \ No newline at end of file diff --git a/portaudio/pa_win/msvc/clean.bat b/portaudio/pa_win/msvc/clean.bat deleted file mode 100755 index 601c0d314..000000000 --- a/portaudio/pa_win/msvc/clean.bat +++ /dev/null @@ -1,7 +0,0 @@ -del *.obj -del *.dll -del *.lib -del *.exp -del *.pch -del *.idb - diff --git a/portaudio/pa_win/msvc/make.bat b/portaudio/pa_win/msvc/make.bat deleted file mode 100755 index 34d6aed48..000000000 --- a/portaudio/pa_win/msvc/make.bat +++ /dev/null @@ -1,8 +0,0 @@ -CALL C:\progra~1\micros~2\VC98\bin\vcvars32 -del *.dll -del *.lib -nmake Makefile.msvc -del *.obj -del *.exp -del *.pch -del *.idb diff --git a/portaudio/pa_win/msvc/portaudio.def b/portaudio/pa_win/msvc/portaudio.def deleted file mode 100644 index 97a1c3502..000000000 --- a/portaudio/pa_win/msvc/portaudio.def +++ /dev/null @@ -1,43 +0,0 @@ -LIBRARY portaudio.dll -EXPORTS - -; -Pa_GetVersion @1 -Pa_GetVersionText @2 -Pa_GetErrorText @3 -Pa_Initialize @4 -Pa_Terminate @5 -Pa_GetHostApiCount @6 -Pa_GetDefaultHostApi @7 -Pa_GetHostApiInfo @8 -Pa_HostApiTypeIdToHostApiIndex @9 -Pa_HostApiDeviceIndexToDeviceIndex @10 -Pa_GetLastHostErrorInfo @11 -Pa_GetDeviceCount @12 -Pa_GetDefaultInputDevice @13 -Pa_GetDefaultOutputDevice @14 -Pa_GetDeviceInfo @15 -Pa_IsFormatSupported @16 -Pa_OpenStream @17 -Pa_OpenDefaultStream @18 -Pa_CloseStream @19 -Pa_SetStreamFinishedCallback @20 -Pa_StartStream @21 -Pa_StopStream @22 -Pa_AbortStream @23 -Pa_IsStreamStopped @24 -Pa_IsStreamActive @25 -Pa_GetStreamInfo @26 -Pa_GetStreamTime @27 -Pa_GetStreamCpuLoad @28 -Pa_ReadStream @29 -Pa_WriteStream @30 -Pa_GetStreamReadAvailable @31 -Pa_GetStreamWriteAvailable @32 -Pa_GetSampleSize @33 -Pa_Sleep @34 -PaAsio_GetAvailableLatencyValues @50 -PaAsio_ShowControlPanel @51 -PaUtil_InitializeX86PlainConverters @52 -PaAsio_GetInputChannelName @53 -PaAsio_GetOutputChannelName @54 \ No newline at end of file diff --git a/portaudio/pa_win/msvc/readme.txt b/portaudio/pa_win/msvc/readme.txt deleted file mode 100644 index 19ced4c4c..000000000 --- a/portaudio/pa_win/msvc/readme.txt +++ /dev/null @@ -1,56 +0,0 @@ -Hello - - This is a small list of steps in order to build portaudio -(Currently v19-devel) into a VC6 DLL and lib file. -This DLL contains all 3 current win32 PA APIS (MM/DS/ASIO) - -1)Copy the source dirs that comes with the ASIO SDK inside pa_win\msvc - so you should now have: - - pa_win\msvc\common - pa_win\msvc\host - pa_win\msvc\host\sample - pa_win\msvc\host\pc - pa_win\msvc\host\mac (not needed) - - You dont need "driver" - - -2)execure "make.bat", this assumes VC6 is installed in - C:\Program Files\Microsoft Visual Studio\ - - if its not, - - Open a command Prompt and execute "vcvars32.bat" which sets the environment - so that you can use Microsoft's "nmake" - EX: C:\Program Files\Microsoft Visual Studio\VC98\Bin\vcvars32.bat - or (C:\progra~1\micros~2\VC98\bin\vcvars32) dumb de dumb - - You should now have seen a line that said: - "Setting environment for using Microsoft Visual C++ tools." - While in pa_win\msvc , type "nmake makefile.msvc" - this _should_ create portaudio.dll and portaudio.lib - -3)Now in any VC6 project, in which you require portaudio, - you can just link with portaudio.lib, and of course include the - relevant headers - (portaudio.h, and/or pa_asio.h , pa_x86_plain_converters.h) See (*) - -4) Your new exe should now use portaudio.dll. - - -Have fun! - -(*): you may want to add/remove some DLL entry points. -Right now those 3 entries are _not_ from portaudio.h - -(from portaudio.def) -(...) -PaAsio_GetAvailableLatencyValues @50 -PaAsio_ShowControlPanel @51 -PaUtil_InitializeX86PlainConverters @52 - - ------ -last update April 16th 2003 -David Viens, davidv@plogue.com \ No newline at end of file diff --git a/portaudio/pa_win/msvc/setenv.bat b/portaudio/pa_win/msvc/setenv.bat deleted file mode 100755 index f4c815a78..000000000 --- a/portaudio/pa_win/msvc/setenv.bat +++ /dev/null @@ -1 +0,0 @@ -C:\progra~1\micros~2\VC98\bin\vcvars32 diff --git a/portaudio/pa_win/pa_win_hostapis.c b/portaudio/pa_win/pa_win_hostapis.c deleted file mode 100644 index 3db1e18ae..000000000 --- a/portaudio/pa_win/pa_win_hostapis.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 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. - */ - -/** @file - Win32 host API initialization function table. - - @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what - the Unix version does, we should consider being consistent. -*/ - - -#include "pa_hostapi.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -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 ); -PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -PaUtilHostApiInitializer *paHostApiInitializers[] = - { - -#ifndef PA_NO_WMME - PaWinMme_Initialize, -#endif - -#ifndef PA_NO_DS - PaWinDs_Initialize, -#endif - -#ifndef PA_NO_ASIO - PaAsio_Initialize, -#endif - -/* -#ifndef PA_NO_WDMKS - PaWinWdm_Initialize, -#endif -*/ - - PaSkeleton_Initialize, /* just for testing */ - - 0 /* NULL terminated array */ - }; - - -int paDefaultHostApiIndex = 0; - diff --git a/portaudio/pa_win/pa_win_util.c b/portaudio/pa_win/pa_win_util.c deleted file mode 100644 index 0395e5c84..000000000 --- a/portaudio/pa_win/pa_win_util.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 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. - */ - -/** @file - Win32 platform-specific support functions. - - @todo Implement workaround for QueryPerformanceCounter() skipping forward - bug. (see msdn kb Q274323). -*/ - -#include <windows.h> -#include <mmsystem.h> /* 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/portaudio/pa_win/pa_x86_plain_converters.c b/portaudio/pa_win/pa_x86_plain_converters.c deleted file mode 100644 index 98442a8c6..000000000 --- a/portaudio/pa_win/pa_x86_plain_converters.c +++ /dev/null @@ -1,1167 +0,0 @@ -#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<<PA_DITHER_BITS_)-1)) -static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_; -#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1) - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32( - 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; - *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_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/portaudio/pa_win/pa_x86_plain_converters.h b/portaudio/pa_win/pa_x86_plain_converters.h deleted file mode 100644 index f56c710fd..000000000 --- a/portaudio/pa_win/pa_x86_plain_converters.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PA_X86_PLAIN_CONVERTERS_H -#define PA_X86_PLAIN_CONVERTERS_H - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** - @brief 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/portaudio/pa_win_ds/CVS/Entries b/portaudio/pa_win_ds/CVS/Entries deleted file mode 100644 index 57e06c1aa..000000000 --- a/portaudio/pa_win_ds/CVS/Entries +++ /dev/null @@ -1,4 +0,0 @@ -/dsound_wrapper.c/1.1.1.1.2.11/Sun Sep 7 13:04:53 2003//Tv19-devel -/dsound_wrapper.h/1.1.1.1.2.8/Sun Jan 16 20:48:37 2005//Tv19-devel -/pa_win_ds.c/1.1.2.51/Thu Jan 26 01:13:18 2006//Tv19-devel -D diff --git a/portaudio/pa_win_ds/CVS/Repository b/portaudio/pa_win_ds/CVS/Repository deleted file mode 100644 index cf1b05e7f..000000000 --- a/portaudio/pa_win_ds/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win_ds diff --git a/portaudio/pa_win_ds/CVS/Root b/portaudio/pa_win_ds/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win_ds/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win_ds/CVS/Tag b/portaudio/pa_win_ds/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win_ds/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win_ds/dsound_wrapper.c b/portaudio/pa_win_ds/dsound_wrapper.c deleted file mode 100644 index 207d28734..000000000 --- a/portaudio/pa_win_ds/dsound_wrapper.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina 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 <stdio.h> -#include <stdlib.h> -#include <math.h> - -#include "dsound_wrapper.h" -#include "pa_trace.h" - -/* - Rather than linking with dxguid.a or using "#define INITGUID" to force a - header file to instantiate the required GUID(s), we define them directly - below. -*/ -#include <initguid.h> // needed for the DEFINE_GUID macro -DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - - -/************************************************************************************/ -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, WORD 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(); - // - // FIXME: The example code I have on the net creates a hidden window that - // is managed by our code - I think we should do that - one hidden - // window for the whole of Pa_DS - // - 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 = (WORD)(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, WORD 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 = (WORD)(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/portaudio/pa_win_ds/dsound_wrapper.h b/portaudio/pa_win_ds/dsound_wrapper.h deleted file mode 100644 index 9e3f565f2..000000000 --- a/portaudio/pa_win_ds/dsound_wrapper.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef __DSOUND_WRAPPER_H -#define __DSOUND_WRAPPER_H -/* - * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 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 - -/* - We are only using DX3 in here, no need to polute the namespace - davidv -*/ -#define DIRECTSOUND_VERSION 0x0300 - -#include <DSound.h> - -#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, - WORD 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, - WORD 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/portaudio/pa_win_ds/pa_win_ds.c b/portaudio/pa_win_ds/pa_win_ds.c deleted file mode 100644 index ef9709066..000000000 --- a/portaudio/pa_win_ds/pa_win_ds.c +++ /dev/null @@ -1,1864 +0,0 @@ -/* - * $Id: pa_win_ds.c,v 1.1.2.51 2006/01/26 01:13:18 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 paInputOverflow callback status flag - - @todo implement 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 check that CoInitialize() CoUninitialize() are always correctly - paired, even in error cases. - - @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error"). - - @todo make sure all buffers have been played before stopping the stream - when the stream callback returns paComplete - - old TODOs from phil, need to work out if these have been done: - O- fix "patest_stop.c" -*/ - -#include <stdio.h> -#include <string.h> /* strlen() */ - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "dsound_wrapper.h" - -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment( lib, "dsound.lib" ) -#pragma comment( lib, "winmm.lib" ) -#endif - -/* - provided in newer platform sdks and x64 - */ -#ifndef DWORD_PTR -#define DWORD_PTR DWORD -#endif - -#define PRINT(x) PA_DEBUG(x); -#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 */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* 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 CollectGUIDsProc(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; - -} 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 */ - - PaStreamCallbackFlags callbackFlags; - -/* 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; - - -/************************************************************************************ -** Duplicate the input string using the allocations allocator. -** A NULL string is converted to a zero length string. -** If memory cannot be allocated, NULL is returned. -**/ -static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src ) -{ - char *result = 0; - - if( src != NULL ) - { - size_t len = strlen(src); - result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) ); - if( result ) - memcpy( (void *) result, src, len+1 ); - } - else - { - result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 ); - if( result ) - result[0] = '\0'; - } - - return result; -} - -/************************************************************************************ -** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary -** information during device enumeration. -*/ -typedef struct DSDeviceNameAndGUID{ - char *name; // allocated from parent's allocations, never deleted by this structure - GUID guid; - LPGUID lpGUID; -} DSDeviceNameAndGUID; - -typedef struct DSDeviceNameAndGUIDVector{ - PaUtilAllocationGroup *allocations; - PaError enumerationError; - - int count; - int free; - DSDeviceNameAndGUID *items; // Allocated using LocalAlloc() -} DSDeviceNameAndGUIDVector; - -static PaError InitializeDSDeviceNameAndGUIDVector( - DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations ) -{ - PaError result = paNoError; - - guidVector->allocations = allocations; - guidVector->enumerationError = paNoError; - - guidVector->count = 0; - guidVector->free = 8; - guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free ); - if( guidVector->items == NULL ) - result = paInsufficientMemory; - - return result; -} - -static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) -{ - PaError result = paNoError; - DSDeviceNameAndGUID *newItems; - int i; - - /* double size of vector */ - int size = guidVector->count + guidVector->free; - guidVector->free += size; - - newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 ); - if( newItems == NULL ) - { - result = paInsufficientMemory; - } - else - { - for( i=0; i < guidVector->count; ++i ) - { - newItems[i].name = guidVector->items[i].name; - if( guidVector->items[i].lpGUID == NULL ) - { - newItems[i].lpGUID = NULL; - } - else - { - newItems[i].lpGUID = &newItems[i].guid; - memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );; - } - } - - LocalFree( guidVector->items ); - guidVector->items = newItems; - } - - return result; -} - -/* - it's safe to call DSDeviceNameAndGUIDVector multiple times -*/ -static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) -{ - PaError result = paNoError; - - if( guidVector->items != NULL ) - { - if( LocalFree( guidVector->items ) != NULL ) - result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */ - - guidVector->items = NULL; - } - - return result; -} - -/************************************************************************************ -** Collect preliminary device information during DirectSound enumeration -*/ -static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, - LPCTSTR lpszDesc, - LPCTSTR lpszDrvName, - LPVOID lpContext ) -{ - DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext; - PaError error; - - (void) lpszDrvName; /* unused variable */ - - if( namesAndGUIDs->free == 0 ) - { - error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs ); - if( error != paNoError ) - { - namesAndGUIDs->enumerationError = error; - return FALSE; - } - } - - /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */ - if( lpGUID == NULL ) - { - namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL; - } - else - { - namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = - &namesAndGUIDs->items[namesAndGUIDs->count].guid; - - memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) ); - } - - namesAndGUIDs->items[namesAndGUIDs->count].name = - DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc ); - if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL ) - { - namesAndGUIDs->enumerationError = paInsufficientMemory; - return FALSE; - } - - ++namesAndGUIDs->count; - --namesAndGUIDs->free; - - return TRUE; -} - - -/* - GUIDs for emulated devices which we blacklist below. - are there more than two of them?? -*/ - -GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01}; -GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02}; - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ -static double defaultSampleRateSearchOrder_[] = - { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, - 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - - -/************************************************************************************ -** Extract capabilities from an output device, and add it to the device info list -** if successful. This function assumes that there is enough room in the -** device info list to accomodate all entries. -** -** The device will not be added to the device list if any errors are encountered. -*/ -static PaError AddOutputDeviceInfoFromDirectSound( - PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) -{ - PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; - PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; - HRESULT hr; - LPDIRECTSOUND lpDirectSound; - DSCAPS caps; - int deviceOK = TRUE; - PaError result = paNoError; - int i; - - /* Copy GUID to the device info structure. Set pointer. */ - if( lpGUID == NULL ) - { - winDsDeviceInfo->lpGUID = NULL; - } - else - { - memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); - winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; - } - - - if( lpGUID ) - { - if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) || - IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) ) - { - PA_DEBUG(("BLACKLISTED: %s \n",name)); - return paNoError; - } - } - - /* Create a DirectSound object for the specified GUID - Note that using CoCreateInstance doesn't work on windows CE. - */ - hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); - - /** try using CoCreateInstance because DirectSoundCreate was hanging under - some circumstances - note this was probably related to the - #define BOOL short bug which has now been fixed - @todo delete this comment and the following code once we've ensured - there is no bug. - */ - /* - hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectSound, (void**)&lpDirectSound ); - - if( hr == S_OK ) - { - hr = IDirectSound_Initialize( lpDirectSound, lpGUID ); - } - */ - - if( hr != DS_OK ) - { - if (hr == DSERR_ALLOCATED) - PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name)); - DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr )); - if (lpGUID) - DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n", - name, - lpGUID->Data1, - lpGUID->Data2, - lpGUID->Data3, - lpGUID->Data4[0], - lpGUID->Data4[1], - lpGUID->Data4[2], - lpGUID->Data4[3], - lpGUID->Data4[4], - lpGUID->Data4[5], - lpGUID->Data4[6], - lpGUID->Data4[7])); - - deviceOK = FALSE; - } - else - { - /* Query device characteristics. */ - memset( &caps, 0, sizeof(caps) ); - caps.dwSize = sizeof(caps); - hr = IDirectSound_GetCaps( lpDirectSound, &caps ); - if( hr != DS_OK ) - { - DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { - -#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 ) - { - deviceInfo->maxInputChannels = 0; - /* Mono or stereo device? */ - deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; - - deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ - - /* initialize defaultSampleRate */ - - if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) - { - /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */ - deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - - for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) - { - if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate - && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){ - - deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i]; - break; - } - } - } - 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. - */ - deviceInfo->defaultSampleRate = 44100.0f; - - DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name )); - } - else - { - deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - } - } - 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->defaultSampleRate = 44100.0f; - DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name )); - } - else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - - - //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate ); - // dwFlags | DSCAPS_CONTINUOUSRATE - } - } - - IDirectSound_Release( lpDirectSound ); - } - - if( deviceOK ) - { - deviceInfo->name = name; - - if( lpGUID == NULL ) - hostApi->info.defaultOutputDevice = hostApi->info.deviceCount; - - hostApi->info.deviceCount++; - } - - return result; -} - - -/************************************************************************************ -** Extract capabilities from an input device, and add it to the device info list -** if successful. This function assumes that there is enough room in the -** device info list to accomodate all entries. -** -** The device will not be added to the device list if any errors are encountered. -*/ -static PaError AddInputDeviceInfoFromDirectSoundCapture( - PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) -{ - PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; - PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; - HRESULT hr; - LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; - DSCCAPS caps; - int deviceOK = TRUE; - PaError result = paNoError; - - /* Copy GUID to the device info structure. Set pointer. */ - if( lpGUID == NULL ) - { - winDsDeviceInfo->lpGUID = NULL; - } - else - { - winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; - memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); - } - - - hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); - - /** try using CoCreateInstance because DirectSoundCreate was hanging under - some circumstances - note this was probably related to the - #define BOOL short bug which has now been fixed - @todo delete this comment and the following code once we've ensured - there is no bug. - */ - /* - hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture ); - */ - if( hr != DS_OK ) - { - DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { - /* Query device characteristics. */ - memset( &caps, 0, sizeof(caps) ); - caps.dwSize = sizeof(caps); - hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); - if( hr != DS_OK ) - { - DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { -#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 ) - { - deviceInfo->maxInputChannels = caps.dwChannels; - 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 */ - -/* constants from a WINE patch by Francois Gouget, see: - http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html - - --- - Date: Fri, 14 May 2004 10:38:12 +0200 (CEST) - From: Francois Gouget <fgouget@ ... .fr> - To: Ross Bencina <rbencina@ ... .au> - Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library - - [snip] - - I give you permission to use the patch below under the BSD license. - http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html - - [snip] -*/ -#ifndef WAVE_FORMAT_48M08 -#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ -#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */ -#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */ -#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ -#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ -#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ -#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ -#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ -#endif - - /* defaultSampleRate */ - if( caps.dwChannels == 2 ) - { - if( caps.dwFormats & WAVE_FORMAT_4S16 ) - deviceInfo->defaultSampleRate = 44100.0; - else if( caps.dwFormats & WAVE_FORMAT_48S16 ) - deviceInfo->defaultSampleRate = 48000.0; - else if( caps.dwFormats & WAVE_FORMAT_2S16 ) - deviceInfo->defaultSampleRate = 22050.0; - else if( caps.dwFormats & WAVE_FORMAT_1S16 ) - deviceInfo->defaultSampleRate = 11025.0; - else if( caps.dwFormats & WAVE_FORMAT_96S16 ) - deviceInfo->defaultSampleRate = 96000.0; - else - deviceInfo->defaultSampleRate = 0.; - } - else if( caps.dwChannels == 1 ) - { - if( caps.dwFormats & WAVE_FORMAT_4M16 ) - deviceInfo->defaultSampleRate = 44100.0; - else if( caps.dwFormats & WAVE_FORMAT_48M16 ) - deviceInfo->defaultSampleRate = 48000.0; - else if( caps.dwFormats & WAVE_FORMAT_2M16 ) - deviceInfo->defaultSampleRate = 22050.0; - else if( caps.dwFormats & WAVE_FORMAT_1M16 ) - deviceInfo->defaultSampleRate = 11025.0; - else if( caps.dwFormats & WAVE_FORMAT_96M16 ) - deviceInfo->defaultSampleRate = 96000.0; - else - deviceInfo->defaultSampleRate = 0.; - } - else deviceInfo->defaultSampleRate = 0.; - } - } - - IDirectSoundCapture_Release( lpDirectSoundCapture ); - } - - if( deviceOK ) - { - deviceInfo->name = name; - - if( lpGUID == NULL ) - hostApi->info.defaultInputDevice = hostApi->info.deviceCount; - - hostApi->info.deviceCount++; - } - - return result; -} - - -/***********************************************************************************/ -PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, deviceCount; - PaWinDsHostApiRepresentation *winDsHostApi; - DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs; - PaDeviceInfo *deviceInfoArray; - - HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */ - if( FAILED(hr) ){ - return paUnanticipatedHostError; - } - - /* initialise guid vectors so they can be safely deleted on error */ - inputNamesAndGUIDs.items = NULL; - outputNamesAndGUIDs.items = NULL; - - 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; - - -/* DSound - enumerate devices to count them and to gather their GUIDs */ - - - result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations ); - if( result != paNoError ) - goto error; - - result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations ); - if( result != paNoError ) - goto error; - - dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs ); - - dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs ); - - if( inputNamesAndGUIDs.enumerationError != paNoError ) - { - result = inputNamesAndGUIDs.enumerationError; - goto error; - } - - if( outputNamesAndGUIDs.enumerationError != paNoError ) - { - result = outputNamesAndGUIDs.enumerationError; - goto error; - } - - deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count; - - 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; - (*hostApi)->deviceInfos[i] = deviceInfo; - } - - for( i=0; i< inputNamesAndGUIDs.count; ++i ) - { - result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi, - inputNamesAndGUIDs.items[i].name, - inputNamesAndGUIDs.items[i].lpGUID ); - if( result != paNoError ) - goto error; - } - - for( i=0; i< outputNamesAndGUIDs.count; ++i ) - { - result = AddOutputDeviceInfoFromDirectSound( winDsHostApi, - outputNamesAndGUIDs.items[i].name, - outputNamesAndGUIDs.items[i].lpGUID ); - if( result != paNoError ) - goto error; - } - } - - result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); - if( result != paNoError ) - goto error; - - result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); - if( result != paNoError ) - goto error; - - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - 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 ); - } - - TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); - TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); - - return result; -} - - -/***********************************************************************************/ -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ - - if( winDsHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winDsHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); - } - - PaUtil_FreeMemory( winDsHostApi ); - - DSW_TerminateDSoundEntryPoints(); - - CoUninitialize(); -} - - -/* 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: - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary - - - check that the device supports sampleRate - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from 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 - */ - - 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 = (unsigned long)(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 = (unsigned long)(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 ); - } - - 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; - - - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - -/* 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", outputParameters->device)); - */ - - 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), - (WORD)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 ]; - DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", 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), - (WORD)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; - - /** @todo Check for overflow */ - } - - /* How much output room is available? */ - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - UINT previousUnderflowCount = dsw->dsw_OutputUnderflows; - DSW_QueryOutputSpace( dsw, &bytesEmpty ); - framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame; - - /* Check for underflow */ - if( dsw->dsw_OutputUnderflows != previousUnderflowCount ) - stream->callbackFlags |= paOutputUnderflow; - } - - 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, stream->callbackFlags ); - stream->callbackFlags = 0; - - /* 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 ); - } - } - - result = paContinue; - numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result ); - stream->framesWritten += numFrames; - - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - /* FIXME: an underflow could happen here */ - - /* 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 ) - { - /* FIXME: an overflow could happen here */ - - /* 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_PTR dwUser, DWORD dw1, DWORD dw2) -{ - PaWinDsStream *stream; - - /* suppress unused variable warnings */ - (void) uID; - (void) uMsg; - (void) dw1; - (void) dw2; - - 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; - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - 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->callbackFlags = 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_PTR) 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 ) -{ - 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 ) -{ - /* suppress unused variable warnings */ - (void) 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; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -/***********************************************************************************/ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - - diff --git a/portaudio/pa_win_wdmks/CVS/Entries b/portaudio/pa_win_wdmks/CVS/Entries deleted file mode 100644 index a41daa9c1..000000000 --- a/portaudio/pa_win_wdmks/CVS/Entries +++ /dev/null @@ -1,3 +0,0 @@ -/pa_win_wdmks.c/1.1.2.4/Sat Nov 19 10:14:01 2005//Tv19-devel -/readme.txt/1.1.2.2/Wed Nov 16 04:39:53 2005//Tv19-devel -D diff --git a/portaudio/pa_win_wdmks/CVS/Repository b/portaudio/pa_win_wdmks/CVS/Repository deleted file mode 100644 index 3e0293860..000000000 --- a/portaudio/pa_win_wdmks/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win_wdmks diff --git a/portaudio/pa_win_wdmks/CVS/Root b/portaudio/pa_win_wdmks/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win_wdmks/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win_wdmks/CVS/Tag b/portaudio/pa_win_wdmks/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win_wdmks/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win_wdmks/pa_win_wdmks.c b/portaudio/pa_win_wdmks/pa_win_wdmks.c deleted file mode 100644 index 9f83b1b06..000000000 --- a/portaudio/pa_win_wdmks/pa_win_wdmks.c +++ /dev/null @@ -1,3269 +0,0 @@ -/* - * $Id: pa_win_wdmks.c,v 1.1.2.4 2005/11/19 10:14:01 rossbencina Exp $ - * PortAudio Windows WDM-KS interface - * - * Author: Andrew Baldwin - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2004 Andrew Baldwin, Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file - @brief Portaudio WDM-KS host API. - - @note This is the implementation of the Portaudio host API using the - Windows WDM/Kernel Streaming API in order to enable very low latency - playback and recording on all modern Windows platforms (e.g. 2K, XP) - Note: This API accesses the device drivers below the usual KMIXER - component which is normally used to enable multi-client mixing and - format conversion. That means that it will lock out all other users - of a device for the duration of active stream using those devices -*/ - -#include <stdio.h> - -/* Debugging/tracing support */ - -#define PA_LOGE_ -#define PA_LOGL_ - -#ifdef __GNUC__ - #include <initguid.h> - #define _WIN32_WINNT 0x0501 - #define WINVER 0x0501 -#endif - -#include <string.h> /* strlen() */ -#include <assert.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 "portaudio.h" - -#include <windows.h> -#include <winioctl.h> - - -#ifdef __GNUC__ - #undef PA_LOGE_ - #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__)) - #undef PA_LOGL_ - #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__)) - /* These defines are set in order to allow the WIndows DirectX - * headers to compile with a GCC compiler such as MinGW - * NOTE: The headers may generate a few warning in GCC, but - * they should compile */ - #define _INC_MMSYSTEM - #define _INC_MMREG - #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */ - #define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid) - #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n ) - #if !defined( DEFINE_WAVEFORMATEX_GUID ) - #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 - #endif - #define WAVE_FORMAT_ADPCM 0x0002 - #define WAVE_FORMAT_IEEE_FLOAT 0x0003 - #define WAVE_FORMAT_ALAW 0x0006 - #define WAVE_FORMAT_MULAW 0x0007 - #define WAVE_FORMAT_MPEG 0x0050 - #define WAVE_FORMAT_DRM 0x0009 - #define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} - #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data) -#endif - -#ifdef _MSC_VER - #define DYNAMIC_GUID(data) {data} - #define _INC_MMREG - #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */ - #undef DEFINE_GUID - #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data} - #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data) - #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n) - #if !defined( DEFINE_WAVEFORMATEX_GUID ) - #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 - #endif - #define WAVE_FORMAT_ADPCM 0x0002 - #define WAVE_FORMAT_IEEE_FLOAT 0x0003 - #define WAVE_FORMAT_ALAW 0x0006 - #define WAVE_FORMAT_MULAW 0x0007 - #define WAVE_FORMAT_MPEG 0x0050 - #define WAVE_FORMAT_DRM 0x0009 -#endif - -#include <ks.h> -#include <ksmedia.h> -#include <tchar.h> -#include <assert.h> -#include <stdio.h> - -/* These next definitions allow the use of the KSUSER DLL */ -typedef KSDDKAPI DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE); -extern HMODULE DllKsUser; -extern KSCREATEPIN* FunctionKsCreatePin; - -/* Forward definition to break circular type reference between pin and filter */ -struct __PaWinWdmFilter; -typedef struct __PaWinWdmFilter PaWinWdmFilter; - -/* The Pin structure - * A pin is an input or output node, e.g. for audio flow */ -typedef struct __PaWinWdmPin -{ - HANDLE handle; - PaWinWdmFilter* parentFilter; - unsigned long pinId; - KSPIN_CONNECT* pinConnect; - unsigned long pinConnectSize; - KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx; - KSPIN_COMMUNICATION communication; - KSDATARANGE* dataRanges; - KSMULTIPLE_ITEM* dataRangesItem; - KSPIN_DATAFLOW dataFlow; - KSPIN_CINSTANCES instances; - unsigned long frameSize; - int maxChannels; - unsigned long formats; - int bestSampleRate; -} -PaWinWdmPin; - -/* The Filter structure - * A filter has a number of pins and a "friendly name" */ -struct __PaWinWdmFilter -{ - HANDLE handle; - int pinCount; - PaWinWdmPin** pins; - TCHAR filterName[MAX_PATH]; - TCHAR friendlyName[MAX_PATH]; - int maxInputChannels; - int maxOutputChannels; - unsigned long formats; - int usageCount; - int bestSampleRate; -}; - -/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */ -typedef struct __PaWinWdmHostApiRepresentation -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup* allocations; - PaWinWdmFilter** filters; - int filterCount; -} -PaWinWdmHostApiRepresentation; - -typedef struct __PaWinWdmDeviceInfo -{ - PaDeviceInfo inheritedDeviceInfo; - PaWinWdmFilter* filter; -} -PaWinWdmDeviceInfo; - -typedef struct __DATAPACKET -{ - KSSTREAM_HEADER Header; - OVERLAPPED Signal; -} DATAPACKET; - -/* PaWinWdmStream - a stream data structure specifically for this implementation */ -typedef struct __PaWinWdmStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaWinWdmPin* recordingPin; - PaWinWdmPin* playbackPin; - char* hostBuffer; - unsigned long framesPerHostIBuffer; - unsigned long framesPerHostOBuffer; - int bytesPerInputFrame; - int bytesPerOutputFrame; - int streamStarted; - int streamActive; - int streamStop; - int streamAbort; - int oldProcessPriority; - HANDLE streamThread; - HANDLE events[5]; /* 2 play + 2 record packets + abort events */ - DATAPACKET packets[4]; /* 2 play + 2 record */ - PaStreamFlags streamFlags; - /* These values handle the case where the user wants to use fewer - * channels than the device has */ - int userInputChannels; - int deviceInputChannels; - int userOutputChannels; - int deviceOutputChannels; - int inputSampleSize; - int outputSampleSize; -} -PaWinWdmStream; - -#include <setupapi.h> - -HMODULE DllKsUser = NULL; -KSCREATEPIN* FunctionKsCreatePin = NULL; - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/* Low level I/O functions */ -static PaError WdmSyncIoctl(HANDLE handle, - unsigned long ioctlNumber, - void* inBuffer, - unsigned long inBufferCount, - void* outBuffer, - unsigned long outBufferCount, - unsigned long* bytesReturned); -static PaError WdmGetPropertySimple(HANDLE handle, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount, - void* instance, - unsigned long instanceCount); -static PaError WdmSetPropertySimple(HANDLE handle, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount, - void* instance, - unsigned long instanceCount); -static PaError WdmGetPinPropertySimple(HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount); -static PaError WdmGetPinPropertyMulti(HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - KSMULTIPLE_ITEM** ksMultipleItem); - -/** Pin management functions */ -static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error); -static void PinFree(PaWinWdmPin* pin); -static void PinClose(PaWinWdmPin* pin); -static PaError PinInstantiate(PaWinWdmPin* pin); -/*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */ -static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state); -static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format); -static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format); - -/* Filter management functions */ -static PaWinWdmFilter* FilterNew( - TCHAR* filterName, - TCHAR* friendlyName, - PaError* error); -static void FilterFree(PaWinWdmFilter* filter); -static PaWinWdmPin* FilterCreateRenderPin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error); -static PaWinWdmPin* FilterFindViableRenderPin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error); -static PaError FilterCanCreateRenderPin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex); -static PaWinWdmPin* FilterCreateCapturePin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error); -static PaWinWdmPin* FilterFindViableCapturePin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error); -static PaError FilterCanCreateCapturePin( - PaWinWdmFilter* filter, - const WAVEFORMATEX* pwfx); -static PaError FilterUse( - PaWinWdmFilter* filter); -static void FilterRelease( - PaWinWdmFilter* filter); - -/* Interface functions */ -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( - struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( - struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( - PaStream* stream, - void *buffer, - unsigned long frames ); -static PaError WriteStream( - PaStream* stream, - const void *buffer, - unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - -/* Utility functions */ -static unsigned long GetWfexSize(const WAVEFORMATEX* wfex); -static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi); -static BOOL PinWrite(HANDLE h, DATAPACKET* p); -static BOOL PinRead(HANDLE h, DATAPACKET* p); -static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples); -static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples); -static DWORD WINAPI ProcessingThread(LPVOID pParam); - -/* Function bodies */ - -static unsigned long GetWfexSize(const WAVEFORMATEX* wfex) -{ - if( wfex->wFormatTag == WAVE_FORMAT_PCM ) - { - return sizeof( WAVEFORMATEX ); - } - else - { - return (sizeof( WAVEFORMATEX ) + wfex->cbSize); - } -} - -/* -Low level pin/filter access functions -*/ -static PaError WdmSyncIoctl( - HANDLE handle, - unsigned long ioctlNumber, - void* inBuffer, - unsigned long inBufferCount, - void* outBuffer, - unsigned long outBufferCount, - unsigned long* bytesReturned) -{ - PaError result = paNoError; - OVERLAPPED overlapped; - int boolResult; - unsigned long dummyBytesReturned; - unsigned long error; - - if( !bytesReturned ) - { - /* User a dummy as the caller hasn't supplied one */ - bytesReturned = &dummyBytesReturned; - } - - FillMemory((void *)&overlapped,sizeof(overlapped),0); - overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); - if( !overlapped.hEvent ) - { - result = paInsufficientMemory; - goto error; - } - overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1); - - boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount, - outBuffer, outBufferCount, bytesReturned, &overlapped); - if( !boolResult ) - { - error = GetLastError(); - if( error == ERROR_IO_PENDING ) - { - error = WaitForSingleObject(overlapped.hEvent,INFINITE); - if( error != WAIT_OBJECT_0 ) - { - result = paUnanticipatedHostError; - goto error; - } - } - else if((( error == ERROR_INSUFFICIENT_BUFFER ) || - ( error == ERROR_MORE_DATA )) && - ( ioctlNumber == IOCTL_KS_PROPERTY ) && - ( outBufferCount == 0 )) - { - boolResult = TRUE; - } - else - { - result = paUnanticipatedHostError; - } - } - if( !boolResult ) - *bytesReturned = 0; - -error: - if( overlapped.hEvent ) - { - CloseHandle( overlapped.hEvent ); - } - return result; -} - -static PaError WdmGetPropertySimple(HANDLE handle, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount, - void* instance, - unsigned long instanceCount) -{ - PaError result; - KSPROPERTY* ksProperty; - unsigned long propertyCount; - - propertyCount = sizeof(KSPROPERTY) + instanceCount; - ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount ); - if( !ksProperty ) - { - return paInsufficientMemory; - } - - FillMemory((void*)ksProperty,sizeof(ksProperty),0); - ksProperty->Set = *guidPropertySet; - ksProperty->Id = property; - ksProperty->Flags = KSPROPERTY_TYPE_GET; - - if( instance ) - { - memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount ); - } - - result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - ksProperty, - propertyCount, - value, - valueCount, - NULL); - - PaUtil_FreeMemory( ksProperty ); - return result; -} - -static PaError WdmSetPropertySimple( - HANDLE handle, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount, - void* instance, - unsigned long instanceCount) -{ - PaError result; - KSPROPERTY* ksProperty; - unsigned long propertyCount = 0; - - propertyCount = sizeof(KSPROPERTY) + instanceCount; - ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount ); - if( !ksProperty ) - { - return paInsufficientMemory; - } - - ksProperty->Set = *guidPropertySet; - ksProperty->Id = property; - ksProperty->Flags = KSPROPERTY_TYPE_SET; - - if( instance ) - { - memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount); - } - - result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - ksProperty, - propertyCount, - value, - valueCount, - NULL); - - PaUtil_FreeMemory( ksProperty ); - return result; -} - -static PaError WdmGetPinPropertySimple( - HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - void* value, - unsigned long valueCount) -{ - PaError result; - - KSP_PIN ksPProp; - ksPProp.Property.Set = *guidPropertySet; - ksPProp.Property.Id = property; - ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; - ksPProp.PinId = pinId; - ksPProp.Reserved = 0; - - result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp, - sizeof(KSP_PIN), - value, - valueCount, - NULL); - - return result; -} - -static PaError WdmGetPinPropertyMulti( - HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - KSMULTIPLE_ITEM** ksMultipleItem) -{ - PaError result; - unsigned long multipleItemSize = 0; - KSP_PIN ksPProp; - - ksPProp.Property.Set = *guidPropertySet; - ksPProp.Property.Id = property; - ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; - ksPProp.PinId = pinId; - ksPProp.Reserved = 0; - - result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp.Property, - sizeof(KSP_PIN), - NULL, - 0, - &multipleItemSize); - if( result != paNoError ) - { - return result; - } - - *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize ); - if( !*ksMultipleItem ) - { - return paInsufficientMemory; - } - - result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp, - sizeof(KSP_PIN), - (void*)*ksMultipleItem, - multipleItemSize, - NULL); - - if( result != paNoError ) - { - PaUtil_FreeMemory( ksMultipleItem ); - } - - return result; -} - - -/* -Create a new pin object belonging to a filter -The pin object holds all the configuration information about the pin -before it is opened, and then the handle of the pin after is opened -*/ -static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error) -{ - PaWinWdmPin* pin; - PaError result; - unsigned long i; - KSMULTIPLE_ITEM* item = NULL; - KSIDENTIFIER* identifier; - KSDATARANGE* dataRange; - - PA_LOGE_; - PA_DEBUG(("Creating pin %d:\n",pinId)); - - /* Allocate the new PIN object */ - pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) ); - if( !pin ) - { - result = paInsufficientMemory; - goto error; - } - - /* Zero the pin object */ - /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */ - - pin->parentFilter = parentFilter; - pin->pinId = pinId; - - /* Allocate a connect structure */ - pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX); - pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize ); - if( !pin->pinConnect ) - { - result = paInsufficientMemory; - goto error; - } - - /* Configure the connect structure with default values */ - pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard; - pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; - pin->pinConnect->Interface.Flags = 0; - pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard; - pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; - pin->pinConnect->Medium.Flags = 0; - pin->pinConnect->PinId = pinId; - pin->pinConnect->PinToHandle = NULL; - pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; - pin->pinConnect->Priority.PrioritySubClass = 1; - pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1); - pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX); - pin->ksDataFormatWfx->DataFormat.Flags = 0; - pin->ksDataFormatWfx->DataFormat.Reserved = 0; - pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; - pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; - - pin->frameSize = 0; /* Unknown until we instantiate pin */ - - /* Get the COMMUNICATION property */ - result = WdmGetPinPropertySimple( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_COMMUNICATION, - &pin->communication, - sizeof(KSPIN_COMMUNICATION)); - if( result != paNoError ) - goto error; - - if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/ - (pin->communication != KSPIN_COMMUNICATION_SINK) && - (pin->communication != KSPIN_COMMUNICATION_BOTH) ) - { - PA_DEBUG(("Not source/sink\n")); - result = paInvalidDevice; - goto error; - } - - /* Get dataflow information */ - result = WdmGetPinPropertySimple( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_DATAFLOW, - &pin->dataFlow, - sizeof(KSPIN_DATAFLOW)); - - if( result != paNoError ) - goto error; - - /* Get the INTERFACE property list */ - result = WdmGetPinPropertyMulti( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_INTERFACES, - &item); - - if( result != paNoError ) - goto error; - - identifier = (KSIDENTIFIER*)(item+1); - - /* Check that at least one interface is STANDARD_STREAMING */ - result = paUnanticipatedHostError; - for( i = 0; i < item->Count; i++ ) - { - if( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) && - ( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) ) - { - result = paNoError; - break; - } - } - - if( result != paNoError ) - { - PA_DEBUG(("No standard streaming\n")); - goto error; - } - - /* Don't need interfaces any more */ - PaUtil_FreeMemory( item ); - item = NULL; - - /* Get the MEDIUM properties list */ - result = WdmGetPinPropertyMulti( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_MEDIUMS, - &item); - - if( result != paNoError ) - goto error; - - identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */ - - /* Check that at least one medium is STANDARD_DEVIO */ - result = paUnanticipatedHostError; - for( i = 0; i < item->Count; i++ ) - { - if( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) && - ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) ) - { - result = paNoError; - break; - } - } - - if( result != paNoError ) - { - PA_DEBUG(("No standard devio\n")); - goto error; - } - /* Don't need mediums any more */ - PaUtil_FreeMemory( item ); - item = NULL; - - /* Get DATARANGES */ - result = WdmGetPinPropertyMulti( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_DATARANGES, - &pin->dataRangesItem); - - if( result != paNoError ) - goto error; - - pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1); - - /* Check that at least one datarange supports audio */ - result = paUnanticipatedHostError; - dataRange = pin->dataRanges; - pin->maxChannels = 0; - pin->bestSampleRate = 0; - pin->formats = 0; - for( i = 0; i <pin->dataRangesItem->Count; i++) - { - PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat)))); - /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */ - if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) || - !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) || - ( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) && - ( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) ) - { - result = paNoError; - /* Record the maximum possible channels with this pin */ - PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels)); - if( (int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels ) - { - pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels; - /*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/ - } - /* Record the formats (bit depths) that are supported */ - if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 ) - { - pin->formats |= paInt16; - PA_DEBUG(("Format 16 bit supported\n")); - } - if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 ) - { - pin->formats |= paInt24; - PA_DEBUG(("Format 24 bit supported\n")); - } - if( ( pin->bestSampleRate != 48000) && - (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) && - (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) ) - { - pin->bestSampleRate = 48000; - PA_DEBUG(("48kHz supported\n")); - } - else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) && - (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) && - (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) ) - { - pin->bestSampleRate = 44100; - PA_DEBUG(("44.1kHz supported\n")); - } - else - { - pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency; - } - } - dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize); - } - - if( result != paNoError ) - goto error; - - /* Get instance information */ - result = WdmGetPinPropertySimple( - parentFilter->handle, - pinId, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_CINSTANCES, - &pin->instances, - sizeof(KSPIN_CINSTANCES)); - - if( result != paNoError ) - goto error; - - /* Success */ - *error = paNoError; - PA_DEBUG(("Pin created successfully\n")); - PA_LOGL_; - return pin; - -error: - /* - Error cleanup - */ - PaUtil_FreeMemory( item ); - if( pin ) - { - PaUtil_FreeMemory( pin->pinConnect ); - PaUtil_FreeMemory( pin->dataRangesItem ); - PaUtil_FreeMemory( pin ); - } - *error = result; - PA_LOGL_; - return NULL; -} - -/* -Safely free all resources associated with the pin -*/ -static void PinFree(PaWinWdmPin* pin) -{ - PA_LOGE_; - if( pin ) - { - PinClose(pin); - if( pin->pinConnect ) - { - PaUtil_FreeMemory( pin->pinConnect ); - } - if( pin->dataRangesItem ) - { - PaUtil_FreeMemory( pin->dataRangesItem ); - } - PaUtil_FreeMemory( pin ); - } - PA_LOGL_; -} - -/* -If the pin handle is open, close it -*/ -static void PinClose(PaWinWdmPin* pin) -{ - PA_LOGE_; - if( pin == NULL ) - { - PA_DEBUG(("Closing NULL pin!")); - PA_LOGL_; - return; - } - if( pin->handle != NULL ) - { - PinSetState( pin, KSSTATE_PAUSE ); - PinSetState( pin, KSSTATE_STOP ); - CloseHandle( pin->handle ); - pin->handle = NULL; - FilterRelease(pin->parentFilter); - } - PA_LOGL_; -} - -/* -Set the state of this (instantiated) pin -*/ -static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state) -{ - PaError result; - - PA_LOGE_; - if( pin == NULL ) - return paInternalError; - if( pin->handle == NULL ) - return paInternalError; - - result = WdmSetPropertySimple( - pin->handle, - &KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_STATE, - &state, - sizeof(state), - NULL, - 0); - PA_LOGL_; - return result; -} - -static PaError PinInstantiate(PaWinWdmPin* pin) -{ - PaError result; - unsigned long createResult; - KSALLOCATOR_FRAMING ksaf; - KSALLOCATOR_FRAMING_EX ksafex; - - PA_LOGE_; - - if( pin == NULL ) - return paInternalError; - if(!pin->pinConnect) - return paInternalError; - - FilterUse(pin->parentFilter); - - createResult = FunctionKsCreatePin( - pin->parentFilter->handle, - pin->pinConnect, - GENERIC_WRITE | GENERIC_READ, - &pin->handle - ); - - PA_DEBUG(("Pin create result = %x\n",createResult)); - if( createResult != ERROR_SUCCESS ) - { - FilterRelease(pin->parentFilter); - pin->handle = NULL; - return paInvalidDevice; - } - - result = WdmGetPropertySimple( - pin->handle, - &KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_ALLOCATORFRAMING, - &ksaf, - sizeof(ksaf), - NULL, - 0); - - if( result != paNoError ) - { - result = WdmGetPropertySimple( - pin->handle, - &KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, - &ksafex, - sizeof(ksafex), - NULL, - 0); - if( result == paNoError ) - { - pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize; - } - } - else - { - pin->frameSize = ksaf.FrameSize; - } - - PA_LOGL_; - - return paNoError; -} - -/* NOT USED -static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state) -{ - PaError result; - - if( state == NULL ) - return paInternalError; - if( pin == NULL ) - return paInternalError; - if( pin->handle == NULL ) - return paInternalError; - - result = WdmGetPropertySimple( - pin->handle, - KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_STATE, - state, - sizeof(KSSTATE), - NULL, - 0); - - return result; -} -*/ -static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format) -{ - unsigned long size; - void* newConnect; - - PA_LOGE_; - - if( pin == NULL ) - return paInternalError; - if( format == NULL ) - return paInternalError; - - size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX); - - if( pin->pinConnectSize != size ) - { - newConnect = PaUtil_AllocateMemory( size ); - if( newConnect == NULL ) - return paInsufficientMemory; - memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) ); - PaUtil_FreeMemory( pin->pinConnect ); - pin->pinConnect = (KSPIN_CONNECT*)newConnect; - pin->pinConnectSize = size; - pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1); - pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT); - } - - memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) ); - pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8)); - - PA_LOGL_; - - return paNoError; -} - -static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format) -{ - KSDATARANGE_AUDIO* dataRange; - unsigned long count; - GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) ); - PaError result = paInvalidDevice; - - PA_LOGE_; - - if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE ) - { - guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat; - } - dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges; - for(count = 0; count<pin->dataRangesItem->Count; count++) - { - if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) || - ( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) )) - { - /* This is an audio or wildcard datarange... */ - if(( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) || - ( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) )) - { - if(( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) || - ( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) ))) - { - - PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count)); - PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize)); - PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels)); - PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample)); - PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency)); - - if( dataRange->MaximumChannels < format->nChannels ) - { - result = paInvalidChannelCount; - continue; - } - if( dataRange->MinimumBitsPerSample > format->wBitsPerSample ) - { - result = paSampleFormatNotSupported; - continue; - } - if( dataRange->MaximumBitsPerSample < format->wBitsPerSample ) - { - result = paSampleFormatNotSupported; - continue; - } - if( dataRange->MinimumSampleFrequency > format->nSamplesPerSec ) - { - result = paInvalidSampleRate; - continue; - } - if( dataRange->MaximumSampleFrequency < format->nSamplesPerSec ) - { - result = paInvalidSampleRate; - continue; - } - /* Success! */ - PA_LOGL_; - return paNoError; - } - } - } - dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize); - } - - PA_LOGL_; - - return result; -} - -/** - * Create a new filter object - */ -static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error) -{ - PaWinWdmFilter* filter; - PaError result; - int pinId; - int valid; - - - /* Allocate the new filter object */ - filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) ); - if( !filter ) - { - result = paInsufficientMemory; - goto error; - } - - /* Zero the filter object - done by AllocateMemory */ - /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */ - - /* Copy the filter name */ - _tcsncpy(filter->filterName, filterName, MAX_PATH); - - /* Copy the friendly name */ - _tcsncpy(filter->friendlyName, friendlyName, MAX_PATH); - - /* Open the filter handle */ - result = FilterUse(filter); - if( result != paNoError ) - { - goto error; - } - - /* Get pin count */ - result = WdmGetPinPropertySimple - ( - filter->handle, - 0, - &KSPROPSETID_Pin, - KSPROPERTY_PIN_CTYPES, - &filter->pinCount, - sizeof(filter->pinCount) - ); - - if( result != paNoError) - { - goto error; - } - - /* Allocate pointer array to hold the pins */ - filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount ); - if( !filter->pins ) - { - result = paInsufficientMemory; - goto error; - } - - /* Create all the pins we can */ - filter->maxInputChannels = 0; - filter->maxOutputChannels = 0; - filter->bestSampleRate = 0; - - valid = 0; - for(pinId = 0; pinId < filter->pinCount; pinId++) - { - /* Create the pin with this Id */ - PaWinWdmPin* newPin; - newPin = PinNew(filter, pinId, &result); - if( result == paInsufficientMemory ) - goto error; - if( newPin != NULL ) - { - filter->pins[pinId] = newPin; - valid = 1; - - /* Get the max output channel count */ - if(( newPin->dataFlow == KSPIN_DATAFLOW_IN ) && - (( newPin->communication == KSPIN_COMMUNICATION_SINK) || - ( newPin->communication == KSPIN_COMMUNICATION_BOTH))) - { - if(newPin->maxChannels > filter->maxOutputChannels) - filter->maxOutputChannels = newPin->maxChannels; - filter->formats |= newPin->formats; - } - /* Get the max input channel count */ - if(( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) && - (( newPin->communication == KSPIN_COMMUNICATION_SINK) || - ( newPin->communication == KSPIN_COMMUNICATION_BOTH))) - { - if(newPin->maxChannels > filter->maxInputChannels) - filter->maxInputChannels = newPin->maxChannels; - filter->formats |= newPin->formats; - } - - if(newPin->bestSampleRate > filter->bestSampleRate) - { - filter->bestSampleRate = newPin->bestSampleRate; - } - } - } - - if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0)) - { - /* No input or output... not valid */ - valid = 0; - } - - if( !valid ) - { - /* No valid pin was found on this filter so we destroy it */ - result = paDeviceUnavailable; - goto error; - } - - /* Close the filter handle for now - * It will be opened later when needed */ - FilterRelease(filter); - - *error = paNoError; - return filter; - -error: - /* - Error cleanup - */ - if( filter ) - { - for( pinId = 0; pinId < filter->pinCount; pinId++ ) - PinFree(filter->pins[pinId]); - PaUtil_FreeMemory( filter->pins ); - if( filter->handle ) - CloseHandle( filter->handle ); - PaUtil_FreeMemory( filter ); - } - *error = result; - return NULL; -} - -/** - * Free a previously created filter - */ -static void FilterFree(PaWinWdmFilter* filter) -{ - int pinId; - PA_LOGL_; - if( filter ) - { - for( pinId = 0; pinId < filter->pinCount; pinId++ ) - PinFree(filter->pins[pinId]); - PaUtil_FreeMemory( filter->pins ); - if( filter->handle ) - CloseHandle( filter->handle ); - PaUtil_FreeMemory( filter ); - } - PA_LOGE_; -} - -/** - * Reopen the filter handle if necessary so it can be used - **/ -static PaError FilterUse(PaWinWdmFilter* filter) -{ - assert( filter ); - - PA_LOGE_; - if( filter->handle == NULL ) - { - /* Open the filter */ - filter->handle = CreateFile( - filter->filterName, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, - NULL); - - if( filter->handle == NULL ) - { - return paDeviceUnavailable; - } - } - filter->usageCount++; - PA_LOGL_; - return paNoError; -} - -/** - * Release the filter handle if nobody is using it - **/ -static void FilterRelease(PaWinWdmFilter* filter) -{ - assert( filter ); - assert( filter->usageCount > 0 ); - - PA_LOGE_; - filter->usageCount--; - if( filter->usageCount == 0 ) - { - if( filter->handle != NULL ) - { - CloseHandle( filter->handle ); - filter->handle = NULL; - } - } - PA_LOGL_; -} - -/** - * Create a render (playback) Pin using the supplied format - **/ -static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error) -{ - PaError result; - PaWinWdmPin* pin; - - assert( filter ); - - pin = FilterFindViableRenderPin(filter,wfex,&result); - if(!pin) - { - goto error; - } - result = PinSetFormat(pin,wfex); - if( result != paNoError ) - { - goto error; - } - result = PinInstantiate(pin); - if( result != paNoError ) - { - goto error; - } - - *error = paNoError; - return pin; - -error: - *error = result; - return NULL; -} - -/** - * Find a pin that supports the given format - **/ -static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error) -{ - int pinId; - PaWinWdmPin* pin; - PaError result = paDeviceUnavailable; - *error = paNoError; - - assert( filter ); - - for( pinId = 0; pinId<filter->pinCount; pinId++ ) - { - pin = filter->pins[pinId]; - if( pin != NULL ) - { - if(( pin->dataFlow == KSPIN_DATAFLOW_IN ) && - (( pin->communication == KSPIN_COMMUNICATION_SINK) || - ( pin->communication == KSPIN_COMMUNICATION_BOTH))) - { - result = PinIsFormatSupported( pin, wfex ); - if( result == paNoError ) - { - return pin; - } - } - } - } - - *error = result; - return NULL; -} - -/** - * Check if there is a pin that should playback - * with the supplied format - **/ -static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex) -{ - PaWinWdmPin* pin; - PaError result; - - assert ( filter ); - - pin = FilterFindViableRenderPin(filter,wfex,&result); - /* result will be paNoError if pin found - * or else an error code indicating what is wrong with the format - **/ - return result; -} - -/** - * Create a capture (record) Pin using the supplied format - **/ -static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error) -{ - PaError result; - PaWinWdmPin* pin; - - assert( filter ); - - pin = FilterFindViableCapturePin(filter,wfex,&result); - if(!pin) - { - goto error; - } - - result = PinSetFormat(pin,wfex); - if( result != paNoError ) - { - goto error; - } - - result = PinInstantiate(pin); - if( result != paNoError ) - { - goto error; - } - - *error = paNoError; - return pin; - -error: - *error = result; - return NULL; -} - -/** - * Find a capture pin that supports the given format - **/ -static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex, - PaError* error) -{ - int pinId; - PaWinWdmPin* pin; - PaError result = paDeviceUnavailable; - *error = paNoError; - - assert( filter ); - - for( pinId = 0; pinId<filter->pinCount; pinId++ ) - { - pin = filter->pins[pinId]; - if( pin != NULL ) - { - if(( pin->dataFlow == KSPIN_DATAFLOW_OUT ) && - (( pin->communication == KSPIN_COMMUNICATION_SINK) || - ( pin->communication == KSPIN_COMMUNICATION_BOTH))) - { - result = PinIsFormatSupported( pin, wfex ); - if( result == paNoError ) - { - return pin; - } - } - } - } - - *error = result; - return NULL; -} - -/** - * Check if there is a pin that should playback - * with the supplied format - **/ -static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter, - const WAVEFORMATEX* wfex) -{ - PaWinWdmPin* pin; - PaError result; - - assert ( filter ); - - pin = FilterFindViableCapturePin(filter,wfex,&result); - /* result will be paNoError if pin found - * or else an error code indicating what is wrong with the format - **/ - return result; -} - -/** - * Build the list of available filters - */ -static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) -{ - PaError result = paNoError; - HDEVINFO handle = NULL; - int device; - int invalidDevices; - int slot; - SP_DEVICE_INTERFACE_DATA interfaceData; - SP_DEVICE_INTERFACE_DATA aliasData; - SP_DEVINFO_DATA devInfoData; - int noError; - const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR)); - unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))]; - SP_DEVICE_INTERFACE_DETAIL_DATA* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)interfaceDetailsArray; - TCHAR friendlyName[MAX_PATH]; - HKEY hkey; - DWORD sizeFriendlyName; - DWORD type; - PaWinWdmFilter* newFilter; - GUID* category = (GUID*)&KSCATEGORY_AUDIO; - GUID* alias_render = (GUID*)&KSCATEGORY_RENDER; - GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE; - DWORD hasAlias; - - PA_LOGE_; - - devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - /* Open a handle to search for devices (filters) */ - handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if( handle == NULL ) - { - return paUnanticipatedHostError; - } - PA_DEBUG(("Setup called\n")); - - /* First let's count the number of devices so we can allocate a list */ - invalidDevices = 0; - for( device = 0;;device++ ) - { - interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - interfaceData.Reserved = 0; - aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - aliasData.Reserved = 0; - noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData); - PA_DEBUG(("Enum called\n")); - if( !noError ) - break; /* No more devices */ - - /* Check this one has the render or capture alias */ - hasAlias = 0; - noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData); - PA_DEBUG(("noError = %d\n",noError)); - if(noError) - { - if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) - { - PA_DEBUG(("Device %d has render alias\n",device)); - hasAlias |= 1; /* Has render alias */ - } - else - { - PA_DEBUG(("Device %d has no render alias\n",device)); - } - } - noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData); - if(noError) - { - if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) - { - PA_DEBUG(("Device %d has capture alias\n",device)); - hasAlias |= 2; /* Has capture alias */ - } - else - { - PA_DEBUG(("Device %d has no capture alias\n",device)); - } - } - if(!hasAlias) - invalidDevices++; /* This was not a valid capture or render audio device */ - - } - /* Remember how many there are */ - wdmHostApi->filterCount = device-invalidDevices; - - PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices)); - - /* Now allocate the list of pointers to devices */ - wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device ); - if( !wdmHostApi->filters ) - { - if(handle != NULL) - SetupDiDestroyDeviceInfoList(handle); - return paInsufficientMemory; - } - - /* Now create filter objects for each interface found */ - slot = 0; - for( device = 0;;device++ ) - { - interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - interfaceData.Reserved = 0; - aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - aliasData.Reserved = 0; - devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - devInfoData.Reserved = 0; - - noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData); - if( !noError ) - break; /* No more devices */ - - /* Check this one has the render or capture alias */ - hasAlias = 0; - noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData); - if(noError) - { - if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) - { - PA_DEBUG(("Device %d has render alias\n",device)); - hasAlias |= 1; /* Has render alias */ - } - } - noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData); - if(noError) - { - if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) - { - PA_DEBUG(("Device %d has capture alias\n",device)); - hasAlias |= 2; /* Has capture alias */ - } - } - if(!hasAlias) - continue; /* This was not a valid capture or render audio device */ - - noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData); - if( noError ) - { - /* Try to get the "friendly name" for this interface */ - sizeFriendlyName = sizeof(friendlyName); - /* Fix contributed by Ben Allison - * Removed KEY_SET_VALUE from flags on following call - * as its causes failure when running without admin rights - * and it was not required */ - hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE); - if(hkey!=INVALID_HANDLE_VALUE) - { - noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName); - if( noError == ERROR_SUCCESS ) - { - PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName)); - RegCloseKey(hkey); - } - else - { - friendlyName[0] = 0; - } - } - newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result); - if( result == paNoError ) - { - PA_DEBUG(("Filter created\n")); - wdmHostApi->filters[slot] = newFilter; - slot++; - } - else - { - PA_DEBUG(("Filter NOT created\n")); - /* As there are now less filters than we initially thought - * we must reduce the count by one */ - wdmHostApi->filterCount--; - } - } - } - - /* Clean up */ - if(handle != NULL) - SetupDiDestroyDeviceInfoList(handle); - - return paNoError; -} - -PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, deviceCount; - PaWinWdmHostApiRepresentation *wdmHostApi; - PaWinWdmDeviceInfo *deviceInfoArray; - PaWinWdmFilter* pFilter; - PaWinWdmDeviceInfo *wdmDeviceInfo; - PaDeviceInfo *deviceInfo; - - PA_LOGE_; - - /* - Attempt to load the KSUSER.DLL without which we cannot create pins - We will unload this on termination - */ - if(DllKsUser == NULL) - { - DllKsUser = LoadLibrary(TEXT("ksuser.dll")); - if(DllKsUser == NULL) - goto error; - } - - FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin"); - if(FunctionKsCreatePin == NULL) - goto error; - - wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) ); - if( !wdmHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - wdmHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !wdmHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - result = BuildFilterList( wdmHostApi ); - if( result != paNoError ) - { - goto error; - } - deviceCount = wdmHostApi->filterCount; - - *hostApi = &wdmHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paWDMKS; - (*hostApi)->info.name = "Windows WDM-KS"; - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - - if( deviceCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory( - wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < deviceCount; ++i ) - { - wdmDeviceInfo = &deviceInfoArray[i]; - deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo; - pFilter = wdmHostApi->filters[i]; - if( pFilter == NULL ) - continue; - wdmDeviceInfo->filter = pFilter; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - deviceInfo->name = (char*)pFilter->friendlyName; - PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName)); - deviceInfo->maxInputChannels = pFilter->maxInputChannels; - if(deviceInfo->maxInputChannels > 0) - { - /* Set the default input device to the first device we find with - * more than zero input channels - **/ - if((*hostApi)->info.defaultInputDevice == paNoDevice) - { - (*hostApi)->info.defaultInputDevice = i; - } - } - - deviceInfo->maxOutputChannels = pFilter->maxOutputChannels; - if(deviceInfo->maxOutputChannels > 0) - { - /* Set the default output device to the first device we find with - * more than zero output channels - **/ - if((*hostApi)->info.defaultOutputDevice == paNoDevice) - { - (*hostApi)->info.defaultOutputDevice = i; - } - } - - /* These low values are not very useful because - * a) The lowest latency we end up with can depend on many factors such - * as the device buffer sizes/granularities, sample rate, channels and format - * b) We cannot know the device buffer sizes until we try to open/use it at - * a particular setting - * So: we give 512x48000Hz frames as the default low input latency - **/ - deviceInfo->defaultLowInputLatency = (512.0/48000.0); - deviceInfo->defaultLowOutputLatency = (512.0/48000.0); - deviceInfo->defaultHighInputLatency = (4096.0/48000.0); - deviceInfo->defaultHighOutputLatency = (4096.0/48000.0); - deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate); - - (*hostApi)->deviceInfos[i] = deviceInfo; - } - } - - (*hostApi)->info.deviceCount = deviceCount; - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - PA_LOGL_; - return result; - -error: - if( DllKsUser != NULL ) - { - FreeLibrary( DllKsUser ); - DllKsUser = NULL; - } - - if( wdmHostApi ) - { - PaUtil_FreeMemory( wdmHostApi->filters ); - if( wdmHostApi->allocations ) - { - PaUtil_FreeAllAllocations( wdmHostApi->allocations ); - PaUtil_DestroyAllocationGroup( wdmHostApi->allocations ); - } - PaUtil_FreeMemory( wdmHostApi ); - } - PA_LOGL_; - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; - int i; - PA_LOGE_; - - if( wdmHostApi->filters ) - { - for( i=0; i<wdmHostApi->filterCount; i++) - { - if( wdmHostApi->filters[i] != NULL ) - { - FilterFree( wdmHostApi->filters[i] ); - wdmHostApi->filters[i] = NULL; - } - } - } - PaUtil_FreeMemory( wdmHostApi->filters ); - if( wdmHostApi->allocations ) - { - PaUtil_FreeAllAllocations( wdmHostApi->allocations ); - PaUtil_DestroyAllocationGroup( wdmHostApi->allocations ); - } - PaUtil_FreeMemory( wdmHostApi ); - PA_LOGL_; -} - -static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount) -{ - PA_LOGE_; - PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat )); - PA_DEBUG(( "sampleRate = %f\n" , sampleRate )); - PA_DEBUG(( "chanelCount = %d\n", channelCount )); - - pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pwfext->Format.nChannels = channelCount; - pwfext->Format.nSamplesPerSec = (int)sampleRate; - if(channelCount == 1) - pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; - else - pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO; - if(sampleFormat == paFloat32) - { - pwfext->Format.nBlockAlign = channelCount * 4; - pwfext->Format.wBitsPerSample = 32; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 32; - pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else if(sampleFormat == paInt32) - { - pwfext->Format.nBlockAlign = channelCount * 4; - pwfext->Format.wBitsPerSample = 32; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 32; - pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else if(sampleFormat == paInt24) - { - pwfext->Format.nBlockAlign = channelCount * 3; - pwfext->Format.wBitsPerSample = 24; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 24; - pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else if(sampleFormat == paInt16) - { - pwfext->Format.nBlockAlign = channelCount * 2; - pwfext->Format.wBitsPerSample = 16; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 16; - pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign; - - PA_LOGL_; -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; - PaWinWdmFilter* pFilter; - int result = paFormatIsSupported; - WAVEFORMATEXTENSIBLE wfx; - - PA_LOGE_; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* Check that the input format is supported */ - FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount); - - pFilter = wdmHostApi->filters[inputParameters->device]; - result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx); - if( result != paNoError ) - { - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx); - if( result != paNoError ) - return result; - } - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* Check that the output format is supported */ - FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount); - - pFilter = wdmHostApi->filters[outputParameters->device]; - result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx); - if( result != paNoError ) - { - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx); - if( result != paNoError ) - return result; - } - - } - else - { - outputChannelCount = 0; - } - - /* - IMPLEMENT ME: - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary - - - check that the device supports sampleRate - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from inputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - if((inputChannelCount == 0)&&(outputChannelCount == 0)) - result = paSampleFormatNotSupported; /* Not right error */ - - PA_LOGL_; - 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; - PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; - PaWinWdmStream *stream = 0; - /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */ - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - int userInputChannels,userOutputChannels; - int size; - PaWinWdmFilter* pFilter; - WAVEFORMATEXTENSIBLE wfx; - - PA_LOGE_; - PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate)); - PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerBuffer)); - - if( inputParameters ) - { - userInputChannels = 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 stream->userInputChannels */ - if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - } - else - { - userInputChannels = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - if( outputParameters ) - { - userOutputChannels = 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 stream->userInputChannels */ - if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - } - else - { - userOutputChannels = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ - } - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - /* Zero the stream object */ - /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */ - - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &wdmHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &wdmHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - /* Instantiate the input pin if necessary */ - if(userInputChannels > 0) - { - result = paSampleFormatNotSupported; - pFilter = wdmHostApi->filters[inputParameters->device]; - stream->userInputChannels = userInputChannels; - - if(((inputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0) - { /* inputSampleFormat is supported, so try to use it */ - hostInputSampleFormat = inputSampleFormat; - FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels); - stream->bytesPerInputFrame = wfx.Format.nBlockAlign; - stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result); - stream->deviceInputChannels = stream->userInputChannels; - } - - if(result != paNoError) - { /* Search through all PaSampleFormats to find one that works */ - hostInputSampleFormat = paFloat32; - - do { - FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels); - stream->bytesPerInputFrame = wfx.Format.nBlockAlign; - stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result); - stream->deviceInputChannels = stream->userInputChannels; - - if(stream->recordingPin == NULL) result = paSampleFormatNotSupported; - if(result != paNoError) hostInputSampleFormat <<= 1; - } - while(result != paNoError && hostInputSampleFormat <= paUInt8); - } - - if(result != paNoError) - { /* None of the PaSampleFormats worked. Set the hostInputSampleFormat to the best fit - * and try a PCM format. - **/ - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat ); - - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result); - if(stream->recordingPin == NULL) result = paSampleFormatNotSupported; - } - - if( result != paNoError ) - { - /* Some or all KS devices can only handle the exact number of channels - * they specify. But PortAudio clients expect to be able to - * at least specify mono I/O on a multi-channel device - * If this is the case, then we will do the channel mapping internally - **/ - if( stream->userInputChannels < pFilter->maxInputChannels ) - { - FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels); - stream->bytesPerInputFrame = wfx.Format.nBlockAlign; - stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result); - stream->deviceInputChannels = pFilter->maxInputChannels; - - if( result != paNoError ) - { - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result); - } - } - } - - if(stream->recordingPin == NULL) - { - goto error; - } - - switch(hostInputSampleFormat) - { - case paInt16: stream->inputSampleSize = 2; break; - case paInt24: stream->inputSampleSize = 3; break; - case paInt32: - case paFloat32: stream->inputSampleSize = 4; break; - } - - stream->recordingPin->frameSize /= stream->bytesPerInputFrame; - PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize)); - } - else - { - stream->recordingPin = NULL; - stream->bytesPerInputFrame = 0; - } - - /* Instantiate the output pin if necessary */ - if(userOutputChannels > 0) - { - result = paSampleFormatNotSupported; - pFilter = wdmHostApi->filters[outputParameters->device]; - stream->userOutputChannels = userOutputChannels; - - if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0) - { - hostOutputSampleFormat = outputSampleFormat; - FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels); - stream->bytesPerOutputFrame = wfx.Format.nBlockAlign; - stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result); - stream->deviceOutputChannels = stream->userOutputChannels; - } - - if(result != paNoError) - { - hostOutputSampleFormat = paFloat32; - - do { - FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels); - stream->bytesPerOutputFrame = wfx.Format.nBlockAlign; - stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result); - stream->deviceOutputChannels = stream->userOutputChannels; - - if(stream->playbackPin == NULL) result = paSampleFormatNotSupported; - if(result != paNoError) hostOutputSampleFormat <<= 1; - } - while(result != paNoError && hostOutputSampleFormat <= paUInt8); - } - - if(result != paNoError) - { - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( pFilter->formats, outputSampleFormat ); - - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result); - if(stream->playbackPin == NULL) result = paSampleFormatNotSupported; - } - - if( result != paNoError ) - { - /* Some or all KS devices can only handle the exact number of channels - * they specify. But PortAudio clients expect to be able to - * at least specify mono I/O on a multi-channel device - * If this is the case, then we will do the channel mapping internally - **/ - if( stream->userOutputChannels < pFilter->maxOutputChannels ) - { - FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels); - stream->bytesPerOutputFrame = wfx.Format.nBlockAlign; - stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result); - stream->deviceOutputChannels = pFilter->maxOutputChannels; - if( result != paNoError ) - { - /* Try a WAVE_FORMAT_PCM instead */ - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - wfx.Format.cbSize = 0; - wfx.Samples.wValidBitsPerSample = 0; - wfx.dwChannelMask = 0; - wfx.SubFormat = GUID_NULL; - stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result); - } - } - } - - if(stream->playbackPin == NULL) - { - goto error; - } - - switch(hostOutputSampleFormat) - { - case paInt16: stream->outputSampleSize = 2; break; - case paInt24: stream->outputSampleSize = 3; break; - case paInt32: - case paFloat32: stream->outputSampleSize = 4; break; - } - - stream->playbackPin->frameSize /= stream->bytesPerOutputFrame; - PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize)); - } - else - { - stream->playbackPin = NULL; - stream->bytesPerOutputFrame = 0; - } - - /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */ - - /* Record the buffer length */ - if(inputParameters) - { - /* Calculate the frames from the user's value - add a bit to round up */ - stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001); - if(stream->framesPerHostIBuffer > (unsigned long)sampleRate) - { /* Upper limit is 1 second */ - stream->framesPerHostIBuffer = (unsigned long)sampleRate; - } - else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize) - { - stream->framesPerHostIBuffer = stream->recordingPin->frameSize; - } - PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer)); - } - - if(outputParameters) - { - /* Calculate the frames from the user's value - add a bit to round up */ - stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001); - if(stream->framesPerHostOBuffer > (unsigned long)sampleRate) - { /* Upper limit is 1 second */ - stream->framesPerHostOBuffer = (unsigned long)sampleRate; - } - else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize) - { - stream->framesPerHostOBuffer = stream->playbackPin->frameSize; - } - PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer)); - } - - /* Host buffer size is bounded to the largest of the input and output - frame sizes */ - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - stream->userInputChannels, inputSampleFormat, hostInputSampleFormat, - stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - max(stream->framesPerHostOBuffer,stream->framesPerHostIBuffer), - paUtilBoundedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - - stream->streamRepresentation.streamInfo.inputLatency = - ((double)stream->framesPerHostIBuffer) / sampleRate; - stream->streamRepresentation.streamInfo.outputLatency = - ((double)stream->framesPerHostOBuffer) / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame)); - PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame)); - - /* Allocate all the buffers for host I/O */ - size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame); - PA_DEBUG(("Buffer size = %d\n",size)); - stream->hostBuffer = (char*)PaUtil_AllocateMemory(size); - PA_DEBUG(("Buffer allocated\n")); - if( !stream->hostBuffer ) - { - PA_DEBUG(("Cannot allocate host buffer!\n")); - result = paInsufficientMemory; - goto error; - } - PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer)); - /* memset(stream->hostBuffer,0,size); */ - - /* Set up the packets */ - stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL); - ResetEvent(stream->events[0]); /* Record buffer 1 */ - stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL); - ResetEvent(stream->events[1]); /* Record buffer 2 */ - stream->events[2] = CreateEvent(NULL, FALSE, FALSE, NULL); - ResetEvent(stream->events[2]); /* Play buffer 1 */ - stream->events[3] = CreateEvent(NULL, FALSE, FALSE, NULL); - ResetEvent(stream->events[3]); /* Play buffer 2 */ - stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL); - ResetEvent(stream->events[4]); /* Abort event */ - if(stream->userInputChannels > 0) - { - DATAPACKET *p = &(stream->packets[0]); - p->Signal.hEvent = stream->events[0]; - p->Header.Data = stream->hostBuffer; - p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame; - p->Header.DataUsed = 0; - p->Header.Size = sizeof(p->Header); - p->Header.PresentationTime.Numerator = 1; - p->Header.PresentationTime.Denominator = 1; - - p = &(stream->packets[1]); - p->Signal.hEvent = stream->events[1]; - p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame; - p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame; - p->Header.DataUsed = 0; - p->Header.Size = sizeof(p->Header); - p->Header.PresentationTime.Numerator = 1; - p->Header.PresentationTime.Denominator = 1; - } - if(stream->userOutputChannels > 0) - { - DATAPACKET *p = &(stream->packets[2]); - p->Signal.hEvent = stream->events[2]; - p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame; - p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame; - p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame; - p->Header.Size = sizeof(p->Header); - p->Header.PresentationTime.Numerator = 1; - p->Header.PresentationTime.Denominator = 1; - - p = &(stream->packets[3]); - p->Signal.hEvent = stream->events[3]; - p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame; - p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame; - p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame; - p->Header.Size = sizeof(p->Header); - p->Header.PresentationTime.Numerator = 1; - p->Header.PresentationTime.Denominator = 1; - } - - stream->streamStarted = 0; - stream->streamActive = 0; - stream->streamStop = 0; - stream->streamAbort = 0; - stream->streamFlags = streamFlags; - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; - - *s = (PaStream*)stream; - - PA_LOGL_; - return result; - -error: - size = 5; - while(size--) - { - if(stream->events[size] != NULL) - { - CloseHandle(stream->events[size]); - stream->events[size] = NULL; - } - } - if(stream->hostBuffer) - PaUtil_FreeMemory( stream->hostBuffer ); - - if(stream->playbackPin) - PinClose(stream->playbackPin); - if(stream->recordingPin) - PinClose(stream->recordingPin); - - if( stream ) - PaUtil_FreeMemory( stream ); - - PA_LOGL_; - 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; - PaWinWdmStream *stream = (PaWinWdmStream*)s; - int size; - - PA_LOGE_; - - assert(!stream->streamStarted); - assert(!stream->streamActive); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - size = 5; - while(size--) - { - if(stream->events[size] != NULL) - { - CloseHandle(stream->events[size]); - stream->events[size] = NULL; - } - } - if(stream->hostBuffer) - PaUtil_FreeMemory( stream->hostBuffer ); - - if(stream->playbackPin) - PinClose(stream->playbackPin); - if(stream->recordingPin) - PinClose(stream->recordingPin); - - PaUtil_FreeMemory( stream ); - - PA_LOGL_; - return result; -} - -/* -Write the supplied packet to the pin -Asynchronous -Should return false on success -*/ -static BOOL PinWrite(HANDLE h, DATAPACKET* p) -{ - unsigned long cbReturned = 0; - return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0, - &p->Header,p->Header.Size,&cbReturned,&p->Signal); -} - -/* -Read to the supplied packet from the pin -Asynchronous -Should return false on success -*/ -static BOOL PinRead(HANDLE h, DATAPACKET* p) -{ - unsigned long cbReturned = 0; - return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0, - &p->Header,p->Header.Size,&cbReturned,&p->Signal); -} - -/* -Copy the first interleaved channel of 16 bit data to the other channels -*/ -static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples) -{ - unsigned short* data = (unsigned short*)buffer; - int channel; - unsigned short sourceSample; - while( samples-- ) - { - sourceSample = *data++; - channel = channels-1; - while( channel-- ) - { - *data++ = sourceSample; - } - } -} - -/* -Copy the first interleaved channel of 24 bit data to the other channels -*/ -static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples) -{ - unsigned char* data = (unsigned char*)buffer; - int channel; - unsigned char sourceSample[3]; - while( samples-- ) - { - sourceSample[0] = data[0]; - sourceSample[1] = data[1]; - sourceSample[2] = data[2]; - data += 3; - channel = channels-1; - while( channel-- ) - { - data[0] = sourceSample[0]; - data[1] = sourceSample[1]; - data[2] = sourceSample[2]; - data += 3; - } - } -} - -/* -Copy the first interleaved channel of 32 bit data to the other channels -*/ -static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples) -{ - unsigned long* data = (unsigned long*)buffer; - int channel; - unsigned long sourceSample; - while( samples-- ) - { - sourceSample = *data++; - channel = channels-1; - while( channel-- ) - { - *data++ = sourceSample; - } - } -} - -static DWORD WINAPI ProcessingThread(LPVOID pParam) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)pParam; - PaStreamCallbackTimeInfo ti; - int cbResult = paContinue; - int inbuf = 0; - int outbuf = 0; - int pending = 0; - PaError result; - unsigned long wait; - unsigned long eventSignaled; - int fillPlaybuf = 0; - int emptyRecordbuf = 0; - int framesProcessed; - unsigned long timeout; - int i; - int doChannelCopy; - int priming = 0; - PaStreamCallbackFlags underover = 0; - - PA_LOGE_; - - ti.inputBufferAdcTime = 0.0; - ti.currentTime = 0.0; - ti.outputBufferDacTime = 0.0; - - /* Get double buffering going */ - - /* Submit buffers */ - if(stream->playbackPin) - { - result = PinSetState(stream->playbackPin, KSSTATE_RUN); - - PA_DEBUG(("play state run = %d;",(int)result)); - SetEvent(stream->events[outbuf+2]); - outbuf = (outbuf+1)&1; - SetEvent(stream->events[outbuf+2]); - outbuf = (outbuf+1)&1; - pending += 2; - priming += 4; - } - if(stream->recordingPin) - { - result = PinSetState(stream->recordingPin, KSSTATE_RUN); - - PA_DEBUG(("recording state run = %d;",(int)result)); - PinRead(stream->recordingPin->handle,&stream->packets[inbuf]); - inbuf = (inbuf+1)&1; /* Increment and wrap */ - PinRead(stream->recordingPin->handle,&stream->packets[inbuf]); - inbuf = (inbuf+1)&1; /* Increment and wrap */ - /* FIXME - do error checking */ - pending += 2; - } - PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate)); - PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate)); - timeout = max( - ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate), - ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate)); - timeout = max(timeout,1); - PA_DEBUG(("Timeout = %ld\n",timeout)); - - while(!stream->streamAbort) - { - fillPlaybuf = 0; - emptyRecordbuf = 0; - - /* Wait for next input or output buffer to be finished with*/ - assert(pending>0); - - if(stream->streamStop) - { - PA_DEBUG(("ss1:pending=%d ",pending)); - } - wait = WaitForMultipleObjects(5, stream->events, FALSE, 0); - if( wait == WAIT_TIMEOUT ) - { - /* No (under|over)flow has ocurred */ - wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout); - eventSignaled = wait - WAIT_OBJECT_0; - } - else - { - eventSignaled = wait - WAIT_OBJECT_0; - if( eventSignaled < 2 ) - { - underover |= paInputOverflow; - PA_DEBUG(("Input overflow\n")); - } - else if(( eventSignaled < 4 )&&(!priming)) - { - underover |= paOutputUnderflow; - PA_DEBUG(("Output underflow\n")); - } - } - - if(stream->streamStop) - { - PA_DEBUG(("ss2:wait=%ld",wait)); - } - if(wait == WAIT_FAILED) - { - PA_DEBUG(("Wait fail = %ld! ",wait)); - break; - } - if(wait == WAIT_TIMEOUT) - { - continue; - } - - if(eventSignaled < 2) - { /* Recording input buffer has been filled */ - if(stream->playbackPin) - { - /* First check if also the next playback buffer has been signaled */ - wait = WaitForSingleObject(stream->events[outbuf+2],0); - if(wait == WAIT_OBJECT_0) - { - /* Yes, so do both buffers at same time */ - fillPlaybuf = 1; - pending--; - /* Was this an underflow situation? */ - if( underover ) - underover |= paOutputUnderflow; /* Yes! */ - } - } - emptyRecordbuf = 1; - pending--; - } - else if(eventSignaled < 4) - { /* Playback output buffer has been emptied */ - if(stream->recordingPin) - { - /* First check if also the next recording buffer has been signaled */ - wait = WaitForSingleObject(stream->events[inbuf],0); - if(wait == WAIT_OBJECT_0) - { /* Yes, so do both buffers at same time */ - emptyRecordbuf = 1; - pending--; - /* Was this an overflow situation? */ - if( underover ) - underover |= paInputOverflow; /* Yes! */ - } - } - fillPlaybuf = 1; - pending--; - } - else - { - /* Abort event! */ - assert(stream->streamAbort); /* Should have been set */ - PA_DEBUG(("ABORTING ")); - break; - } - ResetEvent(stream->events[eventSignaled]); - - if(stream->streamStop) - { - PA_DEBUG(("Stream stop! pending=%d",pending)); - cbResult = paComplete; /* Stop, but play remaining buffers */ - } - - /* Do necessary buffer processing (which will invoke user callback if necessary */ - doChannelCopy = 0; - if(cbResult==paContinue) - { - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) == - (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) ) - PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover); - underover = 0; /* Reset the (under|over)flow status */ - if(fillPlaybuf) - { - PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0); - if( stream->userOutputChannels == 1 ) - { - /* Write the single user channel to the first interleaved block */ - PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels); - /* We will do a copy to the other channels after the data has been written */ - doChannelCopy = 1; - } - else - { - for(i=0;i<stream->userOutputChannels;i++) - { - /* Only write the user output channels. Leave the rest blank */ - PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels); - } - } - } - if(emptyRecordbuf) - { - PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame); - for(i=0;i<stream->userInputChannels;i++) - { - /* Only read as many channels as the user wants */ - PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels); - } - } - /* Only call the EndBufferProcessing function is the total input frames == total output frames */ - if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) == - (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) ) - framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult); - else framesProcessed = 0; - if( doChannelCopy ) - { - /* Copy the first output channel to the other channels */ - switch(stream->outputSampleSize) - { - case 2: - DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer); - break; - case 3: - DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer); - break; - case 4: - DuplicateFirstChannelInt32(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer); - break; - default: - assert(0); /* Unsupported format! */ - break; - } - } - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - } - else - { - fillPlaybuf = 0; - emptyRecordbuf = 0; - } - - /* - if(cbResult != paContinue) - { - PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending)); - } - */ - /* Submit buffers */ - if((fillPlaybuf)&&(cbResult!=paAbort)) - { - if(!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2])) - outbuf = (outbuf+1)&1; /* Increment and wrap */ - pending++; - if( priming ) - priming--; /* Have to prime twice */ - } - if((emptyRecordbuf)&&(cbResult==paContinue)) - { - stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */ - PinRead(stream->recordingPin->handle,&stream->packets[inbuf]); - inbuf = (inbuf+1)&1; /* Increment and wrap */ - pending++; - } - if(pending==0) - { - PA_DEBUG(("pending==0 finished...;")); - break; - } - if((!stream->playbackPin)&&(cbResult!=paContinue)) - { - PA_DEBUG(("record only cbResult=%d...;",cbResult)); - break; - } - } - - PA_DEBUG(("Finished thread")); - - /* Finished, either normally or aborted */ - if(stream->playbackPin) - { - result = PinSetState(stream->playbackPin, KSSTATE_PAUSE); - result = PinSetState(stream->playbackPin, KSSTATE_STOP); - } - if(stream->recordingPin) - { - result = PinSetState(stream->recordingPin, KSSTATE_PAUSE); - result = PinSetState(stream->recordingPin, KSSTATE_STOP); - } - - stream->streamActive = 0; - - if((!stream->streamStop)&&(!stream->streamAbort)) - { - /* Invoke the user stream finished callback */ - /* Only do it from here if not being stopped/aborted by user */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - stream->streamStop = 0; - stream->streamAbort = 0; - - /* Reset process priority if necessary */ - if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS) - { - SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority); - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; - } - - PA_LOGL_; - ExitThread(0); - return 0; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinWdmStream *stream = (PaWinWdmStream*)s; - DWORD dwID; - BOOL ret; - int size; - - PA_LOGE_; - - stream->streamStop = 0; - stream->streamAbort = 0; - size = 5; - while(size--) - { - if(stream->events[size] != NULL) - { - ResetEvent(stream->events[size]); - } - } - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess()); - /* Uncomment the following line to enable dynamic boosting of the process - * priority to real time for best low latency support - * Disabled by default because RT processes can easily block the OS */ - /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS); - PA_DEBUG(("Class ret = %d;",ret));*/ - - stream->streamStarted = 1; - stream->streamThread = CreateThread(NULL, 0, ProcessingThread, stream, 0, &dwID); - if(stream->streamThread == NULL) - { - stream->streamStarted = 0; - result = paInsufficientMemory; - goto end; - } - ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL); - PA_DEBUG(("Priority ret = %d;",ret)); - /* Make the stream active */ - stream->streamActive = 1; - -end: - PA_LOGL_; - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinWdmStream *stream = (PaWinWdmStream*)s; - int doCb = 0; - - PA_LOGE_; - - if(stream->streamActive) - { - doCb = 1; - stream->streamStop = 1; - while(stream->streamActive) - { - PA_DEBUG(("W.")); - Sleep(10); /* Let thread sleep for 10 msec */ - } - } - - PA_DEBUG(("Terminating thread")); - if(stream->streamStarted && stream->streamThread) - { - TerminateThread(stream->streamThread,0); - stream->streamThread = NULL; - } - - stream->streamStarted = 0; - - if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS) - { - SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority); - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; - } - - if(doCb) - { - /* Do user callback now after all state has been reset */ - /* This means it should be safe for the called function */ - /* to invoke e.g. StartStream */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - PA_LOGL_; - return result; -} - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinWdmStream *stream = (PaWinWdmStream*)s; - int doCb = 0; - - PA_LOGE_; - - if(stream->streamActive) - { - doCb = 1; - stream->streamAbort = 1; - SetEvent(stream->events[4]); /* Signal immediately */ - while(stream->streamActive) - { - Sleep(10); - } - } - - if(stream->streamStarted && stream->streamThread) - { - TerminateThread(stream->streamThread,0); - stream->streamThread = NULL; - } - - stream->streamStarted = 0; - - if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS) - { - SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority); - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; - } - - if(doCb) - { - /* Do user callback now after all state has been reset */ - /* This means it should be safe for the called function */ - /* to invoke e.g. StartStream */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - stream->streamActive = 0; - stream->streamStarted = 0; - - PA_LOGL_; - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - int result = 0; - - PA_LOGE_; - - if(!stream->streamStarted) - result = 1; - - PA_LOGL_; - return result; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - int result = 0; - - PA_LOGE_; - - if(stream->streamActive) - result = 1; - - PA_LOGL_; - return result; -} - - -static PaTime GetStreamTime( PaStream* s ) -{ - PA_LOGE_; - PA_LOGL_; - (void)s; - return PaUtil_GetTime(); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - double result; - PA_LOGE_; - result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); - PA_LOGL_; - return result; -} - - -/* - 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 ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - - PA_LOGE_; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - PA_LOGL_; - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - - PA_LOGE_; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - PA_LOGL_; - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - - PA_LOGE_; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - PA_LOGL_; - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinWdmStream *stream = (PaWinWdmStream*)s; - - PA_LOGE_; - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - PA_LOGL_; - return 0; -} \ No newline at end of file diff --git a/portaudio/pa_win_wdmks/readme.txt b/portaudio/pa_win_wdmks/readme.txt deleted file mode 100644 index 1a381fe79..000000000 --- a/portaudio/pa_win_wdmks/readme.txt +++ /dev/null @@ -1,82 +0,0 @@ -Notes about WDM-KS host API ---------------------------- - -Status history --------------- -10th November 2005: -Made following changes: - * OpenStream: Try all PaSampleFormats internally if the the chosen - format is not supported natively. This fixed several problems - with soundcards that soundcards that did not take kindly to - using 24-bit 3-byte formats. - * OpenStream: Make the minimum framesPerHostIBuffer (and framesPerHostOBuffer) - the default frameSize for the playback/recording pin. - * ProcessingThread: Added a switch to only call PaUtil_EndBufferProcessing - if the total input frames equals the total output frames - -5th September 2004: -This is the first public version of the code. It should be considered -an alpha release with zero guarantee not to crash on any particular -system. So far it has only been tested in the author's development -environment, which means a Win2k/SP2 PIII laptop with integrated -SoundMAX driver and USB Tascam US-428 compiled with both MinGW -(GCC 3.3) and MSVC++6 using the MS DirectX 9 SDK. -It has been most widely tested with the MinGW build, with most of the -test programs (particularly paqa_devs and paqa_errs) passing. -There are some notable failures: patest_out_underflow and both of the -blocking I/O tests (as blocking I/O is not implemented). -At this point the code needs to be tested with a much wider variety -of configurations and feedback provided from testers regarding -both working and failing cases. - -What is the WDM-KS host API? ----------------------------- -PortAudio for Windows currently has 3 functional host implementations. -MME uses the oldest Windows audio API which does not offer good -play/record latency. -DirectX improves this, but still imposes a penalty -of 10s of milliseconds due to the system mixing of streams from -multiple applications. -ASIO offers very good latency, but requires special drivers which are -not always available for cheaper audio hardware. Also, when ASIO -drivers are available, they are not always so robust because they -bypass all of the standardised Windows device driver architecture -and hit the hardware their own way. -Alternatively there are a couple of free (but closed source) ASIO -implementations which connect to the lower level Windows -"Kernel Streaming" API, but again these require special installation -by the user, and can be limited in functionality or difficult to use. - -This is where the PortAudio "WDM-KS" host implementation comes in. -It directly connects PortAudio to the same Kernel Streaming API which -those ASIO bridges use. This avoids the mixing penatly of DirectX, -giving at least as good latency as any ASIO driver, but it has the -advantage of working with ANY Windows audio hardware which is available -through the normal MME/DirectX routes without the user requiring -any additional device drivers to be installed, and allowing all -device selection to be done through the normal PortAudio API. - -Note that in general you should only be using this host API if your -application has a real requirement for very low latency audio (<20ms), -either because you are generating sounds in real-time based upon -user input, or you a processing recorded audio in real time. - -The only thing to be aware of is that using the KS interface will -block that device from being used by the rest of system through -the higher level APIs, or conversely, if the system is using -a device, the KS API will not be able to use it. MS recommend that -you should keep the device open only when your application has focus. -In PortAudio terms, this means having a stream Open on a WDMKS device. - -Usage ------ -To add the WDMKS backend to your program which is already using -PortAudio, you must undefine PA_NO_WDMKS from your build file, -and include the pa_win_wdmks\pa_win_wdmks.c into your build. -The file should compile in both C and C++. -You will need a DirectX SDK installed on your system for the -ks.h and ksmedia.h header files. -You will need to link to the system "setupapi" library. -Note that if you use MinGW, you will get more warnings from -the DX header files when using GCC(C), and still a few warnings -with G++(CPP). \ No newline at end of file diff --git a/portaudio/pa_win_wmme/CVS/Entries b/portaudio/pa_win_wmme/CVS/Entries deleted file mode 100644 index a6d8aaf51..000000000 --- a/portaudio/pa_win_wmme/CVS/Entries +++ /dev/null @@ -1,3 +0,0 @@ -/pa_win_wmme.c/1.6.2.88/Thu Feb 16 01:56:45 2006//Tv19-devel -/pa_win_wmme.h/1.1.2.14/Fri Feb 20 14:16:53 2004//Tv19-devel -D diff --git a/portaudio/pa_win_wmme/CVS/Repository b/portaudio/pa_win_wmme/CVS/Repository deleted file mode 100644 index 3d371154e..000000000 --- a/portaudio/pa_win_wmme/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pa_win_wmme diff --git a/portaudio/pa_win_wmme/CVS/Root b/portaudio/pa_win_wmme/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pa_win_wmme/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pa_win_wmme/CVS/Tag b/portaudio/pa_win_wmme/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pa_win_wmme/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pa_win_wmme/pa_win_wmme.c b/portaudio/pa_win_wmme/pa_win_wmme.c deleted file mode 100644 index 1a9ea59e1..000000000 --- a/portaudio/pa_win_wmme/pa_win_wmme.c +++ /dev/null @@ -1,3634 +0,0 @@ -/* - * $Id: pa_win_wmme.c,v 1.6.2.88 2006/02/16 01:56:45 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 Fix buffer catch up code, can sometimes get stuck (perhaps fixed now, - needs to be reviewed and tested.) - - @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput. - - @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may - be called asynchronously from the callback thread. this is bad. - - @todo implement inputBufferAdcTime in callback thread - - @todo review/fix error recovery and cleanup in marked functions - - @todo implement timeInfo for stream priming - - @todo handle the case where the callback returns paAbort or paComplete during stream priming. - - @todo review input overflow and output underflow handling in ReadStream and WriteStream - -Non-critical stuff for the future: - - @todo Investigate supporting host buffer formats > 16 bits - - @todo define UNICODE and _UNICODE in the project settings and see what breaks - -*/ - -/* - How it works: - - For both callback and blocking read/write streams we open the MME devices - in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever - it has finished with a buffer (either filled it for input, or played it - for output). Where necessary we block waiting for Event objects using - WaitMultipleObjects(). - - When implementing a PA callback stream, we set up a high priority thread - which waits on the MME buffer Events and drains/fills the buffers when - they are ready. - - When implementing a PA blocking read/write stream, we simply wait on these - Events (when necessary) inside the ReadStream() and WriteStream() functions. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include <windows.h> -#include <mmsystem.h> -#include <process.h> -#include <assert.h> -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifndef __MWERKS__ -#include <malloc.h> -#include <memory.h> -#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" - -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment(lib, "winmm.lib") -#endif - -/* - provided in newer platform sdks - */ -#ifndef DWORD_PTR -#define DWORD_PTR DWORD -#endif - -/************************************************* Constants ********/ - -#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */ - -#if PA_MME_USE_HIGH_DEFAULT_LATENCY_ - #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4) - #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4) - #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) - #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */ - #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ -#else - #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2) - #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2) - #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) - #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */ - #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ -#endif - -/* Use higher latency for NT because it is even worse at real-time - operation than Win9x. -*/ -#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2) -#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_) - - -#define PA_MME_MIN_TIMEOUT_MSEC_ (1000) - -static const char constInputMapperSuffix_[] = " - Input"; -static const char constOutputMapperSuffix_[] = " - Output"; - -/********************************************************************/ - -typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */ - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -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, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* 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 ) -{ - char *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 ) - - -/* PaError returning wrappers for some commonly used win32 functions - note that we allow passing a null ptr to have no effect. -*/ - -static PaError CreateEventWithPaError( HANDLE *handle, - LPSECURITY_ATTRIBUTES lpEventAttributes, - BOOL bManualReset, - BOOL bInitialState, - LPCTSTR lpName ) -{ - PaError result = paNoError; - - *handle = NULL; - - *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName ); - if( *handle == NULL ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - return result; -} - - -static PaError ResetEventWithPaError( HANDLE handle ) -{ - PaError result = paNoError; - - if( handle ) - { - if( ResetEvent( handle ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - } - - return result; -} - - -static PaError CloseHandleWithPaError( HANDLE handle ) -{ - PaError result = paNoError; - - if( handle ) - { - if( CloseHandle( handle ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - } - - return result; -} - - -/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - int inputDeviceCount, outputDeviceCount; - - /** winMmeDeviceIds is an array of WinMme device ids. - fields in the range [0, inputDeviceCount) are input device ids, - and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output - device ids. - */ - UINT *winMmeDeviceIds; -} -PaWinMmeHostApiRepresentation; - - -typedef struct -{ - PaDeviceInfo inheritedDeviceInfo; - DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */ -} -PaWinMmeDeviceInfo; - - -/************************************************************************* - * 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 UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device ) -{ - assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount ); - - return hostApi->winMmeDeviceIds[ device ]; -} - - -static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) -{ - MMRESULT mmresult; - - switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) - { - case MMSYSERR_NOERROR: - return paNoError; - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - return paDeviceUnavailable; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - return paDeviceUnavailable; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - return paInsufficientMemory; - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - return paSampleFormatNotSupported; - - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - default: - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } -} - - -static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) -{ - MMRESULT mmresult; - - switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) - { - case MMSYSERR_NOERROR: - return paNoError; - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - return paDeviceUnavailable; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - return paDeviceUnavailable; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - return paInsufficientMemory; - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - return paSampleFormatNotSupported; - - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - default: - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - return paUnanticipatedHostError; - } -} - - -static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo, - PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), - int winMmeDeviceId, int channels, double sampleRate ) -{ - PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo; - WAVEFORMATEX waveFormatEx; - - if( sampleRate == 11025.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){ - - return paNoError; - } - - if( sampleRate == 22050.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){ - - return paNoError; - } - - if( sampleRate == 44100.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){ - - return paNoError; - } - - waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; - waveFormatEx.nChannels = (WORD)channels; - waveFormatEx.nSamplesPerSec = (DWORD)sampleRate; - waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short); - waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short)); - waveFormatEx.wBitsPerSample = 16; - waveFormatEx.cbSize = 0; - - return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx ); -} - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ -static double defaultSampleRateSearchOrder_[] = - { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, - 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - -static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId, - PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels ) -{ - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - int i; - - deviceInfo->defaultSampleRate = 0.; - - for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) - { - double sampleRate = defaultSampleRateSearchOrder_[ i ]; - PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate ); - if( paerror == paNoError ) - { - deviceInfo->defaultSampleRate = sampleRate; - break; - } - } -} - - -static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEINCAPS wic; - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - - *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; - } - - winMmeDeviceInfo->dwFormats = wic.dwFormats; - - DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId, - QueryInputWaveFormatEx, deviceInfo->maxInputChannels ); - - *success = 1; - -error: - return result; -} - - -static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEOUTCAPS woc; - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - - *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; - } - - winMmeDeviceInfo->dwFormats = woc.dwFormats; - - DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId, - QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels ); - - *success = 1; - -error: - return result; -} - - -static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency ) -{ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof( osvi ); - GetVersionEx( &osvi ); - - /* Check for NT */ - if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) - { - *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_; - } - else if(osvi.dwMajorVersion >= 5) - { - *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_; - } - else - { - *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_; - } - - *defaultHighLatency = *defaultLowLatency * 2; -} - - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i; - PaWinMmeHostApiRepresentation *winMmeHostApi; - int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount; - PaWinMmeDeviceInfo *deviceInfoArray; - int deviceInfoInitializationSucceeded; - PaTime defaultLowLatency, defaultHighLatency; - - 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->inputDeviceCount = 0; - winMmeHostApi->outputDeviceCount = 0; - - - maximumPossibleDeviceCount = 0; - - inputDeviceCount = waveInGetNumDevs(); - if( inputDeviceCount > 0 ) - maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ - - outputDeviceCount = waveOutGetNumDevs(); - if( outputDeviceCount > 0 ) - maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ - - - if( maximumPossibleDeviceCount > 0 ){ - - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount ); - if( !winMmeHostApi->winMmeDeviceIds ) - { - result = paInsufficientMemory; - goto error; - } - - GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency ); - - if( inputDeviceCount > 0 ){ - /* -1 is the WAVE_MAPPER */ - for( i = -1; i < inputDeviceCount; ++i ){ - UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); - PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, - winMmeDeviceId, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultInputDevice == paNoDevice ) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->inputDeviceCount++; - (*hostApi)->info.deviceCount++; - } - } - } - - if( outputDeviceCount > 0 ){ - /* -1 is the WAVE_MAPPER */ - for( i = -1; i < outputDeviceCount; ++i ){ - UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); - PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, - winMmeDeviceId, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->outputDeviceCount++; - (*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_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - 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 ) -{ - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo; - int inputChannelCount, outputChannelCount; - int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; - UINT winMmeInputDeviceId, winMmeOutputDeviceId; - unsigned int i; - PaError paerror; - - /* The calls to QueryFormatSupported below are intended to detect invalid - sample rates. If we assume that the channel count and format are OK, - then the only thing that could fail is the sample rate. This isn't - strictly true, but I can't think of a better way to test that the - sample rate is valid. - */ - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - inputStreamInfo = inputParameters->hostApiSpecificStreamInfo; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification - && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - { - inputMultipleDeviceChannelCount = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount; - - inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ]; - - /* check that input device can support inputChannelCount */ - if( inputStreamInfo->devices[i].channelCount <= 0 - || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - - if( inputMultipleDeviceChannelCount != inputChannelCount ) - return paIncompatibleHostApiSpecificStreamInfo; - } - else - { - if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */ - - inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ]; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > inputDeviceInfo->maxInputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - outputStreamInfo = outputParameters->hostApiSpecificStreamInfo; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification - && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - { - outputMultipleDeviceChannelCount = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount; - - outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ]; - - /* check that output device can support outputChannelCount */ - if( outputStreamInfo->devices[i].channelCount <= 0 - || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - - if( outputMultipleDeviceChannelCount != outputChannelCount ) - return paIncompatibleHostApiSpecificStreamInfo; - } - else - { - if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */ - - outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ]; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > outputDeviceInfo->maxOutputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - } - - /* - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - for mme all we can do is test that the input and output devices - support the requested sample rate and number of channels. we - cannot test for full duplex compatibility. - */ - - return paFormatIsSupported; -} - - - -static void SelectBufferSizeAndCount( unsigned long baseBufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long maximumBufferSize, unsigned long *hostBufferSize, - unsigned long *hostBufferCount ) -{ - unsigned long sizeMultiplier, bufferCount, latency; - unsigned long nextLatency, nextBufferSize; - int baseBufferSizeIsPowerOfTwo; - - sizeMultiplier = 1; - bufferCount = baseBufferCount; - - /* count-1 below because latency is always determined by one less - than the total number of buffers. - */ - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - - if( latency > requestedLatency ) - { - - /* reduce number of buffers without falling below suggested latency */ - - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - } - - }else if( latency < requestedLatency ){ - - baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1))); - if( baseBufferSizeIsPowerOfTwo ){ - - /* double size of buffers without exceeding requestedLatency */ - - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - sizeMultiplier *= 2; - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - } - - }else{ - - /* increase size of buffers upto first excess of requestedLatency */ - - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - ++sizeMultiplier; - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - } - - if( nextLatency < requestedLatency ) - ++sizeMultiplier; - } - - /* increase number of buffers until requestedLatency is reached */ - - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - } - } - - *hostBufferSize = baseBufferSize * sizeMultiplier; - *hostBufferCount = bufferCount; -} - - -static void ReselectBufferCount( unsigned long bufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long *hostBufferCount ) -{ - unsigned long bufferCount, latency; - unsigned long nextLatency; - - bufferCount = baseBufferCount; - - /* count-1 below because latency is always determined by one less - than the total number of buffers. - */ - latency = bufferSize * (bufferCount-1); - - if( latency > requestedLatency ) - { - /* reduce number of buffers without falling below suggested latency */ - - nextLatency = bufferSize * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = bufferSize * (bufferCount-2); - } - - }else if( latency < requestedLatency ){ - - /* increase number of buffers until requestedLatency is reached */ - - latency = bufferSize * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = bufferSize * (bufferCount-1); - } - } - - *hostBufferCount = bufferCount; -} - - -/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount, - framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values - of the other parameters. -*/ - -static PaError CalculateBufferSettings( - unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount, - unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount, - int inputChannelCount, PaSampleFormat hostInputSampleFormat, - PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, - int outputChannelCount, PaSampleFormat hostOutputSampleFormat, - PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, - double sampleRate, unsigned long framesPerBuffer ) -{ - PaError result = paNoError; - int effectiveInputChannelCount, effectiveOutputChannelCount; - int hostInputFrameSize = 0; - unsigned int i; - - if( inputChannelCount > 0 ) - { - int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat ); - if( hostInputSampleSize < 0 ) - { - result = hostInputSampleSize; - goto error; - } - - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveInputChannelCount to the largest number of - channels on any one device. - */ - effectiveInputChannelCount = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount ) - effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveInputChannelCount = inputChannelCount; - } - - hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount; - - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - if( inputStreamInfo->bufferCount <= 0 - || inputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; - *hostInputBufferCount = inputStreamInfo->bufferCount; - } - else - { - unsigned long hostBufferSizeBytes, hostBufferCount; - unsigned long minimumBufferCount = (outputChannelCount > 0) - ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ - : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; - - unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize; - *hostInputBufferCount = hostBufferCount; - } - } - else - { - *framesPerHostInputBuffer = 0; - *hostInputBufferCount = 0; - } - - if( outputChannelCount > 0 ) - { - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - if( outputStreamInfo->bufferCount <= 0 - || outputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; - *hostOutputBufferCount = outputStreamInfo->bufferCount; - - - if( inputChannelCount > 0 ) /* full duplex */ - { - if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer ) - { - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - /* a custom StreamInfo was used for specifying both input - and output buffer sizes, the larger buffer size - must be a multiple of the smaller buffer size */ - - if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer ) - { - if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - } - else - { - assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer ); - if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - } - } - else - { - /* a custom StreamInfo was not used for specifying the input buffer size, - so use the output buffer size, and approximately the same latency. */ - - *framesPerHostInputBuffer = *framesPerHostOutputBuffer; - *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1; - - if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ) - *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; - } - } - } - } - else - { - unsigned long hostBufferSizeBytes, hostBufferCount; - unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - unsigned long maximumBufferSize; - int hostOutputFrameSize; - int hostOutputSampleSize; - - hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); - if( hostOutputSampleSize < 0 ) - { - result = hostOutputSampleSize; - goto error; - } - - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveOutputChannelCount to the largest number of - channels on any one device. - */ - effectiveOutputChannelCount = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount ) - effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveOutputChannelCount = outputChannelCount; - } - - hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount; - - maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; - - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize; - *hostOutputBufferCount = hostBufferCount; - - - if( inputChannelCount > 0 ) - { - /* ensure that both input and output buffer sizes are the same. - if they don't match at this stage, choose the smallest one - and use that for input and output - */ - - if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer ) - { - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) - { - unsigned long framesPerHostBuffer = *framesPerHostInputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - ReselectBufferCount( - framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *framesPerHostOutputBuffer = framesPerHostBuffer; - *hostOutputBufferCount = hostBufferCount; - } - else - { - unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; - ReselectBufferCount( - framesPerHostBuffer * hostInputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *framesPerHostInputBuffer = framesPerHostBuffer; - *hostInputBufferCount = hostBufferCount; - } - } - } - } - } - else - { - *framesPerHostOutputBuffer = 0; - *hostOutputBufferCount = 0; - } - -error: - return result; -} - - -typedef struct -{ - HANDLE bufferEvent; - void *waveHandles; - unsigned int deviceCount; - /* unsigned int channelCount; */ - WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */ - unsigned int bufferCount; - unsigned int currentBufferIndex; - unsigned int framesPerBuffer; - unsigned int framesUsedInCurrentBuffer; -}PaWinMmeSingleDirectionHandlesAndBuffers; - -/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */ - -static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ); -static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long bytesPerHostSample, - double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ); -static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ); -static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long hostBufferCount, - PaSampleFormat hostSampleFormat, - unsigned long framesPerHostBuffer, - PaWinMmeDeviceAndChannelCount *devices, - int isInput ); -static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ); - - -static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - handlesAndBuffers->bufferEvent = 0; - handlesAndBuffers->waveHandles = 0; - handlesAndBuffers->deviceCount = 0; - handlesAndBuffers->waveHeaders = 0; - handlesAndBuffers->bufferCount = 0; -} - -static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long bytesPerHostSample, - double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ) -{ - PaError result; - MMRESULT mmresult; - unsigned long bytesPerFrame; - WAVEFORMATEX wfx; - signed int i; - - /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() - has already been called to zero some fields */ - - result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL ); - if( result != paNoError ) goto error; - - if( isInput ) - handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount ); - else - handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount ); - if( !handlesAndBuffers->waveHandles ) - { - result = paInsufficientMemory; - goto error; - } - - handlesAndBuffers->deviceCount = deviceCount; - - for( i = 0; i < (signed int)deviceCount; ++i ) - { - if( isInput ) - ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0; - else - ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0; - } - - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nSamplesPerSec = (DWORD) sampleRate; - wfx.cbSize = 0; - - for( i = 0; i < (signed int)deviceCount; ++i ) - { - UINT winMmeDeviceId; - - winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device ); - wfx.nChannels = (WORD)devices[i].channelCount; - - bytesPerFrame = wfx.nChannels * bytesPerHostSample; - - wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate); - wfx.nBlockAlign = (WORD)bytesPerFrame; - wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8); - - /* REVIEW: consider not firing an event for input when a full duplex - stream is being used. this would probably depend on the - neverDropInput flag. */ - - if( isInput ) - mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, - (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT ); - else - mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, - (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT ); - - if( mmresult != MMSYSERR_NOERROR ) - { - switch( mmresult ) - { - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - result = paDeviceUnavailable; - 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 MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - /* falls through */ - default: - result = paUnanticipatedHostError; - if( isInput ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - else - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - goto error; - } - } - - return result; - -error: - TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ ); - - return result; -} - - -static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ) -{ - PaError result = paNoError; - MMRESULT mmresult; - signed int i; - - if( handlesAndBuffers->waveHandles ) - { - for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i ) - { - if( isInput ) - { - if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ) - mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ); - else - mmresult = MMSYSERR_NOERROR; - } - else - { - if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ) - mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ); - else - mmresult = MMSYSERR_NOERROR; - } - - if( mmresult != MMSYSERR_NOERROR && - !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */ - { - result = paUnanticipatedHostError; - if( isInput ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - else - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - /* note that we don't break here, we try to continue closing devices */ - } - } - - PaUtil_FreeMemory( handlesAndBuffers->waveHandles ); - handlesAndBuffers->waveHandles = 0; - } - - if( handlesAndBuffers->bufferEvent ) - { - result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent ); - handlesAndBuffers->bufferEvent = 0; - } - - return result; -} - - -static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long hostBufferCount, - PaSampleFormat hostSampleFormat, - unsigned long framesPerHostBuffer, - PaWinMmeDeviceAndChannelCount *devices, - int isInput ) -{ - PaError result = paNoError; - MMRESULT mmresult; - WAVEHDR *deviceWaveHeaders; - signed int i, j; - - /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() - has already been called to zero some fields */ - - - /* allocate an array of pointers to arrays of wave headers, one array of - wave headers per device */ - handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount ); - if( !handlesAndBuffers->waveHeaders ) - { - result = paInsufficientMemory; - goto error; - } - - for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) - handlesAndBuffers->waveHeaders[i] = 0; - - handlesAndBuffers->bufferCount = hostBufferCount; - - for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) - { - int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) * - framesPerHostBuffer * devices[i].channelCount; - if( bufferBytes < 0 ) - { - result = paInternalError; - goto error; - } - - /* Allocate an array of wave headers for device i */ - deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount ); - if( !deviceWaveHeaders ) - { - result = paInsufficientMemory; - goto error; - } - - for( j=0; j < (signed int)hostBufferCount; ++j ) - deviceWaveHeaders[j].lpData = 0; - - handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders; - - /* Allocate a buffer for each wave header */ - for( j=0; j < (signed int)hostBufferCount; ++j ) - { - deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes ); - if( !deviceWaveHeaders[j].lpData ) - { - result = paInsufficientMemory; - goto error; - } - deviceWaveHeaders[j].dwBufferLength = bufferBytes; - deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */ - - if( isInput ) - { - mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - else /* output */ - { - mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - deviceWaveHeaders[j].dwUser = devices[i].channelCount; - } - } - - return result; - -error: - TerminateWaveHeaders( handlesAndBuffers, isInput ); - - return result; -} - - -static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ) -{ - signed int i, j; - WAVEHDR *deviceWaveHeaders; - - if( handlesAndBuffers->waveHeaders ) - { - for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i ) - { - deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */ - if( deviceWaveHeaders ) - { - for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j ) - { - if( deviceWaveHeaders[j].lpData ) - { - if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF ) - { - if( isInput ) - waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - else - waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - } - - PaUtil_FreeMemory( deviceWaveHeaders[j].lpData ); - } - } - - PaUtil_FreeMemory( deviceWaveHeaders ); - } - } - - PaUtil_FreeMemory( handlesAndBuffers->waveHeaders ); - handlesAndBuffers->waveHeaders = 0; - } -} - - - -/* PaWinMmeStream - a stream data structure specifically for this implementation */ -/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */ -struct PaWinMmeStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - int primeStreamUsingCallback; - - PaWinMmeSingleDirectionHandlesAndBuffers input; - PaWinMmeSingleDirectionHandlesAndBuffers output; - - /* Processing thread management -------------- */ - HANDLE abortEvent; - HANDLE processingThread; - DWORD processingThreadId; - - char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */ - int processingThreadPriority; - int highThreadPriority; - int throttledThreadPriority; - unsigned long throttledSleepMsecs; - - int isStopped; - volatile int isActive; - volatile int stopProcessing; /* stop thread once existing buffers have been returned */ - volatile int abortProcessing; /* stop thread immediately */ - - DWORD allBuffersDurationMs; /* used to calculate timeouts */ -}; - -/* updates deviceCount if PaWinMmeUseMultipleDevices is used */ - -static PaError ValidateWinMmeSpecificStreamInfo( - const PaStreamParameters *streamParameters, - const PaWinMmeStreamInfo *streamInfo, - char *throttleProcessingThreadOnOverload, - unsigned long *deviceCount ) -{ - if( streamInfo ) - { - if( streamInfo->size != sizeof( PaWinMmeStreamInfo ) - || streamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread ) - *throttleProcessingThreadOnOverload = 0; - - if( streamInfo->flags & paWinMmeUseMultipleDevices ) - { - if( streamParameters->device != paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - *deviceCount = streamInfo->deviceCount; - } - } - - return paNoError; -} - -static PaError RetrieveDevicesFromStreamParameters( - struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *streamParameters, - const PaWinMmeStreamInfo *streamInfo, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - PaError result = paNoError; - unsigned int i; - int totalChannelCount; - PaDeviceIndex hostApiDevice; - - if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices ) - { - totalChannelCount = 0; - for( i=0; i < deviceCount; ++i ) - { - /* validate that the device number is within range */ - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - streamInfo->devices[i].device, hostApi ); - if( result != paNoError ) - return result; - - devices[i].device = hostApiDevice; - devices[i].channelCount = streamInfo->devices[i].channelCount; - - totalChannelCount += devices[i].channelCount; - } - - if( totalChannelCount != streamParameters->channelCount ) - { - /* channelCount must match total channels specified by multiple devices */ - return paInvalidChannelCount; /* REVIEW use of this error code */ - } - } - else - { - devices[0].device = streamParameters->device; - devices[0].channelCount = streamParameters->channelCount; - } - - return result; -} - -static PaError ValidateInputChannelCounts( - struct PaUtilHostApiRepresentation *hostApi, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels ) - return paInvalidChannelCount; - } - - return paNoError; -} - -static PaError ValidateOutputChannelCounts( - struct PaUtilHostApiRepresentation *hostApi, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels ) - return paInvalidChannelCount; - } - - return paNoError; -} - - -/* the following macros are intended to improve the readability of the following code */ -#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles ) -#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles ) -#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles ) -#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) ) - -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; - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - PaWinMmeStream *stream = 0; - int bufferProcessorIsInitialized = 0; - int streamRepresentationIsInitialized = 0; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - double suggestedInputLatency, suggestedOutputLatency; - PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; - unsigned long framesPerHostInputBuffer; - unsigned long hostInputBufferCount; - unsigned long framesPerHostOutputBuffer; - unsigned long hostOutputBufferCount; - unsigned long framesPerBufferProcessorCall; - PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ - unsigned long inputDeviceCount = 0; - PaWinMmeDeviceAndChannelCount *outputDevices = 0; - unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ - char throttleProcessingThreadOnOverload = 1; - - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatency = inputParameters->suggestedLatency; - - inputDeviceCount = 1; - - /* validate input hostApiSpecificStreamInfo */ - inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; - result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo, - &throttleProcessingThreadOnOverload, - &inputDeviceCount ); - if( result != paNoError ) return result; - - inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount ); - if( !inputDevices ) return paInsufficientMemory; - - result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount ); - if( result != paNoError ) return result; - - result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount ); - if( result != paNoError ) return result; - - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); - } - else - { - inputChannelCount = 0; - inputSampleFormat = 0; - suggestedInputLatency = 0.; - inputStreamInfo = 0; - hostInputSampleFormat = 0; - } - - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatency = outputParameters->suggestedLatency; - - outputDeviceCount = 1; - - /* validate output hostApiSpecificStreamInfo */ - outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; - result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo, - &throttleProcessingThreadOnOverload, - &outputDeviceCount ); - if( result != paNoError ) return result; - - outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount ); - if( !outputDevices ) return paInsufficientMemory; - - result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount ); - if( result != paNoError ) return result; - - result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount ); - if( result != paNoError ) return result; - - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); - } - else - { - outputChannelCount = 0; - outputSampleFormat = 0; - outputStreamInfo = 0; - hostOutputSampleFormat = 0; - 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, &hostInputBufferCount, - &framesPerHostOutputBuffer, &hostOutputBufferCount, - 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; - } - - InitializeSingleDirectionHandlesAndBuffers( &stream->input ); - InitializeSingleDirectionHandlesAndBuffers( &stream->output ); - - stream->abortEvent = 0; - stream->processingThread = 0; - - stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - ( (streamCallback) - ? &winMmeHostApi->callbackStreamInterface - : &winMmeHostApi->blockingStreamInterface ), - streamCallback, userData ); - streamRepresentationIsInitialized = 1; - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - if( inputParameters && outputParameters ) /* full duplex */ - { - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) - { - assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ - - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else - { - assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ - - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - } - else if( inputParameters ) - { - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else if( outputParameters ) - { - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - - stream->input.framesPerBuffer = framesPerHostInputBuffer; - stream->output.framesPerBuffer = framesPerHostOutputBuffer; - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerBufferProcessorCall, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) goto error; - - bufferProcessorIsInitialized = 1; - - stream->streamRepresentation.streamInfo.inputLatency = - (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) - +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; - stream->streamRepresentation.streamInfo.outputLatency = - (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0; - - /* time to sleep when throttling due to >100% cpu usage. - -a quater of a buffer's duration */ - stream->throttledSleepMsecs = - (unsigned long)(stream->bufferProcessor.framesPerHostBuffer * - stream->bufferProcessor.samplePeriod * .25 * 1000); - - stream->isStopped = 1; - stream->isActive = 0; - - - /* for maximum compatibility with multi-device multichannel drivers, - we first open all devices, then we prepare all buffers, finally - we start all devices ( in StartStream() ). teardown in reverse order. - */ - - if( inputParameters ) - { - result = InitializeWaveHandles( winMmeHostApi, &stream->input, - stream->bufferProcessor.bytesPerHostInputSample, sampleRate, - inputDevices, inputDeviceCount, 1 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( outputParameters ) - { - result = InitializeWaveHandles( winMmeHostApi, &stream->output, - stream->bufferProcessor.bytesPerHostOutputSample, sampleRate, - outputDevices, outputDeviceCount, 0 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( inputParameters ) - { - result = InitializeWaveHeaders( &stream->input, hostInputBufferCount, - hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( outputParameters ) - { - result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount, - hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ ); - if( result != paNoError ) goto error; - - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate); - } - else - { - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate); - } - - - if( streamCallback ) - { - /* abort event is only needed for callback streams */ - result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL ); - if( result != paNoError ) goto error; - } - - *s = (PaStream*)stream; - - return result; - -error: - - if( stream ) - { - if( stream->abortEvent ) - CloseHandle( stream->abortEvent ); - - TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); - TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); - - TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ ); - TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ ); - - if( bufferProcessorIsInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - - if( streamRepresentationIsInitialized ) - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - PaUtil_FreeMemory( stream ); - } - - return result; -} - - -/* return non-zero if all current buffers are done */ -static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) ) - { - return 0; - } - } - - return 1; -} - -static int CurrentInputBuffersAreDone( PaWinMmeStream *stream ) -{ - return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex ); -} - -static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream ) -{ - return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex ); -} - - -/* return non-zero if any buffers are queued */ -static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - unsigned int i, j; - - if( handlesAndBuffers->waveHandles ) - { - for( i=0; i < handlesAndBuffers->bufferCount; ++i ) - { - for( j=0; j < handlesAndBuffers->deviceCount; ++j ) - { - if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) ) - { - return 0; - } - } - } - } - - return 1; -} - - -#define PA_CIRCULAR_INCREMENT_( current, max )\ - ( (((current) + 1) >= (max)) ? (0) : (current+1) ) - -#define PA_CIRCULAR_DECREMENT_( current, max )\ - ( ((current) == 0) ? ((max)-1) : (current-1) ) - - -static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - signed long result = 0; - unsigned int i; - - if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) ) - { - /* we could calculate the following in O(1) if we kept track of the - last done buffer */ - result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer; - - i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount ); - while( i != handlesAndBuffers->currentBufferIndex ) - { - if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) ) - { - result += handlesAndBuffers->framesPerBuffer; - i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount ); - } - else - break; - } - } - - return result; -} - - -static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i], - &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - - stream->input.currentBufferIndex = - PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount ); - - stream->input.framesUsedInCurrentBuffer = 0; - - return result; -} - - -static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i], - &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - - stream->output.currentBufferIndex = - PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); - - stream->output.framesUsedInCurrentBuffer = 0; - - return result; -} - - -/* requeue all but the most recent input with the driver. Used for catching - up after a total input buffer underrun */ -static PaError CatchUpInputBuffers( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - unsigned int i; - - for( i=0; i < stream->input.bufferCount - 1; ++i ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - break; - } - - return result; -} - - -/* take the most recent output and duplicate it to all other output buffers - and requeue them. Used for catching up after a total output buffer underrun. -*/ -static PaError CatchUpOutputBuffers( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - unsigned int i, j; - unsigned int previousBufferIndex = - PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); - - for( i=0; i < stream->output.bufferCount - 1; ++i ) - { - for( j=0; j < stream->output.deviceCount; ++j ) - { - if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData - != stream->output.waveHeaders[j][ previousBufferIndex ].lpData ) - { - CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData, - stream->output.waveHeaders[j][ previousBufferIndex ].lpData, - stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength ); - } - } - - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - break; - } - - return result; -} - - -static DWORD WINAPI ProcessingThreadProc( void *pArg ) -{ - PaWinMmeStream *stream = (PaWinMmeStream *)pArg; - HANDLE events[3]; - int eventCount = 0; - DWORD result = paNoError; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - int hostBuffersAvailable; - signed int hostInputBufferIndex, hostOutputBufferIndex; - PaStreamCallbackFlags statusFlags; - int callbackResult; - int done = 0; - unsigned int channel, i; - unsigned long framesProcessed; - - /* prepare event array for call to WaitForMultipleObjects() */ - if( stream->input.bufferEvent ) - events[eventCount++] = stream->input.bufferEvent; - if( stream->output.bufferEvent ) - events[eventCount++] = stream->output.bufferEvent; - events[eventCount++] = stream->abortEvent; - - statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */ - - /* loop until something causes us to stop */ - do{ - /* wait for MME to signal that a buffer is available, or for - the PA abort event to be signaled. - - When this indicates that one or more buffers are available - NoBuffersAreQueued() and Current*BuffersAreDone are used below to - poll for additional done buffers. NoBuffersAreQueued() will fail - to identify an underrun/overflow if the driver doesn't mark all done - buffers prior to signalling the event. Some drivers do this - (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a - huge problem, it just means that we won't always be able to detect - underflow/overflow. - */ - waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - /** @todo 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 */ - } - - 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( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( NoBuffersAreQueued( &stream->output ) ) - done = 1; /* Will cause thread to return. */ - } - else - { - /* input only 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) ) - { - if( CurrentInputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo - if all of the other buffers are also ready then - we discard all but the most recent. This is an - input buffer overflow. FIXME: these buffers should - be passed to the callback in a paNeverDropInput - stream. - - note that it is also possible for an input overflow - to happen while the callback is processing a buffer. - that is handled further down. - */ - result = CatchUpInputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paInputOverflow; - } - - hostInputBufferIndex = stream->input.currentBufferIndex; - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( CurrentOutputBuffersAreDone( stream ) ) - { - /* ok, we have an output buffer */ - - if( NoBuffersAreQueued( &stream->output ) ) - { - /* - 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. - - note that this catch up code only handles the case where all - buffers have been played out due to this thread not having - woken up at all. a more common case occurs when this thread - is woken up, processes one buffer, but takes too long, and as - a result all the other buffers have become un-queued. that - case is handled further down. - */ - - result = CatchUpOutputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paOutputUnderflow; - } - - hostOutputBufferIndex = stream->output.currentBufferIndex; - } - } - - - if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) || - (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime - from the current wave out position */ - MMTIME mmtime; - double timeBeforeGetPosition, timeAfterGetPosition; - double time; - long framesInBufferRing; - long writePosition; - long playbackPosition; - HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0]; - - mmtime.wType = TIME_SAMPLES; - timeBeforeGetPosition = PaUtil_GetTime(); - waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) ); - timeAfterGetPosition = PaUtil_GetTime(); - - timeInfo.currentTime = timeAfterGetPosition; - - /* approximate time at which wave out position was measured - as half way between timeBeforeGetPosition and timeAfterGetPosition */ - time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5; - - framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer; - playbackPosition = mmtime.u.sample % framesInBufferRing; - - writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer - + stream->output.framesUsedInCurrentBuffer; - - if( playbackPosition >= writePosition ){ - timeInfo.outputBufferDacTime = - time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod ); - }else{ - timeInfo.outputBufferDacTime = - time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod ); - } - } - - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags ); - - /* reset status flags once they have been passed to the buffer processor */ - statusFlags = 0; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; i<stream->input.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; - - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, - stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + - stream->input.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostInputSample, - channelCount ); - - - channel += channelCount; - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; i<stream->output.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - } - - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - stream->input.framesUsedInCurrentBuffer += framesProcessed; - stream->output.framesUsedInCurrentBuffer += framesProcessed; - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - stream->abortProcessing = 1; - done = 1; - /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */ - result = paNoError; - } - else - { - /* User callback has asked us to stop with paComplete or other non-zero value */ - stream->stopProcessing = 1; /* stop once currently queued audio has finished */ - result = paNoError; - } - - - if( PA_IS_INPUT_STREAM_(stream) - && stream->stopProcessing == 0 && stream->abortProcessing == 0 - && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo need to handle PaNeverDropInput here where necessary */ - result = CatchUpInputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paInputOverflow; - } - - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - - - if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing ) - { - if( stream->stopProcessing && - stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer ) - { - /* zero remaining samples in output output buffer and flush */ - - stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - /* we send the entire buffer to the output devices, but we could - just send a partial buffer, rather than zeroing the unused - samples. - */ - } - - if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) - { - /* check for underflow before enquing the just-generated buffer, - but recover from underflow after enquing it. This ensures - that the most recent audio segment is repeated */ - int outputUnderflow = NoBuffersAreQueued( &stream->output ); - - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - done = 1; - - if( outputUnderflow && !done && !stream->stopProcessing ) - { - /* Recover from underflow in the case where the - underflow occured while processing the buffer - we just finished */ - - result = CatchUpOutputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paOutputUnderflow; - } - } - } - - if( stream->throttleProcessingThreadOnOverload != 0 ) - { - if( stream->stopProcessing || stream->abortProcessing ) - { - if( stream->processingThreadPriority != stream->highThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->highThreadPriority ); - stream->processingThreadPriority = stream->highThreadPriority; - } - } - else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. ) - { - if( stream->processingThreadPriority != stream->throttledThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->throttledThreadPriority ); - stream->processingThreadPriority = stream->throttledThreadPriority; - } - - /* sleep to give other processes a go */ - Sleep( stream->throttledSleepMsecs ); - } - else - { - if( stream->processingThreadPriority != stream->highThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->highThreadPriority ); - stream->processingThreadPriority = stream->highThreadPriority; - } - } - } - } - else - { - hostBuffersAvailable = 0; - } - } - while( hostBuffersAvailable && - stream->stopProcessing == 0 && - stream->abortProcessing == 0 && - !done ); - } - } - while( !done ); - - stream->isActive = 0; - - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - 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; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - result = CloseHandleWithPaError( stream->abortEvent ); - if( result != paNoError ) goto error; - - TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); - TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); - - TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ ); - TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - -error: - /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */ - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - MMRESULT mmresult; - unsigned int i, j; - int callbackResult; - unsigned int channel; - unsigned long framesProcessed; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */ - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i<stream->input.bufferCount; ++i ) - { - for( j=0; j<stream->input.deviceCount; ++j ) - { - mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - } - stream->input.currentBufferIndex = 0; - stream->input.framesUsedInCurrentBuffer = 0; - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; i<stream->output.deviceCount; ++i ) - { - if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - - for( i=0; i<stream->output.bufferCount; ++i ) - { - if( stream->primeStreamUsingCallback ) - { - - stream->output.framesUsedInCurrentBuffer = 0; - do{ - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, - &timeInfo, - paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0)); - - if( stream->input.bufferCount > 0 ) - PaUtil_SetNoInput( &stream->bufferProcessor ); - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( j=0; j<stream->output.deviceCount; ++j ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[j][i].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[j][i].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - /* we have stored the number of channels in the buffer in dwUser */ - channel += channelCount; - } - - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - stream->output.framesUsedInCurrentBuffer += framesProcessed; - - if( callbackResult != paContinue ) - { - /** @todo fix this, what do we do if callback result is non-zero during stream - priming? - - for complete: play out primed waveHeaders as usual - for abort: clean up immediately. - */ - } - - }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer ); - - } - else - { - for( j=0; j<stream->output.deviceCount; ++j ) - { - ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength ); - } - } - - /* we queue all channels of a single buffer frame (accross all - devices, because some multidevice multichannel drivers work - better this way */ - for( j=0; j<stream->output.deviceCount; ++j ) - { - mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - stream->output.currentBufferIndex = 0; - stream->output.framesUsedInCurrentBuffer = 0; - } - - - stream->isStopped = 0; - stream->isActive = 1; - stream->stopProcessing = 0; - stream->abortProcessing = 0; - - result = ResetEventWithPaError( stream->input.bufferEvent ); - if( result != paNoError ) goto error; - - result = ResetEventWithPaError( stream->output.bufferEvent ); - if( result != paNoError ) goto error; - - - if( stream->streamRepresentation.streamCallback ) - { - /* callback stream */ - - result = ResetEventWithPaError( stream->abortEvent ); - if( result != paNoError ) 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; - } - - /** @todo could have mme specific stream parameters to allow the user - to set the callback thread priorities */ - stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; - 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; - } - else - { - /* blocking read/write stream */ - - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[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->output.deviceCount; ++i ) - { - if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - - return result; - -error: - /** @todo 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; - signed int hostOutputBufferIndex; - unsigned int channel, waitCount, i; - - /** @todo - REVIEW: 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. - */ - - if( stream->processingThread ) - { - /* callback stream */ - - /* 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 = (int)(stream->allBuffersDurationMs * 1.5); - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_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; - } - else - { - /* blocking read / write stream */ - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( stream->output.framesUsedInCurrentBuffer > 0 ) - { - /* there are still unqueued frames in the current buffer, so flush them */ - - hostOutputBufferIndex = stream->output.currentBufferIndex; - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; i<stream->output.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - - PaUtil_ZeroOutput( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - /* we send the entire buffer to the output devices, but we could - just send a partial buffer, rather than zeroing the unused - samples. - */ - AdvanceToNextOutputBuffer( stream ); - } - - - timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1; - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_MIN_TIMEOUT_MSEC_; - - waitCount = 0; - while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount ) - { - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* keep waiting */ - } - - ++waitCount; - } - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[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->input.deviceCount; ++i ) - { - mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - } - - stream->isStopped = 1; - 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; - - /** @todo - REVIEW: 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. - */ - - if( stream->processingThread ) - { - /* callback stream */ - - /* Tell processing thread to abort immediately */ - stream->abortProcessing = 1; - SetEvent( stream->abortEvent ); - } - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[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->input.deviceCount; ++i ) - { - mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - } - - - if( stream->processingThread ) - { - /* callback stream */ - - PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n")); - - /* Calculate timeOut longer than longest time it could take to return all buffers. */ - timeout = (int)(stream->allBuffersDurationMs * 1.5); - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_MIN_TIMEOUT_MSEC_; - - 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->isStopped = 1; - stream->isActive = 0; - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return stream->isStopped; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - (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 ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - void *userBuffer; - unsigned long framesRead = 0; - unsigned long framesProcessed; - signed int hostInputBufferIndex; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - unsigned int channel, i; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - /* make a local copy of the user buffer pointer(s). this is necessary - because PaUtil_CopyInput() advances these pointers every time - it is called. - */ - if( stream->bufferProcessor.userInputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount ); - if( !userBuffer ) - return paInsufficientMemory; - for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i ) - ((void**)userBuffer)[i] = ((void**)buffer)[i]; - } - - do{ - if( CurrentInputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo REVIEW: consider what to do if the input overflows. - do we requeue all of the buffers? should we be running - a thread to make sure they are always queued? */ - - result = paInputOverflowed; - } - - hostInputBufferIndex = stream->input.currentBufferIndex; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, - stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; i<stream->input.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; - - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, - stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + - stream->input.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostInputSample, - channelCount ); - - channel += channelCount; - } - - framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead ); - - stream->input.framesUsedInCurrentBuffer += framesProcessed; - if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - break; - } - - framesRead += framesProcessed; - - }else{ - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ - } - } - }while( framesRead < frames ); - } - else - { - result = paCanNotReadFromAnOutputOnlyStream; - } - - return result; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - const void *userBuffer; - unsigned long framesWritten = 0; - unsigned long framesProcessed; - signed int hostOutputBufferIndex; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - unsigned int channel, i; - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - /* make a local copy of the user buffer pointer(s). this is necessary - because PaUtil_CopyOutput() advances these pointers every time - it is called. - */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount ); - if( !userBuffer ) - return paInsufficientMemory; - for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i ) - ((const void**)userBuffer)[i] = ((const void**)buffer)[i]; - } - - do{ - if( CurrentOutputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->output ) ) - { - /** @todo REVIEW: consider what to do if the output - underflows. do we requeue all the existing buffers with - zeros? should we run a separate thread to keep the buffers - enqueued at all times? */ - - result = paOutputUnderflowed; - } - - hostOutputBufferIndex = stream->output.currentBufferIndex; - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; i<stream->output.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - - framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten ); - - stream->output.framesUsedInCurrentBuffer += framesProcessed; - if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) - { - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - break; - } - - framesWritten += framesProcessed; - } - else - { - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ - } - } - }while( framesWritten < frames ); - } - else - { - result = paCanNotWriteToAnInputOnlyStream; - } - - return result; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - if( PA_IS_INPUT_STREAM_(stream) ) - return GetAvailableFrames( &stream->input ); - else - return paCanNotReadFromAnOutputOnlyStream; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - if( PA_IS_OUTPUT_STREAM_(stream) ) - return GetAvailableFrames( &stream->output ); - else - return paCanNotWriteToAnInputOnlyStream; -} - - -/* NOTE: the following functions are MME-stream specific, and are called directly - by client code. We need to check for many more error conditions here because - we don't have the benefit of pa_front.c's parameter checking. -*/ - -static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaWinMmeHostApiRepresentation *winMmeHostApi; - - result = PaUtil_ValidateStreamPointer( s ); - if( result != paNoError ) - return result; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paMME ); - if( result != paNoError ) - return result; - - winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - - /* note, the following would be easier if there was a generic way of testing - that a stream belongs to a specific host API */ - - if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface - || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface ) - { - /* s is a WinMME stream */ - *stream = (PaWinMmeStream *)s; - return paNoError; - } - else - { - return paIncompatibleStreamHostApi; - } -} - - -int PaWinMME_GetStreamInputHandleCount( PaStream* s ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError ) - return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0; - else - return result; -} - - -HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError - && PA_IS_INPUT_STREAM_(stream) - && handleIndex >= 0 - && (unsigned int)handleIndex < stream->input.deviceCount ) - return ((HWAVEIN*)stream->input.waveHandles)[handleIndex]; - else - return 0; -} - - -int PaWinMME_GetStreamOutputHandleCount( PaStream* s) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError ) - return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0; - else - return result; -} - - -HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError - && PA_IS_OUTPUT_STREAM_(stream) - && handleIndex >= 0 - && (unsigned int)handleIndex < stream->output.deviceCount ) - return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex]; - else - return 0; -} - - - - - diff --git a/portaudio/pa_win_wmme/pa_win_wmme.h b/portaudio/pa_win_wmme/pa_win_wmme.h deleted file mode 100644 index 1a71633ad..000000000 --- a/portaudio/pa_win_wmme/pa_win_wmme.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef PA_WIN_WMME_H -#define PA_WIN_WMME_H -/* - * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $ - * 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. - * - */ - -/** @file - @brief WMME-specific PortAudio API extension header file. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#define paWinMmeUseLowLevelLatencyParameters (0x01) -#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */ - - -/* 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% - This flag disables any priority throttling. The processing thread will always - run at THREAD_PRIORITY_TIME_CRITICAL. -*/ -#define paWinMmeDontThrottleOverloadedProcessingThread (0x08) - - -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. - - If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters - are supplied for both input and output in a full duplex stream, then the - input and output framesPerBuffer must be the same, or the larger of the - two must be a multiple of the smaller, otherwise a - paIncompatibleHostApiSpecificStreamInfo error will be returned from - Pa_OpenStream(). - */ - unsigned long framesPerBuffer; - unsigned long bufferCount; /* formerly 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; - - -/** Retrieve the number of wave in handles used by a PortAudio WinMME stream. - Returns zero if the stream is output only. - - @return A non-negative value indicating the number of wave in handles - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaWinMME_GetStreamInputHandle -*/ -int PaWinMME_GetStreamInputHandleCount( PaStream* stream ); - - -/** Retrieve a wave in handle used by a PortAudio WinMME stream. - - @param stream The stream to query. - @param handleIndex The zero based index of the wave in handle to retrieve. This - should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1]. - - @return A valid wave in handle, or NULL if an error occurred. - - @see PaWinMME_GetStreamInputHandle -*/ -HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex ); - - -/** Retrieve the number of wave out handles used by a PortAudio WinMME stream. - Returns zero if the stream is input only. - - @return A non-negative value indicating the number of wave out handles - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaWinMME_GetStreamOutputHandle -*/ -int PaWinMME_GetStreamOutputHandleCount( PaStream* stream ); - - -/** Retrieve a wave out handle used by a PortAudio WinMME stream. - - @param stream The stream to query. - @param handleIndex The zero based index of the wave out handle to retrieve. - This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1]. - - @return A valid wave out handle, or NULL if an error occurred. - - @see PaWinMME_GetStreamOutputHandleCount -*/ -HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_WIN_WMME_H */ diff --git a/portaudio/pablio/CVS/Entries b/portaudio/pablio/CVS/Entries deleted file mode 100644 index 22a4c8e8d..000000000 --- a/portaudio/pablio/CVS/Entries +++ /dev/null @@ -1,11 +0,0 @@ -/README.txt/1.1.1.1/Tue Jan 22 00:52:52 2002//Tv19-devel -/pablio.c/1.1.1.1/Tue Jan 22 00:52:53 2002//Tv19-devel -/pablio.def/1.1.1.1/Tue Jan 22 00:52:53 2002//Tv19-devel -/pablio.h/1.1.1.1/Tue Jan 22 00:52:53 2002//Tv19-devel -/ringbuffer.c/1.1.1.1/Tue Jan 22 00:52:53 2002//Tv19-devel -/ringbuffer.h/1.1.1.1/Tue Jan 22 00:52:54 2002//Tv19-devel -/test_rw.c/1.2/Fri Feb 22 22:06:23 2002//Tv19-devel -/test_rw_echo.c/1.1.1.1/Tue Jan 22 00:52:54 2002//Tv19-devel -/test_w_saw.c/1.1.1.1/Tue Jan 22 00:52:54 2002//Tv19-devel -/test_w_saw8.c/1.1.1.1/Tue Jan 22 00:52:55 2002//Tv19-devel -D diff --git a/portaudio/pablio/CVS/Repository b/portaudio/pablio/CVS/Repository deleted file mode 100644 index 71b9cbc0a..000000000 --- a/portaudio/pablio/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -portaudio/pablio diff --git a/portaudio/pablio/CVS/Root b/portaudio/pablio/CVS/Root deleted file mode 100644 index 815fa569f..000000000 --- a/portaudio/pablio/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -:pserver:anonymous@www.portaudio.com:/home/cvs diff --git a/portaudio/pablio/CVS/Tag b/portaudio/pablio/CVS/Tag deleted file mode 100644 index 8e6595a07..000000000 --- a/portaudio/pablio/CVS/Tag +++ /dev/null @@ -1 +0,0 @@ -Tv19-devel diff --git a/portaudio/pablio/README.txt b/portaudio/pablio/README.txt deleted file mode 100644 index 99c7d146e..000000000 --- a/portaudio/pablio/README.txt +++ /dev/null @@ -1,39 +0,0 @@ -README for PABLIO -Portable Audio Blocking I/O Library -Author: Phil Burk - -PABLIO is a simplified interface to PortAudio that provide -read/write style blocking I/O. - -Please see the .DOC file for documentation. - -/* - * More information on PortAudio at: http://www.portaudio.com - * 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. - * - */ - - diff --git a/portaudio/pablio/pablio.c b/portaudio/pablio/pablio.c deleted file mode 100644 index dca995a6e..000000000 --- a/portaudio/pablio/pablio.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * $Id: pablio.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $ - * pablio.c - * Portable Audio Blocking Input/Output utility. - * - * Author: Phil Burk, http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "portaudio.h" -#include "ringbuffer.h" -#include "pablio.h" -#include <string.h> - -/************************************************************************/ -/******** Constants *****************************************************/ -/************************************************************************/ - -#define FRAMES_PER_BUFFER (256) - -/************************************************************************/ -/******** Prototypes ****************************************************/ -/************************************************************************/ - -static int blockingIOCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ); -static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); -static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); - -/************************************************************************/ -/******** Functions *****************************************************/ -/************************************************************************/ - -/* Called from PortAudio. - * Read and write data only if there is room in FIFOs. - */ -static int blockingIOCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - PABLIO_Stream *data = (PABLIO_Stream*)userData; - long numBytes = data->bytesPerFrame * framesPerBuffer; - (void) outTime; - - /* This may get called with NULL inputBuffer during initial setup. */ - if( inputBuffer != NULL ) - { - RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes ); - } - if( outputBuffer != NULL ) - { - int i; - int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes ); - /* Zero out remainder of buffer if we run out of data. */ - for( i=numRead; i<numBytes; i++ ) - { - ((char *)outputBuffer)[i] = 0; - } - } - - return 0; -} - -/* Allocate buffer. */ -static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ) -{ - long numBytes = numFrames * bytesPerFrame; - char *buffer = (char *) malloc( numBytes ); - if( buffer == NULL ) return paInsufficientMemory; - memset( buffer, 0, numBytes ); - return (PaError) RingBuffer_Init( rbuf, numBytes, buffer ); -} - -/* Free buffer. */ -static PaError PABLIO_TermFIFO( RingBuffer *rbuf ) -{ - if( rbuf->buffer ) free( rbuf->buffer ); - rbuf->buffer = NULL; - return paNoError; -} - -/************************************************************ - * Write data to ring buffer. - * Will not return until all the data has been written. - */ -long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) -{ - long bytesWritten; - char *p = (char *) data; - long numBytes = aStream->bytesPerFrame * numFrames; - while( numBytes > 0) - { - bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes ); - numBytes -= bytesWritten; - p += bytesWritten; - if( numBytes > 0) Pa_Sleep(10); - } - return numFrames; -} - -/************************************************************ - * Read data from ring buffer. - * Will not return until all the data has been read. - */ -long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) -{ - long bytesRead; - char *p = (char *) data; - long numBytes = aStream->bytesPerFrame * numFrames; - while( numBytes > 0) - { - bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes ); - numBytes -= bytesRead; - p += bytesRead; - if( numBytes > 0) Pa_Sleep(10); - } - return numFrames; -} - -/************************************************************ - * Return the number of frames that could be written to the stream without - * having to wait. - */ -long GetAudioStreamWriteable( PABLIO_Stream *aStream ) -{ - int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); - return bytesEmpty / aStream->bytesPerFrame; -} - -/************************************************************ - * Return the number of frames that are available to be read from the - * stream without having to wait. - */ -long GetAudioStreamReadable( PABLIO_Stream *aStream ) -{ - int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO ); - return bytesFull / aStream->bytesPerFrame; -} - -/************************************************************/ -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<<numBits); -} - -/************************************************************ - * Opens a PortAudio stream with default characteristics. - * Allocates PABLIO_Stream structure. - * - * 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, - PaSampleFormat format, long flags ) -{ - long bytesPerSample; - long doRead = 0; - long doWrite = 0; - PaError err; - PABLIO_Stream *aStream; - long minNumBuffers; - long numFrames; - - /* Allocate PABLIO_Stream structure for caller. */ - aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) ); - if( aStream == NULL ) return paInsufficientMemory; - memset( aStream, 0, sizeof(PABLIO_Stream) ); - - /* Determine size of a sample. */ - bytesPerSample = Pa_GetSampleSize( format ); - if( bytesPerSample < 0 ) - { - err = (PaError) bytesPerSample; - goto error; - } - aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2; - 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. Just to be safe, adjust size upwards. - */ - minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); - numFrames = minNumBuffers * FRAMES_PER_BUFFER; - numFrames = RoundUpToNextPowerOf2( numFrames ); - - /* Initialize Ring Buffers */ - doRead = ((flags & PABLIO_READ) != 0); - doWrite = ((flags & PABLIO_WRITE) != 0); - if(doRead) - { - err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame ); - if( err != paNoError ) goto error; - } - if(doWrite) - { - long numBytes; - err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame ); - if( err != paNoError ) goto error; - /* Make Write FIFO appear full initially. */ - numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); - RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes ); - } - - /* Open a PortAudio stream that we will use to communicate with the underlying - * audio drivers. */ - err = Pa_OpenStream( - &aStream->stream, - (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice), - (doRead ? aStream->samplesPerFrame : 0 ), - format, - NULL, - (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice), - (doWrite ? aStream->samplesPerFrame : 0 ), - format, - NULL, - sampleRate, - FRAMES_PER_BUFFER, - minNumBuffers, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - blockingIOCallback, - aStream ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( aStream->stream ); - if( err != paNoError ) goto error; - - *rwblPtr = aStream; - return paNoError; - -error: - CloseAudioStream( aStream ); - *rwblPtr = NULL; - return err; -} - -/************************************************************/ -PaError CloseAudioStream( PABLIO_Stream *aStream ) -{ - PaError err; - int bytesEmpty; - int byteSize = aStream->outFIFO.bufferSize; - - /* If we are writing data, make sure we play everything written. */ - if( byteSize > 0 ) - { - bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); - while( bytesEmpty < byteSize ) - { - Pa_Sleep( 10 ); - bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); - } - } - - err = Pa_StopStream( aStream->stream ); - if( err != paNoError ) goto error; - err = Pa_CloseStream( aStream->stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - -error: - PABLIO_TermFIFO( &aStream->inFIFO ); - PABLIO_TermFIFO( &aStream->outFIFO ); - free( aStream ); - return err; -} diff --git a/portaudio/pablio/pablio.def b/portaudio/pablio/pablio.def deleted file mode 100644 index a10f95291..000000000 --- a/portaudio/pablio/pablio.def +++ /dev/null @@ -1,35 +0,0 @@ -LIBRARY PABLIO -DESCRIPTION 'PABLIO Portable Audio Blocking I/O' - -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 - - OpenAudioStream @19 - CloseAudioStream @20 - WriteAudioStream @21 - ReadAudioStream @22 - - Pa_GetSampleSize @23 - - ;123456789012345678901234567890123456 - ;000000000111111111122222222223333333 - - diff --git a/portaudio/pablio/pablio.h b/portaudio/pablio/pablio.h deleted file mode 100644 index a4871f38b..000000000 --- a/portaudio/pablio/pablio.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef _PABLIO_H -#define _PABLIO_H - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -/* - * $Id: pablio.h,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $ - * PABLIO.h - * Portable Audio Blocking read/write utility. - * - * Author: Phil Burk, http://www.softsynth.com/portaudio/ - * - * Include file for PABLIO, the Portable Audio Blocking I/O Library. - * PABLIO is built on top of PortAudio, the Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "portaudio.h" -#include "ringbuffer.h" -#include <string.h> - -typedef struct -{ - RingBuffer inFIFO; - RingBuffer outFIFO; - PortAudioStream *stream; - int bytesPerFrame; - int samplesPerFrame; -} -PABLIO_Stream; - -/* Values for flags for OpenAudioStream(). */ -#define PABLIO_READ (1<<0) -#define PABLIO_WRITE (1<<1) -#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE) -#define PABLIO_MONO (1<<2) -#define PABLIO_STEREO (1<<3) - -/************************************************************ - * Write data to ring buffer. - * Will not return until all the data has been written. - */ -long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); - -/************************************************************ - * Read data from ring buffer. - * Will not return until all the data has been read. - */ -long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); - -/************************************************************ - * Return the number of frames that could be written to the stream without - * having to wait. - */ -long GetAudioStreamWriteable( PABLIO_Stream *aStream ); - -/************************************************************ - * Return the number of frames that are available to be read from the - * stream without having to wait. - */ -long GetAudioStreamReadable( PABLIO_Stream *aStream ); - -/************************************************************ - * Opens a PortAudio stream with default characteristics. - * Allocates PABLIO_Stream structure. - * - * 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 **aStreamPtr, double sampleRate, - PaSampleFormat format, long flags ); - -PaError CloseAudioStream( PABLIO_Stream *aStream ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _PABLIO_H */ diff --git a/portaudio/pablio/ringbuffer.c b/portaudio/pablio/ringbuffer.c deleted file mode 100644 index e0c028909..000000000 --- a/portaudio/pablio/ringbuffer.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * $Id: ringbuffer.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $ - * ringbuffer.c - * Ring Buffer utility.. - * - * Author: Phil Burk, http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "ringbuffer.h" -#include <string.h> - -/*************************************************************************** - * Initialize FIFO. - * numBytes must be power of 2, returns -1 if not. - */ -long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ) -{ - if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */ - rbuf->bufferSize = numBytes; - rbuf->buffer = (char *)dataPtr; - RingBuffer_Flush( rbuf ); - rbuf->bigMask = (numBytes*2)-1; - rbuf->smallMask = (numBytes)-1; - return 0; -} -/*************************************************************************** -** Return number of bytes available for reading. */ -long RingBuffer_GetReadAvailable( RingBuffer *rbuf ) -{ - return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); -} -/*************************************************************************** -** Return number of bytes available for writing. */ -long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ) -{ - return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf)); -} - -/*************************************************************************** -** Clear buffer. Should only be called when buffer is NOT being read. */ -void RingBuffer_Flush( RingBuffer *rbuf ) -{ - rbuf->writeIndex = rbuf->readIndex = 0; -} - -/*************************************************************************** -** Get address of region(s) to which we can write data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or numBytes, whichever is smaller. -*/ -long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, - void **dataPtr1, long *sizePtr1, - void **dataPtr2, long *sizePtr2 ) -{ - long index; - long available = RingBuffer_GetWriteAvailable( rbuf ); - if( numBytes > available ) numBytes = available; - /* Check to see if write is not contiguous. */ - index = rbuf->writeIndex & rbuf->smallMask; - if( (index + numBytes) > rbuf->bufferSize ) - { - /* Write data in two blocks that wrap the buffer. */ - long firstHalf = rbuf->bufferSize - index; - *dataPtr1 = &rbuf->buffer[index]; - *sizePtr1 = firstHalf; - *dataPtr2 = &rbuf->buffer[0]; - *sizePtr2 = numBytes - firstHalf; - } - else - { - *dataPtr1 = &rbuf->buffer[index]; - *sizePtr1 = numBytes; - *dataPtr2 = NULL; - *sizePtr2 = 0; - } - return numBytes; -} - - -/*************************************************************************** -*/ -long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ) -{ - return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask; -} - -/*************************************************************************** -** Get address of region(s) from which we can read data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or numBytes, whichever is smaller. -*/ -long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, - void **dataPtr1, long *sizePtr1, - void **dataPtr2, long *sizePtr2 ) -{ - long index; - long available = RingBuffer_GetReadAvailable( rbuf ); - if( numBytes > available ) numBytes = available; - /* Check to see if read is not contiguous. */ - index = rbuf->readIndex & rbuf->smallMask; - if( (index + numBytes) > rbuf->bufferSize ) - { - /* Write data in two blocks that wrap the buffer. */ - long firstHalf = rbuf->bufferSize - index; - *dataPtr1 = &rbuf->buffer[index]; - *sizePtr1 = firstHalf; - *dataPtr2 = &rbuf->buffer[0]; - *sizePtr2 = numBytes - firstHalf; - } - else - { - *dataPtr1 = &rbuf->buffer[index]; - *sizePtr1 = numBytes; - *dataPtr2 = NULL; - *sizePtr2 = 0; - } - return numBytes; -} -/*************************************************************************** -*/ -long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ) -{ - return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask; -} - -/*************************************************************************** -** Return bytes written. */ -long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ) -{ - long size1, size2, numWritten; - void *data1, *data2; - numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); - if( size2 > 0 ) - { - - memcpy( data1, data, size1 ); - data = ((char *)data) + size1; - memcpy( data2, data, size2 ); - } - else - { - memcpy( data1, data, size1 ); - } - RingBuffer_AdvanceWriteIndex( rbuf, numWritten ); - return numWritten; -} - -/*************************************************************************** -** Return bytes read. */ -long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ) -{ - long size1, size2, numRead; - void *data1, *data2; - numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); - if( size2 > 0 ) - { - memcpy( data, data1, size1 ); - data = ((char *)data) + size1; - memcpy( data, data2, size2 ); - } - else - { - memcpy( data, data1, size1 ); - } - RingBuffer_AdvanceReadIndex( rbuf, numRead ); - return numRead; -} diff --git a/portaudio/pablio/ringbuffer.h b/portaudio/pablio/ringbuffer.h deleted file mode 100644 index eb46a5f86..000000000 --- a/portaudio/pablio/ringbuffer.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef _RINGBUFFER_H -#define _RINGBUFFER_H -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -/* - * $Id: ringbuffer.h,v 1.1.1.1 2002/01/22 00:52:54 phil Exp $ - * ringbuffer.h - * Ring Buffer utility.. - * - * Author: Phil Burk, http://www.softsynth.com - * - * This program is distributed with the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "ringbuffer.h" -#include <string.h> - -typedef struct -{ - long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */ - long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */ - long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */ - long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */ - long smallMask; /* Used for fitting indices to buffer. */ - char *buffer; -} -RingBuffer; -/* - * Initialize Ring Buffer. - * numBytes must be power of 2, returns -1 if not. - */ -long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ); - -/* Clear buffer. Should only be called when buffer is NOT being read. */ -void RingBuffer_Flush( RingBuffer *rbuf ); - -/* Return number of bytes available for writing. */ -long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ); -/* Return number of bytes available for read. */ -long RingBuffer_GetReadAvailable( RingBuffer *rbuf ); -/* Return bytes written. */ -long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ); -/* Return bytes read. */ -long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ); - -/* Get address of region(s) to which we can write data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or numBytes, whichever is smaller. -*/ -long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, - void **dataPtr1, long *sizePtr1, - void **dataPtr2, long *sizePtr2 ); -long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ); - -/* Get address of region(s) from which we can read data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or numBytes, whichever is smaller. -*/ -long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, - void **dataPtr1, long *sizePtr1, - void **dataPtr2, long *sizePtr2 ); - -long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _RINGBUFFER_H */ diff --git a/portaudio/pablio/test_rw.c b/portaudio/pablio/test_rw.c deleted file mode 100644 index 27a94b433..000000000 --- a/portaudio/pablio/test_rw.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * $Id: test_rw.c,v 1.2 2002/02/22 22:06:23 philburk Exp $ - * test_rw.c - * Read input from one stream and write it to another. - * - * Author: Phil Burk, http://www.softsynth.com/portaudio/ - * - * This program uses PABLIO, the Portable Audio Blocking I/O Library. - * PABLIO is built on top of PortAudio, the Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include "pablio.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 NUM_SECONDS (5) -#define SAMPLES_PER_FRAME (2) -#define FRAMES_PER_BLOCK (64) - -/* Select whether we will use floats or shorts. */ -#if 1 -#define SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#else -#define SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#endif - -/*******************************************************************/ -int main(void); -int main(void) -{ - int i; - SAMPLE samples[SAMPLES_PER_FRAME * FRAMES_PER_BLOCK]; - PaError err; - PABLIO_Stream *aStream; - - printf("Full duplex sound test using PortAudio and RingBuffers\n"); - fflush(stdout); - - /* Open simplified blocking I/O layer on top of PortAudio. */ - err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE, - (PABLIO_READ_WRITE | PABLIO_STEREO) ); - if( err != paNoError ) goto error; - - /* Process samples in the foreground. */ - for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) - { - /* Read one block of data into sample array from audio input. */ - ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK ); - /* Write that same block of data to output. */ - WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK ); - } - - CloseAudioStream( aStream ); - - printf("Full duplex sound test complete.\n" ); - fflush(stdout); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} diff --git a/portaudio/pablio/test_rw_echo.c b/portaudio/pablio/test_rw_echo.c deleted file mode 100644 index 7bc4e9b41..000000000 --- a/portaudio/pablio/test_rw_echo.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * $Id: test_rw_echo.c,v 1.1.1.1 2002/01/22 00:52:54 phil Exp $ - * test_rw_echo.c - * Echo delayed input to output. - * - * Author: Phil Burk, http://www.softsynth.com/portaudio/ - * - * This program uses PABLIO, the Portable Audio Blocking I/O Library. - * PABLIO is built on top of PortAudio, the Portable Audio Library. - * - * Note that if you need low latency, you should not use PABLIO. - * Use the PA_OpenStream callback technique which is lower level - * than PABLIO. - * - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "pablio.h" -#include <string.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 (22050) -#define NUM_SECONDS (20) -#define SAMPLES_PER_FRAME (2) - -/* Select whether we will use floats or shorts. */ -#if 1 -#define SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#else -#define SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#endif - -#define NUM_ECHO_FRAMES (2*SAMPLE_RATE) -SAMPLE samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0}; - -/*******************************************************************/ -int main(void); -int main(void) -{ - int i; - PaError err; - PABLIO_Stream *aInStream; - PABLIO_Stream *aOutStream; - int index; - - printf("Full duplex sound test using PABLIO\n"); - fflush(stdout); - - /* Open simplified blocking I/O layer on top of PortAudio. */ - /* Open input first so it can start to fill buffers. */ - err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE, - (PABLIO_READ | PABLIO_STEREO) ); - if( err != paNoError ) goto error; - /* printf("opened input\n"); fflush(stdout); /**/ - - err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE, - (PABLIO_WRITE | PABLIO_STEREO) ); - if( err != paNoError ) goto error; - /* printf("opened output\n"); fflush(stdout); /**/ - - /* Process samples in the foreground. */ - index = 0; - for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ ) - { - /* Write old frame of data to output. */ - /* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */ - WriteAudioStream( aOutStream, &samples[index][0], 1 ); - - /* Read one frame of data into sample array for later output. */ - ReadAudioStream( aInStream, &samples[index][0], 1 ); - index += 1; - if( index >= NUM_ECHO_FRAMES ) index = 0; - - if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/ - } - - CloseAudioStream( aOutStream ); - CloseAudioStream( aInStream ); - - printf("R/W echo sound test complete.\n" ); - fflush(stdout); - return 0; - -error: - fprintf( stderr, "An error occured while using PortAudio\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} diff --git a/portaudio/pablio/test_w_saw.c b/portaudio/pablio/test_w_saw.c deleted file mode 100644 index f333cff17..000000000 --- a/portaudio/pablio/test_w_saw.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * $Id: test_w_saw.c,v 1.1.1.1 2002/01/22 00:52:54 phil Exp $ - * test_w_saw.c - * Generate stereo sawtooth waveforms. - * - * Author: Phil Burk, http://www.softsynth.com - * - * This program uses PABLIO, the Portable Audio Blocking I/O Library. - * PABLIO is built on top of PortAudio, the Portable Audio Library. - * - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "pablio.h" -#include <string.h> - -#define SAMPLE_RATE (44100) -#define NUM_SECONDS (6) -#define SAMPLES_PER_FRAME (2) - -#define FREQUENCY (220.0f) -#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE) -#define FRAMES_PER_BLOCK (100) - -float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME]; -float phases[SAMPLES_PER_FRAME]; - -/*******************************************************************/ -int main(void); -int main(void) -{ - int i,j; - PaError err; - PABLIO_Stream *aOutStream; - - printf("Generate sawtooth waves using PABLIO.\n"); - fflush(stdout); - - /* Open simplified blocking I/O layer on top of PortAudio. */ - err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32, - (PABLIO_WRITE | PABLIO_STEREO) ); - if( err != paNoError ) goto error; - - /* Initialize oscillator phases. */ - phases[0] = 0.0; - phases[1] = 0.0; - - for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) - { - /* Generate sawtooth waveforms in a block for efficiency. */ - for( j=0; j<FRAMES_PER_BLOCK; j++ ) - { - /* Generate a sawtooth wave by incrementing a variable. */ - phases[0] += PHASE_INCREMENT; - /* The signal range is -1.0 to +1.0 so wrap around if we go over. */ - if( phases[0] > 1.0f ) phases[0] -= 2.0f; - samples[j][0] = phases[0]; - - /* On the second channel, generate a sawtooth wave a fifth higher. */ - phases[1] += PHASE_INCREMENT * (3.0f / 2.0f); - if( phases[1] > 1.0f ) phases[1] -= 2.0f; - samples[j][1] = phases[1]; - } - - /* Write samples to output. */ - WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK ); - } - - CloseAudioStream( aOutStream ); - - printf("Sawtooth sound test complete.\n" ); - fflush(stdout); - return 0; - -error: - fprintf( stderr, "An error occured while using PABLIO\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} diff --git a/portaudio/pablio/test_w_saw8.c b/portaudio/pablio/test_w_saw8.c deleted file mode 100644 index 0f7e02e34..000000000 --- a/portaudio/pablio/test_w_saw8.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * $Id: test_w_saw8.c,v 1.1.1.1 2002/01/22 00:52:55 phil Exp $ - * test_w_saw8.c - * Generate stereo 8 bit sawtooth waveforms. - * - * Author: Phil Burk, http://www.softsynth.com - * - * This program uses PABLIO, the Portable Audio Blocking I/O Library. - * PABLIO is built on top of PortAudio, the Portable Audio Library. - * - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> -#include "pablio.h" -#include <string.h> - -#define SAMPLE_RATE (22050) -#define NUM_SECONDS (6) -#define SAMPLES_PER_FRAME (2) - - -#define FRAMES_PER_BLOCK (100) - -unsigned char samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME]; -unsigned char phases[SAMPLES_PER_FRAME]; - -/*******************************************************************/ -int main(void); -int main(void) -{ - int i,j; - PaError err; - PABLIO_Stream *aOutStream; - - printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n"); - fflush(stdout); - - /* Open simplified blocking I/O layer on top of PortAudio. */ - err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8, - (PABLIO_WRITE | PABLIO_STEREO) ); - if( err != paNoError ) goto error; - - /* Initialize oscillator phases to "ground" level for paUInt8. */ - phases[0] = 128; - phases[1] = 128; - - for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) - { - /* Generate sawtooth waveforms in a block for efficiency. */ - for( j=0; j<FRAMES_PER_BLOCK; j++ ) - { - /* Generate a sawtooth wave by incrementing a variable. */ - phases[0] += 1; - /* We don't have to do anything special to wrap when using paUint8 because - * 8 bit arithmetic automatically wraps. */ - samples[j][0] = phases[0]; - - /* On the second channel, generate a higher sawtooth wave. */ - phases[1] += 3; - samples[j][1] = phases[1]; - } - - /* Write samples to output. */ - WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK ); - } - - CloseAudioStream( aOutStream ); - - printf("Sawtooth sound test complete.\n" ); - fflush(stdout); - return 0; - -error: - fprintf( stderr, "An error occured while using PABLIO\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} diff --git a/portmidi/Makefile b/portmidi/Makefile deleted file mode 100644 index 7a87606d3..000000000 --- a/portmidi/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# MAKEFILE FOR PORTMIDI AND PORTTIME - - -# For debugging, define PM_CHECK_ERRORS -PMFLAGS = -DPM_CHECK_ERRORS -# Otherwise do not define PM_CHECK_ERRORS -# PMFLAGS = - -# Use this for linux alsa (0.9x) version -versions = pm_linux/pmlinuxalsa.o -ALSALIB = -lasound -VFLAGS = -DPMALSA - -# Use this for null (a dummy implementation for no Midi I/O: -# versions = pmlinuxnull.o -# ALSALIB = -# VFLAGS = -DPMNULL - -pmlib = pm_linux/libportmidi.a - -ptlib = porttime/libporttime.a - -CC = gcc $(VFLAGS) $(PMFLAGS) -g -Ipm_common -Iporttime - -pmobjects = pm_common/pmutil.o $(versions) pm_linux/pmlinux.o \ - pm_common/portmidi.o pm_linux/pmlinuxalsa.o - -ptobjects = porttime/porttime.o porttime/ptlinux.o - -current: all - -all: $(pmlib) $(ptlib) pm_test/test pm_test/sysex pm_test/midithread \ - pm_test/latency pm_test/midithru - -$(pmlib): Makefile $(pmobjects) - ar -cr $(pmlib) $(pmobjects) - -$(ptlib): Makefile $(ptobjects) - ar -cr $(ptlib) $(ptobjects) - -pm_linux/pmlinuxalsa.o: Makefile pm_linux/pmlinuxalsa.c pm_linux/pmlinuxalsa.h - $(CC) -c pm_linux/pmlinuxalsa.c -o pm_linux/pmlinuxalsa.o - -pm_test/test: Makefile pm_test/test.o $(pmlib) $(ptlib) - $(CC) pm_test/test.c -o pm_test/test $(pmlib) $(ptlib) $(ALSALIB) - -pm_test/sysex: Makefile pm_test/sysex.o $(pmlib) $(ptlib) - $(CC) pm_test/sysex.c -o pm_test/sysex $(pmlib) $(ptlib) $(ALSALIB) - -pm_test/midithread: Makefile pm_test/midithread.o $(pmlib) $(ptlib) - $(CC) pm_test/midithread.c -o pm_test/midithread \ - $(pmlib) $(ptlib) $(ALSALIB) - -pm_test/latency: Makefile $(ptlib) pm_test/latency.o - $(CC) pm_test/latency.c -o pm_test/latency $(pmlib) $(ptlib) \ - $(ALSALIB) -lpthread -lm - -pm_test/midithru: Makefile $(ptlib) pm_test/midithru.o - $(CC) pm_test/midithru.c -o pm_test/midithru $(pmlib) $(ptlib) \ - $(ALSALIB) -lpthread -lm - -porttime/ptlinux.o: Makefile porttime/ptlinux.c - $(CC) -c porttime/ptlinux.c -o porttime/ptlinux.o - -clean: - rm -f *.o *~ core* */*.o */*~ */core* pm_test/*/pm_dll.dll - rm -f *.opt *.ncb *.plg pm_win/Debug/pm_dll.lib pm_win/Release/pm_dll.lib - rm -f pm_test/*.opt pm_test/*.ncb - -cleaner: clean - -cleanest: cleaner - rm -f $(pmlib) $(ptlib) pm_test/test pm_test/sysex pm_test/midithread - rm -f pm_test/latency pm_test/midithru - -backup: cleanest - cd ..; zip -r portmidi.zip portmidi diff --git a/portmidi/README.txt b/portmidi/README.txt deleted file mode 100644 index 76412efd0..000000000 --- a/portmidi/README.txt +++ /dev/null @@ -1,62 +0,0 @@ -README for PortMidi -Roger Dannenberg -6 April 2003 -revised May 2004 - -For Windows, please see also README_WIN.txt and debugging_dlls.txt -in pm_win. - -For Linux, please see also README_LINUX.txt in pm_linux. - -POINTERS VS DEVICE NUMBERS - -When you open a MIDI port, PortMidi allocates a structure to -maintain the state of the open device. Since every device is -also listed in a table, you might think it would be simpler to -use the table index rather than a pointer to identify a device. -This would also help with error checking (it's hard to make -sure a pointer is valid). PortMidi's design parallels that of -PortAudio. - -ERROR HANDLING - -Error handling turned out to be much more complicated than expected. -PortMidi functions return error codes that the caller can check. -In addition, errors may occur asynchronously due to MIDI input. In -this case, the error code is transferred to the next call to -Pm_Read or Pm_Write. Furthermore, an error can arise during a MIDI THRU -operation that is also invoked as a side effect of polling for input. - -Ordinarily, the caller checks for an error code. If the error is -system-dependent, pmHostError is returned and the caller can -call Pm_GetHostErrorText to get a text description of the error. - -Host errors are recorded in the system-specific data allocated for -each open MIDI port. However, if an error occurs on open or close, -we cannot store the error with the device because there will be -no device data (assuming PortMidi cleans up after devices that -are not open). For open and close, we will store the host error -in a global variable. The PortMidi is smart enough to look here -first when the user asks for ErrorText. - -Because output to a MIDI Thru stream can be invoked as a side-effect -of a MIDI read operation, some errors normally associated with -writing MIDI can be returned from Pm_Read. - -DEBUGGING - -If you are building a console application for research, we suggest -compiling with the option PM_CHECK_ERRORS. This will insert a -check for error return values at the end of each PortMidi -function. If an error is encountered, a text message is printed -using printf(), the user is asked to type ENTER, and then exit(-1) -is called to clean up and terminate the program. - -You should not use PM_CHECK_ERRORS if printf() does not work -(e.g. this is not a console application under Windows, or there -is no visible console on some other OS), and you should not use -PM_CHECK_ERRORS if you intend to recover from errors rather than -abruptly terminate the program. - -The Windows version (and perhaps others) also offers a DEBUG -compile-time option. See README_WIN.txt. diff --git a/portmidi/pm_common/pminternal.h b/portmidi/pm_common/pminternal.h deleted file mode 100644 index 20677308b..000000000 --- a/portmidi/pm_common/pminternal.h +++ /dev/null @@ -1,173 +0,0 @@ -/* pminternal.h -- header for interface implementations */ - -/* this file is included by files that implement library internals */ -/* Here is a guide to implementers: - provide an initialization function similar to pm_winmm_init() - add your initialization function to pm_init() - Note that your init function should never require not-standard - libraries or fail in any way. If the interface is not available, - simply do not call pm_add_device. This means that non-standard - libraries should try to do dynamic linking at runtime using a DLL - and return without error if the DLL cannot be found or if there - is any other failure. - implement functions as indicated in pm_fns_type to open, read, write, - close, etc. - call pm_add_device() for each input and output device, passing it a - pm_fns_type structure. - assumptions about pm_fns_type functions are given below. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* these are defined in system-specific file */ -void *pm_alloc(size_t s); -void pm_free(void *ptr); - -/* if an error occurs while opening or closing a midi stream, set these: */ -extern int pm_hosterror; -extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; - -struct pm_internal_struct; - -/* these do not use PmInternal because it is not defined yet... */ -typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi, - PmEvent *buffer); -typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi, - PmTimestamp timestamp); -typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi, - PmTimestamp timestamp); -typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi, - unsigned char byte, PmTimestamp timestamp); -typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi, - PmEvent *buffer); -typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi); -typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi); -/* pm_open_fn should clean up all memory and close the device if any part - of the open fails */ -typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi, - void *driverInfo); -typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi); -/* pm_close_fn should clean up all memory and close the device if any - part of the close fails. */ -typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi); -typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi); -typedef void (*pm_host_error_fn)(struct pm_internal_struct *midi, char * msg, - unsigned int len); -typedef unsigned int (*pm_has_host_error_fn)(struct pm_internal_struct *midi); - -typedef struct { - pm_write_short_fn write_short; /* output short MIDI msg */ - pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */ - pm_end_sysex_fn end_sysex; /* marks end of sysex message */ - pm_write_byte_fn write_byte; /* accumulate one more sysex byte */ - pm_write_realtime_fn write_realtime; /* send real-time message within sysex */ - pm_write_flush_fn write_flush; /* send any accumulated but unsent data */ - pm_synchronize_fn synchronize; /* synchronize portmidi time to stream time */ - pm_open_fn open; /* open MIDI device */ - pm_abort_fn abort; /* abort */ - pm_close_fn close; /* close device */ - pm_poll_fn poll; /* read pending midi events into portmidi buffer */ - pm_has_host_error_fn has_host_error; /* true when device has had host - error message */ - pm_host_error_fn host_error; /* provide text readable host error message - for device (clears and resets) */ -} pm_fns_node, *pm_fns_type; - - -/* when open fails, the dictionary gets this set of functions: */ -extern pm_fns_node pm_none_dictionary; - -typedef struct { - PmDeviceInfo pub; /* some portmidi state also saved in here (for autmatic - device closing (see PmDeviceInfo struct) */ - void *descriptor; /* ID number passed to win32 multimedia API open */ - void *internalDescriptor; /* points to PmInternal device, allows automatic - device closing */ - pm_fns_type dictionary; -} descriptor_node, *descriptor_type; - -extern int pm_descriptor_max; -extern descriptor_type descriptors; -extern int pm_descriptor_index; - -typedef unsigned long (*time_get_proc_type)(void *time_info); - -typedef struct pm_internal_struct { - int device_id; /* which device is open (index to descriptors) */ - short write_flag; /* MIDI_IN, or MIDI_OUT */ - - PmTimeProcPtr time_proc; /* where to get the time */ - void *time_info; /* pass this to get_time() */ - - long buffer_len; /* how big is the buffer */ - PmEvent *buffer; /* storage for: - - midi input - - midi output w/latency != 0 */ - long head; - long tail; - - long latency; /* time delay in ms between timestamps and actual output */ - /* set to zero to get immediate, simple blocking output */ - /* if latency is zero, timestamps will be ignored; */ - /* if midi input device, this field ignored */ - - int overflow; /* set to non-zero if input is dropped */ - int flush; /* flag to drop incoming sysex data because of overflow */ - int sysex_in_progress; /* use for overflow management */ - PmMessage sysex_message; /* buffer for 4 bytes of sysex data */ - int sysex_message_count; /* how many bytes in sysex_message so far */ - - long filters; /* flags that filter incoming message classes */ - int channel_mask; /* filter incoming messages based on channel */ - PmTimestamp last_msg_time; /* timestamp of last message */ - PmTimestamp sync_time; /* time of last synchronization */ - PmTimestamp now; /* set by PmWrite to current time */ - int first_message; /* initially true, used to run first synchronization */ - pm_fns_type dictionary; /* implementation functions */ - void *descriptor; /* system-dependent state */ - -} PmInternal; - -typedef struct { - long head; - long tail; - long len; - long msg_size; - long overflow; - char *buffer; -} PmQueueRep; - -/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */ -void pm_init(void); -void pm_term(void); - -/* defined by portMidi, used by pmwinmm */ -PmError none_write_short(PmInternal *midi, PmEvent *buffer); -PmError none_sysex(PmInternal *midi, PmTimestamp timestamp); -PmError none_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp); -PmTimestamp none_synchronize(PmInternal *midi); - -PmError pm_fail_fn(PmInternal *midi); -PmError pm_success_fn(PmInternal *midi); -PmError pm_add_device(char *interf, char *name, int input, void *descriptor, - pm_fns_type dictionary); -void pm_read_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp); -void pm_begin_sysex(PmInternal *midi); -void pm_end_sysex(PmInternal *midi); -void pm_read_short(PmInternal *midi, PmEvent *event); - -#define none_write_flush pm_fail_fn -#define none_poll pm_fail_fn -#define success_poll pm_success_fn - -#define MIDI_REALTIME_MASK 0xf8 -#define is_real_time(msg) \ - ((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK) - -#ifdef __cplusplus -} -#endif - diff --git a/portmidi/pm_common/pmutil.c b/portmidi/pm_common/pmutil.c deleted file mode 100644 index 1178b80b9..000000000 --- a/portmidi/pm_common/pmutil.c +++ /dev/null @@ -1,132 +0,0 @@ -/* pmutil.c -- some helpful utilities for building midi - applications that use PortMidi - */ -#include "stdlib.h" -#include "memory.h" -#include "portmidi.h" -#include "pmutil.h" -#include "pminternal.h" - - -PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg) -{ - PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); - - /* arg checking */ - if (!queue) - return NULL; - - queue->len = num_msgs * bytes_per_msg; - queue->buffer = pm_alloc(queue->len); - if (!queue->buffer) { - pm_free(queue); - return NULL; - } - queue->head = 0; - queue->tail = 0; - queue->msg_size = bytes_per_msg; - queue->overflow = FALSE; - return queue; -} - - -PmError Pm_QueueDestroy(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - - /* arg checking */ - if (!queue || !queue->buffer) - return pmBadPtr; - - pm_free(queue->buffer); - pm_free(queue); - return pmNoError; -} - - -PmError Pm_Dequeue(PmQueue *q, void *msg) -{ - long head; - PmQueueRep *queue = (PmQueueRep *) q; - - /* arg checking */ - if(!queue) - return pmBadPtr; - - if (queue->overflow) { - queue->overflow = FALSE; - return pmBufferOverflow; - } - - head = queue->head; /* make sure this is written after access */ - if (head == queue->tail) return 0; - memcpy(msg, queue->buffer + head, queue->msg_size); - head += queue->msg_size; - if (head == queue->len) head = 0; - queue->head = head; - return 1; /* success */ -} - - -/* source should not enqueue data if overflow is set */ -/**/ -PmError Pm_Enqueue(PmQueue *q, void *msg) -{ - PmQueueRep *queue = (PmQueueRep *) q; - long tail; - - /* arg checking */ - if (!queue) - return pmBadPtr; - - tail = queue->tail; - memcpy(queue->buffer + tail, msg, queue->msg_size); - tail += queue->msg_size; - if (tail == queue->len) tail = 0; - if (tail == queue->head) { - queue->overflow = TRUE; - /* do not update tail, so message is lost */ - return pmBufferOverflow; - } - queue->tail = tail; - return pmNoError; -} - -int Pm_QueueEmpty(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - if (!queue) return TRUE; - return (queue->head == queue->tail); -} - -int Pm_QueueFull(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - long tail; - - /* arg checking */ - if(!queue) - return pmBadPtr; - - tail = queue->tail; - tail += queue->msg_size; - if (tail == queue->len) { - tail = 0; - } - return (tail == queue->head); -} - -void *Pm_QueuePeek(PmQueue *q) -{ - long head; - PmQueueRep *queue = (PmQueueRep *) q; - - /* arg checking */ - if(!queue) - return NULL; - - head = queue->head; /* make sure this is written after access */ - if (head == queue->tail) return NULL; - return queue->buffer + head; -} - diff --git a/portmidi/pm_common/pmutil.h b/portmidi/pm_common/pmutil.h deleted file mode 100644 index a7fb3ebfa..000000000 --- a/portmidi/pm_common/pmutil.h +++ /dev/null @@ -1,56 +0,0 @@ -/* pmutil.h -- some helpful utilities for building midi - applications that use PortMidi - */ - -typedef void PmQueue; - -/* - A single-reader, single-writer queue is created by - Pm_QueueCreate(), which takes the number of messages and - the message size as parameters. The queue only accepts - fixed sized messages. Returns NULL if memory cannot be allocated. - - Pm_QueueDestroy() destroys the queue and frees its storage. - */ - -PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg); -PmError Pm_QueueDestroy(PmQueue *queue); - -/* - Pm_Dequeue() removes one item from the queue, copying it into msg. - Returns 1 if successful, and 0 if the queue is empty. - Returns pmBufferOverflow, clears the overflow flag, and does not - return a data item if the overflow flag is set. (This protocol - ensures that the reader will be notified when data is lost due - to overflow.) - */ -PmError Pm_Dequeue(PmQueue *queue, void *msg); - - -/* - Pm_Enqueue() inserts one item into the queue, copying it from msg. - Returns pmNoError if successful and pmBufferOverflow if the queue was - already full. If pmBufferOverflow is returned, the overflow flag is set. - */ -PmError Pm_Enqueue(PmQueue *queue, void *msg); - - -/* - Pm_QueueFull() returns non-zero if the queue is full - Pm_QueueEmpty() returns non-zero if the queue is empty - - Either condition may change immediately because a parallel - enqueue or dequeue operation could be in progress. - */ -int Pm_QueueFull(PmQueue *queue); -int Pm_QueueEmpty(PmQueue *queue); - - -/* - Pm_QueuePeek() returns a pointer to the item at the head of the queue, - or NULL if the queue is empty. The item is not removed from the queue. - If queue is in an overflow state, a valid pointer is returned and the - queue remains in the overflow state. - */ -void *Pm_QueuePeek(PmQueue *queue); - diff --git a/portmidi/pm_common/portmidi.c b/portmidi/pm_common/portmidi.c deleted file mode 100644 index e1b962d75..000000000 --- a/portmidi/pm_common/portmidi.c +++ /dev/null @@ -1,980 +0,0 @@ -#include "stdlib.h" -#include "string.h" -#include "portmidi.h" -#include "porttime.h" -#include "pminternal.h" -#include <assert.h> - -#define MIDI_CLOCK 0xf8 -#define MIDI_ACTIVE 0xfe -#define MIDI_STATUS_MASK 0x80 -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 -#define MIDI_START 0xFA -#define MIDI_STOP 0xFC -#define MIDI_CONTINUE 0xFB -#define MIDI_F9 0xF9 -#define MIDI_FD 0xFD -#define MIDI_RESET 0xFF -#define MIDI_NOTE_ON 0x90 -#define MIDI_NOTE_OFF 0x80 -#define MIDI_CHANNEL_AT 0xD0 -#define MIDI_POLY_AT 0xA0 -#define MIDI_PROGRAM 0xC0 -#define MIDI_CONTROL 0xB0 -#define MIDI_PITCHBEND 0xE0 -#define MIDI_MTC 0xF1 -#define MIDI_SONGPOS 0xF2 -#define MIDI_SONGSEL 0xF3 -#define MIDI_TUNE 0xF6 - -#define is_empty(midi) ((midi)->tail == (midi)->head) - -static int pm_initialized = FALSE; -int pm_hosterror = FALSE; -char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; - -#ifdef PM_CHECK_ERRORS - -#include <stdio.h> - -#define STRING_MAX 80 - -static void prompt_and_exit(void) -{ - char line[STRING_MAX]; - printf("type ENTER..."); - fgets(line, STRING_MAX, stdin); - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError pm_errmsg(PmError err) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - printf("PortMidi found host error...\n %s\n", pm_hosterror_text); - pm_hosterror = FALSE; - pm_hosterror_text[0] = 0; /* clear the message */ - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} -#else -#define pm_errmsg(err) err -#endif - -/* -==================================================================== -system implementation of portmidi interface -==================================================================== -*/ - -int pm_descriptor_max = 0; -int pm_descriptor_index = 0; -descriptor_type descriptors = NULL; - -/* pm_add_device -- describe interface/device pair to library - * - * This is called at intialization time, once for each - * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) - * The strings are retained but NOT COPIED, so do not destroy them! - * - * returns pmInvalidDeviceId if device memory is exceeded - * otherwise returns pmNoError - */ -PmError pm_add_device(char *interf, char *name, int input, - void *descriptor, pm_fns_type dictionary) { - if (pm_descriptor_index >= pm_descriptor_max) { - // expand descriptors - descriptor_type new_descriptors = - pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32)); - if (!new_descriptors) return pmInsufficientMemory; - if (descriptors) { - memcpy(new_descriptors, descriptors, - sizeof(descriptor_node) * pm_descriptor_max); - free(descriptors); - } - pm_descriptor_max += 32; - descriptors = new_descriptors; - } - descriptors[pm_descriptor_index].pub.interf = interf; - descriptors[pm_descriptor_index].pub.name = name; - descriptors[pm_descriptor_index].pub.input = input; - descriptors[pm_descriptor_index].pub.output = !input; - - /* default state: nothing to close (for automatic device closing) */ - descriptors[pm_descriptor_index].pub.opened = FALSE; - - /* ID number passed to win32 multimedia API open */ - descriptors[pm_descriptor_index].descriptor = descriptor; - - /* points to PmInternal, allows automatic device closing */ - descriptors[pm_descriptor_index].internalDescriptor = NULL; - - descriptors[pm_descriptor_index].dictionary = dictionary; - - pm_descriptor_index++; - - return pmNoError; -} - - -/* -==================================================================== -portmidi implementation -==================================================================== -*/ - -int Pm_CountDevices( void ) -{ - PmError err = Pm_Initialize(); - if (err) - return pm_errmsg(err); - return pm_descriptor_index; -} - - -const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) -{ - PmError err = Pm_Initialize(); - if (err) - return NULL; - if (id >= 0 && id < pm_descriptor_index) { - return &descriptors[id].pub; - } - return NULL; -} - -/* pm_success_fn -- "noop" function pointer */ -PmError pm_success_fn(PmInternal *midi) { - return pmNoError; -} - -/* none_write -- returns an error if called */ -PmError none_write_short(PmInternal *midi, PmEvent *buffer) -{ - return pmBadPtr; -} - -/* none_sysex -- placeholder for begin_sysex and end_sysex */ -PmError none_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - return pmBadPtr; -} - -PmError none_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - return pmBadPtr; -} - -/* pm_fail_fn -- generic function, returns error if called */ -PmError pm_fail_fn(PmInternal *midi) -{ - return pmBadPtr; -} - -static PmError none_open(PmInternal *midi, void *driverInfo) -{ - return pmBadPtr; -} -static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) { - strcpy(msg,""); -} -static unsigned int none_has_host_error(PmInternal * midi) { - return FALSE; -} -PmTimestamp none_synchronize(PmInternal *midi) { - return 0; -} - -#define none_abort pm_fail_fn -#define none_close pm_fail_fn - -pm_fns_node pm_none_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - none_synchronize, - none_open, - none_abort, - none_close, - none_poll, - none_has_host_error, - none_get_host_error -}; - - -const char *Pm_GetErrorText( PmError errnum ) { - const char *msg; - - switch(errnum) - { - case pmNoError: - msg = ""; - break; - case pmHostError: - msg = "PortMidi: `Host error'"; - break; - case pmInvalidDeviceId: - msg = "PortMidi: `Invalid device ID'"; - break; - case pmInsufficientMemory: - msg = "PortMidi: `Insufficient memory'"; - break; - case pmBufferTooSmall: - msg = "PortMidi: `Buffer too small'"; - break; - case pmBadPtr: - msg = "PortMidi: `Bad pointer'"; - break; - case pmInternalError: - msg = "PortMidi: `Internal PortMidi Error'"; - break; - case pmBufferOverflow: - msg = "PortMidi: `Buffer overflow'"; - break; - case pmBadData: - msg = "PortMidi: `Invalid MIDI message Data'"; - default: - msg = "PortMidi: `Illegal error number'"; - break; - } - return msg; -} - - -/* This can be called whenever you get a pmHostError return value. - * The error will always be in the global pm_hosterror_text. - */ -void Pm_GetHostErrorText(char * msg, unsigned int len) { - assert(msg); - assert(len > 0); - if (pm_hosterror) { /* we have the string already from open or close */ - strncpy(msg, (char *) pm_hosterror_text, len); - pm_hosterror = FALSE; - pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it - might help with debugging */ - msg[len - 1] = 0; /* make sure string is terminated */ - } else { - msg[0] = 0; /* no string to return */ - } -} - - -int Pm_HasHostError(PortMidiStream * stream) { - if (stream) { - PmInternal * midi = (PmInternal *) stream; - pm_hosterror = (*midi->dictionary->has_host_error)(midi); - if (pm_hosterror) { - midi->dictionary->host_error(midi, pm_hosterror_text, - PM_HOST_ERROR_MSG_LEN); - /* now error message is global */ - return TRUE; - } - } - return FALSE; -} - - -PmError Pm_Initialize( void ) { - pm_hosterror_text[0] = 0; /* the null string */ - if (!pm_initialized) { - pm_init(); - pm_initialized = TRUE; - } - return pmNoError; -} - - -PmError Pm_Terminate( void ) { - if (pm_initialized) { - pm_term(); - pm_initialized = FALSE; - } - return pmNoError; -} - - -/* Pm_Read -- read up to length longs from source into buffer */ -/* - returns number of longs actually read, or error code - - When the reader wants data: - if overflow_flag: - do not get anything - empty the buffer (read_ptr = write_ptr) - clear overflow_flag - return pmBufferOverflow - get data - return number of messages -*/ -PmError Pm_Read(PortMidiStream *stream, PmEvent *buffer, long length) { - PmInternal *midi = (PmInternal *) stream; - int n = 0; - long head; - PmError err = pmNoError; - - /* arg checking */ - if(midi == NULL) - err = pmBadPtr; - else if(Pm_HasHostError(midi)) - err = pmHostError; - else if(!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else if(!descriptors[midi->device_id].pub.input) - err = pmBadPtr; - - /* First poll for data in the buffer... - * This either simply checks for data, or attempts first to fill the buffer - * with data from the MIDI hardware; this depends on the implementation. - * We could call Pm_Poll here, but that would redo a lot of redundant - * parameter checking, so I copied some code from Pm_Poll to here: */ - else err = (*(midi->dictionary->poll))(midi); - - if (err != pmNoError) { - return pm_errmsg(err); - } - - head = midi->head; - while (head != midi->tail && n < length) { - PmEvent event = midi->buffer[head++]; - *buffer++ = event; - if (head == midi->buffer_len) head = 0; - n++; - } - midi->head = head; - if (midi->overflow) { - midi->head = midi->tail; - midi->overflow = FALSE; - return pm_errmsg(pmBufferOverflow); - } - return n; -} - -PmError Pm_Poll( PortMidiStream *stream ) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err; - - /* arg checking */ - if(midi == NULL) - err = pmBadPtr; - else if(Pm_HasHostError(midi)) - err = pmHostError; - else if(!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else if(!descriptors[midi->device_id].pub.input) - err = pmBadPtr; - else - err = (*(midi->dictionary->poll))(midi); - - if (err != pmNoError) - return pm_errmsg(err); - else - return midi->head != midi->tail; -} - -/* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and - Pm_WriteSysEx all operate a state machine that "outputs" calls to - write_short, begin_sysex, write_byte, end_sysex, and write_realtime */ - -PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err; - int i; - int bits; - - /* arg checking */ - if(midi == NULL) - err = pmBadPtr; - else if(Pm_HasHostError(midi)) - err = pmHostError; - else if(!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else if(!descriptors[midi->device_id].pub.output) - err = pmBadPtr; - else - err = pmNoError; - - if (err != pmNoError) goto pm_write_error; - - if (midi->latency == 0) { - midi->now = 0; - } else { - midi->now = (*(midi->time_proc))(midi->time_info); - if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) { - /* time to resync */ - midi->now = (*midi->dictionary->synchronize)(midi); - midi->first_message = FALSE; - } - } - - for (i = 0; i < length; i++) { - unsigned long msg = buffer[i].message; - bits = 0; - /* is this a sysex message? */ - if (Pm_MessageStatus(msg) == MIDI_SYSEX) { - if (midi->sysex_in_progress) { - /* error: previous sysex was not terminated by EOX */ - midi->sysex_in_progress = FALSE; - err = pmBadData; - goto pm_write_error; - } - midi->sysex_in_progress = TRUE; - if ((err = (*midi->dictionary->begin_sysex)(midi, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - bits = 8; - /* fall through to continue sysex processing */ - } else if ((msg & MIDI_STATUS_MASK) && - (Pm_MessageStatus(msg) != MIDI_EOX)) { - /* a non-sysex message */ - if (midi->sysex_in_progress) { - /* this should be a non-realtime message */ - if (is_real_time(msg)) { - if ((err = (*midi->dictionary->write_realtime)(midi, - &(buffer[i]))) != pmNoError) - goto pm_write_error; - } else { - midi->sysex_in_progress = FALSE; - err = pmBadData; - /* ignore any error from this, because we already have one */ - /* pass 0 as timestamp -- it's ignored */ - (*midi->dictionary->end_sysex)(midi, 0); - goto pm_write_error; - } - } else { /* regular short midi message */ - if ((err = (*midi->dictionary->write_short)(midi, - &(buffer[i]))) != pmNoError) - goto pm_write_error; - continue; - } - } - if (midi->sysex_in_progress) { /* send sysex bytes until EOX */ - while (bits < 32) { - unsigned char midi_byte = (unsigned char) (msg >> bits); - if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - if (midi_byte == MIDI_EOX) { - midi->sysex_in_progress = FALSE; - if ((err = (*midi->dictionary->end_sysex)(midi, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - break; /* from while loop */ - } - bits += 8; - } - } else { - /* not in sysex mode, but message did not start with status */ - err = pmBadData; - goto pm_write_error; - } - } - /* after all messages are processed, send the data */ - err = (*midi->dictionary->write_flush)(midi); -pm_write_error: - return pm_errmsg(err); -} - - -PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg) -{ - PmEvent event; - - event.timestamp = when; - event.message = msg; - return Pm_Write(stream, &event, 1); -} - - -PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, - unsigned char *msg) -{ - /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */ - /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */ -#define BUFLEN (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage)) - PmEvent buffer[BUFLEN]; - /* the next byte in the buffer is represented by an index, bufx, and - a shift in bits */ - int shift = 0; - int bufx = 0; - buffer[0].message = 0; - buffer[0].timestamp = when; - - while (1) { - /* insert next byte into buffer */ - buffer[bufx].message |= ((*msg) << shift); - shift += 8; - if (shift == 32) { - shift = 0; - bufx++; - if (bufx == BUFLEN) { - PmError err = Pm_Write(stream, buffer, BUFLEN); - if (err) return err; - /* prepare to fill another buffer */ - bufx = 0; - } - buffer[bufx].message = 0; - buffer[bufx].timestamp = when; - } - /* keep inserting bytes until you find MIDI_EOX */ - if (*msg++ == MIDI_EOX) break; - } - - /* we're finished sending full buffers, but there may - * be a partial one left. - */ - if (shift != 0) bufx++; /* add partial message to buffer len */ - if (bufx) { /* bufx is number of PmEvents to send from buffer */ - return Pm_Write(stream, buffer, bufx); - } - return pmNoError; -} - - - -PmError Pm_OpenInput(PortMidiStream** stream, - PmDeviceID inputDevice, - void *inputDriverInfo, - long bufferSize, - PmTimeProcPtr time_proc, - void *time_info) -{ - PmInternal *midi; - PmError err = pmNoError; - pm_hosterror = FALSE; - *stream = NULL; - - /* arg checking */ - if (inputDevice < 0 || inputDevice >= pm_descriptor_index) - err = pmInvalidDeviceId; - else if (!descriptors[inputDevice].pub.input) - err = pmBadPtr; - else if(descriptors[inputDevice].pub.opened) - err = pmBadPtr; - - if (err != pmNoError) - goto error_return; - - /* create portMidi internal data */ - midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); - *stream = midi; - if (!midi) { - err = pmInsufficientMemory; - goto error_return; - } - midi->device_id = inputDevice; - midi->write_flag = FALSE; - midi->time_proc = time_proc; - midi->time_info = time_info; - /* windows adds timestamps in the driver and these are more accurate than - using a time_proc, so do not automatically provide a time proc. Non-win - implementations may want to provide a default time_proc in their - system-specific midi_out_open() method. - */ - if (bufferSize <= 0) bufferSize = 256; /* default buffer size */ - else bufferSize++; /* buffer holds N-1 msgs, so increase request by 1 */ - midi->buffer_len = bufferSize; /* portMidi input storage */ - midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len); - if (!midi->buffer) { - /* free portMidi data */ - *stream = NULL; - pm_free(midi); - err = pmInsufficientMemory; - goto error_return; - } - midi->head = 0; - midi->tail = 0; - midi->latency = 0; /* not used */ - midi->overflow = FALSE; - midi->flush = FALSE; - midi->sysex_in_progress = FALSE; - midi->sysex_message = 0; - midi->sysex_message_count = 0; - midi->filters = PM_FILT_ACTIVE; - midi->channel_mask = 0xFFFF; - midi->sync_time = 0; - midi->first_message = TRUE; - midi->dictionary = descriptors[inputDevice].dictionary; - descriptors[inputDevice].internalDescriptor = midi; - /* open system dependent input device */ - err = (*midi->dictionary->open)(midi, inputDriverInfo); - if (err) { - *stream = NULL; - descriptors[inputDevice].internalDescriptor = NULL; - /* free portMidi data */ - pm_free(midi->buffer); - pm_free(midi); - } else { - /* portMidi input open successful */ - descriptors[inputDevice].pub.opened = TRUE; - } -error_return: - return pm_errmsg(err); -} - - -PmError Pm_OpenOutput(PortMidiStream** stream, - PmDeviceID outputDevice, - void *outputDriverInfo, - long bufferSize, - PmTimeProcPtr time_proc, - void *time_info, - long latency) -{ - PmInternal *midi; - PmError err = pmNoError; - pm_hosterror = FALSE; - *stream = NULL; - - /* arg checking */ - if (outputDevice < 0 || outputDevice >= pm_descriptor_index) - err = pmInvalidDeviceId; - else if (!descriptors[outputDevice].pub.output) - err = pmBadPtr; - else if (descriptors[outputDevice].pub.opened) - err = pmBadPtr; - if (err != pmNoError) - goto error_return; - - /* create portMidi internal data */ - midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); - *stream = midi; - if (!midi) { - err = pmInsufficientMemory; - goto error_return; - } - midi->device_id = outputDevice; - midi->write_flag = TRUE; - midi->time_proc = time_proc; - /* if latency > 0, we need a time reference. If none is provided, - use PortTime library */ - if (time_proc == NULL && latency != 0) { - if (!Pt_Started()) - Pt_Start(1, 0, 0); - /* time_get does not take a parameter, so coerce */ - midi->time_proc = (PmTimeProcPtr) Pt_Time; - } - midi->time_info = time_info; - /* when stream used, this buffer allocated and used by - winmm_out_open; deleted by winmm_out_close */ - midi->buffer_len = bufferSize; - midi->buffer = NULL; - midi->head = 0; /* unused by output */ - midi->tail = 0; /* unused by output */ - /* if latency zero, output immediate (timestamps ignored) */ - /* if latency < 0, use 0 but don't return an error */ - if (latency < 0) latency = 0; - midi->latency = latency; - midi->overflow = FALSE; /* not used */ - midi->flush = FALSE; /* not used */ - midi->sysex_in_progress = FALSE; - midi->sysex_message = 0; /* unused by output */ - midi->sysex_message_count = 0; /* unused by output */ - midi->filters = 0; /* not used for output */ - midi->channel_mask = 0xFFFF; /* not used for output */ - midi->sync_time = 0; - midi->first_message = TRUE; - midi->dictionary = descriptors[outputDevice].dictionary; - descriptors[outputDevice].internalDescriptor = midi; - /* open system dependent output device */ - err = (*midi->dictionary->open)(midi, outputDriverInfo); - if (err) { - *stream = NULL; - descriptors[outputDevice].internalDescriptor = NULL; - /* free portMidi data */ - pm_free(midi); - } else { - /* portMidi input open successful */ - descriptors[outputDevice].pub.opened = TRUE; - } -error_return: - return pm_errmsg(err); -} - -PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - if (midi == NULL) - err = pmBadPtr; - else - midi->channel_mask = mask; - - return pm_errmsg(err); -} - -PmError Pm_SetFilter(PortMidiStream *stream, long filters) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - /* arg checking */ - if (midi == NULL) - err = pmBadPtr; - else if (!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else - midi->filters = filters; - return pm_errmsg(err); -} - - -PmError Pm_Close( PortMidiStream *stream ) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - /* arg checking */ - if (midi == NULL) /* midi must point to something */ - err = pmBadPtr; - /* if it is an open device, the device_id will be valid */ - else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index) - err = pmBadPtr; - /* and the device should be in the opened state */ - else if (!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - - if (err != pmNoError) - goto error_return; - - /* close the device */ - err = (*midi->dictionary->close)(midi); - /* even if an error occurred, continue with cleanup */ - descriptors[midi->device_id].internalDescriptor = NULL; - descriptors[midi->device_id].pub.opened = FALSE; - pm_free(midi->buffer); - pm_free(midi); -error_return: - return pm_errmsg(err); -} - - -PmError Pm_Abort( PortMidiStream* stream ) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err; - /* arg checking */ - if (midi == NULL) - err = pmBadPtr; - if (!descriptors[midi->device_id].pub.output) - err = pmBadPtr; - if (!descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else - err = (*midi->dictionary->abort)(midi); - return pm_errmsg(err); -} - -/* in win32 multimedia API (via callbacks) some of these functions used; assume never fail */ -long pm_next_time(PmInternal *midi) { - - /* arg checking */ - assert(midi != NULL); - assert(!Pm_HasHostError(midi)); - - return midi->buffer[midi->head].timestamp; -} -/* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */ -static int pm_channel_filtered(int status, int mask) -{ - if ((status & 0xF0) == 0xF0) /* 0xF? messages don't have a channel */ - return 0; - return !(Pm_Channel(status & 0x0F) & mask); - /* it'd be easier to return 0 for filtered, 1 for allowed, - but it would different from the other filtering functions - */ - -} -/* The following two functions will checks to see if a MIDI message matches - the filtering criteria. Since the sysex routines only want to filter realtime messages, - we need to have separate routines. - */ - -/* pm_realtime_filtered returns non-zero if the filter will kill the current message. - Note that only realtime messages are checked here. - */ -static int pm_realtime_filtered(int status, long filters) -{ - return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE)) - || ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK)) - || ((status == MIDI_START) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_STOP) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_F9) && (filters & PM_FILT_F9)) - || ((status == MIDI_FD) && (filters & PM_FILT_FD)) - || ((status == MIDI_RESET) && (filters & PM_FILT_RESET)) - || ((status == MIDI_MTC) && (filters & PM_FILT_MTC)) - || ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION)) - || ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT)) - || ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE)); -} -/* pm_status_filtered returns non-zero if a filter will kill the current message, based on status. - Note that sysex and real time are not checked. It is up to the subsystem (winmm, core midi, alsa) - to filter sysex, as it is handled more easily and efficiently at that level. - Realtime message are filtered in pm_realtime_filtered. - */ - -static int pm_status_filtered(int status, long filters) -{ - status &= 0xF0; /* remove channel information */ - return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE)) - || ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE)) - || ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH)) - || ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH)) - || ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM)) - || ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL)) - || ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND)); - -} - -/* pm_read_short and pm_read_byte - are the interface between system-dependent MIDI input handlers - and the system-independent PortMIDI code. - The input handler MUST obey these rules: - 1) all short input messages must be sent to pm_read_short, which - enqueues them to a FIFO for the application. - 2) eash sysex byte should be reported by calling pm_read_byte - (which sets midi->sysex_in_progress). After the eox byte, - pm_read_byte will clear sysex_in_progress and midi->flush - (Note that the overflow flag is managed by pm_read_short - and Pm_Read, so the supplier should not read or write it.) - */ - -/* pm_read_short is the place where all input messages arrive from - system-dependent code such as pmwinmm.c. Here, the messages - are entered into the PortMidi input buffer. - */ - - /* Algorithnm: - if overflow or flush, return - ATOMIC: - enqueue data - if buffer overflow, set overflow - if buffer overflow and sysex_in_progress, set flush - */ -void pm_read_short(PmInternal *midi, PmEvent *event) -{ - long tail; - int status; - /* arg checking */ - assert(midi != NULL); - assert(!Pm_HasHostError(midi)); - /* midi filtering is applied here */ - status = Pm_MessageStatus(event->message); - if (!pm_status_filtered(status, midi->filters) - && !pm_realtime_filtered(status, midi->filters) - && !pm_channel_filtered(status, midi->channel_mask)) { - /* if sysex is in progress and we get a status byte, it had - better be a realtime message or the starting SYSEX byte; - otherwise, we exit the sysex_in_progress state - */ - if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK) && - !is_real_time(status) && status != MIDI_SYSEX ) { - midi->sysex_in_progress = FALSE; - midi->flush = FALSE; - } - - /* don't try to do anything more in an overflow state */ - if (midi->overflow || midi->flush) return; - - /* insert the message */ - tail = midi->tail; - midi->buffer[tail++] = *event; - if (tail == midi->buffer_len) tail = 0; - if (tail == midi->head || midi->overflow) { - midi->overflow = TRUE; - if (midi->sysex_in_progress) midi->flush = TRUE; - /* drop the rest of the message, this must be cleared - by caller when EOX is received */ - return; - } - midi->tail = tail; /* complete the write */ - } -} - - -void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - PmEvent event; - - /* there may be nothing in the buffer */ - if (midi->sysex_message_count == 0) return; /* nothing to flush */ - - event.message = midi->sysex_message; - event.timestamp = timestamp; - pm_read_short(midi, &event); - midi->sysex_message_count = 0; - midi->sysex_message = 0; -} - - -void pm_read_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) -{ - assert(midi); - assert(!Pm_HasHostError(midi)); - /* here is the logic for controlling sysex_in_progress */ - if (midi->sysex_in_progress) { - if (byte == MIDI_EOX) midi->sysex_in_progress = FALSE; - else if (byte == MIDI_SYSEX) { - /* problem: need to terminate the current sysex and start - a new one - */ - pm_flush_sysex(midi, timestamp); - } - } else if (byte == MIDI_SYSEX) { - midi->sysex_in_progress = TRUE; - } else { - /* error: we're getting data bytes or EOX but we're no sysex is - in progress. Drop the data. (Would it be better to report an - error? Is this a host error or a pmBadData error? Is this - ever possible? - */ -#ifdef DEBUG - printf("PortMidi debug msg: unexpected sysex data or EOX\n"); -#endif - return; - } - - if (pm_realtime_filtered(byte, midi->filters)) - return; - /* this awkward expression places the bytes in increasingly higher- - order bytes of the long message */ - midi->sysex_message |= (byte << (8 * midi->sysex_message_count++)); - if (midi->sysex_message_count == 4 || !midi->sysex_in_progress) { - pm_flush_sysex(midi, timestamp); - if (!midi->sysex_in_progress) midi->flush = FALSE; - } -} - - -int pm_queue_full(PmInternal *midi) -{ - long tail; - - /* arg checking */ - assert(midi != NULL); - assert(!Pm_HasHostError(midi)); - - tail = midi->tail + 1; - if (tail == midi->buffer_len) tail = 0; - return tail == midi->head; -} - diff --git a/portmidi/pm_common/portmidi.h b/portmidi/pm_common/portmidi.h deleted file mode 100644 index 8c9c04c2a..000000000 --- a/portmidi/pm_common/portmidi.h +++ /dev/null @@ -1,692 +0,0 @@ -#ifndef PORT_MIDI_H -#define PORT_MIDI_H -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * PortMidi Portable Real-Time MIDI Library - * PortMidi API Header File - * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * Copyright (c) 2001 Roger B. Dannenberg - * - * 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. - * - */ - -/* CHANGELOG FOR PORTMIDI - * - * 15Nov04 Ben Allison - * - sysex output now uses one buffer/message and reallocates buffer - * - if needed - * - filters expanded for many message types and channels - * - detailed changes are as follows: - * ------------- in pmwinmm.c -------------- - * - new #define symbol: OUTPUT_BYTES_PER_BUFFER - * - change SYSEX_BYTES_PER_BUFFER to 1024 - * - added MIDIHDR_BUFFER_LENGTH(x) to correctly count midihdr buffer length - * - change MIDIHDR_SIZE(x) to (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR)) - * - change allocate_buffer to use new MIDIHDR_BUFFER_LENGTH macro - * - new macros for MIDIHDR_SYSEX_SIZE and MIDIHDR_SYSEX_BUFFER_LENGTH - * - similar to above, but counts appropriately for sysex messages - * - added the following members to midiwinmm_struct for sysex data: - * - LPMIDIHDR *sysex_buffers; ** pool of buffers for sysex data ** - * - int num_sysex_buffers; ** how many sysex buffers ** - * - int next_sysex_buffer; ** index of next sysexbuffer to send ** - * - HANDLE sysex_buffer_signal; ** to wait for free sysex buffer ** - * - duplicated allocate_buffer, alocate_buffers and get_free_output_buffer - * - into equivalent sysex_buffer form - * - changed winmm_in_open to initialize new midiwinmm_struct members and - * - to use the new allocate_sysex_buffer() function instead of - * - allocate_buffer() - * - changed winmm_out_open to initialize new members, create sysex buffer - * - signal, and allocate 2 sysex buffers - * - changed winmm_out_delete to free sysex buffers and shut down the sysex - * - buffer signal - * - create new function resize_sysex_buffer which resizes m->hdr to the - * - passed size, and corrects the midiwinmm_struct accordingly. - * - changed winmm_write_byte to use new resize_sysex_buffer function, - * - if resize fails, write current buffer to output and continue - * - changed winmm_out_callback to use buffer_signal or sysex_buffer_signal - * - depending on which buffer was finished - * ------------- in portmidi.h -------------- - * - added pmBufferMaxSize to PmError to indicate that the buffer would be - * - too large for the underlying API - * - added additional filters - * - added prototype, documentation, and helper macro for Pm_SetChannelMask - * ------------- in portmidi.c -------------- - * - added pm_status_filtered() and pm_realtime_filtered() functions to - * separate filtering logic from buffer logic in pm_read_short - * - added Pm_SetChannelMask function - * - added pm_channel_filtered() function - * ------------- in pminternal.h -------------- - * - added member to PortMidiStream for channel mask - * - * 25May04 RBD - * - removed support for MIDI THRU - * - moved filtering from Pm_Read to pm_enqueue to avoid buffer ovfl - * - extensive work on Mac OS X port, especially sysex and error handling - * - * 18May04 RBD - * - removed side-effects from assert() calls. Now you can disable assert(). - * - no longer check pm_hosterror everywhere, fixing a bug where an open - * failure could cause a write not to work on a previously opened port - * until you call Pm_GetHostErrorText(). - * 16May04 RBD and Chris Roberts - * - Some documentation wordsmithing in portmidi.h - * - Dynamically allocate port descriptor structures - * - Fixed parameter error in midiInPrepareBuffer and midiInAddBuffer. - * - * 09Oct03 RBD - * - Changed Thru handling. Now the client does all the work and the client - * must poll or read to keep thru messages flowing. - * - * 31May03 RBD - * - Fixed various bugs. - * - Added linux ALSA support with help from Clemens Ladisch - * - Added Mac OS X support, implemented by Jon Parise, updated and - * integrated by Andrew Zeldis and Zico Kolter - * - Added latency program to build histogram of system latency using PortTime. - * - * 30Jun02 RBD Extensive rewrite of sysex handling. It works now. - * Extensive reworking of error reporting and error text -- no - * longer use dictionary call to delete data; instead, Pm_Open - * and Pm_Close clean up before returning an error code, and - * error text is saved in a system-independent location. - * Wrote sysex.c to test sysex message handling. - * - * 15Jun02 BCT changes: - * - Added pmHostError text handling. - * - For robustness, check PortMidi stream args not NULL. - * - Re-C-ANSI-fied code (changed many C++ comments to C style) - * - Reorganized code in pmwinmm according to input/output functionality (made - * cleanup handling easier to reason about) - * - Fixed Pm_Write calls (portmidi.h says these should not return length but Pm_Error) - * - Cleaned up memory handling (now system specific data deleted via dictionary - * call in PortMidi, allows client to query host errors). - * - Added explicit asserts to verify various aspects of pmwinmm implementation behaves as - * logic implies it should. Specifically: verified callback routines not reentrant and - * all verified status for all unchecked Win32 MMedia API calls perform successfully - * - Moved portmidi initialization and clean-up routines into DLL to fix Win32 MMedia API - * bug (i.e. if devices not explicitly closed, must reboot to debug application further). - * With this change, clients no longer need explicitly call Pm_Initialize, Pm_Terminate, or - * explicitly Pm_Close open devices when using WinMM version of PortMidi. - * - * 23Jan02 RBD Fixed bug in pmwinmm.c thru handling - * - * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to prevent - * opening an input as output and vice versa. - * Added comments and documentation. - * Implemented Pm_Terminate(). - * - * - * IMPORTANT INFORMATION ABOUT A WIN32 BUG: - * - * Windows apparently has a serious midi bug -- if you do not close ports, Windows - * may crash. PortMidi tries to protect against this by using a DLL to clean up. - * - * If client exits for example with: - * i) assert - * ii) Ctrl^c, - * then DLL clean-up routine called. However, when client does something - * really bad (e.g. assigns value to NULL pointer) then DLL CLEANUP ROUTINE - * NEVER RUNS! In this state, if you wait around long enough, you will - * probably get the blue screen of death. Can also go into Pview and there will - * exist zombie process that you can't kill. - * - * NOTES ON HOST ERROR REPORTING: - * - * PortMidi errors (of type PmError) are generic, system-independent errors. - * When an error does not map to one of the more specific PmErrors, the - * catch-all code pmHostError is returned. This means that PortMidi has - * retained a more specific system-dependent error code. The caller can - * get more information by calling Pm_HasHostError() to test if there is - * a pending host error, and Pm_GetHostErrorText() to get a text string - * describing the error. Host errors are reported on a per-device basis - * because only after you open a device does PortMidi have a place to - * record the host error code. I.e. only - * those routines that receive a (PortMidiStream *) argument check and - * report errors. One exception to this is that Pm_OpenInput() and - * Pm_OpenOutput() can report errors even though when an error occurs, - * there is no PortMidiStream* to hold the error. Fortunately, both - * of these functions return any error immediately, so we do not really - * need per-device error memory. Instead, any host error code is stored - * in a global, pmHostError is returned, and the user can call - * Pm_GetHostErrorText() to get the error message (and the invalid stream - * parameter will be ignored.) The functions - * pm_init and pm_term do not fail or raise - * errors. The job of pm_init is to locate all available devices so that - * the caller can get information via PmDeviceInfo(). If an error occurs, - * the device is simply not listed as available. - * - * Host errors come in two flavors: - * a) host error - * b) host error during callback - * These can occur w/midi input or output devices. (b) can only happen - * asynchronously (during callback routines), whereas (a) only occurs while - * synchronously running PortMidi and any resulting system dependent calls - * - * Host-error reporting relies on following assumptions: - * 1) PortMidi routines won't allow system dependent routines to be - * called when args are bogus. - * Thus, in pmwinmm.c it is safe to assume: - * - stream ptr valid - * - currently not operating in "has host error" state - * 2) Host-error reporting relies on a staged delivery of error messages. - * When a host error occurs, the error code is saved with the stream. - * The error is reported as a return code from the next operation on - * the stream. This could be immediately if the error is synchronous, - * or delayed if the error is an asynchronous callback problem. In - * any case, when pmHostError is returned, the error is copied to - * a global, pm_hosterror and the error code stored with the stream - * is cleared. If the user chooses to inquire about the error using - * Pm_GetHostErrorText(), the error will be reported as text. If the - * user ignores the error and makes another call on the stream, the - * call will proceed because the error code associated with the stream - * has been cleared. - * - */ - -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif - -/* default size of buffers for sysex transmission: */ -#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 - - -typedef enum { - pmNoError = 0, - pmHostError = -10000, - pmInvalidDeviceId, /* out of range or output device when input is requested or vice versa */ - pmInsufficientMemory, - pmBufferTooSmall, - pmBufferOverflow, - pmBadPtr, - pmBadData, /* illegal midi data, e.g. missing EOX */ - pmInternalError, - pmBufferMaxSize, /* buffer is already as large as it can be */ -} PmError; - -/* - Pm_Initialize() is the library initialisation function - call this before - using the library. -*/ - -PmError Pm_Initialize( void ); - -/* - Pm_Terminate() is the library termination function - call this after - using the library. -*/ - -PmError Pm_Terminate( void ); - -/* A single PortMidiStream is a descriptor for an open MIDI device. -*/ -typedef void PortMidiStream; -#define PmStream PortMidiStream - -/* - Test whether stream has a pending host error. Normally, the client finds - out about errors through returned error codes, but some errors can occur - asynchronously where the client does not - explicitly call a function, and therefore cannot receive an error code. - The client can test for a pending error using Pm_HasHostError(). If true, - the error can be accessed and cleared by calling Pm_GetErrorText(). The - client does not need to call Pm_HasHostError(). Any pending error will be - reported the next time the client performs an explicit function call on - the stream, e.g. an input or output operation. -*/ -int Pm_HasHostError( PortMidiStream * stream ); - - -/* Translate portmidi error number into human readable message. - These strings are constants (set at compile time) so client has - no need to allocate storage -*/ -const char *Pm_GetErrorText( PmError errnum ); - -/* Translate portmidi host error into human readable message. - These strings are computed at run time, so client has to allocate storage. - After this routine executes, the host error is cleared. -*/ -void Pm_GetHostErrorText(char * msg, unsigned int len); - -#define HDRLENGTH 50 -#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less - than this number of characters */ - -/* - Device enumeration mechanism. - - Device ids range from 0 to Pm_CountDevices()-1. - -*/ -typedef int PmDeviceID; -#define pmNoDevice -1 -typedef struct { - int structVersion; - const char *interf; /* underlying MIDI API, e.g. MMSystem or DirectX */ - const char *name; /* device name, e.g. USB MidiSport 1x1 */ - int input; /* true iff input is available */ - int output; /* true iff output is available */ - int opened; /* used by generic PortMidi code to do error checking on arguments */ - -} PmDeviceInfo; - - -int Pm_CountDevices( void ); -/* - Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() - - Return the default device ID or pmNoDevice if there are no devices. - The result can be passed to Pm_OpenMidi(). - - On the PC, the user can specify a default device by - setting an environment variable. For example, to use device #1. - - set PM_RECOMMENDED_OUTPUT_DEVICE=1 - - The user should first determine the available device ID by using - the supplied application "testin" or "testout". - - In general, the registry is a better place for this kind of info, - and with USB devices that can come and go, using integers is not - very reliable for device identification. Under Windows, if - PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is - *NOT* found in the environment, then the default device is obtained - by looking for a string in the registry under: - HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device - and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device - for a string. The number of the first device with a substring that - matches the string exactly is returned. For example, if the string - in the registry is "USB", and device 1 is named - "In USB MidiSport 1x1", then that will be the default - input because it contains the string "USB". - - In addition to the name, PmDeviceInfo has the member "interf", which - is the interface name. (The "interface" is the underlying software - system or API used by PortMidi to access devices. Examples are - MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) - At present, the only Win32 interface is "MMSystem", the only Linux - interface is "ALSA", and the only Max OS X interface is "CoreMIDI". - To specify both the interface and the device name in the registry, - separate the two with a comma and a space, e.g.: - MMSystem, In USB MidiSport 1x1 - In this case, the string before the comma must be a substring of - the "interf" string, and the string after the space must be a - substring of the "name" name string in order to match the device. - - Note: in the current release, the default is simply the first device - (the input or output device with the lowest PmDeviceID). -*/ -PmDeviceID Pm_GetDefaultInputDeviceID( void ); -PmDeviceID Pm_GetDefaultOutputDeviceID( void ); - -/* - PmTimestamp is used to represent a millisecond clock with arbitrary - start time. The type is used for all MIDI timestampes and clocks. -*/ -typedef long PmTimestamp; -typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); - -/* TRUE if t1 before t2 */ -#define PmBefore(t1,t2) ((t1-t2) < 0) - -/* - Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo 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 PortMidi implementation and must - not be manipulated or freed. The pointer is guaranteed to be valid - between calls to Pm_Initialize() and Pm_Terminate(). -*/ -const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); - -/* - Pm_OpenInput() and Pm_OpenOutput() open devices. - - stream is the address of a PortMidiStream pointer which will receive - a pointer to the newly opened stream. - - inputDevice is the id of the device used for input (see PmDeviceID above). - - inputDriverInfo is a pointer to an optional driver specific data structure - containing additional information for device setup or handle 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 PmDeviceID above.) - - outputDriverInfo is a pointer to an optional driver specific data structure - containing additional information for device setup or handle processing. - outputDriverInfo is never required for correct operation. If not used - outputDriverInfo should be NULL. - - For input, the buffersize specifies the number of input events to be - buffered waiting to be read using Pm_Read(). For output, buffersize - specifies the number of output events to be buffered waiting for output. - (In some cases -- see below -- PortMidi does not buffer output at all - and merely passes data to a lower-level API, in which case buffersize - is ignored.) - - latency is the delay in milliseconds applied to timestamps to determine - when the output should actually occur. (If latency is < 0, 0 is assumed.) - If latency is zero, timestamps are ignored and all output is delivered - immediately. If latency is greater than zero, output is delayed until - the message timestamp plus the latency. (NOTE: time is measured relative - to the time source indicated by time_proc. Timestamps are absolute, not - relative delays or offsets.) In some cases, PortMidi can obtain - better timing than your application by passing timestamps along to the - device driver or hardware. Latency may also help you to synchronize midi - data to audio data by matching midi latency to the audio buffer latency. - - time_proc is a pointer to a procedure that returns time in milliseconds. It - may be NULL, in which case a default millisecond timebase (PortTime) is - used. If the application wants to use PortTime, it should start the timer - (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the - application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, - it may get a ptAlreadyStarted error from Pt_Start, and the application's - preferred time resolution and callback function will be ignored. - time_proc result values are appended to incoming MIDI data, and time_proc - times are used to schedule outgoing MIDI data (when latency is non-zero). - - time_info is a pointer passed to time_proc. - - return value: - Upon success Pm_Open() returns PmNoError and places a pointer to a - valid PortMidiStream in the stream argument. - If a call to Pm_Open() fails a nonzero error code is returned (see - PMError above) and the value of port is invalid. - - Any stream that is successfully opened should eventually be closed - by calling Pm_Close(). - -*/ -PmError Pm_OpenInput( PortMidiStream** stream, - PmDeviceID inputDevice, - void *inputDriverInfo, - long bufferSize, - PmTimeProcPtr time_proc, - void *time_info ); - -PmError Pm_OpenOutput( PortMidiStream** stream, - PmDeviceID outputDevice, - void *outputDriverInfo, - long bufferSize, - PmTimeProcPtr time_proc, - void *time_info, - long latency ); - -/* - Pm_SetFilter() sets filters on an open input stream to drop selected - input types. By default, only active sensing messages are filtered. - To prohibit, say, active sensing and sysex messages, call - Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); - - Filtering is useful when midi routing or midi thru functionality is being - provided by the user application. - For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), - while allowing note-related messages to pass. - Or you may be using a sequencer or drum-machine for MIDI clock information but want to - exclude any notes it may play. - */ - -/* filter active sensing messages (0xFE): */ -#define PM_FILT_ACTIVE 0x1 -/* filter system exclusive messages (0xF0): */ -#define PM_FILT_SYSEX 0x2 -/* filter clock messages (0xF8 only, does not filter clock start, etc.): */ -#define PM_FILT_CLOCK 0x4 -/* filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ -#define PM_FILT_PLAY 0x8 -/* filter undefined F9 messages (some equipment uses this as a 10ms 'tick') */ -#define PM_FILT_F9 0x10 -#define PM_FILT_TICK PM_FILT_F9 -/* filter undefined FD messages */ -#define PM_FILT_FD 0x20 -/* filter undefined real-time messages */ -#define PM_FILT_UNDEFINED (PM_FILT_F9 | PM_FILT_FD) -/* filter reset messages (0xFF) */ -#define PM_FILT_RESET 0x40 -/* filter all real-time messages */ -#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET) -/* filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ -#define PM_FILT_NOTE 0x80 -/* filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ -#define PM_FILT_CHANNEL_AFTERTOUCH 0x100 -/* per-note aftertouch (Ensoniq holds a patent on generating this on keyboards until June 2006) (0xA0-0xAF) */ -#define PM_FILT_POLY_AFTERTOUCH 0x200 -/* filter both channel and poly aftertouch */ -#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) -/* Program changes (0xC0-0xCF) */ -#define PM_FILT_PROGRAM 0x400 -/* Control Changes (CC's) (0xB0-0xBF)*/ -#define PM_FILT_CONTROL 0x800 -/* Pitch Bender (0xE0-0xEF*/ -#define PM_FILT_PITCHBEND 0x1000 -/* MIDI Time Code (0xF1)*/ -#define PM_FILT_MTC 0x2000 -/* Song Position (0xF2) */ -#define PM_FILT_SONG_POSITION 0x4000 -/* Song Select (0xF3)*/ -#define PM_FILT_SONG_SELECT 0x8000 -/* Tuning request (0xF6)*/ -#define PM_FILT_TUNE 0x10000 -/* All System Common messages (mtc, song position, song select, tune request) */ -#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) - - -PmError Pm_SetFilter( PortMidiStream* stream, long filters ); - - -/* - Pm_SetChannelMask() filters incoming messages based on channel. - The mask is a 16-bit bitfield corresponding to appropriate channels - The Pm_Channel macro can assist in calling this function. - i.e. to set receive only input on channel 1, call with - Pm_SetChannelMask(Pm_Channel(1)); - Multiple channels should be OR'd together, like - Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) - - All channels are allowed by default -*/ -#define Pm_Channel(channel) (1<<(channel)) - -PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); - -/* - Pm_Abort() terminates outgoing messages immediately - The caller should immediately close the output port; - this call may result in transmission of a partial midi message. - There is no abort for Midi input because the user can simply - ignore messages in the buffer and close an input device at - any time. - */ -PmError Pm_Abort( PortMidiStream* stream ); - -/* - Pm_Close() closes a midi stream, flushing any pending buffers. - (PortMidi attempts to close open streams when the application - exits -- this is particularly difficult under Windows.) -*/ -PmError Pm_Close( PortMidiStream* stream ); - -/* - Pm_Message() encodes a short Midi message into a long word. If data1 - and/or data2 are not present, use zero. - - Pm_MessageStatus(), Pm_MessageData1(), and - Pm_MessageData2() extract fields from a long-encoded midi message. -*/ -#define Pm_Message(status, data1, data2) \ - ((((data2) << 16) & 0xFF0000) | \ - (((data1) << 8) & 0xFF00) | \ - ((status) & 0xFF)) -#define Pm_MessageStatus(msg) ((msg) & 0xFF) -#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) -#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) - -/* All midi data comes in the form of PmEvent structures. A sysex - message is encoded as a sequence of PmEvent structures, with each - structure carrying 4 bytes of the message, i.e. only the first - PmEvent carries the status byte. - - Note that MIDI allows nested messages: the so-called "real-time" MIDI - messages can be inserted into the MIDI byte stream at any location, - including within a sysex message. MIDI real-time messages are one-byte - messages used mainly for timing (see the MIDI spec). PortMidi retains - the order of non-real-time MIDI messages on both input and output, but - it does not specify exactly how real-time messages are processed. This - is particulary problematic for MIDI input, because the input parser - must either prepare to buffer an unlimited number of sysex message - bytes or to buffer an unlimited number of real-time messages that - arrive embedded in a long sysex message. To simplify things, the input - parser is allowed to pass real-time MIDI messages embedded within a - sysex message, and it is up to the client to detect, process, and - remove these messages as they arrive. - - When receiving sysex messages, the sysex message is terminated - by either an EOX status byte (anywhere in the 4 byte messages) or - by a non-real-time status byte in the low order byte of the message. - If you get a non-real-time status byte but there was no EOX byte, it - means the sysex message was somehow truncated. This is not - considered an error; e.g., a missing EOX can result from the user - disconnecting a MIDI cable during sysex transmission. - - A real-time message can occur within a sysex message. A real-time - message will always occupy a full PmEvent with the status byte in - the low-order byte of the PmEvent message field. (This implies that - the byte-order of sysex bytes and real-time message bytes may not - be preserved -- for example, if a real-time message arrives after - 3 bytes of a sysex message, the real-time message will be delivered - first. The first word of the sysex message will be delivered only - after the 4th byte arrives, filling the 4-byte PmEvent message field. - - The timestamp field is observed when the output port is opened with - a non-zero latency. A timestamp of zero means "use the current time", - which in turn means to deliver the message with a delay of - latency (the latency parameter used when opening the output port.) - Do not expect PortMidi to sort data according to timestamps -- - messages should be sent in the correct order, and timestamps MUST - be non-decreasing. - - A sysex message will generally fill many PmEvent structures. On - output to a PortMidiStream with non-zero latency, the first timestamp - on sysex message data will determine the time to begin sending the - message. PortMidi implementations may ignore timestamps for the - remainder of the sysex message. - - On input, the timestamp ideally denotes the arrival time of the - status byte of the message. The first timestamp on sysex message - data will be valid. Subsequent timestamps may denote - when message bytes were actually received, or they may be simply - copies of the first timestamp. - - Timestamps for nested messages: If a real-time message arrives in - the middle of some other message, it is enqueued immediately with - the timestamp corresponding to its arrival time. The interrupted - non-real-time message or 4-byte packet of sysex data will be enqueued - later. The timestamp of interrupted data will be equal to that of - the interrupting real-time message to insure that timestamps are - non-decreasing. - */ -typedef long PmMessage; -typedef struct { - PmMessage message; - PmTimestamp timestamp; -} PmEvent; - -/* - Pm_Read() retrieves midi data into a buffer, and returns the number - of events read. Result is a non-negative number unless an error occurs, - in which case a PmError value will be returned. - - Buffer Overflow - - The problem: if an input overflow occurs, data will be lost, ultimately - because there is no flow control all the way back to the data source. - When data is lost, the receiver should be notified and some sort of - graceful recovery should take place, e.g. you shouldn't resume receiving - in the middle of a long sysex message. - - With a lock-free fifo, which is pretty much what we're stuck with to - enable portability to the Mac, it's tricky for the producer and consumer - to synchronously reset the buffer and resume normal operation. - - Solution: the buffer managed by PortMidi will be flushed when an overflow - occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) - and ordinary processing resumes as soon as a new message arrives. The - remainder of a partial sysex message is not considered to be a "new - message" and will be flushed as well. - -*/ -PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ); - -/* - Pm_Poll() tests whether input is available, - returning TRUE, FALSE, or an error value. -*/ -PmError Pm_Poll( PortMidiStream *stream); - -/* - Pm_Write() writes midi data from a buffer. This may contain: - - short messages - or - - sysex messages that are converted into a sequence of PmEvent - structures, e.g. sending data from a file or forwarding them - from midi input. - - Use Pm_WriteSysEx() to write a sysex message stored as a contiguous - array of bytes. - - Sysex data may contain embedded real-time messages. -*/ -PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ); - -/* - Pm_WriteShort() writes a timestamped non-system-exclusive midi message. - Messages are delivered in order as received, and timestamps must be - non-decreasing. (But timestamps are ignored if the stream was opened - with latency = 0.) -*/ -PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); - -/* - Pm_WriteSysEx() writes a timestamped system-exclusive midi message. -*/ -PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PORT_MIDI_H */ diff --git a/portmidi/pm_linux/README_LINUX.txt b/portmidi/pm_linux/README_LINUX.txt deleted file mode 100644 index e8a4332f0..000000000 --- a/portmidi/pm_linux/README_LINUX.txt +++ /dev/null @@ -1,32 +0,0 @@ -README_LINUX.txt for PortMidi -Roger Dannenberg -8 June 2004 - -To make PortMidi and PortTime, go back up to the portmidi -directory and type make. - -The Makefile will build all test programs and the portmidi -library. You may want to modify the Makefile to remove the -PM_CHECK_ERRORS definition. For experimental software, -especially programs running from the command line, we -recommend using PM_CHECK_ERRORS -- it will terminate your -program and print a helpful message if any PortMidi -function returns an error code. - -If you do not compile with PM_CHECK_ERRORS, you should -check for errors yourself. - -This code has not been carefully tested; however, -all test programs in pm_test seem to run properly. - -CHANGELOG - -08-Jun-2004 Roger B. Dannenberg - Updated code to use new system abstraction. - -12-Apr-2003 Roger B. Dannenberg - Fixed pm_test/test.c to filter clocks and active messages. - Integrated changes from Clemens Ladisch: - cleaned up pmlinuxalsa.c - record timestamp on sysex input - deallocate some resources previously left open diff --git a/portmidi/pm_linux/pmlinux.c b/portmidi/pm_linux/pmlinux.c deleted file mode 100644 index 8c70319f2..000000000 --- a/portmidi/pm_linux/pmlinux.c +++ /dev/null @@ -1,51 +0,0 @@ -/* pmlinux.c -- PortMidi os-dependent code */ - -/* This file only needs to implement pm_init(), which calls various - routines to register the available midi devices. This file must - be separate from the main portmidi.c file because it is system - dependent, and it is separate from, pmlinuxalsa.c, because it - might need to register non-alsa devices as well. - */ - -#include "stdlib.h" -#include "portmidi.h" -#ifdef PMALSA - #include "pmlinuxalsa.h" -#endif - -#ifdef PMNULL - #include "pmlinuxnull.h" -#endif - -PmError pm_init() -{ - #ifdef PMALSA - pm_linuxalsa_init(); - #endif - #ifdef PMNULL - pm_linuxnull_init(); - #endif -} - -void pm_term(void) -{ - #ifdef PMALSA - pm_linuxalsa_term(); - #endif -} - -PmDeviceID pm_default_input_device_id = -1; -PmDeviceID pm_default_output_device_id = -1; - -PmDeviceID Pm_GetDefaultInputDeviceID() { - return pm_default_input_device_id; -} - -PmDeviceID Pm_GetDefaultOutputDeviceID() { - return pm_default_output_device_id; -} - -void *pm_alloc(size_t s) { return malloc(s); } - -void pm_free(void *ptr) { free(ptr); } - diff --git a/portmidi/pm_linux/pmlinux.h b/portmidi/pm_linux/pmlinux.h deleted file mode 100644 index 75c91f142..000000000 --- a/portmidi/pm_linux/pmlinux.h +++ /dev/null @@ -1,5 +0,0 @@ -/* pmlinux.h */ - -extern PmDeviceID pm_default_input_device_id; -extern PmDeviceID pm_default_output_device_id; - diff --git a/portmidi/pm_linux/pmlinuxalsa.c b/portmidi/pm_linux/pmlinuxalsa.c deleted file mode 100644 index 9b0eee75c..000000000 --- a/portmidi/pm_linux/pmlinuxalsa.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - * pmlinuxalsa.c -- system specific definitions - * - * written by: - * Roger Dannenberg (port to Alsa 0.9.x) - * Clemens Ladisch (provided code examples and invaluable consulting) - * Jason Cohen, Rico Colon, Matt Filippone (Alsa 0.5.x implementation) - */ - -#include "stdlib.h" -#include "portmidi.h" -#include "pminternal.h" -#include "pmlinuxalsa.h" -#include "string.h" -#include "porttime.h" -#include "pmlinux.h" - -#include <alsa/asoundlib.h> - -/* I used many print statements to debug this code. I left them in the - * source, and you can turn them on by changing false to true below: - */ -#define VERBOSE_ON 0 -#define VERBOSE if (VERBOSE_ON) - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 - -#if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9 -#error needs ALSA 0.9.0 or later -#endif - -/* to store client/port in the device descriptor */ -#define MAKE_DESCRIPTOR(client, port) ((void*)(((client) << 8) | (port))) -#define GET_DESCRIPTOR_CLIENT(info) ((((int)(info)) >> 8) & 0xff) -#define GET_DESCRIPTOR_PORT(info) (((int)(info)) & 0xff) - -#define BYTE unsigned char -#define UINT unsigned long - -extern pm_fns_node pm_linuxalsa_in_dictionary; -extern pm_fns_node pm_linuxalsa_out_dictionary; - -static snd_seq_t *seq; // all input comes here, output queue allocated on seq -static int queue, queue_used; /* one for all ports, reference counted */ - -typedef struct alsa_descriptor_struct { - int client; - int port; - int this_port; - int in_sysex; - snd_midi_event_t *parser; - int error; /* host error code */ -} alsa_descriptor_node, *alsa_descriptor_type; - - -/* get_alsa_error_text -- copy error text to potentially short string */ -/**/ -static void get_alsa_error_text(char *msg, int len, int err) -{ - int errlen = strlen(snd_strerror(err)); - if (errlen < len) { - strcpy(msg, snd_strerror(err)); - } else if (len > 20) { - sprintf(msg, "Alsa error %d", err); - } else if (len > 4) { - strcpy(msg, "Alsa"); - } else { - msg[0] = 0; - } -} - - -/* queue is shared by both input and output, reference counted */ -static PmError alsa_use_queue(void) -{ - if (queue_used == 0) { - snd_seq_queue_tempo_t *tempo; - - queue = snd_seq_alloc_queue(seq); - if (queue < 0) { - pm_hosterror = queue; - return pmHostError; - } - snd_seq_queue_tempo_alloca(&tempo); - snd_seq_queue_tempo_set_tempo(tempo, 480000); - snd_seq_queue_tempo_set_ppq(tempo, 480); - pm_hosterror = snd_seq_set_queue_tempo(seq, queue, tempo); - if (pm_hosterror < 0) - return pmHostError; - - snd_seq_start_queue(seq, queue, NULL); - snd_seq_drain_output(seq); - } - ++queue_used; - return pmNoError; -} - - -static void alsa_unuse_queue(void) -{ - if (--queue_used == 0) { - snd_seq_stop_queue(seq, queue, NULL); - snd_seq_drain_output(seq); - snd_seq_free_queue(seq, queue); - VERBOSE printf("queue freed\n"); - } -} - - -/* midi_message_length -- how many bytes in a message? */ -static int midi_message_length(PmMessage message) -{ - message &= 0xff; - if (message < 0x80) { - return 0; - } else if (message < 0xf0) { - static const int length[] = {3, 3, 3, 3, 2, 2, 3}; - return length[(message - 0x80) >> 4]; - } else { - static const int length[] = { - -1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1}; - return length[message - 0xf0]; - } -} - - -static PmError alsa_out_open(PmInternal *midi, void *driverInfo) -{ - void *client_port = descriptors[midi->device_id].descriptor; - alsa_descriptor_type desc = (alsa_descriptor_type) - pm_alloc(sizeof(alsa_descriptor_node)); - snd_seq_port_info_t *info; - int err; - - if (!desc) return pmInsufficientMemory; - - snd_seq_port_info_alloca(&info); - snd_seq_port_info_set_port(info, midi->device_id); - snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_READ); - snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION); - snd_seq_port_info_set_port_specified(info, 1); - err = snd_seq_create_port(seq, info); - if (err < 0) goto free_desc; - - /* fill in fields of desc, which is passed to pm_write routines */ - midi->descriptor = desc; - desc->client = GET_DESCRIPTOR_CLIENT(client_port); - desc->port = GET_DESCRIPTOR_PORT(client_port); - desc->this_port = midi->device_id; - desc->in_sysex = 0; - - desc->error = 0; - - err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &desc->parser); - if (err < 0) goto free_this_port; - - if (midi->latency > 0) { /* must delay output using a queue */ - err = alsa_use_queue(); - if (err < 0) goto free_parser; - - err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); - if (err < 0) goto unuse_queue; /* clean up and return on error */ - } else { - err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); - if (err < 0) goto free_parser; /* clean up and return on error */ - } - return pmNoError; - - unuse_queue: - alsa_unuse_queue(); - free_parser: - snd_midi_event_free(desc->parser); - free_this_port: - snd_seq_delete_port(seq, desc->this_port); - free_desc: - pm_free(desc); - pm_hosterror = err; - if (err < 0) { - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); - } - return pmHostError; -} - - -static PmError alsa_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - snd_seq_event_t ev; - int err; - - snd_seq_ev_clear(&ev); - if (snd_midi_event_encode_byte(desc->parser, byte, &ev) == 1) { - snd_seq_ev_set_dest(&ev, desc->client, desc->port); - snd_seq_ev_set_source(&ev, desc->this_port); - if (midi->latency > 0) { - /* compute relative time of event = timestamp - now + latency */ - PmTimestamp now = (midi->time_proc ? - midi->time_proc(midi->time_info) : - Pt_Time(NULL)); - int when = timestamp; - /* if timestamp is zero, send immediately */ - /* otherwise compute time delay and use delay if positive */ - if (when == 0) when = now; - when = (when - now) + midi->latency; - if (when < 0) when = 0; - VERBOSE printf("timestamp %d now %d latency %d, ", - timestamp, now, midi->latency); - VERBOSE printf("scheduling event after %d\n", when); - /* message is sent in relative ticks, where 1 tick = 1 ms */ - snd_seq_ev_schedule_tick(&ev, queue, 1, when); - /* NOTE: for cases where the user does not supply a time function, - we could optimize the code by not starting Pt_Time and using - the alsa tick time instead. I didn't do this because it would - entail changing the queue management to start the queue tick - count when PortMidi is initialized and keep it running until - PortMidi is terminated. (This should be simple, but it's not - how the code works now.) -RBD */ - } else { /* send event out without queueing */ - VERBOSE printf("direct\n"); - /* ev.queue = SND_SEQ_QUEUE_DIRECT; - ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */ - snd_seq_ev_set_direct(&ev); - } - VERBOSE printf("sending event\n"); - err = snd_seq_event_output(seq, &ev); - if (err < 0) { - desc->error = err; - return pmHostError; - } - } - return pmNoError; -} - - -static PmError alsa_out_close(PmInternal *midi) -{ - int err; - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - if (!desc) return pmBadPtr; - - if (pm_hosterror = snd_seq_disconnect_to(seq, desc->this_port, - desc->client, desc->port)) { - // if there's an error, try to delete the port anyway, but don't - // change the pm_hosterror value so we retain the first error - snd_seq_delete_port(seq, desc->this_port); - } else { // if there's no error, delete the port and retain any error - pm_hosterror = snd_seq_delete_port(seq, desc->this_port); - } - if (midi->latency > 0) alsa_unuse_queue(); - snd_midi_event_free(desc->parser); - pm_free(desc); - if (pm_hosterror) { - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, - pm_hosterror); - return pmHostError; - } - return pmNoError; -} - - -static PmError alsa_in_open(PmInternal *midi, void *driverInfo) -{ - void *client_port = descriptors[midi->device_id].descriptor; - alsa_descriptor_type desc = (alsa_descriptor_type) - pm_alloc(sizeof(alsa_descriptor_node)); - snd_seq_port_info_t *info; - snd_seq_port_subscribe_t *sub; - snd_seq_addr_t addr; - int err; - - if (!desc) return pmInsufficientMemory; - - err = alsa_use_queue(); - if (err < 0) goto free_desc; - - snd_seq_port_info_alloca(&info); - snd_seq_port_info_set_port(info, midi->device_id); - snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_READ); - snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION); - snd_seq_port_info_set_port_specified(info, 1); - err = snd_seq_create_port(seq, info); - if (err < 0) goto free_queue; - - /* fill in fields of desc, which is passed to pm_write routines */ - midi->descriptor = desc; - desc->client = GET_DESCRIPTOR_CLIENT(client_port); - desc->port = GET_DESCRIPTOR_PORT(client_port); - desc->this_port = midi->device_id; - desc->in_sysex = 0; - - desc->error = 0; - - VERBOSE printf("snd_seq_connect_from: %d %d %d\n", - desc->this_port, desc->client, desc->port); - snd_seq_port_subscribe_alloca(&sub); - addr.client = snd_seq_client_id(seq); - addr.port = desc->this_port; - snd_seq_port_subscribe_set_dest(sub, &addr); - addr.client = desc->client; - addr.port = desc->port; - snd_seq_port_subscribe_set_sender(sub, &addr); - snd_seq_port_subscribe_set_time_update(sub, 1); - /* this doesn't seem to work: messages come in with real timestamps */ - snd_seq_port_subscribe_set_time_real(sub, 0); - err = snd_seq_subscribe_port(seq, sub); - /* err = - snd_seq_connect_from(seq, desc->this_port, desc->client, desc->port); */ - if (err < 0) goto free_this_port; /* clean up and return on error */ - return pmNoError; - - free_this_port: - snd_seq_delete_port(seq, desc->this_port); - free_queue: - alsa_unuse_queue(); - free_desc: - pm_free(desc); - pm_hosterror = err; - if (err < 0) { - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); - } - return pmHostError; -} - -static PmError alsa_in_close(PmInternal *midi) -{ - int err; - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - if (!desc) return pmBadPtr; - if (pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port, - desc->client, desc->port)) { - snd_seq_delete_port(seq, desc->this_port); /* try to close port */ - } else { - pm_hosterror = snd_seq_delete_port(seq, desc->this_port); - } - alsa_unuse_queue(); - pm_free(desc); - if (pm_hosterror) { - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, - pm_hosterror); - return pmHostError; - } - return pmNoError; -} - - -static PmError alsa_abort(PmInternal *midi) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - /* This is supposed to flush any pending output. */ - printf("WARNING: alsa_abort not implemented\n"); - return pmNoError; -} - - -#ifdef GARBAGE -This is old code here temporarily for reference -static PmError alsa_write(PmInternal *midi, PmEvent *buffer, long length) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - int i, bytes; - unsigned char byte; - long msg; - - desc->error = 0; - for (; length > 0; length--, buffer++) { - VERBOSE printf("message 0x%x\n", buffer->message); - if (Pm_MessageStatus(buffer->message) == MIDI_SYSEX) - desc->in_sysex = TRUE; - if (desc->in_sysex) { - msg = buffer->message; - for (i = 0; i < 4; i++) { - byte = msg; /* extract next byte to send */ - alsa_write_byte(midi, byte, buffer->timestamp); - if (byte == MIDI_EOX) { - desc->in_sysex = FALSE; - break; - } - if (desc->error < 0) break; - msg >>= 8; /* shift next byte into position */ - } - } else { - bytes = midi_message_length(buffer->message); - msg = buffer->message; - for (i = 0; i < bytes; i++) { - byte = msg; /* extract next byte to send */ - VERBOSE printf("sending 0x%x\n", byte); - alsa_write_byte(midi, byte, buffer->timestamp); - if (desc->error < 0) break; - msg >>= 8; /* shift next byte into position */ - } - } - } - if (desc->error < 0) return pmHostError; - - VERBOSE printf("snd_seq_drain_output: 0x%x\n", seq); - desc->error = snd_seq_drain_output(seq); - if (desc->error < 0) return pmHostError; - - desc->error = pmNoError; - return pmNoError; -} -#endif - - -static PmError alsa_write_flush(PmInternal *midi) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - VERBOSE printf("snd_seq_drain_output: 0x%x\n", seq); - desc->error = snd_seq_drain_output(seq); - if (desc->error < 0) return pmHostError; - - desc->error = pmNoError; - return pmNoError; -} - - -static PmError alsa_write_short(PmInternal *midi, PmEvent *event) -{ - int bytes = midi_message_length(event->message); - long msg = event->message; - int i; - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - for (i = 0; i < bytes; i++) { - unsigned char byte = msg; - VERBOSE printf("sending 0x%x\n", byte); - alsa_write_byte(midi, byte, event->timestamp); - if (desc->error < 0) break; - msg >>= 8; /* shift next byte into position */ - } - if (desc->error < 0) return pmHostError; - desc->error = pmNoError; - return pmNoError; -} - - -/* alsa_sysex -- implements begin_sysex and end_sysex */ -PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) { - return pmNoError; -} - - -static PmTimestamp alsa_synchronize(PmInternal *midi) -{ - return 0; /* linux implementation does not use this synchronize function */ - /* Apparently, Alsa data is relative to the time you send it, and there - is no reference. If this is true, this is a serious shortcoming of - Alsa. If not true, then PortMidi has a serious shortcoming -- it - should be scheduling relative to Alsa's time reference. */ -} - - -static void handle_event(snd_seq_event_t *ev) -{ - int device_id = ev->dest.port; - PmInternal *midi = descriptors[device_id].internalDescriptor; - PmEvent pm_ev; - PmTimeProcPtr time_proc = midi->time_proc; - PmTimestamp timestamp; - - /* time stamp should be in ticks, using our queue where 1 tick = 1ms */ - assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK); - - /* if no time_proc, just return "native" ticks (ms) */ - if (time_proc == NULL) { - timestamp = ev->time.tick; - } else { /* translate time to time_proc basis */ - snd_seq_queue_status_t *queue_status; - snd_seq_queue_status_alloca(&queue_status); - snd_seq_get_queue_status(seq, queue, queue_status); - /* return (now - alsa_now) + alsa_timestamp */ - timestamp = (*time_proc)(midi->time_info) + ev->time.tick - - snd_seq_queue_status_get_tick_time(queue_status); - } - pm_ev.timestamp = timestamp; - switch (ev->type) { - case SND_SEQ_EVENT_NOTEON: - pm_ev.message = Pm_Message(0x90 | ev->data.note.channel, - ev->data.note.note & 0x7f, - ev->data.note.velocity & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_NOTEOFF: - pm_ev.message = Pm_Message(0x80 | ev->data.note.channel, - ev->data.note.note & 0x7f, - ev->data.note.velocity & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_KEYPRESS: - pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel, - ev->data.note.note & 0x7f, - ev->data.note.velocity & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_CONTROLLER: - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, - ev->data.control.param & 0x7f, - ev->data.control.value & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_PGMCHANGE: - pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel, - ev->data.control.value & 0x7f, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_CHANPRESS: - pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel, - ev->data.control.value & 0x7f, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_PITCHBEND: - pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel, - (ev->data.control.value + 0x2000) & 0x7f, - ((ev->data.control.value + 0x2000) >> 7) & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_CONTROL14: - if (ev->data.control.param < 0x20) { - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, - ev->data.control.param, - (ev->data.control.value >> 7) & 0x7f); - pm_read_short(midi, &pm_ev); - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, - ev->data.control.param + 0x20, - ev->data.control.value & 0x7f); - pm_read_short(midi, &pm_ev); - } else { - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, - ev->data.control.param & 0x7f, - ev->data.control.value & 0x7f); - - pm_read_short(midi, &pm_ev); - } - break; - case SND_SEQ_EVENT_SONGPOS: - pm_ev.message = Pm_Message(0xf2, - ev->data.control.value & 0x7f, - (ev->data.control.value >> 7) & 0x7f); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_SONGSEL: - pm_ev.message = Pm_Message(0xf3, - ev->data.control.value & 0x7f, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_QFRAME: - pm_ev.message = Pm_Message(0xf1, - ev->data.control.value & 0x7f, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_START: - pm_ev.message = Pm_Message(0xfa, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_CONTINUE: - pm_ev.message = Pm_Message(0xfb, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_STOP: - pm_ev.message = Pm_Message(0xfc, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_CLOCK: - pm_ev.message = Pm_Message(0xf8, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_TUNE_REQUEST: - pm_ev.message = Pm_Message(0xf6, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_RESET: - pm_ev.message = Pm_Message(0xff, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_SENSING: - pm_ev.message = Pm_Message(0xfe, 0, 0); - pm_read_short(midi, &pm_ev); - break; - case SND_SEQ_EVENT_SYSEX: { - const BYTE *ptr = (const BYTE *) ev->data.ext.ptr; - int i; - long msg = 0; - int shift = 0; - if (!(midi->filters & PM_FILT_SYSEX)) { - for (i = 0; i < ev->data.ext.len; i++) { - pm_read_byte(midi, *ptr++, timestamp); - } - } - break; - } - } -} - -static PmError alsa_poll(PmInternal *midi) -{ - snd_seq_event_t *ev; - while (snd_seq_event_input(seq, &ev) >= 0) { - if (ev) { - handle_event(ev); - } - } - return pmNoError; -} - - -static unsigned int alsa_has_host_error(PmInternal *midi) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - return desc->error; -} - - -static void alsa_get_host_error(PmInternal *midi, char *msg, unsigned int len) -{ - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - int err = (pm_hosterror || desc->error); - get_alsa_error_text(msg, len, err); -} - - -pm_fns_node pm_linuxalsa_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - alsa_synchronize, - alsa_in_open, - alsa_abort, - alsa_in_close, - alsa_poll, - alsa_has_host_error, - alsa_get_host_error -}; - -pm_fns_node pm_linuxalsa_out_dictionary = { - alsa_write_short, - alsa_sysex, - alsa_sysex, - alsa_write_byte, - alsa_write_short, /* short realtime message */ - alsa_write_flush, - alsa_synchronize, - alsa_out_open, - alsa_abort, - alsa_out_close, - none_poll, - alsa_has_host_error, - alsa_get_host_error -}; - - -/* pm_strdup -- copy a string to the heap. Use this rather than strdup so - * that we call pm_alloc, not malloc. This allows portmidi to avoid - * malloc which might cause priority inversion. Probably ALSA is going - * to call malloc anyway, so this extra work here may be pointless. - */ -char *pm_strdup(const char *s) -{ - int len = strlen(s); - char *dup = (char *) pm_alloc(len + 1); - strcpy(dup, s); - return dup; -} - - -PmError pm_linuxalsa_init( void ) -{ - int err; - snd_seq_client_info_t *cinfo; - snd_seq_port_info_t *pinfo; - unsigned int caps; - - err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); - if (err < 0) return; - - snd_seq_client_info_alloca(&cinfo); - snd_seq_port_info_alloca(&pinfo); - - snd_seq_client_info_set_client(cinfo, -1); - while (snd_seq_query_next_client(seq, cinfo) == 0) { - snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); - snd_seq_port_info_set_port(pinfo, -1); - while (snd_seq_query_next_port(seq, pinfo) == 0) { - if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM) - continue; /* ignore Timer and Announce ports on client 0 */ - caps = snd_seq_port_info_get_capability(pinfo); - if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE))) - continue; /* ignore if you cannot read or write port */ - if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) { - if (pm_default_output_device_id == -1) - pm_default_output_device_id = pm_descriptor_index; - pm_add_device("ALSA", - pm_strdup(snd_seq_port_info_get_name(pinfo)), - FALSE, - MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), - snd_seq_port_info_get_port(pinfo)), - &pm_linuxalsa_out_dictionary); - } - if (caps & SND_SEQ_PORT_CAP_SUBS_READ) { - if (pm_default_input_device_id == -1) - pm_default_input_device_id = pm_descriptor_index; - pm_add_device("ALSA", - pm_strdup(snd_seq_port_info_get_name(pinfo)), - TRUE, - MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), - snd_seq_port_info_get_port(pinfo)), - &pm_linuxalsa_in_dictionary); - } - } - } -} - - -void pm_linuxalsa_term(void) -{ - snd_seq_close(seq); -} diff --git a/portmidi/pm_linux/pmlinuxalsa.h b/portmidi/pm_linux/pmlinuxalsa.h deleted file mode 100644 index d4bff16cd..000000000 --- a/portmidi/pm_linux/pmlinuxalsa.h +++ /dev/null @@ -1,6 +0,0 @@ -/* pmlinuxalsa.h -- system-specific definitions */ - -PmError pm_linuxalsa_init(void); -void pm_linuxalsa_term(void); - - diff --git a/portmidi/pm_mac/pmmac.c b/portmidi/pm_mac/pmmac.c deleted file mode 100644 index fbf31c83d..000000000 --- a/portmidi/pm_mac/pmmac.c +++ /dev/null @@ -1,42 +0,0 @@ -/* pmmac.c -- PortMidi os-dependent code */ - -/* This file only needs to implement: -pm_init(), which calls various routines to register the -available midi devices, -Pm_GetDefaultInputDeviceID(), and -Pm_GetDefaultOutputDeviceID(). -It is seperate from pmmacosxcm because we might want to register -non-CoreMIDI devices. -*/ - -#include "stdlib.h" -#include "portmidi.h" -#include "pmmacosxcm.h" - -PmError pm_init() -{ - return pm_macosxcm_init(); -} - -void pm_term(void) -{ - pm_macosxcm_term(); -} - -PmDeviceID pm_default_input_device_id = -1; -PmDeviceID pm_default_output_device_id = -1; - -PmDeviceID Pm_GetDefaultInputDeviceID() -{ - return pm_default_input_device_id; -} - -PmDeviceID Pm_GetDefaultOutputDeviceID() { - return pm_default_output_device_id; -} - -void *pm_alloc(size_t s) { return malloc(s); } - -void pm_free(void *ptr) { free(ptr); } - - diff --git a/portmidi/pm_mac/pmmac.h b/portmidi/pm_mac/pmmac.h deleted file mode 100644 index 2d714254e..000000000 --- a/portmidi/pm_mac/pmmac.h +++ /dev/null @@ -1,4 +0,0 @@ -/* pmmac.h */ - -extern PmDeviceID pm_default_input_device_id; -extern PmDeviceID pm_default_output_device_id; \ No newline at end of file diff --git a/portmidi/pm_mac/pmmacosxcm.c b/portmidi/pm_mac/pmmacosxcm.c deleted file mode 100644 index 5cb5a277f..000000000 --- a/portmidi/pm_mac/pmmacosxcm.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * Platform interface to the MacOS X CoreMIDI framework - * - * Jon Parise <jparise@cmu.edu> - * and subsequent work by Andrew Zeldis and Zico Kolter - * and Roger B. Dannenberg - * - * $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $ - */ - -/* Notes: - since the input and output streams are represented by MIDIEndpointRef - values and almost no other state, we store the MIDIEndpointRef on - descriptors[midi->device_id].descriptor. The only other state we need - is for errors: we need to know if there is an error and if so, what is - the error text. As in pmwinmm.c, we use a structure with two kinds of - host error: "error" and "callback_error". That way, asynchronous callbacks - do not interfere with other error information. - - OS X does not seem to have an error-code-to-text function, so we will - just use text messages instead of error codes. - */ - -#include <stdlib.h> - -#include "portmidi.h" -#include "pminternal.h" -#include "porttime.h" -#include "pmmac.h" -#include "pmmacosxcm.h" - -#include <stdio.h> -#include <string.h> - -#include <CoreServices/CoreServices.h> -#include <CoreMIDI/MIDIServices.h> -#include <CoreAudio/HostTime.h> - -#define PACKET_BUFFER_SIZE 1024 - -/* this is very strange: if I put in a reasonable - number here, e.g. 128, which would allow sysex data - to be sent 128 bytes at a time, then I lose sysex - data in my loopback test. With a buffer size of 4, - we put at most 4 bytes in a packet (but maybe many - packets in a packetList), and everything works fine. - */ -#define SYSEX_BUFFER_SIZE 4 - -#define VERBOSE_ON 1 -#define VERBOSE if (VERBOSE_ON) - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 -#define MIDI_STATUS_MASK 0x80 - -static MIDIClientRef client = NULL; /* Client handle to the MIDI server */ -static MIDIPortRef portIn = NULL; /* Input port handle */ -static MIDIPortRef portOut = NULL; /* Output port handle */ - -extern pm_fns_node pm_macosx_in_dictionary; -extern pm_fns_node pm_macosx_out_dictionary; - -typedef struct midi_macosxcm_struct { - unsigned long sync_time; /* when did we last determine delta? */ - UInt64 delta; /* difference between stream time and real time in ns */ - UInt64 last_time; /* last output time */ - int first_message; /* tells midi_write to sychronize timestamps */ - int sysex_mode; /* middle of sending sysex */ - unsigned long sysex_word; /* accumulate data when receiving sysex */ - unsigned int sysex_byte_count; /* count how many received */ - char error[PM_HOST_ERROR_MSG_LEN]; - char callback_error[PM_HOST_ERROR_MSG_LEN]; - Byte packetBuffer[PACKET_BUFFER_SIZE]; - MIDIPacketList *packetList; /* a pointer to packetBuffer */ - MIDIPacket *packet; - Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */ - MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */ -} midi_macosxcm_node, *midi_macosxcm_type; - -/* private function declarations */ -MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp); -PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp); - -char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint); - - -static int -midi_length(long msg) -{ - int status, high, low; - static int high_lengths[] = { - 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ - 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ - }; - static int low_lengths[] = { - 1, 1, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ - 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ - }; - - status = msg & 0xFF; - high = status >> 4; - low = status & 15; - - return (high != 0xF0) ? high_lengths[high] : low_lengths[low]; -} - -static PmTimestamp midi_synchronize(PmInternal *midi) -{ - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - UInt64 pm_stream_time_2 = - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - PmTimestamp real_time; - UInt64 pm_stream_time; - /* if latency is zero and this is an output, there is no - time reference and midi_synchronize should never be called */ - assert(midi->time_proc); - assert(!(midi->write_flag && midi->latency == 0)); - do { - /* read real_time between two reads of stream time */ - pm_stream_time = pm_stream_time_2; - real_time = (*midi->time_proc)(midi->time_info); - pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - /* repeat if more than 0.5 ms has elapsed */ - } while (pm_stream_time_2 > pm_stream_time + 500000); - m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000); - m->sync_time = real_time; - return real_time; -} - - -/* called when MIDI packets are received */ -static void -readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon) -{ - PmInternal *midi; - midi_macosxcm_type m; - PmEvent event; - MIDIPacket *packet; - unsigned int packetIndex; - unsigned long now; - unsigned int status; - - /* Retrieve the context for this connection */ - midi = (PmInternal *) connRefCon; - m = (midi_macosxcm_type) midi->descriptor; - assert(m); - - /* synchronize time references every 100ms */ - now = (*midi->time_proc)(midi->time_info); - if (m->first_message || m->sync_time + 100 /*ms*/ < now) { - /* time to resync */ - now = midi_synchronize(midi); - m->first_message = FALSE; - } - - packet = (MIDIPacket *) &newPackets->packet[0]; - /* printf("readproc packet status %x length %d\n", packet->data[0], packet->length); */ - for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { - /* Set the timestamp and dispatch this message */ - event.timestamp = - (AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) / - (UInt64) 1000000; - status = packet->data[0]; - /* process packet as sysex data if it begins with MIDI_SYSEX, or - MIDI_EOX or non-status byte */ - if (status == MIDI_SYSEX || status == MIDI_EOX || - !(status & MIDI_STATUS_MASK)) { - int i = 0; - while (i < packet->length) { - pm_read_byte(midi, packet->data[i], event.timestamp); - i++; - } - } else { - /* Build the PmMessage for the PmEvent structure */ - switch (packet->length) { - case 1: - event.message = Pm_Message(packet->data[0], 0, 0); - break; - case 2: - event.message = Pm_Message(packet->data[0], - packet->data[1], 0); - break; - case 3: - event.message = Pm_Message(packet->data[0], - packet->data[1], - packet->data[2]); - break; - default: - /* Skip packets that are too large to fit in a PmMessage */ -#ifdef DEBUG - printf("PortMidi debug msg: large packet skipped\n"); -#endif - continue; - } - pm_read_short(midi, &event); - } - packet = MIDIPacketNext(packet); - } -} - -static PmError -midi_in_open(PmInternal *midi, void *driverInfo) -{ - MIDIEndpointRef endpoint; - midi_macosxcm_type m; - OSStatus macHostError; - - /* insure that we have a time_proc for timing */ - if (midi->time_proc == NULL) { - if (!Pt_Started()) - Pt_Start(1, 0, 0); - /* time_get does not take a parameter, so coerce */ - midi->time_proc = (PmTimeProcPtr) Pt_Time; - } - - endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; - if (endpoint == NULL) { - return pmInvalidDeviceId; - } - - m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ - midi->descriptor = m; - if (!m) { - return pmInsufficientMemory; - } - m->error[0] = 0; - m->callback_error[0] = 0; - m->sync_time = 0; - m->delta = 0; - m->last_time = 0; - m->first_message = TRUE; - m->sysex_mode = FALSE; - m->sysex_word = 0; - m->sysex_byte_count = 0; - m->packetList = NULL; - m->packet = NULL; - - macHostError = MIDIPortConnectSource(portIn, endpoint, midi); - if (macHostError != noErr) { - pm_hosterror = macHostError; - sprintf(pm_hosterror_text, - "Host error %ld: MIDIPortConnectSource() in midi_in_open()", - macHostError); - midi->descriptor = NULL; - pm_free(m); - return pmHostError; - } - - return pmNoError; -} - -static PmError -midi_in_close(PmInternal *midi) -{ - MIDIEndpointRef endpoint; - OSStatus macHostError; - PmError err = pmNoError; - - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - - if (!m) return pmBadPtr; - - endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; - if (endpoint == NULL) { - pm_hosterror = pmBadPtr; - } - - /* shut off the incoming messages before freeing data structures */ - macHostError = MIDIPortDisconnectSource(portIn, endpoint); - if (macHostError != noErr) { - pm_hosterror = macHostError; - sprintf(pm_hosterror_text, - "Host error %ld: MIDIPortDisconnectSource() in midi_in_close()", - macHostError); - err = pmHostError; - } - - midi->descriptor = NULL; - pm_free(midi->descriptor); - - return err; -} - - -static PmError -midi_out_open(PmInternal *midi, void *driverInfo) -{ - midi_macosxcm_type m; - - m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ - midi->descriptor = m; - if (!m) { - return pmInsufficientMemory; - } - m->error[0] = 0; - m->callback_error[0] = 0; - m->sync_time = 0; - m->delta = 0; - m->last_time = 0; - m->first_message = TRUE; - m->sysex_mode = FALSE; - m->sysex_word = 0; - m->sysex_byte_count = 0; - m->packetList = (MIDIPacketList *) m->packetBuffer; - m->packet = NULL; - - return pmNoError; -} - -static PmError -midi_out_close(PmInternal *midi) -{ - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - if (!m) return pmBadPtr; - - midi->descriptor = NULL; - pm_free(midi->descriptor); - - return pmNoError; -} - -static PmError -midi_abort(PmInternal *midi) -{ - return pmNoError; -} - - -static PmError -midi_write_flush(PmInternal *midi) -{ - OSStatus macHostError; - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - MIDIEndpointRef endpoint = - (MIDIEndpointRef) descriptors[midi->device_id].descriptor; - assert(m); - assert(endpoint); - if (m->packet != NULL) { - /* out of space, send the buffer and start refilling it */ - macHostError = MIDISend(portOut, endpoint, m->packetList); - m->packet = NULL; /* indicate no data in packetList now */ - if (macHostError != noErr) goto send_packet_error; - } - return pmNoError; - -send_packet_error: - pm_hosterror = macHostError; - sprintf(pm_hosterror_text, - "Host error %ld: MIDISend() in midi_write()", - macHostError); - return pmHostError; - -} - - -static PmError -send_packet(PmInternal *midi, Byte *message, unsigned int messageLength, - MIDITimeStamp timestamp) -{ - PmError err; - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - assert(m); - - /* printf("add %d to packet %lx len %d\n", message[0], m->packet, messageLength); */ - m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), - m->packet, timestamp, messageLength, - message); - if (m->packet == NULL) { - /* out of space, send the buffer and start refilling it */ - /* make midi->packet non-null to fool midi_write_flush into sending */ - m->packet = (MIDIPacket *) 4; - if ((err = midi_write_flush(midi)) != pmNoError) return err; - m->packet = MIDIPacketListInit(m->packetList); - assert(m->packet); /* if this fails, it's a programming error */ - m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), - m->packet, timestamp, messageLength, - message); - assert(m->packet); /* can't run out of space on first message */ - } - return pmNoError; -} - - -static PmError -midi_write_short(PmInternal *midi, PmEvent *event) -{ - long when = event->timestamp; - long what = event->message; - MIDITimeStamp timestamp; - UInt64 when_ns; - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - Byte message[4]; - unsigned int messageLength; - - if (m->packet == NULL) { - m->packet = MIDIPacketListInit(m->packetList); - /* this can never fail, right? failure would indicate something - unrecoverable */ - assert(m->packet); - } - - /* compute timestamp */ - if (when == 0) when = midi->now; - /* if latency == 0, midi->now is not valid. We will just set it to zero */ - if (midi->latency == 0) when = 0; - when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; - /* make sure we don't go backward in time */ - if (when_ns < m->last_time) when_ns = m->last_time; - m->last_time = when_ns; - timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); - - message[0] = Pm_MessageStatus(what); - message[1] = Pm_MessageData1(what); - message[2] = Pm_MessageData2(what); - messageLength = midi_length(what); - - /* Add this message to the packet list */ - return send_packet(midi, message, messageLength, timestamp); -} - - -static PmError -midi_begin_sysex(PmInternal *midi, PmTimestamp when) -{ - UInt64 when_ns; - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - assert(m); - m->sysex_byte_count = 0; - - /* compute timestamp */ - if (when == 0) when = midi->now; - /* if latency == 0, midi->now is not valid. We will just set it to zero */ - if (midi->latency == 0) when = 0; - when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; - m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); - - if (m->packet == NULL) { - m->packet = MIDIPacketListInit(m->packetList); - /* this can never fail, right? failure would indicate something - unrecoverable */ - assert(m->packet); - } - return pmNoError; -} - - -static PmError -midi_end_sysex(PmInternal *midi, PmTimestamp when) -{ - PmError err; - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - assert(m); - - /* make sure we don't go backward in time */ - if (m->sysex_timestamp < m->last_time) m->sysex_timestamp = m->last_time; - - /* now send what's in the buffer */ - if (m->packet == NULL) { - /* if flush has been called in the meantime, packet list is NULL */ - m->packet = MIDIPacketListInit(m->packetList); - /* this can never fail, right? failure would indicate something - unrecoverable */ - assert(m->packet); - } - - err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count, - m->sysex_timestamp); - m->sysex_byte_count = 0; - if (err != pmNoError) { - m->packet = NULL; /* flush everything in the packet list */ - return err; - } - return pmNoError; -} - - -static PmError -midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) -{ - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - assert(m); - if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) { - PmError err = midi_end_sysex(midi, timestamp); - if (err != pmNoError) return err; - } - m->sysex_buffer[m->sysex_byte_count++] = byte; - return pmNoError; -} - - -static PmError -midi_write_realtime(PmInternal *midi, PmEvent *event) -{ - /* to send a realtime message during a sysex message, first - flush all pending sysex bytes into packet list */ - PmError err = midi_end_sysex(midi, 0); - if (err != pmNoError) return err; - /* then we can just do a normal midi_write_short */ - return midi_write_short(midi, event); -} - -static unsigned int midi_has_host_error(PmInternal *midi) -{ - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - return (m->callback_error[0] != 0) || (m->error[0] != 0); -} - - -static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len) -{ - midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; - msg[0] = 0; /* initialize to empty string */ - if (m) { /* make sure there is an open device to examine */ - if (m->error[0]) { - strncpy(msg, m->error, len); - m->error[0] = 0; /* clear the error */ - } else if (m->callback_error[0]) { - strncpy(msg, m->callback_error, len); - m->callback_error[0] = 0; /* clear the error */ - } - msg[len - 1] = 0; /* make sure string is terminated */ - } -} - - -MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp) -{ - UInt64 nanos; - if (timestamp <= 0) { - return (MIDITimeStamp)0; - } else { - nanos = (UInt64)timestamp * (UInt64)1000000; - return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos); - } -} - -PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp) -{ - UInt64 nanos; - nanos = AudioConvertHostTimeToNanos(timestamp); - return (PmTimestamp)(nanos / (UInt64)1000000); -} - - -char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint) -{ - MIDIEntityRef entity; - MIDIDeviceRef device; - CFStringRef endpointName = NULL, deviceName = NULL, fullName = NULL; - CFStringEncoding defaultEncoding; - char* newName; - - /* get the default string encoding */ - defaultEncoding = CFStringGetSystemEncoding(); - - /* get the entity and device info */ - MIDIEndpointGetEntity(endpoint, &entity); - MIDIEntityGetDevice(entity, &device); - - /* create the nicely formated name */ - MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName); - MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName); - if (deviceName != NULL) { - fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"), - deviceName, endpointName); - } else { - fullName = endpointName; - } - - /* copy the string into our buffer */ - newName = (char*)malloc(CFStringGetLength(fullName) + 1); - CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1, - defaultEncoding); - - /* clean up */ - if (endpointName) CFRelease(endpointName); - if (deviceName) CFRelease(deviceName); - if (fullName) CFRelease(fullName); - - return newName; -} - - - -pm_fns_node pm_macosx_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - none_synchronize, - midi_in_open, - midi_abort, - midi_in_close, - success_poll, - midi_has_host_error, - midi_get_host_error, -}; - -pm_fns_node pm_macosx_out_dictionary = { - midi_write_short, - midi_begin_sysex, - midi_end_sysex, - midi_write_byte, - midi_write_realtime, - midi_write_flush, - midi_synchronize, - midi_out_open, - midi_abort, - midi_out_close, - success_poll, - midi_has_host_error, - midi_get_host_error, -}; - - -PmError pm_macosxcm_init(void) -{ - ItemCount numInputs, numOutputs, numDevices; - MIDIEndpointRef endpoint; - int i; - OSStatus macHostError; - char *error_text; - - /* Determine the number of MIDI devices on the system */ - numDevices = MIDIGetNumberOfDevices(); - numInputs = MIDIGetNumberOfSources(); - numOutputs = MIDIGetNumberOfDestinations(); - - /* Return prematurely if no devices exist on the system - Note that this is not an error. There may be no devices. - Pm_CountDevices() will return zero, which is correct and - useful information - */ - if (numDevices <= 0) { - return pmNoError; - } - - - /* Initialize the client handle */ - macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client); - if (macHostError != noErr) { - error_text = "MIDIClientCreate() in pm_macosxcm_init()"; - goto error_return; - } - - /* Create the input port */ - macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, - NULL, &portIn); - if (macHostError != noErr) { - error_text = "MIDIInputPortCreate() in pm_macosxcm_init()"; - goto error_return; - } - - /* Create the output port */ - macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); - if (macHostError != noErr) { - error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()"; - goto error_return; - } - - /* Iterate over the MIDI input devices */ - for (i = 0; i < numInputs; i++) { - endpoint = MIDIGetSource(i); - if (endpoint == NULL) { - continue; - } - - /* set the first input we see to the default */ - if (pm_default_input_device_id == -1) - pm_default_input_device_id = pm_descriptor_index; - - /* Register this device with PortMidi */ - pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), - TRUE, (void*)endpoint, &pm_macosx_in_dictionary); - } - - /* Iterate over the MIDI output devices */ - for (i = 0; i < numOutputs; i++) { - endpoint = MIDIGetDestination(i); - if (endpoint == NULL) { - continue; - } - - /* set the first output we see to the default */ - if (pm_default_output_device_id == -1) - pm_default_output_device_id = pm_descriptor_index; - - /* Register this device with PortMidi */ - pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), - FALSE, (void*)endpoint, &pm_macosx_out_dictionary); - } - return pmNoError; - -error_return: - pm_hosterror = macHostError; - sprintf(pm_hosterror_text, "Host error %ld: %s\n", macHostError, error_text); - pm_macosxcm_term(); /* clear out any opened ports */ - return pmHostError; -} - -void pm_macosxcm_term(void) -{ - if (client != NULL) MIDIClientDispose(client); - if (portIn != NULL) MIDIPortDispose(portIn); - if (portOut != NULL) MIDIPortDispose(portOut); -} diff --git a/portmidi/pm_mac/pmmacosxcm.h b/portmidi/pm_mac/pmmacosxcm.h deleted file mode 100644 index 172593595..000000000 --- a/portmidi/pm_mac/pmmacosxcm.h +++ /dev/null @@ -1,4 +0,0 @@ -/* system-specific definitions */ - -PmError pm_macosxcm_init(void); -void pm_macosxcm_term(void); \ No newline at end of file diff --git a/portmidi/pm_test/latency.c b/portmidi/pm_test/latency.c deleted file mode 100644 index 87b1965b8..000000000 --- a/portmidi/pm_test/latency.c +++ /dev/null @@ -1,278 +0,0 @@ -/* latency.c -- measure latency of OS */ - -#include "porttime.h" -#include "portmidi.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -/* Latency is defined here to mean the time starting when a - process becomes ready to run, and ending when the process - actually runs. Latency is due to contention for the - processor, usually due to other processes, OS activity - including device drivers handling interrupts, and - waiting for the scheduler to suspend the currently running - process and activate the one that is waiting. - - Latency can affect PortMidi applications: if a process fails - to wake up promptly, MIDI input may sit in the input buffer - waiting to be handled, and MIDI output may not be generated - with accurate timing. Using the latency parameter when - opening a MIDI output port allows the caller to defer timing - to PortMidi, which in most implementations will pass the - data on to the OS. By passing timestamps and data to the - OS kernel, device driver, or even hardware, there are fewer - sources of latency that can affect the ultimate timing of - the data. On the other hand, the application must generate - and deliver the data ahead of the timestamp. The amount by - which data is computed early must be at least as large as - the worst-case latency to avoid timing problems. - - Latency is even more important in audio applications. If an - application lets an audio output buffer underflow, an audible - pop or click is produced. Audio input buffers can overflow, - causing data to be lost. In general the audio buffers must - be large enough to buffer the worst-case latency that the - application will encounter. - - This program measures latency by recording the difference - between the scheduled callback time and the current real time. - We do not really know the scheduled callback time, so we will - record the differences between the real time of each callback - and the real time of the previous callback. Differences that - are larger than the scheduled difference are recorded. Smaller - differences indicate the system is recovering from an earlier - latency, so these are ignored. - Since printing by the callback process can cause all sorts of - delays, this program records latency observations in a - histogram. When the program is stopped, the histogram is - printed to the console. - - Optionally the system can be tested under a load of MIDI input, - MIDI output, or both. If MIDI input is selected, the callback - thread will read any waiting MIDI events each iteration. You - must generate events on this interface for the test to actually - put any appreciable load on PortMidi. If MIDI output is - selected, alternating note on and note off events are sent each - X iterations, where you specify X. For example, with a timer - callback period of 2ms and X=1, a MIDI event is sent every 2ms. - - - INTERPRETING RESULTS: Time is quantized to 1ms, so there is - some uncertainty due to rounding. A microsecond latency that - spans the time when the clock is incremented will be reported - as a latency of 1. On the other hand, a latency of almost - 1ms that falls between two clock ticks will be reported as - zero. In general, if the highest nonzero bin is numbered N, - then the maximum latency is N+1. - -CHANGE LOG - -18-Jul-03 Mark Nelson -- Added code to generate MIDI or receive - MIDI during test, and made period user-settable. - */ - -#define HIST_LEN 21 /* how many 1ms bins in the histogram */ - -#define STRING_MAX 80 /* used for console input */ - -#define INPUT_BUFFER_SIZE 100 -#define OUTPUT_BUFFER_SIZE 0 - -#ifndef max -#define max(a, b) ((a) > (b) ? (a) : (b)) -#endif -#ifndef min -#define min(a, b) ((a) <= (b) ? (a) : (b)) -#endif - -int get_number(char *prompt); - -PtTimestamp previous_callback_time = 0; - -int period; /* milliseconds per callback */ - -long histogram[HIST_LEN]; -long max_latency = 0; /* worst latency observed */ -long out_of_range = 0; /* how many points outside of HIST_LEN? */ - -int test_in, test_out; /* test MIDI in and/or out? */ -int output_period; /* output MIDI every __ iterations if test_out true */ -int iteration = 0; -PmStream *in, *out; -int note_on = 0; /* is the note currently on? */ - -/* callback function for PortTime -- computes histogram */ -void pt_callback(PtTimestamp timestamp, void *userData) -{ - PtTimestamp difference = timestamp - previous_callback_time - period; - previous_callback_time = timestamp; - - /* allow 5 seconds for the system to settle down */ - if (timestamp < 5000) return; - - iteration++; - /* send a note on/off if user requested it */ - if (test_out && (iteration % output_period == 0)) { - PmEvent buffer[1]; - buffer[0].timestamp = Pt_Time(NULL); - if (note_on) { - /* note off */ - buffer[0].message = Pm_Message(0x90, 60, 0); - note_on = 0; - } else { - /* note on */ - buffer[0].message = Pm_Message(0x90, 60, 100); - note_on = 1; - } - Pm_Write(out, buffer, 1); - iteration = 0; - } - - /* read all waiting events (if user requested) */ - if (test_in) { - PmError status; - PmEvent buffer[1]; - do { - status = Pm_Poll(in); - if (status == TRUE) { - Pm_Read(in,buffer,1); - } - } while (status == TRUE); - } - - if (difference < 0) return; /* ignore when system is "catching up" */ - - /* update the histogram */ - if (difference < HIST_LEN) { - histogram[difference]++; - } else { - out_of_range++; - } - - if (max_latency < difference) max_latency = difference; -} - - -int main() -{ - char line[STRING_MAX]; - int i; - int len; - int choice; - PtTimestamp stop; - printf("Latency histogram.\n"); - period = get_number("Choose timer period (in ms): "); - assert(period >= 1); - printf("Benchmark with:\n\t%s\n\t%s\n\t%s\n\t%s\n", - "1. No MIDI traffic", - "2. MIDI input", - "3. MIDI output", - "4. MIDI input and output"); - choice = get_number("? "); - switch (choice) { - case 1: test_in = 0; test_out = 0; break; - case 2: test_in = 1; test_out = 0; break; - case 3: test_in = 0; test_out = 1; break; - case 4: test_in = 1; test_out = 1; break; - default: assert(0); - } - if (test_in || test_out) { - /* list device information */ - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if ((test_in && info->input) || - (test_out && info->output)) { - printf("%d: %s, %s", i, info->interf, info->name); - if (info->input) printf(" (input)"); - if (info->output) printf(" (output)"); - printf("\n"); - } - } - /* open stream(s) */ - if (test_in) { - int i = get_number("MIDI input device number: "); - Pm_OpenInput(&in, - i, - NULL, - INPUT_BUFFER_SIZE, - (long (*)(void *)) Pt_Time, - NULL); - /* turn on filtering; otherwise, input might overflow in the - 5-second period before timer callback starts reading midi */ - Pm_SetFilter(in, PM_FILT_ACTIVE | PM_FILT_CLOCK); - } - if (test_out) { - int i = get_number("MIDI output device number: "); - PmEvent buffer[1]; - Pm_OpenOutput(&out, - i, - NULL, - OUTPUT_BUFFER_SIZE, - (long (*)(void *)) Pt_Time, - NULL, - 0); /* no latency scheduling */ - - /* send a program change to force a status byte -- this fixes - a problem with a buggy linux MidiSport driver, and shouldn't - hurt anything else - */ - buffer[0].timestamp = 0; - buffer[0].message = Pm_Message(0xC0, 0, 0); /* program change */ - Pm_Write(out, buffer, 1); - - output_period = get_number( - "MIDI out should be sent every __ callback iterations: "); - - assert(output_period >= 1); - } - } - - printf("%s%s", "Latency measurements will start in 5 seconds. ", - "Type return to stop: "); - Pt_Start(period, &pt_callback, 0); - fgets(line, STRING_MAX, stdin); - stop = Pt_Time(); - Pt_Stop(); - - /* courteously turn off the last note, if necessary */ - if (note_on) { - PmEvent buffer[1]; - buffer[0].timestamp = Pt_Time(NULL); - buffer[0].message = Pm_Message(0x90, 60, 0); - Pm_Write(out, buffer, 1); - } - - /* print the histogram */ - printf("Duration of test: %g seconds\n\n", max(0, stop - 5000) * 0.001); - printf("Latency(ms) Number of occurrences\n"); - /* avoid printing beyond last non-zero histogram entry */ - len = min(HIST_LEN, max_latency + 1); - for (i = 0; i < len; i++) { - printf("%2d %10ld\n", i, histogram[i]); - } - printf("Number of points greater than %dms: %ld\n", - HIST_LEN - 1, out_of_range); - printf("Maximum latency: %ld milliseconds\n", max_latency); - printf("\nNote that due to rounding, actual latency can be 1ms higher\n"); - printf("than the numbers reported here.\n"); - printf("Type return to exit..."); - fgets(line, STRING_MAX, stdin); - return 0; -} - - -/* read a number from console */ -int get_number(char *prompt) -{ - char line[STRING_MAX]; - int n = 0, i; - printf(prompt); - while (n != 1) { - n = scanf("%d", &i); - fgets(line, STRING_MAX, stdin); - - } - return i; -} diff --git a/portmidi/pm_test/latency.dsp b/portmidi/pm_test/latency.dsp deleted file mode 100644 index ff6bbbd71..000000000 --- a/portmidi/pm_test/latency.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="latency" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=latency - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "latency.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "latency.mak" CFG="latency - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "latency - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "latency - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "latency - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "latency___Win32_Release" -# PROP BASE Intermediate_Dir "latency___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "latencyRelease" -# PROP Intermediate_Dir "latencyRelease" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../porttime" /I "../pm_common" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "latency - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "latency___Win32_Debug" -# PROP BASE Intermediate_Dir "latency___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "latencyDebug" -# PROP Intermediate_Dir "latencyDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../porttime" /I "../pm_common" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "latency - Win32 Release" -# Name "latency - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\latency.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/portmidi/pm_test/midithread.c b/portmidi/pm_test/midithread.c deleted file mode 100644 index 861b347cd..000000000 --- a/portmidi/pm_test/midithread.c +++ /dev/null @@ -1,327 +0,0 @@ -/* midithread.c -- example program showing how to do midi processing - in a preemptive thread - - Notes: if you handle midi I/O from your main program, there will be - some delay before handling midi messages whenever the program is - doing something like file I/O, graphical interface updates, etc. - - To handle midi with minimal delay, you should do all midi processing - in a separate, high priority thread. A convenient way to get a high - priority thread in windows is to use the timer callback provided by - the PortTime library. That is what we show here. - - If the high priority thread writes to a file, prints to the console, - or does just about anything other than midi processing, this may - create delays, so all this processing should be off-loaded to the - "main" process or thread. Communication between threads can be tricky. - If one thread is writing at the same time the other is reading, very - tricky race conditions can arise, causing programs to behave - incorrectly, but only under certain timing conditions -- a terrible - thing to debug. Advanced programmers know this as a synchronization - problem. See any operating systems textbook for the complete story. - - To avoid synchronization problems, a simple, reliable approach is - to communicate via messages. PortMidi offers a message queue as a - datatype, and operations to insert and remove messages. Use two - queues as follows: midi_to_main transfers messages from the midi - thread to the main thread, and main_to_midi transfers messages from - the main thread to the midi thread. Queues are safe for use between - threads as long as ONE thread writes and ONE thread reads. You must - NEVER allow two threads to write to the same queue. - - This program transposes incoming midi data by an amount controlled - by the main program. To change the transposition, type an integer - followed by return. The main program sends this via a message queue - to the midi thread. To quit, type 'q' followed by return. - - The midi thread can also send a pitch to the main program on request. - Type 'm' followed by return to wait for the next midi message and - print the pitch. - - This program illustrates: - Midi processing in a high-priority thread. - Communication with a main process via message queues. - - */ - -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "assert.h" -#include "portmidi.h" -#include "pmutil.h" -#include "porttime.h" - -/* if INPUT_BUFFER_SIZE is 0, PortMidi uses a default value */ -#define INPUT_BUFFER_SIZE 0 - -#define OUTPUT_BUFFER_SIZE 100 -#define DRIVER_INFO NULL -#define TIME_PROC NULL -#define TIME_INFO NULL -/* use zero latency because we want output to be immediate */ -#define LATENCY 0 - -#define STRING_MAX 80 - -/**********************************/ -/* DATA USED ONLY BY process_midi */ -/* (except during initialization) */ -/**********************************/ - -int active = FALSE; -int monitor = FALSE; -int midi_thru = TRUE; - -long transpose; -PmStream *midi_in; -PmStream *midi_out; - -/****************************/ -/* END OF process_midi DATA */ -/****************************/ - -/* shared queues */ -PmQueue *midi_to_main; -PmQueue *main_to_midi; - -#define QUIT_MSG 1000 -#define MONITOR_MSG 1001 -#define THRU_MSG 1002 - -/* timer interrupt for processing midi data */ -void process_midi(PtTimestamp timestamp, void *userData) -{ - PmError result; - PmEvent buffer; /* just one message at a time */ - long msg; - - /* do nothing until initialization completes */ - if (!active) - return; - - /* check for messages */ - do { - result = Pm_Dequeue(main_to_midi, &msg); - if (result) { - if (msg >= -127 && msg <= 127) - transpose = msg; - else if (msg == QUIT_MSG) { - /* acknowledge receipt of quit message */ - Pm_Enqueue(midi_to_main, &msg); - active = FALSE; - return; - } else if (msg == MONITOR_MSG) { - /* main has requested a pitch. monitor is a flag that - * records the request: - */ - monitor = TRUE; - } else if (msg == THRU_MSG) { - /* toggle Thru on or off */ - midi_thru = !midi_thru; - } - } - } while (result); - - /* see if there is any midi input to process */ - do { - result = Pm_Poll(midi_in); - if (result) { - long status, data1, data2; - if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) - continue; - if (midi_thru) - Pm_Write(midi_out, &buffer, 1); - /* unless there was overflow, we should have a message now */ - status = Pm_MessageStatus(buffer.message); - data1 = Pm_MessageData1(buffer.message); - data2 = Pm_MessageData2(buffer.message); - if ((status & 0xF0) == 0x90 || - (status & 0xF0) == 0x80) { - - /* this is a note-on or note-off, so transpose and send */ - data1 += transpose; - - /* keep within midi pitch range, keep proper pitch class */ - while (data1 > 127) - data1 -= 12; - while (data1 < 0) - data1 += 12; - - /* send the message */ - buffer.message = Pm_Message(status, data1, data2); - Pm_Write(midi_out, &buffer, 1); - - /* if monitor is set, send the pitch to the main thread */ - if (monitor) { - Pm_Enqueue(midi_to_main, &data1); - monitor = FALSE; /* only send one pitch per request */ - } - } - } - } while (result); -} - -void exit_with_message(char *msg) -{ - char line[STRING_MAX]; - printf("%s\n", msg); - fgets(line, STRING_MAX, stdin); - exit(1); -} - -int main() -{ - int id; - long n; - const PmDeviceInfo *info; - char line[STRING_MAX]; - int spin; - int done = FALSE; - - /* determine what type of test to run */ - printf("begin PortMidi multithread test...\n"); - - /* note that it is safe to call PortMidi from the main thread for - initialization and opening devices. You should not make any - calls to PortMidi from this thread once the midi thread begins. - to make PortMidi calls. - */ - - /* make the message queues */ - /* messages can be of any size and any type, but all messages in - * a given queue must have the same size. We'll just use long's - * for our messages in this simple example - */ - midi_to_main = Pm_QueueCreate(32, sizeof(long)); - assert(midi_to_main != NULL); - main_to_midi = Pm_QueueCreate(32, sizeof(long)); - assert(main_to_midi != NULL); - - /* a little test of enqueue and dequeue operations. Ordinarily, - * you would call Pm_Enqueue from one thread and Pm_Dequeue from - * the other. Since the midi thread is not running, this is safe. - */ - n = 1234567890; - Pm_Enqueue(midi_to_main, &n); - n = 987654321; - Pm_Enqueue(midi_to_main, &n); - Pm_Dequeue(midi_to_main, &n); - if (n != 1234567890) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - Pm_Dequeue(midi_to_main, &n); - if(n != 987654321) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - - /* always start the timer before you start midi */ - Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */ - /* the timer will call our function, process_midi() every millisecond */ - - Pm_Initialize(); - - id = Pm_GetDefaultOutputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default output device (%d).", id); - exit_with_message(""); - } - printf("Opening output device %s %s\n", info->interf, info->name); - - /* use zero latency because we want output to be immediate */ - Pm_OpenOutput(&midi_out, - id, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - LATENCY); - - id = Pm_GetDefaultInputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default input device (%d).", id); - exit_with_message(""); - } - printf("Opening input device %s %s\n", info->interf, info->name); - Pm_OpenInput(&midi_in, - id, - DRIVER_INFO, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - - active = TRUE; /* enable processing in the midi thread -- yes, this - is a shared variable without synchronization, but - this simple assignment is safe */ - - printf("Enter midi input; it will be transformed as specified by...\n"); - printf("%s\n%s\n%s\n", - "Type 'q' to quit, 'm' to monitor next pitch, t to toggle thru or", - "type a number to specify transposition.", - "Must terminate with [ENTER]"); - - while (!done) { - long msg; - int len; - fgets(line, STRING_MAX, stdin); - /* remove the newline: */ - len = strlen(line); - if (len > 0) line[len - 1] = 0; /* overwrite the newline char */ - if (strcmp(line, "q") == 0) { - msg = QUIT_MSG; - Pm_Enqueue(main_to_midi, &msg); - /* wait for acknowlegement */ - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - done = TRUE; /* leave the command loop and wrap up */ - } else if (strcmp(line, "m") == 0) { - msg = MONITOR_MSG; - Pm_Enqueue(main_to_midi, &msg); - printf("Waiting for note...\n"); - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - printf("... pitch is %ld\n", msg); - } else if (strcmp(line, "t") == 0) { - /* reading midi_thru asynchronously could give incorrect results, - e.g. if you type "t" twice before the midi thread responds to - the first one, but we'll do it this way anyway. Perhaps a more - correct way would be to wait for an acknowledgement message - containing the new state. */ - printf("Setting THRU %s\n", (midi_thru ? "off" : "on")); - msg = THRU_MSG; - Pm_Enqueue(main_to_midi, &msg); - } else if (sscanf(line, "%ld", &msg) == 1) { - if (msg >= -127 && msg <= 127) { - /* send transposition value */ - printf("Transposing by %ld\n", msg); - Pm_Enqueue(main_to_midi, &msg); - } else { - printf("Transposition must be within -127...127\n"); - } - } else { - printf("%s\n%s\n%s\n", - "Type 'q' to quit, 'm' to monitor next pitch, or", - "type a number to specify transposition.", - "Must terminate with [ENTER]"); - } - } - - /* at this point, midi thread is inactive and we need to shut down - * the midi input and output - */ - Pt_Stop(); /* stop the timer */ - Pm_QueueDestroy(midi_to_main); - Pm_QueueDestroy(main_to_midi); - - /* Belinda! if close fails here, some memory is deleted, right??? */ - Pm_Close(midi_in); - Pm_Close(midi_out); - - printf("finished portMidi multithread test...enter any character to quit [RETURN]..."); - fgets(line, STRING_MAX, stdin); - return 0; -} diff --git a/portmidi/pm_test/midithread.dsp b/portmidi/pm_test/midithread.dsp deleted file mode 100644 index 7dc0a16f4..000000000 --- a/portmidi/pm_test/midithread.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="midithread" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=midithread - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "midithread.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "midithread.mak" CFG="midithread - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "midithread - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "midithread - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "midithread - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "midithreadRelease" -# PROP Intermediate_Dir "midithreadRelease" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "midithread - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "midithreadDebug" -# PROP BASE Intermediate_Dir "midithreadDebug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "midithreadDebug" -# PROP Intermediate_Dir "midithreadDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "midithread - Win32 Release" -# Name "midithread - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\midithread.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/portmidi/pm_test/midithru.c b/portmidi/pm_test/midithru.c deleted file mode 100644 index 270246fbf..000000000 --- a/portmidi/pm_test/midithru.c +++ /dev/null @@ -1,364 +0,0 @@ -/* midithru.c -- example program implementing background thru processing */ - -/* suppose you want low-latency midi-thru processing, but your application - wants to take advantage of the input buffer and timestamped data so that - it does not have to operate with very low latency. - - This program illustrates how to use a timer callback from PortTime to - implement a low-latency process that handles midi thru, including correctly - merging midi data from the application with midi data from the input port. - - The main application, which runs in the main program thread, will use an - interface similar to that of PortMidi, but since PortMidi does not allow - concurrent threads to share access to a stream, the application will - call private methods that transfer MIDI messages to and from the timer - thread. All PortMidi API calls are made from the timer thread. - */ - -/* DESIGN - -All setup will be done by the main thread. Then, all direct access to -PortMidi will be handed off to the timer callback thread. - -After this hand-off, the main thread will get/send messages via a queue. - -The goal is to send incoming messages to the midi output while merging -any midi data generated by the application. Sysex is a problem here -because you cannot insert (merge) a midi message while a sysex is in -progress. There are at least three ways to implement midi thru with -sysex messages: - -1) Turn them off. If your application does not need them, turn them off - with Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_SYSEX). You will - not receive sysex (or active sensing messages), so you will not have - to handle them. - -2) Make them atomic. As you receive sysex messages, copy the data into - a (big) buffer. Ideally, expand the buffer as needed -- sysex messages - do not have any maximum length. Even more ideally, use a list structure - and real-time memory allocation to avoid latency in the timer thread. - When a full sysex message is received, send it to the midi output all - at once. - -3) Process sysex incrementally. Send sysex data to midi output as it - arrives. Block any non-real-time messages from the application until - the sysex message completes. There is the risk that an incomplete - sysex message will block messages forever, so implement a 5-second - timeout: if no sysex data is seen for 5 seconds, release the block, - possibly losing the rest of the sysex message. - - Application messages must be processed similarly: once started, a - sysex message will block MIDI THRU processing. We will assume that - the application will not abort a sysex message, so timeouts are not - necessary here. - -This code implements (3). - -Latency is also an issue. PortMidi requires timestamps to be in -non-decreasing order. Since we'll be operating with a low-latency -timer thread, we can just set the latency to zero meaning timestamps -are ignored by PortMidi. This will allow thru to go through with -minimal latency. The application, however, needs to use timestamps -because we assume it is high latency (the whole purpose of this -example is to illustrate how to get low-latency thru with a high-latency -application.) So the callback thread will implement midi timing by -observing timestamps. The current timestamp will be available in the -global variable current_timestamp. - -*/ - - -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "assert.h" -#include "portmidi.h" -#include "pmutil.h" -#include "porttime.h" - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 - -/* active is set true when midi processing should start */ -int active = FALSE; -/* process_midi_exit_flag is set when the timer thread shuts down */ -int process_midi_exit_flag; - -PmStream *midi_in; -PmStream *midi_out; - -/* shared queues */ -#define IN_QUEUE_SIZE 1024 -#define OUT_QUEUE_SIZE 1024 -PmQueue *in_queue; -PmQueue *out_queue; -PmTimestamp current_timestamp = 0; -int thru_sysex_in_progress = FALSE; -int app_sysex_in_progress = FALSE; -PmTimestamp last_timestamp = 0; - - -/* time proc parameter for Pm_MidiOpen */ -long midithru_time_proc(void *info) -{ - return current_timestamp; -} - - -/* timer interrupt for processing midi data. - Incoming data is delivered to main program via in_queue. - Outgoing data from main program is delivered via out_queue. - Incoming data from midi_in is copied with low latency to midi_out. - Sysex messages from either source block messages from the other. - */ -void process_midi(PtTimestamp timestamp, void *userData) -{ - PmError result; - PmEvent buffer; /* just one message at a time */ - - current_timestamp++; /* update every millisecond */ - /* if (current_timestamp % 1000 == 0) - printf("time %d\n", current_timestamp); */ - - /* do nothing until initialization completes */ - if (!active) { - /* this flag signals that no more midi processing will be done */ - process_midi_exit_flag = TRUE; - return; - } - - /* see if there is any midi input to process */ - if (!app_sysex_in_progress) { - do { - result = Pm_Poll(midi_in); - if (result) { - long status; - if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) - continue; - - /* record timestamp of most recent data */ - last_timestamp = current_timestamp; - - /* the data might be the end of a sysex message that - has timed out, in which case we must ignore it. - It's a continuation of a sysex message if status - is actually a data byte (high-order bit is zero). */ - status = Pm_MessageStatus(buffer.message); - if (((status & 0x80) == 0) && !thru_sysex_in_progress) { - continue; /* ignore this data */ - } - - /* implement midi thru */ - /* note that you could output to multiple ports or do other - processing here if you wanted - */ - /* printf("thru: %x\n", buffer.message); */ - Pm_Write(midi_out, &buffer, 1); - - /* send the message to the application */ - /* you might want to filter clock or active sense messages here - to avoid sending a bunch of junk to the application even if - you want to send it to MIDI THRU - */ - Pm_Enqueue(in_queue, &buffer); - - /* sysex processing */ - if (status == MIDI_SYSEX) thru_sysex_in_progress = TRUE; - else if ((status & 0xF8) != 0xF8) { - /* not MIDI_SYSEX and not real-time, so */ - thru_sysex_in_progress = FALSE; - } - if (thru_sysex_in_progress && /* look for EOX */ - (((buffer.message & 0xFF) == MIDI_EOX) || - (((buffer.message >> 8) & 0xFF) == MIDI_EOX) || - (((buffer.message >> 16) & 0xFF) == MIDI_EOX) || - (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) { - thru_sysex_in_progress = FALSE; - } - } - } while (result); - } - - - /* see if there is application midi data to process */ - while (!Pm_QueueEmpty(out_queue)) { - /* see if it is time to output the next message */ - PmEvent *next = (PmEvent *) Pm_QueuePeek(out_queue); - assert(next); /* must be non-null because queue is not empty */ - if (next->timestamp <= current_timestamp) { - /* time to send a message, first make sure it's not blocked */ - long status = Pm_MessageStatus(buffer.message); - if ((status & 0xF8) == 0xF8) { - ; /* real-time messages are not blocked */ - } else if (thru_sysex_in_progress) { - /* maybe sysex has timed out (output becomes unblocked) */ - if (last_timestamp + 5000 < current_timestamp) { - thru_sysex_in_progress = FALSE; - } else break; /* output is blocked, so exit loop */ - } - Pm_Dequeue(out_queue, &buffer); - Pm_Write(midi_out, &buffer, 1); - - /* inspect message to update app_sysex_in_progress */ - if (status == MIDI_SYSEX) app_sysex_in_progress = TRUE; - else if ((status & 0xF8) != 0xF8) { - /* not MIDI_SYSEX and not real-time, so */ - app_sysex_in_progress = FALSE; - } - if (app_sysex_in_progress && /* look for EOX */ - (((buffer.message & 0xFF) == MIDI_EOX) || - (((buffer.message >> 8) & 0xFF) == MIDI_EOX) || - (((buffer.message >> 16) & 0xFF) == MIDI_EOX) || - (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) { - app_sysex_in_progress = FALSE; - } - } else break; /* wait until indicated timestamp */ - } -} - - -void exit_with_message(char *msg) -{ -#define STRING_MAX 80 - char line[STRING_MAX]; - printf("%s\nType ENTER...", msg); - fgets(line, STRING_MAX, stdin); - exit(1); -} - - -void initialize() -/* set up midi processing thread and open midi streams */ -{ - /* note that it is safe to call PortMidi from the main thread for - initialization and opening devices. You should not make any - calls to PortMidi from this thread once the midi thread begins. - to make PortMidi calls. - */ - - /* note that this routine provides minimal error checking. If - you use the PortMidi library compiled with PM_CHECK_ERRORS, - then error messages will be printed and the program will exit - if an error is encountered. Otherwise, you should add some - error checking to this code. - */ - - const PmDeviceInfo *info; - int id; - - /* make the message queues */ - in_queue = Pm_QueueCreate(IN_QUEUE_SIZE, sizeof(PmEvent)); - assert(in_queue != NULL); - out_queue = Pm_QueueCreate(OUT_QUEUE_SIZE, sizeof(PmEvent)); - assert(out_queue != NULL); - - /* always start the timer before you start midi */ - Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */ - /* the timer will call our function, process_midi() every millisecond */ - - Pm_Initialize(); - - id = Pm_GetDefaultOutputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default output device (%d).", id); - exit_with_message(""); - } - printf("Opening output device %s %s\n", info->interf, info->name); - - /* use zero latency because we want output to be immediate */ - Pm_OpenOutput(&midi_out, - id, - NULL /* driver info */, - OUT_QUEUE_SIZE, - &midithru_time_proc, - NULL /* time info */, - 0 /* Latency */); - - id = Pm_GetDefaultInputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default input device (%d).", id); - exit_with_message(""); - } - printf("Opening input device %s %s\n", info->interf, info->name); - Pm_OpenInput(&midi_in, - id, - NULL /* driver info */, - 0 /* use default input size */, - &midithru_time_proc, - NULL /* time info */); - /* Note: if you set a filter here, then this will filter what goes - to the MIDI THRU port. You may not want to do this. - */ - Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK); - - active = TRUE; /* enable processing in the midi thread -- yes, this - is a shared variable without synchronization, but - this simple assignment is safe */ - -} - - -void finalize() -{ - /* the timer thread could be in the middle of accessing PortMidi stuff */ - /* to detect that it is done, we first clear process_midi_exit_flag and - then wait for the timer thread to set it - */ - process_midi_exit_flag = FALSE; - active = FALSE; - /* busy wait for flag from timer thread that it is done */ - while (!process_midi_exit_flag) ; - /* at this point, midi thread is inactive and we need to shut down - * the midi input and output - */ - Pt_Stop(); /* stop the timer */ - Pm_QueueDestroy(in_queue); - Pm_QueueDestroy(out_queue); - - Pm_Close(midi_in); - Pm_Close(midi_out); - - Pm_Terminate(); -} - - -int main(int argc, char *argv[]) -{ - PmTimestamp last_time = 0; - PmEvent buffer; - - /* determine what type of test to run */ - printf("begin PortMidi midithru program...\n"); - - initialize(); /* set up and start midi processing */ - - printf("%s\n%s\n", - "This program will run for 60 seconds, or until you play middle C,", - "echoing all input with a 2 second delay."); - - while (current_timestamp < 60000) { - /* just to make the point that this is not a low-latency process, - spin until half a second has elapsed */ - last_time = last_time + 500; - while (last_time > current_timestamp) ; - - /* now read data and send it after changing timestamps */ - while (Pm_Dequeue(in_queue, &buffer) == 1) { - /* printf("timestamp %d\n", buffer.timestamp); */ - /* printf("message %x\n", buffer.message); */ - buffer.timestamp = buffer.timestamp + 2000; /* delay */ - Pm_Enqueue(out_queue, &buffer); - /* play middle C to break out of loop */ - if (Pm_MessageStatus(buffer.message) == 0x90 && - Pm_MessageData1(buffer.message) == 60) { - goto quit_now; - } - } - } -quit_now: - finalize(); - exit_with_message("finished PortMidi midithru program."); - return 0; /* never executed, but keeps the compiler happy */ -} diff --git a/portmidi/pm_test/midithru.dsp b/portmidi/pm_test/midithru.dsp deleted file mode 100644 index 83f28cfcf..000000000 --- a/portmidi/pm_test/midithru.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="midithru" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=midithru - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "midithru.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "midithru.mak" CFG="midithru - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "midithru - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "midithru - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "midithru - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "midithruRelease" -# PROP Intermediate_Dir "midithruRelease" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "midithru - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "midithruDebug" -# PROP BASE Intermediate_Dir "midithruDebug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "midithruDebug" -# PROP Intermediate_Dir "midithruDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "midithru - Win32 Release" -# Name "midithru - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\midithru.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/portmidi/pm_test/midithru.dsw b/portmidi/pm_test/midithru.dsw deleted file mode 100644 index b244a1044..000000000 --- a/portmidi/pm_test/midithru.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "midithread"=.\midithru.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/portmidi/pm_test/sysex.c b/portmidi/pm_test/sysex.c deleted file mode 100644 index f49bf962f..000000000 --- a/portmidi/pm_test/sysex.c +++ /dev/null @@ -1,319 +0,0 @@ -/* sysex.c -- example program showing how to send and receive sysex - messages - - Messages are stored in a file using 2-digit hexadecimal numbers, - one per byte, separated by blanks, with up to 32 numbers per line: - F0 14 A7 4B ... - - */ - -#include "stdio.h" -#include "stdlib.h" -#include "assert.h" -#include "portmidi.h" -#include "porttime.h" -#include "string.h" - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 - -#define STRING_MAX 80 - -int latency = 0; - -/* read a number from console */ -/**/ -int get_number(char *prompt) -{ - char line[STRING_MAX]; - int n = 0, i; - printf(prompt); - while (n != 1) { - n = scanf("%d", &i); - fgets(line, STRING_MAX, stdin); - - } - return i; -} - - -/* loopback test -- send/rcv from 2 to 1000 bytes of random midi data */ -/**/ -void loopback_test() -{ - int outp; - int inp; - PmStream *midi_in; - PmStream *midi_out; - unsigned char msg[1024]; - char line[80]; - long len; - int i; - int data; - PmEvent event; - int shift; - - Pt_Start(1, 0, 0); - - printf("Connect a midi cable from an output port to an input port.\n"); - printf("This test will send random data via sysex message from output\n"); - printf("to input and check that the correct data was received.\n"); - outp = get_number("Type output device number: "); - /* Open output with 1ms latency -- when latency is non-zero, the Win32 - implementation supports sending sysex messages incrementally in a - series of buffers. This is nicer than allocating a big buffer for the - message, and it also seems to work better. Either way works. - */ - Pm_OpenOutput(&midi_out, outp, NULL, 0, NULL, NULL, latency); - inp = get_number("Type input device number: "); - /* since we are going to send and then receive, make sure the input buffer - is large enough for the entire message */ - Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL); - - srand((unsigned int) Pt_Time()); /* seed for random numbers */ - - while (1) { - PmError count; - long start_time; - long error_position; - long expected = 0; - long actual = 0; - printf("Type return to send message, q to quit: "); - fgets(line, STRING_MAX, stdin); - if (line[0] == 'q') goto cleanup; - - /* compose the message */ - len = rand() % 998 + 2; /* len only counts data bytes */ - msg[0] = (char) MIDI_SYSEX; /* start of SYSEX message */ - /* data bytes go from 1 to len */ - for (i = 0; i < len; i++) { - msg[i + 1] = rand() & 0x7f; /* MIDI data */ - } - /* final EOX goes in len+1, total of len+2 bytes in msg */ - msg[len + 1] = (char) MIDI_EOX; - - /* sanity check: before we send, there should be no queued data */ - count = Pm_Read(midi_in, &event, 1); - - if (count != 0) { - printf("Before sending anything, a MIDI message was found in\n"); - printf("the input buffer. Please try again.\n"); - break; - } - - /* send the message */ - printf("Sending %ld byte sysex message.\n", len + 2); - Pm_WriteSysEx(midi_out, 0, msg); - - /* receive the message and compare to msg[] */ - data = 0; - shift = 0; - i = 0; - start_time = Pt_Time(); - error_position = -1; - /* allow up to 2 seconds for transmission */ - while (data != MIDI_EOX && start_time + 2000 > Pt_Time()) { - count = Pm_Read(midi_in, &event, 1); - /* CAUTION: this causes busy waiting. It would be better to - be in a polling loop to avoid being compute bound. PortMidi - does not support a blocking read since this is so seldom - useful. There is no timeout, so if we don't receive a sysex - message, or at least an EOX, the program will hang here. - */ - if (count == 0) continue; - - /* printf("read %lx ", event.message); - fflush(stdout); */ - - /* compare 4 bytes of data until you reach an eox */ - for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) { - data = (event.message >> shift) & 0xFF; - if (data != msg[i] && error_position < 0) { - error_position = i; - expected = msg[i]; - actual = data; - } - i++; - } - } - if (error_position >= 0) { - printf("Error at byte %ld: sent %lx recd %lx\n", error_position, expected, actual); - } else if (i != len + 2) { - printf("Error: byte %d not received\n", i); - } else { - printf("Correctly "); - } - printf("received %d byte sysex message.\n", i); - } -cleanup: - Pm_Close(midi_out); - Pm_Close(midi_in); - return; -} - - -#define is_real_time_msg(msg) ((0xF0 & Pm_MessageStatus(msg)) == 0xF8) - - -void receive_sysex() -{ - char line[80]; - FILE *f; - PmStream *midi; - int shift = 0; - int data = 0; - int bytes_on_line = 0; - PmEvent msg; - - /* determine which output device to use */ - int i = get_number("Type input device number: "); - - /* open input device */ - Pm_OpenInput(&midi, i, NULL, 512, NULL, NULL); - printf("Midi Input opened, type file for sysex data: "); - - /* open file */ - fgets(line, STRING_MAX, stdin); - /* remove the newline character */ - if (strlen(line) > 0) line[strlen(line) - 1] = 0; - f = fopen(line, "w"); - if (!f) { - printf("Could not open %s\n", line); - Pm_Close(midi); - return; - } - - printf("Ready to receive a sysex message\n"); - - /* read data and write to file */ - while (data != MIDI_EOX) { - PmError count; - count = Pm_Read(midi, &msg, 1); - /* CAUTION: this causes busy waiting. It would be better to - be in a polling loop to avoid being compute bound. PortMidi - does not support a blocking read since this is so seldom - useful. - */ - if (count == 0) continue; - /* ignore real-time messages */ - if (is_real_time_msg(Pm_MessageStatus(msg.message))) continue; - - /* write 4 bytes of data until you reach an eox */ - for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) { - data = (msg.message >> shift) & 0xFF; - /* if this is a status byte that's not MIDI_EOX, the sysex - message is incomplete and there is no more sysex data */ - if (data & 0x80 && data != MIDI_EOX) break; - fprintf(f, "%2x ", data); - if (++bytes_on_line >= 16) { - fprintf(f, "\n"); - bytes_on_line = 0; - } - } - } - fclose(f); - Pm_Close(midi); -} - - -void send_sysex() -{ - char line[80]; - FILE *f; - PmStream *midi; - int data; - int shift = 0; - PmEvent msg; - - /* determine which output device to use */ - int i = get_number("Type output device number: "); - - msg.timestamp = 0; /* no need for timestamp */ - - /* open output device */ - Pm_OpenOutput(&midi, i, NULL, 0, NULL, NULL, latency); - printf("Midi Output opened, type file with sysex data: "); - - /* open file */ - fgets(line, STRING_MAX, stdin); - /* remove the newline character */ - if (strlen(line) > 0) line[strlen(line) - 1] = 0; - f = fopen(line, "r"); - if (!f) { - printf("Could not open %s\n", line); - Pm_Close(midi); - return; - } - - /* read file and send data */ - msg.message = 0; - while (1) { - /* get next byte from file */ - - if (fscanf(f, "%x", &data) == 1) { - /* printf("read %x, ", data); */ - /* OR byte into message at proper offset */ - msg.message |= (data << shift); - shift += 8; - } - /* send the message if it's full (shift == 32) or if we are at end */ - if (shift == 32 || data == MIDI_EOX) { - /* this will send sysex data 4 bytes at a time -- it would - be much more efficient to send multiple PmEvents at once - but this method is simpler. See Pm_WriteSysex for a more - efficient code example. - */ - Pm_Write(midi, &msg, 1); - msg.message = 0; - shift = 0; - } - if (data == MIDI_EOX) { /* end of message */ - fclose(f); - Pm_Close(midi); - return; - } - } -} - - -int main() -{ - int i; - char line[80]; - - /* list device information */ - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - printf("%d: %s, %s", i, info->interf, info->name); - if (info->input) printf(" (input)"); - if (info->output) printf(" (output)"); - printf("\n"); - } - latency = get_number("Latency in milliseconds (0 to send data immediatedly,\n" - " >0 to send timestamped messages): "); - while (1) { - printf("Type r to receive sysex, s to send," - " l for loopback test, q to quit: "); - fgets(line, STRING_MAX, stdin); - switch (line[0]) { - case 'r': - receive_sysex(); - break; - case 's': - send_sysex(); - break; - case 'l': - loopback_test(); - case 'q': - exit(0); - default: - break; - } - } - return 0; -} - - - - - diff --git a/portmidi/pm_test/sysex.dsp b/portmidi/pm_test/sysex.dsp deleted file mode 100644 index 329d3ef96..000000000 --- a/portmidi/pm_test/sysex.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="sysex" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=sysex - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "sysex.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "sysex.mak" CFG="sysex - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "sysex - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "sysex - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "sysex - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "sysexRelease" -# PROP Intermediate_Dir "sysexRelease" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\pm_common" /I "..\porttime" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\Release\portmidi.lib ..\porttime\Release\porttime.lib ..\pm_win\Release\pm_dll.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "sysex - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "sysexDebug" -# PROP Intermediate_Dir "sysexDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\pm_common" /I "..\porttime" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "sysex - Win32 Release" -# Name "sysex - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\sysex.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/portmidi/pm_test/test.c b/portmidi/pm_test/test.c deleted file mode 100644 index ade8564d2..000000000 --- a/portmidi/pm_test/test.c +++ /dev/null @@ -1,469 +0,0 @@ -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define INPUT_BUFFER_SIZE 100 -#define OUTPUT_BUFFER_SIZE 0 -#define DRIVER_INFO NULL -#define TIME_PROC ((long (*)(void *)) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ - -long latency = 0; - -/* crash the program to test whether midi ports are closed */ -/**/ -void doSomethingReallyStupid() { - int * tmp = NULL; - *tmp = 5; -} - - -/* exit the program without any explicit cleanup */ -/**/ -void doSomethingStupid() { - assert(0); -} - - -/* read a number from console */ -/**/ -int get_number(char *prompt) -{ - char line[STRING_MAX]; - int n = 0, i; - printf(prompt); - while (n != 1) { - n = scanf("%d", &i); - fgets(line, STRING_MAX, stdin); - - } - return i; -} - - -/* - * the somethingStupid parameter can be set to simulate a program crash. - * We want PortMidi to close Midi ports automatically in the event of a - * crash because Windows does not (and this may cause an OS crash) - */ -void main_test_input(unsigned int somethingStupid) { - PmStream * midi; - PmError status, length; - PmEvent buffer[1]; - int num = 10; - int i = get_number("Type input number: "); - /* It is recommended to start timer before Midi; otherwise, PortMidi may - start the timer with its (default) parameters - */ - TIME_START; - - /* open input device */ - Pm_OpenInput(&midi, - i, - DRIVER_INFO, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - - printf("Midi Input opened. Reading %d Midi messages...\n",num); - Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Poll(midi)) { - Pm_Read(midi, buffer, 1); - } - /* now start paying attention to messages */ - i = 0; /* count messages as they arrive */ - while (i < num) { - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi,buffer, 1); - if (length > 0) { - printf("Got message %d: time %ld, %2lx %2lx %2lx\n", - i, - buffer[0].timestamp, - Pm_MessageStatus(buffer[0].message), - Pm_MessageData1(buffer[0].message), - Pm_MessageData2(buffer[0].message)); - i++; - } else { - assert(0); - } - } - /* simulate crash if somethingStupid is 1 or 2 */ - if ((i > (num/2)) && (somethingStupid == 1)) { - doSomethingStupid(); - } else if ((i > (num/2)) && (somethingStupid == 2)) { - doSomethingReallyStupid(); - } - } - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close..."); - - Pm_Close(midi); - printf("done closing..."); -} - - - -void main_test_output() { - PmStream * midi; - char line[80]; - long off_time; - int chord[] = { 60, 67, 76, 83, 90 }; - #define chord_size 5 - PmEvent buffer[chord_size]; - PmTimestamp timestamp; - - /* determine which output device to use */ - int i = get_number("Type output number: "); - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device -- since PortMidi avoids opening a timer - when latency is zero, we will pass in a NULL timer pointer - for that case. If PortMidi tries to access the time_proc, - we will crash, so this test will tell us something. */ - Pm_OpenOutput(&midi, - i, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - (latency == 0 ? NULL : TIME_PROC), - (latency == 0 ? NULL : TIME_INFO), - latency); - printf("Midi Output opened with %ld ms latency.\n", latency); - - /* output note on/off w/latency offset; hold until user prompts */ - printf("ready to send program 1 change... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - /* if we were writing midi for immediate output, we could always use - timestamps of zero, but since we may be writing with latency, we - will explicitly set the timestamp to "now" by getting the time. - The source of timestamps should always correspond to the TIME_PROC - and TIME_INFO parameters used in Pm_OpenOutput(). */ - buffer[0].timestamp = TIME_PROC(TIME_INFO); - buffer[0].message = Pm_Message(0xC0, 0, 0); - Pm_Write(midi, buffer, 1); - - printf("ready to note-on... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - buffer[0].timestamp = TIME_PROC(TIME_INFO); - buffer[0].message = Pm_Message(0x90, 60, 100); - Pm_Write(midi, buffer, 1); - printf("ready to note-off... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - buffer[0].timestamp = TIME_PROC(TIME_INFO); - buffer[0].message = Pm_Message(0x90, 60, 0); - Pm_Write(midi, buffer, 1); - - /* output short note on/off w/latency offset; hold until user prompts */ - printf("ready to note-on (short form)... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - Pm_WriteShort(midi, TIME_PROC(TIME_INFO), - Pm_Message(0x90, 60, 100)); - printf("ready to note-off (short form)... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - Pm_WriteShort(midi, TIME_PROC(TIME_INFO), - Pm_Message(0x90, 60, 0)); - - /* output several note on/offs to test timing. - Should be 1s between notes */ - printf("chord will arpeggiate if latency > 0\n"); - printf("ready to chord-on/chord-off... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - timestamp = TIME_PROC(TIME_INFO); - for (i = 0; i < chord_size; i++) { - buffer[i].timestamp = timestamp + 1000 * i; - buffer[i].message = Pm_Message(0x90, chord[i], 100); - } - Pm_Write(midi, buffer, chord_size); - - off_time = timestamp + 1000 + chord_size * 1000; - while (TIME_PROC(TIME_INFO) < off_time) - /* busy wait */; - for (i = 0; i < chord_size; i++) { - buffer[i].timestamp = timestamp + 1000 * i; - buffer[i].message = Pm_Message(0x90, chord[i], 0); - } - Pm_Write(midi, buffer, chord_size); - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close and terminate... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - - Pm_Close(midi); - Pm_Terminate(); - printf("done closing and terminating...\n"); -} - - -void main_test_both() -{ - int i = 0; - int in, out; - PmStream * midi, * midiOut; - PmEvent buffer[1]; - PmError status, length; - int num = 10; - - in = get_number("Type input number: "); - out = get_number("Type output number: "); - - /* In is recommended to start timer before PortMidi */ - TIME_START; - - Pm_OpenOutput(&midiOut, - out, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - latency); - printf("Midi Output opened with %ld ms latency.\n", latency); - /* open input device */ - Pm_OpenInput(&midi, - in, - DRIVER_INFO, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - printf("Midi Input opened. Reading %d Midi messages...\n",num); - Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Poll(midi)) { - Pm_Read(midi, buffer, 1); - } - i = 0; - while (i < num) { - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi,buffer,1); - if (length > 0) { - Pm_Write(midiOut, buffer, 1); - printf("Got message %d: time %ld, %2lx %2lx %2lx\n", - i, - buffer[0].timestamp, - Pm_MessageStatus(buffer[0].message), - Pm_MessageData1(buffer[0].message), - Pm_MessageData2(buffer[0].message)); - i++; - } else { - assert(0); - } - } - } - - /* since close device should not needed, lets get - rid of it just to make sure program exit closes MIDI devices */ - /* Pm_Close(midi); - Pm_Close(midiOut); - Pm_Terminate(); */ -} - - -/* main_test_stream exercises windows winmm API's stream mode */ -/* The winmm stream mode is used for latency>0, and sends - timestamped messages. The timestamps are relative (delta) - times, whereas PortMidi times are absolute. Since peculiar - things happen when messages are not always sent in advance, - this function allows us to exercise the system and test it. - */ -void main_test_stream() { - PmStream * midi; - char line[80]; - PmEvent buffer[16]; - - /* determine which output device to use */ - int i = get_number("Type output number: "); - - latency = 500; /* ignore LATENCY for this test and - fix the latency at 500ms */ - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device */ - Pm_OpenOutput(&midi, - i, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - latency); - printf("Midi Output opened with %ld ms latency.\n", latency); - - /* output note on/off w/latency offset; hold until user prompts */ - printf("ready to send output... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - - /* if we were writing midi for immediate output, we could always use - timestamps of zero, but since we may be writing with latency, we - will explicitly set the timestamp to "now" by getting the time. - The source of timestamps should always correspond to the TIME_PROC - and TIME_INFO parameters used in Pm_OpenOutput(). */ - buffer[0].timestamp = TIME_PROC(TIME_INFO); - buffer[0].message = Pm_Message(0xC0, 0, 0); - buffer[1].timestamp = buffer[0].timestamp; - buffer[1].message = Pm_Message(0x90, 60, 100); - buffer[2].timestamp = buffer[0].timestamp + 1000; - buffer[2].message = Pm_Message(0x90, 62, 100); - buffer[3].timestamp = buffer[0].timestamp + 2000; - buffer[3].message = Pm_Message(0x90, 64, 100); - buffer[4].timestamp = buffer[0].timestamp + 3000; - buffer[4].message = Pm_Message(0x90, 66, 100); - buffer[5].timestamp = buffer[0].timestamp + 4000; - buffer[5].message = Pm_Message(0x90, 60, 0); - buffer[6].timestamp = buffer[0].timestamp + 4000; - buffer[6].message = Pm_Message(0x90, 62, 0); - buffer[7].timestamp = buffer[0].timestamp + 4000; - buffer[7].message = Pm_Message(0x90, 64, 0); - buffer[8].timestamp = buffer[0].timestamp + 4000; - buffer[8].message = Pm_Message(0x90, 66, 0); - - Pm_Write(midi, buffer, 9); -#ifdef SEND8 - /* Now, we're ready for the real test. - Play 4 notes at now, now+500, now+1000, and now+1500 - Then wait until now+2000. - Play 4 more notes as before. - We should hear 8 evenly spaced notes. */ - now = TIME_PROC(TIME_INFO); - for (i = 0; i < 4; i++) { - buffer[i * 2].timestamp = now + (i * 500); - buffer[i * 2].message = Pm_Message(0x90, 60, 100); - buffer[i * 2 + 1].timestamp = now + 250 + (i * 500); - buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0); - } - Pm_Write(midi, buffer, 8); - - while (Pt_Time() < now + 2500) - /* busy wait */; - /* now we are 500 ms behind schedule, but since the latency - is 500, the delay should not be audible */ - now += 2000; - for (i = 0; i < 4; i++) { - buffer[i * 2].timestamp = now + (i * 500); - buffer[i * 2].message = Pm_Message(0x90, 60, 100); - buffer[i * 2 + 1].timestamp = now + 250 + (i * 500); - buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0); - } - Pm_Write(midi, buffer, 8); -#endif - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close and terminate... (type RETURN):"); - fgets(line, STRING_MAX, stdin); - - Pm_Close(midi); - Pm_Terminate(); - printf("done closing and terminating...\n"); -} - - -void show_usage() -{ - printf("Usage: test [-h] [-l latency-in-ms]\n"); - exit(0); -} - -int main(int argc, char *argv[]) -{ - int i = 0, n = 0; - char line[STRING_MAX]; - int test_input = 0, test_output = 0, test_both = 0, somethingStupid = 0; - int stream_test = 0; - int latency_valid = FALSE; - - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) { - i = i + 1; - latency = atoi(argv[i]); - printf("Latency will be %ld\n", latency); - latency_valid = TRUE; - } else { - show_usage(); - } - } - - while (!latency_valid) { - printf("Latency in ms: "); - if (scanf("%ld", &latency) == 1) { - latency_valid = TRUE; - } - } - - /* determine what type of test to run */ - printf("begin portMidi test...\n"); - printf("%s%s%s%s%s", - "enter your choice...\n 1: test input\n", - " 2: test input (fail w/assert)\n", - " 3: test input (fail w/NULL assign)\n", - " 4: test output\n 5: test both\n", - " 6: stream test\n"); - while (n != 1) { - n = scanf("%d", &i); - fgets(line, STRING_MAX, stdin); - switch(i) { - case 1: - test_input = 1; - break; - case 2: - test_input = 1; - somethingStupid = 1; - break; - case 3: - test_input = 1; - somethingStupid = 2; - break; - case 4: - test_output = 1; - break; - case 5: - test_both = 1; - break; - case 6: - stream_test = 1; - break; - default: - printf("got %d (invalid input)\n", n); - break; - } - } - - /* list device information */ - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (((test_input | test_both) & info->input) | - ((test_output | test_both | stream_test) & info->output)) { - printf("%d: %s, %s", i, info->interf, info->name); - if (info->input) printf(" (input)"); - if (info->output) printf(" (output)"); - printf("\n"); - } - } - - /* run test */ - if (stream_test) { - main_test_stream(); - } else if (test_input) { - main_test_input(somethingStupid); - } else if (test_output) { - main_test_output(); - } else if (test_both) { - main_test_both(); - } - - printf("finished portMidi test...type ENTER to quit..."); - fgets(line, STRING_MAX, stdin); - return 0; -} diff --git a/portmidi/pm_test/test.dsp b/portmidi/pm_test/test.dsp deleted file mode 100644 index 66ba3e913..000000000 --- a/portmidi/pm_test/test.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=test - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "test.mak" CFG="test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "testRelease" -# PROP Intermediate_Dir "testRelease" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\Release\portmidi.lib ..\porttime\Release\porttime.lib ..\pm_win\Release\pm_dll.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "testDebug" -# PROP Intermediate_Dir "testDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "test - Win32 Release" -# Name "test - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\test.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/portmidi/pm_test/txdata.syx b/portmidi/pm_test/txdata.syx deleted file mode 100644 index 1e06e5a6e..000000000 --- a/portmidi/pm_test/txdata.syx +++ /dev/null @@ -1,257 +0,0 @@ -20 0 1d 4 c 6 0 34 1 4d 4 d 1f 7 3 6 - c 5e 4 4d d b 18 5 3 6 0 3d 1 4a 16 18 -1f 8 3 6 d 0 1 63 4 13 3a 23 0 0 0 2 - c 2 4 0 63 32 0 0 0 32 0 47 72 61 6e 64 -50 69 61 6e 6f 63 63 63 32 32 32 0 0 0 0 0 -10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 9 9 f c 27 2 35 37 10 1f 4 3 4 - d 19 4 56 5 16 1f f 8 d c 0 43 60 4 e -1f c 3 7 e 0 43 63 5 10 3c 14 8 2 1b 56 - 5 2 4 0 63 32 0 0 0 32 0 4c 6f 54 69 6e -65 38 31 5a 20 63 63 63 32 32 32 0 7f 0 1 0 -18 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f e f e 9 0 3 43 2d e 1f f 5 7 - f 16 43 5a 0 0 1f 12 6 8 d 0 3 63 4 0 -1f 12 6 8 f 0 2 63 4 6 34 14 0 1 2 4e -18 2 4 0 63 32 0 32 0 32 0 44 79 6e 6f 6d -69 74 65 45 50 63 63 63 32 32 32 0 70 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f b 1 b 8 18 40 5f a e 1f 1f 0 a - f 0 40 5f 4 0 1f 1f 0 a f 0 40 63 5 6 -1f 1f 0 a f 0 40 5f 0 8 1f 20 0 3 0 5a -18 4 4 0 63 32 32 0 0 32 0 50 65 72 63 4f -72 67 61 6e 20 63 63 63 32 32 32 0 0 0 0 0 - 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f b 7 f 9 0 4 49 13 13 1f 8 7 5 - e 0 2 58 0 c 1f 6 4 6 f 23 3 46 10 a -1f 7 8 c d 0 2 63 8 b 2 1c 0 0 0 52 -18 4 4 0 63 32 0 32 0 32 0 54 68 69 6e 20 -43 6c 61 76 20 63 63 63 32 32 32 0 70 0 20 0 -10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f c 0 6 1 a 4 50 20 e 1f c 0 6 - 1 a 4 50 1f 8 1f b 9 5 e 0 2 63 5 e -1f b 9 5 e 0 3 63 4 8 4 1a 0 0 0 52 -1d 2 4 0 63 32 0 32 0 32 0 42 72 69 74 65 -43 65 6c 73 74 63 63 63 32 32 32 0 20 0 26 0 - 1 0 8 4 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 f 1f 4 8 f 0 3a 51 4 b e 1f 0 8 - f 0 22 4b 4 3 f 1a b 8 d 0 3b 36 9 3 -12 1f 0 8 f 0 22 5d 4 b 3a 1e 19 5 0 52 -18 4 4 0 63 32 0 0 0 32 0 54 72 75 6d 70 -65 74 38 31 5a 63 63 63 32 32 32 0 0 0 50 0 -51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 c 5 0 8 0 0 2 4a 4 b f 1f 0 8 - f 0 2 3f 4 3 1f f 0 8 0 23 3 44 b 3 -10 1f 0 9 f 0 2 5e 4 c 3a 1f 19 7 0 52 -18 4 4 0 63 32 0 0 0 32 0 46 6c 75 67 65 -6c 68 6f 72 6e 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 10 1f 0 8 f 0 42 4a 0 3 11 1f 0 8 - f a 43 51 0 3 11 9 0 8 d 0 42 2b 16 6 -10 1f 0 9 f 0 42 63 4 b 3a 1e 9 9 0 5a -24 4 4 0 63 32 31 0 0 32 0 52 61 73 70 41 -6c 74 6f 20 20 63 63 63 32 32 32 0 10 0 20 0 -54 0 20 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 10 9 2 6 d 0 41 3e 4 15 c b 2 3 - e 0 41 4f 4 12 c e 2 8 d 0 42 4b a 1c - d b 1 9 e 0 3 63 a 14 0 23 f 2 1b 5e -18 4 5 0 63 28 50 32 0 32 0 48 61 72 6d 6f -6e 69 63 61 20 63 63 63 32 32 32 0 50 10 50 0 -50 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1c 2 0 4 e 63 0 4e 4 3 d 5 0 6 - e 63 1 56 a 8 12 7 0 6 9 63 2 47 1b e - a a 0 5 f 0 1 63 4 b 32 1a 8 d 0 52 - c 4 4 0 63 32 0 0 0 32 0 44 6f 75 62 6c -65 42 61 73 73 63 63 63 32 32 32 0 10 0 0 0 - 3 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 b 4 0 4 f 14 2 49 9 6 a 7 0 4 - f 14 2 51 a 0 8 1f 0 5 f 0 1 63 9 6 - a 1f 0 5 f 0 1 63 a 0 3c 1f 6 9 0 52 - 5 4 4 0 63 32 0 0 0 32 0 48 69 53 74 72 -69 6e 67 20 31 63 63 63 32 32 32 0 2 0 30 0 -32 0 10 5 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 10 13 f 4 a 0 3 3b 14 14 1f e 8 7 - 9 0 2 42 5 e 18 13 d 9 c 0 2 3c 13 8 -1f 11 7 4 f 0 42 63 4 10 3a 1b 0 0 0 52 -1d 4 4 0 63 32 0 0 0 32 0 48 61 72 70 20 -20 20 20 20 20 63 63 63 32 32 32 8 0 0 21 0 - 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 6 6 4 f 0 40 48 5 0 c 8 7 5 - f 5 0 52 4 0 f 7 3 7 e 8 3 63 4 6 - f 8 4 5 f 0 3 63 4 6 7c 1f 0 6 0 4a -11 2 4 0 63 32 0 0 0 32 0 46 61 6e 66 61 -72 54 70 74 73 63 63 63 32 32 32 6 1 0 38 0 - 8 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 d b 0 1 c 0 2 2c 3d 3 d 7 0 1 - c 0 2 1f 3c 3 d 1f 0 5 f 0 2 63 5 6 - d 1f 0 5 f 0 2 63 4 0 3c 63 0 2f 0 53 -11 4 4 0 63 32 0 0 0 32 0 42 72 65 61 74 -68 4f 72 67 6e 63 63 63 32 32 32 4 30 5 50 0 -11 0 18 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 9 0 6 0 27 2 51 19 b 1c 6 0 8 - 0 37 2 47 a 3 1f a 0 9 0 3d 2 4d a e -1f 12 8 8 f 0 3 61 4 b 28 1f 0 3 0 52 - c 3 4 0 63 32 1 32 0 32 0 4e 79 6c 6f 6e -47 75 69 74 20 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f e e f f 0 3 48 2d 6 1f f 4 f - f 25 3 5b 0 0 1f 12 6 c e 1c 3 55 0 10 -1f 13 7 8 e 6 4 62 4 e 3b 14 0 0 0 42 -18 2 4 0 63 32 0 32 0 32 0 47 75 69 74 61 -72 20 23 31 20 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 19 8 a 3 0 3 63 10 18 1f c 5 b - 5 0 3 52 0 b 1f 19 6 b 5 0 3 63 a 16 -1f f 11 9 7 0 4 63 4 3 3a 14 0 0 0 42 -18 2 4 0 63 32 0 32 0 32 0 46 75 6e 6b 79 -20 50 69 63 6b 63 63 63 32 32 32 0 30 0 0 0 - 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 1 0 8 4 0 3 3d a 1e 1f 1 0 8 - 0 0 0 43 0 10 1f 9 6 8 c 1b 7 46 1c 1e -1f 9 0 9 9 0 1 63 4 3 3a 1c 0 0 0 52 - c 4 5 0 63 4b 0 0 0 32 0 45 6c 65 63 42 -61 73 73 20 31 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f f f e 9 0 3 46 1d 16 1f f 5 e - e d 3 63 0 b 1f 13 6 5 d 1c 3 63 0 0 -1f 13 6 8 f 0 4 63 4 6 3b 1f 0 0 0 42 - c 4 4 0 63 32 0 32 0 32 0 53 79 6e 46 75 -6e 6b 42 61 73 63 63 63 32 32 32 d 6c 0 0 0 -70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 10 7 8 3 0 3 4f 4 3 1f 9 0 8 - 0 0 1 4a 0 b 1f 11 0 8 0 0 1 47 4 8 -1f 9 0 8 0 0 0 63 0 b 39 19 0 7 0 52 - c 2 4 0 63 32 0 32 0 32 0 4c 61 74 65 6c -79 42 61 73 73 63 63 63 32 32 32 2 0 0 0 0 -40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 13 12 0 9 d 22 0 51 0 b 1f 14 0 5 - 8 24 40 5c 0 3 1f 11 0 6 c 2c 0 53 9 0 -10 1f 0 b f 0 0 5c a e 3a 22 11 e 1e 5e -18 7 4 0 63 32 0 32 0 32 0 53 79 6e 63 20 -4c 65 61 64 20 63 63 63 32 32 32 0 70 0 40 0 - 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 13 1e 0 9 e 0 0 63 3f b 1f 14 0 5 - e 24 1 51 4 3 1f 14 0 f 1 0 41 4d 8 3 - f 1f 0 b f 0 2 63 4 b 3b 20 11 12 33 56 -18 4 4 0 63 37 e 0 0 32 0 4a 61 7a 7a 20 -46 6c 75 74 65 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 15 13 d 3 d 1e 2 50 18 e 15 14 9 4 - c 1e 2 56 11 8 1b 1f f 7 f 0 1 63 4 6 -1a 1f e 6 f 0 2 63 4 0 7c b 0 8 0 62 -18 4 4 0 63 32 0 0 0 32 0 4a 61 76 61 20 -4a 69 76 65 20 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 0 0 4 f 0 40 63 3c 0 b 8 7 7 - f 5 0 63 4 6 f 5 3 7 f 8 0 3b 5 6 - e 8 4 5 f 0 3 63 3 0 7e 1d 6 f 0 4a -11 0 4 0 63 32 0 0 0 32 0 42 61 61 64 42 -72 65 61 74 68 63 63 63 32 32 32 6 30 0 38 0 - 1 0 46 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 0 0 4 f 0 40 47 2f 0 e 8 7 7 - f 5 0 4c 0 6 13 1c d c 6 8 0 63 5 6 -14 11 d b 0 0 3 63 4 0 7a 10 0 51 0 68 -17 0 4 0 63 32 0 0 0 32 0 56 6f 63 61 6c -4e 75 74 73 20 63 63 63 32 32 32 6 30 0 30 0 - 1 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 1f 0 5 f 0 0 41 32 3 1f 14 10 5 - 5 1 2 63 7 3 1f b 12 8 f 0 1 63 c 3 -1f 1f f 8 f 0 1 63 4 3 39 23 0 0 0 62 -18 7 4 0 63 32 0 0 0 32 0 57 61 74 65 72 -47 6c 61 73 73 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 16 2 0 4 6 9 1 4f 8 0 19 e 1 4 - 0 20 1 43 19 0 1f 12 10 6 7 0 0 54 3d 3 -16 d 6 6 2 1e 3 61 8 e 3a 20 1 14 0 42 - c 2 4 2 63 63 63 0 0 32 0 46 75 7a 7a 79 -20 4b 6f 74 6f 63 63 63 32 32 32 0 0 0 0 b -50 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1c 8 0 3 e 0 1 55 12 3 1c 7 0 1 - e 2e 1 58 27 b e 4 0 2 a 0 2 63 4 a - d 9 0 2 c 1 2 63 10 b 4 54 0 47 0 53 -18 7 4 0 63 32 0 0 0 32 0 42 72 74 68 62 -65 6c 6c 73 20 63 63 63 32 32 32 0 4 0 40 0 -40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1a 4 1 1 b 16 0 47 5 3 15 e 0 1 - d 0 0 4c 5 16 1c 6 4 2 7 0 0 63 4 16 -18 18 3 1 e 0 0 5e 4 10 24 7 0 4 0 62 -24 4 4 0 63 32 0 0 0 32 0 54 75 62 65 20 -42 65 6c 6c 73 63 63 63 32 32 32 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 1f 13 3 0 0 0 5f 3d 6 1f 12 13 2 - 0 0 1 52 5 2 1f 14 13 3 0 0 1 56 28 5 -1e b 13 f 9 0 0 63 6 3 3b 63 0 63 0 73 -23 7 4 0 63 32 0 0 0 32 0 4e 6f 69 73 65 -20 53 68 6f 74 63 63 63 32 32 32 8 0 0 0 8 - 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1f 16 0 3 7 0 1 50 0 3 1f 18 3 3 - 3 22 0 63 0 14 1d 7 6 3 6 0 1 3c 8 3 -1f 5 7 3 0 0 1 63 4 1b 39 23 0 8 0 42 -18 4 4 0 63 32 0 0 0 32 0 48 61 6e 64 20 -44 72 75 6d 20 63 63 63 32 32 32 0 1 0 3 0 - 1 0 1 3 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 7d f7 \ No newline at end of file diff --git a/portmidi/pm_win/README_WIN.txt b/portmidi/pm_win/README_WIN.txt deleted file mode 100644 index df120e960..000000000 --- a/portmidi/pm_win/README_WIN.txt +++ /dev/null @@ -1,183 +0,0 @@ -File: PortMidi Win32 Readme -Author: Belinda Thom, June 16 2002 -Revised by: Roger Dannenberg, June 2002, May 2004 - -============================================================================= -USING PORTMIDI: -============================================================================= - -PortMidi has been created using a DLL because the Win32 MMedia API doesn't -handle midiInput properly in the debugger. Specifically, it doesn't clean up -after itself if the user (i.e. you, a PortMidi application) hasn't explicitly -closed all open midi input devices. This lack of cleanup can lead to much -pain and agony, including the blue-screen-of-death. This situation becomes -increasingly unacceptable when you are debugging your code, so a portMidi DLL -seemed to be the most elegant solution. - -Using Microsoft Visual C++ project files (provided with PortMidi), there -are two configurations of the PortMidi library. The Debug version is -intended for debugging, especially in a console application. The Debug -version enables some extra error checking and outputs some text as well -as a prompt to type ENTER so that you don't lose any debugging text when -the program exits. You can turn off this extra debugging info by taking -out the compile-time definition for DEBUG. This debugging version also -defines PM_CHECK_ERRORS, which forces a check for error return codes from -every call to PortMidi. You can disable this checking (especially if you -want to handle error codes in your own way) by removing PM_CHECK_ERRORS -from the predefined symbols list in the Settings dialog box. - -PortMidi is designed to run without a console and should work perfectly -well within a graphical user interface application. The Release version -is both optimized and lacking the debugging printout code of the Debug -version. - -Read the portmidi.h file for PortMidi API details on using the PortMidi API. -See <...>\pm_dll_test\test.c or <...>\multithread\test.c for usage examples. - -============================================================================= -TO INSTALL PORTMIDI: -============================================================================= -1) download portmidi.zip - -2) unzip portmidi.zip into directory: <...>\portmidi - -============================================================================= -TO COMPILE PORTMIDI: -============================================================================= - -3) go to this directory - -4) click on the portmidi.dsw workspace - -5) the following projects exist within this workspace: - - portmidi (the PortMidi library) - - pm_dll (the dll library used to close midi ports on program exit) - - porttime (a small portable library implementing timer facilities) - - test (simple midi I/O testing) - - multithread (an example illustrating low-latency MIDI processing - using a dedicated low-latency thread) - - sysex (simple sysex message I/O testing) - - latency (uses porttime to measure system latency) - -6) verify that all project settings are for Win32 Debug release: - - hit Alt-F7 - - highlight all three projects in left part of Project Settings window; - - "Settings For" should say "Win32 Debug" - -7) set pm_dll as the active project (e.g. Project->Select Active Project) - -8) use Build->Batch Build ... to build everything in the project - -9) The settings for these projects were distributed in the zip file, so - compile should just work. - -10) IMPORTANT! PortMidi uses a DLL, pm_dll.dll, but there is no simple way - to set up projects to use pm_dll. THEREFORE, you need to copy DLLs - as follows (you can do this with <...>\portmidi\pm_win\copy-dll.bat): - copy <...>\portmidi\pm_win\Debug\pm_dll.dll to: - <...>\portmidi\pm_test\latencyDebug\pm_dll.dll - <...>\portmidi\pm_test\midithreadDebug\pm_dll.dll - <...>\portmidi\pm_test\sysexDebug\pm_dll.dll - <...>\portmidi\pm_test\testDebug\pm_dll.dll - <...>\portmidi\pm_test\midithruDebug\pm_dll.dll - and copy <...>\portmidi\pm_win\Release\pm_dll.dll to: - <...>\portmidi\pm_test\latencyRelease\pm_dll.dll - <...>\portmidi\pm_test\midithreadRelease\pm_dll.dll - <...>\portmidi\pm_test\sysexRelease\pm_dll.dll - <...>\portmidi\pm_test\testRelease\pm_dll.dll - <...>\portmidi\pm_test\midithruRelease\pm_dll.dll - each time you rebuild the pm_dll project, these copies must be redone! - - Since Windows will look in the executable directory for DLLs, we - recommend that you always install a copy of pm_dll.dll (either the - debug version or the release version) in the same directory as the - application using PortMidi. The release DLL is about 40KB. This will - ensure that the application uses the correct DLL. - -11) run test project; use the menu that shows up from the command prompt to - test that portMidi works on your system. tests include: - - verify midi output works - - verify midi input works - - verify midi input w/midi thru works - -12) run other projects if you wish: sysex, latency, and midithread - -============================================================================ -TO CREATE YOUR OWN PORTMIDI CLIENT APPLICATION: -============================================================================ - -NOTE: this section needs to be reviewed and tested. My suggestion would -be to copy the test project file (test.dsp) and modify it. -RBD - -The easiest way is to start a new project w/in the portMidi workspace: - -1) To open new project: - - File->New->Projects - - Location: <...>\portmidi\<yourProjectName> - - check Add to current workspace - - select Win32 Console Application (recommended for now) - - do *NOT* select the "make dependency" box (you will explicitly do this - in the next step) - - Click OK - - Select "An Empty Project" and click Finish - -2) Now this project will be the active project. Make it explicitly depend - on PortMidi dll: - - Project->Dependencies - - Click pm_dll - -3) Important! in order to be able to use portMidi DLL from your new project - and set breakpoints, copy following files from <...>\pm_dll\Debug into - <...>\<yourProjectName>\Debug directory: - pm_dll.lib - pm_dll.dll - each time you rebuild pm_dll, these copies must be redone! - -4) add whatever files you wish to add to your new project, using portMidi - calls as desired (see USING PORTMIDI at top of this readme) - -5) when you include portMidi files, do so like this: - - #include "..\pm_dll\portmidi.h" - - etc. - -6) build and run your project - -============================================================================ -DESIGN NOTES -============================================================================ - -The DLL is used so that PortMidi can (usually) close open devices when the -program terminates. Failure to close input devices under WinNT, Win2K, and -probably later systems causes the OS to crash. - -This is accomplished with a .LIB/.DLL pair, linking to the .LIB -in order to access functions in the .DLL. - -PortMidi for Win32 exists as a simple library, -with Win32-specific code in pmwin.c and MM-specific code in pmwinmm.c. -pmwin.c uses a DLL in pmdll.c to call Pm_Terminate() when the program -exits to make sure that all MIDI ports are closed. - -Orderly cleanup after errors are encountered is based on a fixed order of -steps and state changes to reflect each step. Here's the order: - -To open input: - initialize return value to NULL - - allocate the PmInternal strucure (representation of PortMidiStream) - return value is (non-null) PmInternal structure - - allocate midi buffer - set buffer field of PmInternal structure - - call system-dependent open code - - allocate midiwinmm_type for winmm dependent data - set descriptor field of PmInternal structure - - open device - set handle field of midiwinmm_type structure - - allocate buffer 1 for sysex - buffer is added to input port - - allocate buffer 2 for sysex - buffer is added to input port - - return - - return - - - diff --git a/portmidi/pm_win/copy-dll.bat b/portmidi/pm_win/copy-dll.bat deleted file mode 100644 index 34ccbeddf..000000000 --- a/portmidi/pm_win/copy-dll.bat +++ /dev/null @@ -1,13 +0,0 @@ -copy Debug\pm_dll.dll ..\pm_test\testDebug\pm_dll.dll -copy Debug\pm_dll.dll ..\pm_test\sysexDebug\pm_dll.dll -copy Debug\pm_dll.dll ..\pm_test\midithreadDebug\pm_dll.dll -copy Debug\pm_dll.dll ..\pm_test\latencyDebug\pm_dll.dll -copy Debug\pm_dll.dll ..\pm_test\midithruDebug\pm_dll.dll - -copy Release\pm_dll.dll ..\pm_test\testRelease\pm_dll.dll -copy Release\pm_dll.dll ..\pm_test\sysexRelease\pm_dll.dll -copy Release\pm_dll.dll ..\pm_test\midithreadRelease\pm_dll.dll -copy Release\pm_dll.dll ..\pm_test\latencyRelease\pm_dll.dll -copy Release\pm_dll.dll ..\pm_test\midithruRelease\pm_dll.dll - - diff --git a/portmidi/pm_win/debugging_dlls.txt b/portmidi/pm_win/debugging_dlls.txt deleted file mode 100644 index 82b81a5b5..000000000 --- a/portmidi/pm_win/debugging_dlls.txt +++ /dev/null @@ -1,145 +0,0 @@ -======================================================================================================================== -Methods for Debugging DLLs -======================================================================================================================== -If you have the source for both the DLL and the calling program, open the project for the calling executable file and -debug the DLL from there. If you load a DLL dynamically, you must specify it in the Additional DLLs category of the -Debug tab in the Project Settings dialog box. - -If you have the source for the DLL only, open the project that builds the DLL. Use the Debug tab in the Project -Settings dialog box to specify the executable file that calls the DLL. - -You can also debug a DLL without a project. For example, maybe you just picked up a DLL and source code but you -don’t have an associated project or workspace. You can use the Open command on the File menu to select the .DLL -file you want to debug. The debug information should be in either the .DLL or the related .PDB file. After -Visual C++ opens the file, on the Build menu click Start Debug and Go to begin debugging. - -To debug a DLL using the project for the executable file - -From the Project menu, click Settings. -The Project Settings dialog box appears. - -Choose the Debug tab. - - -In the Category drop-down list box, select General. - - -In the Program Arguments text box, type any command-line arguments required by the executable file. - - -In the Category drop-down list box, select Additional DLLs. - - -In the Local Name column, type the names of DLLs to debug. -If you are debugging remotely, the Remote Name column appears. In this column, type the complete path for the -remote module to map to the local module name. - -In the Preload column, select the check box if you want to load the module before debugging begins. - - -Click OK to store the information in your project. - - -From the Build menu, click Start Debug and Go to start the debugger. -You can set breakpoints in the DLL or the calling program. You can open a source file for the DLL and set breakpoints -in that file, even though it is not a part of the executable file’s project. - -To debug a DLL using the project for the DLL - -From the Project menu, click Settings. -The Project Settings dialog box appears. - -Choose the Debug tab. - - -In the Category drop-down list box, select General. - - -In the Executable For Debug Session text box, type the name of the executable file that calls the DLL. - - -In the Category list box, select Additional DLLs. - - -In the Local Module Name column, type the name of the DLLs you want to debug. - - -Click OK to store the information in your project. - - -Set breakpoints as required in your DLL source files or on function symbols in the DLL. - - -From the Build menu, click Start Debug and Go to start the debugger. -To debug a DLL created with an external project - -From the Project menu, click Settings. -The Project Settings dialog box appears. - -Choose the Debug tab. - - -In the Category drop-down list box, select General. - - -In the Executable For Debug Session text box, type the name of the DLL that your external makefile builds. - - -Click OK to store the information in your project. - - -Build a debug version of the DLL with symbolic debugging information, if you don’t already have one. - - -Follow one of the two procedures immediately preceding this one to debug the DLL. - -======================================================================================================================== -Why Don’t My DLL Breakpoints Work? -======================================================================================================================== -Some reasons why your breakpoints don’t work as expected are listed here, along with solutions or work-arounds for each. -If you follow the instructions in one topic and are still having breakpoint problems, look at some of the other topics. -Often breakpoint problems result from a combination of conditions. - -You can't set a breakpoint in a source file when the corresponding symbolic information isn't loaded into memory by -the debugger. -You cannot set a breakpoint in any source file when the corresponding symbolic information will not be loaded into memory -by the debugger. -Symptoms include messages such as "the breakpoint cannot be set" or a simple, noninformational beep. - -When setting breakpoints before the code to be debugged has been started, the debugger uses a breakpoint list to keep -track of how and where to set breakpoints. When you actually begin the debugging session, the debugger loads the symbolic -information for all the code to be debugged and then walks through its breakpoint list, attempting to set the -breakpoints. - -However, if one or more of the code modules have not been designated to the debugger, there will be no symbolic -information for the debugger to use when walking through its breakpoint list. Situations where this is likely to -occur include: - -Attempts to set breakpoints in a DLL before the call to LoadLibrary. - -Setting a breakpoint in an ActiveX server before the container has started the server. - -Other similar cases. - -To prevent this behavior in Visual C++, specify all additional DLLs and COM servers in the Additional DLLs field -in the Debug/Options dialog box to notify the debugger that you want it to load symbolic debug information for -additional .DLL files. When this has been done, breakpoints set in code that has not yet been loaded into memory -will be "virtual" breakpoints. When the code is actually loaded into memory by the loader, these become physical -breakpoints. Make sure that these additional debugging processes are not already running when you start your -debugging session. The debugging process and these additional processes must be sychronized at the same beginning -point to work correctly, hitting all breakpoints. - -Breakpoints are missed when more than one copy of a DLL is on your hard disk. -Having more than one copy of a DLL on your hard drive, especially if it is in your Windows directory, can cause -debugger confusion. The debugger will load the symbolic information for the DLL specified to it at run time (with the -Additional DLLs field in the Debug/Options dialog box), while Windows has actually loaded a different copy of the -DLL itself into memory. Because there is no way to force the debugger to load a specific DLL, it is a good idea to -keep only one version of a DLL at a time in your path, current directory, and Windows directory. - -You can’t set "Break When Expression Has Changed" breakpoints on a variable local to a DLL. -Setting a "Break When Expression Has Changed" breakpoint on a variable local to a DLL function before the call -to LoadLibrary causes the breakpoint to be virtual (there are no physical addresses for the DLL in memory yet). -Virtual breakpoints involving expressions pose a special problem. The DLL must be specified to the debugger at -startup (causing its symbolic information to be loaded). In addition, the DLL's executable code must also be loaded -into memory before this kind of breakpoint can be set. This means that the calling application's code must be -executed to the point after its call to LoadLibrary before the debugger will allow this type of breakpoint to be set. diff --git a/portmidi/pm_win/pm_dll.dsp b/portmidi/pm_win/pm_dll.dsp deleted file mode 100644 index d08e2de77..000000000 --- a/portmidi/pm_win/pm_dll.dsp +++ /dev/null @@ -1,107 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pm_dll" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=pm_dll - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "pm_dll.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "pm_dll.mak" CFG="pm_dll - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pm_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "pm_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "pm_dll - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "pm_win\Release" -# PROP Intermediate_Dir "pm_win\Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 - -!ELSEIF "$(CFG)" == "pm_dll - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "pm_win\Debug" -# PROP Intermediate_Dir "pm_win\Debug" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "pm_common" /D "_WINDOWS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /FR /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /dll /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pm_dll - Win32 Release" -# Name "pm_dll - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\pmdll.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\pmdll.h -# End Source File -# End Group -# End Target -# End Project diff --git a/portmidi/pm_win/pmdll.c b/portmidi/pm_win/pmdll.c deleted file mode 100644 index c3acba33e..000000000 --- a/portmidi/pm_win/pmdll.c +++ /dev/null @@ -1,49 +0,0 @@ -/* -==================================================================== -DLL to perform action when program shuts down -==================================================================== -*/ - -#include "windows.h" -#include "pmdll.h" - -static close_fn_ptr_type close_function = NULL; - - -DLL_EXPORT pm_set_close_function(close_fn_ptr_type close_fn_ptr) -{ - close_function = close_fn_ptr; -} - - -static void Initialize( void ) { - return; -} - -static void Terminate( void ) { - if (close_function) { - (*close_function)(); - } -} - - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, //DLL module handle - DWORD fdwReason, //for calling function - LPVOID lbpvReserved)//reserved -{ - switch(fdwReason) { - case DLL_PROCESS_ATTACH: - /* when DLL starts, run this */ - Initialize(); - break; - case DLL_PROCESS_DETACH: - /* when DLL ends, this run (note: verified this run */ - Terminate(); - break; - default: - break; - } - return TRUE; -} - - diff --git a/portmidi/pm_win/pmdll.h b/portmidi/pm_win/pmdll.h deleted file mode 100644 index e5ad1c5bd..000000000 --- a/portmidi/pm_win/pmdll.h +++ /dev/null @@ -1,5 +0,0 @@ -#define DLL_EXPORT __declspec( dllexport ) - -typedef void (*close_fn_ptr_type)(); - -DLL_EXPORT pm_set_close_function(close_fn_ptr_type close_fn_ptr); diff --git a/portmidi/pm_win/pmwin.c b/portmidi/pm_win/pmwin.c deleted file mode 100644 index b289194b6..000000000 --- a/portmidi/pm_win/pmwin.c +++ /dev/null @@ -1,113 +0,0 @@ -/* pmwin.c -- PortMidi os-dependent code */ - -/* This file only needs to implement: - pm_init(), which calls various routines to register the - available midi devices, - Pm_GetDefaultInputDeviceID(), and - Pm_GetDefaultOutputDeviceID(). - This file must - be separate from the main portmidi.c file because it is system - dependent, and it is separate from, say, pmwinmm.c, because it - might need to register devices for winmm, directx, and others. - */ - -#include "stdlib.h" -#include "portmidi.h" -#include "pminternal.h" -#include "pmwinmm.h" -#ifdef USE_DLL_FOR_CLEANUP -#include "pmdll.h" /* used to close ports on exit */ -#endif -#ifdef DEBUG -#include "stdio.h" -#endif - -/* pm_exit is called when the program exits. - It calls pm_term to make sure PortMidi is properly closed. - If DEBUG is on, we prompt for input to avoid losing error messages. - */ -static void pm_exit(void) { - pm_term(); -#ifdef DEBUG -#define STRING_MAX 80 - { - char line[STRING_MAX]; - printf("Type ENTER...\n"); - /* note, w/o this prompting, client console application can not see one - of its errors before closing. */ - fgets(line, STRING_MAX, stdin); - } -#endif -} - - -/* pm_init is the windows-dependent initialization.*/ -void pm_init(void) -{ -#ifdef USE_DLL_FOR_CLEANUP - /* we were hoping a DLL could offer more robust cleanup after errors, - but the DLL does not seem to run after crashes. Thus, the atexit() - mechanism is just as powerful, and simpler to implement. - */ - pm_set_close_function(pm_exit); -#ifdef DEBUG - printf("registered pm_term with cleanup DLL\n"); -#endif -#else - atexit(pm_exit); -#ifdef DEBUG - printf("registered pm_exit with atexit()\n"); -#endif -#endif - pm_winmm_init(); - /* initialize other APIs (DirectX?) here */ -} - - -void pm_term(void) { - pm_winmm_term(); -} - - -PmDeviceID Pm_GetDefaultInputDeviceID() { - /* This routine should check the environment and the registry - as specified in portmidi.h, but for now, it just returns - the first device of the proper input/output flavor. - */ - int i; - Pm_Initialize(); /* make sure descriptors exist! */ - for (i = 0; i < pm_descriptor_index; i++) { - if (descriptors[i].pub.input) { - return i; - } - } - return pmNoDevice; -} - -PmDeviceID Pm_GetDefaultOutputDeviceID() { - /* This routine should check the environment and the registry - as specified in portmidi.h, but for now, it just returns - the first device of the proper input/output flavor. - */ - int i; - Pm_Initialize(); /* make sure descriptors exist! */ - for (i = 0; i < pm_descriptor_index; i++) { - if (descriptors[i].pub.output) { - return i; - } - } - return pmNoDevice; - return 0; -} - -#include "stdio.h" - -void *pm_alloc(size_t s) { - return malloc(s); -} - - -void pm_free(void *ptr) { - free(ptr); -} - diff --git a/portmidi/pm_win/pmwinmm.c b/portmidi/pm_win/pmwinmm.c deleted file mode 100644 index 5bfb0cff2..000000000 --- a/portmidi/pm_win/pmwinmm.c +++ /dev/null @@ -1,1547 +0,0 @@ -/* pmwinmm.c -- system specific definitions */ - -#include "windows.h" -#include "mmsystem.h" -#include "portmidi.h" -#include "pminternal.h" -#include "pmwinmm.h" -#include "string.h" -#include "porttime.h" - -/* asserts used to verify portMidi code logic is sound; later may want - something more graceful */ -#include <assert.h> - -#ifdef DEBUG -/* this printf stuff really important for debugging client app w/host errors. - probably want to do something else besides read/write from/to console - for portability, however */ -#define STRING_MAX 80 -#include "stdio.h" -#endif - -#define streql(x, y) (strcmp(x, y) == 0) - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 - -/* callback routines */ -static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn, - WORD wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); -static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg, - DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); -static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg, - DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); - -extern pm_fns_node pm_winmm_in_dictionary; -extern pm_fns_node pm_winmm_out_dictionary; - -static void winmm_out_delete(PmInternal *midi); /* forward reference */ - -#define SYSEX_BYTES_PER_BUFFER 1024 -/* 3 midi messages per buffer */ -#define OUTPUT_BYTES_PER_BUFFER 36 - -#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3) -#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR)) -#define MIDIHDR_BUFFER_LENGTH(x) (x) -#define MIDIHDR_SIZE(x) (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR)) - -/* -============================================================================== -win32 mmedia system specific structure passed to midi callbacks -============================================================================== -*/ - -/* global winmm device info */ -MIDIINCAPS *midi_in_caps = NULL; -MIDIINCAPS midi_in_mapper_caps; -UINT midi_num_inputs = 0; -MIDIOUTCAPS *midi_out_caps = NULL; -MIDIOUTCAPS midi_out_mapper_caps; -UINT midi_num_outputs = 0; - -/* per device info */ -typedef struct midiwinmm_struct -{ - - union { - HMIDISTRM stream; /* windows handle for stream */ - HMIDIOUT out; /* windows handle for out calls */ - HMIDIIN in; /* windows handle for in calls */ - } handle; - - /* midi output messages are sent in these buffers, which are allocated - * in a round-robin fashion, using next_buffer as an index - */ - LPMIDIHDR *buffers; /* pool of buffers for midi in or out data */ - int num_buffers; /* how many buffers */ - int next_buffer; /* index of next buffer to send */ - HANDLE buffer_signal; /* used to wait for buffer to become free */ - - LPMIDIHDR *sysex_buffers; /* pool of buffers for sysex data */ - int num_sysex_buffers; /* how many sysex buffers */ - int next_sysex_buffer; /* index of next sysexbuffer to send */ - HANDLE sysex_buffer_signal; /* used to wait for sysex buffer to become free */ - - unsigned long last_time; /* last output time */ - int first_message; /* flag: treat first message differently */ - int sysex_mode; /* middle of sending sysex */ - unsigned long sysex_word; /* accumulate data when receiving sysex */ - unsigned int sysex_byte_count; /* count how many received or to send */ - LPMIDIHDR hdr; /* the message accumulating sysex to send */ - unsigned long sync_time; /* when did we last determine delta? */ - long delta; /* difference between stream time and - real time */ - int error; /* host error from doing port midi call */ - int callback_error; /* host error from midi in or out callback */ -} -midiwinmm_node, *midiwinmm_type; - - -/* -============================================================================= -general MIDI device queries -============================================================================= -*/ -static void pm_winmm_general_inputs() -{ - UINT i; - WORD wRtn; - midi_num_inputs = midiInGetNumDevs(); - midi_in_caps = pm_alloc(sizeof(MIDIINCAPS) * midi_num_inputs); - - if (midi_in_caps == NULL) { - // if you can't open a particular system-level midi interface - // (such as winmm), we just consider that system or API to be - // unavailable and move on without reporting an error. This - // may be the wrong thing to do, especially in this case. - return ; - } - - for (i = 0; i < midi_num_inputs; i++) { - wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i], - sizeof(MIDIINCAPS)); - if (wRtn == MMSYSERR_NOERROR) { - /* ignore errors here -- if pm_descriptor_max is exceeded, some - devices will not be accessible. */ - pm_add_device("MMSystem", midi_in_caps[i].szPname, TRUE, - (void *) i, &pm_winmm_in_dictionary); - } - } -} - - -static void pm_winmm_mapper_input() -{ - WORD wRtn; - /* Note: if MIDIMAPPER opened as input (documentation implies you - can, but current system fails to retrieve input mapper - capabilities) then you still should retrieve some formof - setup info. */ - wRtn = midiInGetDevCaps((UINT) MIDIMAPPER, - (LPMIDIINCAPS) & midi_in_mapper_caps, sizeof(MIDIINCAPS)); - if (wRtn == MMSYSERR_NOERROR) { - pm_add_device("MMSystem", midi_in_mapper_caps.szPname, TRUE, - (void *) MIDIMAPPER, &pm_winmm_in_dictionary); - } -} - - -static void pm_winmm_general_outputs() -{ - UINT i; - DWORD wRtn; - midi_num_outputs = midiOutGetNumDevs(); - midi_out_caps = pm_alloc( sizeof(MIDIOUTCAPS) * midi_num_outputs ); - - if (midi_out_caps == NULL) { - // no error is reported -- see pm_winmm_general_inputs - return ; - } - - for (i = 0; i < midi_num_outputs; i++) { - wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i], - sizeof(MIDIOUTCAPS)); - if (wRtn == MMSYSERR_NOERROR) { - pm_add_device("MMSystem", midi_out_caps[i].szPname, FALSE, - (void *) i, &pm_winmm_out_dictionary); - } - } -} - - -static void pm_winmm_mapper_output() -{ - WORD wRtn; - /* Note: if MIDIMAPPER opened as output (pseudo MIDI device - maps device independent messages into device dependant ones, - via NT midimapper program) you still should get some setup info */ - wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS) - & midi_out_mapper_caps, sizeof(MIDIOUTCAPS)); - if (wRtn == MMSYSERR_NOERROR) { - pm_add_device("MMSystem", midi_out_mapper_caps.szPname, FALSE, - (void *) MIDIMAPPER, &pm_winmm_out_dictionary); - } -} - - -/* -========================================================================================= -host error handling -========================================================================================= -*/ -static unsigned int winmm_has_host_error(PmInternal * midi) -{ - midiwinmm_type m = (midiwinmm_type)midi->descriptor; - return m->callback_error || m->error; -} - - -/* str_copy_len -- like strcat, but won't overrun the destination string */ -/* - * returns length of resulting string - */ -static int str_copy_len(char *dst, char *src, int len) -{ - strncpy(dst, src, len); - /* just in case suffex is greater then len, terminate with zero */ - dst[len - 1] = 0; - return strlen(dst); -} - - -static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len) -{ - /* precondition: midi != NULL */ - midiwinmm_node * m = (midiwinmm_node *) midi->descriptor; - char *hdr1 = "Host error: "; - char *hdr2 = "Host callback error: "; - - msg[0] = 0; /* initialize result string to empty */ - - if (descriptors[midi->device_id].pub.input) { - /* input and output use different winmm API calls */ - if (m) { /* make sure there is an open device to examine */ - if (m->error != MMSYSERR_NOERROR) { - int n = str_copy_len(msg, hdr1, len); - /* read and record host error */ - int err = midiInGetErrorText(m->error, msg + n, len - n); - assert(err == MMSYSERR_NOERROR); - m->error = MMSYSERR_NOERROR; - } else if (m->callback_error != MMSYSERR_NOERROR) { - int n = str_copy_len(msg, hdr2, len); - int err = midiInGetErrorText(m->callback_error, msg + n, - len - n); - assert(err == MMSYSERR_NOERROR); - m->callback_error = MMSYSERR_NOERROR; - } - } - } else { /* output port */ - if (m) { - if (m->error != MMSYSERR_NOERROR) { - int n = str_copy_len(msg, hdr1, len); - int err = midiOutGetErrorText(m->error, msg + n, len - n); - assert(err == MMSYSERR_NOERROR); - m->error = MMSYSERR_NOERROR; - } else if (m->callback_error != MMSYSERR_NOERROR) { - int n = str_copy_len(msg, hdr2, len); - int err = midiOutGetErrorText(m->callback_error, msg + n, - len = n); - assert(err == MMSYSERR_NOERROR); - m->callback_error = MMSYSERR_NOERROR; - } - } - } -} - - -/* -============================================================================= -buffer handling -============================================================================= -*/ -static MIDIHDR *allocate_buffer(long data_size) -{ - /* - * with short messages, the MIDIEVENT structure contains the midi message, - * so there is no need for additional data - */ - - LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SIZE(data_size)); - MIDIEVENT *evt; - if (!hdr) return NULL; - evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */ - hdr->lpData = (LPSTR) evt; - hdr->dwBufferLength = MIDIHDR_BUFFER_LENGTH(data_size); /* was: sizeof(MIDIEVENT) + data_size; */ - hdr->dwFlags = 0; - hdr->dwUser = 0; - return hdr; -} - -static MIDIHDR *allocate_sysex_buffer(long data_size) -{ - /* we're actually allocating slightly more than data_size because one more word of - * data is contained in MIDIEVENT. We include the size of MIDIEVENT because we need - * the MIDIEVENT header in addition to the data - */ - LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size)); - MIDIEVENT *evt; - if (!hdr) return NULL; - evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */ - hdr->lpData = (LPSTR) evt; - hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size); /* was: sizeof(MIDIEVENT) + data_size; */ - hdr->dwFlags = 0; - hdr->dwUser = 0; - return hdr; -} - -static PmError allocate_buffers(midiwinmm_type m, long data_size, long count) -{ - PmError rslt = pmNoError; - /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */ - m->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count); - if (!m->buffers) return pmInsufficientMemory; - m->num_buffers = count; - while (count > 0) { - LPMIDIHDR hdr = allocate_buffer(data_size); - if (!hdr) rslt = pmInsufficientMemory; - count--; - m->buffers[count] = hdr; /* this may be NULL if allocation fails */ - } - return rslt; -} - -static PmError allocate_sysex_buffers(midiwinmm_type m, long data_size, long count) -{ - PmError rslt = pmNoError; - /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */ - m->sysex_buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count); - if (!m->sysex_buffers) return pmInsufficientMemory; - m->num_sysex_buffers = count; - while (count > 0) { - LPMIDIHDR hdr = allocate_sysex_buffer(data_size); - if (!hdr) rslt = pmInsufficientMemory; - count--; - m->sysex_buffers[count] = hdr; /* this may be NULL if allocation fails */ - } - return rslt; -} - -static LPMIDIHDR get_free_sysex_buffer(PmInternal *midi) -{ - LPMIDIHDR r = NULL; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (!m->sysex_buffers) { - if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER, 2)) { - return NULL; - } - } - /* busy wait until we find a free buffer */ - while (TRUE) { - int i; - for (i = 0; i < m->num_sysex_buffers; i++) { - m->next_sysex_buffer++; - if (m->next_sysex_buffer >= m->num_sysex_buffers) m->next_sysex_buffer = 0; - r = m->sysex_buffers[m->next_sysex_buffer]; - if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_sysex_buffer; - } - /* after scanning every buffer and not finding anything, block */ - WaitForSingleObject(m->sysex_buffer_signal, INFINITE); - } -found_sysex_buffer: - r->dwBytesRecorded = 0; - m->error = midiOutPrepareHeader(m->handle.out, r, sizeof(MIDIHDR)); - return r; -} - - -static LPMIDIHDR get_free_output_buffer(PmInternal *midi) -{ - LPMIDIHDR r = NULL; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (!m->buffers) { - if (allocate_buffers(m, OUTPUT_BYTES_PER_BUFFER, 2)) { - return NULL; - } - } - /* busy wait until we find a free buffer */ - while (TRUE) { - int i; - for (i = 0; i < m->num_buffers; i++) { - m->next_buffer++; - if (m->next_buffer >= m->num_buffers) m->next_buffer = 0; - r = m->buffers[m->next_buffer]; - if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer; - } - /* after scanning every buffer and not finding anything, block */ - WaitForSingleObject(m->buffer_signal, INFINITE); - } -found_buffer: - r->dwBytesRecorded = 0; - m->error = midiOutPrepareHeader(m->handle.out, r, sizeof(MIDIHDR)); - return r; -} - -static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_size) -{ - LPMIDIHDR big; - int i; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - /* buffer must be smaller than 64k, but be also be a multiple of 4 */ - if (new_size > 65520) { - if (old_size >= 65520) - return pmBufferMaxSize; - else - new_size = 65520; - } - /* allocate a bigger message */ - big = allocate_sysex_buffer(new_size); - /* printf("expand to %d bytes\n", new_size);*/ - if (!big) return pmInsufficientMemory; - m->error = midiOutPrepareHeader(m->handle.out, big, sizeof(MIDIHDR)); - if (m->error) { - pm_free(big); - return pmHostError; - } - /* make sure we're not going to overwrite any memory */ - assert(old_size <= new_size); - memcpy(big->lpData, m->hdr->lpData, old_size); - - /* find which buffer this was, and replace it */ - - for (i = 0;i < m->num_sysex_buffers;i++) { - if (m->sysex_buffers[i] == m->hdr) { - m->sysex_buffers[i] = big; - pm_free(m->hdr); - m->hdr = big; - break; - } - } - assert(i != m->num_sysex_buffers); - - return pmNoError; - -} -/* -========================================================================================= -begin midi input implementation -========================================================================================= -*/ - -static PmError winmm_in_open(PmInternal *midi, void *driverInfo) -{ - DWORD dwDevice; - int i = midi->device_id; - midiwinmm_type m; - LPMIDIHDR hdr; - long buffer_len; - dwDevice = (DWORD) descriptors[i].descriptor; - - /* create system dependent device data */ - m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */ - midi->descriptor = m; - if (!m) goto no_memory; - m->handle.in = NULL; - m->buffers = NULL; - m->num_buffers = 0; - m->next_buffer = 0; - m->sysex_buffers = NULL; - m->num_sysex_buffers = 0; - m->next_sysex_buffer = 0; - m->last_time = 0; - m->first_message = TRUE; /* not used for input */ - m->sysex_mode = FALSE; - m->sysex_word = 0; - m->sysex_byte_count = 0; - m->sync_time = 0; - m->delta = 0; - m->error = MMSYSERR_NOERROR; - m->callback_error = MMSYSERR_NOERROR; - - /* open device */ - pm_hosterror = midiInOpen(&(m->handle.in), /* input device handle */ - dwDevice, /* device ID */ - (DWORD) winmm_in_callback, /* callback address */ - (DWORD) midi, /* callback instance data */ - CALLBACK_FUNCTION); /* callback is a procedure */ - if (pm_hosterror) goto free_descriptor; - - /* allocate first buffer for sysex data */ - buffer_len = midi->buffer_len - 1; - if (midi->buffer_len < 32) - buffer_len = PM_DEFAULT_SYSEX_BUFFER_SIZE; - - hdr = allocate_sysex_buffer(buffer_len); - if (!hdr) goto close_device; - pm_hosterror = midiInPrepareHeader(m->handle.in, hdr, sizeof(MIDIHDR)); - if (pm_hosterror) { - pm_free(hdr); - goto close_device; - } - pm_hosterror = midiInAddBuffer(m->handle.in, hdr, sizeof(MIDIHDR)); - if (pm_hosterror) goto close_device; - - /* allocate second buffer */ - hdr = allocate_sysex_buffer(buffer_len); - if (!hdr) goto close_device; - pm_hosterror = midiInPrepareHeader(m->handle.in, hdr, sizeof(MIDIHDR)); - if (pm_hosterror) { - pm_free(hdr); - goto reset_device; /* because first buffer was added */ - } - pm_hosterror = midiInAddBuffer(m->handle.in, hdr, sizeof(MIDIHDR)); - if (pm_hosterror) goto reset_device; - - /* start device */ - pm_hosterror = midiInStart(m->handle.in); - if (pm_hosterror) goto reset_device; - return pmNoError; - - /* undo steps leading up to the detected error */ -reset_device: - /* ignore return code (we already have an error to report) */ - midiInReset(m->handle.in); -close_device: - midiInClose(m->handle.in); /* ignore return code */ -free_descriptor: - midi->descriptor = NULL; - pm_free(m); -no_memory: - if (pm_hosterror) { - int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text, - PM_HOST_ERROR_MSG_LEN); - assert(err == MMSYSERR_NOERROR); - return pmHostError; - } - /* if !pm_hosterror, then the error must be pmInsufficientMemory */ - return pmInsufficientMemory; - /* note: if we return an error code, the device will be - closed and memory will be freed. It's up to the caller - to free the parameter midi */ -} - - -/* winmm_in_close -- close an open midi input device */ -/* - * assume midi is non-null (checked by caller) - */ -static PmError winmm_in_close(PmInternal *midi) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (!m) return pmBadPtr; - /* device to close */ - if (pm_hosterror = midiInStop(m->handle.in)) { - midiInReset(m->handle.in); /* try to reset and close port */ - midiInClose(m->handle.in); - } else if (pm_hosterror = midiInReset(m->handle.in)) { - midiInClose(m->handle.in); /* best effort to close midi port */ - } else { - pm_hosterror = midiInClose(m->handle.in); - } - midi->descriptor = NULL; - pm_free(m); /* delete */ - if (pm_hosterror) { - int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text, - PM_HOST_ERROR_MSG_LEN); - assert(err == MMSYSERR_NOERROR); - return pmHostError; - } - return pmNoError; -} - - -/* Callback function executed via midiInput SW interrupt (via midiInOpen). */ -static void FAR PASCAL winmm_in_callback( - HMIDIIN hMidiIn, /* midiInput device Handle */ - WORD wMsg, /* midi msg */ - DWORD dwInstance, /* application data */ - DWORD dwParam1, /* MIDI data */ - DWORD dwParam2) /* device timestamp (wrt most recent midiInStart) */ -{ - static int entry = 0; - PmInternal *midi = (PmInternal *) dwInstance; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - - if (++entry > 1) { - assert(FALSE); - } - - /* for simplicity, this logic perhaps overly conservative */ - /* note also that this might leak memory if buffers are being - returned as a result of midiInReset */ - if (m->callback_error) { - entry--; - return ; - } - - switch (wMsg) { - case MIM_DATA: { - /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of - message LOB; - dwParam2 is time message received by input device driver, specified - in [ms] from when midiInStart called. - each message is expanded to include the status byte */ - - long new_driver_time = dwParam2; - - if ((dwParam1 & 0x80) == 0) { - /* not a status byte -- ignore it. This happens running the - sysex.c test under Win2K with MidiMan USB 1x1 interface. - Is it a driver bug or a Win32 bug? If not, there's a bug - here somewhere. -RBD - */ - } else { /* data to process */ - PmEvent event; - if (midi->time_proc) - dwParam2 = (*midi->time_proc)(midi->time_info); - event.timestamp = dwParam2; - event.message = dwParam1; - pm_read_short(midi, &event); - } - break; - } - case MIM_LONGDATA: { - MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1; - unsigned char *data = lpMidiHdr->lpData; - unsigned int i = 0; - long size = sizeof(MIDIHDR) + lpMidiHdr->dwBufferLength; - /* ignore sysex data, but free returned buffers */ - if (lpMidiHdr->dwBytesRecorded > 0 && - midi->filters & PM_FILT_SYSEX) { - m->callback_error = midiInAddBuffer(hMidiIn, lpMidiHdr, - sizeof(MIDIHDR)); - break; - } - if (midi->time_proc) - dwParam2 = (*midi->time_proc)(midi->time_info); - - while (i < lpMidiHdr->dwBytesRecorded) { - /* collect bytes from *data into a word */ - pm_read_byte(midi, *data, dwParam2); - data++; - i++; - } - /* when a device is closed, the pending MIM_LONGDATA buffers are - returned to this callback with dwBytesRecorded == 0. In this - case, we do not want to send them back to the interface (if - we do, the interface will not close, and Windows OS may hang). */ - if (lpMidiHdr->dwBytesRecorded > 0) { - m->callback_error = midiInAddBuffer(hMidiIn, lpMidiHdr, - sizeof(MIDIHDR)); - } else { - pm_free(lpMidiHdr); - } - break; - } - case MIM_OPEN: /* fall thru */ - case MIM_CLOSE: - case MIM_ERROR: - case MIM_LONGERROR: - default: - break; - } - entry--; -} - -/* -========================================================================================= -begin midi output implementation -========================================================================================= -*/ - -/* begin helper routines used by midiOutStream interface */ - -/* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */ -static int add_to_buffer(midiwinmm_type m, LPMIDIHDR hdr, - unsigned long delta, unsigned long msg) -{ - unsigned long *ptr = (unsigned long *) - (hdr->lpData + hdr->dwBytesRecorded); - *ptr++ = delta; /* dwDeltaTime */ - *ptr++ = 0; /* dwStream */ - *ptr++ = msg; /* dwEvent */ - hdr->dwBytesRecorded += 3 * sizeof(long); - /* if the addition of three more words (a message) would extend beyond - the buffer length, then return TRUE (full) - */ - return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength; -} - -#ifdef GARBAGE -static void start_sysex_buffer(LPMIDIHDR hdr, unsigned long delta) -{ - unsigned long *ptr = (unsigned long *) hdr->lpData; - *ptr++ = delta; - *ptr++ = 0; - *ptr = MEVT_F_LONG; - hdr->dwBytesRecorded = 3 * sizeof(long); -} - -static int add_byte_to_buffer(midiwinmm_type m, LPMIDIHDR hdr, - unsigned char midi_byte) -{ - allocate message if hdr is null - send message if it is full - add byte to non - full message - unsigned char *ptr = (unsigned char *) (hdr->lpData + hdr->dwBytesRecorded); - *ptr = midi_byte; - return ++hdr->dwBytesRecorded >= hdr->dwBufferLength; -} -#endif - - -static PmTimestamp pm_time_get(midiwinmm_type m) -{ - MMTIME mmtime; - MMRESULT wRtn; - mmtime.wType = TIME_TICKS; - mmtime.u.ticks = 0; - wRtn = midiStreamPosition(m->handle.stream, &mmtime, sizeof(mmtime)); - assert(wRtn == MMSYSERR_NOERROR); - return mmtime.u.ticks; -} - -#ifdef GARBAGE -static unsigned long synchronize(PmInternal *midi, midiwinmm_type m) -{ - unsigned long pm_stream_time_2 = pm_time_get(m); - unsigned long real_time; - unsigned long pm_stream_time; - /* figure out the time */ - do { - /* read real_time between two reads of stream time */ - pm_stream_time = pm_stream_time_2; - real_time = (*midi->time_proc)(midi->time_info); - pm_stream_time_2 = pm_time_get(m); - /* repeat if more than 1ms elapsed */ - } while (pm_stream_time_2 > pm_stream_time + 1); - m->delta = pm_stream_time - real_time; - m->sync_time = real_time; - return real_time; -} -#endif - - -/* end helper routines used by midiOutStream interface */ - - -static PmError winmm_out_open(PmInternal *midi, void *driverInfo) -{ - DWORD dwDevice; - int i = midi->device_id; - midiwinmm_type m; - MIDIPROPTEMPO propdata; - MIDIPROPTIMEDIV divdata; - dwDevice = (DWORD) descriptors[i].descriptor; - - /* create system dependent device data */ - m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */ - midi->descriptor = m; - if (!m) goto no_memory; - m->handle.out = NULL; - m->buffers = NULL; - m->num_buffers = 0; - m->next_buffer = 0; - m->sysex_buffers = NULL; - m->num_sysex_buffers = 0; - m->next_sysex_buffer = 0; - m->last_time = 0; - m->first_message = TRUE; /* we treat first message as special case */ - m->sysex_mode = FALSE; - m->sysex_word = 0; - m->sysex_byte_count = 0; - m->hdr = NULL; - m->sync_time = 0; - m->delta = 0; - m->error = MMSYSERR_NOERROR; - m->callback_error = MMSYSERR_NOERROR; - - /* create a signal */ - m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL); - m->sysex_buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL); - - /* this should only fail when there are very serious problems */ - assert(m->buffer_signal); - assert(m->sysex_buffer_signal); - - /* open device */ - if (midi->latency == 0) { - /* use simple midi out calls */ - pm_hosterror = midiOutOpen((LPHMIDIOUT) & m->handle.out, /* device Handle */ - dwDevice, /* device ID */ - (DWORD) winmm_out_callback, - (DWORD) midi, /* callback instance data */ - CALLBACK_FUNCTION); /* callback type */ - } else { - /* use stream-based midi output (schedulable in future) */ - pm_hosterror = midiStreamOpen(&m->handle.stream, /* device Handle */ - (LPUINT) & dwDevice, /* device ID pointer */ - 1, /* reserved, must be 1 */ - (DWORD) winmm_streamout_callback, - (DWORD) midi, /* callback instance data */ - CALLBACK_FUNCTION); - } - if (pm_hosterror != MMSYSERR_NOERROR) { - goto free_descriptor; - } - - if (midi->latency != 0) { - long dur = 0; - /* with stream output, specified number of buffers allocated here */ - int count = midi->buffer_len; - if (count == 0) - count = midi->latency / 2; /* how many buffers to get */ - - propdata.cbStruct = sizeof(MIDIPROPTEMPO); - propdata.dwTempo = 480000; /* microseconds per quarter */ - pm_hosterror = midiStreamProperty(m->handle.stream, - (LPBYTE) & propdata, - MIDIPROP_SET | MIDIPROP_TEMPO); - if (pm_hosterror) goto close_device; - - divdata.cbStruct = sizeof(MIDIPROPTEMPO); - divdata.dwTimeDiv = 480; /* divisions per quarter */ - pm_hosterror = midiStreamProperty(m->handle.stream, - (LPBYTE) & divdata, - MIDIPROP_SET | MIDIPROP_TIMEDIV); - if (pm_hosterror) goto close_device; - - /* allocate at least 3 buffers */ - if (count < 3) count = 3; - if (allocate_buffers(m, OUTPUT_BYTES_PER_BUFFER, count)) goto free_buffers; - if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER, 2)) goto free_buffers; - /* start device */ - pm_hosterror = midiStreamRestart(m->handle.stream); - if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers; - } - return pmNoError; - -free_buffers: - /* buffers are freed below by winmm_out_delete */ -close_device: - midiOutClose(m->handle.out); -free_descriptor: - midi->descriptor = NULL; - winmm_out_delete(midi); /* frees buffers and m */ -no_memory: - if (pm_hosterror) { - int err = midiOutGetErrorText(pm_hosterror, (char *) pm_hosterror_text, - PM_HOST_ERROR_MSG_LEN); - assert(err == MMSYSERR_NOERROR); - return pmHostError; - } - return pmInsufficientMemory; -} - - -/* winmm_out_delete -- carefully free data associated with midi */ -/**/ -static void winmm_out_delete(PmInternal *midi) -{ - /* delete system dependent device data */ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (m) { - if (m->buffer_signal) { - /* don't report errors -- better not to stop cleanup */ - CloseHandle(m->buffer_signal); - } - if (m->sysex_buffer_signal) { - /* don't report errors -- better not to stop cleanup */ - CloseHandle(m->sysex_buffer_signal); - } - if (m->buffers) { - /* if using stream output, free buffers */ - int i; - for (i = 0; i < m->num_buffers; i++) { - if (m->buffers[i]) pm_free(m->buffers[i]); - } - pm_free(m->buffers); - } - - if (m->sysex_buffers) { - /* free sysex buffers */ - int i; - for (i = 0; i < m->num_sysex_buffers; i++) { - if (m->sysex_buffers[i]) pm_free(m->sysex_buffers[i]); - } - pm_free(m->sysex_buffers); - } - } - midi->descriptor = NULL; - pm_free(m); /* delete */ -} - - -/* see comments for winmm_in_close */ -static PmError winmm_out_close(PmInternal *midi) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (m->handle.out) { - /* device to close */ - if (midi->latency == 0) { - pm_hosterror = midiOutClose(m->handle.out); - } else { - pm_hosterror = midiStreamClose(m->handle.stream); - } - /* regardless of outcome, free memory */ - winmm_out_delete(midi); - } - if (pm_hosterror) { - int err = midiOutGetErrorText(pm_hosterror, - (char *) pm_hosterror_text, - PM_HOST_ERROR_MSG_LEN); - assert(err == MMSYSERR_NOERROR); - return pmHostError; - } - return pmNoError; -} - - -static PmError winmm_out_abort(PmInternal *midi) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - m->error = MMSYSERR_NOERROR; - - /* only stop output streams */ - if (midi->latency > 0) { - m->error = midiStreamStop(m->handle.stream); - } - return m->error ? pmHostError : pmNoError; -} - -#ifdef GARBAGE -static PmError winmm_write_sysex_byte(PmInternal *midi, unsigned char byte) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - unsigned char *msg_buffer; - - /* at the beginning of sysex, m->hdr is NULL */ - if (!m->hdr) { /* allocate a buffer if none allocated yet */ - m->hdr = get_free_output_buffer(midi); - if (!m->hdr) return pmInsufficientMemory; - m->sysex_byte_count = 0; - } - /* figure out where to write byte */ - msg_buffer = (unsigned char *) (m->hdr->lpData); - assert(m->hdr->lpData == (char *) (m->hdr + 1)); - - /* check for overflow */ - if (m->sysex_byte_count >= m->hdr->dwBufferLength) { - /* allocate a bigger message -- double it every time */ - LPMIDIHDR big = allocate_buffer(m->sysex_byte_count * 2); - /* printf("expand to %d bytes\n", m->sysex_byte_count * 2); */ - if (!big) return pmInsufficientMemory; - m->error = midiOutPrepareHeader(m->handle.out, big, - sizeof(MIDIHDR)); - if (m->error) { - m->hdr = NULL; - return pmHostError; - } - memcpy(big->lpData, msg_buffer, m->sysex_byte_count); - msg_buffer = (unsigned char *) (big->lpData); - if (m->buffers[0] == m->hdr) { - m->buffers[0] = big; - pm_free(m->hdr); - /* printf("freed m->hdr\n"); */ - } else if (m->buffers[1] == m->hdr) { - m->buffers[1] = big; - pm_free(m->hdr); - /* printf("freed m->hdr\n"); */ - } - m->hdr = big; - } - - /* append byte to message */ - msg_buffer[m->sysex_byte_count++] = byte; - - /* see if we have a complete message */ - if (byte == MIDI_EOX) { - m->hdr->dwBytesRecorded = m->sysex_byte_count; - /* - { int i; int len = m->hdr->dwBytesRecorded; - printf("OutLongMsg %d ", len); - for (i = 0; i < len; i++) { - printf("%2x ", msg_buffer[i]); - } - } - */ - m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR)); - m->hdr = NULL; /* stop using this message buffer */ - if (m->error) return pmHostError; - } - return pmNoError; -} -#endif - - -static PmError winmm_write_short(PmInternal *midi, PmEvent *event) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - PmError rslt = pmNoError; - assert(m); - if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */ - m->error = midiOutShortMsg(m->handle.out, event->message); - if (m->error) rslt = pmHostError; - } else { /* use midiStream interface -- pass data through buffers */ - unsigned long when = event->timestamp; - unsigned long delta; - int full; - if (when == 0) when = midi->now; - /* when is in real_time; translate to intended stream time */ - when = when + m->delta + midi->latency; - /* make sure we don't go backward in time */ - if (when < m->last_time) when = m->last_time; - delta = when - m->last_time; - m->last_time = when; - /* before we insert any data, we must have a buffer */ - if (m->hdr == NULL) { - /* stream interface: buffers allocated when stream is opened */ - m->hdr = get_free_output_buffer(midi); - } - full = add_to_buffer(m, m->hdr, delta, event->message); - if (full) { - m->error = midiStreamOut(m->handle.stream, m->hdr, - sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - m->hdr = NULL; - } - } - return rslt; -} - - -static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - PmError rslt = pmNoError; - if (midi->latency == 0) { - /* do nothing -- it's handled in winmm_write_byte */ - } else { - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - /* sysex expects an empty buffer */ - if (m->hdr) { - m->error = midiStreamOut(m->handle.stream, m->hdr, sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - } - m->hdr = NULL; - } - return rslt; -} - - -static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - PmError rslt = pmNoError; - assert(m); - - if (midi->latency == 0) { - /* Not using the stream interface. The entire sysex message is - in m->hdr, and we send it using midiOutLongMsg. - */ - m->hdr->dwBytesRecorded = m->sysex_byte_count; - /* - { int i; int len = m->hdr->dwBytesRecorded; - printf("OutLongMsg %d ", len); - for (i = 0; i < len; i++) { - printf("%2x ", msg_buffer[i]); - } - } - */ - - m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - } else if (m->hdr) { - /* Using stream interface. There are accumulated bytes in m->hdr - to send using midiStreamOut - */ - /* add bytes recorded to MIDIEVENT length, but don't - count the MIDIEVENT data (3 longs) */ - MIDIEVENT *evt = (MIDIEVENT *) m->hdr->lpData; - evt->dwEvent += m->hdr->dwBytesRecorded - 3 * sizeof(long); - /* round up BytesRecorded to multiple of 4 */ - m->hdr->dwBytesRecorded = (m->hdr->dwBytesRecorded + 3) & ~3; - - m->error = midiStreamOut(m->handle.stream, m->hdr, - sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - } - m->hdr = NULL; /* make sure we don't send it again */ - return rslt; -} - - -static PmError winmm_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - /* write a sysex byte */ - PmError rslt = pmNoError; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - assert(m); - if (midi->latency == 0) { - /* Not using stream interface. Accumulate the entire message into - m->hdr */ - unsigned char *msg_buffer; - /* at the beginning of sysex, m->hdr is NULL */ - if (!m->hdr) { /* allocate a buffer if none allocated yet */ - m->hdr = get_free_sysex_buffer(midi); - if (!m->hdr) return pmInsufficientMemory; - m->sysex_byte_count = 0; - } - /* figure out where to write byte */ - msg_buffer = (unsigned char *) (m->hdr->lpData); - assert(m->hdr->lpData == (char *) (m->hdr + 1)); - - /* append byte to message */ - msg_buffer[m->sysex_byte_count++] = byte; - - /* check for overflow */ - if (m->sysex_byte_count >= m->hdr->dwBufferLength) { - rslt = resize_sysex_buffer(midi, m->sysex_byte_count, m->sysex_byte_count * 2); - - if (rslt == pmBufferMaxSize) /* if the buffer can't be resized */ - rslt = winmm_end_sysex(midi, timestamp); /* write what we've got and continue */ - - } - - } else { /* latency is not zero, use stream interface: accumulate - sysex data in m->hdr and send whenever the buffer fills */ - int full; - unsigned char *ptr; - - /* if m->hdr does not exist, allocate it */ - if (m->hdr == NULL) { - unsigned long when = (unsigned long) timestamp; - unsigned long delta; - unsigned long *ptr; - if (when == 0) when = midi->now; - /* when is in real_time; translate to intended stream time */ - when = when + m->delta + midi->latency; - /* make sure we don't go backward in time */ - if (when < m->last_time) when = m->last_time; - delta = when - m->last_time; - m->last_time = when; - - m->hdr = get_free_sysex_buffer(midi); - assert(m->hdr); - ptr = (unsigned long *) m->hdr->lpData; - *ptr++ = delta; - *ptr++ = 0; - *ptr = MEVT_F_LONG; - m->hdr->dwBytesRecorded = 3 * sizeof(long); - } - - /* add the data byte */ - ptr = (unsigned char *) (m->hdr->lpData + m->hdr->dwBytesRecorded); - *ptr = byte; - full = ++m->hdr->dwBytesRecorded >= m->hdr->dwBufferLength; - - /* see if we need to resize */ - if (full) { - int bytesRecorded = m->hdr->dwBytesRecorded; /* this field gets wiped out, so we'll save it */ - rslt = resize_sysex_buffer(midi, bytesRecorded, 2 * bytesRecorded); - m->hdr->dwBytesRecorded = bytesRecorded; - - if (rslt == pmBufferMaxSize) /* if buffer can't be resized */ - rslt = winmm_end_sysex(midi, timestamp); /* write what we've got and continue */ - } - } - return rslt; -} - - - -static PmError winmm_write_flush(PmInternal *midi) -{ - PmError rslt = pmNoError; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - assert(m); - if (midi->latency == 0) { - /* all messages are sent immediately */ - } else if ((m->hdr) && (!midi->sysex_in_progress)) { - /* sysex messages are sent upon completion, but ordinary messages - may be sitting in a buffer - */ - m->error = midiStreamOut(m->handle.stream, m->hdr, sizeof(MIDIHDR)); - m->hdr = NULL; - if (m->error) rslt = pmHostError; - } - return rslt; -} - -static PmTimestamp winmm_synchronize(PmInternal *midi) -{ - midiwinmm_type m; - unsigned long pm_stream_time_2; - unsigned long real_time; - unsigned long pm_stream_time; - - /* only synchronize if we are using stream interface */ - if (midi->latency == 0) return 0; - - /* figure out the time */ - m = (midiwinmm_type) midi->descriptor; - pm_stream_time_2 = pm_time_get(m); - - do { - /* read real_time between two reads of stream time */ - pm_stream_time = pm_stream_time_2; - real_time = (*midi->time_proc)(midi->time_info); - pm_stream_time_2 = pm_time_get(m); - /* repeat if more than 1ms elapsed */ - } while (pm_stream_time_2 > pm_stream_time + 1); - m->delta = pm_stream_time - real_time; - m->sync_time = real_time; - return real_time; -} - - -#ifdef GARBAGE -static PmError winmm_write(PmInternal *midi, - PmEvent *buffer, - long length) -{ - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - unsigned long now; - int i; - long msg; - PmError rslt = pmNoError; - - m->error = MMSYSERR_NOERROR; - if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */ - for (i = 0; (i < length) && (rslt == pmNoError); i++) { - int b = 0; /* count sysex bytes as they are handled */ - msg = buffer[i].message; - if ((msg & 0xFF) == MIDI_SYSEX) { - /* start a sysex message */ - m->sysex_mode = TRUE; - unsigned char midi_byte = (unsigned char) msg; - rslt = winmm_write_sysex_byte(midi, midi_byte); - b = 8; - } else if ((msg & 0x80) && ((msg & 0xFF) != MIDI_EOX)) { - /* a non-sysex message */ - m->error = midiOutShortMsg(m->handle.out, msg); - if (m->error) rslt = pmHostError; - /* any non-real-time message will terminate sysex message */ - if (!is_real_time(msg)) m->sysex_mode = FALSE; - } - /* transmit sysex bytes until we find EOX */ - if (m->sysex_mode) { - while (b < 32 /*bits*/ && (rslt == pmNoError)) { - unsigned char midi_byte = (unsigned char) (msg >> b); - rslt = winmm_write_sysex_byte(midi, midi_byte); - if (midi_byte == MIDI_EOX) { - b = 24; /* end of message */ - m->sysex_mode = FALSE; - } - b += 8; - } - } - } - } else { /* use midiStream interface -- pass data through buffers */ - LPMIDIHDR hdr = NULL; - now = (*midi->time_proc)(midi->time_info); - if (m->first_message || m->sync_time + 100 /*ms*/ < now) { - /* time to resync */ - now = synchronize(midi, m); - m->first_message = FALSE; - } - for (i = 0; i < length && rslt == pmNoError; i++) { - unsigned long when = buffer[i].timestamp; - unsigned long delta; - if (when == 0) when = now; - /* when is in real_time; translate to intended stream time */ - when = when + m->delta + midi->latency; - /* make sure we don't go backward in time */ - if (when < m->last_time) when = m->last_time; - delta = when - m->last_time; - m->last_time = when; - /* before we insert any data, we must have a buffer */ - if (hdr == NULL) { - /* stream interface: buffers allocated when stream is opened */ - hdr = get_free_output_buffer(midi); - assert(hdr); - if (m->sysex_mode) { - /* we are in the middle of a sysex message */ - start_sysex_buffer(hdr, delta); - } - } - msg = buffer[i].message; - if ((msg & 0xFF) == MIDI_SYSEX) { - /* sysex expects an empty buffer */ - if (hdr->dwBytesRecorded != 0) { - m->error = midiStreamOut(m->handle.stream, hdr, sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - hdr = get_free_output_buffer(midi); - assert(hdr); - } - /* when we see a MIDI_SYSEX, we always enter sysex mode and call - start_sysex_buffer() */ - start_sysex_buffer(hdr, delta); - m->sysex_mode = TRUE; - } - /* allow a non-real-time status byte to terminate sysex message */ - if (m->sysex_mode && (msg & 0x80) && (msg & 0xFF) != MIDI_SYSEX && - !is_real_time(msg)) { - /* I'm not sure what WinMM does if you send an incomplete sysex - message, but the best way out of this mess seems to be to - recreate the code used when you encounter an EOX, so ... - */ - MIDIEVENT *evt = (MIDIEVENT) hdr->lpData; - evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long); - /* round up BytesRecorded to multiple of 4 */ - hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3; - m->error = midiStreamOut(m->handle.stream, hdr, - sizeof(MIDIHDR)); - if (m->error) { - rslt = pmHostError; - } - hdr = NULL; /* make sure we don't send it again */ - m->sysex_mode = FALSE; /* skip to normal message send code */ - } - if (m->sysex_mode) { - int b = 0; /* count bytes as they are handled */ - while (b < 32 /* bits per word */ && (rslt == pmNoError)) { - int full; - unsigned char midi_byte = (unsigned char) (msg >> b); - if (!hdr) { - hdr = get_free_output_buffer(midi); - assert(hdr); - /* get ready to put sysex bytes in buffer */ - start_sysex_buffer(hdr, delta); - } - full = add_byte_to_buffer(m, hdr, midi_byte); - if (midi_byte == MIDI_EOX) { - b = 24; /* pretend this is last byte to exit loop */ - m->sysex_mode = FALSE; - } - /* see if it's time to send buffer, note that by always - sending complete sysex message right away, we can use - this code to set up the MIDIEVENT properly - */ - if (full || midi_byte == MIDI_EOX) { - /* add bytes recorded to MIDIEVENT length, but don't - count the MIDIEVENT data (3 longs) */ - MIDIEVENT *evt = (MIDIEVENT *) hdr->lpData; - evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long); - /* round up BytesRecorded to multiple of 4 */ - hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3; - m->error = midiStreamOut(m->handle.stream, hdr, - sizeof(MIDIHDR)); - if (m->error) { - rslt = pmHostError; - } - hdr = NULL; /* make sure we don't send it again */ - } - b += 8; /* shift to next byte */ - } - /* test rslt here in case it was set when we terminated a sysex early - (see above) */ - } else if (rslt == pmNoError) { - int full = add_to_buffer(m, hdr, delta, msg); - if (full) { - m->error = midiStreamOut(m->handle.stream, hdr, - sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - hdr = NULL; - } - } - } - if (hdr && rslt == pmNoError) { - if (m->sysex_mode) { - MIDIEVENT *evt = (MIDIEVENT *) hdr->lpData; - evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long); - /* round up BytesRecorded to multiple of 4 */ - hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3; - } - m->error = midiStreamOut(m->handle.stream, hdr, sizeof(MIDIHDR)); - if (m->error) rslt = pmHostError; - } - } - return rslt; -} -#endif - - -/* winmm_out_callback -- recycle sysex buffers */ -static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg, - DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) -{ - int i; - PmInternal *midi = (PmInternal *) dwInstance; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - LPMIDIHDR hdr = (LPMIDIHDR) dwParam1; - int err = 0; /* set to 0 so that no buffer match will also be an error */ - static int entry = 0; - if (++entry > 1) { - assert(FALSE); - } - if (m->callback_error || wMsg != MOM_DONE) { - entry--; - return ; - } - /* Future optimization: eliminate UnprepareHeader calls -- they aren't - necessary; however, this code uses the prepared-flag to indicate which - buffers are free, so we need to do something to flag empty buffers if - we leave them prepared - */ - m->callback_error = midiOutUnprepareHeader(m->handle.out, hdr, - sizeof(MIDIHDR)); - /* notify waiting sender that a buffer is available */ - /* any errors could be reported via callback_error, but this is always - treated as a Midi error, so we'd have to write a lot more code to - detect that a non-Midi error occurred and do the right thing to find - the corresponding error message text. Therefore, just use assert() - */ - - /* determine if this is an output buffer or a sysex buffer */ - - for (i = 0 ;i < m->num_buffers;i++) { - if (hdr == m->buffers[i]) { - err = SetEvent(m->buffer_signal); - break; - } - } - for (i = 0 ;i < m->num_sysex_buffers;i++) { - if (hdr == m->sysex_buffers[i]) { - err = SetEvent(m->sysex_buffer_signal); - break; - } - } - assert(err); /* false -> error */ - entry--; -} - - -/* winmm_streamout_callback -- unprepare (free) buffer header */ -static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg, - DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) -{ - PmInternal *midi = (PmInternal *) dwInstance; - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - LPMIDIHDR hdr = (LPMIDIHDR) dwParam1; - int err; - static int entry = 0; - if (++entry > 1) { - /* We've reentered this routine. I assume this never happens, but - check to make sure. Apparently, it is possible that this callback - can be called reentrantly because it happened once while debugging. - It looks like this routine is actually reentrant so we can remove - the assertion if necessary. */ - assert(FALSE); - } - if (m->callback_error || wMsg != MOM_DONE) { - entry--; - return ; - } - m->callback_error = midiOutUnprepareHeader(m->handle.out, hdr, - sizeof(MIDIHDR)); - err = SetEvent(m->buffer_signal); - assert(err); /* false -> error */ - entry--; -} - - -/* -========================================================================================= -begin exported functions -========================================================================================= -*/ - -#define winmm_in_abort pm_fail_fn -pm_fns_node pm_winmm_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - winmm_synchronize, - winmm_in_open, - winmm_in_abort, - winmm_in_close, - success_poll, - winmm_has_host_error, - winmm_get_host_error - }; - -pm_fns_node pm_winmm_out_dictionary = { - winmm_write_short, - winmm_begin_sysex, - winmm_end_sysex, - winmm_write_byte, - winmm_write_short, /* short realtime message */ - winmm_write_flush, - winmm_synchronize, - winmm_out_open, - winmm_out_abort, - winmm_out_close, - none_poll, - winmm_has_host_error, - winmm_get_host_error - }; - - -/* initialize winmm interface. Note that if there is something wrong - with winmm (e.g. it is not supported or installed), it is not an - error. We should simply return without having added any devices to - the table. Hence, no error code is returned. Furthermore, this init - code is called along with every other supported interface, so the - user would have a very hard time figuring out what hardware and API - generated the error. Finally, it would add complexity to pmwin.c to - remember where the error code came from in order to convert to text. - */ -void pm_winmm_init( void ) -{ - pm_winmm_mapper_input(); - pm_winmm_mapper_output(); - pm_winmm_general_inputs(); - pm_winmm_general_outputs(); -} - - -/* no error codes are returned, even if errors are encountered, because - there is probably nothing the user could do (e.g. it would be an error - to retry. - */ -void pm_winmm_term( void ) -{ - int i; -#ifdef DEBUG - char msg[PM_HOST_ERROR_MSG_LEN]; -#endif - int doneAny = 0; -#ifdef DEBUG - printf("pm_winmm_term called\n"); -#endif - for (i = 0; i < pm_descriptor_index; i++) { - PmInternal * midi = descriptors[i].internalDescriptor; - if (midi) { - midiwinmm_type m = (midiwinmm_type) midi->descriptor; - if (m->handle.out) { - /* close next open device*/ -#ifdef DEBUG - if (doneAny == 0) { - printf("begin closing open devices...\n"); - doneAny = 1; - } - /* report any host errors; this EXTEREMELY useful when - trying to debug client app */ - if (winmm_has_host_error(midi)) { - winmm_get_host_error(midi, msg, PM_HOST_ERROR_MSG_LEN); - printf(msg); - } -#endif - /* close all open ports */ - (*midi->dictionary->close)(midi); - } - } - } -#ifdef DEBUG - if (doneAny) { - printf("warning: devices were left open. They have been closed.\n"); - } - printf("pm_winmm_term exiting\n"); -#endif - pm_descriptor_index = 0; -} diff --git a/portmidi/pm_win/pmwinmm.h b/portmidi/pm_win/pmwinmm.h deleted file mode 100644 index 53c5fe284..000000000 --- a/portmidi/pm_win/pmwinmm.h +++ /dev/null @@ -1,5 +0,0 @@ -/* midiwin32.h -- system-specific definitions */ - -void pm_winmm_init( void ); -void pm_winmm_term( void ); - diff --git a/portmidi/portmidi.dsp b/portmidi/portmidi.dsp deleted file mode 100644 index 699cc120e..000000000 --- a/portmidi/portmidi.dsp +++ /dev/null @@ -1,124 +0,0 @@ -# Microsoft Developer Studio Project File - Name="portmidi" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=portmidi - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "portmidi.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "portmidi.mak" CFG="portmidi - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "portmidi - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "portmidi - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "portmidi - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "pm_win\Release" -# PROP Intermediate_Dir "pm_win\Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "pm_common" /I "porttime" /I "pm_win" /D "WIN32" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "portmidi - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "pm_win\Debug" -# PROP Intermediate_Dir "pm_win\Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "pm_common" /I "porttime" /I "pm_win" /D "_LIB" /D "DEBUG" /D "PM_CHECK_ERRORS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "portmidi - Win32 Release" -# Name "portmidi - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\pm_common\pmutil.c -# End Source File -# Begin Source File - -SOURCE=.\pm_win\pmwin.c -# End Source File -# Begin Source File - -SOURCE=.\pm_win\pmwinmm.c -# End Source File -# Begin Source File - -SOURCE=.\pm_common\portmidi.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\pm_common\pminternal.h -# End Source File -# Begin Source File - -SOURCE=.\pm_common\pmutil.h -# End Source File -# Begin Source File - -SOURCE=.\pm_win\pmwinmm.h -# End Source File -# Begin Source File - -SOURCE=.\pm_common\portmidi.h -# End Source File -# End Group -# End Target -# End Project diff --git a/portmidi/portmidi.dsw b/portmidi/portmidi.dsw deleted file mode 100644 index 1ccfb5bf2..000000000 --- a/portmidi/portmidi.dsw +++ /dev/null @@ -1,158 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "latency"=.\PM_TEST\latency.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name portmidi - End Project Dependency - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Project: "midithread"=.\pm_test\midithread.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pm_dll - End Project Dependency - Begin Project Dependency - Project_Dep_Name portmidi - End Project Dependency - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Project: "midithru"=.\pm_test\midithru.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pm_dll - End Project Dependency - Begin Project Dependency - Project_Dep_Name portmidi - End Project Dependency - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Project: "pm_dll"=.\pm_win\pm_dll.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "portmidi"=.\portmidi.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Project: "porttime"=.\porttime\porttime.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "sysex"=.\pm_test\sysex.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pm_dll - End Project Dependency - Begin Project Dependency - Project_Dep_Name portmidi - End Project Dependency - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Project: "test"=.\pm_test\test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pm_dll - End Project Dependency - Begin Project Dependency - Project_Dep_Name portmidi - End Project Dependency - Begin Project Dependency - Project_Dep_Name porttime - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/portmidi/porttime/porttime.c b/portmidi/porttime/porttime.c deleted file mode 100644 index 71b06f4ab..000000000 --- a/portmidi/porttime/porttime.c +++ /dev/null @@ -1,3 +0,0 @@ -/* porttime.c -- portable API for millisecond timer */ - -/* There is no machine-independent implementation code to put here */ diff --git a/portmidi/porttime/porttime.dsp b/portmidi/porttime/porttime.dsp deleted file mode 100644 index 364373c97..000000000 --- a/portmidi/porttime/porttime.dsp +++ /dev/null @@ -1,104 +0,0 @@ -# Microsoft Developer Studio Project File - Name="porttime" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=porttime - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "porttime.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "porttime.mak" CFG="porttime - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "porttime - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "porttime - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "porttime - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "porttime - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_LIB" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "porttime - Win32 Release" -# Name "porttime - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\porttime.c -# End Source File -# Begin Source File - -SOURCE=.\ptwinmm.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\porttime.h -# End Source File -# End Group -# End Target -# End Project diff --git a/portmidi/porttime/porttime.h b/portmidi/porttime/porttime.h deleted file mode 100644 index 762a71af8..000000000 --- a/portmidi/porttime/porttime.h +++ /dev/null @@ -1,36 +0,0 @@ -/* porttime.h -- portable interface to millisecond timer */ - -/* CHANGE LOG FOR PORTTIME - 10-Jun-03 Mark Nelson & RBD - boost priority of timer thread in ptlinux.c implementation - */ - -/* Should there be a way to choose the source of time here? */ - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef enum { - ptNoError = 0, - ptHostError = -10000, - ptAlreadyStarted, - ptAlreadyStopped, - ptInsufficientMemory -} PtError; - - -typedef long PtTimestamp; - -typedef void (PtCallback)( PtTimestamp timestamp, void *userData ); - - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData); -PtError Pt_Stop( void); -int Pt_Started( void); -PtTimestamp Pt_Time( void); - -#ifdef __cplusplus -} -#endif diff --git a/portmidi/porttime/ptlinux.c b/portmidi/porttime/ptlinux.c deleted file mode 100644 index c99abf04d..000000000 --- a/portmidi/porttime/ptlinux.c +++ /dev/null @@ -1,120 +0,0 @@ -/* ptlinux.c -- portable timer implementation for linux */ - - -/* IMPLEMENTATION NOTES (by Mark Nelson): - -Unlike Windows, Linux has no system call to request a periodic callback, -so if Pt_Start() receives a callback parameter, it must create a thread -that wakes up periodically and calls the provided callback function. -If running as superuser, use setpriority() to renice thread to -20. -One could also set the timer thread to a real-time priority (SCHED_FIFO -and SCHED_RR), but this is dangerous for This is necessary because -if the callback hangs it'll never return. A more serious reason -is that the current scheduler implementation busy-waits instead -of sleeping when realtime threads request a sleep of <=2ms (as a way -to get around the 10ms granularity), which means the thread would never -let anyone else on the CPU. - -CHANGE LOG - -18-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer - thread. Simplified implementation notes. - -*/ - -#include "porttime.h" -#include "sys/time.h" -#include "sys/resource.h" -#include "sys/timeb.h" -#include "pthread.h" - -#define TRUE 1 -#define FALSE 0 - -static int time_started_flag = FALSE; -static struct timeb time_offset = {0, 0, 0, 0}; -static pthread_t pt_thread_pid; - -/* note that this is static data -- we only need one copy */ -typedef struct { - int id; - int resolution; - PtCallback *callback; - void *userData; -} pt_callback_parameters; - -static int pt_callback_proc_id = 0; - -static void *Pt_CallbackProc(void *p) -{ - pt_callback_parameters *parameters = (pt_callback_parameters *) p; - int mytime = 1; - /* to kill a process, just increment the pt_callback_proc_id */ - /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, - parameters->id); */ - if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20); - while (pt_callback_proc_id == parameters->id) { - /* wait for a multiple of resolution ms */ - struct timeval timeout; - int delay = mytime++ * parameters->resolution - Pt_Time(); - if (delay < 0) delay = 0; - timeout.tv_sec = 0; - timeout.tv_usec = delay * 1000; - select(0, NULL, NULL, NULL, &timeout); - (*(parameters->callback))(Pt_Time(), parameters->userData); - } - printf("Pt_CallbackProc exiting\n"); -// free(parameters); - return NULL; -} - - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData) -{ - if (time_started_flag) return ptNoError; - ftime(&time_offset); /* need this set before process runs */ - if (callback) { - int res; - pt_callback_parameters *parms = (pt_callback_parameters *) - malloc(sizeof(pt_callback_parameters)); - if (!parms) return ptInsufficientMemory; - parms->id = pt_callback_proc_id; - parms->resolution = resolution; - parms->callback = callback; - parms->userData = userData; - res = pthread_create(&pt_thread_pid, NULL, - Pt_CallbackProc, parms); - if (res != 0) return ptHostError; - } - time_started_flag = TRUE; - return ptNoError; -} - - -PtError Pt_Stop() -{ - printf("Pt_Stop called\n"); - pt_callback_proc_id++; - time_started_flag = FALSE; - return ptNoError; -} - - -int Pt_Started() -{ - return time_started_flag; -} - - -PtTimestamp Pt_Time() -{ - long seconds, milliseconds; - struct timeb now; - ftime(&now); - seconds = now.time - time_offset.time; - milliseconds = now.millitm - time_offset.millitm; - return seconds * 1000 + milliseconds; -} - - - diff --git a/portmidi/porttime/ptmacosx_cf.c b/portmidi/porttime/ptmacosx_cf.c deleted file mode 100644 index 1837246bd..000000000 --- a/portmidi/porttime/ptmacosx_cf.c +++ /dev/null @@ -1,132 +0,0 @@ -/* ptmacosx.c -- portable timer implementation for mac os x */ - -#include <stdlib.h> -#include <stdio.h> -#include <pthread.h> -#include <CoreFoundation/CoreFoundation.h> - -#import <mach/mach.h> -#import <mach/mach_error.h> -#import <mach/mach_time.h> -#import <mach/clock.h> - -#include "porttime.h" - -#define THREAD_IMPORTANCE 30 -#define LONG_TIME 1000000000.0 - -static int time_started_flag = FALSE; -static CFAbsoluteTime startTime = 0.0; -static CFRunLoopRef timerRunLoop; - -typedef struct { - int resolution; - PtCallback *callback; - void *userData; -} PtThreadParams; - - -void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info) -{ - PtThreadParams *params = (PtThreadParams*)info; - (*params->callback)(Pt_Time(), params->userData); -} - -static void* Pt_Thread(void *p) -{ - CFTimeInterval timerInterval; - CFRunLoopTimerContext timerContext; - CFRunLoopTimerRef timer; - PtThreadParams *params = (PtThreadParams*)p; - //CFTimeInterval timeout; - - /* raise the thread's priority */ - kern_return_t error; - thread_extended_policy_data_t extendedPolicy; - thread_precedence_policy_data_t precedencePolicy; - - extendedPolicy.timeshare = 0; - error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, - (thread_policy_t)&extendedPolicy, - THREAD_EXTENDED_POLICY_COUNT); - if (error != KERN_SUCCESS) { - mach_error("Couldn't set thread timeshare policy", error); - } - - precedencePolicy.importance = THREAD_IMPORTANCE; - error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, - (thread_policy_t)&precedencePolicy, - THREAD_PRECEDENCE_POLICY_COUNT); - if (error != KERN_SUCCESS) { - mach_error("Couldn't set thread precedence policy", error); - } - - /* set up the timer context */ - timerContext.version = 0; - timerContext.info = params; - timerContext.retain = NULL; - timerContext.release = NULL; - timerContext.copyDescription = NULL; - - /* create a new timer */ - timerInterval = (double)params->resolution / 1000.0; - timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval, - 0, 0, Pt_CFTimerCallback, &timerContext); - - timerRunLoop = CFRunLoopGetCurrent(); - CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode")); - - /* run until we're told to stop by Pt_Stop() */ - CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false); - - CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode")); - CFRelease(timer); - free(params); - - return NULL; -} - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData) -{ - PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams)); - pthread_t pthread_id; - - // /* make sure we're not already playing */ - if (time_started_flag) return ptAlreadyStarted; - startTime = CFAbsoluteTimeGetCurrent(); - - if (callback) { - - params->resolution = resolution; - params->callback = callback; - params->userData = userData; - - pthread_create(&pthread_id, NULL, Pt_Thread, params); - } - - time_started_flag = TRUE; - return ptNoError; -} - - -PtError Pt_Stop() -{ - - CFRunLoopStop(timerRunLoop); - time_started_flag = FALSE; - return ptNoError; -} - - -int Pt_Started() -{ - return time_started_flag; -} - - -PtTimestamp Pt_Time() -{ - CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); - return (PtTimestamp) ((now - startTime) * 1000.0); -} - diff --git a/portmidi/porttime/ptmacosx_mach.c b/portmidi/porttime/ptmacosx_mach.c deleted file mode 100644 index 935d99bb7..000000000 --- a/portmidi/porttime/ptmacosx_mach.c +++ /dev/null @@ -1,123 +0,0 @@ -/* ptmacosx.c -- portable timer implementation for mac os x */ - -#include <stdlib.h> -#include <stdio.h> -#include <CoreAudio/HostTime.h> - -#import <mach/mach.h> -#import <mach/mach_error.h> -#import <mach/mach_time.h> -#import <mach/clock.h> - -#include "porttime.h" -#include "sys/time.h" -#include "pthread.h" - -#define NSEC_PER_MSEC 1000000 -#define THREAD_IMPORTANCE 30 - -static int time_started_flag = FALSE; -static UInt64 start_time; -static pthread_t pt_thread_pid; - -/* note that this is static data -- we only need one copy */ -typedef struct { - int id; - int resolution; - PtCallback *callback; - void *userData; -} pt_callback_parameters; - -static int pt_callback_proc_id = 0; - -static void *Pt_CallbackProc(void *p) -{ - pt_callback_parameters *parameters = (pt_callback_parameters *) p; - int mytime = 1; - - kern_return_t error; - thread_extended_policy_data_t extendedPolicy; - thread_precedence_policy_data_t precedencePolicy; - - extendedPolicy.timeshare = 0; - error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, - (thread_policy_t)&extendedPolicy, - THREAD_EXTENDED_POLICY_COUNT); - if (error != KERN_SUCCESS) { - mach_error("Couldn't set thread timeshare policy", error); - } - - precedencePolicy.importance = THREAD_IMPORTANCE; - error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, - (thread_policy_t)&precedencePolicy, - THREAD_PRECEDENCE_POLICY_COUNT); - if (error != KERN_SUCCESS) { - mach_error("Couldn't set thread precedence policy", error); - } - - - /* to kill a process, just increment the pt_callback_proc_id */ - printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); - while (pt_callback_proc_id == parameters->id) { - /* wait for a multiple of resolution ms */ - UInt64 wait_time; - int delay = mytime++ * parameters->resolution - Pt_Time(); - if (delay < 0) delay = 0; - wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); - wait_time += AudioGetCurrentHostTime(); - error = mach_wait_until(wait_time); - (*(parameters->callback))(Pt_Time(), parameters->userData); - } - printf("Pt_CallbackProc exiting\n"); - free(parameters); - return NULL; -} - - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData) -{ - if (time_started_flag) return ptAlreadyStarted; - start_time = AudioGetCurrentHostTime(); - - if (callback) { - int res; - pt_callback_parameters *parms; - - parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters)); - if (!parms) return ptInsufficientMemory; - parms->id = pt_callback_proc_id; - parms->resolution = resolution; - parms->callback = callback; - parms->userData = userData; - res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); - if (res != 0) return ptHostError; - } - - time_started_flag = TRUE; - return ptNoError; -} - - -PtError Pt_Stop() -{ - printf("Pt_Stop called\n"); - pt_callback_proc_id++; - time_started_flag = FALSE; - return ptNoError; -} - - -int Pt_Started() -{ - return time_started_flag; -} - - -PtTimestamp Pt_Time() -{ - UInt64 clock_time, nsec_time; - clock_time = AudioGetCurrentHostTime() - start_time; - nsec_time = AudioConvertHostTimeToNanos(clock_time); - return (PtTimestamp)(nsec_time / NSEC_PER_MSEC); -} - diff --git a/portmidi/porttime/ptwinmm.c b/portmidi/porttime/ptwinmm.c deleted file mode 100644 index bbfebb0ec..000000000 --- a/portmidi/porttime/ptwinmm.c +++ /dev/null @@ -1,65 +0,0 @@ -/* ptwinmm.c -- portable timer implementation for win32 */ - - -#include "porttime.h" -#include "windows.h" -#include "time.h" - - -TIMECAPS caps; - -static long time_offset = 0; -static int time_started_flag = FALSE; -static long time_resolution; -static MMRESULT timer_id; -static PtCallback *time_callback; - -void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD dwUser, - DWORD dw1, DWORD dw2) -{ - (*time_callback)(Pt_Time(), (void *) dwUser); -} - - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData) -{ - if (time_started_flag) return ptAlreadyStarted; - timeBeginPeriod(resolution); - time_resolution = resolution; - time_offset = timeGetTime(); - time_started_flag = TRUE; - time_callback = callback; - if (callback) { - timer_id = timeSetEvent(resolution, 1, winmm_time_callback, - (DWORD) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); - if (!timer_id) return ptHostError; - } - return ptNoError; -} - - -PtError Pt_Stop() -{ - if (!time_started_flag) return ptAlreadyStopped; - if (time_callback && timer_id) { - timeKillEvent(timer_id); - time_callback = NULL; - timer_id = 0; - } - time_started_flag = FALSE; - timeEndPeriod(time_resolution); - return ptNoError; -} - - -int Pt_Started() -{ - return time_started_flag; -} - - -PtTimestamp Pt_Time() -{ - return timeGetTime() - time_offset; -} - diff --git a/src/s_midi_sgi.c b/src/s_midi_sgi.c deleted file mode 100644 index 4634b5712..000000000 --- a/src/s_midi_sgi.c +++ /dev/null @@ -1,203 +0,0 @@ -/* Copyright (c) 1997-1999 Miller Puckette. -* For information on usage and redistribution, and for a DISCLAIMER OF ALL -* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ - -#include "m_pd.h" -#include "s_stuff.h" -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#ifdef HAVE_BSTRING_H -#include <bstring.h> -#endif -#include <sys/types.h> -#include <sys/time.h> - -#include <dmedia/audio.h> -#include <sys/fpu.h> -#include <dmedia/midi.h> -int mdInit(void); /* prototype was messed up in midi.h */ -/* #include "sys/select.h" */ - - - /* - set the special "flush zero" but (FS, bit 24) in the - Control Status Register of the FPU of R4k and beyond - so that the result of any underflowing operation will - be clamped to zero, and no exception of any kind will - be generated on the CPU. - - thanks to cpirazzi@cp.esd.sgi.com (Chris Pirazzi). - */ - -static void sgi_flush_all_underflows_to_zero(void) -{ - union fpc_csr f; - f.fc_word = get_fpc_csr(); - f.fc_struct.flush = 1; - set_fpc_csr(f.fc_word); -} - -#define NPORT 2 - -static MDport sgi_inport[NPORT]; -static MDport sgi_outport[NPORT]; - -void sgi_open_midi(int midiin, int midiout) -{ - int i; - int sgi_nports = mdInit(); - if (sgi_nports < 0) sgi_nports = 0; - else if (sgi_nports > NPORT) sgi_nports = NPORT; - if (sys_verbose) - { - if (!sgi_nports) - { - post("no serial ports are configured for MIDI;"); - post("if you want to use MIDI, try exiting Pd, typing"); - post("'startmidi -d /dev/ttyd2' to a shell, and restarting Pd."); - } - else if (sgi_nports == 1) - post("Found one MIDI port on %s", mdGetName(0)); - else if (sgi_nports == 2) - post("Found MIDI ports on %s and %s", - mdGetName(0), mdGetName(1)); - } - if (midiin) - { - for (i = 0; i < sgi_nports; i++) - { - if (!(sgi_inport[i] = mdOpenInPort(mdGetName(i)))) - error("MIDI input port %d: open failed", i+1);; - } - } - if (midiout) - { - for (i = 0; i < sgi_nports; i++) - { - if (!(sgi_outport[i] = mdOpenOutPort(mdGetName(i)))) - error("MIDI output port %d: open failed", i+1);; - } - } - return; -} - -void sys_putmidimess(int portno, int a, int b, int c) -{ - MDevent mdv; - if (portno >= NPORT || portno < 0 || !sgi_outport[portno]) return; - mdv.msg[0] = a; - mdv.msg[1] = b; - mdv.msg[2] = c; - mdv.msg[3] = 0; - mdv.sysexmsg = 0; - mdv.stamp = 0; - mdv.msglen = 0; - if (mdSend(sgi_outport[portno], &mdv, 1) < 0) - error("MIDI output error\n"); - post("msg out %d %d %d", a, b, c); -} - -void sys_putmidibyte(int portno, int foo) -{ - error("MIDI raw byte output not available on SGI"); -} - -void inmidi_noteon(int portno, int channel, int pitch, int velo); -void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); -void inmidi_programchange(int portno, int channel, int value); -void inmidi_pitchbend(int portno, int channel, int value); -void inmidi_aftertouch(int portno, int channel, int value); -void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); - -void sys_poll_midi(void) -{ - int i; - MDport *mp; - for (i = 0, mp = sgi_inport; i < NPORT; i++, mp++) - { - int ret, status, b1, b2, nfds; - MDevent mdv; - fd_set inports; - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (!*mp) continue; - FD_ZERO(&inports); - FD_SET(mdGetFd(*mp), &inports); - - if (select(mdGetFd(*mp)+1 , &inports, 0, 0, &timeout) < 0) - perror("midi select"); - if (FD_ISSET(mdGetFd(*mp),&inports)) - { - if (mdReceive(*mp, &mdv, 1) < 0) - error("failure receiving message\n"); - else if (mdv.msg[0] == MD_SYSEX) mdFree(mdv.sysexmsg); - - else - { - int status = mdv.msg[0]; - int channel = (status & 0xf) + 1; - int b1 = mdv.msg[1]; - int b2 = mdv.msg[2]; - switch(status & 0xf0) - { - case MD_NOTEOFF: - inmidi_noteon(i, channel, b1, 0); - break; - case MD_NOTEON: - inmidi_noteon(i, channel, b1, b2); - break; - case MD_POLYKEYPRESSURE: - inmidi_polyaftertouch(i, channel, b1, b2); - break; - case MD_CONTROLCHANGE: - inmidi_controlchange(i, channel, b1, b2); - break; - case MD_PITCHBENDCHANGE: - inmidi_pitchbend(i, channel, ((b2 << 7) + b1)); - break; - case MD_PROGRAMCHANGE: - inmidi_programchange(i, channel, b1); - break; - case MD_CHANNELPRESSURE: - inmidi_aftertouch(i, channel, b1); - break; - } - } - } - } -} - -void sys_do_open_midi(int nmidiin, int *midiinvec, - int nmidiout, int *midioutvec) -{ - sgi_open_midi(nmidiin!=0, nmidiout!=0); -} - - -void sys_close_midi( void) -{ - /* ??? */ -} - - -void midi_getdevs(char *indevlist, int *nindevs, - char *outdevlist, int *noutdevs, int maxndev, int devdescsize) -{ - int i, nindev = 0, noutdev = 0; - for (i = 0; i < mdInit(); i++) - { - if (nindev < maxndev) - { - strcpy(indevlist + nindev * devdescsize, mdGetName(i)); - nindev++; - - strcpy(outdevlist + noutdev * devdescsize, mdGetName(i)); - noutdev++; - } - } - *nindevs = nindev; - *noutdevs = noutdev; -} -- GitLab