From fda31a9ecca13f8f4856f7bb8070ba125b4330e2 Mon Sep 17 00:00:00 2001 From: Diane Bruce Date: Wed, 9 Aug 2006 21:22:08 +0000 Subject: [PATCH] - more obsolete dirs git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/trunk@253 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- portaudio-v19/pa_asio/ASIO-README.txt | 137 - .../pa_asio/Callback_adaptation_.pdf | Bin 50527 -> 0 bytes portaudio-v19/pa_asio/Pa_ASIO.pdf | Bin 50778 -> 0 bytes .../pa_asio/iasiothiscallresolver.cpp | 563 --- portaudio-v19/pa_asio/iasiothiscallresolver.h | 197 - portaudio-v19/pa_asio/pa_asio.cpp | 2958 -------------- portaudio-v19/pa_asio/pa_asio.h | 122 - portaudio-v19/pa_mac/pa_mac_hostapis.c | 79 - portaudio-v19/pa_tests/README.txt | 60 - portaudio-v19/pa_tests/debug_convert.c | 131 - portaudio-v19/pa_tests/debug_dither_calc.c | 55 - portaudio-v19/pa_tests/debug_dual.c | 183 - portaudio-v19/pa_tests/debug_multi_in.c | 179 - portaudio-v19/pa_tests/debug_multi_out.c | 144 - portaudio-v19/pa_tests/debug_record.c | 339 -- portaudio-v19/pa_tests/debug_record_reuse.c | 351 -- portaudio-v19/pa_tests/debug_sine.c | 192 - portaudio-v19/pa_tests/debug_sine_amp.c | 157 - portaudio-v19/pa_tests/debug_sine_formats.c | 202 - portaudio-v19/pa_tests/debug_srate.c | 265 -- portaudio-v19/pa_tests/debug_test1.c | 114 - portaudio-v19/pa_tests/pa_devs.c | 230 -- portaudio-v19/pa_tests/pa_fuzz.c | 168 - portaudio-v19/pa_tests/pa_minlat.c | 187 - portaudio-v19/pa_tests/paqa_devs.c | 347 -- portaudio-v19/pa_tests/paqa_errs.c | 380 -- portaudio-v19/pa_tests/patest1.c | 192 - portaudio-v19/pa_tests/patest_buffer.c | 180 - portaudio-v19/pa_tests/patest_callbackstop.c | 221 - portaudio-v19/pa_tests/patest_clip.c | 178 - portaudio-v19/pa_tests/patest_dither.c | 178 - portaudio-v19/pa_tests/patest_hang.c | 152 - portaudio-v19/pa_tests/patest_in_overflow.c | 224 - portaudio-v19/pa_tests/patest_latency.c | 182 - portaudio-v19/pa_tests/patest_leftright.c | 173 - portaudio-v19/pa_tests/patest_longsine.c | 140 - portaudio-v19/pa_tests/patest_many.c | 198 - portaudio-v19/pa_tests/patest_maxsines.c | 204 - portaudio-v19/pa_tests/patest_mono.c | 145 - .../patest_mono_asio_channel_select.c | 155 - portaudio-v19/pa_tests/patest_multi_sine.c | 194 - portaudio-v19/pa_tests/patest_out_underflow.c | 217 - portaudio-v19/pa_tests/patest_pink.c | 269 -- portaudio-v19/pa_tests/patest_prime.c | 223 - portaudio-v19/pa_tests/patest_read_record.c | 229 -- portaudio-v19/pa_tests/patest_record.c | 337 -- portaudio-v19/pa_tests/patest_ringmix.c | 79 - portaudio-v19/pa_tests/patest_saw.c | 125 - portaudio-v19/pa_tests/patest_sine.c | 151 - portaudio-v19/pa_tests/patest_sine8.c | 212 - portaudio-v19/pa_tests/patest_sine_formats.c | 196 - portaudio-v19/pa_tests/patest_sine_time.c | 208 - portaudio-v19/pa_tests/patest_start_stop.c | 162 - portaudio-v19/pa_tests/patest_stop.c | 313 -- portaudio-v19/pa_tests/patest_stop_playout.c | 432 -- portaudio-v19/pa_tests/patest_sync.c | 259 -- portaudio-v19/pa_tests/patest_toomanysines.c | 173 - portaudio-v19/pa_tests/patest_two_rates.c | 168 - portaudio-v19/pa_tests/patest_underflow.c | 151 - portaudio-v19/pa_tests/patest_wire.c | 284 -- portaudio-v19/pa_tests/patest_write_sine.c | 147 - portaudio-v19/pa_unix_oss/low_latency_tip.txt | Bin 3111 -> 0 bytes portaudio-v19/pa_unix_oss/pa_unix_oss.c | 1923 --------- portaudio-v19/pa_unix_oss/pa_unix_oss.c.orig | 1922 --------- portaudio-v19/pa_unix_oss/recplay.c | 114 - portaudio-v19/pa_win_ds/dsound_wrapper.c | 616 --- portaudio-v19/pa_win_ds/dsound_wrapper.h | 130 - portaudio-v19/pa_win_ds/pa_win_ds.c | 1857 --------- portaudio-v19/pa_win_wdmks/pa_win_wdmks.c | 3269 --------------- portaudio-v19/pa_win_wdmks/readme.txt | 82 - portaudio-v19/pa_win_wmme/pa_win_wmme.c | 3623 ----------------- portaudio-v19/pa_win_wmme/pa_win_wmme.h | 160 - 72 files changed, 28487 deletions(-) delete mode 100644 portaudio-v19/pa_asio/ASIO-README.txt delete mode 100644 portaudio-v19/pa_asio/Callback_adaptation_.pdf delete mode 100644 portaudio-v19/pa_asio/Pa_ASIO.pdf delete mode 100644 portaudio-v19/pa_asio/iasiothiscallresolver.cpp delete mode 100644 portaudio-v19/pa_asio/iasiothiscallresolver.h delete mode 100644 portaudio-v19/pa_asio/pa_asio.cpp delete mode 100644 portaudio-v19/pa_asio/pa_asio.h delete mode 100644 portaudio-v19/pa_mac/pa_mac_hostapis.c delete mode 100644 portaudio-v19/pa_tests/README.txt delete mode 100644 portaudio-v19/pa_tests/debug_convert.c delete mode 100644 portaudio-v19/pa_tests/debug_dither_calc.c delete mode 100644 portaudio-v19/pa_tests/debug_dual.c delete mode 100644 portaudio-v19/pa_tests/debug_multi_in.c delete mode 100644 portaudio-v19/pa_tests/debug_multi_out.c delete mode 100644 portaudio-v19/pa_tests/debug_record.c delete mode 100644 portaudio-v19/pa_tests/debug_record_reuse.c delete mode 100644 portaudio-v19/pa_tests/debug_sine.c delete mode 100644 portaudio-v19/pa_tests/debug_sine_amp.c delete mode 100644 portaudio-v19/pa_tests/debug_sine_formats.c delete mode 100644 portaudio-v19/pa_tests/debug_srate.c delete mode 100644 portaudio-v19/pa_tests/debug_test1.c delete mode 100644 portaudio-v19/pa_tests/pa_devs.c delete mode 100644 portaudio-v19/pa_tests/pa_fuzz.c delete mode 100644 portaudio-v19/pa_tests/pa_minlat.c delete mode 100644 portaudio-v19/pa_tests/paqa_devs.c delete mode 100644 portaudio-v19/pa_tests/paqa_errs.c delete mode 100644 portaudio-v19/pa_tests/patest1.c delete mode 100644 portaudio-v19/pa_tests/patest_buffer.c delete mode 100644 portaudio-v19/pa_tests/patest_callbackstop.c delete mode 100644 portaudio-v19/pa_tests/patest_clip.c delete mode 100644 portaudio-v19/pa_tests/patest_dither.c delete mode 100644 portaudio-v19/pa_tests/patest_hang.c delete mode 100644 portaudio-v19/pa_tests/patest_in_overflow.c delete mode 100644 portaudio-v19/pa_tests/patest_latency.c delete mode 100644 portaudio-v19/pa_tests/patest_leftright.c delete mode 100644 portaudio-v19/pa_tests/patest_longsine.c delete mode 100644 portaudio-v19/pa_tests/patest_many.c delete mode 100644 portaudio-v19/pa_tests/patest_maxsines.c delete mode 100644 portaudio-v19/pa_tests/patest_mono.c delete mode 100644 portaudio-v19/pa_tests/patest_mono_asio_channel_select.c delete mode 100644 portaudio-v19/pa_tests/patest_multi_sine.c delete mode 100644 portaudio-v19/pa_tests/patest_out_underflow.c delete mode 100644 portaudio-v19/pa_tests/patest_pink.c delete mode 100644 portaudio-v19/pa_tests/patest_prime.c delete mode 100644 portaudio-v19/pa_tests/patest_read_record.c delete mode 100644 portaudio-v19/pa_tests/patest_record.c delete mode 100644 portaudio-v19/pa_tests/patest_ringmix.c delete mode 100644 portaudio-v19/pa_tests/patest_saw.c delete mode 100644 portaudio-v19/pa_tests/patest_sine.c delete mode 100644 portaudio-v19/pa_tests/patest_sine8.c delete mode 100644 portaudio-v19/pa_tests/patest_sine_formats.c delete mode 100644 portaudio-v19/pa_tests/patest_sine_time.c delete mode 100644 portaudio-v19/pa_tests/patest_start_stop.c delete mode 100644 portaudio-v19/pa_tests/patest_stop.c delete mode 100644 portaudio-v19/pa_tests/patest_stop_playout.c delete mode 100644 portaudio-v19/pa_tests/patest_sync.c delete mode 100644 portaudio-v19/pa_tests/patest_toomanysines.c delete mode 100644 portaudio-v19/pa_tests/patest_two_rates.c delete mode 100644 portaudio-v19/pa_tests/patest_underflow.c delete mode 100644 portaudio-v19/pa_tests/patest_wire.c delete mode 100644 portaudio-v19/pa_tests/patest_write_sine.c delete mode 100644 portaudio-v19/pa_unix_oss/low_latency_tip.txt delete mode 100644 portaudio-v19/pa_unix_oss/pa_unix_oss.c delete mode 100644 portaudio-v19/pa_unix_oss/pa_unix_oss.c.orig delete mode 100644 portaudio-v19/pa_unix_oss/recplay.c delete mode 100644 portaudio-v19/pa_win_ds/dsound_wrapper.c delete mode 100644 portaudio-v19/pa_win_ds/dsound_wrapper.h delete mode 100644 portaudio-v19/pa_win_ds/pa_win_ds.c delete mode 100644 portaudio-v19/pa_win_wdmks/pa_win_wdmks.c delete mode 100644 portaudio-v19/pa_win_wdmks/readme.txt delete mode 100644 portaudio-v19/pa_win_wmme/pa_win_wmme.c delete mode 100644 portaudio-v19/pa_win_wmme/pa_win_wmme.h diff --git a/portaudio-v19/pa_asio/ASIO-README.txt b/portaudio-v19/pa_asio/ASIO-README.txt deleted file mode 100644 index 9fb74ae16..000000000 --- a/portaudio-v19/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/portaudio-v19/pa_asio/Callback_adaptation_.pdf b/portaudio-v19/pa_asio/Callback_adaptation_.pdf deleted file mode 100644 index 76bf67863524626aa079d74629c9dd9f720b0b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50527 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 - - 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/portaudio-v19/pa_asio/iasiothiscallresolver.h b/portaudio-v19/pa_asio/iasiothiscallresolver.h deleted file mode 100644 index 2ecfed799..000000000 --- a/portaudio-v19/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/portaudio-v19/pa_asio/pa_asio.cpp b/portaudio-v19/pa_asio/pa_asio.cpp deleted file mode 100644 index 6593e7277..000000000 --- a/portaudio-v19/pa_asio/pa_asio.cpp +++ /dev/null @@ -1,2958 +0,0 @@ -/* - * $Id$ - * 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/portaudio-v19/pa_asio/pa_asio.h b/portaudio-v19/pa_asio/pa_asio.h deleted file mode 100644 index 573930572..000000000 --- a/portaudio-v19/pa_asio/pa_asio.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef PA_ASIO_H -#define PA_ASIO_H -/* - * $Id$ - * PortAudio Portable Real-Time Audio Library - * ASIO specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/** @file - @brief ASIO-specific PortAudio API extension header file. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** Retrieve legal latency settings for the specificed device, in samples. - - @param device The global index of the device about which the query is being made. - @param minLatency A pointer to the location which will recieve the minimum latency value. - @param maxLatency A pointer to the location which will recieve the maximum latency value. - @param preferredLatency A pointer to the location which will recieve the preferred latency value. - @param granularity A pointer to the location which will recieve the granularity. This value - determines which values between minLatency and maxLatency are available. ie the step size, - if granularity is -1 then available latency settings are powers of two. - - @see ASIOGetBufferSize in the ASIO SDK. - - @todo This function should have a better name, any suggestions? -*/ -PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, - long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ); - - -/** Display the ASIO control panel for the specified device. - - @param device The global index of the device whose control panel is to be displayed. - @param systemSpecific On Windows, the calling application's main window handle, - on Macintosh this value should be zero. -*/ -PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ); - - - - -/** Retrieve a pointer to a string containing the name of the specified - input channel. The string is valid until Pa_Terminate is called. - - The string will be no longer than 32 characters including the null terminator. -*/ -PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ); - - -/** Retrieve a pointer to a string containing the name of the specified - input channel. The string is valid until Pa_Terminate is called. - - The string will be no longer than 32 characters including the null terminator. -*/ -PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ); - - -#define paAsioUseChannelSelectors (0x01) - -typedef struct PaAsioStreamInfo{ - unsigned long size; /**< sizeof(PaAsioStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paASIO */ - unsigned long version; /**< 1 */ - - unsigned long flags; - - /* Support for opening only specific channels of an ASIO device. - If the paAsioUseChannelSelectors flag is set, channelSelectors is a - pointer to an array of integers specifying the device channels to use. - When used, the length of the channelSelectors array must match the - corresponding channelCount parameter to Pa_OpenStream() otherwise a - crash may result. - The values in the selectors array must specify channels within the - range of supported channels for the device or paInvalidChannelCount will - result. - */ - int *channelSelectors; -}PaAsioStreamInfo; - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_ASIO_H */ diff --git a/portaudio-v19/pa_mac/pa_mac_hostapis.c b/portaudio-v19/pa_mac/pa_mac_hostapis.c deleted file mode 100644 index f8306c302..000000000 --- a/portaudio-v19/pa_mac/pa_mac_hostapis.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * $Id$ - * Portable Audio I/O Library Windows initialization table - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** @file -Mac OS host API initialization function table. -*/ - - -#include "pa_hostapi.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacSm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - PaError PaMacAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -PaUtilHostApiInitializer *paHostApiInitializers[] = -{ -#ifdef PA_USE_COREAUDIO - PaMacCore_Initialize, -#endif - -#ifdef PA_USE_SM - PaMacSm_Initialize, -#endif - -#ifdef PA_USE_JACK - PaJack_Initialize, -#endif - -#ifdef PA_USE_ASIO - PaMacAsio_Initialize, -#endif - - PaSkeleton_Initialize, /* just for testing */ - - 0 /* NULL terminated array */ -}; - - -int paDefaultHostApiIndex = 0; diff --git a/portaudio-v19/pa_tests/README.txt b/portaudio-v19/pa_tests/README.txt deleted file mode 100644 index f3a0b8226..000000000 --- a/portaudio-v19/pa_tests/README.txt +++ /dev/null @@ -1,60 +0,0 @@ -This directory contains various programs to test PortAudio. The files -named patest_* are tests, the files named debug_* are just scratch -files that may or may not work. - -All following tests are up to date with the V19 API. They should all compile -(without any warnings on GCC 3.3). Note that this does not necissarily mean that -the tests pass, just that they compile. - - x- paqa_devs.c - x- paqa_errs.c (needs reviewing) - x- patest1.c - x- patest_buffer.c - x- patest_callbackstop.c - x- patest_clip.c (last test fails, dither doesn't currently force clip in V19) - x- patest_dither.c - x- patest_hang.c - x- patest_latency.c - x- patest_leftright.c - x- patest_longsine.c - x- patest_many.c - x- patest_maxsines.c - o- patest_mono.c - x- patest_multi_sine.c - x- patest_pink.c - x- patest_prime.c - x- patest_read_record.c - x- patest_record.c - x- patest_ringmix.c - x- patest_saw.c - x- patest_sine.c - x- patest_sine8.c - x- patest_sine_formats.c - x- patest_sine_time.c - x- patest_start_stop.c - x- patest_stop.c - x- patest_sync.c - x- patest_toomanysines.c - o- patest_two_rates.c - x- patest_underflow.c - x- patest_wire.c - x- patest_write_sine.c - x- pa_devs.c - x- pa_fuzz.c - x- pa_minlat.c - -The debug_ files are still in V18 format and may need some V19 adaption. -Feel free to fix them, most simply require adjusting to the new API. - -o- pa_tests/debug_convert.c -o- pa_tests/debug_dither_calc.c -o- pa_tests/debug_dual.c -o- pa_tests/debug_multi_in.c -o- pa_tests/debug_multi_out.c -o- pa_tests/debug_record.c -o- pa_tests/debug_record_reuse.c -o- pa_tests/debug_sine.c -o- pa_tests/debug_sine_amp.c -o- pa_tests/debug_sine_formats.c -o- pa_tests/debug_srate.c -o- pa_tests/debug_test1.c diff --git a/portaudio-v19/pa_tests/debug_convert.c b/portaudio-v19/pa_tests/debug_convert.c deleted file mode 100644 index a17b556e4..000000000 --- a/portaudio-v19/pa_tests/debug_convert.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * $Id$ - * Convert tagged values. - * - * Author: Phil Burk - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -//#define OUTPUT_DEVICE (11) -#define NUM_SECONDS (8) -#define SLEEP_DUR (800) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) - -#define NUM_BUFFERS (0) - -typedef struct -{ - unsigned int framesToGo; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - short *out = (short*)outputBuffer; - int i; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - if( data->framesToGo < framesPerBuffer ) finished = 1; - - for( i=0; i -#include -#include "portaudio.h" -#include "pa_host.h" - -/*******************************************************************/ -int main(void); -int main(void) -{ - long max,min; - int i; - - for( i=0; i<10000; i++ ) - { - long dither = PaConvert_TriangularDither(); - // printf("dither = 0x%08X\n", dither ); - if( dither < min ) min = dither; - else if( dither > max ) max = dither; - } - printf("min = 0x%08X = %d, max = 0x%08X = %d\n", min, min, max, max ); -} diff --git a/portaudio-v19/pa_tests/debug_dual.c b/portaudio-v19/pa_tests/debug_dual.c deleted file mode 100644 index 35ba032d0..000000000 --- a/portaudio-v19/pa_tests/debug_dual.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * $Id$ - * debug_dual.c - * Try to open TWO streams on separate cards. - * Play a sine sweep using the Portable Audio api for several seconds. - * Hacked test for debugging PA. - * - * Author: Phil Burk - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#define DEV_ID_1 (13) -#define DEV_ID_2 (15) -#define NUM_SECONDS (8) -#define SLEEP_DUR (800) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#if 0 -#define MIN_LATENCY_MSEC (200) -#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) -#else -#define NUM_BUFFERS (0) -#endif -#define MIN_FREQ (100.0f) -#define MAX_FREQ (4000.0f) -#define FREQ_SCALAR (1.00002f) -#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (400) -typedef struct -{ - float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation - float phase_increment; - float left_phase; - float right_phase; -} -paTestData; -/* Convert phase between and 1.0 to sine value - * using linear interpolation. - */ -float LookupSine( paTestData *data, float phase ); -float LookupSine( paTestData *data, float phase ) -{ - float fIndex = phase*TABLE_SIZE; - int index = (int) fIndex; - float fract = fIndex - index; - float lo = data->sine[index]; - float hi = data->sine[index+1]; - float val = lo + fract*(hi-lo); - return val; -} -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - - for( i=0; ileft_phase); /* left */ - *out++ = LookupSine(data, data->right_phase); /* right */ - data->left_phase += data->phase_increment; - if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; - data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ - if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; - /* sweep frequency then start over. */ - data->phase_increment *= FREQ_SCALAR; - if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); - } - return 0; -} - -PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, - paTestData *data ); -/*******************************************************************/ -int main(void); -int main(void) -{ - PortAudioStream *stream1, *stream2; - PaError err; - paTestData DATA1, DATA2; - printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS ); - err = Pa_Initialize(); - if( err != paNoError ) goto error; - err = TestStart( &stream1, DEV_ID_1, &DATA1 ); - if( err != paNoError ) goto error; - err = TestStart( &stream2, DEV_ID_2, &DATA2 ); - if( err != paNoError ) goto error; - printf("Hit ENTER\n"); - getchar(); - err = Pa_StopStream( stream1 ); - if( err != paNoError ) goto error; - err = Pa_StopStream( stream2 ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} -PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data ) -{ - PortAudioStream *stream; - PaError err; - int i; - /* initialise sinusoidal wavetable */ - for( i=0; isine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); - } - data->sine[TABLE_SIZE] = data->sine[0]; // set guard point - data->left_phase = data->right_phase = 0.0; - data->phase_increment = CalcPhaseIncrement(MIN_FREQ); - printf("PortAudio Test: output device = %d\n", devID ); - err = Pa_OpenStream( - &stream, - paNoDevice, - 0, /* no input */ - paFloat32, /* 32 bit floating point input */ - NULL, - devID, - 2, /* stereo output */ - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ - paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - data ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - *streamPtr = stream; - return 0; -error: - return err; -} diff --git a/portaudio-v19/pa_tests/debug_multi_in.c b/portaudio-v19/pa_tests/debug_multi_in.c deleted file mode 100644 index def729c1b..000000000 --- a/portaudio-v19/pa_tests/debug_multi_in.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * $Id$ - * debug_multi_in.c - * Pass output from each of multiple channels - * to a stereo output using the Portable Audio api. - * Hacked test for debugging PA. - * - * Author: Phil Burk http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include -#include "portaudio.h" -//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec") -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -//#define OUTPUT_DEVICE (18) -#define SAMPLE_RATE (22050) -#define FRAMES_PER_BUFFER (256) -#define MIN_LATENCY_MSEC (400) -#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -typedef struct -{ - int liveChannel; - int numChannels; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - float *in = (float*)inputBuffer; - int i; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - if( in == NULL ) return 0; - for( i=0; i<(int)framesPerBuffer; i++ ) - { - /* Copy one channel of input to output. */ - *out++ = in[data->liveChannel]; - *out++ = in[data->liveChannel]; - in += data->numChannels; - } - return 0; -} -/*******************************************************************/ -int PaFindDeviceByName( const char *name ) -{ - int i; - int numDevices; - const PaDeviceInfo *pdi; - int len = strlen( name ); - PaDeviceID result = paNoDevice; - numDevices = Pa_CountDevices(); - for( i=0; iname, len ) == 0 ) - { - result = i; - break; - } - } - return result; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PortAudioStream *stream; - PaError err; - paTestData data; - int i; - PaDeviceID inputDevice; - const PaDeviceInfo *pdi; - printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS ); - data.liveChannel = 0; - err = Pa_Initialize(); - if( err != paNoError ) goto error; -#ifdef INPUT_DEVICE_NAME - printf("Try to use device: %s\n", INPUT_DEVICE_NAME ); - inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME); - if( inputDevice == paNoDevice ) - { - printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME ); - inputDevice = Pa_GetDefaultInputDeviceID(); - } -#else - printf("Using default input device.\n"); - inputDevice = Pa_GetDefaultInputDeviceID(); -#endif - pdi = Pa_GetDeviceInfo( inputDevice ); - if( pdi == NULL ) - { - printf("Could not get device info!\n"); - goto error; - } - data.numChannels = pdi->maxInputChannels; - printf("Input Device name is %s\n", pdi->name ); - printf("Input Device has %d channels.\n", pdi->maxInputChannels); - err = Pa_OpenStream( - &stream, - inputDevice, - pdi->maxInputChannels, - paFloat32, /* 32 bit floating point input */ - NULL, - OUTPUT_DEVICE, - 2, - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - data.liveChannel = 0; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - for( i=0; i -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#define FREQ_INCR (300.0 / SAMPLE_RATE) -#define MAX_CHANNELS (64) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -typedef struct -{ - int numChannels; - double phases[MAX_CHANNELS]; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - int frameIndex, channelIndex; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) - { - for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) - { - /* Output sine wave on every channel. */ - *out++ = (float) sin(data->phases[channelIndex]); - - /* Play each channel at a higher frequency. */ - data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); - if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); - } - } - - return 0; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PortAudioStream *stream; - PaError err; - const PaDeviceInfo *pdi; - paTestData data = {0}; - printf("PortAudio Test: output sine wave on each channel.\n" ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); - data.numChannels = pdi->maxOutputChannels; - if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; - printf("Number of Channels = %d\n", data.numChannels ); - - err = Pa_OpenStream( - &stream, - paNoDevice, /* default input device */ - 0, /* no input */ - paFloat32, /* 32 bit floating point input */ - NULL, - OUTPUT_DEVICE, - data.numChannels, - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Hit ENTER to stop sound.\n"); - fflush(stdout); - getchar(); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - Pa_CloseStream( stream ); - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/debug_record.c b/portaudio-v19/pa_tests/debug_record.c deleted file mode 100644 index 57b11f581..000000000 --- a/portaudio-v19/pa_tests/debug_record.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * $Id$ - * debug_record.c - * Record input into an array. - * Save array to a file. - * Based on patest_record.c but with various ugly debug hacks thrown in. - * - * Author: Phil Burk http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include -#include "portaudio.h" -#define SAMPLE_RATE (22050) -#define NUM_SECONDS (10) -#define SLEEP_DUR_MSEC (200) -#define FRAMES_PER_BUFFER (1<<10) -#define NUM_REC_BUFS (0) - -#if 1 -#define PA_SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#else -#define PA_SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#endif - -typedef struct -{ - long frameIndex; /* Index into sample array. */ - long maxFrameIndex; - long samplesPerFrame; - long numSamples; - SAMPLE *recordedSamples; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int recordCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE *rptr = (SAMPLE*)inputBuffer; - SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; - long framesToCalc; - unsigned long i; - int finished; - unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; - - (void) outputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( framesLeft < framesPerBuffer ) - { - framesToCalc = framesLeft; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - finished = 0; - } - if( inputBuffer == NULL ) - { - for( i=0; iframeIndex += framesToCalc; - return finished; -} -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int playCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; - SAMPLE *wptr = (SAMPLE*)outputBuffer; - unsigned long i; - int finished; - unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; - if( outputBuffer == NULL ) return 0; - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( framesLeft < framesPerBuffer ) - { - /* final buffer... */ - for( i=0; iframeIndex += framesLeft; - finished = 1; - } - else - { - for( i=0; iframeIndex += framesPerBuffer; - finished = 0; - } - return finished; -} - -/****************************************************************/ -PaError TestRecording( paTestData *dataPtr ) -{ - PortAudioStream *stream; - PaError err; - int i; - - /* Record some audio. */ - err = Pa_OpenStream( - &stream, - Pa_GetDefaultInputDeviceID(), - dataPtr->samplesPerFrame, /* stereo input */ - PA_SAMPLE_TYPE, - NULL, - paNoDevice, - 0, - PA_SAMPLE_TYPE, - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - recordCallback, - dataPtr ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Now recording!\n"); fflush(stdout); - for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) - { - if( Pa_StreamActive( stream ) <= 0) - { - printf("Stream inactive!\n"); - break; - } - if( dataPtr->maxFrameIndex <= dataPtr->frameIndex ) - { - printf("Buffer recording complete.\n"); - break; - } - Pa_Sleep(100); - printf("index = %d\n", dataPtr->frameIndex ); fflush(stdout); - } - - printf("Finished loop. Close stream.\n"); fflush(stdout); - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - printf("Done.\n"); fflush(stdout); - { - SAMPLE max = 0; - SAMPLE posVal; - int i; - for( i=0; inumSamples; i++ ) - { - posVal = dataPtr->recordedSamples[i]; - if( posVal < 0 ) posVal = -posVal; - if( posVal > max ) max = posVal; - } - printf("Largest recorded sample = %d\n", max ); - } - /* Write recorded data to a file. */ -#if 0 - { - FILE *fid; - fid = fopen("recorded.raw", "wb"); - if( fid == NULL ) - { - printf("Could not open file."); - } - else - { - fwrite( dataPtr->recordedSamples, dataPtr->samplesPerFrame * sizeof(SAMPLE), totalFrames, fid ); - fclose( fid ); - printf("Wrote data to 'recorded.raw'\n"); - } - } -#endif - -error: - return err; -} - -/****************************************************************/ -PaError TestPlayback( paTestData *dataPtr ) -{ - PortAudioStream *stream; - PaError err; - int i; - - /* Playback recorded data. */ - dataPtr->frameIndex = 0; - printf("Begin playback.\n"); fflush(stdout); - err = Pa_OpenStream( - &stream, - paNoDevice, - 0, /* NO input */ - PA_SAMPLE_TYPE, - NULL, - Pa_GetDefaultOutputDeviceID(), - dataPtr->samplesPerFrame, /* stereo output */ - PA_SAMPLE_TYPE, - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - playCallback, - dataPtr ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Waiting for playback to finish.\n"); fflush(stdout); - for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) - { - Pa_Sleep(100); - printf("index = %d\n", dataPtr->frameIndex ); - } - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - -error: - return err; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - paTestData data; - long totalFrames; - long numBytes; - long i; - printf("patest_record.c\n"); fflush(stdout); - - data.frameIndex = 0; - data.samplesPerFrame = 2; - data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE; - - printf("totalFrames = %d\n", totalFrames ); fflush(stdout); - data.numSamples = totalFrames * data.samplesPerFrame; - - numBytes = data.numSamples * sizeof(SAMPLE); - data.recordedSamples = (SAMPLE *) malloc( numBytes ); - if( data.recordedSamples == NULL ) - { - printf("Could not allocate record array.\n"); - exit(1); - } - for( i=0; i -#include -#include -#include "portaudio.h" -#define SAMPLE_RATE (22050) -#define NUM_SECONDS (4) -#define SLEEP_DUR_MSEC (200) -#define FRAMES_PER_BUFFER (256) -#define NUM_REC_BUFS (0) - -#if 1 -#define PA_SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#else -#define PA_SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#endif - -typedef struct -{ - long frameIndex; /* Index into sample array. */ - long maxFrameIndex; - long samplesPerFrame; - long numSamples; - PortAudioStream *outputStream; - PortAudioStream *inputStream; - SAMPLE *recordedSamples; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int recordCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE *rptr = (SAMPLE*)inputBuffer; - SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; - long framesToCalc; - unsigned long i; - int finished; - unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; - - (void) outputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( framesLeft < framesPerBuffer ) - { - framesToCalc = framesLeft; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - finished = 0; - } - if( inputBuffer == NULL ) - { - for( i=0; iframeIndex += framesToCalc; - return finished; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int playCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; - SAMPLE *wptr = (SAMPLE*)outputBuffer; - unsigned long i; - int finished; - unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; - if( outputBuffer == NULL ) return 0; - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( framesLeft < framesPerBuffer ) - { - /* final buffer... */ - for( i=0; iframeIndex += framesLeft; - finished = 1; - } - else - { - for( i=0; iframeIndex += framesPerBuffer; - finished = 0; - } - return finished; -} - -/****************************************************************/ -PaError TestRecording( paTestData *dataPtr ) -{ - PaError err; - int i; - int lastIndex = 0; - -/* Open input stream if not already open. */ - if( dataPtr->inputStream == NULL ) - { - /* Record some audio. */ - err = Pa_OpenStream( - &dataPtr->inputStream, - Pa_GetDefaultInputDeviceID(), - dataPtr->samplesPerFrame, /* stereo input */ - PA_SAMPLE_TYPE, - NULL, - paNoDevice, - 0, - PA_SAMPLE_TYPE, - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - recordCallback, - dataPtr ); - if( err != paNoError ) goto error; - } - - dataPtr->frameIndex = 0; - - err = Pa_StartStream( dataPtr->inputStream ); - if( err != paNoError ) goto error; - - printf("Now recording!\n"); fflush(stdout); - for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) - { - int frameIndex, delta; - Pa_Sleep(SLEEP_DUR_MSEC); - - frameIndex = dataPtr->frameIndex; - if( Pa_StreamActive( dataPtr->inputStream ) <= 0) - { - printf("Stream inactive!\n"); - break; - } - if( dataPtr->maxFrameIndex <= frameIndex ) - { - printf("Buffer recording complete.\n"); - break; - } - - delta = frameIndex - lastIndex; - lastIndex = frameIndex; - printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout); - } - - err = Pa_StopStream( dataPtr->inputStream ); - if( err != paNoError ) goto error; - - printf("Done.\n"); fflush(stdout); - -error: - return err; -} - -/****************************************************************/ -PaError TestPlayback( paTestData *dataPtr ) -{ - PaError err; - int i; - int lastIndex = 0; - - /* Playback recorded data. */ - dataPtr->frameIndex = 0; - printf("Begin playback.\n"); fflush(stdout); - -/* Open output stream if not already open. */ - if( dataPtr->outputStream == NULL ) - { - err = Pa_OpenStream( - &dataPtr->outputStream, - paNoDevice, - 0, /* NO input */ - PA_SAMPLE_TYPE, - NULL, - Pa_GetDefaultOutputDeviceID(), - dataPtr->samplesPerFrame, /* stereo output */ - PA_SAMPLE_TYPE, - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - playCallback, - dataPtr ); - if( err != paNoError ) goto error; - } - - err = Pa_StartStream( dataPtr->outputStream ); - if( err != paNoError ) goto error; - - printf("Waiting for playback to finish.\n"); fflush(stdout); - for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) - { - int frameIndex, delta; - Pa_Sleep(SLEEP_DUR_MSEC); - frameIndex = dataPtr->frameIndex; - delta = frameIndex - lastIndex; - lastIndex = frameIndex; - printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout); - } - - err = Pa_StopStream( dataPtr->outputStream ); - if( err != paNoError ) goto error; - -error: - return err; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - paTestData data = { 0 }; - long totalFrames; - long numBytes; - long i; - printf("patest_record.c\n"); fflush(stdout); - -/* Set up test data structure and sample array. */ - data.frameIndex = 0; - data.samplesPerFrame = 2; - data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE; - - printf("totalFrames = %d\n", totalFrames ); fflush(stdout); - data.numSamples = totalFrames * data.samplesPerFrame; - - numBytes = data.numSamples * sizeof(SAMPLE); - data.recordedSamples = (SAMPLE *) malloc( numBytes ); - if( data.recordedSamples == NULL ) - { - printf("Could not allocate record array.\n"); - exit(1); - } - for( i=0; i - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -//#define OUTPUT_DEVICE (11) -#define NUM_SECONDS (8) -#define SLEEP_DUR (800) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#if 0 -#define MIN_LATENCY_MSEC (200) -#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) -#else -#define NUM_BUFFERS (0) -#endif -#define MIN_FREQ (100.0f) -#define MAX_FREQ (4000.0f) -#define FREQ_SCALAR (1.00002f) -#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (400) -typedef struct -{ - float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation - float phase_increment; - float left_phase; - float right_phase; - unsigned int framesToGo; -} -paTestData; -/* Convert phase between and 1.0 to sine value - * using linear interpolation. - */ -float LookupSine( paTestData *data, float phase ); -float LookupSine( paTestData *data, float phase ) -{ - float fIndex = phase*TABLE_SIZE; - int index = (int) fIndex; - float fract = fIndex - index; - float lo = data->sine[index]; - float hi = data->sine[index+1]; - float val = lo + fract*(hi-lo); - return val; -} -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - int framesToCalc; - int i; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - if( data->framesToGo < framesPerBuffer ) - { - framesToCalc = data->framesToGo; - data->framesToGo = 0; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - data->framesToGo -= framesPerBuffer; - } - - for( i=0; ileft_phase); /* left */ - *out++ = LookupSine(data, data->right_phase); /* right */ - data->left_phase += data->phase_increment; - if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; - data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ - if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; - /* sweep frequency then start over. */ - data->phase_increment *= FREQ_SCALAR; - if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); - } - /* zero remainder of final buffer */ - for( ; i<(int)framesPerBuffer; i++ ) - { - *out++ = 0; /* left */ - *out++ = 0; /* right */ - } - return finished; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PortAudioStream *stream; - PaError err; - paTestData data; - int i; - int totalSamps; - printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS ); - /* initialise sinusoidal wavetable */ - for( i=0; i -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#define FREQ_INCR (300.0 / SAMPLE_RATE) -#define MAX_CHANNELS (64) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -typedef struct -{ - int numChannels; - double phases[MAX_CHANNELS]; - float amplitude; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - int frameIndex, channelIndex; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) - { - for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) - { - /* Output sine wave on every channel. */ - *out++ = (float) ( data->amplitude * sin(data->phases[channelIndex]) ); - - /* Play each channel at a higher frequency. */ - data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); - if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); - } - } - - return 0; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - char pad[256]; - PortAudioStream *stream; - PaError err; - const PaDeviceInfo *pdi; - paTestData data = {0}; - printf("PortAudio Test: output sine wave on each channel.\n" ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); - data.numChannels = pdi->maxOutputChannels; - if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; - printf("Number of Channels = %d\n", data.numChannels ); - data.amplitude = 1.0; - - err = Pa_OpenStream( - &stream, - paNoDevice, /* default input device */ - 0, /* no input */ - paFloat32, /* 32 bit floating point input */ - NULL, - OUTPUT_DEVICE, - data.numChannels, - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - do - { - printf("Current amplitude = %f\n", data.amplitude ); - printf("Enter new amplitude or 'q' to quit.\n"); - fflush(stdout); - gets( pad ); - if( pad[0] != 'q' ) - { - // I tried to use atof but it seems to be broken on Mac OS X 10.1 - float amp; - sscanf( pad, "%f", & ); - data.amplitude = amp; - } - } while( pad[0] != 'q' ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - Pa_CloseStream( stream ); - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/debug_sine_formats.c b/portaudio-v19/pa_tests/debug_sine_formats.c deleted file mode 100644 index a1fce567c..000000000 --- a/portaudio-v19/pa_tests/debug_sine_formats.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * $Id$ - * patest_sine_formats.c - * Play a sine wave using the Portable Audio api for several seconds. - * Test various data formats. - * - * Author: Phil Burk - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (10) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) - -#define LEFT_FREQ (SAMPLE_RATE/512.0) /* So we hit 1.0 */ -#define RIGHT_FREQ (500.0) - -#define AMPLITUDE (1.0) - -/* Select ONE format for testing. */ -#define TEST_UINT8 (1) -#define TEST_INT8 (0) -#define TEST_INT16 (0) -#define TEST_FLOAT32 (0) - -#if TEST_UINT8 -#define TEST_FORMAT paUInt8 -typedef unsigned char SAMPLE_t; -#define SAMPLE_ZERO (0x80) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) -#define FORMAT_NAME "Unsigned 8 Bit" - -#elif TEST_INT8 -#define TEST_FORMAT paInt8 -typedef char SAMPLE_t; -#define SAMPLE_ZERO (0) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) -#define FORMAT_NAME "Signed 8 Bit" - -#elif TEST_INT16 -#define TEST_FORMAT paInt16 -typedef short SAMPLE_t; -#define SAMPLE_ZERO (0) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x))) -#define FORMAT_NAME "Signed 16 Bit" - -#elif TEST_FLOAT32 -#define TEST_FORMAT paFloat32 -typedef float SAMPLE_t; -#define SAMPLE_ZERO (0.0) -#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x)) -#define FORMAT_NAME "Float 32 Bit" -#endif - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - - -typedef struct -{ - double left_phase; - double right_phase; - unsigned int framesToGo; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE_t *out = (SAMPLE_t *)outputBuffer; - SAMPLE_t sample; - int i; - int framesToCalc; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - if( data->framesToGo < framesPerBuffer ) - { - framesToCalc = data->framesToGo; - data->framesToGo = 0; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - data->framesToGo -= framesPerBuffer; - } - - for( i=0; ileft_phase += (LEFT_FREQ / SAMPLE_RATE); - if( data->left_phase > 1.0) data->left_phase -= 1.0; - sample = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); /**/ - *out++ = sample; -/* *out++ = sample; /**/ -/* *out++ = 0; /**/ - - data->right_phase += (RIGHT_FREQ / SAMPLE_RATE); - if( data->right_phase > 1.0) data->right_phase -= 1.0; - *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. ))); /**/ -/* *out++ = 0; /* */ - } - /* zero remainder of final buffer */ - for( ; i<(int)framesPerBuffer; i++ ) - { - *out++ = SAMPLE_ZERO; /* left */ - *out++ = SAMPLE_ZERO; /* right */ - } - return finished; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PortAudioStream *stream; - PaError err; - paTestData data; - int totalSamps; - - printf("PortAudio Test: output " FORMAT_NAME "\n"); - - - data.left_phase = data.right_phase = 0.0; - data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - err = Pa_OpenStream( - &stream, - paNoDevice,/* default input device */ - 0, /* no input */ - TEST_FORMAT, - NULL, - Pa_GetDefaultOutputDeviceID(), /* default output device */ - 2, /* stereo output */ - TEST_FORMAT, - NULL, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS ); - while( Pa_StreamActive( stream ) ) Pa_Sleep(10); - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - - printf("PortAudio Test Finished: " FORMAT_NAME "\n"); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/debug_srate.c b/portaudio-v19/pa_tests/debug_srate.c deleted file mode 100644 index 07371f28e..000000000 --- a/portaudio-v19/pa_tests/debug_srate.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * $Id$ - * debug_record_reuse.c - * Record input into an array. - * Save array to a file. - * Based on patest_record.c but with various ugly debug hacks thrown in. - * Loop twice and reuse same streams. - * - * Author: Phil Burk http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include -#include "portaudio.h" - -#define EWS88MT_12_REC (1) -#define EWS88MT_12_PLAY (10) -#define SBLIVE_REC (2) -#define SBLIVE_PLAY (11) - -#if 0 -#define INPUT_DEVICE_ID Pa_GetDefaultInputDeviceID() -#define OUTPUT_DEVICE_ID Pa_GetDefaultOutputDeviceID() -#else -#define INPUT_DEVICE_ID (EWS88MT_12_REC) -#define OUTPUT_DEVICE_ID (SBLIVE_PLAY) -#endif - -#define INPUT_SAMPLE_RATE (22050.0) -#define OUTPUT_SAMPLE_RATE (22050.0) -#define NUM_SECONDS (4) -#define SLEEP_DUR_MSEC (1000) -#define FRAMES_PER_BUFFER (64) -#define NUM_REC_BUFS (0) -#define SAMPLES_PER_FRAME (2) - -#define PA_SAMPLE_TYPE paInt16 -typedef short SAMPLE; - -typedef struct -{ - long frameIndex; /* Index into sample array. */ -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int recordCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData *) userData; - (void) outputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( inputBuffer != NULL ) - { - data->frameIndex += framesPerBuffer; - } - return 0; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int playCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData *) userData; - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) outTime; - - if( outputBuffer != NULL ) - { - data->frameIndex += framesPerBuffer; - } - return 0; -} - -/****************************************************************/ -PaError MeasureStreamRate( PortAudioStream *stream, paTestData *dataPtr, double *ratePtr ) -{ - PaError err; - int i; - int totalFrames = 0; - int totalMSec = 0; - - dataPtr->frameIndex = 0; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) - { - int delta, endIndex; - - int startIndex = dataPtr->frameIndex; - Pa_Sleep(SLEEP_DUR_MSEC); - endIndex = dataPtr->frameIndex; - - delta = endIndex - startIndex; - totalFrames += delta; - totalMSec += SLEEP_DUR_MSEC; - - printf("index = %d, delta = %d\n", endIndex, delta ); fflush(stdout); - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - *ratePtr = (totalFrames * 1000.0) / totalMSec; - -error: - return err; -} - -void ReportRate( double measuredRate, double expectedRate ) -{ - double error; - - error = (measuredRate - expectedRate) / expectedRate; - error = (error < 0 ) ? -error : error; - - printf("Measured rate = %6.1f, expected rate = %6.1f\n", - measuredRate, expectedRate ); - if( error > 0.1 ) - { - printf("ERROR: unexpected rate! --------------------- ERROR!\n"); - } - else - { - printf("SUCCESS: rate within tolerance!\n"); - } -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - paTestData data = { 0 }; - long i; - double rate; - const PaDeviceInfo *pdi; - - PortAudioStream *outputStream; - PortAudioStream *inputStream; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - - pdi = Pa_GetDeviceInfo( INPUT_DEVICE_ID ); - printf("Input device = %s\n", pdi->name ); - pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE_ID ); - printf("Output device = %s\n", pdi->name ); - -/* Open input stream. */ - err = Pa_OpenStream( - &inputStream, - INPUT_DEVICE_ID, - SAMPLES_PER_FRAME, /* stereo input */ - PA_SAMPLE_TYPE, - NULL, - paNoDevice, - 0, - PA_SAMPLE_TYPE, - NULL, - INPUT_SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - recordCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_OpenStream( - &outputStream, - paNoDevice, - 0, /* NO input */ - PA_SAMPLE_TYPE, - NULL, - OUTPUT_DEVICE_ID, - SAMPLES_PER_FRAME, /* stereo output */ - PA_SAMPLE_TYPE, - NULL, - OUTPUT_SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - playCallback, - &data ); - if( err != paNoError ) goto error; - -/* Record and playback multiple times. */ - for( i=0; i<2; i++ ) - { - printf("Measuring INPUT ------------------------- \n"); - err = MeasureStreamRate( inputStream, &data, &rate ); - if( err != paNoError ) goto error; - ReportRate( rate, INPUT_SAMPLE_RATE ); - - printf("Measuring OUTPUT ------------------------- \n"); - err = MeasureStreamRate( outputStream, &data, &rate ); - if( err != paNoError ) goto error; - ReportRate( rate, OUTPUT_SAMPLE_RATE ); - } - -/* Clean up. */ - err = Pa_CloseStream( inputStream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( outputStream ); - if( err != paNoError ) goto error; - - if( err != paNoError ) goto error; - - Pa_Terminate(); - - printf("Test complete.\n"); fflush(stdout); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - if( err == paHostError ) - { - fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() ); - } - return -1; -} diff --git a/portaudio-v19/pa_tests/debug_test1.c b/portaudio-v19/pa_tests/debug_test1.c deleted file mode 100644 index 1321f62c8..000000000 --- a/portaudio-v19/pa_tests/debug_test1.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * $Id$ - patest1.c - Ring modulate the audio input with a 441hz sine wave for 20 seconds - using the Portable Audio api - Author: Ross Bencina - Modifications: - April 5th, 2001 - PLB - Check for NULL inputBuffer. -*/ -#include -#include -#include "portaudio.h" -#ifndef M_PI -#define M_PI (3.14159265) -#endif -typedef struct -{ - float sine[100]; - int phase; - int sampsToGo; -} -patest1data; -static int patest1Callback( void *inputBuffer, void *outputBuffer, - unsigned long bufferFrames, - PaTimestamp outTime, void *userData ) -{ - patest1data *data = (patest1data*)userData; - float *in = (float*)inputBuffer; - float *out = (float*)outputBuffer; - int framesToCalc = bufferFrames; - unsigned long i; - int finished = 0; - if(inputBuffer == NULL) return 0; - if( data->sampsToGo < bufferFrames ) - { - finished = 1; - } - for( i=0; iphase >= 100 ) - data->phase = 0; - } - data->sampsToGo -= bufferFrames; - /* zero remainder of final buffer if not already done */ - for( ; i -#include -#include "portaudio.h" - -#ifdef WIN32 -#ifndef PA_NO_ASIO -#include "pa_asio.h" -#endif -#endif - -/*******************************************************************/ -static void PrintSupportedStandardSampleRates( - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters ) -{ - static double standardSampleRates[] = { - 8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, - 44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */ - }; - int i, printCount; - PaError err; - - printCount = 0; - for( i=0; standardSampleRates[i] > 0; i++ ) - { - err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] ); - if( err == paFormatIsSupported ) - { - if( printCount == 0 ) - { - printf( "\t%8.2f", standardSampleRates[i] ); - printCount = 1; - } - else if( printCount == 4 ) - { - printf( ",\n\t%8.2f", standardSampleRates[i] ); - printCount = 1; - } - else - { - printf( ", %8.2f", standardSampleRates[i] ); - ++printCount; - } - } - } - if( !printCount ) - printf( "None\n" ); - else - printf( "\n" ); -} -/*******************************************************************/ -int main(void); -int main(void) -{ - int i, numDevices, defaultDisplayed; - const PaDeviceInfo *deviceInfo; - PaStreamParameters inputParameters, outputParameters; - PaError err; - - - Pa_Initialize(); - - printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n", - Pa_GetVersion(), Pa_GetVersionText() ); - - - numDevices = Pa_GetDeviceCount(); - if( numDevices < 0 ) - { - printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); - err = numDevices; - goto error; - } - - printf( "Number of devices = %d\n", numDevices ); - for( i=0; ihostApi )->defaultInputDevice ) - { - const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); - printf( "[ Default %s Input", hostInfo->name ); - defaultDisplayed = 1; - } - - if( i == Pa_GetDefaultOutputDevice() ) - { - printf( (defaultDisplayed ? "," : "[") ); - printf( " Default Output" ); - defaultDisplayed = 1; - } - else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice ) - { - const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); - printf( (defaultDisplayed ? "," : "[") ); - printf( " Default %s Output", hostInfo->name ); - defaultDisplayed = 1; - } - - if( defaultDisplayed ) - printf( " ]\n" ); - - /* print device info fields */ - printf( "Name = %s\n", deviceInfo->name ); - printf( "Host API = %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name ); - printf( "Max inputs = %d", deviceInfo->maxInputChannels ); - printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels ); - - printf( "Default low input latency = %8.3f\n", deviceInfo->defaultLowInputLatency ); - printf( "Default low output latency = %8.3f\n", deviceInfo->defaultLowOutputLatency ); - printf( "Default high input latency = %8.3f\n", deviceInfo->defaultHighInputLatency ); - printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency ); - -#ifdef WIN32 -#ifndef PA_NO_ASIO -/* ASIO specific latency information */ - if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ){ - long minLatency, maxLatency, preferredLatency, granularity; - - err = PaAsio_GetAvailableLatencyValues( i, - &minLatency, &maxLatency, &preferredLatency, &granularity ); - - printf( "ASIO minimum buffer size = %ld\n", minLatency ); - printf( "ASIO maximum buffer size = %ld\n", maxLatency ); - printf( "ASIO preferred buffer size = %ld\n", preferredLatency ); - - if( granularity == -1 ) - printf( "ASIO buffer granularity = power of 2\n" ); - else - printf( "ASIO buffer granularity = %ld\n", granularity ); - } -#endif /* !PA_NO_ASIO */ -#endif /* WIN32 */ - - printf( "Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate ); - - /* poll for standard sample rates */ - inputParameters.device = i; - inputParameters.channelCount = deviceInfo->maxInputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = i; - outputParameters.channelCount = deviceInfo->maxOutputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ - outputParameters.hostApiSpecificStreamInfo = NULL; - - if( inputParameters.channelCount > 0 ) - { - printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n", - inputParameters.channelCount ); - PrintSupportedStandardSampleRates( &inputParameters, NULL ); - } - - if( outputParameters.channelCount > 0 ) - { - printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n", - outputParameters.channelCount ); - PrintSupportedStandardSampleRates( NULL, &outputParameters ); - } - - if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 ) - { - printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n", - inputParameters.channelCount, outputParameters.channelCount ); - PrintSupportedStandardSampleRates( &inputParameters, &outputParameters ); - } - } - - Pa_Terminate(); - - printf("----------------------------------------------\n"); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/pa_fuzz.c b/portaudio-v19/pa_tests/pa_fuzz.c deleted file mode 100644 index 127c2e3e6..000000000 --- a/portaudio-v19/pa_tests/pa_fuzz.c +++ /dev/null @@ -1,168 +0,0 @@ -/** @file pa_fuzz.c - @brief Distort input like a fuzz boz. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -/* -** Note that many of the older ISA sound cards on PCs do NOT support -** full duplex audio (simultaneous record and playback). -** And some only support full duplex at lower sample rates. -*/ -#define SAMPLE_RATE (44100) -#define PA_SAMPLE_TYPE paFloat32 -#define FRAMES_PER_BUFFER (64) - -typedef float SAMPLE; - -float CubicAmplifier( float input ); -static int fuzzCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - -/* Non-linear amplifier with soft distortion curve. */ -float CubicAmplifier( float input ) -{ - float output, temp; - if( input < 0.0 ) - { - temp = input + 1.0f; - output = (temp * temp * temp) - 1.0f; - } - else - { - temp = input - 1.0f; - output = (temp * temp * temp) + 1.0f; - } - - return output; -} -#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x)))) - -static int gNumNoInputs = 0; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int fuzzCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - SAMPLE *out = (SAMPLE*)outputBuffer; - const SAMPLE *in = (const SAMPLE*)inputBuffer; - unsigned int i; - (void) timeInfo; /* Prevent unused variable warnings. */ - (void) statusFlags; - (void) userData; - - if( inputBuffer == NULL ) - { - for( i=0; idefaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - &inputParameters, - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - 0, /* paClipOff, */ /* we won't output out of range samples so don't bother clipping them */ - fuzzCallback, - NULL ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Hit ENTER to stop program.\n"); - getchar(); - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - printf("Finished. gNumNoInputs = %d\n", gNumNoInputs ); - Pa_Terminate(); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} diff --git a/portaudio-v19/pa_tests/pa_minlat.c b/portaudio-v19/pa_tests/pa_minlat.c deleted file mode 100644 index 8400dfd27..000000000 --- a/portaudio-v19/pa_tests/pa_minlat.c +++ /dev/null @@ -1,187 +0,0 @@ -/** @file pa_minlat.c - @brief Experiment with different numbers of buffers to determine the - minimum latency for a computer. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include -#include -#include "portaudio.h" - -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -#define DEFAULT_BUFFER_SIZE (32) - -typedef struct -{ - double left_phase; - double right_phase; -} -paTestData; - -/* Very simple synthesis routine to generate two sine waves. */ -static int paminlatCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - double left_phaseInc = 0.02; - double right_phaseInc = 0.06; - - double left_phase = data->left_phase; - double right_phase = data->right_phase; - - for( i=0; i TWOPI ) left_phase -= TWOPI; - *out++ = (float) sin( left_phase ); - - right_phase += right_phaseInc; - if( right_phase > TWOPI ) right_phase -= TWOPI; - *out++ = (float) sin( right_phase ); - } - - data->left_phase = left_phase; - data->right_phase = right_phase; - return 0; -} - -int main( int argc, char **argv ); -int main( int argc, char **argv ) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int go; - int outLatency = 0; - int minLatency = DEFAULT_BUFFER_SIZE * 2; - int framesPerBuffer; - double sampleRate = 44100.0; - char str[256]; - - printf("pa_minlat - Determine minimum latency for your computer.\n"); - printf(" usage: pa_minlat {userBufferSize}\n"); - printf(" for example: pa_minlat 64\n"); - printf("Adjust your stereo until you hear a smooth tone in each speaker.\n"); - printf("Then try to find the smallest number of frames that still sounds smooth.\n"); - printf("Note that the sound will stop momentarily when you change the number of buffers.\n"); - - /* Get bufferSize from command line. */ - framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE; - printf("Frames per buffer = %d\n", framesPerBuffer ); - - data.left_phase = data.right_phase = 0.0; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outLatency = sampleRate * 200.0 / 1000.0; /* 200 msec. */ - - /* Try different numBuffers in a loop. */ - go = 1; - while( go ) - { - outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */ - outputParameters.channelCount = 2; /* Stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */ - outputParameters.suggestedLatency = (double)outLatency / sampleRate; /* In seconds. */ - outputParameters.hostApiSpecificStreamInfo = NULL; - - printf("Latency = %d frames = %6.1f msec.\n", outLatency, outputParameters.suggestedLatency * 1000.0 ); - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - sampleRate, - framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - paminlatCallback, - &data ); - if( err != paNoError ) goto error; - if( stream == NULL ) goto error; - - /* Start audio. */ - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - /* Ask user for a new nlatency. */ - printf("\nMove windows around to see if the sound glitches.\n"); - printf("Latency now %d, enter new number of frames, or 'q' to quit: ", outLatency ); - fgets( str, 256, stdin ); - { - /* Get rid of newline */ - size_t l = strlen( str ) - 1; - if( str[ l ] == '\n') - str[ l ] = '\0'; - } - if( str[0] == 'q' ) go = 0; - else - { - outLatency = atol( str ); - if( outLatency < minLatency ) - { - printf( "Latency below minimum of %d! Set to minimum!!!\n", minLatency ); - outLatency = minLatency; - } - } - /* Stop sound until ENTER hit. */ - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - } - printf("A good setting for latency would be somewhat higher than\n"); - printf("the minimum latency that worked.\n"); - printf("PortAudio: Test finished.\n"); - Pa_Terminate(); - return 0; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return 1; -} diff --git a/portaudio-v19/pa_tests/paqa_devs.c b/portaudio-v19/pa_tests/paqa_devs.c deleted file mode 100644 index f52143c19..000000000 --- a/portaudio-v19/pa_tests/paqa_devs.c +++ /dev/null @@ -1,347 +0,0 @@ -/** @file paqa_devs.c - @brief Self Testing Quality Assurance app for PortAudio - Try to open each device and run through all the - possible configurations. - - @author Phil Burk http://www.softsynth.com - - Pieter adapted to V19 API. Test now relies heavily on - Pa_IsFormatSupported(). Uses same 'standard' sample rates - as in test pa_devs.c. -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#include "pa_trace.h" - -/****************************************** Definitions ***********/ -#define MODE_INPUT (0) -#define MODE_OUTPUT (1) - -typedef struct PaQaData -{ - unsigned long framesLeft; - int numChannels; - int bytesPerSample; - int mode; - short sawPhase; - PaSampleFormat format; -} -PaQaData; - -/****************************************** Prototypes ***********/ -static void TestDevices( int mode ); -static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels ); -static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels, PaSampleFormat format ); -static int QaCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - -/****************************************** Globals ***********/ -static int gNumPassed = 0; -static int gNumFailed = 0; - -/****************************************** Macros ***********/ -/* Print ERROR if it fails. Tally success or failure. */ -/* Odd do-while wrapper seems to be needed for some compilers. */ -#define EXPECT(_exp) \ - do \ - { \ - if ((_exp)) {\ - /* printf("SUCCESS for %s\n", #_exp ); */ \ - gNumPassed++; \ - } \ - else { \ - printf("ERROR - 0x%x - %s for %s\n", result, \ - ((result == 0) ? "-" : Pa_GetErrorText(result)), \ - #_exp ); \ - gNumFailed++; \ - goto error; \ - } \ - } while(0) - -/*******************************************************************/ -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int QaCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - unsigned long i; - short phase; - PaQaData *data = (PaQaData *) userData; - (void) inputBuffer; - - /* Play simle sawtooth wave. */ - if( data->mode == MODE_OUTPUT ) - { - phase = data->sawPhase; - switch( data->format ) - { - case paFloat32: - { - float *out = (float *) outputBuffer; - for( i=0; inumChannels == 2 ) - { - *out++ = (float) (phase * (1.0 / 32768.0)); - } - } - } - break; - - case paInt32: - { - int *out = (int *) outputBuffer; - for( i=0; inumChannels == 2 ) - { - *out++ = ((int) phase ) << 16; - } - } - } - break; - case paInt16: - { - short *out = (short *) outputBuffer; - for( i=0; inumChannels == 2 ) - { - *out++ = phase; - } - } - } - break; - - default: - { - unsigned char *out = (unsigned char *) outputBuffer; - unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; - for( i=0; isawPhase = phase; - } - /* Are we through yet? */ - if( data->framesLeft > framesPerBuffer ) - { - PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft ); - data->framesLeft -= framesPerBuffer; - return 0; - } - else - { - PaUtil_AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft ); - data->framesLeft = 0; - return 1; - } -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError result; - EXPECT( ((result=Pa_Initialize()) == 0) ); - printf("Test OUTPUT ---------------\n"); - TestDevices( MODE_OUTPUT ); - printf("Test INPUT ---------------\n"); - TestDevices( MODE_INPUT ); -error: - Pa_Terminate(); - printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); - return 0; -} - -/******************************************************************* -* Try each output device, through its full range of capabilities. */ -static void TestDevices( int mode ) -{ - int id, jc, i; - int maxChannels; - const PaDeviceInfo *pdi; - static double standardSampleRates[] = { 8000.0, 9600.0, 11025.0, 12000.0, - 16000.0, 22050.0, 24000.0, - 32000.0, 44100.0, 48000.0, - 88200.0, 96000.0, - -1.0 }; /* Negative terminated list. */ - int numDevices = Pa_GetDeviceCount(); - for( id=0; idmaxInputChannels : pdi->maxOutputChannels); - - for( jc=1; jc<=maxChannels; jc++ ) - { - printf("Name = %s\n", pdi->name ); - /* Try each standard sample rate. */ - for( i=0; standardSampleRates[i] > 0; i++ ) - { - TestFormats( mode, (PaDeviceIndex)id, standardSampleRates[i], jc ); - } - } - } -} - -/*******************************************************************/ -static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels ) -{ - TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); - TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); - TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); - /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */ -} - -/*******************************************************************/ -static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels, PaSampleFormat format ) -{ - PaStreamParameters inputParameters, outputParameters, *ipp, *opp; - PaStream *stream = NULL; - PaError result = paNoError; - PaQaData myData; - #define FRAMES_PER_BUFFER (64) - - /* Setup data for synthesis thread. */ - myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */ - myData.numChannels = numChannels; - myData.mode = mode; - myData.format = format; - switch( format ) - { - case paFloat32: - case paInt32: - case paInt24: - myData.bytesPerSample = 4; - break; -/* case paPackedInt24: - myData.bytesPerSample = 3; - break; */ - default: - myData.bytesPerSample = 2; - break; - } - - if( mode == MODE_INPUT ) - { - inputParameters.device = deviceID; - inputParameters.channelCount = numChannels; - inputParameters.sampleFormat = format; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - ipp = &inputParameters; - } - else - ipp = NULL; - if( mode == MODE_OUTPUT ) /* Pa_GetDeviceInfo(paNoDevice) COREDUMPS!!! */ - { - outputParameters.device = deviceID; - outputParameters.channelCount = numChannels; - outputParameters.sampleFormat = format; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - opp = &outputParameters; - } - else - opp = NULL; - - if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, sampleRate )) - { - printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %lu -------\n", - ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT", - deviceID, sampleRate, numChannels, (unsigned long)format); - EXPECT( ((result = Pa_OpenStream( &stream, - ipp, - opp, - sampleRate, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - QaCallback, - &myData ) ) == 0) ); - if( stream ) - { - PaTime oldStamp, newStamp; - unsigned long oldFrames; - int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400; - /* Was: - int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); - int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate); - */ - int msec = (int)( 3.0 * - (( mode == MODE_INPUT ) ? inputParameters.suggestedLatency : outputParameters.suggestedLatency )); - if( msec < minDelay ) msec = minDelay; - printf("msec = %d\n", msec); /**/ - EXPECT( ((result=Pa_StartStream( stream )) == 0) ); - /* Check to make sure PortAudio is advancing timeStamp. */ - oldStamp = Pa_GetStreamTime(stream); - Pa_Sleep(msec); - newStamp = Pa_GetStreamTime(stream); - printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/ - EXPECT( (oldStamp < newStamp) ); - /* Check to make sure callback is decrementing framesLeft. */ - oldFrames = myData.framesLeft; - Pa_Sleep(msec); - printf("oldFrames = %lu, myData.framesLeft = %lu\n", oldFrames, myData.framesLeft ); /**/ - EXPECT( (oldFrames > myData.framesLeft) ); - EXPECT( ((result=Pa_CloseStream( stream )) == 0) ); - stream = NULL; - } - } -error: - if( stream != NULL ) Pa_CloseStream( stream ); - return result; -} diff --git a/portaudio-v19/pa_tests/paqa_errs.c b/portaudio-v19/pa_tests/paqa_errs.c deleted file mode 100644 index 3f9077173..000000000 --- a/portaudio-v19/pa_tests/paqa_errs.c +++ /dev/null @@ -1,380 +0,0 @@ -/** @file paqa_errs.c - @brief Self Testing Quality Assurance app for PortAudio - Do lots of bad things to test error reporting. - @author Phil Burk http://www.softsynth.com - Pieter Suurmond adapted to V19 API. -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include - -#include "portaudio.h" - -/*--------- Definitions ---------*/ -#define MODE_INPUT (0) -#define MODE_OUTPUT (1) -#define FRAMES_PER_BUFFER (64) -#define SAMPLE_RATE (44100.0) - -typedef struct PaQaData -{ - unsigned long framesLeft; - int numChannels; - int bytesPerSample; - int mode; -} -PaQaData; - -static int gNumPassed = 0; /* Two globals */ -static int gNumFailed = 0; - -/*------------------- Macros ------------------------------*/ -/* Print ERROR if it fails. Tally success or failure. Odd */ -/* do-while wrapper seems to be needed for some compilers. */ - -#define EXPECT(_exp) \ - do \ - { \ - if ((_exp)) {\ - gNumPassed++; \ - } \ - else { \ - printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ - gNumFailed++; \ - goto error; \ - } \ - } while(0) - -#define HOPEFOR(_exp) \ - do \ - { \ - if ((_exp)) {\ - gNumPassed++; \ - } \ - else { \ - printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ - gNumFailed++; \ - } \ - } while(0) - -/*-------------------------------------------------------------------------*/ -/* This routine will be called by the PortAudio engine when audio is needed. - It may be called at interrupt level on some machines so don't do anything - that could mess up the system like calling malloc() or free(). -*/ -static int QaCallback( const void* inputBuffer, - void* outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void* userData ) -{ - unsigned long i; - unsigned char* out = (unsigned char *) outputBuffer; - PaQaData* data = (PaQaData *) userData; - - (void)inputBuffer; /* Prevent "unused variable" warnings. */ - - /* Zero out buffer so we don't hear terrible noise. */ - if( data->mode == MODE_OUTPUT ) - { - unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; - for( i=0; iframesLeft > framesPerBuffer ) - { - data->framesLeft -= framesPerBuffer; - return 0; - } - else - { - data->framesLeft = 0; - return 1; - } -} - -static PaDeviceIndex FindInputOnlyDevice(void) -{ - PaDeviceIndex result = Pa_GetDefaultInputDevice(); - if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxOutputChannels == 0 ) - return result; - - for( result = 0; result < Pa_GetDeviceCount(); ++result ) - { - if( Pa_GetDeviceInfo(result)->maxOutputChannels == 0 ) - return result; - } - - return paNoDevice; -} - -static PaDeviceIndex FindOutputOnlyDevice(void) -{ - PaDeviceIndex result = Pa_GetDefaultOutputDevice(); - if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxInputChannels == 0 ) - return result; - - for( result = 0; result < Pa_GetDeviceCount(); ++result ) - { - if( Pa_GetDeviceInfo(result)->maxInputChannels == 0 ) - return result; - } - - return paNoDevice; -} - -/*-------------------------------------------------------------------------------------------------*/ -static int TestBadOpens( void ) -{ - PaStream* stream = NULL; - PaError result; - PaQaData myData; - PaStreamParameters ipp, opp; - - /* Setup data for synthesis thread. */ - myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ - myData.numChannels = 1; - myData.mode = MODE_OUTPUT; - - /*----------------------------- No devices specified: */ - ipp.device = opp.device = paNoDevice; - ipp.channelCount = opp.channelCount = 0; /* Also no channels. */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - /* Take the low latency of the default device for all subsequent tests. */ - ipp.suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultLowInputLatency; - opp.suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->defaultLowOutputLatency; - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- No devices specified #2: */ - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, NULL, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- Out of range input device specified: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = Pa_GetDeviceCount(); /* And no output device, and no channels. */ - opp.channelCount = 0; opp.device = paNoDevice; - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- Out of range output device specified: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */ - opp.channelCount = 0; opp.device = Pa_GetDeviceCount(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- Zero input channels: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = Pa_GetDefaultInputDevice(); - opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no output channels. */ - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); - - /*----------------------------- Zero output channels: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no input channels. */ - opp.channelCount = 0; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); - - /*----------------------------- Nonzero input and output channels but no output device: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 2; ipp.device = Pa_GetDefaultInputDevice(); /* Both stereo. */ - opp.channelCount = 2; opp.device = paNoDevice; - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- Nonzero input and output channels but no input device: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 2; ipp.device = paNoDevice; - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidDevice)); - - /*----------------------------- NULL stream pointer: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; /* Output is more likely than input. */ - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); /* Only 2 output channels. */ - HOPEFOR(((result = Pa_OpenStream(NULL, &ipp, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paBadStreamPtr)); - - /*----------------------------- Low sample rate: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - 1.0, FRAMES_PER_BUFFER, /* 1 cycle per second (1 Hz) is too low. */ - paClipOff, QaCallback, &myData )) == paInvalidSampleRate)); - - /*----------------------------- High sample rate: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - 10000000.0, FRAMES_PER_BUFFER, /* 10^6 cycles per second (10 MHz) is too high. */ - paClipOff, QaCallback, &myData )) == paInvalidSampleRate)); - - /*----------------------------- NULL callback: */ - /* NULL callback is valid in V19 -- it means use blocking read/write stream - - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, - NULL, - &myData )) == paNullCallback)); - */ - - /*----------------------------- Bad flag: */ - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; - opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - 255, /* Is 8 maybe legal V19 API? */ - QaCallback, &myData )) == paInvalidFlag)); - - /*----------------------------- using input device as output device: */ - if( FindInputOnlyDevice() != paNoDevice ) - { - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */ - opp.channelCount = 2; opp.device = FindInputOnlyDevice(); - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); - } - - /*----------------------------- using output device as input device: */ - if( FindOutputOnlyDevice() != paNoDevice ) - { - ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; - ipp.sampleFormat = opp.sampleFormat = paFloat32; - ipp.channelCount = 2; ipp.device = FindOutputOnlyDevice(); - opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no channels. */ - HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); - - } - - if( stream != NULL ) Pa_CloseStream( stream ); - return result; -} - -/*-----------------------------------------------------------------------------------------*/ -static int TestBadActions( void ) -{ - PaStream* stream = NULL; - PaError result; - PaQaData myData; - PaStreamParameters opp; - - /* Setup data for synthesis thread. */ - myData.framesLeft = (unsigned long)(SAMPLE_RATE * 100); /* 100 seconds */ - myData.numChannels = 1; - myData.mode = MODE_OUTPUT; - - opp.device = Pa_GetDefaultOutputDevice(); /* Default output. */ - opp.channelCount = 2; /* Stereo output. */ - opp.hostApiSpecificStreamInfo = NULL; - opp.sampleFormat = paFloat32; - opp.suggestedLatency = Pa_GetDeviceInfo(opp.device)->defaultLowOutputLatency; - - HOPEFOR(((result = Pa_OpenStream(&stream, NULL, /* Take NULL as input parame- */ - &opp, /* ters, meaning try only output. */ - SAMPLE_RATE, FRAMES_PER_BUFFER, - paClipOff, QaCallback, &myData )) == paNoError)); - - HOPEFOR(((result = Pa_StartStream(NULL)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_StopStream(NULL)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_IsStreamStopped(NULL)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_IsStreamActive(NULL)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_CloseStream(NULL)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_SetStreamFinishedCallback(NULL, NULL)) == paBadStreamPtr)); - HOPEFOR(((result = !Pa_GetStreamInfo(NULL)))); - HOPEFOR(((result = Pa_GetStreamTime(NULL)) == 0.0)); - HOPEFOR(((result = Pa_GetStreamCpuLoad(NULL)) == 0.0)); - HOPEFOR(((result = Pa_ReadStream(NULL, NULL, 0)) == paBadStreamPtr)); - HOPEFOR(((result = Pa_WriteStream(NULL, NULL, 0)) == paBadStreamPtr)); - - /** @todo test Pa_GetStreamReadAvailable and Pa_GetStreamWriteAvailable */ - - if (stream != NULL) Pa_CloseStream(stream); - return result; -} - -/*---------------------------------------------------------------------*/ -int main(void); -int main(void) -{ - PaError result; - - EXPECT(((result = Pa_Initialize()) == paNoError)); - TestBadOpens(); - TestBadActions(); -error: - Pa_Terminate(); - printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed); - return 0; -} diff --git a/portaudio-v19/pa_tests/patest1.c b/portaudio-v19/pa_tests/patest1.c deleted file mode 100644 index 15fdfdd3d..000000000 --- a/portaudio-v19/pa_tests/patest1.c +++ /dev/null @@ -1,192 +0,0 @@ -/** @file patest1.c - @brief Ring modulate the audio input with a sine wave for 20 seconds. - @author Ross Bencina -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define SAMPLE_RATE (44100) - -typedef struct -{ - float sine[100]; - int phase; - int sampsToGo; -} -patest1data; - -static int patest1Callback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - patest1data *data = (patest1data*)userData; - float *in = (float*)inputBuffer; - float *out = (float*)outputBuffer; - int framesToCalc = framesPerBuffer; - unsigned long i = 0; - int finished; - - if( data->sampsToGo < framesPerBuffer ) - { - framesToCalc = data->sampsToGo; - finished = paComplete; - } - else - { - finished = paContinue; - } - - for( ; isine[data->phase]; /* left */ - *out++ = *in++ * data->sine[data->phase++]; /* right */ - if( data->phase >= 100 ) - data->phase = 0; - } - - data->sampsToGo -= framesToCalc; - - /* zero remainder of final buffer if not already done */ - for( ; idefaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - &inputParameters, - &outputParameters, - (double)SAMPLE_RATE, /* Samplerate in Hertz. */ - 512, /* Small buffers */ - paClipOff, /* We won't output out of range samples so don't bother clipping them. */ - patest1Callback, - &data ); - if( err != paNoError ) goto done; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto done; - - printf( "Press any key to end.\n" ); fflush(stdout); - - getc( stdin ); /* wait for input before exiting */ - - err = Pa_AbortStream( stream ); - if( err != paNoError ) goto done; - - printf( "Waiting for stream to complete...\n" ); - - /* sleep until playback has finished */ - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(1000); - if( err < 0 ) goto done; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto done; - -done: - Pa_Terminate(); - - if( err != paNoError ) - { - fprintf( stderr, "An error occured while using portaudio\n" ); - if( err == paUnanticipatedHostError ) - { - fprintf( stderr, " unanticipated host error.\n"); - herr = Pa_GetLastHostErrorInfo(); - if (herr) - { - fprintf( stderr, " Error number: %ld\n", herr->errorCode ); - if (herr->errorText) - fprintf( stderr, " Error text: %s\n", herr->errorText ); - } - else - fprintf( stderr, " Pa_GetLastHostErrorInfo() failed!\n" ); - } - else - { - fprintf( stderr, " Error number: %d\n", err ); - fprintf( stderr, " Error text: %s\n", Pa_GetErrorText( err ) ); - } - - err = 1; /* Always return 0 or 1, but no other return codes. */ - } - - printf( "bye\n" ); - - return err; -} diff --git a/portaudio-v19/pa_tests/patest_buffer.c b/portaudio-v19/pa_tests/patest_buffer.c deleted file mode 100644 index 39c5dd21d..000000000 --- a/portaudio-v19/pa_tests/patest_buffer.c +++ /dev/null @@ -1,180 +0,0 @@ -/** @file patest_buffer.c - @brief Test opening streams with different buffer sizes. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include -#include "portaudio.h" -#define NUM_SECONDS (1) -#define SAMPLE_RATE (44100) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (200) - -#define BUFFER_TABLE 9 -long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345}; - -typedef struct -{ - short sine[TABLE_SIZE]; - int left_phase; - int right_phase; - unsigned int sampsToGo; -} -paTestData; -PaError TestOnce( int buffersize ); - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patest1Callback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - short *out = (short*)outputBuffer; - unsigned int i; - int finished = 0; - (void) inputBuffer; /* Prevent "unused variable" warnings. */ - - if( data->sampsToGo < framesPerBuffer ) - { - /* final buffer... */ - - for( i=0; isampsToGo; i++ ) - { - *out++ = data->sine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - /* zero remainder of final buffer */ - for( ; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - data->sampsToGo -= framesPerBuffer; - } - return finished; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - int i; - PaError err; - printf("Test opening streams with different buffer sizes\n\n"); - - for (i = 0 ; i < BUFFER_TABLE; i++) - { - printf("Buffer size %ld\n", buffer_table[i]); - err = TestOnce(buffer_table[i]); - if( err < 0 ) return 0; - - } - return 0; -} - - -PaError TestOnce( int buffersize ) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - int totalSamps; - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - buffersize, /* frames per buffer */ - (paClipOff | paDitherOff), - patest1Callback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Waiting for sound to finish.\n"); - Pa_Sleep(1000); - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - return paNoError; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_callbackstop.c b/portaudio-v19/pa_tests/patest_callbackstop.c deleted file mode 100644 index 2da642e43..000000000 --- a/portaudio-v19/pa_tests/patest_callbackstop.c +++ /dev/null @@ -1,221 +0,0 @@ -/** @file patest_callbackstop.c - @brief Test the paComplete callback result code. - @author Ross Bencina -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (5) -#define NUM_LOOPS (4) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (67) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int phase; - unsigned long generatedFramesCount; - volatile int callbackReturnedPaComplete; - volatile int callbackInvokedAfterReturningPaComplete; -} -TestData; - -/* - This routine will be called by the PortAudio stream when audio is needed. - It may be called at interrupt level on some machines so don't do anything - that could mess up the system like calling malloc() or free(). -*/ -static int TestCallback( const void *input, void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - TestData *data = (TestData*)userData; - float *out = (float*)output; - unsigned long i; - float x; - - (void) input; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - - - if( data->callbackReturnedPaComplete ) - data->callbackInvokedAfterReturningPaComplete = 1; - - for( i=0; isine[ data->phase++ ]; - if( data->phase >= TABLE_SIZE ) - data->phase -= TABLE_SIZE; - - *out++ = x; /* left */ - *out++ = x; /* right */ - } - - data->generatedFramesCount += frameCount; - if( data->generatedFramesCount >= (NUM_SECONDS * SAMPLE_RATE) ) - { - data->callbackReturnedPaComplete = 1; - return paComplete; - } - else - { - return paContinue; - } -} - -/*----------------------------------------------------------------------------*/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - TestData data; - int i, j; - - - printf( "PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", - SAMPLE_RATE, FRAMES_PER_BUFFER ); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* output will be in-range, so no need to clip */ - TestCallback, - &data ); - if( err != paNoError ) goto error; - - printf("Repeating test %d times.\n", NUM_LOOPS ); - - for( i=0; i < NUM_LOOPS; ++i ) - { - data.phase = 0; - data.generatedFramesCount = 0; - data.callbackReturnedPaComplete = 0; - data.callbackInvokedAfterReturningPaComplete = 0; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for %d seconds.\n", NUM_SECONDS ); - - /* wait for the callback to complete generating NUM_SECONDS of tone */ - - do - { - Pa_Sleep( 500 ); - } - while( !data.callbackReturnedPaComplete ); - - printf( "Callback returned paComplete.\n" ); - printf( "Waiting for buffers to finish playing...\n" ); - - /* wait for stream to become inactive, - or for a timeout of approximately NUM_SECONDS - */ - - j = 0; - while( (err = Pa_IsStreamActive( stream )) == 1 && j < NUM_SECONDS * 2 ) - { - printf(".\n" ); - Pa_Sleep( 500 ); - ++j; - } - - if( err < 0 ) - { - goto error; - } - else if( err == 1 ) - { - printf( "TEST FAILED: Timed out waiting for buffers to finish playing.\n" ); - } - else - { - printf("Buffers finished.\n" ); - } - - if( data.callbackInvokedAfterReturningPaComplete ) - { - printf( "TEST FAILED: Callback was invoked after returning paComplete.\n" ); - } - - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - } - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_clip.c b/portaudio-v19/pa_tests/patest_clip.c deleted file mode 100644 index 1d17d11b2..000000000 --- a/portaudio-v19/pa_tests/patest_clip.c +++ /dev/null @@ -1,178 +0,0 @@ -/** @file patest_clip.c - @brief Play a sine wave for several seconds at an amplitude - that would require clipping. - - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (4) -#define SAMPLE_RATE (44100) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (200) - -typedef struct paTestData -{ - float sine[TABLE_SIZE]; - float amplitude; - int left_phase; - int right_phase; -} -paTestData; - -PaError PlaySine( paTestData *data, unsigned long flags, float amplitude ); - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int sineCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - float amplitude = data->amplitude; - unsigned int i; - (void) inputBuffer; /* Prevent "unused variable" warnings. */ - (void) timeInfo; - (void) statusFlags; - - for( i=0; isine[data->left_phase]; /* left */ - *out++ = amplitude * data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - return 0; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - paTestData data; - int i; - - printf("PortAudio Test: output sine wave with and without clipping.\n"); - /* initialise sinusoidal wavetable */ - for( i=0; ileft_phase = data->right_phase = 0; - data->amplitude = amplitude; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - 1024, - flags, - sineCallback, - data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - Pa_Sleep( NUM_SECONDS * 1000 ); - printf("CPULoad = %8.6f\n", Pa_GetStreamCpuLoad( stream ) ); - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - return paNoError; -error: - return err; -} diff --git a/portaudio-v19/pa_tests/patest_dither.c b/portaudio-v19/pa_tests/patest_dither.c deleted file mode 100644 index 10f03e42b..000000000 --- a/portaudio-v19/pa_tests/patest_dither.c +++ /dev/null @@ -1,178 +0,0 @@ -/** @file patest_dither.c - @brief Attempt to hear difference between dithered and non-dithered signal. - - This only has an effect if the native format is 16 bit. - - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include - -#include "portaudio.h" - -#define NUM_SECONDS (5) -#define SAMPLE_RATE (44100) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (200) - -typedef struct paTestData -{ - float sine[TABLE_SIZE]; - float amplitude; - int left_phase; - int right_phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int sineCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - float amplitude = data->amplitude; - unsigned int i; - (void) inputBuffer; - - for( i=0; isine[data->left_phase]; /* left */ - *out++ = amplitude * data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - return 0; -} - -/*****************************************************************************/ -/* - V18 version did not call Pa_Terminate() if Pa_Initialize() failed. - This V19 version ALWAYS calls Pa_Terminate(). PS. -*/ -PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude ); -PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude ) -{ - PaStream* stream; - PaStreamParameters outputParameters; - PaError err; - - data->left_phase = data->right_phase = 0; - data->amplitude = amplitude; - - err = Pa_Initialize(); - if (err != paNoError) - goto done; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */ - /* When you change this, also */ - /* adapt the callback routine! */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device ) - ->defaultLowOutputLatency; /* Low latency. */ - err = Pa_OpenStream( &stream, - NULL, /* No input. */ - &outputParameters, - SAMPLE_RATE, - 1024, /* frames per buffer */ - flags, - sineCallback, - (void*)data ); - if (err != paNoError) - goto done; - - err = Pa_StartStream( stream ); - if (err != paNoError) - goto done; - - Pa_Sleep( NUM_SECONDS * 1000 ); - printf("CPULoad = %8.6f\n", Pa_GetStreamCpuLoad(stream)); - - err = Pa_CloseStream( stream ); -done: - Pa_Sleep( 250 ); /* Just a small silence. */ - Pa_Terminate(); - return err; -} - - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - paTestData DATA; - int i; - float amplitude = 4.0 / (1<<15); - - printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n"); - /* initialise sinusoidal wavetable */ - for( i=0; i - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include - -#include "portaudio.h" - -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (1024) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -typedef struct paTestData -{ - int sleepFor; - double phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - double phaseInc = 0.02; - double phase = data->phase; - - (void) inputBuffer; /* Prevent unused argument warning. */ - - for( i=0; i TWOPI ) phase -= TWOPI; - /* This is not a very efficient way to calc sines. */ - *out++ = (float) sin( phase ); /* mono */ - } - - if( data->sleepFor > 0 ) - { - Pa_Sleep( data->sleepFor ); - } - - data->phase = phase; - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream* stream; - PaStreamParameters outputParameters; - PaError err; - int i; - paTestData data = {0}; - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", - SAMPLE_RATE, FRAMES_PER_BUFFER ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */ - outputParameters.channelCount = 1; /* Mono output. */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point. */ - outputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device) - ->defaultLowOutputLatency; - err = Pa_OpenStream(&stream, - NULL, /* No input. */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* No out of range samples. */ - patestCallback, - &data); - if (err != paNoError) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - /* Gradually increase sleep time. */ - /* Was: for( i=0; i<10000; i+= 1000 ) */ - for(i=0; i <= 1000; i += 100) - { - printf("Sleep for %d milliseconds in audio callback.\n", i ); - data.sleepFor = i; - Pa_Sleep( ((i<1000) ? 1000 : i) ); - } - - printf("Suffer for 10 seconds.\n"); - Pa_Sleep( 10000 ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_in_overflow.c b/portaudio-v19/pa_tests/patest_in_overflow.c deleted file mode 100644 index a0be2c6a1..000000000 --- a/portaudio-v19/pa_tests/patest_in_overflow.c +++ /dev/null @@ -1,224 +0,0 @@ -/** @file patest_in_overflow.c - @brief Count input overflows (using paInputOverflow flag) under - overloaded and normal conditions. - This test uses the same method to overload the stream as does - patest_out_underflow.c -- it generates sine waves until the cpu load - exceeds a certain level. However this test is only concerned with - input and so doesn't ouput any sound. - - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2004 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define MAX_SINES (500) -#define MAX_LOAD (1.2) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (512) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -typedef struct paTestData -{ - int sineCount; - double phases[MAX_SINES]; - int countOverflows; - int inputOverflowCount; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float out; /* variable to hold dummy output */ - unsigned long i; - int j; - int finished = paContinue; - (void) timeInfo; /* Prevent unused variable warning. */ - (void) inputBuffer; /* Prevent unused variable warning. */ - (void) outputBuffer; /* Prevent unused variable warning. */ - - if( data->countOverflows && (statusFlags & paInputOverflow) ) - data->inputOverflowCount++; - - for( i=0; isineCount; j++ ) - { - /* Advance phase of next oscillator. */ - phase = data->phases[j]; - phase += phaseInc; - if( phase > TWOPI ) phase -= TWOPI; - - phaseInc *= 1.02; - if( phaseInc > 0.5 ) phaseInc *= 0.5; - - /* This is not a very efficient way to calc sines. */ - output += (float) sin( phase ); - data->phases[j] = phase; - } - /* this is an input-only stream so we don't actually use the output */ - out = (float) (output / data->sineCount); - (void) out; /* suppress unused variable warning*/ - } - - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters inputParameters; - PaStream *stream; - PaError err; - int safeSineCount, stressedSineCount; - int safeOverflowCount, stressedOverflowCount; - paTestData data = {0}; - double load; - - - printf("PortAudio Test: input only, no sound output. Load callback by performing calculations, count input overflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n", - SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ - inputParameters.channelCount = 1; /* mono output */ - inputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, /* no output */ - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Establishing load conditions...\n" ); - - /* Determine number of sines required to get to 50% */ - do - { - data.sineCount++; - Pa_Sleep( 100 ); - - load = Pa_GetStreamCpuLoad( stream ); - printf("sineCount = %d, CPU load = %f\n", data.sineCount, load ); - } - while( load < 0.5 && data.sineCount < (MAX_SINES-1)); - - safeSineCount = data.sineCount; - - /* Calculate target stress value then ramp up to that level*/ - stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD ); - if( stressedSineCount > MAX_SINES ) - stressedSineCount = MAX_SINES; - for( ; data.sineCount < stressedSineCount; data.sineCount++ ) - { - Pa_Sleep( 100 ); - load = Pa_GetStreamCpuLoad( stream ); - printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load ); - } - - printf("Counting overflows for 5 seconds.\n"); - data.countOverflows = 1; - Pa_Sleep( 5000 ); - - stressedOverflowCount = data.inputOverflowCount; - - data.countOverflows = 0; - data.sineCount = safeSineCount; - - printf("Resuming safe load...\n"); - Pa_Sleep( 1500 ); - data.inputOverflowCount = 0; - Pa_Sleep( 1500 ); - load = Pa_GetStreamCpuLoad( stream ); - printf("sineCount = %d, CPU load = %f\n", data.sineCount, load ); - - printf("Counting overflows for 5 seconds.\n"); - data.countOverflows = 1; - Pa_Sleep( 5000 ); - - safeOverflowCount = data.inputOverflowCount; - - printf("Stop stream.\n"); - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - - if( stressedOverflowCount == 0 ) - printf("Test failed, no input overflows detected under stress.\n"); - else if( safeOverflowCount != 0 ) - printf("Test failed, %d unexpected overflows detected under safe load.\n", safeOverflowCount); - else - printf("Test passed, %d expected input overflows detected under stress, 0 unexpected overflows detected under safe load.\n", stressedOverflowCount ); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_latency.c b/portaudio-v19/pa_tests/patest_latency.c deleted file mode 100644 index f4225112a..000000000 --- a/portaudio-v19/pa_tests/patest_latency.c +++ /dev/null @@ -1,182 +0,0 @@ -/** @file - @brief Hear the latency caused by big buffers. - Play a sine wave and change frequency based on letter input. - @author Phil Burk , and Darren Gibbs -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (64) - -#define MIN_FREQ (100.0f) -#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (400) - -typedef struct -{ - float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */ - float phase_increment; - float left_phase; - float right_phase; -} -paTestData; - -float LookupSine( paTestData *data, float phase ); -/* Convert phase between and 1.0 to sine value - * using linear interpolation. - */ -float LookupSine( paTestData *data, float phase ) -{ - float fIndex = phase*TABLE_SIZE; - int index = (int) fIndex; - float fract = fIndex - index; - float lo = data->sine[index]; - float hi = data->sine[index+1]; - float val = lo + fract*(hi-lo); - return val; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - int i; - - (void) inputBuffer; /* Prevent unused variable warning. */ - - for( i=0; ileft_phase); /* left */ - *out++ = LookupSine(data, data->right_phase); /* right */ - data->left_phase += data->phase_increment; - if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; - data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ - if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; - } - return 0; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream *stream; - PaStreamParameters outputParameters; - PaError err; - paTestData data; - int i; - int done = 0; - - printf("PortAudio Test: enter letter then hit ENTER.\n" ); - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - printf("Requested output latency = %.4f seconds.\n", outputParameters.suggestedLatency ); - printf("%d frames per buffer.\n.", FRAMES_PER_BUFFER ); - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Play ASCII keyboard. Hit 'q' to stop. (Use RETURN key on Mac)\n"); - fflush(stdout); - while ( !done ) - { - float freq; - int index; - char c; - do - { - c = getchar(); - } - while( c < ' '); /* Strip white space and control chars. */ - - if( c == 'q' ) done = 1; - index = c % 26; - freq = MIN_FREQ + (index * 40.0); - data.phase_increment = CalcPhaseIncrement(freq); - } - printf("Call Pa_StopStream()\n"); - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_leftright.c b/portaudio-v19/pa_tests/patest_leftright.c deleted file mode 100644 index 158223649..000000000 --- a/portaudio-v19/pa_tests/patest_leftright.c +++ /dev/null @@ -1,173 +0,0 @@ -/** @file patest_leftright.c - @brief Play different tone sine waves that - alternate between left and right channel. - - The low tone should be on the left channel. - - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (8) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (512) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int left_phase; - int right_phase; - int toggle; - int countDown; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - /* Prevent unused variable warnings. */ - (void) inputBuffer; - - for( i=0; itoggle ) - { - *out++ = data->sine[data->left_phase]; /* left */ - *out++ = 0; /* right */ - } - else - { - *out++ = 0; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - } - - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - - if( data->countDown < 0 ) - { - data->countDown = SAMPLE_RATE; - data->toggle = !data->toggle; - } - data->countDown -= framesPerBuffer; - - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream *stream; - PaStreamParameters outputParameters; - PaError err; - paTestData data; - int i; - int timeout; - - printf("Play different tone sine waves that alternate between left and right channel.\n"); - printf("The low tone should be on the left channel.\n"); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( &stream, - NULL, /* No input. */ - &outputParameters, /* As above. */ - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for several seconds.\n"); - timeout = NUM_SECONDS * 4; - while( timeout > 0 ) - { - Pa_Sleep( 300 ); /*(Irix very much likes sleeps <= 1000 ms.)*/ - timeout -= 1; - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_longsine.c b/portaudio-v19/pa_tests/patest_longsine.c deleted file mode 100644 index 3a6ec39ef..000000000 --- a/portaudio-v19/pa_tests/patest_longsine.c +++ /dev/null @@ -1,140 +0,0 @@ -/** @file patest_longsine.c - @brief Play a sine wave until ENTER hit. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include - -#include "portaudio.h" - -#define SAMPLE_RATE (44100) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int left_phase; - int right_phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback(const void* inputBuffer, - void* outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void* userData) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - (void) inputBuffer; /* Prevent unused argument warning. */ - for( i=0; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - return 0; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - printf("PortAudio Test: output sine wave.\n"); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( &stream, - NULL, /* No input. */ - &outputParameters, /* As above. */ - SAMPLE_RATE, - 256, /* Frames per buffer. */ - paClipOff, /* No out of range samples expected. */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Hit ENTER to stop program.\n"); - getchar(); - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - - printf("Test finished.\n"); - return err; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_many.c b/portaudio-v19/pa_tests/patest_many.c deleted file mode 100644 index 6a678467b..000000000 --- a/portaudio-v19/pa_tests/patest_many.c +++ /dev/null @@ -1,198 +0,0 @@ -/** @file patest_many.c - @brief Start and stop the PortAudio Driver multiple times. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include -#include "portaudio.h" -#define NUM_SECONDS (1) -#define SAMPLE_RATE (44100) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (200) -typedef struct -{ - short sine[TABLE_SIZE]; - int left_phase; - int right_phase; - unsigned int sampsToGo; -} -paTestData; -PaError TestOnce( void ); -static int patest1Callback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patest1Callback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - short *out = (short*)outputBuffer; - unsigned int i; - int finished = 0; - (void) inputBuffer; /* Prevent "unused variable" warnings. */ - - if( data->sampsToGo < framesPerBuffer ) - { - /* final buffer... */ - - for( i=0; isampsToGo; i++ ) - { - *out++ = data->sine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - /* zero remainder of final buffer */ - for( ; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - data->sampsToGo -= framesPerBuffer; - } - return finished; -} -/*******************************************************************/ -#ifdef MACINTOSH -int main(void); -int main(void) -{ - int i; - PaError err; - int numLoops = 10; - printf("Loop %d times.\n", numLoops ); - for( i=0; i 1 ) - { - numLoops = atoi(argv[1]); - } - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - 1024, /* frames per buffer */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patest1Callback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Waiting for sound to finish.\n"); - Pa_Sleep(1000); - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - return paNoError; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_maxsines.c b/portaudio-v19/pa_tests/patest_maxsines.c deleted file mode 100644 index 56592ea47..000000000 --- a/portaudio-v19/pa_tests/patest_maxsines.c +++ /dev/null @@ -1,204 +0,0 @@ -/** @file patest_maxsines.c - @brief How many sine waves can we calculate and play in less than 80% CPU Load. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define MAX_SINES (500) -#define MAX_USAGE (0.8) -#define SAMPLE_RATE (44100) -#define FREQ_TO_PHASE_INC(freq) (freq/(float)SAMPLE_RATE) - -#define MIN_PHASE_INC FREQ_TO_PHASE_INC(200.0f) -#define MAX_PHASE_INC (MIN_PHASE_INC * (1 << 5)) - -#define FRAMES_PER_BUFFER (512) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -#define TABLE_SIZE (512) - -typedef struct paTestData -{ - int numSines; - float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */ - float phases[MAX_SINES]; -} -paTestData; - -/* Convert phase between and 1.0 to sine value - * using linear interpolation. - */ -float LookupSine( paTestData *data, float phase ); -float LookupSine( paTestData *data, float phase ) -{ - float fIndex = phase*TABLE_SIZE; - int index = (int) fIndex; - float fract = fIndex - index; - float lo = data->sine[index]; - float hi = data->sine[index+1]; - float val = lo + fract*(hi-lo); - return val; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback(const void* inputBuffer, - void* outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void* userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - float outSample; - float scaler; - int numForScale; - unsigned long i; - int j; - int finished = 0; - (void) inputBuffer; /* Prevent unused argument warning. */ - - /* Determine amplitude scaling factor */ - numForScale = data->numSines; - if( numForScale < 8 ) numForScale = 8; /* prevent pops at beginning */ - scaler = 1.0f / numForScale; - - for( i=0; inumSines; j++ ) - { - /* Advance phase of next oscillator. */ - phase = data->phases[j]; - phase += phaseInc; - if( phase >= 1.0 ) phase -= 1.0; - - output += LookupSine(data, phase); - data->phases[j] = phase; - - phaseInc *= 1.02f; - if( phaseInc > MAX_PHASE_INC ) phaseInc = MIN_PHASE_INC; - } - - outSample = (float) (output * scaler); - *out++ = outSample; /* Left */ - *out++ = outSample; /* Right */ - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - int i; - PaStream* stream; - PaStreamParameters outputParameters; - PaError err; - paTestData data = {0}; - double load; - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultHighOutputLatency; - err = Pa_OpenStream(&stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* No out of range samples should occur. */ - patestCallback, - &data); - if( err != paNoError ) - goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) - goto error; - - /* Play an increasing number of sine waves until we hit MAX_USAGE */ - do { - data.numSines++; - Pa_Sleep(200); - load = Pa_GetStreamCpuLoad(stream); - printf("numSines = %d, CPU load = %f\n", data.numSines, load ); - fflush(stdout); - } while((load < MAX_USAGE) && (data.numSines < MAX_SINES)); - - Pa_Sleep(2000); /* Stay for 2 seconds around 80% CPU. */ - - err = Pa_StopStream( stream ); - if( err != paNoError ) - goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) - goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_mono.c b/portaudio-v19/pa_tests/patest_mono.c deleted file mode 100644 index e7b57362f..000000000 --- a/portaudio-v19/pa_tests/patest_mono.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * $Id$ - * patest_sine.c - * Play a monophonic sine wave using the Portable Audio api for several seconds. - * - * Authors: - * Ross Bencina - * Phil Burk - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (10) -#define SAMPLE_RATE (44100) -#define AMPLITUDE (0.8) -#define FRAMES_PER_BUFFER (64) -#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice() - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - /* avoid unused variable warnings */ - (void) inputBuffer; - (void) timeInfo; - (void) statusFlags; - for( i=0; isine[data->phase]; /* left */ - data->phase += 1; - if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE; - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - printf("PortAudio Test: output MONO sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for %d seconds.\n", NUM_SECONDS ); fflush(stdout); - Pa_Sleep( NUM_SECONDS * 1000 ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} \ No newline at end of file diff --git a/portaudio-v19/pa_tests/patest_mono_asio_channel_select.c b/portaudio-v19/pa_tests/patest_mono_asio_channel_select.c deleted file mode 100644 index 4c4ffccdf..000000000 --- a/portaudio-v19/pa_tests/patest_mono_asio_channel_select.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * $Id$ - * patest_sine.c - * Play a monophonic sine wave using the Portable Audio api for several seconds. - * - * Authors: - * Ross Bencina - * Phil Burk - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#include "pa_asio.h" - -#define NUM_SECONDS (10) -#define SAMPLE_RATE (44100) -#define AMPLITUDE (0.8) -#define FRAMES_PER_BUFFER (64) -#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice() - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - /* avoid unused variable warnings */ - (void) inputBuffer; - (void) timeInfo; - (void) statusFlags; - for( i=0; isine[data->phase]; /* left */ - data->phase += 1; - if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE; - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaAsioStreamInfo asioOutputInfo; - PaStream *stream; - PaError err; - paTestData data; - int outputChannelSelectors[1]; - int i; - printf("PortAudio Test: output MONO sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - - asioOutputInfo.size = sizeof(PaAsioStreamInfo); - asioOutputInfo.hostApiType = paASIO; - asioOutputInfo.version = 1; - asioOutputInfo.flags = paAsioUseChannelSelectors; - outputChannelSelectors[0] = 1; /* select the second (right) ASIO device channel */ - asioOutputInfo.channelSelectors = outputChannelSelectors; - outputParameters.hostApiSpecificStreamInfo = &asioOutputInfo; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for %d seconds.\n", NUM_SECONDS ); fflush(stdout); - Pa_Sleep( NUM_SECONDS * 1000 ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_multi_sine.c b/portaudio-v19/pa_tests/patest_multi_sine.c deleted file mode 100644 index 161c88222..000000000 --- a/portaudio-v19/pa_tests/patest_multi_sine.c +++ /dev/null @@ -1,194 +0,0 @@ -/** @file patest_multi_sine.c - @brief Play a different sine wave on each channel. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include - -#include "portaudio.h" - -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#define FREQ_INCR (300.0 / SAMPLE_RATE) -#define MAX_CHANNELS (64) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -typedef struct -{ - short interleaved; /* Nonzero for interleaved / zero for non-interleaved. */ - int numChannels; /* Actually used. */ - double phases[MAX_CHANNELS]; /* Each channel gets its' own frequency. */ -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback(const void* inputBuffer, - void* outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void* userData) -{ - int frameIndex, channelIndex; - float** outputs = (float**)outputBuffer; - paTestData* data = (paTestData*)userData; - - (void) inputBuffer; /* Prevent unused arg warning. */ - if (data->interleaved) - { - float *out = (float*)outputBuffer; /* interleaved version */ - for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) - { - for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) - { - /* Output sine wave on every channel. */ - *out++ = (float) sin(data->phases[channelIndex]); - - /* Play each channel at a higher frequency. */ - data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); - if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); - } - } - } - else - { - for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) - { - for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) - { - /* Output sine wave on every channel. */ - outputs[channelIndex][frameIndex] = (float) sin(data->phases[channelIndex]); - - /* Play each channel at a higher frequency. */ - data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); - if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); - } - } - } - return 0; -} - -/*******************************************************************/ -int test(short interleaved) -{ - PaStream* stream; - PaStreamParameters outputParameters; - PaError err; - const PaDeviceInfo* pdi; - paTestData data; - short n; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device, max channels. */ - pdi = Pa_GetDeviceInfo(outputParameters.device); - outputParameters.channelCount = pdi->maxOutputChannels; - if (outputParameters.channelCount > MAX_CHANNELS) - outputParameters.channelCount = MAX_CHANNELS; - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = pdi->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - data.interleaved = interleaved; - data.numChannels = outputParameters.channelCount; - for (n = 0; n < data.numChannels; n++) - data.phases[n] = 0.0; /* Phases wrap and maybe don't need initialisation. */ - printf("%d ", data.numChannels); - if (interleaved) - printf("interleaved "); - else - { - printf(" non-interleaved "); - outputParameters.sampleFormat |= paNonInterleaved; - } - printf("channels.\n"); - - err = Pa_OpenStream(&stream, - NULL, /* No input. */ - &outputParameters, - SAMPLE_RATE, /* Sample rate. */ - FRAMES_PER_BUFFER, /* Frames per buffer. */ - paClipOff, /* Samples never out of range, no clipping. */ - patestCallback, - &data); - if (err == paNoError) - { - err = Pa_StartStream(stream); - if (err == paNoError) - { - printf("Hit ENTER to stop this test.\n"); - getchar(); - err = Pa_StopStream(stream); - } - Pa_CloseStream( stream ); - } - return err; -} - - -/*******************************************************************/ -int main(void) -{ - PaError err; - - printf("PortAudio Test: output sine wave on each channel.\n" ); - - err = Pa_Initialize(); - if (err != paNoError) - goto done; - - err = test(1); /* 1 means interleaved. */ - if (err != paNoError) - goto done; - - err = test(0); /* 0 means not interleaved. */ - if (err != paNoError) - goto done; - - printf("Test finished.\n"); -done: - if (err) - { - fprintf(stderr, "An error occured while using the portaudio stream\n"); - fprintf(stderr, "Error number: %d\n", err ); - fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err)); - } - Pa_Terminate(); - return 0; -} diff --git a/portaudio-v19/pa_tests/patest_out_underflow.c b/portaudio-v19/pa_tests/patest_out_underflow.c deleted file mode 100644 index 8e6f27897..000000000 --- a/portaudio-v19/pa_tests/patest_out_underflow.c +++ /dev/null @@ -1,217 +0,0 @@ -/** @file patest_out_underflow.c - @brief Count output underflows (using paOutputUnderflow flag) - under overloaded and normal conditions. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2004 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define MAX_SINES (500) -#define MAX_LOAD (1.2) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (512) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -typedef struct paTestData -{ - int sineCount; - double phases[MAX_SINES]; - int countUnderflows; - int outputUnderflowCount; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int j; - int finished = paContinue; - (void) timeInfo; /* Prevent unused variable warning. */ - (void) inputBuffer; /* Prevent unused variable warning. */ - - - if( data->countUnderflows && (statusFlags & paOutputUnderflow) ) - data->outputUnderflowCount++; - - for( i=0; isineCount; j++ ) - { - /* Advance phase of next oscillator. */ - phase = data->phases[j]; - phase += phaseInc; - if( phase > TWOPI ) phase -= TWOPI; - - phaseInc *= 1.02; - if( phaseInc > 0.5 ) phaseInc *= 0.5; - - /* This is not a very efficient way to calc sines. */ - output += (float) sin( phase ); - data->phases[j] = phase; - } - *out++ = (float) (output / data->sineCount); - } - - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - int safeSineCount, stressedSineCount; - int safeUnderflowCount, stressedUnderflowCount; - paTestData data = {0}; - double load; - - - printf("PortAudio Test: output sine waves, count underflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n", - SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 1; /* mono output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Establishing load conditions...\n" ); - - /* Determine number of sines required to get to 50% */ - do - { - data.sineCount++; - Pa_Sleep( 100 ); - - load = Pa_GetStreamCpuLoad( stream ); - printf("sineCount = %d, CPU load = %f\n", data.sineCount, load ); - } - while( load < 0.5 && data.sineCount < (MAX_SINES-1)); - - safeSineCount = data.sineCount; - - /* Calculate target stress value then ramp up to that level*/ - stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD ); - if( stressedSineCount > MAX_SINES ) - stressedSineCount = MAX_SINES; - for( ; data.sineCount < stressedSineCount; data.sineCount++ ) - { - Pa_Sleep( 100 ); - load = Pa_GetStreamCpuLoad( stream ); - printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load ); - } - - printf("Counting underflows for 5 seconds.\n"); - data.countUnderflows = 1; - Pa_Sleep( 5000 ); - - stressedUnderflowCount = data.outputUnderflowCount; - - data.countUnderflows = 0; - data.sineCount = safeSineCount; - - printf("Resuming safe load...\n"); - Pa_Sleep( 1500 ); - data.outputUnderflowCount = 0; - Pa_Sleep( 1500 ); - load = Pa_GetStreamCpuLoad( stream ); - printf("sineCount = %d, CPU load = %f\n", data.sineCount, load ); - - printf("Counting underflows for 5 seconds.\n"); - data.countUnderflows = 1; - Pa_Sleep( 5000 ); - - safeUnderflowCount = data.outputUnderflowCount; - - printf("Stop stream.\n"); - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - - if( stressedUnderflowCount == 0 ) - printf("Test failed, no output underflows detected under stress.\n"); - else if( safeUnderflowCount != 0 ) - printf("Test failed, %d unexpected underflows detected under safe load.\n", safeUnderflowCount); - else - printf("Test passed, %d expected output underflows detected under stress, 0 unexpected underflows detected under safe load.\n", stressedUnderflowCount ); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_pink.c b/portaudio-v19/pa_tests/patest_pink.c deleted file mode 100644 index f2db85d9c..000000000 --- a/portaudio-v19/pa_tests/patest_pink.c +++ /dev/null @@ -1,269 +0,0 @@ -/** @file patest_pink.c - @brief Generate Pink Noise using Gardner method. - - Optimization suggested by James McCartney uses a tree - to select which random value to replace. -

