From c1b10d55375dd8ecdf7b223d1f12541983422764 Mon Sep 17 00:00:00 2001 From: Miller Puckette Date: Sat, 18 Aug 2007 23:32:44 +0000 Subject: Download and adjust sources for new portaudio, portmidi. Add experimental callback scheduling. svn path=/trunk/; revision=8657 --- pd/extra/sigmund~/sigmund~-help.pd | 38 +- pd/portaudio/LICENSE.txt | 65 - pd/portaudio/README.txt | 81 - pd/portaudio/V19-devel-readme.txt | 222 -- pd/portaudio/pa_asio/ASIO-README.txt | 137 - pd/portaudio/pa_asio/Callback_adaptation_.pdf | Bin 50527 -> 0 bytes pd/portaudio/pa_asio/Pa_ASIO.pdf | Bin 50778 -> 0 bytes pd/portaudio/pa_asio/iasiothiscallresolver.cpp | 563 --- pd/portaudio/pa_asio/iasiothiscallresolver.h | 197 -- pd/portaudio/pa_asio/pa_asio.cpp | 2958 ---------------- pd/portaudio/pa_asio/pa_asio.h | 122 - pd/portaudio/pa_common/pa_allocation.c | 234 -- pd/portaudio/pa_common/pa_allocation.h | 95 - pd/portaudio/pa_common/pa_converters.c | 1926 ---------- pd/portaudio/pa_common/pa_converters.h | 254 -- pd/portaudio/pa_common/pa_cpuload.c | 96 - pd/portaudio/pa_common/pa_cpuload.h | 63 - pd/portaudio/pa_common/pa_dither.c | 204 -- pd/portaudio/pa_common/pa_dither.h | 91 - pd/portaudio/pa_common/pa_endianness.h | 113 - pd/portaudio/pa_common/pa_front.c | 1981 ----------- pd/portaudio/pa_common/pa_hostapi.h | 244 -- pd/portaudio/pa_common/pa_process.c | 1763 ---------- pd/portaudio/pa_common/pa_process.h | 741 ---- pd/portaudio/pa_common/pa_skeleton.c | 807 ----- pd/portaudio/pa_common/pa_stream.c | 141 - pd/portaudio/pa_common/pa_stream.h | 196 -- pd/portaudio/pa_common/pa_trace.c | 88 - pd/portaudio/pa_common/pa_trace.h | 70 - pd/portaudio/pa_common/pa_types.h | 65 - pd/portaudio/pa_common/pa_util.h | 167 - pd/portaudio/pa_common/portaudio.h | 1124 ------ pd/portaudio/pa_dll_switch/PaDllEntry.h | 184 - .../pa_dll_switch/letter_from_tim_010817.txt | Bin 1176 -> 0 bytes pd/portaudio/pa_dll_switch/loadPA_DLL.cpp | 203 -- pd/portaudio/pa_dll_switch/pa_lib.c | 827 ----- pd/portaudio/pa_dll_switch/portaudio.h | 439 --- pd/portaudio/pa_jack/pa_jack.c | 1714 --------- pd/portaudio/pa_linux_alsa/pa_linux_alsa.c | 3682 -------------------- pd/portaudio/pa_linux_alsa/pa_linux_alsa.h | 64 - pd/portaudio/pa_mac/pa_mac_hostapis.c | 79 - pd/portaudio/pa_mac_core/notes.txt | 145 - pd/portaudio/pa_mac_core/pa_mac_core.c | 2105 ----------- pd/portaudio/pa_mac_core/pa_mac_core.h | 69 - pd/portaudio/pa_mac_core/pa_mac_core_utilities.c | 466 --- pd/portaudio/pa_unix/pa_unix_hostapis.c | 64 - pd/portaudio/pa_unix/pa_unix_util.c | 192 - pd/portaudio/pa_unix/pa_unix_util.h | 73 - pd/portaudio/pa_unix_oss/pa_unix_oss.c | 1924 ---------- pd/portaudio/pa_win/pa_win_hostapis.c | 86 - pd/portaudio/pa_win/pa_win_util.c | 134 - pd/portaudio/pa_win/pa_x86_plain_converters.c | 1167 ------- pd/portaudio/pa_win/pa_x86_plain_converters.h | 19 - pd/portaudio/pa_win_ds/dsound_wrapper.c | 616 ---- pd/portaudio/pa_win_ds/dsound_wrapper.h | 130 - pd/portaudio/pa_win_ds/pa_win_ds.c | 1864 ---------- pd/portaudio/pa_win_wdmks/pa_win_wdmks.c | 3269 ----------------- pd/portaudio/pa_win_wdmks/readme.txt | 82 - pd/portaudio/pa_win_wmme/pa_win_wmme.c | 3634 ------------------- pd/portaudio/pa_win_wmme/pa_win_wmme.h | 160 - pd/portaudio/pablio/ringbuffer.c | 199 -- pd/portaudio/pablio/ringbuffer.h | 101 - pd/portmidi/CHANGELOG.txt | 158 + pd/portmidi/Makefile | 77 - pd/portmidi/README.txt | 51 +- pd/portmidi/license.txt | 40 + pd/portmidi/portmidi.dsp | 124 - pd/portmidi/portmidi.dsw | 158 - pd/src/CHANGELOG.txt | 4 + pd/src/configure.in | 131 +- pd/src/m_pd.h | 2 +- pd/src/m_sched.c | 80 +- pd/src/makefile.dependencies | 0 pd/src/makefile.nt | 85 +- pd/src/s_audio.c | 237 +- pd/src/s_audio_mmio.c | 4 +- pd/src/s_audio_pa.c | 157 +- pd/src/s_audio_pablio.c | 55 - pd/src/s_file.c | 16 +- pd/src/s_inter.c | 4 + pd/src/s_main.c | 25 +- pd/src/s_midi_pm.c | 1 - pd/src/s_stuff.h | 20 +- pd/src/t_tkcmd.c | 12 +- pd/src/u_main.tk | 23 +- pd/src/x_arithmetic.c | 3 - 86 files changed, 748 insertions(+), 39256 deletions(-) delete mode 100644 pd/portaudio/LICENSE.txt delete mode 100644 pd/portaudio/README.txt delete mode 100644 pd/portaudio/V19-devel-readme.txt delete mode 100644 pd/portaudio/pa_asio/ASIO-README.txt delete mode 100644 pd/portaudio/pa_asio/Callback_adaptation_.pdf delete mode 100644 pd/portaudio/pa_asio/Pa_ASIO.pdf delete mode 100644 pd/portaudio/pa_asio/iasiothiscallresolver.cpp delete mode 100644 pd/portaudio/pa_asio/iasiothiscallresolver.h delete mode 100644 pd/portaudio/pa_asio/pa_asio.cpp delete mode 100644 pd/portaudio/pa_asio/pa_asio.h delete mode 100644 pd/portaudio/pa_common/pa_allocation.c delete mode 100644 pd/portaudio/pa_common/pa_allocation.h delete mode 100644 pd/portaudio/pa_common/pa_converters.c delete mode 100644 pd/portaudio/pa_common/pa_converters.h delete mode 100644 pd/portaudio/pa_common/pa_cpuload.c delete mode 100644 pd/portaudio/pa_common/pa_cpuload.h delete mode 100644 pd/portaudio/pa_common/pa_dither.c delete mode 100644 pd/portaudio/pa_common/pa_dither.h delete mode 100644 pd/portaudio/pa_common/pa_endianness.h delete mode 100644 pd/portaudio/pa_common/pa_front.c delete mode 100644 pd/portaudio/pa_common/pa_hostapi.h delete mode 100644 pd/portaudio/pa_common/pa_process.c delete mode 100644 pd/portaudio/pa_common/pa_process.h delete mode 100644 pd/portaudio/pa_common/pa_skeleton.c delete mode 100644 pd/portaudio/pa_common/pa_stream.c delete mode 100644 pd/portaudio/pa_common/pa_stream.h delete mode 100644 pd/portaudio/pa_common/pa_trace.c delete mode 100644 pd/portaudio/pa_common/pa_trace.h delete mode 100644 pd/portaudio/pa_common/pa_types.h delete mode 100644 pd/portaudio/pa_common/pa_util.h delete mode 100644 pd/portaudio/pa_common/portaudio.h delete mode 100644 pd/portaudio/pa_dll_switch/PaDllEntry.h delete mode 100644 pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt delete mode 100644 pd/portaudio/pa_dll_switch/loadPA_DLL.cpp delete mode 100644 pd/portaudio/pa_dll_switch/pa_lib.c delete mode 100644 pd/portaudio/pa_dll_switch/portaudio.h delete mode 100644 pd/portaudio/pa_jack/pa_jack.c delete mode 100644 pd/portaudio/pa_linux_alsa/pa_linux_alsa.c delete mode 100644 pd/portaudio/pa_linux_alsa/pa_linux_alsa.h delete mode 100644 pd/portaudio/pa_mac/pa_mac_hostapis.c delete mode 100644 pd/portaudio/pa_mac_core/notes.txt delete mode 100644 pd/portaudio/pa_mac_core/pa_mac_core.c delete mode 100644 pd/portaudio/pa_mac_core/pa_mac_core.h delete mode 100644 pd/portaudio/pa_mac_core/pa_mac_core_utilities.c delete mode 100644 pd/portaudio/pa_unix/pa_unix_hostapis.c delete mode 100644 pd/portaudio/pa_unix/pa_unix_util.c delete mode 100644 pd/portaudio/pa_unix/pa_unix_util.h delete mode 100644 pd/portaudio/pa_unix_oss/pa_unix_oss.c delete mode 100644 pd/portaudio/pa_win/pa_win_hostapis.c delete mode 100644 pd/portaudio/pa_win/pa_win_util.c delete mode 100644 pd/portaudio/pa_win/pa_x86_plain_converters.c delete mode 100644 pd/portaudio/pa_win/pa_x86_plain_converters.h delete mode 100644 pd/portaudio/pa_win_ds/dsound_wrapper.c delete mode 100644 pd/portaudio/pa_win_ds/dsound_wrapper.h delete mode 100644 pd/portaudio/pa_win_ds/pa_win_ds.c delete mode 100644 pd/portaudio/pa_win_wdmks/pa_win_wdmks.c delete mode 100644 pd/portaudio/pa_win_wdmks/readme.txt delete mode 100644 pd/portaudio/pa_win_wmme/pa_win_wmme.c delete mode 100644 pd/portaudio/pa_win_wmme/pa_win_wmme.h delete mode 100644 pd/portaudio/pablio/ringbuffer.c delete mode 100644 pd/portaudio/pablio/ringbuffer.h create mode 100644 pd/portmidi/CHANGELOG.txt delete mode 100644 pd/portmidi/Makefile create mode 100644 pd/portmidi/license.txt delete mode 100644 pd/portmidi/portmidi.dsp delete mode 100644 pd/portmidi/portmidi.dsw delete mode 100644 pd/src/makefile.dependencies (limited to 'pd') diff --git a/pd/extra/sigmund~/sigmund~-help.pd b/pd/extra/sigmund~/sigmund~-help.pd index f3556c9c..c60ca9a8 100644 --- a/pd/extra/sigmund~/sigmund~-help.pd +++ b/pd/extra/sigmund~/sigmund~-help.pd @@ -1,4 +1,4 @@ -#N canvas 209 199 580 617 12; +#N canvas 193 41 580 617 12; #X text 42 4 sigmund~ - sinusoidal analysis and pitch tracking; #N canvas 432 117 573 597 using-with-tables 0; #X obj 29 368 print peak; @@ -91,7 +91,7 @@ of a note at or near the previously output pitch.; #X connect 1 0 2 0; #X connect 2 0 3 0; #X restore 330 531 pd setting-parameters; -#N canvas 190 230 640 535 sinusoid-tracking 0; +#N canvas 149 65 641 815 sinusoid-tracking 1; #X obj 124 267 sigmund~ -npeak 10 peaks; #X obj 124 214 phasor~; #X obj 124 144 loadbang; @@ -112,14 +112,28 @@ of a note at or near the previously output pitch.; #X text 419 442 peak amplitude (linear); #X text 464 416 cosine component; #X text 499 390 sine component; -#X text 42 26 You can ask for sinusoidal peaks in decreasing order -of amplitude or arranged into maximally continuous tracks for resynthesis. -(Or you can ask for both.) In any case \, out come lists of five numbers -\, one for each sinusoid at each analysis period. The first is the -number of the sinusoid (so you can use "route" to claw them apart). -The other four are as shown:; #X text 79 505 loudest partial; #X text 332 508 quietest partial; +#X text 36 4 You can ask for sinusoidal peaks in decreasing order of +amplitude or arranged into maximally continuous tracks for resynthesis. +(Or you can ask for both.) If you ask for peaks \, out come lists of +five numbers \, one for each sinusoid at each analysis period. The +first is the index number of the sinusoid (so you can use "route" to +claw them apart). The other four are as shown:; +#X obj 204 611 osc~ 440; +#X obj 204 635 *~; +#X obj 205 689 unpack 0 0 0 0; +#X floatatom 205 782 5 0 0 0 - - -; +#X floatatom 245 760 5 0 0 0 - - -; +#X floatatom 285 737 5 0 0 0 - - -; +#X floatatom 326 713 5 0 0 0 - - -; +#X obj 246 638 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 +1; +#X text 43 535 If you ask for "tracks" \, the output is four numbers: +index \, frequency \, and amplitude as before \, and finally a flag +which is one for a new track \, zero for a continuation \, minus one +for an empty track.; +#X obj 205 662 sigmund~ -npts 16384 -hop 8192 -npeak 1 tracks; #X connect 0 0 4 0; #X connect 1 0 0 0; #X connect 2 0 9 0; @@ -135,6 +149,14 @@ The other four are as shown:; #X connect 11 1 13 0; #X connect 11 2 14 0; #X connect 11 3 15 0; +#X connect 23 0 24 0; +#X connect 24 0 32 0; +#X connect 25 0 26 0; +#X connect 25 1 27 0; +#X connect 25 2 28 0; +#X connect 25 3 29 0; +#X connect 30 0 24 1; +#X connect 32 0 25 0; #X restore 330 508 pd sinusoid-tracking; #X text 52 165 tracks - output sinusoidal peaks organized into tracks ; diff --git a/pd/portaudio/LICENSE.txt b/pd/portaudio/LICENSE.txt deleted file mode 100644 index 105da3f7..00000000 --- a/pd/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/ - * Implementation - * Copyright (c) 1999-2000 - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ \ No newline at end of file diff --git a/pd/portaudio/README.txt b/pd/portaudio/README.txt deleted file mode 100644 index 4cfc6166..00000000 --- a/pd/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/pd/portaudio/V19-devel-readme.txt b/pd/portaudio/V19-devel-readme.txt deleted file mode 100644 index ae5570eb..00000000 --- a/pd/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_ where MN is the module name, for example -the windows mme initializer should be named PaWinWmme_Initialize(). - -Every host api should define an initializer which returns an error code -and a PaHostApiInterface*. The initializer should only return an error other -than paNoError if it encounters an unexpected and fatal error (memory allocation -error for example). In general, there may be conditions under which it returns -a NULL interface pointer and also returns paNoError. For example, if the ASIO -implementation detects that ASIO is not installed, it should return a -NULL interface, and paNoError. - -Platform-specific shared functions should begin with Pa_ where PN is the -platform name. eg. PaWin_ for windows, PaUnix_ for unix. - -The above two conventions should also be followed whenever it is necessary to -share functions accross multiple source files. - -Two utilities for debug messages are provided. The PA_DEBUG macro defined in -pa_implementation.h provides a simple way to print debug messages to stderr. -Due to real-time performance issues, PA_DEBUG may not be suitable for use -within the portaudio processing callback, or in other threads. In such cases -the event tracing facility provided in pa_trace.h may be more appropriate. - -If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API -will be logged to stderr along with parameter and return values. - - -TODO: - (this list is totally out of date) - - finish coding converter functions in pa_converters.c (anyone?) - - implement block adaption in pa_process.c (phil?) - - fix all current tests to work with new code. this should mostly involve - changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc. - - write some new tests to exercise the multi-api functions - - write (doxygen) documentation for pa_trace (phil?) - - remove unused typeids from PaHostAPITypeID - - create a global configuration file which documents which PA_ defines can be - used for configuration - - need a coding standard for comment formatting - - migrate directx (phil) - - migrate asio (ross?, stephane?) - - see top of pa_win_wmme.c for MME todo items (ross) - - write style guide document (ross) - - -DESIGN ISSUES: - (this list is totally out of date) - - consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API - - switch to new latency parameter mechanism now (?) - - question: if input or outputDriverInfo structures are passed for a different - hostApi from the one being called, do we return an error or just ignore - them? (i think return error) - - consider renaming PortAudioCallback to PaStreamCallback - - consider renaming PaError, PaResult - - -ASSORTED DISORGANISED NOTES: - - NOTE: - pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do: - - checks the device info to make sure that the device supports the requested sample rate, - it may also change the sample rate to the "closest available" sample rate if it - is within a particular error margin - - rationale for breaking up internalPortAudioStream: - each implementation has its own requirements and behavior, and should be - able to choose the best way to operate without being limited by the - constraints imposed by a common infrastructure. in other words the - implementations should be able to pick and choose services from the - common infrastructure. currently identified services include: - - - cpu load tracking - - buffering and conversion service (same code works for input and output) - - should support buffer multiplexing (non-integer length input and output buffers) - - in-place conversion where possible (only for callback, read/write always copies) - - should manage allocation of temporary buffers if necessary - - instrumentation (should be able to be disabled): callback count, framesProcessed - - common data: magic, streamInterface, callback, userdata - - -- conversion functions: - - should handle temp buffer allocation - - dithering (random number state per-stream) - - buffer size mismatches - - with new buffer slip rules, temp buffers may always be needed - - we should aim for in-place conversion wherever possible - - does phil's code support in-place conversion? (yes) - -- dicuss relationship between user and host buffer sizes - - completely independent.. individual implementations may constrain - host buffer sizes if necessary - - -- discuss device capabilities: - - i'd like to be able to request certain information: - - channel count for example - diff --git a/pd/portaudio/pa_asio/ASIO-README.txt b/pd/portaudio/pa_asio/ASIO-README.txt deleted file mode 100644 index 9fb74ae1..00000000 --- a/pd/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 . - - -Building PortAudio with ASIO support ------------------------------------- - -To build PortAudio with ASIO support you need to compile and link with -pa_asio.c, and files from the ASIO SDK (see below), along with the common -files from pa_common/ and platform specific files from pa_win/ (for Win32) -or pa_mac/ (for Macintosh). - -If you are compiling with a non-Microsoft compiler on windows, also -compile and link with iasiothiscallresolver.cpp (see below for -an explanation). - -For some platforms (MingW, possibly Mac), you may simply -be able to type: - -./configure --with-host_os=mingw --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2] -make - -./configure --with-host_os=darwin --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2] -make - -and life will be good. - - -Obtaining the ASIO SDK ----------------------- - -In order to build PortAudio with ASIO support, you need to download -the ASIO SDK (version 2.0) from Steinberg. Steinberg makes the ASIO -SDK available to anyone free of charge, however they do not permit its -source code to be distributed. - -NOTE: In some cases the ASIO SDK may require patching, see below -for further details. - -http://www.steinberg.net/en/ps/support/3rdparty/asio_sdk/ - -If the above link is broken search Google for: -"download steinberg ASIO SDK" - - - -Building the ASIO SDK on Macintosh ----------------------------------- - -To build the ASIO SDK on Macintosh you need to compile and link with the -following files from the ASIO SDK: - -host/asiodrivers.cpp -host/mac/asioshlib.cpp -host/mac/codefragements.cpp - - - -Building the ASIO SDK on Windows --------------------------------- - -To build the ASIO SDK on Windows you need to compile and link with the -following files from the ASIO SDK: - -asio_sdk\common\asio.cpp -asio_sdk\host\asiodrivers.cpp -asio_sdk\host\pc\asiolist.cpp - -You may also need to adjust your include paths to support inclusion of -header files from the above directories. - -The ASIO SDK depends on the following COM API functions: -CoInitialize, CoUninitialize, CoCreateInstance, CLSIDFromString -For compilation with MinGW you will need to link with -lole32, for -Borland link with Import32.lib. - - - -Non-Microsoft (MSVC) Compilers on Windows including Borland and GCC -------------------------------------------------------------------- - -Steinberg did not specify a calling convention in the IASIO interface -definition. This causes the Microsoft compiler to use the proprietary -thiscall convention which is not compatible with other compilers, such -as compilers from Borland (BCC and C++Builder) and GNU (gcc). -Steinberg's ASIO SDK will compile but crash on initialization if -compiled with a non-Microsoft compiler on Windows. - -PortAudio solves this problem using the iasiothiscallresolver library -which is included in the distribution. When building ASIO support for -non-Microsoft compilers, be sure to compile and link with -iasiothiscallresolver.cpp. Note that iasiothiscallresolver includes -conditional directives which cause it to have no effect if it is -compiled with a Microsoft compiler, or on the Macintosh. - -If you use configure and make (see above), this should be handled -automatically for you. - -For further information about the IASIO thiscall problem see this page: -http://www.audiomulch.com/~rossb/code/calliasio - - - -Macintosh ASIO SDK Bug Patch ----------------------------- - -There is a bug in the ASIO SDK that causes the Macintosh version to -often fail during initialization. Below is a patch that you can apply. - -In codefragments.cpp replace getFrontProcessDirectory function with -the following one (GetFrontProcess replaced by GetCurrentProcess). - - -bool CodeFragments::getFrontProcessDirectory(void *specs) -{ - FSSpec *fss = (FSSpec *)specs; - ProcessInfoRec pif; - ProcessSerialNumber psn; - - memset(&psn,0,(long)sizeof(ProcessSerialNumber)); - // if(GetFrontProcess(&psn) == noErr) // wrong !!! - if(GetCurrentProcess(&psn) == noErr) // correct !!! - { - pif.processName = 0; - pif.processAppSpec = fss; - pif.processInfoLength = sizeof(ProcessInfoRec); - if(GetProcessInformation(&psn, &pif) == noErr) - return true; - } - return false; -} - - ---- diff --git a/pd/portaudio/pa_asio/Callback_adaptation_.pdf b/pd/portaudio/pa_asio/Callback_adaptation_.pdf deleted file mode 100644 index 76bf6786..00000000 Binary files a/pd/portaudio/pa_asio/Callback_adaptation_.pdf and /dev/null differ diff --git a/pd/portaudio/pa_asio/Pa_ASIO.pdf b/pd/portaudio/pa_asio/Pa_ASIO.pdf deleted file mode 100644 index ac5ecadb..00000000 Binary files a/pd/portaudio/pa_asio/Pa_ASIO.pdf and /dev/null differ diff --git a/pd/portaudio/pa_asio/iasiothiscallresolver.cpp b/pd/portaudio/pa_asio/iasiothiscallresolver.cpp deleted file mode 100644 index 8dfefbd6..00000000 --- a/pd/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 - - 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 -#include - -// 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/pd/portaudio/pa_asio/iasiothiscallresolver.h b/pd/portaudio/pa_asio/iasiothiscallresolver.h deleted file mode 100644 index 2ecfed79..00000000 --- a/pd/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 -// -// Note that this #include must come after the other ASIO SDK -// #includes, for example: -// -// #include -// #include -// #include -// #include -// #include -// -// Actually the important thing is to #include -// after . 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 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 -#include /* 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 -// after the asio #includes (this file _must_ be -// included _after_ the asio #includes) - -#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name)) - - -#endif /* !defined(_MSC_VER) */ - -#endif /* Win32 */ - -#endif /* included_iasiothiscallresolver_h */ - - diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp deleted file mode 100644 index 21987c71..00000000 --- a/pd/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 and : Phil Burk - 13-04-02 Removes another compiler warning : Stephane Letz - 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz - 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 -#include -#include -//#include - -#include -#include - -#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 -#include -#include -#else -*/ -/* -#include -#include -#include -*/ -/* -#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(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast(v));} -#elif WINDOWS - inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast(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 driver names from ASIO, returned in a char** - allocated in . -*/ -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; igetDriverNames( 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 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(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; iinputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0]; - stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1]; - } - } - else - { - stream->inputBufferPtrs[0] = 0; - stream->inputBufferPtrs[1] = 0; - } - - if( outputChannelCount > 0 ) - { - stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2]; - stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount]; - - for( i=0; ioutputBufferPtrs[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; iinputChannelCount; 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; iinputChannelCount; ++i ) - PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] ); - - PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); - for( i=0; ioutputChannelCount; ++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; ioutputChannelCount; 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(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/pd/portaudio/pa_asio/pa_asio.h b/pd/portaudio/pa_asio/pa_asio.h deleted file mode 100644 index 230fb2d8..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_allocation.c b/pd/portaudio/pa_common/pa_allocation.c deleted file mode 100644 index 035b4d0b..00000000 --- a/pd/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 . The remaining - links will have NULL buffer members, and each link will point to - the next link except the last, which will point to -*/ -static struct PaUtilAllocationGroupLink *AllocateLinks( long count, - struct PaUtilAllocationGroupLink *nextBlock, - struct PaUtilAllocationGroupLink *nextSpare ) -{ - struct PaUtilAllocationGroupLink *result; - int i; - - result = (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; ilinkCount = PA_INITIAL_LINK_COUNT_; - result->linkBlocks = &links[0]; - result->spareLinks = &links[1]; - result->allocations = 0; - } - else - { - PaUtil_FreeMemory( links ); - } - } - - return result; -} - - -void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ) -{ - struct PaUtilAllocationGroupLink *current = group->linkBlocks; - struct PaUtilAllocationGroupLink *next; - - while( current ) - { - next = current->next; - PaUtil_FreeMemory( current->buffer ); - current = next; - } - - PaUtil_FreeMemory( group ); -} - - -void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ) -{ - struct PaUtilAllocationGroupLink *links, *link; - void *result = 0; - - /* allocate more links if necessary */ - if( !group->spareLinks ) - { - /* double the link count on each block allocation */ - links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks ); - if( links ) - { - group->linkCount += group->linkCount; - group->linkBlocks = &links[0]; - group->spareLinks = &links[1]; - } - } - - if( group->spareLinks ) - { - result = PaUtil_AllocateMemory( size ); - if( result ) - { - link = group->spareLinks; - group->spareLinks = link->next; - - link->buffer = result; - link->next = group->allocations; - - group->allocations = link; - } - } - - return result; -} - - -void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ) -{ - struct PaUtilAllocationGroupLink *current = group->allocations; - struct PaUtilAllocationGroupLink *previous = 0; - - if( buffer == 0 ) - return; - - /* find the right link and remove it */ - while( current ) - { - if( current->buffer == buffer ) - { - 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/pd/portaudio/pa_common/pa_allocation.h b/pd/portaudio/pa_common/pa_allocation.h deleted file mode 100644 index fb9321a0..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_converters.c b/pd/portaudio/pa_common/pa_converters.c deleted file mode 100644 index a7e3a06c..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_converters.h b/pd/portaudio/pa_common/pa_converters.h deleted file mode 100644 index 831c9c64..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_cpuload.c b/pd/portaudio/pa_common/pa_cpuload.c deleted file mode 100644 index e70fbf4e..00000000 --- a/pd/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 - -#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/pd/portaudio/pa_common/pa_cpuload.h b/pd/portaudio/pa_common/pa_cpuload.h deleted file mode 100644 index f77d9199..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_dither.c b/pd/portaudio/pa_common/pa_dither.c deleted file mode 100644 index 0600db62..00000000 --- a/pd/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<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/pd/portaudio/pa_common/pa_dither.h b/pd/portaudio/pa_common/pa_dither.h deleted file mode 100644 index 70369e18..00000000 --- a/pd/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: -
-    signed long in = *
-    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
-    signed short out = (signed short)(((in>>1) + dither) >> 15);
-
- @return - A signed long with a range of +32767 to -32768 -*/ -signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - -/** - @brief Calculate 2 LSB dither signal with a triangular distribution. - Ranged for adding to a pre-scaled float. -
-    float in = *
-    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
-    // use smaller scaler to prevent overflow when we add the dither
-    signed short out = (signed short)(in*(32766.0f) + dither );
-
- @return - A float with a range of -2.0 to +1.99999. -*/ -float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_DITHER_H */ diff --git a/pd/portaudio/pa_common/pa_endianness.h b/pd/portaudio/pa_common/pa_endianness.h deleted file mode 100644 index cb6f8ad5..00000000 --- a/pd/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. 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/pd/portaudio/pa_common/pa_front.c b/pd/portaudio/pa_common/pa_front.c deleted file mode 100644 index 696df8b2..00000000 --- a/pd/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 TODO List - -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 -#include -#include -#include -#include /* 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 - belongs and returns it. if is - non-null, the host specific device index is returned in it. - returns -1 if is out of range. - -*/ -static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex ) -{ - int i=0; - - if( !PA_IS_INITIALISED_ ) - return -1; - - if( device < 0 ) - return -1; - - while( i < hostApisCount_ - && device >= hostApis_[i]->info.deviceCount ) - { - - device -= hostApis_[i]->info.deviceCount; - ++i; - } - - if( i >= hostApisCount_ ) - return -1; - - if( hostSpecificDeviceIndex ) - *hostSpecificDeviceIndex = device; - - return i; -} - - -static void AddOpenStream( PaStream* stream ) -{ - ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_; - firstOpenStream_ = (PaUtilStreamRepresentation*)stream; -} - - -static void RemoveOpenStream( PaStream* stream ) -{ - PaUtilStreamRepresentation *previous = NULL; - PaUtilStreamRepresentation *current = firstOpenStream_; - - while( current != NULL ) - { - if( ((PaStream*)current) == stream ) - { - if( previous == NULL ) - { - firstOpenStream_ = current->nextOpenStream; - } - else - { - previous->nextOpenStream = current->nextOpenStream; - } - return; - } - else - { - previous = current; - current = current->nextOpenStream; - } - } -} - - -static void CloseOpenStreams( void ) -{ - /* we call Pa_CloseStream() here to ensure that the same destruction - logic is used for automatically closed streams */ - - while( firstOpenStream_ != NULL ) - Pa_CloseStream( firstOpenStream_ ); -} - - -PaError Pa_Initialize( void ) -{ - PaError result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint( "Pa_Initialize called.\n" ); -#endif - - if( PA_IS_INITIALISED_ ) - { - ++initializationCount_; - result = paNoError; - } - else - { - PA_VALIDATE_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/pd/portaudio/pa_common/pa_hostapi.h b/pd/portaudio/pa_common/pa_hostapi.h deleted file mode 100644 index d0550706..00000000 --- a/pd/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 - 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/pd/portaudio/pa_common/pa_process.c b/pd/portaudio/pa_common/pa_process.c deleted file mode 100644 index 4a52165b..00000000 --- a/pd/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 -#include /* 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; iinputChannelCount; ++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; iinputChannelCount; ++i ) - { - bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount ); - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - } - } - else - { - for( i=0; iinputChannelCount; ++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; ioutputChannelCount; ++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; ioutputChannelCount; ++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; iinputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; - } - - userInput = bp->tempInputBufferPtrs; - } - - for( i=0; iinputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, 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; ioutputChannelCount; ++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; ioutputChannelCount; ++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; ioutputChannelCount; ++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; joutputChannelCount; ++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; iinputChannelCount; ++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; iinputChannelCount; ++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; iinputChannelCount; ++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; ioutputChannelCount; ++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; ioutputChannelCount; ++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; ioutputChannelCount; ++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/pd/portaudio/pa_common/pa_process.h b/pd/portaudio/pa_common/pa_process.h deleted file mode 100644 index c52e9ea0..00000000 --- a/pd/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. - -

Overview

- - 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. - - -

Initialization, resetting and termination

- - 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. - - -

Using the buffer processor for a callback stream

- - 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. - - -

Using the buffer processor for a blocking read/write stream

- - 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 copy 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 copy 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/pd/portaudio/pa_common/pa_skeleton.c b/pd/portaudio/pa_common/pa_skeleton.c deleted file mode 100644 index ebc1f501..00000000 --- a/pd/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 /* 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/pd/portaudio/pa_common/pa_stream.c b/pd/portaudio/pa_common/pa_stream.c deleted file mode 100644 index 044dde29..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_stream.h b/pd/portaudio/pa_common/pa_stream.h deleted file mode 100644 index 8b86943b..00000000 --- a/pd/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/pd/portaudio/pa_common/pa_trace.c b/pd/portaudio/pa_common/pa_trace.c deleted file mode 100644 index e36329fd..00000000 --- a/pd/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 -#include -#include -#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 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/pd/portaudio/pa_common/pa_util.h b/pd/portaudio/pa_common/pa_util.h deleted file mode 100644 index d20badd2..00000000 --- a/pd/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/pd/portaudio/pa_common/portaudio.h b/pd/portaudio/pa_common/portaudio.h deleted file mode 100644 index c5967b0b..00000000 --- a/pd/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. -
- set PA_RECOMMENDED_OUTPUT_DEVICE=1
-
- The user should first determine the available device ids by using - the supplied application "pa_devs". -*/ -PaDeviceIndex Pa_GetDefaultOutputDevice( void ); - - -/** The type used to represent monotonic time in seconds that can be used - for syncronisation. The type is used for the outTime argument to the - PaStreamCallback and as the result of Pa_GetStreamTime(). - - @see PaStreamCallback, Pa_GetStreamTime -*/ -typedef double PaTime; - - -/** 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/pd/portaudio/pa_dll_switch/PaDllEntry.h b/pd/portaudio/pa_dll_switch/PaDllEntry.h deleted file mode 100644 index e070054b..00000000 --- a/pd/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/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt b/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt deleted file mode 100644 index a535cd1d..00000000 Binary files a/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt and /dev/null differ diff --git a/pd/portaudio/pa_dll_switch/loadPA_DLL.cpp b/pd/portaudio/pa_dll_switch/loadPA_DLL.cpp deleted file mode 100644 index 043eda87..00000000 --- a/pd/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/pd/portaudio/pa_dll_switch/pa_lib.c b/pd/portaudio/pa_dll_switch/pa_lib.c deleted file mode 100644 index 86601592..00000000 --- a/pd/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 -#include -#include -#include - -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifdef _WIN32 -#ifndef __MWERKS__ -#include -#endif /* __MWERKS__ */ -#else /* !_WIN32 */ -#include -#endif /* _WIN32 */ - -#include "portaudio.h" -#include "pa_host.h" -#include "pa_trace.h" - -/* The reason we might NOT want to validate the rate before opening the stream - * is because many DirectSound drivers lie about the rates they actually support. - */ -#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */ - -/* -O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion -*/ - -#ifndef FALSE - #define FALSE (0) - #define TRUE (!FALSE) -#endif - -#define PRINT(x) { printf x; fflush(stdout); } -#define ERR_RPT(x) PRINT(x) -#define DBUG(x) /* PRINT(x) */ -#define DBUGX(x) /* PRINT(x) */ - -static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */ - -static PaError Pa_KillStream( PortAudioStream *stream, int abort ); - -/***********************************************************************/ -int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate ) -{ - double err, minErr = allowableError; - int i, bestFit = -1; - - for( i=0; inumSampleRates == -1 ) - { - /* Is it out of range? */ - if( (requestedFrameRate < pdi->sampleRates[0]) || - (requestedFrameRate > pdi->sampleRates[1]) ) - { - return paInvalidSampleRate; - } - - *closestFrameRatePtr = requestedFrameRate; - } - else - { - bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate ); - if( bestRateIndex < 0 ) return paInvalidSampleRate; - *closestFrameRatePtr = pdi->sampleRates[bestRateIndex]; - } - return paNoError; -} - -/*************************************************************************/ -DLL_API PaError Pa_OpenStream( - PortAudioStream** streamPtrPtr, - PaDeviceID inputDeviceID, - int numInputChannels, - PaSampleFormat inputSampleFormat, - void *inputDriverInfo, - PaDeviceID outputDeviceID, - int numOutputChannels, - PaSampleFormat outputSampleFormat, - void *outputDriverInfo, - double sampleRate, - unsigned long framesPerBuffer, - unsigned long numberOfBuffers, - unsigned long streamFlags, - PortAudioCallback *callback, - void *userData ) -{ - internalPortAudioStream *past = NULL; - PaError result = paNoError; - int bitsPerInputSample; - int bitsPerOutputSample; - /* Print passed parameters. */ - DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n", - streamPtrPtr, inputDeviceID, numInputChannels, - inputSampleFormat, inputDriverInfo )); - DBUG((" %d, %d, %d, %p, /* output */\n", - outputDeviceID, numOutputChannels, - outputSampleFormat, outputDriverInfo )); - DBUG((" %g, %d, %d, 0x%x, , %p )\n", - sampleRate, framesPerBuffer, numberOfBuffers, - streamFlags, userData )); - - /* Check for parameter errors. */ - if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag; - if( streamPtrPtr == NULL ) return paBadStreamPtr; - if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */ - if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */ - if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId; - if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId; - if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount; - -#if SUPPORT_AUDIO_CAPTURE - if( inputDeviceID >= 0 ) - { - PaError size = Pa_GetSampleSize( inputSampleFormat ); - if( size < 0 ) return size; - bitsPerInputSample = 8 * size; - if( (numInputChannels <= 0) ) return paInvalidChannelCount; - } -#else - if( inputDeviceID >= 0 ) - { - return paInvalidChannelCount; - } -#endif /* SUPPORT_AUDIO_CAPTURE */ - else - { - if( numInputChannels > 0 ) return paInvalidChannelCount; - bitsPerInputSample = 0; - } - - if( outputDeviceID >= 0 ) - { - PaError size = Pa_GetSampleSize( outputSampleFormat ); - if( size < 0 ) return size; - bitsPerOutputSample = 8 * size; - if( (numOutputChannels <= 0) ) return paInvalidChannelCount; - } - else - { - if( numOutputChannels > 0 ) return paInvalidChannelCount; - bitsPerOutputSample = 0; - } - - if( callback == NULL ) return paNullCallback; - - /* Allocate and clear stream structure. */ - past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) ); - if( past == NULL ) return paInsufficientMemory; - memset( past, 0, sizeof(internalPortAudioStream) ); - AddTraceMessage("Pa_OpenStream: past", (long) past ); - - past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */ - past->past_FramesPerUserBuffer = framesPerBuffer; - past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */ - past->past_Callback = callback; - past->past_UserData = userData; - past->past_OutputSampleFormat = outputSampleFormat; - past->past_InputSampleFormat = inputSampleFormat; - past->past_OutputDeviceID = outputDeviceID; - past->past_InputDeviceID = inputDeviceID; - past->past_NumInputChannels = numInputChannels; - past->past_NumOutputChannels = numOutputChannels; - past->past_Flags = streamFlags; - - /* Check for absurd sample rates. */ - if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) - { - result = paInvalidSampleRate; - goto cleanup; - } - - /* Allocate buffers that may be used for format conversion from user to native buffers. */ - if( numInputChannels > 0 ) - { - -#if PA_VALIDATE_RATE - result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate ); - if( result < 0 ) - { - goto cleanup; - } -#else - past->past_SampleRate = sampleRate; -#endif - /* Allocate single Input buffer. */ - past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8); - past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize); - if( past->past_InputBuffer == NULL ) - { - result = paInsufficientMemory; - goto cleanup; - } - } - else - { - past->past_InputBuffer = NULL; - } - - /* Allocate single Output buffer. */ - if( numOutputChannels > 0 ) - { -#if PA_VALIDATE_RATE - result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate ); - if( result < 0 ) - { - goto cleanup; - } -#else - past->past_SampleRate = sampleRate; -#endif - past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8); - past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize); - if( past->past_OutputBuffer == NULL ) - { - result = paInsufficientMemory; - goto cleanup; - } - } - else - { - past->past_OutputBuffer = NULL; - } - - result = PaHost_OpenStream( past ); - if( result < 0 ) goto cleanup; - - *streamPtrPtr = (void *) past; - - return result; - -cleanup: - if( past != NULL ) Pa_CloseStream( past ); - *streamPtrPtr = NULL; - return result; -} - - -/*************************************************************************/ -DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream, - int numInputChannels, - int numOutputChannels, - PaSampleFormat sampleFormat, - double sampleRate, - unsigned long framesPerBuffer, - unsigned long numberOfBuffers, - PortAudioCallback *callback, - void *userData ) -{ - return Pa_OpenStream( - stream, - ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice), - numInputChannels, sampleFormat, NULL, - ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), - numOutputChannels, sampleFormat, NULL, - sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData ); -} - -/*************************************************************************/ -DLL_API PaError Pa_CloseStream( PortAudioStream* stream) -{ - PaError result; - internalPortAudioStream *past; - - DBUG(("Pa_CloseStream()\n")); - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - Pa_AbortStream( past ); - result = PaHost_CloseStream( past ); - - if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize ); - if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize ); - PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) ); - - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StartStream( PortAudioStream *stream ) -{ - PaError result = paHostError; - internalPortAudioStream *past; - - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - past->past_FrameCount = 0.0; - - if( past->past_NumInputChannels > 0 ) - { - result = PaHost_StartInput( past ); - DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - if( past->past_NumOutputChannels > 0 ) - { - result = PaHost_StartOutput( past ); - DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - result = PaHost_StartEngine( past ); - DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - - return paNoError; - -error: - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StopStream( PortAudioStream *stream ) -{ - return Pa_KillStream( stream, 0 ); -} - -/*************************************************************************/ -DLL_API PaError Pa_AbortStream( PortAudioStream *stream ) -{ - return Pa_KillStream( stream, 1 ); -} - -/*************************************************************************/ -static PaError Pa_KillStream( PortAudioStream *stream, int abort ) -{ - PaError result = paNoError; - internalPortAudioStream *past; - - DBUG(("Pa_StopStream().\n")); - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - - if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) ) - { - result = PaHost_StopEngine( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result)); - if( result < 0 ) goto error; - } - - if( past->past_NumInputChannels > 0 ) - { - result = PaHost_StopInput( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result)); - if( result != paNoError ) goto error; - } - - if( past->past_NumOutputChannels > 0 ) - { - result = PaHost_StopOutput( past, abort ); - DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result)); - if( result != paNoError ) goto error; - } - -error: - past->past_Usage = 0; - past->past_IfLastExitValid = 0; - - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_StreamActive( PortAudioStream *stream ) -{ - internalPortAudioStream *past; - if( stream == NULL ) return paBadStreamPtr; - past = (internalPortAudioStream *) stream; - return PaHost_StreamActive( past ); -} - -/*************************************************************************/ -DLL_API const char *Pa_GetErrorText( PaError errnum ) -{ - const char *msg; - - switch(errnum) - { - case paNoError: msg = "Success"; break; - case paHostError: msg = "Host error."; break; - case paInvalidChannelCount: msg = "Invalid number of channels."; break; - case paInvalidSampleRate: msg = "Invalid sample rate."; break; - case paInvalidDeviceId: msg = "Invalid device ID."; break; - case paInvalidFlag: msg = "Invalid flag."; break; - case paSampleFormatNotSupported: msg = "Sample format not supported"; break; - case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break; - case paInsufficientMemory: msg = "Insufficient memory."; break; - case paBufferTooBig: msg = "Buffer too big."; break; - case paBufferTooSmall: msg = "Buffer too small."; break; - case paNullCallback: msg = "No callback routine specified."; break; - case paBadStreamPtr: msg = "Invalid stream pointer."; break; - case paTimedOut : msg = "Wait Timed Out."; break; - case paInternalError: msg = "Internal PortAudio Error."; break; - default: msg = "Illegal error number."; break; - } - return msg; -} - -/* - Get CPU Load as a fraction of total CPU time. - A value of 0.5 would imply that PortAudio and the sound generating - callback was consuming roughly 50% of the available CPU time. - The amount may vary depending on CPU load. - This function may be called from the callback function. -*/ -DLL_API double Pa_GetCPULoad( PortAudioStream* stream) -{ - internalPortAudioStream *past; - if( stream == NULL ) return (double) paBadStreamPtr; - past = (internalPortAudioStream *) stream; - return past->past_Usage; -} - -/************************************************************* -** Calculate 2 LSB dither signal with a triangular distribution. -** Ranged properly for adding to a 32 bit integer prior to >>15. -*/ -#define DITHER_BITS (15) -#define DITHER_SCALE (1.0f / ((1<>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); - /* High pass filter to reduce audibility. */ - highPass = current - previous; - previous = current; - return highPass; -} - -/************************************************************************* -** Called by host code. -** Convert input from Int16, call user code, then convert output -** to Int16 format for native use. -** Assumes host native format is paInt16. -** Returns result from user callback. -*/ -long Pa_CallConvertInt16( internalPortAudioStream *past, - short *nativeInputBuffer, - short *nativeOutputBuffer ) -{ - long temp; - long bytesEmpty = 0; - long bytesFilled = 0; - int userResult; - unsigned int i; - void *inputBuffer = NULL; - void *outputBuffer = NULL; - -#if SUPPORT_AUDIO_CAPTURE - /* Get native data from DirectSound. */ - if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) - { - /* Convert from native format to PA format. */ - unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels; - switch(past->past_InputSampleFormat) - { - - case paFloat32: - { - float *inBufPtr = (float *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - for( i=0; ipast_InputBuffer; - inputBuffer = past->past_InputBuffer; - for( i=0; ipast_InputBuffer; - inputBuffer = past->past_InputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i> 8); - } - } - else - { - for( i=0; i> 7; - temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - inBufPtr[i] = (char)(temp >> 8); - } - } - break; - } - - case paUInt8: - { - /* Convert 16 bit data to 8 bit unsigned chars */ - unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer; - inputBuffer = past->past_InputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i> 8)) + 0x80; - } - } - else - { - /* If you dither then you have to clip because dithering could push the signal out of range! */ - for( i=0; i> 7; - temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - inBufPtr[i] = (unsigned char)(temp + 0x80); - } - } - break; - } - - default: - break; - } - } -#endif /* SUPPORT_AUDIO_CAPTURE */ - - /* Are we doing output time? */ - if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) - { - /* May already be in native format so just write directly to native buffer. */ - outputBuffer = (past->past_OutputSampleFormat == paInt16) ? - nativeOutputBuffer : past->past_OutputBuffer; - } - /* - AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); - AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); - */ - /* Call user callback routine. */ - userResult = past->past_Callback( - inputBuffer, - outputBuffer, - past->past_FramesPerUserBuffer, - past->past_FrameCount, - past->past_UserData ); - - past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; - - /* Convert to native format if necessary. */ - if( outputBuffer != NULL ) - { - unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels; - switch(past->past_OutputSampleFormat) - { - case paFloat32: - { - float *outBufPtr = (float *) past->past_OutputBuffer; - if( past->past_Flags & paDitherOff ) - { - if( past->past_Flags & paClipOff ) /* NOTHING */ - { - for( i=0; i 0x7FFF) ? 0x7FFF : temp)); - } - } - } - else - { - /* If you dither then you have to clip because dithering could push the signal out of range! */ - for( i=0; i 0x7FFF) ? 0x7FFF : temp)); - } - } - break; - } - - case paInt32: - { - int *outBufPtr = (int *) past->past_OutputBuffer; - if( past->past_Flags & paDitherOff ) - { - for( i=0; i> 16 ); - } - } - else - { - for( i=0; i> 1) + Pa_TriangularDither(); - temp = temp >> 15; - *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); - } - } - break; - } - - case paInt8: - { - char *outBufPtr = (char *) past->past_OutputBuffer; - for( i=0; ipast_OutputBuffer; - for( i=0; ipast_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) - { - inputBuffer = nativeInputBuffer; // FIXME - } - - /* Are we doing output time? */ - if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) - { - /* May already be in native format so just write directly to native buffer. */ - outputBuffer = (past->past_OutputSampleFormat == paFloat32) ? - nativeOutputBuffer : past->past_OutputBuffer; - } - /* - AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); - AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); - */ - /* Call user callback routine. */ - userResult = past->past_Callback( - inputBuffer, - outputBuffer, - past->past_FramesPerUserBuffer, - past->past_FrameCount, - past->past_UserData ); - - past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; - - /* Convert to native format if necessary. */ // FIXME - return userResult; -} - -/*************************************************************************/ -DLL_API PaError Pa_Initialize( void ) -{ - if( gInitCount++ > 0 ) return paNoError; - ResetTraceMessages(); - return PaHost_Init(); -} - -DLL_API PaError Pa_Terminate( void ) -{ - PaError result = paNoError; - - if( gInitCount == 0 ) return paNoError; - else if( --gInitCount == 0 ) - { - result = PaHost_Term(); - DumpTraceMessages(); - } - return result; -} - -/*************************************************************************/ -DLL_API PaError Pa_GetSampleSize( PaSampleFormat format ) -{ - int size; - switch(format ) - { - - case paUInt8: - case paInt8: - size = 1; - break; - - case paInt16: - size = 2; - break; - - case paPackedInt24: - size = 3; - break; - - case paFloat32: - case paInt32: - case paInt24: - size = 4; - break; - - default: - size = paSampleFormatNotSupported; - break; - } - return (PaError) size; -} - - diff --git a/pd/portaudio/pa_dll_switch/portaudio.h b/pd/portaudio/pa_dll_switch/portaudio.h deleted file mode 100644 index 9632521e..00000000 --- a/pd/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/pd/portaudio/pa_jack/pa_jack.c b/pd/portaudio/pa_jack/pa_jack.c deleted file mode 100644 index 2199365f..00000000 --- a/pd/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 - * Copyright (c) 2004 Arve Knudsen - * Copyright (c) 2002 Joshua Haberman - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include /* EBUSY */ -#include /* sig_atomic_t */ -#include -#include - -#include -#include - -#include "pa_util.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_process.h" -#include "pa_allocation.h" -#include "pa_cpuload.h" -#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; - /* */ - stream->samplesPerFrame = 2; - stream->bytesPerFrame = sizeof(float) * stream->samplesPerFrame; - /* */ - 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/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c deleted file mode 100644 index 2f88fc2f..00000000 --- a/pd/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 - * Copyright (c) 2005-2006 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. - */ - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include -#undef ALSA_PCM_NEW_HW_PARAMS_API -#undef ALSA_PCM_NEW_SW_PARAMS_API - -#include -#include /* strlen() */ -#include -#include -#include -#include -#include -#include -#include /* 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/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h deleted file mode 100644 index e6f44b16..00000000 --- a/pd/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/pd/portaudio/pa_mac/pa_mac_hostapis.c b/pd/portaudio/pa_mac/pa_mac_hostapis.c deleted file mode 100644 index 2e71577e..00000000 --- a/pd/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/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt deleted file mode 100644 index ad66f358..00000000 --- a/pd/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/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c deleted file mode 100644 index 16eb0824..00000000 --- a/pd/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 /* strlen(), memcmp() etc. */ - -#include -#include - - -#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; idevCount; ++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; jdevCount; ++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> 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; imNumberBuffers; ++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/pd/portaudio/pa_mac_core/pa_mac_core.h b/pd/portaudio/pa_mac_core/pa_mac_core.h deleted file mode 100644 index 5994294a..00000000 --- a/pd/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/pd/portaudio/pa_mac_core/pa_mac_core_utilities.c b/pd/portaudio/pa_mac_core/pa_mac_core_utilities.c deleted file mode 100644 index f2cbd29c..00000000 --- a/pd/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 -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 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 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/pd/portaudio/pa_unix/pa_unix_hostapis.c b/pd/portaudio/pa_unix/pa_unix_hostapis.c deleted file mode 100644 index 9bddc2e0..00000000 --- a/pd/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/pd/portaudio/pa_unix/pa_unix_util.c b/pd/portaudio/pa_unix/pa_unix_util.c deleted file mode 100644 index 88ca6092..00000000 --- a/pd/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 -#include -#include -#include -#include -#include -#include /* 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/pd/portaudio/pa_unix/pa_unix_util.h b/pd/portaudio/pa_unix/pa_unix_util.h deleted file mode 100644 index 01dda01e..00000000 --- a/pd/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/pd/portaudio/pa_unix_oss/pa_unix_oss.c b/pd/portaudio/pa_unix_oss/pa_unix_oss.c deleted file mode 100644 index 125fb8ce..00000000 --- a/pd/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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ -# include -# define DEVICE_NAME_BASE "/dev/dsp" -#elif defined __linux__ -# include -# define DEVICE_NAME_BASE "/dev/dsp" -#else -# include /* JH20010905 */ -# define DEVICE_NAME_BASE "/dev/audio" -#endif - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" -#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<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/pd/portaudio/pa_win/pa_win_hostapis.c b/pd/portaudio/pa_win/pa_win_hostapis.c deleted file mode 100644 index 3db1e18a..00000000 --- a/pd/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/pd/portaudio/pa_win/pa_win_util.c b/pd/portaudio/pa_win/pa_win_util.c deleted file mode 100644 index 0395e5c8..00000000 --- a/pd/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 -#include /* for timeGetTime() */ - -#include "pa_util.h" - - -/* - Track memory allocations to avoid leaks. - */ - -#if PA_TRACK_MEMORY -static int numAllocations_ = 0; -#endif - - -void *PaUtil_AllocateMemory( long size ) -{ - void *result = GlobalAlloc( GPTR, size ); - -#if PA_TRACK_MEMORY - if( result != NULL ) numAllocations_ += 1; -#endif - return result; -} - - -void PaUtil_FreeMemory( void *block ) -{ - if( block != NULL ) - { - GlobalFree( block ); -#if PA_TRACK_MEMORY - numAllocations_ -= 1; -#endif - - } -} - - -int PaUtil_CountCurrentlyAllocatedBlocks( void ) -{ -#if PA_TRACK_MEMORY - return numAllocations_; -#else - return 0; -#endif -} - - -void Pa_Sleep( long msec ) -{ - Sleep( msec ); -} - -static int usePerformanceCounter_; -static double secondsPerTick_; - -void PaUtil_InitializeClock( void ) -{ - LARGE_INTEGER ticksPerSecond; - - if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 ) - { - usePerformanceCounter_ = 1; - secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart; - } - else - { - usePerformanceCounter_ = 0; - } -} - - -double PaUtil_GetTime( void ) -{ - LARGE_INTEGER time; - - if( usePerformanceCounter_ ) - { - /* FIXME: - according to this knowledge-base article, QueryPerformanceCounter - can skip forward by seconds! - http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323& - - it may be better to use the rtdsc instruction using inline asm, - however then a method is needed to calculate a ticks/seconds ratio. - */ - QueryPerformanceCounter( &time ); - return time.QuadPart * secondsPerTick_; - } - else - { - return timeGetTime() * .001; - } -} diff --git a/pd/portaudio/pa_win/pa_x86_plain_converters.c b/pd/portaudio/pa_win/pa_x86_plain_converters.c deleted file mode 100644 index 98442a8c..00000000 --- a/pd/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< source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int32Scaler_ // stack: (int)0x7FFFFFFF - - Float32_To_Int32_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF - /* - note: we could store to a temporary qword here which would cause - wraparound distortion instead of int indefinite 0x10. that would - be more work, and given that not enabling clipping is only advisable - when you know that your signal isn't going to clip it isn't worth it. - */ - fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // REVIEW - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - *dest = (signed long) scaled; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int32Scaler_ // stack: (int)0x7FFFFFFF - - Float32_To_Int32_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int32_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF - fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF - jmp Float32_To_Int32_Clip_stored - - Float32_To_Int32_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFFFF // convert to maximum range integers - mov dword ptr [edi], edx - - Float32_To_Int32_Clip_stored: - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - /* - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - - while( count-- ) - { - // REVIEW - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - *dest = (signed long) dithered; - - - src += sourceStride; - dest += destinationStride; - } - */ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt32Scaler_ // stack: int scaler - - Float32_To_Int32_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int32_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler - fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler - jmp Float32_To_Int32_DitherClip_stored - - Float32_To_Int32_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFFFF // convert to maximum range integers - mov dword ptr [edi], edx - - Float32_To_Int32_DitherClip_stored: - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - double scaled = *src * 0x7FFFFFFF; - temp = (signed long) scaled; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - signed long tempInt32; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int24Scaler_ // stack: (int)0x7FFFFF - - Float32_To_Int24_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF - fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF - mov edx, tempInt32 - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - temp = (signed long) scaled; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - signed long tempInt32; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int24Scaler_ // stack: (int)0x7FFFFF - - Float32_To_Int24_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int24_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF - fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF - mov edx, tempInt32 - jmp Float32_To_Int24_Clip_store - - Float32_To_Int24_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFF // convert to maximum range integers - - Float32_To_Int24_Clip_store: - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - - // FIXME: the dither amplitude here appears to be too small by 8 bits - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - - temp = (signed long) dithered; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - signed long tempInt32; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt24Scaler_ // stack: int scaler - - Float32_To_Int24_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int24_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler - fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler - mov edx, tempInt32 - jmp Float32_To_Int24_DitherClip_store - - Float32_To_Int24_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFF // convert to maximum range integers - - Float32_To_Int24_DitherClip_store: - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - - short samp = (short) (*src * (32767.0f)); - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int16Scaler_ // stack: (int)0x7FFF - - Float32_To_Int16_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF - fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - long samp = (signed long) (*src * (32767.0f)); - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (signed short) samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int16Scaler_ // stack: (int)0x7FFF - - Float32_To_Int16_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int16_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF - fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF - jmp Float32_To_Int16_Clip_stored - - Float32_To_Int16_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add dx, 0x7FFF // convert to maximum range integers - mov word ptr [edi], dx // store clamped into into dest - - Float32_To_Int16_Clip_stored: - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - float dithered = (*src * (32766.0f)) + dither; - signed long samp = (signed long) dithered; - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (signed short) samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt16Scaler_ // stack: int scaler - - Float32_To_Int16_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int16_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler - fistp word ptr [edi] // store scaled int into dest, stack: int scaler - jmp Float32_To_Int16_DitherClip_stored - - Float32_To_Int16_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add dx, 0x7FFF // convert to maximum range integers - mov word ptr [edi], dx // store clamped into into dest - - Float32_To_Int16_DitherClip_stored: - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -void PaUtil_InitializeX86PlainConverters( void ) -{ - paConverters.Float32_To_Int32 = Float32_To_Int32; - paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip; - paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip; - - paConverters.Float32_To_Int24 = Float32_To_Int24; - paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip; - paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip; - - paConverters.Float32_To_Int16 = Float32_To_Int16; - paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip; - paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip; -} - -/* -------------------------------------------------------------------------- */ diff --git a/pd/portaudio/pa_win/pa_x86_plain_converters.h b/pd/portaudio/pa_win/pa_x86_plain_converters.h deleted file mode 100644 index f56c710f..00000000 --- a/pd/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/pd/portaudio/pa_win_ds/dsound_wrapper.c b/pd/portaudio/pa_win_ds/dsound_wrapper.c deleted file mode 100644 index 207d2873..00000000 --- a/pd/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 -#include -#include - -#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 // 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/pd/portaudio/pa_win_ds/dsound_wrapper.h b/pd/portaudio/pa_win_ds/dsound_wrapper.h deleted file mode 100644 index 9e3f565f..00000000 --- a/pd/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 - -#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/pd/portaudio/pa_win_ds/pa_win_ds.c b/pd/portaudio/pa_win_ds/pa_win_ds.c deleted file mode 100644 index ef970906..00000000 --- a/pd/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 -#include /* strlen() */ - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "dsound_wrapper.h" - -#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 - To: Ross Bencina - 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/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c b/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c deleted file mode 100644 index 934d44b6..00000000 --- a/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c +++ /dev/null @@ -1,3269 +0,0 @@ -/* - * $Id: pa_win_wdmks.c,v 1.23 2007-08-06 16:39:54 millerpuckette 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 - -/* Debugging/tracing support */ - -#define PA_LOGE_ -#define PA_LOGL_ - -#ifdef __GNUC__ - #include - #define _WIN32_WINNT 0x0501 - #define WINVER 0x0501 -#endif - -#include /* strlen() */ -#include - -#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 -#include - - -#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 -#include -#include -#include -#include - -/* 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 - -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 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; countdataRangesItem->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; pinIdpinCount; 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; pinIdpinCount; 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; ifilterCount; 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;iuserOutputChannels;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;iuserInputChannels;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/pd/portaudio/pa_win_wdmks/readme.txt b/pd/portaudio/pa_win_wdmks/readme.txt deleted file mode 100644 index 1a381fe7..00000000 --- a/pd/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/pd/portaudio/pa_win_wmme/pa_win_wmme.c b/pd/portaudio/pa_win_wmme/pa_win_wmme.c deleted file mode 100644 index 1a9ea59e..00000000 --- a/pd/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 -#include -#include -#include -#include -#include -#include -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifndef __MWERKS__ -#include -#include -#endif /* __MWERKS__ */ - -#include "portaudio.h" -#include "pa_trace.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "pa_win_wmme.h" - -#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; iinput.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; ioutput.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; iinput.bufferCount; ++i ) - { - for( j=0; jinput.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; ioutput.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; ioutput.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; joutput.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; joutput.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; joutput.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; ioutput.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; ibufferProcessor.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; iinput.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; ibufferProcessor.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; ioutput.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/pd/portaudio/pa_win_wmme/pa_win_wmme.h b/pd/portaudio/pa_win_wmme/pa_win_wmme.h deleted file mode 100644 index 1a71633a..00000000 --- a/pd/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/pd/portaudio/pablio/ringbuffer.c b/pd/portaudio/pablio/ringbuffer.c deleted file mode 100644 index 3901f598..00000000 --- a/pd/portaudio/pablio/ringbuffer.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * $Id: ringbuffer.c,v 1.22 2007-08-06 16:39:54 millerpuckette 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 -#include -#include -#include "ringbuffer.h" -#include - -/*************************************************************************** - * 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/pd/portaudio/pablio/ringbuffer.h b/pd/portaudio/pablio/ringbuffer.h deleted file mode 100644 index 50981a9a..00000000 --- a/pd/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.23 2007-08-06 16:39:54 millerpuckette 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 -#include -#include -#include "ringbuffer.h" -#include - -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/pd/portmidi/CHANGELOG.txt b/pd/portmidi/CHANGELOG.txt new file mode 100644 index 00000000..fd118e81 --- /dev/null +++ b/pd/portmidi/CHANGELOG.txt @@ -0,0 +1,158 @@ +/* CHANGELOG FOR PORTMIDI + * + * 17Jan07 Roger Dannenberg + * - Lots more help for Common Lisp user in pm_cl + * - Minor fix to eliminate a compiler warning + * - Went back to single library in OS X for both portmidi and porttime + * + * 16Jan07 Roger Dannenberg + * - OOPS! fixed bug where short messages all had zero data + * - Makefile.osx static library build now makes universal (i386 + ppc) + * binaries + * + * 15Jan07 Roger Dannenberg + * - multiple rewrites of sysex handling code to take care of + * error-handling, embedded messages, message filtering, + * driver bugs, and host limitations. + * - fixed windows to use dwBufferLength rather than + * dwBytesRecorded for long buffer output (fix by Nigel Brown) + * - Win32 MME code always appends an extra zero to long buffer + * output to work around a problem with earlier versions of Midi Yoke + * - Added mm, a command line Midi Monitor to pm_test suite + * - Revised copyright notice to match PortAudio/MIT license (requests + * are moved out of the license proper and into a separate paragraph) + * + * 18Oct06 Roger Dannenberg + * - replace FIFO in pmutil with Light Pipe-based multiprocessor-safe alg. + * - replace FIFO in portmidi.c with PmQueue from pmutil + * + * 07Oct06 cpr & Roger Dannenberg + * - overhaul of CoreMIDI input to handle running status and multiple + * - messages per packet, with additional error detection + * - added Leigh Smith and Rick Taube support for Common Lisp and + * - dynamic link libraries in OSX + * - initialize static global seq = NULL in pmlinuxalsa.c + * + * 05Sep06 Sebastien Frippiat + * - check if (ALSA) seq exists before closing it in pm_linuxalsa_term() + * + * 05Sep06 Andreas Micheler and Cecilio + * - fixed memory leak by freeing someo objects in pm_winmm_term() + * - and another leak by freeing descriptors in Pm_Terminate() + * + * 23Aug06 RBD + * - various minor fixes + * + * 04Nov05 Olivier Tristan + * - changes to OS X to properly retrieve real device name on CoreMidi + * + * 19Jul05 Roger Dannenberg + * - included pmBufferMaxSize in Pm_GetErrorText() + * + * 23Mar05 Torgier Strand Henriksen + * - cleaner termination of porttime thread under Linux + * + * 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(). + * + */ diff --git a/pd/portmidi/Makefile b/pd/portmidi/Makefile deleted file mode 100644 index 7a87606d..00000000 --- a/pd/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/pd/portmidi/README.txt b/pd/portmidi/README.txt index 76412efd..0ab950d0 100644 --- a/pd/portmidi/README.txt +++ b/pd/portmidi/README.txt @@ -1,12 +1,17 @@ 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. +VERSION: this is the 17-Jan-07 version of PortMidi. -For Linux, please see also README_LINUX.txt in pm_linux. +Documentation for PortMidi is found in pm_common/portmidi.h. + +Additional documentation: + - Windows: see pm_win/README_WIN.txt and pm_win/debugging_dlls.txt + - Linux: see pm_linux/README_LINUX.txt + - Mac OSX: see pm_mac/README_MAC.txt + - Common Lisp: see pm_cl/README_CL.txt + +---------- some notes on the design of PortMidi ---------- POINTERS VS DEVICE NUMBERS @@ -22,26 +27,38 @@ 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. +In addition, errors may occur asynchronously due to MIDI input. +However, for Windows, there are virtually no errors that can +occur if the code is correct and not passing bogus values. One +exception is an error that the system is out of memory, but my +guess is that one is unlikely to recover gracefully from that. +Therefore, all errors in callbacks are guarded by assert(), which +means not guarded at all in release configurations. 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, +Host error codes are system-specific and 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. +are not open). For open and close, we will convert the error +to text, copy it to a global string, and set pm_hosterror, a +global flag. + +Similarly, whenever a Read or Write operation returns pmHostError, +the corresponding error string is copied to a global string +and pm_hosterror is set. This makes getting error strings +simple and uniform, although it does cost a string copy and some +overhead even if the user does not want to look at the error data. -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. +The system-specific Read, Write, Poll, etc. implementations should +check for asynchronous errors and return immediately if one is +found so that these get reported. This happens in the Mac OS X +code, where lots of things are happening in callbacks, but again, +in Windows, there are no error codes recorded in callbacks. DEBUGGING diff --git a/pd/portmidi/license.txt b/pd/portmidi/license.txt new file mode 100644 index 00000000..b04523be --- /dev/null +++ b/pd/portmidi/license.txt @@ -0,0 +1,40 @@ +/* + * PortMidi Portable Real-Time MIDI Library + * + * license.txt -- a copy of the PortMidi copyright notice and license information + * + * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * Copyright (c) 2001-2006 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. + * + * 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. + */ + +/* + * The text above constitutes the entire PortMidi license; however, + * the PortMusic community also makes the following non-binding requests: + * + * 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. It is also + * requested that these non-binding requests be included along with the + * license above. + */ diff --git a/pd/portmidi/portmidi.dsp b/pd/portmidi/portmidi.dsp deleted file mode 100644 index 699cc120..00000000 --- a/pd/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/pd/portmidi/portmidi.dsw b/pd/portmidi/portmidi.dsw deleted file mode 100644 index 1ccfb5bf..00000000 --- a/pd/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/pd/src/CHANGELOG.txt b/pd/src/CHANGELOG.txt index ccd8641d..6c0dc1e3 100644 --- a/pd/src/CHANGELOG.txt +++ b/pd/src/CHANGELOG.txt @@ -2,6 +2,10 @@ This file describes implementation and API changes; stuff more visible to the user appears in the "release notes" instead. See the bottom of this file for original notes on source stype and organization. +0.41.0 + +add support for callback-based audio I/O; changes in + 0.40.0 0.39.0 diff --git a/pd/src/configure.in b/pd/src/configure.in index 6ea60a50..e328f6aa 100644 --- a/pd/src/configure.in +++ b/pd/src/configure.in @@ -40,7 +40,7 @@ AC_ARG_ENABLE(static, [ --enable-static link statically], static=$enableval) AC_ARG_ENABLE(setuid, [ --enable-setuid install as setuid (linux)], setuid=$enableval) -AC_ARG_ENABLE(fftw, [ --enable-fftw use FFTW package], +AC_ARG_ENABLE(fftw, [ --enable-fftw use FFTW package], fftw=$enableval) dnl Checks for programs. @@ -185,9 +185,6 @@ dnl This should be fixed so Pd can use ALSA shared libraries where appropriate. EXT=pd_linux CPPFLAGS="-DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD\ -DUSEAPI_OSS \ - -I../portaudio/pa_common -I../portaudio/pablio \ - -I../portmidi/pm_common \ - -I../portmidi/pm_linux \ -fno-strict-aliasing" SYSSRC="s_midi_oss.c s_audio_oss.c" if test x$alsa == "xyes"; @@ -197,32 +194,41 @@ dnl This should be fixed so Pd can use ALSA shared libraries where appropriate. LDFLAGS=$LDFLAGS" -lasound" fi - if test x$portaudio == "xyes"; - - then - CPPFLAGS=$CPPFLAGS" -DUSEAPI_PORTAUDIO -DPA19" + CPPFLAGS=$CPPFLAGS" -DUSEAPI_PORTAUDIO -DHAVE_SYS_SOUNDCARD_H \ + -Wno-error \ + -I../portaudio/include -I../portaudio/src/common \ + -I../portaudio/src/os/unix/ \ + -I../portmidi/pm_common \ + -I../portmidi/pm_linux" SYSSRC="s_audio_pa.c \ s_audio_pablio.c \ s_audio_paring.c \ - ../portaudio/pa_common/pa_allocation.c \ - ../portaudio/pa_common/pa_converters.c \ - ../portaudio/pa_common/pa_cpuload.c \ - ../portaudio/pa_common/pa_dither.c \ - ../portaudio/pa_common/pa_front.c \ - ../portaudio/pa_common/pa_process.c \ - ../portaudio/pa_common/pa_skeleton.c \ - ../portaudio/pa_common/pa_stream.c \ - ../portaudio/pa_common/pa_trace.c \ - ../portaudio/pa_unix/pa_unix_hostapis.c \ - ../portaudio/pa_unix/pa_unix_util.c \ - ../portaudio/pa_unix_oss/pa_unix_oss.c "$SYSSRC + ../portaudio/src/common/pa_allocation.c \ + ../portaudio/src/common/pa_converters.c \ + ../portaudio/src/common/pa_cpuload.c \ + ../portaudio/src/common/pa_dither.c \ + ../portaudio/src/common/pa_front.c \ + ../portaudio/src/common/pa_process.c \ + ../portaudio/src/common/pa_skeleton.c \ + ../portaudio/src/common/pa_stream.c \ + ../portaudio/src/common/pa_trace.c \ + ../portaudio/src/common/pa_debugprint.c \ + ../portaudio/src/common/pa_ringbuffer.c \ + ../portaudio/src/os/unix/pa_unix_hostapis.c \ + ../portaudio/src/os/unix/pa_unix_util.c \ + ../portaudio/src/hostapi/oss/pa_unix_oss.c "$SYSSRC if test x$alsa == "xyes"; then - SYSSRC=$SYSSRC" ../portaudio/pa_linux_alsa/pa_linux_alsa.c" - CPPFLAGS=$CPPFLAGS" -Wno-error" - fi + SYSSRC="../portaudio/src/hostapi/alsa/pa_linux_alsa.c "$SYSSRC + CPPFLAGS=$CPPFLAGS" -DPA_USE_ALSA" + fi + if test x$jack == "xyes"; + then + SYSSRC="../portaudio/src/hostapi/jack/pa_jack.c "$SYSSRC + CPPFLAGS=$CPPFLAGS" -DPA_USE_JACK" + fi fi if test x$setuid == "xyes"; then @@ -257,10 +263,11 @@ then -framework Carbon -framework CoreMIDI" EXT=pd_darwin CPPFLAGS="-DDL_OPEN -DMACOSX -DUNISTD -I/usr/X11R6/include \ - -I../portaudio/pa_common -I../portaudio/pablio \ + -I../portaudio/include -I../portaudio/src/common \ + -I../portaudio/src/os/mac_osx/ \ -I../portmidi/pm_common -I../portmidi/pm_mac \ -I../portmidi/porttime \ - -DUSEAPI_PORTAUDIO -DPA19 -DPA_USE_COREAUDIO" + -DUSEAPI_PORTAUDIO -DPA19 -DPA_USE_COREAUDIO -DNEWBUFFER" if test `uname -r` = 7.9.0; then MORECFLAGS="-DMACOSX3 -DPA_BIG_ENDIAN -Wno-error" @@ -274,18 +281,22 @@ then SYSSRC="s_midi_pm.c s_audio_pa.c \ s_audio_pablio.c \ s_audio_paring.c \ - ../portaudio/pa_common/pa_allocation.c \ - ../portaudio/pa_common/pa_converters.c \ - ../portaudio/pa_common/pa_cpuload.c \ - ../portaudio/pa_common/pa_dither.c \ - ../portaudio/pa_common/pa_front.c \ - ../portaudio/pa_common/pa_process.c \ - ../portaudio/pa_common/pa_skeleton.c \ - ../portaudio/pa_common/pa_stream.c \ - ../portaudio/pa_common/pa_trace.c \ - ../portaudio/pa_unix/pa_unix_util.c \ - ../portaudio/pa_mac_core/pa_mac_core.c \ - ../portaudio/pa_mac/pa_mac_hostapis.c \ + ../portaudio/src/common/pa_allocation.c \ + ../portaudio/src/common/pa_converters.c \ + ../portaudio/src/common/pa_cpuload.c \ + ../portaudio/src/common/pa_dither.c \ + ../portaudio/src/common/pa_front.c \ + ../portaudio/src/common/pa_process.c \ + ../portaudio/src/common/pa_skeleton.c \ + ../portaudio/src/common/pa_stream.c \ + ../portaudio/src/common/pa_trace.c \ + ../portaudio/src/common/pa_debugprint.c \ + ../portaudio/src/common/pa_ringbuffer.c \ + ../portaudio/src/os/unix/pa_unix_util.c \ + ../portaudio/src/os/mac_osx/pa_mac_hostapis.c \ + ../portaudio/src/hostapi/coreaudio/pa_mac_core.c \ + ../portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c \ + ../portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c \ ../portmidi/pm_mac/pmmac.c \ ../portmidi/pm_mac/pmmacosxcm.c \ ../portmidi/pm_common/pmutil.c \ @@ -335,26 +346,30 @@ if test `uname -s` == MINGW32_NT-5.0; then EXT=dll MORECFLAGS="-DUSEAPI_PORTAUDIO -DPA19 -DMSW -DPA_NO_DS -DPD_INTERNAL \ - -I../portaudio/pa_common -I../portaudio/pablio \ + -I../portaudio/include -I../portaudio/src/common \ + -I../portaudio/src/os/win/ \ -mwindows -mms-bitfields "$MORECFLAGS PDLIB=$PDLIB" -lwsock32 -lwinmm -lole32 -lstdc++" SYSSRC="s_audio_pa.c s_audio_pablio.c s_audio_paring.c \ s_audio_mmio.c s_midi_mmio.c \ - ../portaudio/pa_common/pa_allocation.c \ - ../portaudio/pa_common/pa_converters.c \ - ../portaudio/pa_common/pa_cpuload.c \ - ../portaudio/pa_common/pa_dither.c \ - ../portaudio/pa_common/pa_front.c \ - ../portaudio/pa_common/pa_process.c \ - ../portaudio/pa_common/pa_skeleton.c \ - ../portaudio/pa_common/pa_stream.c \ - ../portaudio/pa_common/pa_trace.c \ - ../portaudio/pa_win/pa_win_util.c \ - ../portaudio/pa_win/pa_win_hostapis.c \ - ../portaudio/pa_win_wmme/pa_win_wmme.c" - ASIOSRC="../portaudio/pa_asio/iasiothiscallresolver.cpp \ - ../portaudio/pa_asio/pa_asio.cpp ../asio/asio.cpp \ + ../portaudio/src/common/pa_allocation.c \ + ../portaudio/src/common/pa_converters.c \ + ../portaudio/src/common/pa_cpuload.c \ + ../portaudio/src/common/pa_dither.c \ + ../portaudio/src/common/pa_front.c \ + ../portaudio/src/common/pa_process.c \ + ../portaudio/src/common/pa_skeleton.c \ + ../portaudio/src/common/pa_stream.c \ + ../portaudio/src/common/pa_trace.c \ + ../portaudio/src/common/pa_debugprint.c \ + ../portaudio/src/common/pa_ringbuffer.c \ + ../portaudio/src/os/win/pa_win_util.c \ + ../portaudio/src/os/win/pa_win_hostapis.c \ + ../portaudio/src/os/win/pa_x86_plain_converters.c \ + ../portaudio/src/hostapi/wmme/pa_win_wmme.c" + ASIOSRC="../portaudio/src/hostapi/asio/iasiothiscallresolver.cpp \ + ../portaudio/src/hostapi/pa_asio/asio.cpp ../asio/asio.cpp \ ../asio/asiodrivers.cpp ../asio/asiolist.cpp" STRIPFLAG="--strip-unneeded" GUINAME="pdtcl.dll" @@ -382,18 +397,6 @@ else SYSSRC=$SYSSRC" d_fft_mayer.c d_fftroutine.c" fi -# extra flags for alpha machines -if test `uname -m | awk '{print $1}'` = alpha; -then - MORECFLAGS=$MORECFLAGS" -mieee -mcpu=ev56" -fi - -# test for compaq compiler---not sure what this does or how to test it. -if test x$CC == xccc; -then - MORECFLAGS=$MORECFLAGS" -g3 -D__COMPAQC__ -arch host" -fi - ## JMZ{ ## this does not do very much, but i guess it is a good idea to use it... AC_SYS_LARGEFILE diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h index 0b0ec5fa..5460c517 100644 --- a/pd/src/m_pd.h +++ b/pd/src/m_pd.h @@ -11,7 +11,7 @@ extern "C" { #define PD_MAJOR_VERSION 0 #define PD_MINOR_VERSION 41 #define PD_BUGFIX_VERSION 0 -#define PD_TEST_VERSION "test05" +#define PD_TEST_VERSION "test06" /* old name for "MSW" flag -- we have to take it for the sake of many old "nmakefiles" for externs, which will define NT and not MSW */ diff --git a/pd/src/m_sched.c b/pd/src/m_sched.c index bce50cd5..b21e6e79 100644 --- a/pd/src/m_sched.c +++ b/pd/src/m_sched.c @@ -7,6 +7,9 @@ #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" +#ifdef MSW +#include +#endif /* LATER consider making this variable. It's now the LCM of all sample rates we expect to see: 32000, 44100, 48000, 88200, 96000. */ @@ -15,7 +18,8 @@ #define THREAD_LOCKING #include "pthread.h" - +#define SYS_QUIT_QUIT 1 +#define SYS_QUIT_RESTART 2 static int sys_quit; double sys_time; static double sys_time_per_msec = TIMEUNITPERSEC / 1000.; @@ -326,18 +330,23 @@ void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) void dsp_tick(void); -static int sched_usedacs = 1; +static int sched_useaudio = SCHED_AUDIO_POLL; static double sched_referencerealtime, sched_referencelogicaltime; double sys_time_per_dsp_tick; -void sched_set_using_dacs(int flag) +void sched_set_using_audio(int flag) { - sched_usedacs = flag; - if (!flag) + sched_useaudio = flag; + if (flag == SCHED_AUDIO_NONE) { sched_referencerealtime = sys_getrealtime(); sched_referencelogicaltime = clock_getlogicaltime(); } + if (flag == SCHED_AUDIO_CALLBACK && sched_useaudio != SCHED_AUDIO_CALLBACK) + sys_quit = SYS_QUIT_RESTART; + if (flag != SCHED_AUDIO_CALLBACK && sched_useaudio == SCHED_AUDIO_CALLBACK) + post("sorry, can't turn off callbacks yet; restart Pd"); /* not right yet! */ + sys_time_per_dsp_tick = (TIMEUNITPERSEC) * ((double)sys_schedblocksize) / sys_dacsr; } @@ -385,7 +394,7 @@ nonzero if you actually used the time; otherwise we're really really idle and will now sleep. */ int (*sys_idlehook)(void); -int m_scheduler( void) +static void m_pollingscheduler( void) { int idlecount = 0; sys_time_per_dsp_tick = (TIMEUNITPERSEC) * @@ -410,7 +419,7 @@ int m_scheduler( void) sys_addhist(0); waitfortick: - if (sched_usedacs) + if (sched_useaudio != SCHED_AUDIO_NONE) { #ifdef THREAD_LOCKING /* T.Grill - send_dacs may sleep -> @@ -433,6 +442,11 @@ int m_scheduler( void) if (!(idlecount & 31)) { static double idletime; + if (sched_useaudio != SCHED_AUDIO_POLL) + { + bug("m_pollingscheduler\n"); + return; + } /* on 32nd idle, start a clock watch; every 32 ensuing idles, check it */ if (idlecount == 32) @@ -441,7 +455,7 @@ int m_scheduler( void) { post("audio I/O stuck... closing audio\n"); sys_close_audio(); - sched_set_using_dacs(0); + sched_set_using_audio(SCHED_AUDIO_NONE); goto waitfortick; } } @@ -475,7 +489,6 @@ int m_scheduler( void) { sched_pollformeters(); sys_reportidle(); - #ifdef THREAD_LOCKING sys_unlock(); /* unlock while we idle */ #endif @@ -489,20 +502,61 @@ int m_scheduler( void) #ifdef THREAD_LOCKING sys_lock(); #endif - sys_addhist(5); sched_didnothing++; - } } #ifdef THREAD_LOCKING sys_unlock(); #endif +} - return (0); +void sched_audio_callbackfn(void) +{ + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + sys_addhist(1); + sched_tick(sys_time + sys_time_per_dsp_tick); + sys_addhist(2); + sys_pollmidiqueue(); + sys_addhist(3); + sys_pollgui(); + sys_addhist(5); + sched_pollformeters(); + sys_addhist(0); +} + +static void m_callbackscheduler(void) +{ + sys_initmidiqueue(); + while (1) + { +#ifdef MSW + Sleep(1000); +#else + sleep(1); +#endif + if (sys_idlehook) + sys_idlehook(); + } } +int m_mainloop(void) +{ + while (sys_quit != SYS_QUIT_QUIT) + { + if (sched_useaudio == SCHED_AUDIO_CALLBACK) + m_callbackscheduler(); + else m_pollingscheduler(); + if (sys_quit == SYS_QUIT_RESTART) + { + sys_quit = 0; + sys_close_audio(); + sys_reopen_audio(); + } + } + return (0); +} /* ------------ thread locking ------------------- */ @@ -534,5 +588,5 @@ int sys_trylock(void) {} void sys_exit(void) { - sys_quit = 1; + sys_quit = SYS_QUIT_QUIT; } diff --git a/pd/src/makefile.dependencies b/pd/src/makefile.dependencies deleted file mode 100644 index e69de29b..00000000 diff --git a/pd/src/makefile.nt b/pd/src/makefile.nt index fc624896..d60651cd 100644 --- a/pd/src/makefile.nt +++ b/pd/src/makefile.nt @@ -38,19 +38,20 @@ SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \ $(SYSSRC) PADIR = ..\portaudio -INCPA = -I$(PADIR) -I$(PADIR)\pa_common -I$(PADIR)\pablio -I..\lib\asio -SRCPA = $(PADIR)/pa_common/pa_stream.c \ - $(PADIR)/pa_common/pa_trace.c \ - $(PADIR)/pa_common/pa_skeleton.c \ - $(PADIR)/pa_common/pa_process.c \ - $(PADIR)/pa_common/pa_front.c \ - $(PADIR)/pa_common/pa_dither.c \ - $(PADIR)/pa_common/pa_cpuload.c \ - $(PADIR)/pa_common/pa_converters.c \ - $(PADIR)/pa_common/pa_allocation.c \ - $(PADIR)/pa_win/pa_win_util.c \ - $(PADIR)/pa_win/pa_win_hostapis.c \ - $(PADIR)/pa_win_wmme/pa_win_wmme.c +INCPA = -I$(PADIR)\include -I$(PADIR)\src\common -I..\lib\asio +PASRC = $(PADIR)\src +SRCPA = $(PASRC)/common/pa_stream.c \ + $(PASRC)/common/pa_trace.c \ + $(PASRC)/common/pa_skeleton.c \ + $(PASRC)/common/pa_process.c \ + $(PASRC)/common/pa_front.c \ + $(PASRC)/common/pa_dither.c \ + $(PASRC)/common/pa_cpuload.c \ + $(PASRC)/common/pa_converters.c \ + $(PASRC)/common/pa_allocation.c \ + $(PASRC)/os/win/pa_win_hostapis.c \ + $(PASRC)/os/win/pa_win_util.c \ + $(PASRC)/hostapi/wmme/pa_win_wmme.c # $(PADIR)/pa_win_wdmks/pa_win_wdmks.c SRCASIO = $(PADIR)/pa_asio/pa_asio.cpp @@ -63,12 +64,12 @@ $(LDIR)\odbc32.lib $(LDIR)\odbccp32.lib ..\lib\asio\asiolib.lib PAOBJ = pa_stream.obj pa_trace.obj pa_skeleton.obj pa_process.obj \ pa_front.obj pa_dither.obj pa_cpuload.obj pa_converters.obj \ - pa_allocation.obj pa_win_util.obj pa_win_hostapis.obj pa_asio.obj \ + pa_allocation.obj pa_win_hostapis.obj pa_win_util.obj pa_asio.obj \ pa_win_wmme.obj # pa_win_wdmks.obj PMDIR = ..\portmidi -INCPM = -I$(PMDIR)\pm_common -I$(PMDIR)\pm_win -I$(PMDIR)\porttime +INCPM = -I$(PMDIR)\pm_common -I$(PMDIR)\pm_win -I$(PMDIR)\porttime -DNEWBUFFER SRCPM = $(PADIR)/pm_common/portmidi.c \ $(PMDIR)/pm_common/pmutil.c \ $(PMDIR)/porttime/porttime.c \ @@ -127,37 +128,37 @@ s_entry_com.obj: s_entry.c ..\bin\pd.lib $(LIB) $(ASIOLIB) # explicit rules to compile portaudio sources: -pa_stream.obj: $(PADIR)\pa_common\pa_stream.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_stream.c -pa_trace.obj: $(PADIR)\pa_common\pa_trace.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_trace.c -pa_skeleton.obj: $(PADIR)\pa_common\pa_skeleton.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_skeleton.c -pa_process.obj: $(PADIR)\pa_common\pa_process.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_process.c -pa_front.obj: $(PADIR)\pa_common\pa_front.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_front.c -pa_dither.obj: $(PADIR)\pa_common\pa_dither.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_dither.c -pa_cpuload.obj: $(PADIR)\pa_common\pa_cpuload.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_cpuload.c -pa_converters.obj: $(PADIR)\pa_common\pa_converters.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_converters.c -pa_allocation.obj: $(PADIR)\pa_common\pa_allocation.c - cl /c $(ALLCF) $(PADIR)\pa_common\pa_allocation.c - -pa_win_util.obj: $(PADIR)\pa_win\pa_win_util.c - cl /c $(ALLCF) $(PADIR)\pa_win\pa_win_util.c -pa_win_hostapis.obj: $(PADIR)\pa_win\pa_win_hostapis.c - cl /c $(ALLCF) $(PADIR)\pa_win\pa_win_hostapis.c -pa_win_wmme.obj: $(PADIR)\pa_win_wmme\pa_win_wmme.c - cl /c $(ALLCF) $(PADIR)\pa_win_wmme\pa_win_wmme.c +pa_stream.obj: $(PASRC)\common\pa_stream.c + cl /c $(ALLCF) $(PASRC)\common\pa_stream.c +pa_trace.obj: $(PASRC)\common\pa_trace.c + cl /c $(ALLCF) $(PASRC)\common\pa_trace.c +pa_skeleton.obj: $(PASRC)\common\pa_skeleton.c + cl /c $(ALLCF) $(PASRC)\common\pa_skeleton.c +pa_process.obj: $(PASRC)\common\pa_process.c + cl /c $(ALLCF) $(PASRC)\common\pa_process.c +pa_front.obj: $(PASRC)\common\pa_front.c + cl /c $(ALLCF) $(PASRC)\common\pa_front.c +pa_dither.obj: $(PASRC)\common\pa_dither.c + cl /c $(ALLCF) $(PASRC)\common\pa_dither.c +pa_cpuload.obj: $(PASRC)\common\pa_cpuload.c + cl /c $(ALLCF) $(PASRC)\common\pa_cpuload.c +pa_converters.obj: $(PASRC)\common\pa_converters.c + cl /c $(ALLCF) $(PASRC)\common\pa_converters.c +pa_allocation.obj: $(PASRC)\common\pa_allocation.c + cl /c $(ALLCF) $(PASRC)\common\pa_allocation.c + +pa_win_hostapis.obj: $(PASRC)\os\win\pa_win_hostapis.c + cl /c $(ALLCF) $(PASRC)\os\win\pa_win_hostapis.c +pa_win_util.obj: $(PASRC)\os\win\pa_win_util.c + cl /c $(ALLCF) $(PASRC)\os\win\pa_win_util.c +pa_win_wmme.obj: $(PASRC)\hostapi\wmme\pa_win_wmme.c + cl /c $(ALLCF) $(PASRC)\hostapi\wmme\pa_win_wmme.c pa_win_wdmks.obj: $(PADIR)\pa_win_wdmks\pa_win_wdmks.c cl /c $(ALLCF) \ -DWINVER=0x400 -DKSAUDIO_SPEAKER_DIRECTOUT \ $(PADIR)\pa_win_wdmks\pa_win_wdmks.c -pa_asio.obj: $(PADIR)\pa_asio\pa_asio.cpp - cl /c $(ALLCF) $(PADIR)\pa_asio\pa_asio.cpp +pa_asio.obj: $(PASRC)\hostapi\asio\pa_asio.cpp + cl /c $(ALLCF) $(PASRC)\hostapi\asio\pa_asio.cpp portmidi.obj: $(PMDIR)\pm_common\portmidi.c cl /c $(ALLCF) $(PMDIR)\pm_common\portmidi.c diff --git a/pd/src/s_audio.c b/pd/src/s_audio.c index f33a135b..a211ae98 100644 --- a/pd/src/s_audio.c +++ b/pd/src/s_audio.c @@ -29,7 +29,7 @@ typedef long t_pa_sample; #define DEVDESCSIZE 80 static void audio_getdevs(char *indevlist, int *nindevs, - char *outdevlist, int *noutdevs, int *canmulti, + char *outdevlist, int *noutdevs, int *canmulti, int *cancallback, int maxndev, int devdescsize); /* these are set in this file when opening audio, but then may be reduced, @@ -65,6 +65,9 @@ static int audio_audiooutdev[MAXAUDIOOUTDEV]; static int audio_audiochoutdev[MAXAUDIOOUTDEV]; static int audio_rate; static int audio_advance; +static int audio_callback; + +void sched_audio_callbackfn(void); static int audio_isopen(void) { @@ -76,7 +79,7 @@ static int audio_isopen(void) void sys_get_audio_params( int *pnaudioindev, int *paudioindev, int *chindev, int *pnaudiooutdev, int *paudiooutdev, int *choutdev, - int *prate, int *padvance) + int *prate, int *padvance, int *pcallback) { int i; *pnaudioindev = audio_naudioindev; @@ -89,12 +92,13 @@ void sys_get_audio_params( choutdev[i] = audio_audiochoutdev[i]; *prate = audio_rate; *padvance = audio_advance; + *pcallback = audio_callback; } void sys_save_audio_params( int naudioindev, int *audioindev, int *chindev, int naudiooutdev, int *audiooutdev, int *choutdev, - int rate, int advance) + int rate, int advance, int callback) { int i; audio_naudioindev = naudioindev; @@ -107,6 +111,7 @@ void sys_save_audio_params( audio_audiochoutdev[i] = choutdev[i]; audio_rate = rate; audio_advance = advance; + audio_callback = callback; } /* init routines for any API which needs to set stuff up before @@ -165,14 +170,14 @@ void sys_setchsr(int chin, int chout, int sr) /* ----------------------- public routines ----------------------- */ - /* open audio devices (after cleaning up the specified device and channel - vectors). The audio devices are "zero based" (i.e. "0" means the first - one.) We also save the cleaned-up device specification so that we - can later re-open audio and/or show the settings on a dialog window. */ + /* set audio device settings (after cleaning up the specified device and + channel vectors). The audio devices are "zero based" (i.e. "0" means the + first one.) We can later re-open audio and/or show the settings on a\ + dialog window. */ -void sys_open_audio(int naudioindev, int *audioindev, int nchindev, +void sys_set_audio_settings(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, - int *choutdev, int rate, int advance, int enable) + int *choutdev, int rate, int advance, int callback) { int i, *ip; int defaultchannels = SYS_DEFAULTCH; @@ -181,10 +186,9 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int realinchans[MAXAUDIOINDEV], realoutchans[MAXAUDIOOUTDEV]; char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; - int indevs = 0, outdevs = 0, canmulti = 0; + int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; audio_getdevs(indevlist, &indevs, outdevlist, &outdevs, &canmulti, - MAXNDEV, DEVDESCSIZE); - + &cancallback, MAXNDEV, DEVDESCSIZE); if (sys_externalschedlib) { return; @@ -319,73 +323,11 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, outchans += choutdev[i]; nrealoutdev++; } - /* if no input or output devices seem to have been specified, - this really means just disable audio, which we now do. */ - if (!inchans && !outchans) - enable = 0; sys_schedadvance = advance * 1000; sys_setchsr(inchans, outchans, rate); sys_log_error(ERR_NOTHING); - if (enable) - { -#ifdef USEAPI_PORTAUDIO - if (sys_audioapi == API_PORTAUDIO) - { - int blksize = (sys_blocksize ? sys_blocksize : 64); - pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout, - blksize, sys_advance_samples/blksize, - (naudiooutdev > 0 ? audioindev[0] : 0), - (naudiooutdev > 0 ? audiooutdev[0] : 0)); - } -else -#endif -#ifdef USEAPI_JACK - if (sys_audioapi == API_JACK) - jack_open_audio((nrealindev > 0 ? realinchans[0] : 0), - (nrealoutdev > 0 ? realoutchans[0] : 0), rate); - - else -#endif -#ifdef USEAPI_OSS - if (sys_audioapi == API_OSS) - oss_open_audio(nrealindev, realindev, nrealindev, realinchans, - nrealoutdev, realoutdev, nrealoutdev, realoutchans, rate); - else -#endif -#ifdef USEAPI_ALSA - /* for alsa, only one device is supported; it may - be open for both input and output. */ - if (sys_audioapi == API_ALSA) - alsa_open_audio(nrealindev, audioindev, nrealindev, realinchans, - nrealoutdev, audiooutdev, nrealoutdev, realoutchans, rate); - else -#endif -#ifdef USEAPI_SGI - if (sys_audioapi == API_SGI) - { - xtern int sgi_open_audio(int nindev, int *indev, int nchin, - int *chin, int noutdev, int *outdev, int nchout, int *chout, - int rate); - sgi_open_audio(naudioindev, audioindev, nchindev, chindev, - naudiooutdev, audiooutdev, nchoutdev, choutdev, rate); - } - else -#endif -#ifdef USEAPI_MMIO - if (sys_audioapi == API_MMIO) - mmio_open_audio(nrealindev, audioindev, nrealindev, realinchans, - nrealoutdev, audiooutdev, nrealoutdev, realoutchans, rate); - else -#endif - post("unknown audio API specified"); - } - sys_save_audio_params(naudioindev, audioindev, chindev, - naudiooutdev, audiooutdev, choutdev, sys_dacsr, advance); - if (sys_inchannels == 0 && sys_outchannels == 0) - enable = 0; - audio_state = enable; - sys_vgui("set pd_whichapi %d\n", (audio_isopen() ? sys_audioapi : 0)); - sched_set_using_dacs(enable); + sys_save_audio_params(nrealindev, realindev, realinchans, + nrealoutdev, realoutdev, realoutchans, sys_dacsr, advance, callback); } void sys_close_audio(void) @@ -416,14 +358,6 @@ void sys_close_audio(void) alsa_close_audio(); else #endif -#ifdef USEAPI_SGI - if (sys_audioapi == API_SGI) - { - extern void sgi_close_audio(void); - sgi_close_audio(); - } - else -#endif #ifdef USEAPI_MMIO if (sys_audioapi == API_MMIO) mmio_close_audio(); @@ -431,6 +365,7 @@ void sys_close_audio(void) #endif post("sys_close_audio: unknown API %d", sys_audioapi); sys_inchannels = sys_outchannels = 0; + sched_set_using_audio(SCHED_AUDIO_NONE); } /* open audio using whatever parameters were last used */ @@ -438,11 +373,67 @@ void sys_reopen_audio( void) { int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; - int rate, advance; + int rate, advance, callback, outcome = 0; sys_get_audio_params(&naudioindev, audioindev, chindev, - &naudiooutdev, audiooutdev, choutdev, &rate, &advance); - sys_open_audio(naudioindev, audioindev, naudioindev, chindev, - naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, 1); + &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); + if (!naudioindev && !naudiooutdev) + { + sched_set_using_audio(SCHED_AUDIO_NONE); + return; + } +#ifdef USEAPI_PORTAUDIO + if (sys_audioapi == API_PORTAUDIO) + { + int blksize = (sys_blocksize ? sys_blocksize : 64); + outcome = pa_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate, sys_soundin, + sys_soundout, blksize, sys_advance_samples/blksize, + (naudioindev > 0 ? audioindev[0] : 0), + (naudiooutdev > 0 ? audiooutdev[0] : 0), + (callback ? sched_audio_callbackfn : 0)); + } + else +#endif +#ifdef USEAPI_JACK + if (sys_audioapi == API_JACK) + outcome = jack_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudioindev > 0 ? choutdev[0] : 0), rate); + + else +#endif +#ifdef USEAPI_OSS + if (sys_audioapi == API_OSS) + outcome = oss_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif +#ifdef USEAPI_ALSA + /* for alsa, only one device is supported; it may + be open for both input and output. */ + if (sys_audioapi == API_ALSA) + outcome = alsa_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif +#ifdef USEAPI_MMIO + if (sys_audioapi == API_MMIO) + outcome = mmio_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif + post("unknown audio API specified"); + if (outcome) /* failed */ + { + audio_state = 0; + sched_set_using_audio(SCHED_AUDIO_NONE); + } + else + { + audio_state = 1; + sched_set_using_audio( + (callback ? SCHED_AUDIO_CALLBACK : SCHED_AUDIO_POLL)); + } + sys_vgui("set pd_whichapi %d\n", (outcome == 0 ? sys_audioapi : 0)); } int sys_send_dacs(void) @@ -489,14 +480,6 @@ int sys_send_dacs(void) return (alsa_send_dacs()); else #endif -#ifdef USEAPI_SGI - if (sys_audioapi == API_SGI) - { - extern int sgi_send_dacs(void); - return (sgi_send_dacs()); - } - else -#endif #ifdef USEAPI_MMIO if (sys_audioapi == API_MMIO) return (mmio_send_dacs()); @@ -539,15 +522,17 @@ void sys_reportidle(void) } static void audio_getdevs(char *indevlist, int *nindevs, - char *outdevlist, int *noutdevs, int *canmulti, + char *outdevlist, int *noutdevs, int *canmulti, int *cancallback, int maxndev, int devdescsize) { audio_init(); + *cancallback = 0; /* may be overridden by specific API implementation */ #ifdef USEAPI_PORTAUDIO if (sys_audioapi == API_PORTAUDIO) { pa_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); + *cancallback = 1; } else #endif @@ -575,17 +560,6 @@ static void audio_getdevs(char *indevlist, int *nindevs, } else #endif -#ifdef USEAPI_SGI - if (sys_audioapi == API_SGI) - { - extern void sgi_getdevs(char *indevlist, int *nindevs, - char *outdevlist, int *noutdevs, int *canmulti, - int maxndev, int devdescsize); - sgi_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, - maxndev, devdescsize); - } - else -#endif #ifdef USEAPI_MMIO if (sys_audioapi == API_MMIO) { @@ -611,10 +585,10 @@ static void audio_getdevs(char *indevlist, int *nindevs, static void sys_listaudiodevs(void ) { char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; - int nindevs = 0, noutdevs = 0, i, canmulti = 0; + int nindevs = 0, noutdevs = 0, i, canmulti = 0, cancallback = 0; audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, - MAXNDEV, DEVDESCSIZE); + &cancallback, MAXNDEV, DEVDESCSIZE); if (!nindevs) post("no audio input devices found"); @@ -653,13 +627,13 @@ void glob_audio_properties(t_pd *dummy, t_floatarg flongform) audioinchan1, audioinchan2, audioinchan3, audioinchan4, audiooutdev1, audiooutdev2, audiooutdev3, audiooutdev4, audiooutchan1, audiooutchan2, audiooutchan3, audiooutchan4; - int rate, advance; + int rate, advance, callback; /* these are all the devices on your system: */ char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; - int nindevs = 0, noutdevs = 0, canmulti = 0, i; + int nindevs = 0, noutdevs = 0, canmulti = 0, cancallback = 0, i; audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, - MAXNDEV, DEVDESCSIZE); + &cancallback, MAXNDEV, DEVDESCSIZE); sys_gui("global audio_indevlist; set audio_indevlist {}\n"); for (i = 0; i < nindevs; i++) @@ -672,7 +646,7 @@ void glob_audio_properties(t_pd *dummy, t_floatarg flongform) outdevlist + i * DEVDESCSIZE); sys_get_audio_params(&naudioindev, audioindev, chindev, - &naudiooutdev, audiooutdev, choutdev, &rate, &advance); + &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); /* post("naudioindev %d naudiooutdev %d longform %f", naudioindev, naudiooutdev, flongform); */ @@ -699,12 +673,13 @@ void glob_audio_properties(t_pd *dummy, t_floatarg flongform) "pdtk_audio_dialog %%s \ %d %d %d %d %d %d %d %d \ %d %d %d %d %d %d %d %d \ -%d %d %d %d\n", +%d %d %d %d %d\n", audioindev1, audioindev2, audioindev3, audioindev4, audioinchan1, audioinchan2, audioinchan3, audioinchan4, audiooutdev1, audiooutdev2, audiooutdev3, audiooutdev4, audiooutchan1, audiooutchan2, audiooutchan3, audiooutchan4, - rate, advance, canmulti, (flongform != 0)); + rate, advance, canmulti, (cancallback ? callback : -1), + (flongform != 0)); gfxstub_deleteforkey(0); gfxstub_new(&glob_pdobject, (void *)glob_audio_properties, buf); } @@ -721,6 +696,7 @@ void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) /* the new values the dialog came back with: */ int newrate = atom_getintarg(16, argc, argv); int newadvance = atom_getintarg(17, argc, argv); + int newcallback = atom_getintarg(18, argc, argv); int statewas; for (i = 0; i < 4; i++) @@ -753,11 +729,18 @@ void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) noutdev++; } } - - sys_close_audio(); - sys_open_audio(nindev, newaudioindev, nindev, newaudioinchan, + + if (newcallback < 0) + newcallback = 0; + post("callback %d new %d",audio_callback, newcallback) ; + if (audio_callback == newcallback) + sys_close_audio(); + sys_set_audio_settings(nindev, newaudioindev, nindev, newaudioinchan, noutdev, newaudiooutdev, noutdev, newaudiooutchan, - newrate, newadvance, 1); + newrate, newadvance, (newcallback >= 0 ? newcallback : 0)); + post("callback %d new %d",audio_callback, newcallback) ; + if (audio_callback == newcallback) + sys_reopen_audio(); } void sys_listdevs(void ) @@ -782,12 +765,6 @@ void sys_listdevs(void ) sys_listaudiodevs(); else #endif -#ifdef USEAPI_SGI - extern void sgi_listaudiodevs(void); - if (sys_audioapi == API_SGI) - sgi_listaudiodevs(); - else -#endif #ifdef USEAPI_MMIO if (sys_audioapi == API_MMIO) sys_listaudiodevs(); @@ -841,7 +818,6 @@ void glob_audio_setapi(void *dummy, t_floatarg f) { sys_close_audio(); audio_state = 0; - sched_set_using_dacs(0); } } @@ -856,10 +832,7 @@ void sys_set_audio_state(int onoff) else { if (audio_isopen()) - { sys_close_audio(); - sched_set_using_dacs(0); - } } audio_state = onoff; } @@ -888,9 +861,6 @@ void sys_get_audio_apis(char *buf) #else sprintf(buf + strlen(buf), "{portaudio %d} ", API_PORTAUDIO); #endif -#ifdef USEAPI_SGI - sprintf(buf + strlen(buf), "{SGI %d} ", API_SGI); n++; -#endif #endif n++; #endif @@ -901,7 +871,6 @@ void sys_get_audio_apis(char *buf) /* then again, if only one API (or none) we don't offer any choice. */ if (n < 2) strcpy(buf, "{}"); - } #ifdef USEAPI_ALSA diff --git a/pd/src/s_audio_mmio.c b/pd/src/s_audio_mmio.c index a949767a..cf79f135 100644 --- a/pd/src/s_audio_mmio.c +++ b/pd/src/s_audio_mmio.c @@ -694,7 +694,7 @@ idle: /* ------------------- public routines -------------------------- */ -void mmio_open_audio(int naudioindev, int *audioindev, +int mmio_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate) { @@ -724,7 +724,7 @@ void mmio_open_audio(int naudioindev, int *audioindev, (nt_nwaveout > 1 ? WAVE_MAPPER : -1) : audiooutdev[0]); if (naudiooutdev > 1 || naudioindev > 1) post("separate audio device choice not supported; using sequential devices."); - mmio_do_open_audio(); + return (mmio_do_open_audio()); } diff --git a/pd/src/s_audio_pa.c b/pd/src/s_audio_pa.c index 627b0015..f05c41d8 100644 --- a/pd/src/s_audio_pa.c +++ b/pd/src/s_audio_pa.c @@ -23,22 +23,138 @@ static PABLIO_Stream *pa_stream; static int pa_inchans, pa_outchans; static float *pa_soundin, *pa_soundout; +static t_audiocallback pa_callback; #define MAX_PA_CHANS 32 #define MAX_SAMPLES_PER_FRAME MAX_PA_CHANS * DEFDACBLKSIZE -#ifndef PA19 -#define Pa_GetDeviceCount Pa_CountDevices -#endif +static int pa_lowlevel_callback(const void *inputBuffer, + void *outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, + void *userData) +{ + int i; + unsigned int j; + float *fbuf, *fp2, *fp3, *soundiop; + if (framesPerBuffer != DEFDACBLKSIZE) + { + fprintf(stderr, "ignoring buffer size %d\n", framesPerBuffer); + return; + } + if (inputBuffer != NULL) + { + fbuf = (float *)inputBuffer; + soundiop = pa_soundin; + for (i = 0, fp2 = fbuf; i < pa_inchans; i++, fp2++) + for (j = 0, fp3 = fp2; j < framesPerBuffer; j++, fp3 += pa_inchans) + *soundiop++ = *fp3; + } + else memset((void *)pa_soundin, 0, + framesPerBuffer * pa_inchans * sizeof(float)); + (*pa_callback)(); + if (outputBuffer != NULL) + { + fbuf = (float *)outputBuffer; + soundiop = pa_soundout; + for (i = 0, fp2 = fbuf; i < pa_outchans; i++, fp2++) + for (j = 0, fp3 = fp2; j < framesPerBuffer; j++, fp3 += pa_outchans) + *fp3 = *soundiop++; + } + + return 0; +} + +PaError pa_open_callback(double sampleRate, int inchannels, int outchannels, + int framesperbuf, int nbuffers, int indeviceno, int outdeviceno) +{ + long bytesPerSample; + PaError err; + PABLIO_Stream *pastream; + long numFrames; + PaStreamParameters instreamparams, outstreamparams; + + if (indeviceno < 0) + { + indeviceno = Pa_GetDefaultInputDevice(); + fprintf(stderr, "using default input device number: %d\n", indeviceno); + } + if (outdeviceno < 0) + { + outdeviceno = Pa_GetDefaultOutputDevice(); + fprintf(stderr, "using default output device number: %d\n", outdeviceno); + } + /* fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", + nchannels, flags, nbuffers, framesperbuf); */ + + /* Allocate PABLIO_Stream structure for caller. */ + pastream = (PABLIO_Stream *)malloc( sizeof(PABLIO_Stream)); + if (pastream == NULL) + return (1); + memset(pastream, 0, sizeof(PABLIO_Stream)); + + /* Determine size of a sample. */ + bytesPerSample = Pa_GetSampleSize(paFloat32); + if (bytesPerSample < 0) + { + err = (PaError) bytesPerSample; + goto error; + } + pastream->insamplesPerFrame = inchannels; + pastream->inbytesPerFrame = bytesPerSample * pastream->insamplesPerFrame; + pastream->outsamplesPerFrame = outchannels; + pastream->outbytesPerFrame = bytesPerSample * pastream->outsamplesPerFrame; + + numFrames = nbuffers * framesperbuf; + + instreamparams.device = indeviceno; + instreamparams.channelCount = inchannels; + instreamparams.sampleFormat = paFloat32; + instreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + instreamparams.hostApiSpecificStreamInfo = 0; + + outstreamparams.device = outdeviceno; + outstreamparams.channelCount = outchannels; + outstreamparams.sampleFormat = paFloat32; + outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + outstreamparams.hostApiSpecificStreamInfo = 0; + + err = Pa_OpenStream( + &pastream->stream, + (inchannels ? &instreamparams : 0), + (outchannels ? &outstreamparams : 0), + sampleRate, + DEFDACBLKSIZE, + paNoFlag, /* portaudio will clip for us */ + pa_lowlevel_callback, + pastream); + if (err != paNoError) + goto error; + + err = Pa_StartStream(pastream->stream); + if (err != paNoError) + { + fprintf(stderr, "Pa_StartStream failed; closing audio stream...\n"); + CloseAudioStream( pastream ); + goto error; + } + pa_stream = pastream; + return paNoError; +error: + pa_stream = NULL; + return err; +} int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, t_sample *soundout, int framesperbuf, int nbuffers, - int indeviceno, int outdeviceno) + int indeviceno, int outdeviceno, t_audiocallback callbackfn) { PaError err; static int initialized; int j, devno, pa_indev = 0, pa_outdev = 0; - + + pa_callback = callbackfn; + if (callbackfn) + fprintf(stderr, "callback enabled\n"); if (!initialized) { /* Initialize PortAudio */ @@ -105,26 +221,34 @@ int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, post("output device %d, channels %d", pa_outdev, outchans); post("framesperbuf %d, nbufs %d", framesperbuf, nbuffers); } - if (inchans || outchans) + pa_inchans = inchans; + pa_outchans = outchans; + pa_soundin = soundin; + pa_soundout = soundout; + if (! inchans && !outchans) + return(0); + if (callbackfn) + { + pa_callback = callbackfn; + err = pa_open_callback(rate, inchans, outchans, + framesperbuf, nbuffers, pa_indev, pa_outdev); + } + else + { err = OpenAudioStream( &pa_stream, rate, paFloat32, inchans, outchans, framesperbuf, nbuffers, pa_indev, pa_outdev); - else err = 0; + } if ( err != paNoError ) { - fprintf( stderr, "Error number %d occured opening portaudio stream\n", + fprintf(stderr, "Error number %d opening portaudio stream\n", err); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); Pa_Terminate(); - sys_inchannels = sys_outchannels = 0; return (1); } else if (sys_verbose) post("... opened OK."); - pa_inchans = inchans; - pa_outchans = outchans; - pa_soundin = soundin; - pa_soundout = soundout; return (0); } @@ -235,17 +359,10 @@ void pa_listdevs(void) /* lifted from pa_devs.c in portaudio */ fprintf(stderr, " %s;", pdi->name ); fprintf(stderr, "%d inputs, ", pdi->maxInputChannels ); fprintf(stderr, "%d outputs", pdi->maxOutputChannels ); -#ifdef PA19 if ( i == Pa_GetDefaultInputDevice() ) fprintf(stderr, " (Default Input)"); if ( i == Pa_GetDefaultOutputDevice() ) fprintf(stderr, " (Default Output)"); -#else - if ( i == Pa_GetDefaultInputDeviceID() ) - fprintf(stderr, " (Default Input)"); - if ( i == Pa_GetDefaultOutputDeviceID() ) - fprintf(stderr, " (Default Output)"); -#endif fprintf(stderr, "\n"); } diff --git a/pd/src/s_audio_pablio.c b/pd/src/s_audio_pablio.c index 5827533f..0873f269 100644 --- a/pd/src/s_audio_pablio.c +++ b/pd/src/s_audio_pablio.c @@ -58,17 +58,11 @@ static void NPa_Sleep(int n) /* MSP wrapper to check we never stall... */ /******** Prototypes ****************************************************/ /************************************************************************/ -#ifdef PA19 static int blockingIOCallback( const void *inputBuffer, void *outputBuffer, /*MSP */ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData ); -#else -static int blockingIOCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ); -#endif static PaError PABLIO_InitFIFO( sys_ringbuf *rbuf, long numFrames, long bytesPerFrame ); static PaError PABLIO_TermFIFO( sys_ringbuf *rbuf ); @@ -79,17 +73,11 @@ static PaError PABLIO_TermFIFO( sys_ringbuf *rbuf ); /* Called from PortAudio. * Read and write data only if there is room in FIFOs. */ -#ifdef PA19 static int blockingIOCallback( const void *inputBuffer, void *outputBuffer, /* MSP */ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData ) -#else -static int blockingIOCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -#endif { PABLIO_Stream *data = (PABLIO_Stream*)userData; (void) outTime; @@ -223,11 +211,7 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, PaError err; PABLIO_Stream *aStream; long numFrames; -#ifdef PA19 PaStreamParameters instreamparams, outstreamparams; /* MSP */ -#else - long minNumBuffers; -#endif /* fprintf(stderr, "open %lf fmt %d flags %d ch: %d fperbuf: %d nbuf: %d devs: %d %d\n", @@ -236,20 +220,12 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, if (indeviceno < 0) /* MSP... */ { -#ifdef PA19 indeviceno = Pa_GetDefaultInputDevice(); -#else - indeviceno = Pa_GetDefaultInputDeviceID(); -#endif fprintf(stderr, "using default input device number: %d\n", indeviceno); } if (outdeviceno < 0) { -#ifdef PA19 outdeviceno = Pa_GetDefaultOutputDevice(); -#else - outdeviceno = Pa_GetDefaultOutputDeviceID(); -#endif fprintf(stderr, "using default output device number: %d\n", outdeviceno); } /* fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", @@ -277,7 +253,6 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, err = Pa_Initialize(); if( err != paNoError ) goto error; -#ifdef PA19 numFrames = nbuffers * framesperbuf; /* ...MSP */ instreamparams.device = indeviceno; /* MSP... */ @@ -292,17 +267,6 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; outstreamparams.hostApiSpecificStreamInfo = 0; /* ... MSP */ -#else -/* Warning: numFrames must be larger than amount of data processed per - interrupt inside PA to prevent glitches. */ /* MSP */ - minNumBuffers = Pa_GetMinNumBuffers(framesperbuf, sampleRate); - if (minNumBuffers > nbuffers) - fprintf(stderr, - "warning: number of buffers %d less than recommended minimum %d\n", - (int)nbuffers, (int)minNumBuffers); -#endif - - numFrames = nbuffers * framesperbuf; /* fprintf(stderr, "numFrames %d\n", numFrames); */ /* Initialize Ring Buffers */ @@ -327,7 +291,6 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, /* Open a PortAudio stream that we will use to communicate with the underlying * audio drivers. */ -#ifdef PA19 err = Pa_OpenStream( &aStream->stream, (doRead ? &instreamparams : 0), /* MSP */ @@ -337,24 +300,6 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, paNoFlag, /* MSP -- portaudio will clip for us */ blockingIOCallback, aStream ); -#else - err = Pa_OpenStream( - &aStream->stream, - (doRead ? indeviceno : paNoDevice), /* MSP */ - (doRead ? aStream->insamplesPerFrame : 0 ), - format, - NULL, - (doWrite ? outdeviceno : paNoDevice), /* MSP */ - (doWrite ? aStream->outsamplesPerFrame : 0 ), - format, - NULL, - sampleRate, - framesperbuf, /* MSP */ - nbuffers, /* MSP */ - paNoFlag, /* MSP -- portaudio will clip for us */ - blockingIOCallback, - aStream ); -#endif if( err != paNoError ) goto error; err = Pa_StartStream( aStream->stream ); diff --git a/pd/src/s_file.c b/pd/src/s_file.c index 53d71bfa..c81d423b 100644 --- a/pd/src/s_file.c +++ b/pd/src/s_file.c @@ -288,7 +288,7 @@ void sys_loadpreferences( void) int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; int nmidiindev, midiindev[MAXMIDIINDEV]; int nmidioutdev, midioutdev[MAXMIDIOUTDEV]; - int i, rate = 0, advance = 0, api, nolib, maxi; + int i, rate = 0, advance = 0, callback = 0, api, nolib, maxi; char prefbuf[MAXPDSTRING], keybuf[80]; sys_initloadpreferences(); @@ -337,8 +337,11 @@ void sys_loadpreferences( void) sscanf(prefbuf, "%d", &rate); if (sys_getpreference("audiobuf", prefbuf, MAXPDSTRING)) sscanf(prefbuf, "%d", &advance); - sys_open_audio(naudioindev, audioindev, naudioindev, chindev, - naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, 0); + if (sys_getpreference("callback", prefbuf, MAXPDSTRING)) + sscanf(prefbuf, "%d", &callback); + sys_set_audio_settings(naudioindev, audioindev, naudioindev, chindev, + naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, + callback); /* load MIDI preferences */ /* JMZ/MB: brackets for initializing */ @@ -423,7 +426,7 @@ void glob_savepreferences(t_pd *dummy) { int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; - int i, rate, advance; + int i, rate, advance, callback; char buf1[MAXPDSTRING], buf2[MAXPDSTRING]; int nmidiindev, midiindev[MAXMIDIINDEV]; int nmidioutdev, midioutdev[MAXMIDIOUTDEV]; @@ -436,7 +439,7 @@ void glob_savepreferences(t_pd *dummy) sys_putpreference("audioapi", buf1); sys_get_audio_params(&naudioindev, audioindev, chindev, - &naudiooutdev, audiooutdev, choutdev, &rate, &advance); + &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); sys_putpreference("noaudioin", (naudioindev <= 0 ? "True" : "False")); for (i = 0; i < naudioindev; i++) @@ -459,6 +462,9 @@ void glob_savepreferences(t_pd *dummy) sprintf(buf1, "%d", rate); sys_putpreference("rate", buf1); + sprintf(buf1, "%d", callback); + sys_putpreference("callback", buf1); + /* MIDI settings */ sys_get_midi_params(&nmidiindev, midiindev, &nmidioutdev, midioutdev); sys_putpreference("nomidiin", (nmidiindev <= 0 ? "True" : "False")); diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c index e6c69bf6..9945466f 100644 --- a/pd/src/s_inter.c +++ b/pd/src/s_inter.c @@ -1079,6 +1079,10 @@ int sys_startgui(const char *guidir) goto foundit; nohomedir: /* Perform the same search among system applications. */ + strcpy(filename, + "/usr/bin/wish"); + if (stat(filename, &statbuf) >= 0) + goto foundit; strcpy(filename, "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell"); if (stat(filename, &statbuf) >= 0) diff --git a/pd/src/s_main.c b/pd/src/s_main.c index c170aec5..ded67c88 100644 --- a/pd/src/s_main.c +++ b/pd/src/s_main.c @@ -31,7 +31,7 @@ int sys_argparse(int argc, char **argv); void sys_findprogdir(char *progname); int sys_startgui(const char *guipath); int sys_rcfile(void); -int m_scheduler(void); +int m_mainloop(void); void sys_addhelppath(char *p); #ifdef USEAPI_ALSA void alsa_adddev(char *name); @@ -43,6 +43,7 @@ int sys_noloadbang; int sys_nogui; int sys_hipriority = -1; /* -1 = don't care; 0 = no; 1 = yes */ int sys_guisetportnumber; /* if started from the GUI, this is the port # */ +int sys_nosleep = 0; /* skip all "sleep" calls and spin instead */ char *sys_guicmd; t_symbol *sys_libdir; @@ -60,6 +61,7 @@ int sys_midioutdevlist[MAXMIDIOUTDEV] = {1}; char sys_font[100] = "courier"; /* tb: font name */ static int sys_main_srate; static int sys_main_advance; +static int sys_main_callback; static int sys_listplease; int sys_externalschedlib; @@ -69,7 +71,7 @@ char sys_extraflagsstring[MAXPDSTRING]; /* here the "-1" counts signify that the corresponding vector hasn't been - specified in command line arguments; sys_open_audio will detect this + specified in command line arguments; sys_set_audio_settings will detect it and fill things in. */ static int sys_nsoundin = -1; static int sys_nsoundout = -1; @@ -81,7 +83,6 @@ static int sys_nchout = -1; static int sys_chinlist[MAXAUDIOINDEV]; static int sys_choutlist[MAXAUDIOOUTDEV]; -int sys_nosleep = 0; /* skip all "sleep" calls and spin instead */ t_sample* get_sys_soundout() { return sys_soundout; } t_sample* get_sys_soundin() { return sys_soundin; } int* get_sys_main_advance() { return &sys_main_advance; } @@ -313,7 +314,7 @@ int sys_main(int argc, char **argv) sys_reopen_midi(); sys_reopen_audio(); /* run scheduler until it quits */ - return (m_scheduler()); + return (m_mainloop()); } } @@ -580,6 +581,11 @@ int sys_argparse(int argc, char **argv) sys_main_advance = atoi(argv[1]); argc -= 2; argv += 2; } + else if (!strcmp(*argv, "-callback")) + { + sys_main_callback = 1; + argc--; argv++; + } else if (!strcmp(*argv, "-blocksize")) { sys_setblocksize(atoi(argv[1])); @@ -899,7 +905,7 @@ static void sys_afterargparse(void) int i; int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; - int nchindev, nchoutdev, rate, advance; + int nchindev, nchoutdev, rate, advance, callback; int nmidiindev = 0, midiindev[MAXMIDIINDEV]; int nmidioutdev = 0, midioutdev[MAXMIDIOUTDEV]; /* add "extra" library to path */ @@ -935,7 +941,7 @@ static void sys_afterargparse(void) else are the default. Overwrite them with any results of argument parsing, and store them again. */ sys_get_audio_params(&naudioindev, audioindev, chindev, - &naudiooutdev, audiooutdev, choutdev, &rate, &advance); + &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); if (sys_nchin >= 0) { nchindev = sys_nchin; @@ -981,8 +987,11 @@ static void sys_afterargparse(void) advance = sys_main_advance; if (sys_main_srate) rate = sys_main_srate; - sys_open_audio(naudioindev, audioindev, nchindev, chindev, - naudiooutdev, audiooutdev, nchoutdev, choutdev, rate, advance, 0); + if (sys_main_callback) + callback = sys_main_callback; + sys_set_audio_settings(naudioindev, audioindev, nchindev, chindev, + naudiooutdev, audiooutdev, nchoutdev, choutdev, rate, advance, + callback); sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 0); } diff --git a/pd/src/s_midi_pm.c b/pd/src/s_midi_pm.c index b0993268..831f3f06 100644 --- a/pd/src/s_midi_pm.c +++ b/pd/src/s_midi_pm.c @@ -21,7 +21,6 @@ #include "portaudio.h" #include "portmidi.h" #include "porttime.h" -#include "pminternal.h" static PmStream *mac_midiindevlist[MAXMIDIINDEV]; static PmStream *mac_midioutdevlist[MAXMIDIOUTDEV]; diff --git a/pd/src/s_stuff.h b/pd/src/s_stuff.h index 17974833..4cde9c42 100644 --- a/pd/src/s_stuff.h +++ b/pd/src/s_stuff.h @@ -71,10 +71,10 @@ extern int sys_blocksize; /* audio I/O block size in sample frames */ extern float sys_dacsr; extern int sys_schedadvance; extern int sys_sleepgrain; -void sys_open_audio(int naudioindev, int *audioindev, +void sys_set_audio_settings(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, - int srate, int advance, int enable); + int srate, int advance, int callback); void sys_reopen_audio( void); void sys_close_audio(void); @@ -139,7 +139,11 @@ EXTERN void sys_log_error(int type); #define ERR_DACSLEPT 2 #define ERR_RESYNC 3 #define ERR_DATALATE 4 -void sched_set_using_dacs(int flag); + +#define SCHED_AUDIO_NONE 0 +#define SCHED_AUDIO_POLL 1 +#define SCHED_AUDIO_CALLBACK 2 +void sched_set_using_audio(int flag); /* s_inter.c */ @@ -205,9 +209,11 @@ void sys_setvirtualalarm( void); #define DEFAULTADVANCE 50 #endif +typedef void (*t_audiocallback)(void); + int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, t_sample *soundout, int framesperbuf, int nbuffers, - int indeviceno, int outdeviceno); + int indeviceno, int outdeviceno, t_audiocallback callback); void pa_close_audio(void); int pa_send_dacs(void); void sys_reportidle(void); @@ -245,7 +251,7 @@ void jack_getdevs(char *indevlist, int *nindevs, int maxndev, int devdescsize); void jack_listdevs(void); -void mmio_open_audio(int naudioindev, int *audioindev, +int mmio_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate); void mmio_close_audio( void); @@ -269,11 +275,11 @@ void linux_alsa_devname(char *devname); void sys_get_audio_params( int *pnaudioindev, int *paudioindev, int *chindev, int *pnaudiooutdev, int *paudiooutdev, int *choutdev, - int *prate, int *padvance); + int *prate, int *padvance, int *callback); void sys_save_audio_params( int naudioindev, int *audioindev, int *chindev, int naudiooutdev, int *audiooutdev, int *choutdev, - int rate, int advance); + int rate, int advance, int callback); /* s_file.c */ diff --git a/pd/src/t_tkcmd.c b/pd/src/t_tkcmd.c index 61e66691..c32dc346 100644 --- a/pd/src/t_tkcmd.c +++ b/pd/src/t_tkcmd.c @@ -619,19 +619,11 @@ int Pdtcl_Init(Tcl_Interp *interp) { const char *argv = Tcl_GetVar(interp, "argv", 0); int portno, argno = 0; - /* argument passing seems to be different in MSW as opposed to - unix-likes. Here we check if we got sent a "port number" as an - argument. If so. we're to connect to a previously running pd (i.e., - pd got started first). If not, we start Pd from here. */ -#ifdef MSW if (argv && (portno = atoi(argv)) > 1) -#else - char *firstspace; - if (argv && (firstspace = strchr(argv, ' ')) && (portno = atoi(firstspace)) > 1) -#endif pdgui_setsock(portno); #ifdef DEBUGCONNECT - debugfd = fopen("/Users/msp/bratwurst", "w"); + pd_portno = portno; + debugfd = fopen("/tmp/bratwurst", "w"); fprintf(debugfd, "turning stderr back on\n"); fflush(debugfd); dup2(fileno(debugfd), 2); diff --git a/pd/src/u_main.tk b/pd/src/u_main.tk index b44aedab..2a736ca5 100644 --- a/pd/src/u_main.tk +++ b/pd/src/u_main.tk @@ -3479,7 +3479,7 @@ proc audio_apply {id} { global audio_outdev1 audio_outdev2 audio_outdev3 audio_outdev4 global audio_outchan1 audio_outchan2 audio_outchan3 audio_outchan4 global audio_outenable1 audio_outenable2 audio_outenable3 audio_outenable4 - global audio_sr audio_advance + global audio_sr audio_advance audio_callback pd [concat pd audio-dialog \ $audio_indev1 \ @@ -3500,6 +3500,7 @@ proc audio_apply {id} { [expr $audio_outchan4 * ( $audio_outenable4 ? 1 : -1 ) ]\ $audio_sr \ $audio_advance \ + $audio_callback \ \;] } @@ -3543,14 +3544,15 @@ proc audio_popup {name buttonname varname devlist} { proc pdtk_audio_dialog {id indev1 indev2 indev3 indev4 \ inchan1 inchan2 inchan3 inchan4 \ outdev1 outdev2 outdev3 outdev4 \ - outchan1 outchan2 outchan3 outchan4 sr advance multi longform} { + outchan1 outchan2 outchan3 outchan4 sr advance multi callback \ + longform} { global audio_indev1 audio_indev2 audio_indev3 audio_indev4 global audio_inchan1 audio_inchan2 audio_inchan3 audio_inchan4 global audio_inenable1 audio_inenable2 audio_inenable3 audio_inenable4 global audio_outdev1 audio_outdev2 audio_outdev3 audio_outdev4 global audio_outchan1 audio_outchan2 audio_outchan3 audio_outchan4 global audio_outenable1 audio_outenable2 audio_outenable3 audio_outenable4 - global audio_sr audio_advance + global audio_sr audio_advance audio_callback global audio_indevlist audio_outdevlist global pd_indev pd_outdev @@ -3584,7 +3586,7 @@ proc pdtk_audio_dialog {id indev1 indev2 indev3 indev4 \ set audio_sr $sr set audio_advance $advance - + set audio_callback $callback toplevel $id wm title $id {audio} wm protocol $id WM_DELETE_WINDOW [concat audio_cancel $id] @@ -3597,9 +3599,10 @@ proc pdtk_audio_dialog {id indev1 indev2 indev3 indev4 \ -command "audio_apply $id" button $id.buttonframe.ok -text {OK}\ -command "audio_ok $id" - pack $id.buttonframe.cancel -side left -expand 1 - pack $id.buttonframe.apply -side left -expand 1 - pack $id.buttonframe.ok -side left -expand 1 + button $id.buttonframe.save -text {Save all settings}\ + -command "audio_apply $id \; pd pd save-preferences \\;" + pack $id.buttonframe.cancel $id.buttonframe.apply $id.buttonframe.ok \ + $id.buttonframe.save -side left -expand 1 # sample rate and advance frame $id.srf @@ -3610,7 +3613,11 @@ proc pdtk_audio_dialog {id indev1 indev2 indev3 indev4 \ label $id.srf.l2 -text "delay (msec):" entry $id.srf.x2 -textvariable audio_advance -width 4 pack $id.srf.l1 $id.srf.x1 $id.srf.l2 $id.srf.x2 -side left - + if {$audio_callback >= 0} { + checkbutton $id.srf.x3 -variable audio_callback \ + -text {use callbacks} -anchor e + pack $id.srf.x3 -side left + } # input device 1 frame $id.in1f pack $id.in1f -side top diff --git a/pd/src/x_arithmetic.c b/pd/src/x_arithmetic.c index 224636d8..c55e3ea8 100644 --- a/pd/src/x_arithmetic.c +++ b/pd/src/x_arithmetic.c @@ -581,9 +581,6 @@ typedef struct _atan2 static void *atan2_new(void) { t_atan2 *x = (t_atan2 *)pd_new(atan2_class); - static int warned; - if (!warned) - post("warning: atan2 inlets switched from Pd 0.37 to 0.38"), warned=1; floatinlet_new(&x->x_ob, &x->x_f); x->x_f = 0; outlet_new(&x->x_ob, &s_float); -- cgit v1.2.1