-	x x x x x x x x x x x x x x x x 
-	x   x   x   x   x   x   x   x   
-	x       x       x       x       
-	 x               x               
-	   x   
-
- Tree is generated by counting trailing zeros in an increasing index. - When the index is zero, no random number is selected. - - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" - -#define PINK_MAX_RANDOM_ROWS (30) -#define PINK_RANDOM_BITS (24) -#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) - -typedef struct -{ - long pink_Rows[PINK_MAX_RANDOM_ROWS]; - long pink_RunningSum; /* Used to optimize summing of generators. */ - int pink_Index; /* Incremented each sample. */ - int pink_IndexMask; /* Index wrapped by ANDing with this mask. */ - float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */ -} -PinkNoise; - -/* Prototypes */ -static unsigned long GenerateRandomNumber( void ); -void InitializePinkNoise( PinkNoise *pink, int numRows ); -float GeneratePinkNoise( PinkNoise *pink ); - -/************************************************************/ -/* Calculate pseudo-random 32 bit number based on linear congruential method. */ -static unsigned long GenerateRandomNumber( void ) -{ - /* Change this seed for different random sequences. */ - static unsigned long randSeed = 22222; - randSeed = (randSeed * 196314165) + 907633515; - return randSeed; -} - -/************************************************************/ -/* Setup PinkNoise structure for N rows of generators. */ -void InitializePinkNoise( PinkNoise *pink, int numRows ) -{ - int i; - long pmax; - pink->pink_Index = 0; - pink->pink_IndexMask = (1<pink_Scalar = 1.0f / pmax; - /* Initialize rows. */ - for( i=0; ipink_Rows[i] = 0; - pink->pink_RunningSum = 0; -} - -#define PINK_MEASURE -#ifdef PINK_MEASURE -float pinkMax = -999.0; -float pinkMin = 999.0; -#endif - -/* Generate Pink noise values between -1.0 and +1.0 */ -float GeneratePinkNoise( PinkNoise *pink ) -{ - long newRandom; - long sum; - float output; - /* Increment and mask index. */ - pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask; - /* If index is zero, don't update any random values. */ - if( pink->pink_Index != 0 ) - { - /* Determine how many trailing zeros in PinkIndex. */ - /* This algorithm will hang if n==0 so test first. */ - int numZeros = 0; - int n = pink->pink_Index; - while( (n & 1) == 0 ) - { - n = n >> 1; - numZeros++; - } - /* Replace the indexed ROWS random value. - * Subtract and add back to RunningSum instead of adding all the random - * values together. Only one changes each time. - */ - pink->pink_RunningSum -= pink->pink_Rows[numZeros]; - newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; - pink->pink_RunningSum += newRandom; - pink->pink_Rows[numZeros] = newRandom; - } - - /* Add extra white noise value. */ - newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; - sum = pink->pink_RunningSum + newRandom; - /* Scale to range of -1.0 to 0.9999. */ - output = pink->pink_Scalar * sum; -#ifdef PINK_MEASURE - /* Check Min/Max */ - if( output > pinkMax ) pinkMax = output; - else if( output < pinkMin ) pinkMin = output; -#endif - return output; -} - -/*******************************************************************/ -#define PINK_TEST -#ifdef PINK_TEST - -/* Context for callback routine. */ -typedef struct -{ - PinkNoise leftPink; - PinkNoise rightPink; - unsigned int sampsToGo; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback(const void* inputBuffer, - void* outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void* userData) -{ - int finished; - int i; - int numFrames; - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - (void) inputBuffer; /* Prevent "unused variable" warnings. */ - - /* Are we almost at end. */ - if( data->sampsToGo < framesPerBuffer ) - { - numFrames = data->sampsToGo; - finished = 1; - } - else - { - numFrames = framesPerBuffer; - finished = 0; - } - for( i=0; ileftPink ); - *out++ = GeneratePinkNoise( &data->rightPink ); - } - data->sampsToGo -= numFrames; - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream* stream; - PaError err; - paTestData data; - PaStreamParameters outputParameters; - int totalSamps; - static const double SR = 44100.0; - static const int FPB = 2048; /* Frames per buffer: 46 ms buffers. */ - - /* Initialize two pink noise signals with different numbers of rows. */ - InitializePinkNoise( &data.leftPink, 12 ); - InitializePinkNoise( &data.rightPink, 16 ); - - /* Look at a few values. */ - { - int i; - float pink; - for( i=0; i<20; i++ ) - { - pink = GeneratePinkNoise( &data.leftPink ); - printf("Pink = %f\n", pink ); - } - } - - data.sampsToGo = totalSamps = (int)(60.0 * SR); /* Play a whole minute. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - /* Open a stereo PortAudio stream so we can hear the result. */ - outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */ - outputParameters.channelCount = 2; /* Stereo output, most likely supported. */ - outputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */ - outputParameters.suggestedLatency = - Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; - err = Pa_OpenStream(&stream, - NULL, /* No input. */ - &outputParameters, - SR, /* Sample rate. */ - FPB, /* Frames per buffer. */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Stereo pink noise for one minute...\n"); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); - if( err < 0 ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; -#ifdef PINK_MEASURE - printf("Pink min = %f, max = %f\n", pinkMin, pinkMax ); -#endif - Pa_Terminate(); - return 0; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return 0; -} -#endif /* PINK_TEST */ diff --git a/portaudio-v19/pa_tests/patest_prime.c b/portaudio-v19/pa_tests/patest_prime.c deleted file mode 100644 index 47d1f5f56..000000000 --- a/portaudio-v19/pa_tests/patest_prime.c +++ /dev/null @@ -1,223 +0,0 @@ -/** @file patest_prime.c - @brief Test stream priming mode. - @author Ross Bencina http://www.audiomulch.com/~rossb -*/ - -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" -#include "pa_util.h" - -#define NUM_BEEPS (3) -#define SAMPLE_RATE (44100) -#define SAMPLE_PERIOD (1.0/44100.0) -#define FRAMES_PER_BUFFER (256) -#define BEEP_DURATION (400) -#define IDLE_DURATION (SAMPLE_RATE*2) /* 2 seconds */ -#define SLEEP_MSEC (50) - -#define STATE_BKG_IDLE (0) -#define STATE_BKG_BEEPING (1) - -typedef struct -{ - float leftPhase; - float rightPhase; - int state; - int beepCountdown; - int idleCountdown; -} -paTestData; - -static void InitializeTestData( paTestData *testData ) -{ - testData->leftPhase = 0; - testData->rightPhase = 0; - testData->state = STATE_BKG_BEEPING; - testData->beepCountdown = BEEP_DURATION; - testData->idleCountdown = IDLE_DURATION; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, void *userData ) -{ - /* Cast data passed through stream to our structure. */ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - int result = paContinue; - - /* supress unused parameter warnings */ - (void) inputBuffer; - (void) timeInfo; - (void) statusFlags; - - for( i=0; istate ) - { - case STATE_BKG_IDLE: - *out++ = 0.0; /* left */ - *out++ = 0.0; /* right */ - --data->idleCountdown; - - if( data->idleCountdown <= 0 ) result = paComplete; - break; - - case STATE_BKG_BEEPING: - if( data->beepCountdown <= 0 ) - { - data->state = STATE_BKG_IDLE; - *out++ = 0.0; /* left */ - *out++ = 0.0; /* right */ - } - else - { - /* Play sawtooth wave. */ - *out++ = data->leftPhase; /* left */ - *out++ = data->rightPhase; /* right */ - /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ - data->leftPhase += 0.01f; - /* When signal reaches top, drop back down. */ - if( data->leftPhase >= 1.0f ) data->leftPhase -= 2.0f; - /* higher pitch so we can distinguish left and right. */ - data->rightPhase += 0.03f; - if( data->rightPhase >= 1.0f ) data->rightPhase -= 2.0f; - } - --data->beepCountdown; - break; - } - } - - return result; -} - -/*******************************************************************/ -static PaError DoTest( int flags ) -{ - PaStream *stream; - PaError err; - paTestData data; - PaStreamParameters outputParameters; - - InitializeTestData( &data ); - - outputParameters.device = Pa_GetDefaultOutputDevice(); - outputParameters.channelCount = 2; - outputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.sampleFormat = paFloat32; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - - /* Open an audio I/O stream. */ - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - paClipOff | flags, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("hear \"BEEP\"\n" ); - fflush(stdout); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(SLEEP_MSEC); - if( err < 0 ) goto error; - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - return err; -error: - return err; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err = paNoError; - int i; - - /* Initialize library before making any other calls. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - printf("PortAudio Test: Testing stream playback with no priming.\n"); - printf("PortAudio Test: you should see BEEP before you hear it.\n"); - printf("BEEP %d times.\n", NUM_BEEPS ); - - for( i=0; i< NUM_BEEPS; ++i ) - { - err = DoTest( 0 ); - if( err != paNoError ) - goto error; - } - - printf("PortAudio Test: Testing stream playback with priming.\n"); - printf("PortAudio Test: you should see BEEP around the same time you hear it.\n"); - for( i=0; i< NUM_BEEPS; ++i ) - { - err = DoTest( paPrimeOutputBuffersUsingStreamCallback ); - if( err != paNoError ) - goto error; - } - - printf("Test finished.\n"); - - Pa_Terminate(); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_read_record.c b/portaudio-v19/pa_tests/patest_read_record.c deleted file mode 100644 index 5e9ca0109..000000000 --- a/portaudio-v19/pa_tests/patest_read_record.c +++ /dev/null @@ -1,229 +0,0 @@ -/** @file patest_read_record.c - @brief Record input into an array; Save array to a file; Playback recorded - data. Implemented using the blocking API (Pa_ReadStream(), Pa_WriteStream() ) - @author Phil Burk http://www.softsynth.com - @author Ross Bencina rossb@audiomulch.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" - -/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */ -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (1024) -#define NUM_SECONDS (5) -#define NUM_CHANNELS (2) -/* #define DITHER_FLAG (paDitherOff) */ -#define DITHER_FLAG (0) /**/ - -/* Select sample format. */ -#if 1 -#define PA_SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#define SAMPLE_SILENCE (0.0f) -#define PRINTF_S_FORMAT "%.8f" -#elif 1 -#define PA_SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#define SAMPLE_SILENCE (0) -#define PRINTF_S_FORMAT "%d" -#elif 0 -#define PA_SAMPLE_TYPE paInt8 -typedef char SAMPLE; -#define SAMPLE_SILENCE (0) -#define PRINTF_S_FORMAT "%d" -#else -#define PA_SAMPLE_TYPE paUInt8 -typedef unsigned char SAMPLE; -#define SAMPLE_SILENCE (128) -#define PRINTF_S_FORMAT "%d" -#endif - - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters inputParameters, outputParameters; - PaStream *stream; - PaError err; - SAMPLE *recordedSamples; - int i; - int totalFrames; - int numSamples; - int numBytes; - SAMPLE max, average, val; - - - printf("patest_read_record.c\n"); fflush(stdout); - - totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ - numSamples = totalFrames * NUM_CHANNELS; - - numBytes = numSamples * sizeof(SAMPLE); - recordedSamples = (SAMPLE *) malloc( numBytes ); - if( recordedSamples == NULL ) - { - printf("Could not allocate record array.\n"); - exit(1); - } - for( i=0; idefaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - /* Record some audio. -------------------------------------------- */ - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, /* &outputParameters, */ - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Now recording!!\n"); fflush(stdout); - - err = Pa_ReadStream( stream, recordedSamples, totalFrames ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - /* Measure maximum peak amplitude. */ - max = 0; - average = 0; - for( i=0; i max ) - { - max = val; - } - average += val; - } - - average = average / numSamples; - - printf("Sample max amplitude = "PRINTF_S_FORMAT"\n", max ); - printf("Sample average = "PRINTF_S_FORMAT"\n", average ); -/* Was as below. Better choose at compile time because this - keeps generating compiler-warnings: - if( PA_SAMPLE_TYPE == paFloat32 ) - { - printf("sample max amplitude = %f\n", max ); - printf("sample average = %f\n", average ); - } - else - { - printf("sample max amplitude = %d\n", max ); - printf("sample average = %d\n", average ); - } -*/ - /* Write recorded data to a file. */ -#if 0 - { - FILE *fid; - fid = fopen("recorded.raw", "wb"); - if( fid == NULL ) - { - printf("Could not open file."); - } - else - { - fwrite( recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); - fclose( fid ); - printf("Wrote data to 'recorded.raw'\n"); - } - } -#endif - - /* Playback recorded data. -------------------------------------------- */ - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = NUM_CHANNELS; - outputParameters.sampleFormat = PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - printf("Begin playback.\n"); fflush(stdout); - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if( err != paNoError ) goto error; - - if( stream ) - { - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - printf("Waiting for playback to finish.\n"); fflush(stdout); - - err = Pa_WriteStream( stream, recordedSamples, totalFrames ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - printf("Done.\n"); fflush(stdout); - } - free( recordedSamples ); - - Pa_Terminate(); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return -1; -} - diff --git a/portaudio-v19/pa_tests/patest_record.c b/portaudio-v19/pa_tests/patest_record.c deleted file mode 100644 index 7ba35825d..000000000 --- a/portaudio-v19/pa_tests/patest_record.c +++ /dev/null @@ -1,337 +0,0 @@ -/** @file patest_record.c - @brief Record input into an array; Save array to a file; Playback recorded data. - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" - -/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */ -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (1024) -#define NUM_SECONDS (5) -#define NUM_CHANNELS (2) -/* #define DITHER_FLAG (paDitherOff) */ -#define DITHER_FLAG (0) /**/ - -/* Select sample format. */ -#if 1 -#define PA_SAMPLE_TYPE paFloat32 -typedef float SAMPLE; -#define SAMPLE_SILENCE (0.0f) -#define PRINTF_S_FORMAT "%.8f" -#elif 1 -#define PA_SAMPLE_TYPE paInt16 -typedef short SAMPLE; -#define SAMPLE_SILENCE (0) -#define PRINTF_S_FORMAT "%d" -#elif 0 -#define PA_SAMPLE_TYPE paInt8 -typedef char SAMPLE; -#define SAMPLE_SILENCE (0) -#define PRINTF_S_FORMAT "%d" -#else -#define PA_SAMPLE_TYPE paUInt8 -typedef unsigned char SAMPLE; -#define SAMPLE_SILENCE (128) -#define PRINTF_S_FORMAT "%d" -#endif - -typedef struct -{ - int frameIndex; /* Index into sample array. */ - int maxFrameIndex; - SAMPLE *recordedSamples; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int recordCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - const SAMPLE *rptr = (const SAMPLE*)inputBuffer; - SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; - long framesToCalc; - long i; - int finished; - unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; - - (void) outputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - (void) userData; - - if( framesLeft < framesPerBuffer ) - { - framesToCalc = framesLeft; - finished = paComplete; - } - else - { - framesToCalc = framesPerBuffer; - finished = paContinue; - } - - if( inputBuffer == NULL ) - { - for( i=0; iframeIndex += framesToCalc; - return finished; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int playCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; - SAMPLE *wptr = (SAMPLE*)outputBuffer; - unsigned int i; - int finished; - unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; - - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - (void) userData; - - if( framesLeft < framesPerBuffer ) - { - /* final buffer... */ - for( i=0; iframeIndex += framesLeft; - finished = paComplete; - } - else - { - for( i=0; iframeIndex += framesPerBuffer; - finished = paContinue; - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters inputParameters, - outputParameters; - PaStream* stream; - PaError err = paNoError; - paTestData data; - int i; - int totalFrames; - int numSamples; - int numBytes; - SAMPLE max, val; - double average; - - printf("patest_record.c\n"); fflush(stdout); - - data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ - data.frameIndex = 0; - numSamples = totalFrames * NUM_CHANNELS; - numBytes = numSamples * sizeof(SAMPLE); - data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */ - if( data.recordedSamples == NULL ) - { - printf("Could not allocate record array.\n"); - goto done; - } - for( i=0; idefaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - /* Record some audio. -------------------------------------------- */ - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, /* &outputParameters, */ - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - recordCallback, - &data ); - if( err != paNoError ) goto done; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto done; - printf("Now recording!!\n"); fflush(stdout); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) - { - Pa_Sleep(1000); - printf("index = %d\n", data.frameIndex ); fflush(stdout); - } - if( err < 0 ) goto done; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto done; - - /* Measure maximum peak amplitude. */ - max = 0; - average = 0.0; - for( i=0; i max ) - { - max = val; - } - average += val; - } - - average = average / (double)numSamples; - - printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max ); - printf("sample average = %lf\n", average ); - - /* Write recorded data to a file. */ -#if 0 - { - FILE *fid; - fid = fopen("recorded.raw", "wb"); - if( fid == NULL ) - { - printf("Could not open file."); - } - else - { - fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); - fclose( fid ); - printf("Wrote data to 'recorded.raw'\n"); - } - } -#endif - - /* Playback recorded data. -------------------------------------------- */ - data.frameIndex = 0; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - printf("Begin playback.\n"); fflush(stdout); - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - playCallback, - &data ); - if( err != paNoError ) goto done; - - if( stream ) - { - err = Pa_StartStream( stream ); - if( err != paNoError ) goto done; - - printf("Waiting for playback to finish.\n"); fflush(stdout); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); - if( err < 0 ) goto done; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto done; - - printf("Done.\n"); fflush(stdout); - } - -done: - Pa_Terminate(); - if( data.recordedSamples ) /* Sure it is NULL or valid. */ - free( data.recordedSamples ); - if( err != paNoError ) - { - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - err = 1; /* Always return 0 or 1, but no other return codes. */ - } - return err; -} - diff --git a/portaudio-v19/pa_tests/patest_ringmix.c b/portaudio-v19/pa_tests/patest_ringmix.c deleted file mode 100644 index 1df062388..000000000 --- a/portaudio-v19/pa_tests/patest_ringmix.c +++ /dev/null @@ -1,79 +0,0 @@ -/** @file patest_ringmix.c - @brief Ring modulate inputs to left output, mix inputs to right output. -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - - -#include "stdio.h" -#include "portaudio.h" -/* This will be called asynchronously by the PortAudio engine. */ -static int myCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - const float *in = (const float *) inputBuffer; - float *out = (float *) outputBuffer; - float leftInput, rightInput; - unsigned int i; - - /* Read input buffer, process data, and fill output buffer. */ - for( i=0; i -#include -#include "portaudio.h" -#define NUM_SECONDS (4) -#define SAMPLE_RATE (44100) - -typedef struct -{ - float left_phase; - float right_phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - /* Cast data passed through stream to our structure. */ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - (void) inputBuffer; /* Prevent unused variable warning. */ - - for( i=0; ileft_phase; /* left */ - *out++ = data->right_phase; /* right */ - /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ - data->left_phase += 0.01f; - /* When signal reaches top, drop back down. */ - if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; - /* higher pitch so we can distinguish left and right. */ - data->right_phase += 0.03f; - if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; - } - return 0; -} - -/*******************************************************************/ -static paTestData data; -int main(void); -int main(void) -{ - PaStream *stream; - PaError err; - - printf("PortAudio Test: output sawtooth wave.\n"); - /* Initialize our data for use by callback. */ - data.left_phase = data.right_phase = 0.0; - /* Initialize library before making any other calls. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - /* Open an audio I/O stream. */ - err = Pa_OpenDefaultStream( &stream, - 0, /* no input channels */ - 2, /* stereo output */ - paFloat32, /* 32 bit floating point output */ - SAMPLE_RATE, - 256, /* frames per buffer */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - /* Sleep for several seconds. */ - Pa_Sleep(NUM_SECONDS*1000); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_sine.c b/portaudio-v19/pa_tests/patest_sine.c deleted file mode 100644 index c952b5805..000000000 --- a/portaudio-v19/pa_tests/patest_sine.c +++ /dev/null @@ -1,151 +0,0 @@ -/** @file patest_sine.c - @brief Play a sine wave for several seconds. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (5) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (64) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int left_phase; - int right_phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - - (void) timeInfo; /* Prevent unused variable warnings. */ - (void) statusFlags; - (void) inputBuffer; - - for( i=0; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - - return paContinue; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for %d seconds.\n", NUM_SECONDS ); - Pa_Sleep( NUM_SECONDS * 1000 ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_sine8.c b/portaudio-v19/pa_tests/patest_sine8.c deleted file mode 100644 index 5a9d1fe72..000000000 --- a/portaudio-v19/pa_tests/patest_sine8.c +++ /dev/null @@ -1,212 +0,0 @@ -/** @file patest_sine8.c - @brief Test 8 bit data: play a sine wave for several seconds. - @author Ross Bencina -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (8) -#define SAMPLE_RATE (44100) -#define TABLE_SIZE (200) -#define TEST_UNSIGNED (0) - -#if TEST_UNSIGNED -#define TEST_FORMAT paUInt8 -#else -#define TEST_FORMAT paInt8 -#endif - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -typedef struct -{ -#if TEST_UNSIGNED - unsigned char sine[TABLE_SIZE]; -#else - char sine[TABLE_SIZE]; -#endif - int left_phase; - int right_phase; - unsigned int framesToGo; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - char *out = (char*)outputBuffer; - int i; - int framesToCalc; - int finished = 0; - (void) inputBuffer; /* Prevent unused variable warnings. */ - - if( data->framesToGo < framesPerBuffer ) - { - framesToCalc = data->framesToGo; - data->framesToGo = 0; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - data->framesToGo -= framesPerBuffer; - } - - for( i=0; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - /* zero remainder of final buffer */ - for( ; i<(int)framesPerBuffer; i++ ) - { -#if TEST_UNSIGNED - *out++ = (unsigned char) 0x80; /* left */ - *out++ = (unsigned char) 0x80; /* right */ -#else - *out++ = 0; /* left */ - *out++ = 0; /* right */ -#endif - - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream* stream; - PaError err; - paTestData data; - PaTime streamOpened; - int i, totalSamps; - -#if TEST_UNSIGNED - printf("PortAudio Test: output UNsigned 8 bit sine wave.\n"); -#else - printf("PortAudio Test: output signed 8 bit sine wave.\n"); -#endif - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - err = Pa_OpenStream( &stream, - NULL, /* No input. */ - &outputParameters, - SAMPLE_RATE, - 256, /* Frames per buffer. */ - paClipOff, /* We won't output out of range samples so don't bother clipping them. */ - patestCallback, - &data ); - if( err != paNoError ) - goto error; - - streamOpened = Pa_GetStreamTime( stream ); /* Time in seconds when stream was opened (approx). */ - - err = Pa_StartStream( stream ); - if( err != paNoError ) - goto error; - - /* Watch until sound is halfway finished. */ - /* (Was ( Pa_StreamTime( stream ) < (totalSamps/2) ) in V18. */ - while( (Pa_GetStreamTime( stream ) - streamOpened) < (PaTime)NUM_SECONDS / 2.0 ) - Pa_Sleep(10); - - /* Stop sound until ENTER hit. (Hu? don't see any keyboard-input here.) */ - err = Pa_StopStream( stream ); - if( err != paNoError ) - goto error; - - printf("Pause for 2 seconds.\n"); - Pa_Sleep( 2000 ); - - err = Pa_StartStream( stream ); - if( err != paNoError ) - goto error; - - printf("Waiting for sound to finish.\n"); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) - Pa_Sleep(100); - if( err < 0 ) - goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) - goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_sine_formats.c b/portaudio-v19/pa_tests/patest_sine_formats.c deleted file mode 100644 index 448a4ebe9..000000000 --- a/portaudio-v19/pa_tests/patest_sine_formats.c +++ /dev/null @@ -1,196 +0,0 @@ -/** @file patest_sine_formats.c - @brief Play a sine wave for several seconds. Test various data formats. - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (10) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (512) -#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */ -#define RIGHT_FREQ (500.0) -#define AMPLITUDE (1.0) - -/* Select ONE format for testing. */ -#define TEST_UINT8 (0) -#define TEST_INT8 (0) -#define TEST_INT16 (1) -#define TEST_FLOAT32 (0) - -#if TEST_UINT8 -#define TEST_FORMAT paUInt8 -typedef unsigned char SAMPLE_t; -#define SAMPLE_ZERO (0x80) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) -#define FORMAT_NAME "Unsigned 8 Bit" - -#elif TEST_INT8 -#define TEST_FORMAT paInt8 -typedef char SAMPLE_t; -#define SAMPLE_ZERO (0) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) -#define FORMAT_NAME "Signed 8 Bit" - -#elif TEST_INT16 -#define TEST_FORMAT paInt16 -typedef short SAMPLE_t; -#define SAMPLE_ZERO (0) -#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x))) -#define FORMAT_NAME "Signed 16 Bit" - -#elif TEST_FLOAT32 -#define TEST_FORMAT paFloat32 -typedef float SAMPLE_t; -#define SAMPLE_ZERO (0.0) -#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x)) -#define FORMAT_NAME "Float 32 Bit" -#endif - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - - -typedef struct -{ - double left_phase; - double right_phase; - unsigned int framesToGo; -} -paTestData; -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - SAMPLE_t *out = (SAMPLE_t *)outputBuffer; - int i; - int framesToCalc; - int finished = 0; - (void) inputBuffer; /* Prevent unused variable warnings. */ - - if( data->framesToGo < framesPerBuffer ) - { - framesToCalc = data->framesToGo; - data->framesToGo = 0; - finished = 1; - } - else - { - framesToCalc = framesPerBuffer; - data->framesToGo -= framesPerBuffer; - } - - for( i=0; ileft_phase += (LEFT_FREQ / SAMPLE_RATE); - if( data->left_phase > 1.0) data->left_phase -= 1.0; - *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); - - data->right_phase += (RIGHT_FREQ / SAMPLE_RATE); - if( data->right_phase > 1.0) data->right_phase -= 1.0; - *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. ))); - } - /* zero remainder of final buffer */ - for( ; i<(int)framesPerBuffer; i++ ) - { - *out++ = SAMPLE_ZERO; /* left */ - *out++ = SAMPLE_ZERO; /* right */ - } - return finished; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream *stream; - PaStreamParameters outputParameters; - PaError err; - paTestData data; - int totalSamps; - - printf("PortAudio Test: output " FORMAT_NAME "\n"); - - data.left_phase = data.right_phase = 0.0; - data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */ - outputParameters.channelCount = 2; /* Stereo output */ - outputParameters.sampleFormat = TEST_FORMAT; /* Selected above. */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - err = Pa_OpenStream( &stream, - NULL, /* No input. */ - &outputParameters, /* As above. */ - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS ); - - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); - if( err < 0 ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - - printf("PortAudio Test Finished: " FORMAT_NAME "\n"); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_sine_time.c b/portaudio-v19/pa_tests/patest_sine_time.c deleted file mode 100644 index cf45275fe..000000000 --- a/portaudio-v19/pa_tests/patest_sine_time.c +++ /dev/null @@ -1,208 +0,0 @@ -/** @file patest_sine_time.c - @brief Play a sine wave for several seconds, pausing in the middle. - Uses the Pa_GetStreamTime() call. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include - -#include "portaudio.h" -#include "pa_util.h" - -#define NUM_SECONDS (8) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (64) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -#define TABLE_SIZE (200) - -typedef struct -{ - double left_phase; - double right_phase; - volatile PaTime outTime; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - - double left_phaseInc = 0.02; - double right_phaseInc = 0.06; - - double left_phase = data->left_phase; - double right_phase = data->right_phase; - - (void) statusFlags; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - data->outTime = timeInfo->outputBufferDacTime; - - for( i=0; i TWOPI ) left_phase -= TWOPI; - *out++ = (float) sin( left_phase ); - - right_phase += right_phaseInc; - if( right_phase > TWOPI ) right_phase -= TWOPI; - *out++ = (float) sin( right_phase ); - } - - data->left_phase = left_phase; - data->right_phase = right_phase; - - return paContinue; -} - -/*******************************************************************/ -static void ReportStreamTime( PaStream *stream, paTestData *data ); -static void ReportStreamTime( PaStream *stream, paTestData *data ) -{ - PaTime streamTime, latency, outTime; - - streamTime = Pa_GetStreamTime( stream ); - outTime = data->outTime; - if( outTime < 0.0 ) - { - printf("Stream time = %8.1f\n", streamTime ); - } - else - { - latency = outTime - streamTime; - printf("Stream time = %8.4f, outTime = %8.4f, latency = %8.4f\n", - streamTime, outTime, latency ); - } - fflush(stdout); -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - PaTime startTime; - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - data.left_phase = data.right_phase = 0; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - /* Watch until sound is halfway finished. */ - printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout); - - data.outTime = -1.0; /* mark time for callback as undefined */ - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - startTime = Pa_GetStreamTime( stream ); - - do - { - ReportStreamTime( stream, &data ); - Pa_Sleep(100); - } while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) ); - - /* Stop sound for 2 seconds. */ - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - printf("Pause for 2 seconds.\n"); fflush(stdout); - Pa_Sleep( 2000 ); - - data.outTime = -1.0; /* mark time for callback as undefined */ - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - startTime = Pa_GetStreamTime( stream ); - - printf("Play until sound is finished.\n"); fflush(stdout); - do - { - ReportStreamTime( stream, &data ); - Pa_Sleep(100); - } while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) ); - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_start_stop.c b/portaudio-v19/pa_tests/patest_start_stop.c deleted file mode 100644 index 03ade1721..000000000 --- a/portaudio-v19/pa_tests/patest_start_stop.c +++ /dev/null @@ -1,162 +0,0 @@ -/** @file patest_start_stop.c - @brief Play a sine wave for several seconds - - start and stop the stream multiple times. - - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice() /* default output device */ - -#define NUM_SECONDS (3) -#define NUM_LOOPS (4) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (400) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int left_phase; - int right_phase; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - - (void) timeInfo; /* Prevent unused variable warnings. */ - (void) statusFlags; - (void) inputBuffer; - - for( i=0; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - - return paContinue; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - - for( i=0; i -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) -#define SLEEP_DUR (200) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (256) -#define LATENCY_SECONDS (3.f) -#define FRAMES_PER_NOTE (SAMPLE_RATE/2) -#define MAX_REPEATS (2) -#define FUNDAMENTAL (400.0f / SAMPLE_RATE) -#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f) -#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f) -#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f) -#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f) -#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f) -#define MODE_FINISH (0) -#define MODE_STOP (1) -#define MODE_ABORT (2) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TABLE_SIZE (400) - -typedef struct -{ - float waveform[TABLE_SIZE + 1]; /* Add one for guard point for interpolation. */ - float phase_increment; - float phase; - float *tune; - int notesPerTune; - int frameCounter; - int noteCounter; - int repeatCounter; - PaTime outTime; - int stopMode; - int done; -} -paTestData; - -/************* Prototypes *****************************/ -int TestStopMode( paTestData *data ); -float LookupWaveform( paTestData *data, float phase ); - -/****************************************************** - * Convert phase between 0.0 and 1.0 to waveform value - * using linear interpolation. - */ -float LookupWaveform( paTestData *data, float phase ) -{ - float fIndex = phase*TABLE_SIZE; - int index = (int) fIndex; - float fract = fIndex - index; - float lo = data->waveform[index]; - float hi = data->waveform[index+1]; - float val = lo + fract*(hi-lo); - return val; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - float value; - unsigned int i = 0; - int finished = paContinue; - - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - - - /* data->outTime = outTime; */ - - if( !data->done ) - { - for( i=0; iframeCounter >= FRAMES_PER_NOTE ) - { - data->noteCounter += 1; - data->frameCounter = 0; - /* Are we done with this tune? */ - if( data->noteCounter >= data->notesPerTune ) - { - data->noteCounter = 0; - data->repeatCounter += 1; - /* Are we totally done? */ - if( data->repeatCounter >= MAX_REPEATS ) - { - data->done = 1; - if( data->stopMode == MODE_FINISH ) - { - finished = paComplete; - break; - } - } - } - data->phase_increment = data->tune[data->noteCounter]; - } - value = LookupWaveform(data, data->phase); - *out++ = value; /* left */ - *out++ = value; /* right */ - data->phase += data->phase_increment; - if( data->phase >= 1.0f ) data->phase -= 1.0f; - - data->frameCounter += 1; - } - } - /* zero remainder of final buffer */ - for( ; idone = 0; - data->phase = 0.0; - data->frameCounter = 0; - data->noteCounter = 0; - data->repeatCounter = 0; - data->phase_increment = data->tune[data->noteCounter]; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = OUTPUT_DEVICE; - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = LATENCY_SECONDS; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - if( data->stopMode == MODE_FINISH ) - { - while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) - { - /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, - data->noteCounter, data->repeatCounter ); - fflush(stdout); */ - Pa_Sleep( SLEEP_DUR ); - } - if( err < 0 ) goto error; - } - else - { - while( data->repeatCounter < MAX_REPEATS ) - { - /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, - data->noteCounter, data->repeatCounter ); - fflush(stdout); */ - Pa_Sleep( SLEEP_DUR ); - } - } - - if( data->stopMode == MODE_ABORT ) - { - printf("Call Pa_AbortStream()\n"); - err = Pa_AbortStream( stream ); - } - else - { - printf("Call Pa_StopStream()\n"); - err = Pa_StopStream( stream ); - } - if( err != paNoError ) goto error; - - printf("Call Pa_CloseStream()\n"); fflush(stdout); - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - - return err; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_stop_playout.c b/portaudio-v19/pa_tests/patest_stop_playout.c deleted file mode 100644 index 41134d581..000000000 --- a/portaudio-v19/pa_tests/patest_stop_playout.c +++ /dev/null @@ -1,432 +0,0 @@ -/** @file patest_stop_playout.c - @brief Test whether all queued samples are played when Pa_StopStream() - is used with a callback or read/write stream, or when the callback - returns paComplete. - @author Ross Bencina -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com/ - * Copyright (c) 1999-2004 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (1024) - -#define TONE_SECONDS (1) /* long tone */ -#define TONE_FADE_SECONDS (.04) /* fades at start and end of long tone */ -#define GAP_SECONDS (.25) /* gap between long tone and blip */ -#define BLIP_SECONDS (.035) /* short blip */ - -#define NUM_REPEATS (3) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (2048) -typedef struct -{ - float sine[TABLE_SIZE+1]; - - int repeatCount; - - double phase; - double lowIncrement, highIncrement; - - int gap1Length, toneLength, toneFadesLength, gap2Length, blipLength; - int gap1Countdown, toneCountdown, gap2Countdown, blipCountdown; -} -TestData; - - -static void RetriggerTestSignalGenerator( TestData *data ) -{ - data->phase = 0.; - data->gap1Countdown = data->gap1Length; - data->toneCountdown = data->toneLength; - data->gap2Countdown = data->gap2Length; - data->blipCountdown = data->blipLength; -} - - -static void ResetTestSignalGenerator( TestData *data ) -{ - data->repeatCount = 0; - RetriggerTestSignalGenerator( data ); -} - - -static void InitTestSignalGenerator( TestData *data ) -{ - int signalLengthModBufferLength, i; - - /* initialise sinusoidal wavetable */ - for( i=0; isine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); - } - data->sine[TABLE_SIZE] = data->sine[0]; /* guard point for linear interpolation */ - - - - data->lowIncrement = (330. / SAMPLE_RATE) * TABLE_SIZE; - data->highIncrement = (1760. / SAMPLE_RATE) * TABLE_SIZE; - - data->gap1Length = GAP_SECONDS * SAMPLE_RATE; - data->toneLength = TONE_SECONDS * SAMPLE_RATE; - data->toneFadesLength = TONE_FADE_SECONDS * SAMPLE_RATE; - data->gap2Length = GAP_SECONDS * SAMPLE_RATE; - data->blipLength = BLIP_SECONDS * SAMPLE_RATE; - - /* adjust signal length to be a multiple of the buffer length */ - signalLengthModBufferLength = (data->gap1Length + data->toneLength + data->gap2Length + data->blipLength) % FRAMES_PER_BUFFER; - if( signalLengthModBufferLength > 0 ) - data->toneLength += signalLengthModBufferLength; - - ResetTestSignalGenerator( data ); -} - - -#define MIN( a, b ) (((a)<(b))?(a):(b)) - -static void GenerateTestSignal( TestData *data, float *stereo, int frameCount ) -{ - int framesGenerated = 0; - float output; - long index; - float fraction; - int count, i; - - while( framesGenerated < frameCount && data->repeatCount < NUM_REPEATS ) - { - if( framesGenerated < frameCount && data->gap1Countdown > 0 ){ - count = MIN( frameCount - framesGenerated, data->gap1Countdown ); - for( i=0; i < count; ++i ) - { - *stereo++ = 0.f; - *stereo++ = 0.f; - } - - data->gap1Countdown -= count; - framesGenerated += count; - } - - if( framesGenerated < frameCount && data->toneCountdown > 0 ){ - count = MIN( frameCount - framesGenerated, data->toneCountdown ); - for( i=0; i < count; ++i ) - { - /* tone with data->lowIncrement phase increment */ - index = (long)data->phase; - fraction = data->phase - index; - output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction; - - data->phase += data->lowIncrement; - while( data->phase >= TABLE_SIZE ) - data->phase -= TABLE_SIZE; - - /* apply fade to ends */ - - if( data->toneCountdown < data->toneFadesLength ) - { - /* cosine-bell fade out at end */ - output *= (-cos(((float)data->toneCountdown / (float)data->toneFadesLength) * M_PI) + 1.) * .5; - } - else if( data->toneCountdown > data->toneLength - data->toneFadesLength ) - { - /* cosine-bell fade in at start */ - output *= (cos(((float)(data->toneCountdown - (data->toneLength - data->toneFadesLength)) / (float)data->toneFadesLength) * M_PI) + 1.) * .5; - } - - output *= .5; /* play tone half as loud as blip */ - - *stereo++ = output; - *stereo++ = output; - - data->toneCountdown--; - } - - framesGenerated += count; - } - - if( framesGenerated < frameCount && data->gap2Countdown > 0 ){ - count = MIN( frameCount - framesGenerated, data->gap2Countdown ); - for( i=0; i < count; ++i ) - { - *stereo++ = 0.f; - *stereo++ = 0.f; - } - - data->gap2Countdown -= count; - framesGenerated += count; - } - - if( framesGenerated < frameCount && data->blipCountdown > 0 ){ - count = MIN( frameCount - framesGenerated, data->blipCountdown ); - for( i=0; i < count; ++i ) - { - /* tone with data->highIncrement phase increment */ - index = (long)data->phase; - fraction = data->phase - index; - output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction; - - data->phase += data->highIncrement; - while( data->phase >= TABLE_SIZE ) - data->phase -= TABLE_SIZE; - - /* cosine-bell envelope over whole blip */ - output *= (-cos( ((float)data->blipCountdown / (float)data->blipLength) * 2. * M_PI) + 1.) * .5; - - *stereo++ = output; - *stereo++ = output; - - data->blipCountdown--; - } - - framesGenerated += count; - } - - - if( data->blipCountdown == 0 ) - { - RetriggerTestSignalGenerator( data ); - data->repeatCount++; - } - } - - if( framesGenerated < frameCount ) - { - count = frameCount - framesGenerated; - for( i=0; i < count; ++i ) - { - *stereo++ = 0.f; - *stereo++ = 0.f; - } - } -} - - -static int IsTestSignalFinished( TestData *data ) -{ - if( data->repeatCount >= NUM_REPEATS ) - return 1; - else - return 0; -} - - -static int TestCallback1( const void *inputBuffer, void *outputBuffer, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - - GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount ); - - if( IsTestSignalFinished( (TestData*)userData ) ) - return paComplete; - else - return paContinue; -} - - -volatile int testCallback2Finished = 0; - -static int TestCallback2( const void *inputBuffer, void *outputBuffer, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - - GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount ); - - if( IsTestSignalFinished( (TestData*)userData ) ) - testCallback2Finished = 1; - - return paContinue; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - TestData data; - float writeBuffer[ FRAMES_PER_BUFFER * 2 ]; - - printf("PortAudio Test: check that stopping stream plays out all queued samples. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - InitTestSignalGenerator( &data ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 2; /* stereo output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - -/* test paComplete ---------------------------------------------------------- */ - - ResetTestSignalGenerator( &data ); - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - TestCallback1, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - - printf("\nPlaying 'tone-blip' %d times using callback, stops by returning paComplete from callback.\n", NUM_REPEATS ); - printf("If final blip is not intact, callback+paComplete implementation may be faulty.\n\n" ); - - while( (err = Pa_IsStreamActive( stream )) == 1 ) - Pa_Sleep( 5 ); - - if( err != 0 ) goto error; - - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Sleep( 500 ); - -/* test Pa_StopStream() with callback --------------------------------------- */ - - ResetTestSignalGenerator( &data ); - - testCallback2Finished = 0; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - TestCallback2, - &data ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - - printf("\nPlaying 'tone-blip' %d times using callback, stops by calling Pa_StopStream.\n", NUM_REPEATS ); - printf("If final blip is not intact, callback+Pa_StopStream implementation may be faulty.\n\n" ); - - /* note that polling a volatile flag is not a good way to synchronise with - the callback, but it's the best we can do portably. */ - while( !testCallback2Finished ) - Pa_Sleep( 2 ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Sleep( 500 ); - -/* test Pa_StopStream() with Pa_WriteStream --------------------------------- */ - - ResetTestSignalGenerator( &data ); - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - - printf("\nPlaying 'tone-blip' %d times using Pa_WriteStream, stops by calling Pa_StopStream.\n", NUM_REPEATS ); - printf("If final blip is not intact, Pa_WriteStream+Pa_StopStream implementation may be faulty.\n\n" ); - - do{ - GenerateTestSignal( &data, writeBuffer, FRAMES_PER_BUFFER ); - err = Pa_WriteStream( stream, writeBuffer, FRAMES_PER_BUFFER ); - if( err != paNoError ) goto error; - - }while( !IsTestSignalFinished( &data ) ); - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - -/* -------------------------------------------------------------------------- */ - - Pa_Terminate(); - printf("Test finished.\n"); - - return err; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_sync.c b/portaudio-v19/pa_tests/patest_sync.c deleted file mode 100644 index 01e471420..000000000 --- a/portaudio-v19/pa_tests/patest_sync.c +++ /dev/null @@ -1,259 +0,0 @@ -/** @file patest_sync.c - @brief Test time stamping and synchronization of audio and video. - - A high latency is used so we can hear the difference in time. - Random durations are used so we know we are hearing the right beep - and not the one before or after. - - Sequence of events: - -# Foreground requests a beep. - -# Background randomly schedules a beep. - -# Foreground waits for the beep to be heard based on PaUtil_GetTime(). - -# Foreground outputs video (printf) in sync with audio. - -# Repeat. - - @author Phil Burk http://www.softsynth.com -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" -#include "pa_util.h" -#define NUM_BEEPS (6) -#define SAMPLE_RATE (44100) -#define SAMPLE_PERIOD (1.0/44100.0) -#define FRAMES_PER_BUFFER (256) -#define BEEP_DURATION (400) -#define LATENCY_MSEC (2000) -#define SLEEP_MSEC (10) -#define TIMEOUT_MSEC (15000) - -#define STATE_BKG_IDLE (0) -#define STATE_BKG_PENDING (1) -#define STATE_BKG_BEEPING (2) -typedef struct -{ - float left_phase; - float right_phase; - int state; - int requestBeep; /* Set by foreground, cleared by background. */ - PaTime beepTime; - int beepCount; - double latency; /* For debugging. */ -} -paTestData; - -static unsigned long GenerateRandomNumber( void ); -/************************************************************/ -/* Calculate pseudo-random 32 bit number based on linear congruential method. */ -static unsigned long GenerateRandomNumber( void ) -{ - static unsigned long randSeed = 99887766; /* Change this for different random sequences. */ - randSeed = (randSeed * 196314165) + 907633515; - return randSeed; -} - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, void *userData ) -{ - /* Cast data passed through stream to our structure. */ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned int i; - (void) inputBuffer; - - data->latency = timeInfo->outputBufferDacTime - timeInfo->currentTime; - - for( i=0; istate ) - { - case STATE_BKG_IDLE: - /* Schedule beep at some random time in the future. */ - if( data->requestBeep ) - { - int random = GenerateRandomNumber() >> 14; - data->beepTime = timeInfo->outputBufferDacTime + (( (double)(random + SAMPLE_RATE)) * SAMPLE_PERIOD ); - data->state = STATE_BKG_PENDING; - } - *out++ = 0.0; /* left */ - *out++ = 0.0; /* right */ - break; - - case STATE_BKG_PENDING: - if( (timeInfo->outputBufferDacTime + (i*SAMPLE_PERIOD)) >= data->beepTime ) - { - data->state = STATE_BKG_BEEPING; - data->beepCount = BEEP_DURATION; - data->left_phase = data->right_phase = 0.0; - } - *out++ = 0.0; /* left */ - *out++ = 0.0; /* right */ - break; - - case STATE_BKG_BEEPING: - if( data->beepCount <= 0 ) - { - data->state = STATE_BKG_IDLE; - data->requestBeep = 0; - *out++ = 0.0; /* left */ - *out++ = 0.0; /* right */ - } - else - { - /* Play sawtooth wave. */ - *out++ = data->left_phase; /* left */ - *out++ = data->right_phase; /* right */ - /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ - data->left_phase += 0.01f; - /* When signal reaches top, drop back down. */ - if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; - /* higher pitch so we can distinguish left and right. */ - data->right_phase += 0.03f; - if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; - } - data->beepCount -= 1; - break; - - default: - data->state = STATE_BKG_IDLE; - break; - } - } - return 0; -} -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStream *stream; - PaError err; - paTestData DATA; - int i, timeout; - PaTime previousTime; - PaStreamParameters outputParameters; - printf("PortAudio Test: you should see BEEP at the same time you hear it.\n"); - printf("Wait for a few seconds random delay between BEEPs.\n"); - printf("BEEP %d times.\n", NUM_BEEPS ); - /* Initialize our DATA for use by callback. */ - DATA.left_phase = DATA.right_phase = 0.0; - DATA.state = STATE_BKG_IDLE; - DATA.requestBeep = 0; - /* Initialize library before making any other calls. */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); - outputParameters.channelCount = 2; - outputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.sampleFormat = paFloat32; - outputParameters.suggestedLatency = (double)LATENCY_MSEC / 1000; - - /* Open an audio I/O stream. */ - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, /* frames per buffer */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &DATA ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("started\n"); - fflush(stdout); - - previousTime = Pa_GetStreamTime( stream ); - for( i=0; i 0 ) ) Pa_Sleep(SLEEP_MSEC); - if( timeout <= 0 ) - { - fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" ); - goto error; - } - printf("calc beep for %9.3f, latency = %6.3f\n", DATA.beepTime, DATA.latency ); - fflush(stdout); - - /* Wait for scheduled beep time. */ - timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC); - while( (Pa_GetStreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) ) - { - Pa_Sleep(SLEEP_MSEC); - } - if( timeout <= 0 ) - { - fprintf( stderr, "Timed out waiting for time. Now = %9.3f, Beep for %9.3f.\n", - PaUtil_GetTime(), DATA.beepTime ); - goto error; - } - - /* Beep should be sounding now so print synchronized BEEP. */ - printf("hear \"BEEP\" at %9.3f, delta = %9.3f\n", - Pa_GetStreamTime( stream ), (DATA.beepTime - previousTime) ); - fflush(stdout); - - previousTime = DATA.beepTime; - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_toomanysines.c b/portaudio-v19/pa_tests/patest_toomanysines.c deleted file mode 100644 index b7b22667c..000000000 --- a/portaudio-v19/pa_tests/patest_toomanysines.c +++ /dev/null @@ -1,173 +0,0 @@ -/** @file patest_toomanysines.c - @brief Play more sine waves than we can handle in real time as a stress test. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define MAX_SINES (500) -#define MAX_LOAD (1.2) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (512) -#ifndef M_PI -#define M_PI (3.14159265) -#endif -#define TWOPI (M_PI * 2.0) - -typedef struct paTestData -{ - int numSines; - double phases[MAX_SINES]; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int j; - int finished = 0; - (void) inputBuffer; /* Prevent unused variable warning. */ - - for( i=0; inumSines; j++ ) - { - /* Advance phase of next oscillator. */ - phase = data->phases[j]; - phase += phaseInc; - if( phase > TWOPI ) phase -= TWOPI; - - phaseInc *= 1.02; - if( phaseInc > 0.5 ) phaseInc *= 0.5; - - /* This is not a very efficient way to calc sines. */ - output += (float) sin( phase ); - data->phases[j] = phase; - } - *out++ = (float) (output / data->numSines); - } - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - int numStress; - paTestData data = {0}; - double load; - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d. MAX_LOAD = %f\n", - SAMPLE_RATE, FRAMES_PER_BUFFER, MAX_LOAD ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = 1; /* mono output */ - outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - /* Determine number of sines required to get to 50% */ - do - { - data.numSines++; - Pa_Sleep( 100 ); - - load = Pa_GetStreamCpuLoad( stream ); - printf("numSines = %d, CPU load = %f\n", data.numSines, load ); - } - while( load < 0.5 ); - - /* Calculate target stress value then ramp up to that level*/ - numStress = (int) (2.0 * data.numSines * MAX_LOAD ); - for( ; data.numSines < numStress; data.numSines++ ) - { - Pa_Sleep( 200 ); - load = Pa_GetStreamCpuLoad( stream ); - printf("STRESSING: numSines = %d, CPU load = %f\n", data.numSines, load ); - } - - printf("Suffer for 5 seconds.\n"); - Pa_Sleep( 5000 ); - - printf("Stop stream.\n"); - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_two_rates.c b/portaudio-v19/pa_tests/patest_two_rates.c deleted file mode 100644 index 99532411f..000000000 --- a/portaudio-v19/pa_tests/patest_two_rates.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * $Id$ - * patest_two_rates.c - * Play two streams at different rates to make sure they don't interfere. - * - * Author: Phil Burk http://www.softsynth.com - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) -#define SAMPLE_RATE_1 (44100) -#define SAMPLE_RATE_2 (44100) -#define FRAMES_PER_BUFFER (256) -#define FREQ_INCR (0.1) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -typedef struct -{ - double phase; - double numFrames; -} paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - int frameIndex, channelIndex; - int finished = 0; - (void) outTime; /* Prevent unused variable warnings. */ - (void) inputBuffer; - - for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) - { - /* Generate sine wave. */ - float value = (float) 0.3 * sin(data->phase); - /* Stereo - two channels. */ - *out++ = value; - *out++ = value; - - data->phase += FREQ_INCR; - if( data->phase >= (2.0 * M_PI) ) data->phase -= (2.0 * M_PI); - } - - return 0; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - PortAudioStream *stream1; - PortAudioStream *stream2; - paTestData data1 = {0}; - paTestData data2 = {0}; - printf("PortAudio Test: two rates.\n" ); - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - /* Start first stream. **********************/ - err = Pa_OpenStream( - &stream1, - paNoDevice, /* default input device */ - 0, /* no input */ - paFloat32, /* 32 bit floating point input */ - NULL, - OUTPUT_DEVICE, - 2, /* Stereo */ - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE_1, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data1 ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream1 ); - if( err != paNoError ) goto error; - - Pa_Sleep( 3 * 1000 ); - - /* Start second stream. **********************/ - err = Pa_OpenStream( - &stream2, - paNoDevice, /* default input device */ - 0, /* no input */ - paFloat32, /* 32 bit floating point input */ - NULL, - OUTPUT_DEVICE, - 2, /* Stereo */ - paFloat32, /* 32 bit floating point output */ - NULL, - SAMPLE_RATE_2, - FRAMES_PER_BUFFER, /* frames per buffer */ - 0, /* number of buffers, if zero then use default minimum */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data2 ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream2 ); - if( err != paNoError ) goto error; - - Pa_Sleep( 3 * 1000 ); - - err = Pa_StopStream( stream2 ); - if( err != paNoError ) goto error; - - Pa_Sleep( 3 * 1000 ); - - err = Pa_StopStream( stream1 ); - if( err != paNoError ) goto error; - - Pa_CloseStream( stream2 ); - Pa_CloseStream( stream1 ); - - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_underflow.c b/portaudio-v19/pa_tests/patest_underflow.c deleted file mode 100644 index 7ff3de6a4..000000000 --- a/portaudio-v19/pa_tests/patest_underflow.c +++ /dev/null @@ -1,151 +0,0 @@ -/** @file patest_underflow.c - @brief Simulate an output buffer underflow condition. - Tests whether the stream can be stopped when underflowing buffers. - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.audiomulch.com/portaudio/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (20) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (2048) -#define MSEC_PER_BUFFER ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE ) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) -typedef struct -{ - float sine[TABLE_SIZE]; - int left_phase; - int right_phase; - int sleepTime; -} -paTestData; - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int patestCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - paTestData *data = (paTestData*)userData; - float *out = (float*)outputBuffer; - unsigned long i; - int finished = 0; - (void) inputBuffer; /* Prevent unused variable warnings. */ - for( i=0; isine[data->left_phase]; /* left */ - *out++ = data->sine[data->right_phase]; /* right */ - data->left_phase += 1; - if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; - data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ - if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; - } - - /* Cause underflow to occur. */ - if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime ); - data->sleepTime += 1; - - return finished; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - paTestData data; - int i; - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, - &data ); - if( err != paNoError ) goto error; - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - while( data.sleepTime < (2 * MSEC_PER_BUFFER) ) - { - printf("SleepTime = %d\n", data.sleepTime ); - Pa_Sleep( data.sleepTime ); - } - - printf("Try to stop stream.\n"); - err = Pa_StopStream( stream ); /* */ - err = Pa_AbortStream( stream ); /* */ - if( err != paNoError ) goto error; - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - Pa_Terminate(); - printf("Test finished.\n"); - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_tests/patest_wire.c b/portaudio-v19/pa_tests/patest_wire.c deleted file mode 100644 index d83ce22d6..000000000 --- a/portaudio-v19/pa_tests/patest_wire.c +++ /dev/null @@ -1,284 +0,0 @@ -/** @file patest_wire.c - @brief Pass input directly to output. - - Note that some HW devices, for example many ISA audio cards - on PCs, do NOT support full duplex! For a PC, you normally need - a PCI based audio card such as the SBLive. - - @author Phil Burk http://www.softsynth.com - - While adapting to V19-API, I excluded configs with framesPerCallback=0 - because of an assert in file pa_common/pa_process.c. Pieter, Oct 9, 2003. - -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define SAMPLE_RATE (44100) - -typedef struct WireConfig_s -{ - int isInputInterleaved; - int isOutputInterleaved; - int numInputChannels; - int numOutputChannels; - int framesPerCallback; -} WireConfig_t; - -#define USE_FLOAT_INPUT (1) -#define USE_FLOAT_OUTPUT (1) - -/* Latencies set to defaults. */ - -#if USE_FLOAT_INPUT - #define INPUT_FORMAT paFloat32 - typedef float INPUT_SAMPLE; -#else - #define INPUT_FORMAT paInt16 - typedef short INPUT_SAMPLE; -#endif - -#if USE_FLOAT_OUTPUT - #define OUTPUT_FORMAT paFloat32 - typedef float OUTPUT_SAMPLE; -#else - #define OUTPUT_FORMAT paInt16 - typedef short OUTPUT_SAMPLE; -#endif - -double gInOutScaler = 1.0; -#define CONVERT_IN_TO_OUT(in) ((OUTPUT_SAMPLE) ((in) * gInOutScaler)) - -#define INPUT_DEVICE (Pa_GetDefaultInputDevice()) -#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) - -static PaError TestConfiguration( WireConfig_t *config ); - -static int wireCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may be called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ - -static int wireCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - INPUT_SAMPLE *in; - OUTPUT_SAMPLE *out; - int inStride; - int outStride; - int inDone = 0; - int outDone = 0; - WireConfig_t *config = (WireConfig_t *) userData; - unsigned int i; - int inChannel, outChannel; - - /* This may get called with NULL inputBuffer during initial setup. */ - if( inputBuffer == NULL) return 0; - - inChannel=0, outChannel=0; - while( !(inDone && outDone) ) - { - if( config->isInputInterleaved ) - { - in = ((INPUT_SAMPLE*)inputBuffer) + inChannel; - inStride = config->numInputChannels; - } - else - { - in = ((INPUT_SAMPLE**)inputBuffer)[inChannel]; - inStride = 1; - } - - if( config->isOutputInterleaved ) - { - out = ((OUTPUT_SAMPLE*)outputBuffer) + outChannel; - outStride = config->numOutputChannels; - } - else - { - out = ((OUTPUT_SAMPLE**)outputBuffer)[outChannel]; - outStride = 1; - } - - for( i=0; inumInputChannels - 1)) inChannel++; - else inDone = 1; - if(outChannel < (config->numOutputChannels - 1)) outChannel++; - else outDone = 1; - } - return 0; -} - -/*******************************************************************/ -int main(void); -int main(void) -{ - PaError err; - WireConfig_t CONFIG; - WireConfig_t *config = &CONFIG; - int configIndex = 0;; - - err = Pa_Initialize(); - if( err != paNoError ) goto error; - - printf("Please connect audio signal to input and listen for it on output!\n"); - printf("input format = %lu\n", INPUT_FORMAT ); - printf("output format = %lu\n", OUTPUT_FORMAT ); - printf("input device ID = %d\n", INPUT_DEVICE ); - printf("output device ID = %d\n", OUTPUT_DEVICE ); - - if( INPUT_FORMAT == OUTPUT_FORMAT ) - { - gInOutScaler = 1.0; - } - else if( (INPUT_FORMAT == paInt16) && (OUTPUT_FORMAT == paFloat32) ) - { - gInOutScaler = 1.0/32768.0; - } - else if( (INPUT_FORMAT == paFloat32) && (OUTPUT_FORMAT == paInt16) ) - { - gInOutScaler = 32768.0; - } - - for( config->isInputInterleaved = 0; config->isInputInterleaved < 2; config->isInputInterleaved++ ) - { - for( config->isOutputInterleaved = 0; config->isOutputInterleaved < 2; config->isOutputInterleaved++ ) - { - for( config->numInputChannels = 1; config->numInputChannels < 3; config->numInputChannels++ ) - { - for( config->numOutputChannels = 1; config->numOutputChannels < 3; config->numOutputChannels++ ) - { - /* If framesPerCallback = 0, assertion fails in file pa_common/pa_process.c, line 1413: EX. */ - for( config->framesPerCallback = 64; config->framesPerCallback < 129; config->framesPerCallback += 64 ) - { - printf("-----------------------------------------------\n" ); - printf("Configuration #%d\n", configIndex++ ); - err = TestConfiguration( config ); - /* Give user a chance to bail out. */ - if( err == 1 ) - { - err = paNoError; - goto done; - } - else if( err != paNoError ) goto error; - } - } - } - } - } - -done: - Pa_Terminate(); - printf("Full duplex sound test complete.\n"); fflush(stdout); - printf("Hit ENTER to quit.\n"); fflush(stdout); - getchar(); - return 0; - -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - printf("Hit ENTER to quit.\n"); fflush(stdout); - getchar(); - return -1; -} - -static PaError TestConfiguration( WireConfig_t *config ) -{ - int c; - PaError err; - PaStream *stream; - PaStreamParameters inputParameters, outputParameters; - - printf("input %sinterleaved!\n", (config->isInputInterleaved ? " " : "NOT ") ); - printf("output %sinterleaved!\n", (config->isOutputInterleaved ? " " : "NOT ") ); - printf("input channels = %d\n", config->numInputChannels ); - printf("output channels = %d\n", config->numOutputChannels ); - printf("framesPerCallback = %d\n", config->framesPerCallback ); - - inputParameters.device = INPUT_DEVICE; /* default input device */ - inputParameters.channelCount = config->numInputChannels; - inputParameters.sampleFormat = INPUT_FORMAT | (config->isInputInterleaved ? 0 : paNonInterleaved); - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = OUTPUT_DEVICE; /* default output device */ - outputParameters.channelCount = config->numOutputChannels; - outputParameters.sampleFormat = OUTPUT_FORMAT | (config->isOutputInterleaved ? 0 : paNonInterleaved); - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - &inputParameters, - &outputParameters, - SAMPLE_RATE, - config->framesPerCallback, /* frames per buffer */ - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - wireCallback, - config ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Hit ENTER for next configuration, or 'q' to quit.\n"); fflush(stdout); - c = getchar(); - - printf("Closing stream.\n"); - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - if( c == 'q' ) return 1; - -error: - return err; -} diff --git a/portaudio-v19/pa_tests/patest_write_sine.c b/portaudio-v19/pa_tests/patest_write_sine.c deleted file mode 100644 index f0fcba0b6..000000000 --- a/portaudio-v19/pa_tests/patest_write_sine.c +++ /dev/null @@ -1,147 +0,0 @@ -/** @file patest_write_sine.c - @brief Play a sine wave for several seconds using the blocking API (Pa_WriteStream()) - @author Ross Bencina - @author Phil Burk -*/ -/* - * $Id$ - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com/ - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include -#include -#include "portaudio.h" - -#define NUM_SECONDS (5) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (1024) - -#ifndef M_PI -#define M_PI (3.14159265) -#endif - -#define TABLE_SIZE (200) - - -int main(void); -int main(void) -{ - PaStreamParameters outputParameters; - PaStream *stream; - PaError err; - float buffer[FRAMES_PER_BUFFER][2]; /* stereo output buffer */ - float sine[TABLE_SIZE]; /* sine wavetable */ - int left_phase = 0; - int right_phase = 0; - int left_inc = 1; - int right_inc = 3; /* higher pitch so we can distinguish left and right. */ - int i, j, k; - int bufferCount; - - - printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); - - /* initialise sinusoidal wavetable */ - for( i=0; idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if( err != paNoError ) goto error; - - - printf( "Play 3 times, higher each time.\n" ); - - for( k=0; k < 3; ++k ) - { - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - printf("Play for %d seconds.\n", NUM_SECONDS ); - - bufferCount = ((NUM_SECONDS * SAMPLE_RATE) / FRAMES_PER_BUFFER); - - for( i=0; i < bufferCount; i++ ) - { - for( j=0; j < FRAMES_PER_BUFFER; j++ ) - { - buffer[j][0] = sine[left_phase]; /* left */ - buffer[j][1] = sine[right_phase]; /* right */ - left_phase += left_inc; - if( left_phase >= TABLE_SIZE ) left_phase -= TABLE_SIZE; - right_phase += right_inc; - if( right_phase >= TABLE_SIZE ) right_phase -= TABLE_SIZE; - } - - err = Pa_WriteStream( stream, buffer, FRAMES_PER_BUFFER ); - if( err != paNoError ) goto error; - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - ++left_inc; - ++right_inc; - - Pa_Sleep( 1000 ); - } - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - Pa_Terminate(); - printf("Test finished.\n"); - - return err; -error: - Pa_Terminate(); - fprintf( stderr, "An error occured while using the portaudio stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; -} diff --git a/portaudio-v19/pa_unix_oss/low_latency_tip.txt b/portaudio-v19/pa_unix_oss/low_latency_tip.txt deleted file mode 100644 index 2d982b79baa6d943c1fae0c7122d190ebf46c6f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3111 zcmb_e+in}l5zX^_MZtMka>JV_VPg}vVpflr&clN4ctL+YE&U7Sw$Nrvj#V9W!?cY$ zC`VjAku{{~Rhz%cu2sOcwF=P{YCodiN5gbJ8_|3=JEPZ&vp2N-oMqY46z(6RY;Pke z$K-9jli>L7sC9Nzb3>tG>YT;nPVE#zLoZ2p=y`+zuucjt4j znzm*?BwZ#ng2H1zTe6N`Z2Ruw#V&fWO%hDt8*A(&%LaudV*{1eZ3Uktl?S~8iB!hA z4iV=)QKv(NT|$s>@^F8*`uOqY?g59#vK(duiF%}_blL|h_!Ov5SQ!vkvB4hHI>iZ2 zZ@277mB=AIxn5q8_F3z=)O8I;2cDTWT-$Cq%(8oM2{3g{TfO6yivs4LYSN~S3BQd& zKGjIU5>{Id9SD$XKA8uE-U#Q32C}f&fTs)xe=|im9A&2K=wb4PVT7OkZGbtoAj*?b3 z)&=B4jfFNtg;*&23oUg{2ObQGF6fD#vlGlxpq}xv04oqiA>>-9I+1$hd6NAJRRvpk z09K`fJ+qKO;cSaH9tlIqH4n~)^h-u!y#lAcIIx8CX?cBxmn}P}sCj^`ACfMx5>^mA z&l|Ari2eB?2|Sb@FaMThtMVjLMJW7{eR^bPxF!)&tM;S8r^KAiAH}7*pz{}%)#9(Vj-Ggjm=Su)t=X{?UG_?nC7T#B#1!kJEZ|m7JR8qb zB&W=lX?eR`eLn-j=A5*XT1g-+ikZoEJmA5EC6 zRslDV-6O`az2ZDbyKe=hHX!}DzTlP(*gDAP6jnrvo;Q*zkZOD0q~9Kz3t5N|ZbF`Z zkH*;GM0gMlc9f;zgzr@d?P5CZx^7Zy6Q2@A1@9*4fJB1tlf`gB-}pgZ(=xT|Yj9oL zwt+e@Jmlw#kts96Y>^q0#>=R8k+T><*(G6x44v`pyYc)D&CeF+ujw~4JQ~rzO-h+D z{SIuxw)J{?>(KV#S0tO-OFwR;%D)z5GATU$E-g=g?!n_X9uukAhiZaH_+3#2I!Kh` zC$tz3`^S=DiQX(qNc3x)hkCI7@%rlihllI+PY>&x``f$a$ImxE-Ve#gJjcwFwi_x$ z*r_@KlS&1~fwQNx*=+V(8ery2YaM?`=!B@D7)F$Fsw-OiB`hipywcl>a8ro*2Yqdr z@|u-{As|nkv*=}X=+8c4On9hG@FIeiT))Lk{ZQHf-KH%aI6sh~q5O)B3ir8h8i1`NFBGL+-OowG}5Ic&Q9Z3qTP8RN+1(124i>TSCJz5w|)W&`}Qe zslm -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ -# include -# include -# define DEVICE_NAME_BASE "/dev/dsp" -#elif defined __linux__ -#include -#include -# 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 ); - } - deviceInfos[numDevices - 1] = deviceInfo; - - if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = i; - if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = i; - } - - /* 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 */ - - /* -#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 ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ - - pthread_testcancel(); - - if( stream->callbackStop && callbackResult == paContinue ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless - * the stream has been recently started, we will have to go right ahead and read/write in blocking - * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch - * to non-blocking mode. - */ - if( !initiateProcessing ) - { - PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */ - assert( framesAvail % stream->framesPerHostBuffer == 0 ); - } - else - { - framesAvail = stream->framesPerHostBuffer; - } - - while( framesAvail > 0 ) - { - unsigned long frames = framesAvail; - - pthread_testcancel(); - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* Read data */ - if ( stream->capture ) - { - PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) ); - assert( frames == framesAvail ); - } - -#if ( SOUND_VERSION >= 0x030904 ) - /* - Check with OSS to see if there have been any under/overruns - since last time we checked. - */ - /* - if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 ) - { - if( errinfo.play_underruns ) - cbFlags |= paOutputUnderflow ; - if( errinfo.record_underruns ) - cbFlags |= paInputUnderflow ; - } - else - PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) )); - */ -#endif - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - cbFlags = 0; - PA_ENSURE( SetUpBuffers( stream, framesAvail ) ); - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &callbackResult ); - assert( framesProcessed == framesAvail ); - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if ( stream->playback ) - { - frames = framesAvail; - - PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) ); - assert( frames == framesAvail ); - - /* TODO: handle bytesWritten != bytesRequested (slippage?) */ - } - - framesAvail -= framesProcessed; - stream->framesProcessed += framesProcessed; - - if( callbackResult != paContinue ) - break; - } - - if( initiateProcessing || !triggered ) - { - /* Non-blocking */ - if( stream->capture ) - PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) ); - if( stream->playback && !stream->sharedDevice ) - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - - initiateProcessing = 0; - sem_post( &stream->semaphore ); - } - - if( callbackResult != paContinue ) - { - stream->callbackAbort = callbackResult == paAbort; - if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - break; - } - } - - pthread_cleanup_pop( 1 ); - -error: - pthread_exit( NULL ); -} - -/** Close the stream. - * - */ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - assert( stream ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaOssStream_Terminate( stream ); - - return result; -} - -/** Start the stream. - * - * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual - * callback will be repeatedly called in a separate thread. If a separate thread is started this function - * will block untill it has started processing audio, otherwise audio processing is started directly. - */ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - stream->isActive = 1; - stream->isStopped = 0; - stream->lastPosPtr = 0; - stream->lastStreamBytes = 0; - stream->framesProcessed = 0; - - /* only use the thread for callback streams */ - if( stream->bufferProcessor.streamCallback ) - { - PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) ); - sem_wait( &stream->semaphore ); - } - else - PA_ENSURE( PaOssStream_Prepare( stream ) ); - -error: - return result; -} - -static PaError RealStop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - if( stream->callbackMode ) - { - if( abort ) - stream->callbackAbort = 1; - else - stream->callbackStop = 1; - - PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) ); - - stream->callbackStop = stream->callbackAbort = 0; - } - else - PA_ENSURE( PaOssStream_Stop( stream, abort ) ); - - stream->isStopped = 1; - -error: - return result; -} - -/** Stop the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued - * buffers. - */ -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 0 ); -} - -/** Abort the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued - * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing - * the OSS device. - */ -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 1 ); -} - -/** Is the stream in the Stopped state. - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isStopped); -} - -/** Is the stream in the Active state. - * - */ -static PaError IsStreamActive( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isActive); -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - count_info info; - int delta; - - if( stream->playback ) { - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) { - delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate; - } - } - else { - if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) { - delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate; - } - } - - /* the ioctl failed, but we can still give a coarse estimate */ - - return stream->framesProcessed / stream->sampleRate; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesRead; - unsigned long framesRequested; - void *userBuffer; - - /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userInputIsInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->capture->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount ); - } - - while( frames ) - { - framesRequested = PA_MIN( frames, stream->capture->hostFrames ); - - bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture ); - bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested ); - if ( bytesRequested != bytesRead ) - return paUnanticipatedHostError; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount ); - PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested ); - frames -= framesRequested; - } - return paNoError; -} - - -static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesWritten; - unsigned long framesConverted; - const void *userBuffer; - - /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - userBuffer = buffer; - else - { - /* Copy channels into local array */ - userBuffer = stream->playback->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount ); - } - - while( frames ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount ); - - framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames ); - frames -= framesConverted; - - bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback ); - bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested ); - - if ( bytesRequested != bytesWritten ) - return paUnanticipatedHostError; - } - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - audio_buf_info info; - - if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 ) - return paUnanticipatedHostError; - return info.fragments * stream->capture->hostFrames; -} - - -/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - int delay = 0; - - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 ) - return paUnanticipatedHostError; - - return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback ); -} - diff --git a/portaudio-v19/pa_unix_oss/pa_unix_oss.c.orig b/portaudio-v19/pa_unix_oss/pa_unix_oss.c.orig deleted file mode 100644 index 3d81433cb..000000000 --- a/portaudio-v19/pa_unix_oss/pa_unix_oss.c.orig +++ /dev/null @@ -1,1922 +0,0 @@ -/* - * $Id: pa_unix_oss.c,v 1.6.2.26 2005/12/10 11:35:24 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 ); - } - deviceInfos[numDevices - 1] = deviceInfo; - - if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = i; - if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = i; - } - - /* 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 */ - - /* -#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 ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ - - pthread_testcancel(); - - if( stream->callbackStop && callbackResult == paContinue ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless - * the stream has been recently started, we will have to go right ahead and read/write in blocking - * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch - * to non-blocking mode. - */ - if( !initiateProcessing ) - { - PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */ - assert( framesAvail % stream->framesPerHostBuffer == 0 ); - } - else - { - framesAvail = stream->framesPerHostBuffer; - } - - while( framesAvail > 0 ) - { - unsigned long frames = framesAvail; - - pthread_testcancel(); - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* Read data */ - if ( stream->capture ) - { - PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) ); - assert( frames == framesAvail ); - } - -#if ( SOUND_VERSION >= 0x030904 ) - /* - Check with OSS to see if there have been any under/overruns - since last time we checked. - */ - /* - if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 ) - { - if( errinfo.play_underruns ) - cbFlags |= paOutputUnderflow ; - if( errinfo.record_underruns ) - cbFlags |= paInputUnderflow ; - } - else - PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) )); - */ -#endif - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - cbFlags = 0; - PA_ENSURE( SetUpBuffers( stream, framesAvail ) ); - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &callbackResult ); - assert( framesProcessed == framesAvail ); - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if ( stream->playback ) - { - frames = framesAvail; - - PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) ); - assert( frames == framesAvail ); - - /* TODO: handle bytesWritten != bytesRequested (slippage?) */ - } - - framesAvail -= framesProcessed; - stream->framesProcessed += framesProcessed; - - if( callbackResult != paContinue ) - break; - } - - if( initiateProcessing || !triggered ) - { - /* Non-blocking */ - if( stream->capture ) - PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) ); - if( stream->playback && !stream->sharedDevice ) - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - - initiateProcessing = 0; - sem_post( &stream->semaphore ); - } - - if( callbackResult != paContinue ) - { - stream->callbackAbort = callbackResult == paAbort; - if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - break; - } - } - - pthread_cleanup_pop( 1 ); - -error: - pthread_exit( NULL ); -} - -/** Close the stream. - * - */ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - assert( stream ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaOssStream_Terminate( stream ); - - return result; -} - -/** Start the stream. - * - * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual - * callback will be repeatedly called in a separate thread. If a separate thread is started this function - * will block untill it has started processing audio, otherwise audio processing is started directly. - */ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - stream->isActive = 1; - stream->isStopped = 0; - stream->lastPosPtr = 0; - stream->lastStreamBytes = 0; - stream->framesProcessed = 0; - - /* only use the thread for callback streams */ - if( stream->bufferProcessor.streamCallback ) - { - PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) ); - sem_wait( &stream->semaphore ); - } - else - PA_ENSURE( PaOssStream_Prepare( stream ) ); - -error: - return result; -} - -static PaError RealStop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - if( stream->callbackMode ) - { - if( abort ) - stream->callbackAbort = 1; - else - stream->callbackStop = 1; - - PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) ); - - stream->callbackStop = stream->callbackAbort = 0; - } - else - PA_ENSURE( PaOssStream_Stop( stream, abort ) ); - - stream->isStopped = 1; - -error: - return result; -} - -/** Stop the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued - * buffers. - */ -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 0 ); -} - -/** Abort the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued - * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing - * the OSS device. - */ -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 1 ); -} - -/** Is the stream in the Stopped state. - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isStopped); -} - -/** Is the stream in the Active state. - * - */ -static PaError IsStreamActive( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isActive); -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - count_info info; - int delta; - - if( stream->playback ) { - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) { - delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate; - } - } - else { - if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) { - delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/; - return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate; - } - } - - /* the ioctl failed, but we can still give a coarse estimate */ - - return stream->framesProcessed / stream->sampleRate; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesRead; - unsigned long framesRequested; - void *userBuffer; - - /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userInputIsInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->capture->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount ); - } - - while( frames ) - { - framesRequested = PA_MIN( frames, stream->capture->hostFrames ); - - bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture ); - bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested ); - if ( bytesRequested != bytesRead ) - return paUnanticipatedHostError; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount ); - PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested ); - frames -= framesRequested; - } - return paNoError; -} - - -static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesWritten; - unsigned long framesConverted; - const void *userBuffer; - - /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - userBuffer = buffer; - else - { - /* Copy channels into local array */ - userBuffer = stream->playback->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount ); - } - - while( frames ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount ); - - framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames ); - frames -= framesConverted; - - bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback ); - bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested ); - - if ( bytesRequested != bytesWritten ) - return paUnanticipatedHostError; - } - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - audio_buf_info info; - - if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 ) - return paUnanticipatedHostError; - return info.fragments * stream->capture->hostFrames; -} - - -/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - int delay = 0; - - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 ) - return paUnanticipatedHostError; - - return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback ); -} - diff --git a/portaudio-v19/pa_unix_oss/recplay.c b/portaudio-v19/pa_unix_oss/recplay.c deleted file mode 100644 index 9d4c78cfa..000000000 --- a/portaudio-v19/pa_unix_oss/recplay.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * recplay.c - * Phil Burk - * Minimal record and playback test. - * - */ -#include -#include -#include -#ifndef __STDC__ -/* #include */ -#endif /* __STDC__ */ -#include -#ifdef __STDC__ -#include -#else /* __STDC__ */ -#include -#endif /* __STDC__ */ -#include - -#define NUM_BYTES (64*1024) -#define BLOCK_SIZE (4*1024) - -#define AUDIO "/dev/dsp" - -char buffer[NUM_BYTES]; - -int audioDev = 0; - -main (int argc, char *argv[]) -{ - int numLeft; - char *ptr; - int num; - int samplesize; - - /********** RECORD ********************/ - /* Open audio device. */ - audioDev = open (AUDIO, O_RDONLY, 0); - if (audioDev == -1) - { - perror (AUDIO); - exit (-1); - } - - /* Set to 16 bit samples. */ - samplesize = 16; - ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); - if (samplesize != 16) - { - perror("Unable to set the sample size."); - exit(-1); - } - - /* Record in blocks */ - printf("Begin recording.\n"); - numLeft = NUM_BYTES; - ptr = buffer; - while( numLeft >= BLOCK_SIZE ) - { - if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 ) - { - perror (AUDIO); - exit (-1); - } - else - { - printf("Read %d bytes\n", num); - ptr += num; - numLeft -= num; - } - } - - close( audioDev ); - - /********** PLAYBACK ********************/ - /* Open audio device for writing. */ - audioDev = open (AUDIO, O_WRONLY, 0); - if (audioDev == -1) - { - perror (AUDIO); - exit (-1); - } - - /* Set to 16 bit samples. */ - samplesize = 16; - ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); - if (samplesize != 16) - { - perror("Unable to set the sample size."); - exit(-1); - } - - /* Play in blocks */ - printf("Begin playing.\n"); - numLeft = NUM_BYTES; - ptr = buffer; - while( numLeft >= BLOCK_SIZE ) - { - if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 ) - { - perror (AUDIO); - exit (-1); - } - else - { - printf("Wrote %d bytes\n", num); - ptr += num; - numLeft -= num; - } - } - - close( audioDev ); -} diff --git a/portaudio-v19/pa_win_ds/dsound_wrapper.c b/portaudio-v19/pa_win_ds/dsound_wrapper.c deleted file mode 100644 index e29019a8d..000000000 --- a/portaudio-v19/pa_win_ds/dsound_wrapper.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * $Id$ - * 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/portaudio-v19/pa_win_ds/dsound_wrapper.h b/portaudio-v19/pa_win_ds/dsound_wrapper.h deleted file mode 100644 index 713f4a71d..000000000 --- a/portaudio-v19/pa_win_ds/dsound_wrapper.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef __DSOUND_WRAPPER_H -#define __DSOUND_WRAPPER_H -/* - * $Id$ - * 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/portaudio-v19/pa_win_ds/pa_win_ds.c b/portaudio-v19/pa_win_ds/pa_win_ds.c deleted file mode 100644 index 10f19f22b..000000000 --- a/portaudio-v19/pa_win_ds/pa_win_ds.c +++ /dev/null @@ -1,1857 +0,0 @@ -/* - * $Id$ - * 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 - - -#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 )); - 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 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) stream, TIME_PERIODIC ); - } - if( stream->timerID == 0 ) - { - stream->isActive = 0; - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - - stream->isStarted = TRUE; - -error: - return result; -} - - -/***********************************************************************************/ -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinDsStream *stream = (PaWinDsStream*)s; - HRESULT hr; - int timeoutMsec; - - stream->stopProcessing = 1; - /* Set timeout at 20% beyond maximum time we might wait. */ - timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate); - while( stream->isActive && (timeoutMsec > 0) ) - { - Sleep(10); - timeoutMsec -= 10; - } - if( stream->timerID != 0 ) - { - timeKillEvent(stream->timerID); /* Stop callback timer. */ - stream->timerID = 0; - } - - - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - hr = DSW_StopOutput( &stream->directSoundWrapper ); - } - - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - hr = DSW_StopInput( &stream->directSoundWrapper ); - } - - stream->isStarted = FALSE; - - return result; -} - - -/***********************************************************************************/ -static PaError AbortStream( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - stream->abortProcessing = 1; - return StopStream( s ); -} - - -/***********************************************************************************/ -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return !stream->isStarted; -} - - -/***********************************************************************************/ -static PaError IsStreamActive( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return stream->isActive; -} - -/***********************************************************************************/ -static PaTime GetStreamTime( PaStream *s ) -{ - /* suppress unused variable warnings */ - (void) s; - - -/* - new behavior for GetStreamTime is to return a stream based seconds clock - used for the outTime parameter to the callback. - FIXME: delete this comment when the other unnecessary related code has - been cleaned from this file. - - PaWinDsStream *stream = (PaWinDsStream*)s; - DSoundWrapper *dsw; - dsw = &stream->directSoundWrapper; - return dsw->dsw_FramesPlayed; -*/ - return PaUtil_GetTime(); -} - - -/***********************************************************************************/ -static double GetStreamCpuLoad( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - - -/*********************************************************************************** - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -/***********************************************************************************/ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - - diff --git a/portaudio-v19/pa_win_wdmks/pa_win_wdmks.c b/portaudio-v19/pa_win_wdmks/pa_win_wdmks.c deleted file mode 100644 index 34d0ee6e6..000000000 --- a/portaudio-v19/pa_win_wdmks/pa_win_wdmks.c +++ /dev/null @@ -1,3269 +0,0 @@ -/* - * $Id$ - * 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/portaudio-v19/pa_win_wdmks/readme.txt b/portaudio-v19/pa_win_wdmks/readme.txt deleted file mode 100644 index 1a381fe79..000000000 --- a/portaudio-v19/pa_win_wdmks/readme.txt +++ /dev/null @@ -1,82 +0,0 @@ -Notes about WDM-KS host API ---------------------------- - -Status history --------------- -10th November 2005: -Made following changes: - * OpenStream: Try all PaSampleFormats internally if the the chosen - format is not supported natively. This fixed several problems - with soundcards that soundcards that did not take kindly to - using 24-bit 3-byte formats. - * OpenStream: Make the minimum framesPerHostIBuffer (and framesPerHostOBuffer) - the default frameSize for the playback/recording pin. - * ProcessingThread: Added a switch to only call PaUtil_EndBufferProcessing - if the total input frames equals the total output frames - -5th September 2004: -This is the first public version of the code. It should be considered -an alpha release with zero guarantee not to crash on any particular -system. So far it has only been tested in the author's development -environment, which means a Win2k/SP2 PIII laptop with integrated -SoundMAX driver and USB Tascam US-428 compiled with both MinGW -(GCC 3.3) and MSVC++6 using the MS DirectX 9 SDK. -It has been most widely tested with the MinGW build, with most of the -test programs (particularly paqa_devs and paqa_errs) passing. -There are some notable failures: patest_out_underflow and both of the -blocking I/O tests (as blocking I/O is not implemented). -At this point the code needs to be tested with a much wider variety -of configurations and feedback provided from testers regarding -both working and failing cases. - -What is the WDM-KS host API? ----------------------------- -PortAudio for Windows currently has 3 functional host implementations. -MME uses the oldest Windows audio API which does not offer good -play/record latency. -DirectX improves this, but still imposes a penalty -of 10s of milliseconds due to the system mixing of streams from -multiple applications. -ASIO offers very good latency, but requires special drivers which are -not always available for cheaper audio hardware. Also, when ASIO -drivers are available, they are not always so robust because they -bypass all of the standardised Windows device driver architecture -and hit the hardware their own way. -Alternatively there are a couple of free (but closed source) ASIO -implementations which connect to the lower level Windows -"Kernel Streaming" API, but again these require special installation -by the user, and can be limited in functionality or difficult to use. - -This is where the PortAudio "WDM-KS" host implementation comes in. -It directly connects PortAudio to the same Kernel Streaming API which -those ASIO bridges use. This avoids the mixing penatly of DirectX, -giving at least as good latency as any ASIO driver, but it has the -advantage of working with ANY Windows audio hardware which is available -through the normal MME/DirectX routes without the user requiring -any additional device drivers to be installed, and allowing all -device selection to be done through the normal PortAudio API. - -Note that in general you should only be using this host API if your -application has a real requirement for very low latency audio (<20ms), -either because you are generating sounds in real-time based upon -user input, or you a processing recorded audio in real time. - -The only thing to be aware of is that using the KS interface will -block that device from being used by the rest of system through -the higher level APIs, or conversely, if the system is using -a device, the KS API will not be able to use it. MS recommend that -you should keep the device open only when your application has focus. -In PortAudio terms, this means having a stream Open on a WDMKS device. - -Usage ------ -To add the WDMKS backend to your program which is already using -PortAudio, you must undefine PA_NO_WDMKS from your build file, -and include the pa_win_wdmks\pa_win_wdmks.c into your build. -The file should compile in both C and C++. -You will need a DirectX SDK installed on your system for the -ks.h and ksmedia.h header files. -You will need to link to the system "setupapi" library. -Note that if you use MinGW, you will get more warnings from -the DX header files when using GCC(C), and still a few warnings -with G++(CPP). \ No newline at end of file diff --git a/portaudio-v19/pa_win_wmme/pa_win_wmme.c b/portaudio-v19/pa_win_wmme/pa_win_wmme.c deleted file mode 100644 index 86b6cdf14..000000000 --- a/portaudio-v19/pa_win_wmme/pa_win_wmme.c +++ /dev/null @@ -1,3623 +0,0 @@ -/* - * $Id$ - * 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 - -/************************************************* 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)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT ); - else - mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, - (DWORD)handlesAndBuffers->bufferEvent, (DWORD)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 - { - if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ) - mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ); - } - - 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/portaudio-v19/pa_win_wmme/pa_win_wmme.h b/portaudio-v19/pa_win_wmme/pa_win_wmme.h deleted file mode 100644 index 458f88f99..000000000 --- a/portaudio-v19/pa_win_wmme/pa_win_wmme.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef PA_WIN_WMME_H -#define PA_WIN_WMME_H -/* - * $Id$ - * 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 */