diff --git a/3rdparty/portaudio/CMakeLists.txt b/3rdparty/portaudio/CMakeLists.txt new file mode 100644 index 0000000000..febdf2f017 --- /dev/null +++ b/3rdparty/portaudio/CMakeLists.txt @@ -0,0 +1,344 @@ +# $Id: $ +# +# For a "How-To" please refer to the Portaudio documentation at: +# http://www.portaudio.com/trac/wiki/TutorialDir/Compile/CMake +# +PROJECT( portaudio ) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +OPTION(PA_CONFIG_LIB_OUTPUT_PATH "Make sure that output paths are kept neat" OFF) +IF(CMAKE_CL_64) +SET(TARGET_POSTFIX x64) +IF(PA_CONFIG_LIB_OUTPUT_PATH) +SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin/x64) +ENDIF(PA_CONFIG_LIB_OUTPUT_PATH) +ELSE(CMAKE_CL_64) +SET(TARGET_POSTFIX x86) +IF(PA_CONFIG_LIB_OUTPUT_PATH) +SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin/Win32) +ENDIF(PA_CONFIG_LIB_OUTPUT_PATH) +ENDIF(CMAKE_CL_64) + +IF(WIN32 AND MSVC) +OPTION(PA_DLL_LINK_WITH_STATIC_RUNTIME "Link with static runtime libraries (minimizes runtime dependencies)" ON) +IF(PA_DLL_LINK_WITH_STATIC_RUNTIME) + FOREACH(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + IF(${flag_var} MATCHES "/MD") + STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + ENDIF(${flag_var} MATCHES "/MD") + ENDFOREACH(flag_var) +ENDIF(PA_DLL_LINK_WITH_STATIC_RUNTIME) + +ENDIF(WIN32 AND MSVC) + +IF(WIN32) +OPTION(PA_UNICODE_BUILD "Enable Portaudio Unicode build" ON) + +SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_support) +# Try to find DirectX SDK +FIND_PACKAGE(DXSDK) +# Try to find ASIO SDK (assumes that portaudio and asiosdk folders are side-by-side, see +# http://www.portaudio.com/trac/wiki/TutorialDir/Compile/WindowsASIOMSVC) +FIND_PACKAGE(ASIOSDK) + +IF(ASIOSDK_FOUND) +OPTION(PA_USE_ASIO "Enable support for ASIO" ON) +ELSE(ASIOSDK_FOUND) +OPTION(PA_USE_ASIO "Enable support for ASIO" OFF) +ENDIF(ASIOSDK_FOUND) +IF(DXSDK_FOUND) +OPTION(PA_USE_DS "Enable support for DirectSound" ON) +ELSE(DXSDK_FOUND) +OPTION(PA_USE_DS "Enable support for DirectSound" OFF) +ENDIF(DXSDK_FOUND) +OPTION(PA_USE_WMME "Enable support for MME" ON) +OPTION(PA_USE_WASAPI "Enable support for WASAPI" ON) +OPTION(PA_USE_WDMKS "Enable support for WDMKS" ON) +OPTION(PA_USE_WDMKS_DEVICE_INFO "Use WDM/KS API for device info" ON) +MARK_AS_ADVANCED(PA_USE_WDMKS_DEVICE_INFO) +IF(PA_USE_DS) +OPTION(PA_USE_DIRECTSOUNDFULLDUPLEXCREATE "Use DirectSound full duplex create" ON) +MARK_AS_ADVANCED(PA_USE_DIRECTSOUNDFULLDUPLEXCREATE) +ENDIF(PA_USE_DS) +ENDIF(WIN32) + +# Set variables for DEF file expansion +IF(NOT PA_USE_ASIO) +SET(DEF_EXCLUDE_ASIO_SYMBOLS ";") +ENDIF(NOT PA_USE_ASIO) + +IF(NOT PA_USE_WASAPI) +SET(DEF_EXCLUDE_WASAPI_SYMBOLS ";") +ENDIF(NOT PA_USE_WASAPI) + +IF(PA_USE_WDMKS_DEVICE_INFO) +ADD_DEFINITIONS(-DPAWIN_USE_WDMKS_DEVICE_INFO) +ENDIF(PA_USE_WDMKS_DEVICE_INFO) + +IF(PA_USE_DIRECTSOUNDFULLDUPLEXCREATE) +ADD_DEFINITIONS(-DPAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE) +ENDIF(PA_USE_DIRECTSOUNDFULLDUPLEXCREATE) + +####################################### +IF(WIN32) +INCLUDE_DIRECTORIES(src/os/win) +ENDIF(WIN32) + +IF(PA_USE_ASIO) +INCLUDE_DIRECTORIES(${ASIOSDK_ROOT_DIR}/common) +INCLUDE_DIRECTORIES(${ASIOSDK_ROOT_DIR}/host) +INCLUDE_DIRECTORIES(${ASIOSDK_ROOT_DIR}/host/pc) + +SET(PA_ASIO_INCLUDES + include/pa_asio.h +) + +SET(PA_ASIO_SOURCES + src/hostapi/asio/pa_asio.cpp +) + +SET(PA_ASIOSDK_SOURCES + ${ASIOSDK_ROOT_DIR}/common/asio.cpp + ${ASIOSDK_ROOT_DIR}/host/pc/asiolist.cpp + ${ASIOSDK_ROOT_DIR}/host/asiodrivers.cpp +) + +SOURCE_GROUP("hostapi\\ASIO" FILES + ${PA_ASIO_SOURCES} +) + +SOURCE_GROUP("hostapi\\ASIO\\ASIOSDK" FILES + ${PA_ASIOSDK_SOURCES} +) +ENDIF(PA_USE_ASIO) + +IF(PA_USE_DS) +INCLUDE_DIRECTORIES(${DXSDK_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(src/os/win) + +SET(PA_DS_INCLUDES + include/pa_win_ds.h + src/hostapi/dsound/pa_win_ds_dynlink.h +) + +SET(PA_DS_SOURCES + src/hostapi/dsound/pa_win_ds.c + src/hostapi/dsound/pa_win_ds_dynlink.c +) + +SOURCE_GROUP("hostapi\\dsound" FILES + ${PA_DS_INCLUDES} + ${PA_DS_SOURCES} +) +ENDIF(PA_USE_DS) + +IF(PA_USE_WMME) + +SET(PA_WMME_INCLUDES + include/pa_win_wmme.h +) + +SET(PA_WMME_SOURCES + src/hostapi/wmme/pa_win_wmme.c +) + +SOURCE_GROUP("hostapi\\wmme" FILES + ${PA_WMME_SOURCES} +) +ENDIF(PA_USE_WMME) + +IF(PA_USE_WASAPI) + +SET(PA_WASAPI_INCLUDES + include/pa_win_wasapi.h +) + +SET(PA_WASAPI_SOURCES + src/hostapi/wasapi/pa_win_wasapi.c +) + +SOURCE_GROUP("hostapi\\wasapi" FILES + ${PA_WASAPI_SOURCES} +) +ENDIF(PA_USE_WASAPI) + +IF(PA_USE_WDMKS) + +SET(PA_WDMKS_INCLUDES + include/pa_win_wdmks.h +) + +SET(PA_WDMKS_SOURCES + src/hostapi/wdmks/pa_win_wdmks.c +) + +SOURCE_GROUP("hostapi\\wdmks" FILES + ${PA_WDMKS_SOURCES} +) +ENDIF(PA_USE_WDMKS) + +SET(PA_SKELETON_SOURCES + src/hostapi/skeleton/pa_hostapi_skeleton.c +) + +SOURCE_GROUP("hostapi\\skeleton" + ${PA_SKELETON_SOURCES}) + +####################################### +IF(WIN32) +SET(PA_INCLUDES + include/portaudio.h + ${PA_ASIO_INCLUDES} + ${PA_DS_INCLUDES} + ${PA_WMME_INCLUDES} + ${PA_WASAPI_INCLUDES} + ${PA_WDMKS_INCLUDES} +) +ENDIF(WIN32) + +SOURCE_GROUP("include" FILES + ${PA_INCLUDES} +) + +SET(PA_COMMON_INCLUDES + src/common/pa_allocation.h + src/common/pa_converters.h + src/common/pa_cpuload.h + src/common/pa_debugprint.h + src/common/pa_dither.h + src/common/pa_endianness.h + src/common/pa_hostapi.h + src/common/pa_memorybarrier.h + src/common/pa_process.h + src/common/pa_ringbuffer.h + src/common/pa_stream.h + src/common/pa_trace.h + src/common/pa_types.h + src/common/pa_util.h +) + +SET(PA_COMMON_SOURCES + src/common/pa_allocation.c + src/common/pa_converters.c + src/common/pa_cpuload.c + src/common/pa_debugprint.c + src/common/pa_dither.c + src/common/pa_front.c + src/common/pa_process.c + src/common/pa_ringbuffer.c + src/common/pa_stream.c + src/common/pa_trace.c +) + +SOURCE_GROUP("common" FILES + ${PA_COMMON_INCLUDES} + ${PA_COMMON_SOURCES} +) + +SOURCE_GROUP("cmake_generated" FILES + ${CMAKE_CURRENT_BINARY_DIR}/portaudio_cmake.def + ${CMAKE_CURRENT_BINARY_DIR}/options_cmake.h +) + +IF(WIN32) +SET(PA_PLATFORM_SOURCES + src/os/win/pa_win_hostapis.c + src/os/win/pa_win_util.c + src/os/win/pa_win_waveformat.c + src/os/win/pa_win_wdmks_utils.c + src/os/win/pa_win_coinitialize.c + src/os/win/pa_x86_plain_converters.c +) + +SOURCE_GROUP("os\\win" FILES + ${PA_PLATFORM_SOURCES} +) +ENDIF(WIN32) + +INCLUDE_DIRECTORIES( include ) +INCLUDE_DIRECTORIES( src/common ) + +IF(WIN32 AND MSVC) +ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) +ENDIF(WIN32 AND MSVC) + +ADD_DEFINITIONS(-DPORTAUDIO_CMAKE_GENERATED) +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) + +SET(SOURCES_LESS_ASIO_SDK + ${PA_COMMON_SOURCES} + ${PA_ASIO_SOURCES} + ${PA_DS_SOURCES} + ${PA_WMME_SOURCES} + ${PA_WASAPI_SOURCES} + ${PA_WDMKS_SOURCES} + ${PA_SKELETON_SOURCES} + ${PA_PLATFORM_SOURCES} +) + +IF(PA_UNICODE_BUILD) +SET_SOURCE_FILES_PROPERTIES( + ${SOURCES_LESS_ASIO_SDK} + PROPERTIES + COMPILE_DEFINITIONS "UNICODE;_UNICODE" +) +ENDIF(PA_UNICODE_BUILD) + +ADD_LIBRARY(portaudio SHARED + ${PA_INCLUDES} + ${PA_COMMON_INCLUDES} + ${SOURCES_LESS_ASIO_SDK} + ${PA_ASIOSDK_SOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/portaudio_cmake.def + ${CMAKE_CURRENT_BINARY_DIR}/options_cmake.h +) + +ADD_LIBRARY(portaudio_static STATIC + ${PA_INCLUDES} + ${PA_COMMON_INCLUDES} + ${SOURCES_LESS_ASIO_SDK} + ${PA_ASIOSDK_SOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/options_cmake.h +) + +# Configure the exports file according to settings +SET(GENERATED_MESSAGE "CMake generated file, do NOT edit! Use CMake-GUI to change configuration instead.") +CONFIGURE_FILE( cmake_support/template_portaudio.def ${CMAKE_CURRENT_BINARY_DIR}/portaudio_cmake.def @ONLY ) +# Configure header for options (PA_USE_xxx) +CONFIGURE_FILE( cmake_support/options_cmake.h.in ${CMAKE_CURRENT_BINARY_DIR}/options_cmake.h @ONLY ) + +IF(WIN32) +# If we use DirectSound, we need this for the library to be found (if not in VS project settings) +IF(PA_USE_DS AND DXSDK_FOUND) +TARGET_LINK_LIBRARIES(portaudio ${DXSDK_DSOUND_LIBRARY}) +ENDIF(PA_USE_DS AND DXSDK_FOUND) + +# If we use WDM/KS we need setupapi.lib +IF(PA_USE_WDMKS) +TARGET_LINK_LIBRARIES(portaudio setupapi) +ENDIF(PA_USE_WDMKS) + +SET_TARGET_PROPERTIES(portaudio PROPERTIES OUTPUT_NAME portaudio_${TARGET_POSTFIX}) +SET_TARGET_PROPERTIES(portaudio_static PROPERTIES OUTPUT_NAME portaudio_static_${TARGET_POSTFIX}) +ENDIF(WIN32) + +OPTION(PA_BUILD_TESTS "Include test projects" OFF) +OPTION(PA_BUILD_EXAMPLES "Include example projects" OFF) + +# Prepared for inclusion of test files +IF(PA_BUILD_TESTS) +SUBDIRS(test) +ENDIF(PA_BUILD_TESTS) + +# Prepared for inclusion of test files +IF(PA_BUILD_EXAMPLES) +SUBDIRS(examples) +ENDIF(PA_BUILD_EXAMPLES) + +################################# + diff --git a/3rdparty/portaudio/build/msvc/portaudio.vcproj b/3rdparty/portaudio/build/msvc/portaudio.vcproj index 4a7e3525cd..5c91a11f58 100644 --- a/3rdparty/portaudio/build/msvc/portaudio.vcproj +++ b/3rdparty/portaudio/build/msvc/portaudio.vcproj @@ -942,6 +942,10 @@ RelativePath="..\..\include\pa_win_waveformat.h" > + + diff --git a/3rdparty/portaudio/build/msvc/portaudio.vcxproj b/3rdparty/portaudio/build/msvc/portaudio.vcxproj index 646d1eba34..20f2440104 100644 --- a/3rdparty/portaudio/build/msvc/portaudio.vcxproj +++ b/3rdparty/portaudio/build/msvc/portaudio.vcxproj @@ -639,6 +639,7 @@ + diff --git a/3rdparty/portaudio/build/msvc/portaudio.vcxproj.filters b/3rdparty/portaudio/build/msvc/portaudio.vcxproj.filters index 30203b3068..29aa7178a7 100644 --- a/3rdparty/portaudio/build/msvc/portaudio.vcxproj.filters +++ b/3rdparty/portaudio/build/msvc/portaudio.vcxproj.filters @@ -175,5 +175,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/3rdparty/portaudio/cmake_support/FindASIOSDK.cmake b/3rdparty/portaudio/cmake_support/FindASIOSDK.cmake new file mode 100644 index 0000000000..55ad33d956 --- /dev/null +++ b/3rdparty/portaudio/cmake_support/FindASIOSDK.cmake @@ -0,0 +1,41 @@ +# $Id: $ +# +# - Try to find the ASIO SDK +# Once done this will define +# +# ASIOSDK_FOUND - system has ASIO SDK +# ASIOSDK_ROOT_DIR - path to the ASIO SDK base directory +# ASIOSDK_INCLUDE_DIR - the ASIO SDK include directory + +if(WIN32) +else(WIN32) + message(FATAL_ERROR "FindASIOSDK.cmake: Unsupported platform ${CMAKE_SYSTEM_NAME}" ) +endif(WIN32) + +file(GLOB results "${CMAKE_CURRENT_SOURCE_DIR}/../as*") +foreach(f ${results}) + if(IS_DIRECTORY ${f}) + set(ASIOSDK_PATH_HINT ${ASIOSDK_PATH_HINT} ${f}) + endif() +endforeach() + +find_path(ASIOSDK_ROOT_DIR + common/asio.h + HINTS + ${ASIOSDK_PATH_HINT} +) + +find_path(ASIOSDK_INCLUDE_DIR + asio.h + PATHS + ${ASIOSDK_ROOT_DIR}/common +) + +# handle the QUIETLY and REQUIRED arguments and set ASIOSDK_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ASIOSDK DEFAULT_MSG ASIOSDK_ROOT_DIR ASIOSDK_INCLUDE_DIR) + +MARK_AS_ADVANCED( + ASIOSDK_ROOT_DIR ASIOSDK_INCLUDE_DIR +) diff --git a/3rdparty/portaudio/cmake_support/FindDXSDK.cmake b/3rdparty/portaudio/cmake_support/FindDXSDK.cmake new file mode 100644 index 0000000000..3d2f021d87 --- /dev/null +++ b/3rdparty/portaudio/cmake_support/FindDXSDK.cmake @@ -0,0 +1,59 @@ +# $Id: $ +# +# - Try to find the DirectX SDK +# Once done this will define +# +# DXSDK_FOUND - system has DirectX SDK +# DXSDK_ROOT_DIR - path to the DirectX SDK base directory +# DXSDK_INCLUDE_DIR - the DirectX SDK include directory +# DXSDK_LIBRARY_DIR - DirectX SDK libraries path +# +# DXSDK_DSOUND_LIBRARY - Path to dsound.lib +# + +if(WIN32) +else(WIN32) + message(FATAL_ERROR "FindDXSDK.cmake: Unsupported platform ${CMAKE_SYSTEM_NAME}" ) +endif(WIN32) + +find_path(DXSDK_ROOT_DIR + include/dxsdkver.h + HINTS + $ENV{DXSDK_DIR} +) + +find_path(DXSDK_INCLUDE_DIR + dxsdkver.h + PATHS + ${DXSDK_ROOT_DIR}/include +) + +IF(CMAKE_CL_64) +find_path(DXSDK_LIBRARY_DIR + dsound.lib + PATHS + ${DXSDK_ROOT_DIR}/lib/x64 +) +ELSE(CMAKE_CL_64) +find_path(DXSDK_LIBRARY_DIR + dsound.lib + PATHS + ${DXSDK_ROOT_DIR}/lib/x86 +) +ENDIF(CMAKE_CL_64) + +find_library(DXSDK_DSOUND_LIBRARY + dsound.lib + PATHS + ${DXSDK_LIBRARY_DIR} +) + +# handle the QUIETLY and REQUIRED arguments and set DXSDK_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DXSDK DEFAULT_MSG DXSDK_ROOT_DIR DXSDK_INCLUDE_DIR) + +MARK_AS_ADVANCED( + DXSDK_ROOT_DIR DXSDK_INCLUDE_DIR + DXSDK_LIBRARY_DIR DXSDK_DSOUND_LIBRARY +) diff --git a/3rdparty/portaudio/cmake_support/options_cmake.h.in b/3rdparty/portaudio/cmake_support/options_cmake.h.in new file mode 100644 index 0000000000..cd076058e6 --- /dev/null +++ b/3rdparty/portaudio/cmake_support/options_cmake.h.in @@ -0,0 +1,31 @@ +/* $Id: $ + + !!! @GENERATED_MESSAGE@ !!! + + Header file configured by CMake to convert CMake options/vars to macros. It is done this way because if set via + preprocessor options, MSVC f.i. has no way of knowing when an option (or var) changes as there is no dependency chain. + + The generated "options_cmake.h" should be included like so: + + #ifdef PORTAUDIO_CMAKE_GENERATED + #include "options_cmake.h" + #endif + + so that non-CMake build environments are left intact. + + Source template: cmake_support/options_cmake.h.in +*/ + +#ifdef _WIN32 +#if defined(PA_USE_ASIO) || defined(PA_USE_DS) || defined(PA_USE_WMME) || defined(PA_USE_WASAPI) || defined(PA_USE_WDMKS) +#error "This header needs to be included before pa_hostapi.h!!" +#endif + +#cmakedefine01 PA_USE_ASIO +#cmakedefine01 PA_USE_DS +#cmakedefine01 PA_USE_WMME +#cmakedefine01 PA_USE_WASAPI +#cmakedefine01 PA_USE_WDMKS +#else +#error "Platform currently not supported by CMake script" +#endif diff --git a/3rdparty/portaudio/cmake_support/template_portaudio.def b/3rdparty/portaudio/cmake_support/template_portaudio.def new file mode 100644 index 0000000000..8ce2a98bf2 --- /dev/null +++ b/3rdparty/portaudio/cmake_support/template_portaudio.def @@ -0,0 +1,53 @@ +; $Id: $ +; +; !!! @GENERATED_MESSAGE@ !!! +EXPORTS + +; +Pa_GetVersion @1 +Pa_GetVersionText @2 +Pa_GetErrorText @3 +Pa_Initialize @4 +Pa_Terminate @5 +Pa_GetHostApiCount @6 +Pa_GetDefaultHostApi @7 +Pa_GetHostApiInfo @8 +Pa_HostApiTypeIdToHostApiIndex @9 +Pa_HostApiDeviceIndexToDeviceIndex @10 +Pa_GetLastHostErrorInfo @11 +Pa_GetDeviceCount @12 +Pa_GetDefaultInputDevice @13 +Pa_GetDefaultOutputDevice @14 +Pa_GetDeviceInfo @15 +Pa_IsFormatSupported @16 +Pa_OpenStream @17 +Pa_OpenDefaultStream @18 +Pa_CloseStream @19 +Pa_SetStreamFinishedCallback @20 +Pa_StartStream @21 +Pa_StopStream @22 +Pa_AbortStream @23 +Pa_IsStreamStopped @24 +Pa_IsStreamActive @25 +Pa_GetStreamInfo @26 +Pa_GetStreamTime @27 +Pa_GetStreamCpuLoad @28 +Pa_ReadStream @29 +Pa_WriteStream @30 +Pa_GetStreamReadAvailable @31 +Pa_GetStreamWriteAvailable @32 +Pa_GetSampleSize @33 +Pa_Sleep @34 +@DEF_EXCLUDE_ASIO_SYMBOLS@PaAsio_GetAvailableBufferSizes @50 +@DEF_EXCLUDE_ASIO_SYMBOLS@PaAsio_ShowControlPanel @51 +PaUtil_InitializeX86PlainConverters @52 +@DEF_EXCLUDE_ASIO_SYMBOLS@PaAsio_GetInputChannelName @53 +@DEF_EXCLUDE_ASIO_SYMBOLS@PaAsio_GetOutputChannelName @54 +PaUtil_SetDebugPrintFunction @55 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_GetDeviceDefaultFormat @56 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_GetDeviceRole @57 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_ThreadPriorityBoost @58 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_ThreadPriorityRevert @59 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_GetFramesPerHostBuffer @60 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_GetJackDescription @61 +@DEF_EXCLUDE_WASAPI_SYMBOLS@PaWasapi_GetJackCount @62 diff --git a/3rdparty/portaudio/doc/src/mainpage.dox b/3rdparty/portaudio/doc/src/mainpage.dox index 3b3cf86714..8f40edeb79 100644 --- a/3rdparty/portaudio/doc/src/mainpage.dox +++ b/3rdparty/portaudio/doc/src/mainpage.dox @@ -1,60 +1,60 @@ -/* doxygen index page */ -/** @mainpage - -@section overview Overview - -PortAudio is a cross-platform, open-source C language library for real-time audio input and output. The library provides functions that allow your software to acquire and output real-time audio streams from your computer's hardware audio interfaces. It is designed to simplify writing cross-platform audio applications, and also to simplify the development of audio software in general by hiding the complexities of dealing directly with each native audio API. PortAudio is used to implement sound recording, editing and mixing applications, software synthesizers, effects processors, music players, internet telephony applications, software defined radios and more. Supported platforms include MS Windows, Mac OS X and Linux. Third-party language bindings make it possible to call PortAudio from other programming languages including C++, C#, Python, PureBasic, FreePascal and Lazarus. - - -@section start_here Start here - -- @ref api_overview
-A top-down view of the PortAudio API, its capabilities, functions and data structures - -- PortAudio Tutorials
-Get started writing code with PortAudio tutorials - -- @ref examples_src "Examples"
-Simple example programs demonstrating PortAudio usage - -- @ref License
-PortAudio is licenced under the MIT Expat open source licence. We make a non-binding request for you to contribute your changes back to the project. - - -@section reference API Reference - -- portaudio.h Portable API
-Detailed documentation for each portable API function and data type - -- @ref public_header "Host API Specific Extensions"
-Documentation for non-portable platform-specific host API extensions - - -@section resources Resources - -- The PortAudio website - -- Our mailing list for users and developers
- -- The PortAudio wiki - - -@section developer_resources Developer Resources - -@if INTERNAL -- @ref srcguide -@endif - -- Our Trac wiki and issue tracking system - -- Coding guidelines - -If you're interested in helping out with PortAudio development we're more than happy for you to be involved. Just drop by the PortAudio mailing list and ask how you can help. Or check out the starter tickets in Trac. - - -@section older_api_versions Older API Versions - -This documentation covers the current API version: PortAudio V19, API version 2.0. API 2.0 differs in a number of ways from previous versions (most often encountered in PortAudio V18), please consult the enhancement proposals for details of what was added/changed for V19: -http://www.portaudio.com/docs/proposals/index.html - +/* doxygen index page */ +/** @mainpage + +@section overview Overview + +PortAudio is a cross-platform, open-source C language library for real-time audio input and output. The library provides functions that allow your software to acquire and output real-time audio streams from your computer's hardware audio interfaces. It is designed to simplify writing cross-platform audio applications, and also to simplify the development of audio software in general by hiding the complexities of dealing directly with each native audio API. PortAudio is used to implement sound recording, editing and mixing applications, software synthesizers, effects processors, music players, internet telephony applications, software defined radios and more. Supported platforms include MS Windows, Mac OS X and Linux. Third-party language bindings make it possible to call PortAudio from other programming languages including C++, C#, Python, PureBasic, FreePascal and Lazarus. + + +@section start_here Start here + +- @ref api_overview
+A top-down view of the PortAudio API, its capabilities, functions and data structures + +- @ref tutorial_start
+Get started writing code with PortAudio tutorials + +- @ref examples_src "Examples"
+Simple example programs demonstrating PortAudio usage + +- @ref License
+PortAudio is licenced under the MIT Expat open source licence. We make a non-binding request for you to contribute your changes back to the project. + + +@section reference API Reference + +- portaudio.h Portable API
+Detailed documentation for each portable API function and data type + +- @ref public_header "Host API Specific Extensions"
+Documentation for non-portable platform-specific host API extensions + + +@section resources Resources + +- The PortAudio website + +- Our mailing list for users and developers
+ +- The PortAudio wiki + + +@section developer_resources Developer Resources + +@if INTERNAL +- @ref srcguide +@endif + +- Our wiki and issue tracking system + +- Coding guidelines + +If you're interested in helping out with PortAudio development we're more than happy for you to be involved. Just drop by the PortAudio mailing list and ask how you can help. Or check out the starter tickets. + + +@section older_api_versions Older API Versions + +This documentation covers the current API version: PortAudio V19, API version 2.0. API 2.0 differs in a number of ways from previous versions (most often encountered in PortAudio V18), please consult the enhancement proposals for details of what was added/changed for V19: +http://www.portaudio.com/docs/proposals/index.html + */ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/blocking_read_write.dox b/3rdparty/portaudio/doc/src/tutorial/blocking_read_write.dox new file mode 100644 index 0000000000..8905ee3810 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/blocking_read_write.dox @@ -0,0 +1,68 @@ +/** @page blocking_read_write Blocking Read/Write Functions +@ingroup tutorial + +PortAudio V19 adds a huge advance over previous versions with a feature called Blocking I/O. Although it may have lower performance that the callback method described earlier in this tutorial, blocking I/O is easier to understand and is, in some cases, more compatible with third party systems than the callback method. Most people starting audio programming also find Blocking I/O easier to learn. + +Blocking I/O works in much the same way as the callback method except that instead of providing a function to provide (or consume) audio data, you must feed data to (or consume data from) PortAudio at regular intervals, usually inside a loop. The example below, excepted from patest_read_write_wire.c, shows how to open the default device, and pass data from its input to its output for a set period of time. Note that we use the default high latency values to help avoid underruns since we are usually reading and writing audio data from a relatively low priority thread, and there is usually extra buffering required to make blocking I/O work. + +Note that not all API's implement Blocking I/O at this point, so for maximum portability or performance, you'll still want to use callbacks. + +@code + /* -- initialize PortAudio -- */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + /* -- setup input and output -- */ + inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ + inputParameters.channelCount = NUM_CHANNELS; + inputParameters.sampleFormat = PA_SAMPLE_TYPE; + inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ; + inputParameters.hostApiSpecificStreamInfo = NULL; + + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = NUM_CHANNELS; + outputParameters.sampleFormat = PA_SAMPLE_TYPE; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + /* -- setup stream -- */ + err = Pa_OpenStream( + &stream, + &inputParameters, + &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; + + /* -- start stream -- */ + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Wire on. Will run one minute.\n"); fflush(stdout); + + /* -- Here's the loop where we pass data from input to output -- */ + for( i=0; i<(60*SAMPLE_RATE)/FRAMES_PER_BUFFER; ++i ) + { + err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER ); + if( err ) goto xrun; + err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER ); + if( err ) goto xrun; + } + /* -- Now we stop the stream -- */ + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + /* -- don't forget to cleanup! -- */ + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + Pa_Terminate(); + return 0; +@endcode + + +Previous: \ref querying_devices | Next: \ref exploring + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_cmake.dox b/3rdparty/portaudio/doc/src/tutorial/compile_cmake.dox new file mode 100644 index 0000000000..ddf5eae610 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_cmake.dox @@ -0,0 +1,29 @@ +/** @page compile_cmake Creating MSVC Build Files via CMake +@ingroup tutorial + +This is a simple "How-to" for creating build files for Microsoft Visual C++ via CMake and the CMakeLists.txt file + +1. Install CMake if you haven't got it already ([http://www.cmake.org], minimum version required is 2.8). + +2. If you want ASIO support you need to D/L the ASIO2 SDK from Steinberg, and place it according to \ref compile_windows_asio_msvc + +3. Run the CMake GUI application and browse to source files directory and build directory: + a. The source files directory ("Where is the source code") is where the portaudio CMakeLists.txt file is located. + b. The build directory ("Where to build the binaries") is pretty much anywhere you like. A common practice though is to have the build directory located outside the + source files tree (a so called "out-of-source build") + +4. Click Configure. This will prompt you to select which build files to generate. Note Only Microsoft Visual C++ build files currently supported! + +5. In the CMake option list, enable the PORTAUDIO_xxx options you need, then click Configure again (Note that after this there are no options marked with red color) + +6. Click Generate and you'll now (hopefully) have your VS build files in your previously defined build directory. + +Both ASIO and DirectX SDK are automatically searched for by the CMake script, so if you have DirectX SDK installed and have placed the ASIO2 SDK according to point 2 above, you should be able to build portaudio with !DirectSound and ASIO support. + +Should you later on decide to change a portaudio option, just jump in at step 5 above (MSVC will then prompt you to reload projects/solutions/workspace) + +--- Robert Bielik + +Back to the Tutorial: \ref tutorial_start + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_linux.dox b/3rdparty/portaudio/doc/src/tutorial/compile_linux.dox new file mode 100644 index 0000000000..232cac67ea --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_linux.dox @@ -0,0 +1,77 @@ +/** @page compile_linux Building Portaudio for Linux +@ingroup tutorial + +Note: this page has not been reviewed, and may contain errors. + +@section comp_linux1 Installing ALSA Development Kit + +The OSS sound API is very old and not well supported. It is recommended that you use the ALSA sound API. +The PortAudio configure script will look for the ALSA SDK. You can install the ALSA SDK on Ubuntu using: + +@code +sudo apt-get install libasound-dev +@endcode + +You might need to use yum, or some other package manager, instead of apt-get on your machine. +If you do not install ALSA then you might get a message when testing that says you have no audio devices. + +You can find out more about ALSA here: http://www.alsa-project.org/ + +@section comp_linux2 Configuring and Compiling PortAudio + +You can build PortAudio in Linux Environments using the standard configure/make tools: + +@code +./configure && make +@endcode + +That will build PortAudio using Jack, ALSA and OSS in whatever combination they are found on your system. For example, if you have Jack and OSS but not ALSA, it will build using Jack and OSS but not ALSA. This step also builds a number of tests, which can be found in the bin directory of PortAudio. It's a good idea to run some of these tests to make sure PortAudio is working correctly. + +@section comp_linux3 Using PortAudio in your Projects + +To use PortAudio in your apps, you can simply install the .so files: + +@code +make install +@endcode + +Projects built this way will expect PortAudio to be installed on target systems in order to run. If you want to build a more self-contained binary, you may use the libportaudio.a file: + +@code +cp lib/.libs/libportaudio.a /YOUR/PROJECT/DIR +@endcode + +You may also need to copy portaudio.h, located in the include/ directory of PortAudio into your project. Note that you will usually need to link with the approriate libraries that you used, such as ALSA and JACK, as well as with librt and libpthread. For example: + +@code +gcc -lrt -lasound -ljack -lpthread -o YOUR_BINARY main.c libportaudio.a +@endcode + +@section comp_linux4 Linux Extensions + +Note that the ALSA PortAudio back-end adds a few extensions to the standard API that you may take advantage of. To use these functions be sure to include the pa_linux_alsa.h file found in the include file in the PortAudio folder. This file contains further documentation on the following functions: + + PaAlsaStreamInfo/PaAlsa_InitializeStreamInfo:: + Objects of the !PaAlsaStreamInfo type may be used for the !hostApiSpecificStreamInfo attribute of a !PaStreamParameters object, in order to specify the name of an ALSA device to open directly. Specify the device via !PaAlsaStreamInfo.deviceString, after initializing the object with PaAlsa_InitializeStreamInfo. + + PaAlsa_EnableRealtimeScheduling:: + PA ALSA supports real-time scheduling of the audio callback thread (using the FIFO pthread scheduling policy), via the extension PaAlsa_EnableRealtimeScheduling. Call this on the stream before starting it with the enableScheduling parameter set to true or false, to enable or disable this behaviour respectively. + + PaAlsa_GetStreamInputCard:: + Use this function to get the ALSA-lib card index of the stream's input device. + + PaAlsa_GetStreamOutputCard:: + Use this function to get the ALSA-lib card index of the stream's output device. + +Of particular importance is PaAlsa_EnableRealtimeScheduling, which allows ALSA to run at a high priority to prevent ordinary processes on the system from preempting audio playback. Without this, low latency audio playback will be irregular and will contain frequent drop-outs. + +@section comp_linux5 Linux Debugging + +Eliot Blennerhassett writes: + +On linux build, use e.g. "libtool gdb bin/patest_sine8" to debug that program. +This is because on linux bin/patest_sine8 is a libtool shell script that wraps +bin/.libs/patest_sine8 and allows it to find the appropriate libraries within +the build tree. + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_mac_coreaudio.dox b/3rdparty/portaudio/doc/src/tutorial/compile_mac_coreaudio.dox new file mode 100644 index 0000000000..6984d1e7c6 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_mac_coreaudio.dox @@ -0,0 +1,121 @@ +/** @page compile_mac_coreaudio Building Portaudio for Mac OS X +@ingroup tutorial + +@section comp_mac_ca_1 Requirements + +* OS X 10.4 or later. PortAudio v19 currently only compiles and runs on OS X version 10.4 or later. Because of its heavy reliance on memory barriers, it's not clear how easy it would be to back-port PortAudio to OS X version 10.3. Leopard support requires the 2007 snapshot or later. + +* Apple's Xcode and its related tools installed in the default location. There is no Xcode project for PortAudio. + +* Mac 10.4 SDK. Look for "/Developer/SDKs/MacOSX10.4u.sdk" folder on your system. It may be installed with XCode. If not then you can download it from Apple Developer Connection. http://connect.apple.com/ + +@section comp_mac_ca_2 Building + +To build PortAudio, simply use the Unix-style "./configure && make": + +@code + ./configure && make +@endcode + +You do not need to do "make install", and we don't recommend it; however, you may be using software that instructs you to do so, in which case you should follow those instructions. (Note from Phil: I had to do "sudo make install" after the command above, otherwise XCode complained that it could not find "/usr/local/lib/libportaudio.dylib" when I compiled an example.) + +The result of these steps will be a file named "libportaudio.dylib" in the directory "usr/local/lib/". + +By default, this will create universal binaries and therefore requires the Universal SDK from Apple, included with XCode 2.1 and higher. + +@section comp_mac_ca_3 Other Build Options + +There are a variety of other options for building PortAudio. The default described above is recommended as it is the most supported and tested; however, your needs may differ and require other options, which are described below. + +@subsection comp_mac_ca_3.1 Building Non-Universal Libraries + +By default, PortAudio is built as a universal binary. This includes 64-bit versions if you are compiling on 10.5, Leopard. If you want a "thin", or single architecture library, you have two options: + + * build a non-universal library using configure options. + * use lipo(1) on whatever part of the library you plan to use. + +Note that the first option may require an extremely recent version of PortAudio (February 5th '08 at least). + +@subsection comp_mac_ca_3.2 Building with --disable-mac-universal + +To build a non-universal library for the host architecture, simply use the --disable-mac-universal option with configure. + +@code + ./configure --disable-mac-universal && make +@endcode + +The --disable-mac-universal option may also be used in conjunction with environment variables to give you more control over the universal binary build process. For example, to build a universal binary for the i386 and ppc architectures using the 10.4u sdk (which is the default on 10.4, but not 10.5), you might specify this configure command line: + +@code + CFLAGS="-O2 -g -Wall -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3" \ + LDFLAGS="-arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3" \ + ./configure --disable-mac-universal --disable-dependency-tracking +@endcode + +For more info, see Apple's documentation on the matter: + + * http://developer.apple.com/technotes/tn2005/tn2137.html + * http://developer.apple.com/documentation/Porting/Conceptual/PortingUnix/intro/chapter_1_section_1.html + +@subsection comp_mac_ca_3.3 Using lipo + +The second option is to build normally, and use lipo (1) to extract the architectures you want. For example, if you want a "thin", i386 library only: + +@code + lipo lib/.libs/libportaudio.a -thin i386 -output libportaudio.a +@endcode + +or if you want to extract a single architecture fat file: + +@code + lipo lib/.libs/libportaudio.a -extract i386 -output libportaudio.a +@endcode + +@subsection comp_mac_ca_3.4 Building With Debug Options + +By default, PortAudio on the mac is built without any debugging options. This is because asserts are generally inappropriate for a production environment and debugging information has been suspected, though not proven, to cause trouble with some interfaces. If you would like to compile with debugging, you must run configure with the appropriate flags. For example: + +@code + ./configure --enable-mac-debug && make +@endcode + +This will enable -g and disable -DNDEBUG which will effectively enable asserts. + +@section comp_mac_ca_4 Using the Library in XCode Projects + +If you are planning to follow the rest of the tutorial, several project types will work. You can create a "Standard Tool" under "Command Line Utility". If you are not following the rest of the tutorial, any type of project should work with PortAudio, but these instructions may not work perfectly. + +Once you've compiled PortAudio, the easiest and recommended way to use PortAudio in your XCode project is to add "/include/portaudio.h" and "/lib/.libs/libportaudio.a" to your project. Because "/lib/.libs/" is a hidden directory, you won't be able to navigate to it using the finder or the standard Mac OS file dialogs by clicking on files and folders. You can use command-shift-G in the finder to specify the exact path, or, from the shell, if you are in the portaudio directory, you can enter this command: + +@code + open lib/.libs +@endcode + +Then drag the "libportaudio.a" file into your XCode project and place it in the "External Frameworks and Libraries" group, if the project type has it. If not you can simply add it to the top level folder of the project. + +You will need to add the following frameworks to your XCode project: + + - CoreAudio.framework + - AudioToolbox.framework + - AudioUnit.framework + - CoreServices.framework + +@section comp_mac_ca_5 Using the Library in Other Projects + +For gcc/Make style projects, include "include/portaudio.h" and link "libportaudio.a", and use the frameworks listed in the previous section. How you do so depends on your build. + +@section comp_mac_ca_6 Using Mac-only Extensions to PortAudio + +For additional, Mac-only extensions to the PortAudio interface, you may also want to grab "include/pa_mac_core.h". This file contains some special, mac-only features relating to sample-rate conversion, channel mapping, performance and device hogging. See "src/hostapi/coreaudio/notes.txt" for more details on these features. + +@section comp_mac_ca_7 What Happened to Makefile.darwin? + +Note, there used to be a special makefile just for darwin. This is no longer supported because you can build universal binaries from the standard configure routine. If you find this file in your directory structure it means you have an outdated version of PortAudio. + +@code + make -f Makefile.darwin +@endcode + +Back to the Tutorial: \ref tutorial_start + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_windows.dox b/3rdparty/portaudio/doc/src/tutorial/compile_windows.dox new file mode 100644 index 0000000000..63f1f63f7f --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_windows.dox @@ -0,0 +1,97 @@ +/** @page compile_windows Building Portaudio for Windows using Microsoft Visual Studio +@ingroup tutorial + +Below is a list of steps to build PortAudio into a dll and lib file. The resulting dll file may contain all five current win32 PortAudio APIs: MME, DirectSound, WASAPI, WDM/KS and ASIO, depending on the preprocessor definitions set in step 9 below. + +PortAudio can be compiled using Visual C++ Express Edition which is available free from Microsoft. If you do not already have a C++ development environment, simply download and install. These instructions have been observed to succeed using Visual Studio 2010 as well. + +1) PortAudio for Windows requires the files dsound.h and dsconf.h. Download and install the DirectX SDK from http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=3021d52b-514e-41d3-ad02-438a3ba730ba to obtain these files. If you installed the DirectX SDK then the !DirectSound libraries and header files should be found automatically by Visual !Studio/Visual C++. If you get an error saying dsound.h or dsconf.h is missing, you can declare these paths by hand. Alternatively, you can copy dsound.h and dsconf.h to portaudio\\include. There should also be a file named ''dsound.lib'' in C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib. + +2) For ASIO support, download the ASIO SDK from Steinberg at http://www.steinberg.net/en/company/developer.html. The SDK is free but you will need to set up a developer account with Steinberg. Copy the entire ASIOSDK2 folder into src\\hostapi\\asio\\. Rename it from ASIOSDK2 to ASIOSDK. To build without ASIO (or other host API) see the "Building without ASIO support" section below. + +3) If you have Visual Studio 6.0, 7.0(VC.NET/2001) or 7.1(VC.2003), open portaudio.dsp and convert if needed. + +4) If you have Visual Studio 2005, Visual C++ 2008 Express Edition or Visual Studio 2010, double click the portaudio.sln file located in build\\msvc\\. Doing so will open Visual Studio or Visual C++. Click "Finish" if a wizard appears. The sln file contains four configurations: Win32 and Win64 in both Release and Debug variants. + +@section comp_win1 For Visual Studio 2005, Visual C++ 2008 Express Edition or Visual Studio 2010 + +5) Open Project -> portaudio Properties and select "Configuration Properties" in the tree view. + +6) Select "all configurations" in the "Configurations" combo box above. Select "All Platforms" in the "Platforms" combo box. + +7) Now set a few options: + +C/C++ -> Optimization -> Omit frame pointers = Yes + +C/C++ -> Code Generation -> Runtime library = /MT + +Optional: C/C++ -> Code Generation -> Floating point model = fast + +NOTE: For most users it is not necessary to explicitly set the structure member alignment; the default should work fine. However some languages require, for example, 4-byte alignment. If you are having problems with portaudio.h structure members not being properly read or written to, it may be necessary to explicitly set this value by going to C/C++ -> Code Generation -> Struct member alignment and setting it to an appropriate value (four is a common value). If your compiler is configurable, you should ensure that it is set to use the same structure member alignment value as used for the PortAudio build. + +Click "Ok" when you have finished setting these parameters. + +@section comp_win2 Preprocessor Definitions + +Since the preprocessor definitions are different for each configuration and platform, you'll need to edit these individually for each configuration/platform combination that you want to modify using the "Configurations" and "Platforms" combo boxes. + +8) To suppress PortAudio runtime debug console output, go to Project -> Properties -> Configuration Properties -> C/C++ -> Preprocessor. In the field 'Preprocessor Definitions', find PA_ENABLE_DEBUG_OUTPUT and remove it. The console will not output debug messages. + +9) Also in the preprocessor definitions you need to explicitly define the audio APIs you wish to use. For Windows the available API definitions are: + +PA_USE_ASIO[[BR]] +PA_USE_DS (DirectSound)[[BR]] +PA_USE_WMME (MME)[[BR]] +PA_USE_WASAPI[[BR]] +PA_USE_WDMKS[[BR]] +PA_USE_SKELETON + +For each of these, the value of 0 indicates that support for this API should not be included. The value 1 indicates that support for this API should be included. + +@section comp_win3 Building + +As when setting Preprocessor definitions, building is a per-configuration per-platform process. Follow these instructions for each configuration/platform combination that you're interested in. + +10) From the Build menu click Build -> Build solution. For 32-bit compilations, the dll file created by this process (portaudio_x86.dll) can be found in the directory build\\msvc\\Win32\\Release. For 64-bit compilations, the dll file is called portaudio_x64.dll, and is found in the directory build\\msvc\\x64\\Release. + +11) Now, any project which requires portaudio can be linked with portaudio_x86.lib (or _x64) and include the relevant headers (portaudio.h, and/or pa_asio.h , pa_x86_plain_converters.h) You may want to add/remove some DLL entry points. Right now those 6 entries are not from portaudio.h: + +(from portaudio.def) +@code +... +PaAsio_GetAvailableLatencyValues @50 +PaAsio_ShowControlPanel @51 +PaUtil_InitializeX86PlainConverters @52 +PaAsio_GetInputChannelName @53 +PaAsio_GetOutputChannelName @54 +PaUtil_SetLogPrintFunction @55 +@endcode + +@section comp_win4 Building without ASIO support + +To build PortAudio without ASIO support you need to: + +1) Make sure your project doesn't try to build any ASIO SDK files. If you're using one of the shipped projects, remove the ASIO related files from the project. + +2) Make sure your project doesn't try to build the PortAudio ASIO implementation files: + +src\\hostapi\\pa_asio.cpp src\\hostapi\\iasiothiscallresolver.cpp + +If you're using one of the shipped projects, remove them from the project. + +3) Define the preprocessor symbols in the project properties as described in step 9 above. In VS2005 this can be accomplished by selecting +Project Properties -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions. Omitting PA_USE_ASIO or setting it to 0 stops src\\os\\win\\pa_win_hostapis.c from trying to initialize the PortAudio ASIO implementation. + +4) Remove PaAsio_* entry points from portaudio.def + + +----- +David Viens, davidv@plogue.com + +Updated by Chris on 5/26/2011 + +Improvements by John Clements on 12/15/2011 + +Back to the Tutorial: \ref tutorial_start + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_windows_asio_msvc.dox b/3rdparty/portaudio/doc/src/tutorial/compile_windows_asio_msvc.dox new file mode 100644 index 0000000000..8a8e10724d --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_windows_asio_msvc.dox @@ -0,0 +1,95 @@ +/** @page compile_windows_asio_msvc Building Portaudio for Windows with ASIO support using MSVC +@ingroup tutorial + +@section comp_win_asiomsvc1 Portaudio Windows ASIO with MSVC + +This tutorial describes how to build PortAudio with ASIO support using MSVC *from scratch*, without an existing Visual Studio project. For instructions for building PortAudio (including ASIO support) using the bundled Visual Studio project file see the compiling instructions for \ref compile_windows. + +ASIO is a low latency audio API from Steinberg. To compile an ASIO +application, you must first download the ASIO SDK from Steinberg. You also +need to obtain ASIO drivers for your audio device. Download the ASIO SDK from Steinberg at http://www.steinberg.net/en/company/developer.html. The SDK is free but you will need to set up a developer account with Steinberg. + +This tutorial assumes that you have 3 directories set up at the same level (side by side), one containing PortAudio, one containing the ASIO SDK and one containing your Visual Studio project: + +@code +/ASIOSDK2 +/portaudio +/DirContainingYourVisualStudioProject +@endcode + +First, make sure that the Steinberg SDK and the portaudio files are "side by side" in the same directory. + +Open Microsoft Visual C++ and create a new blank Console exe Project/Workspace in that same directory. + +For example, the paths for all three groups might read like this: + +@code +C:\Program Files\Microsoft Visual Studio\VC98\My Projects\ASIOSDK2 +C:\Program Files\Microsoft Visual Studio\VC98\My Projects\portaudio +C:\Program Files\Microsoft Visual Studio\VC98\My Projects\Sawtooth +@endcode + + +Next, add the following Steinberg ASIO SDK files to the project Source Files: + +@code +asio.cpp (ASIOSDK2\common) +asiodrivers.cpp (ASIOSDK2\host) +asiolist.cpp (ASIOSDK2\host\pc) +@endcode + + +Then, add the following PortAudio files to the project Source Files: + +@code +pa_asio.cpp (portaudio\src\hostapi\asio) +pa_allocation.c (portaudio\src\common) +pa_converters.c (portaudio\src\common) +pa_cpuload.c (portaudio\src\common) +pa_dither.c (portaudio\src\common) +pa_front.c (portaudio\src\common) +pa_process.c (portaudio\src\common) +pa_ringbuffer.c (portaudio\src\common) +pa_stream.c (portaudio\src\common) +pa_trace.c (portaudio\src\common) +pa_win_hostapis.c (portaudio\src\os\win) +pa_win_util.c (portaudio\src\os\win) +pa_win_waveformat.c (portaudio\src\os\win) +pa_x86_plain_converters.c (portaudio\src\os\win) +patest_saw.c (portaudio\test) (Or another file containing main() + for the console exe to be built.) +@endcode + + +Although not strictly necessary, you may also want to add the following files to the project Header Files: + +@code +portaudio.h (portaudio\include) +pa_asio.h (portaudio\include) +@endcode + +These header files define the interfaces to the PortAudio API. + + +Next, go to Project Settings > All Configurations > C/C++ > Preprocessor > Preprocessor definitions and add +PA_USE_ASIO=1 to any entries that might be there. + +eg: WIN32;_CONSOLE;_MBCS changes to WIN32;_CONSOLE,_MBCS;PA_USE_ASIO=1 + +Then, on the same Project Settings tab, go down to Additional include directories: and enter the following relative include paths. + +@code +..\portaudio\include,..\portaudio\src\common,..\asiosdk2\common,..\asiosdk2\host,..\asiosdk2\host\pc +@endcode + +You'll need to make sure the relative paths are correct for the particular directory layout you're using. The above should work fine if you use the side-by-side layout we recommended earlier. + +You should now be able to build any of the test executables in the portaudio\test directory. +We suggest that you start with patest_saw.c because it's one of the simplest test files. + +--- Chris Share, Tom McCandless, Ross Bencina + +[wiki:UsingThePortAudioSvnRepository SVN instructions] +Back to the Tutorial: \ref tutorial_start + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/compile_windows_mingw.dox b/3rdparty/portaudio/doc/src/tutorial/compile_windows_mingw.dox new file mode 100644 index 0000000000..47bba386a9 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/compile_windows_mingw.dox @@ -0,0 +1,53 @@ +/** @page compile_windows_mingw Building Portaudio for Windows with MinGW +@ingroup tutorial + +@section comp_mingw1 Portaudio for Windows With MinGW + +The following document is still being reviewed + += MinGW/MSYS = + +From the [http://www.mingw.org MinGW projectpage]: + +MinGW: A collection of freely available and freely distributable +Windows specific header files and import libraries, augmenting +the GNU Compiler Collection, (GCC), and its associated +tools, (GNU binutils). MinGW provides a complete Open Source +programming tool set which is suitable for the development of +native Windows programs that do not depend on any 3rd-party C +runtime DLLs. + +MSYS: A Minimal SYStem providing a POSIX compatible Bourne shell +environment, with a small collection of UNIX command line +tools. Primarily developed as a means to execute the configure +scripts and Makefiles used to build Open Source software, but +also useful as a general purpose command line interface to +replace Windows cmd.exe. + +MinGW provides a compiler/linker toolchain while MSYS is required +to actually run the PortAudio configure script. + +Once MinGW and MSYS are installed (see the [http://www.mingw.org/MinGWiki MinGW-Wiki]) open an MSYS shell and run the famous: + +@code +./configure +make +make install +@endcode + +The above should create a working version though you might want to +provide '--prefix=' to configure. + +'./configure --help' gives details as to what can be tinkered with. + +--- Mikael Magnusson + +To update your copy or check out a fresh copy of the source + +[wiki:UsingThePortAudioSvnRepository SVN instructions] + +--- Bob !McGwier + +Back to the Tutorial: \ref tutorial_start + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/exploring.dox b/3rdparty/portaudio/doc/src/tutorial/exploring.dox new file mode 100644 index 0000000000..9dd0873484 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/exploring.dox @@ -0,0 +1,15 @@ +/** @page exploring Exploring PortAudio +@ingroup tutorial + +Now that you have a good idea of how PortAudio works, you can try out the example programs. You'll find them in the examples/ directory in the PortAudio distribution. + +For an example of playing a sine wave, see examples/paex_sine.c. + +For an example of recording and playing back a sound, see examples/paex_record.c. + +I also encourage you to examine the source for the PortAudio libraries. If you have suggestions on ways to improve them, please let us know. If you want to implement PortAudio on a new platform, please let us know as well so we can coordinate people's efforts. + + +Previous: \ref blocking_read_write | Next: This is the end of the tutorial. + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/initializing_portaudio.dox b/3rdparty/portaudio/doc/src/tutorial/initializing_portaudio.dox new file mode 100644 index 0000000000..9439c350d6 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/initializing_portaudio.dox @@ -0,0 +1,29 @@ +/** @page initializing_portaudio Initializing PortAudio +@ingroup tutorial + +@section tut_init1 Initializing PortAudio + +Before making any other calls to PortAudio, you 'must' call Pa_Initialize(). This will trigger a scan of available devices which can be queried later. Like most PA functions, it will return a result of type paError. If the result is not paNoError, then an error has occurred. +@code +err = Pa_Initialize(); +if( err != paNoError ) goto error; +@endcode + +You can get a text message that explains the error message by passing it to Pa_GetErrorText( err ). For Example: + +@code +printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) ); +@endcode + +It is also important, when you are done with PortAudio, to Terminate it: + +@code +err = Pa_Terminate(); +if( err != paNoError ) + printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) ); +@endcode + + +Previous: \ref writing_a_callback | Next: \ref open_default_stream + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/open_default_stream.dox b/3rdparty/portaudio/doc/src/tutorial/open_default_stream.dox new file mode 100644 index 0000000000..7512d1e7eb --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/open_default_stream.dox @@ -0,0 +1,48 @@ +/** @page open_default_stream Opening a Stream Using Defaults +@ingroup tutorial + +The next step is to open a stream, which is similar to opening a file. You can specify whether you want audio input and/or output, how many channels, the data format, sample rate, etc. Opening a ''default'' stream means opening the default input and output devices, which saves you the trouble of getting a list of devices and choosing one from the list. (We'll see how to do that later.) +@code +#define SAMPLE_RATE (44100) +static paTestData data; + +..... + + PaStream *stream; + PaError err; + + /* 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, i.e. the number + of sample frames that PortAudio will + request from the callback. Many apps + may want to use + paFramesPerBufferUnspecified, which + tells PortAudio to pick the best, + possibly changing, buffer size.*/ + patestCallback, /* this is your callback function */ + &data ); /*This is a pointer that will be passed to + your callback*/ + if( err != paNoError ) goto error; +@endcode + +The data structure and callback are described in \ref writing_a_callback. + +The above example opens the stream for writing, which is sufficient for playback. It is also possible to open a stream for reading, to do recording, or both reading and writing, for simultaneous recording and playback or even real-time audio processing. If you plan to do playback and recording at the same time, open only one stream with valid input and output parameters. + +There are some caveats to note about simultaneous read/write: + + - Some platforms can only open a read/write stream using the same device. + - Although multiple streams can be opened, it is difficult to synchronize them. + - Some platforms don't support opening multiple streams on the same device. + - Using multiple streams may not be as well tested as other features. + - The PortAudio library calls must be made from the same thread or synchronized by the user. + + +Previous: \ref initializing_portaudio | Next: \ref start_stop_abort + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/querying_devices.dox b/3rdparty/portaudio/doc/src/tutorial/querying_devices.dox new file mode 100644 index 0000000000..0c23274954 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/querying_devices.dox @@ -0,0 +1,111 @@ +/** @page querying_devices Enumerating and Querying PortAudio Devices +@ingroup tutorial + +@section tut_query1 Querying Devices + +It is often fine to use the default device as we did previously in this tutorial, but there are times when you'll want to explicitly choose the device from a list of available devices on the system. To see a working example of this, check out pa_devs.c in the tests/ directory of the PortAudio source code. To do so, you'll need to first initialize PortAudio and Query for the number of Devices: + +@code + int numDevices; + + numDevices = Pa_GetDeviceCount(); + if( numDevices < 0 ) + { + printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } +@endcode + + +If you want to get information about each device, simply loop through as follows: + +@code + const PaDeviceInfo *deviceInfo; + + for( i=0; idefaultLowInputLatency ; + inputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host's API docs for info on using this field + + + bzero( &outputParameters, sizeof( outputParameters ) ); //not necessary if you are filling in all the fields + outputParameters.channelCount = outChan; + outputParameters.device = outDevNum; + outputParameters.hostApiSpecificStreamInfo = NULL; + outputParameters.sampleFormat = paFloat32; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(outDevNum)->defaultLowOutputLatency ; + outputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host's API docs for info on using this field + + err = Pa_OpenStream( + &stream, + &inputParameters, + &outputParameters, + srate, + framesPerBuffer, + paNoFlag, //flags that can be used to define dither, clip settings and more + portAudioCallback, //your callback function + (void *)this ); //data to be passed to callback. In C++, it is frequently (void *)this + //don't forget to check errors! +@endcode + + +Previous: \ref utility_functions | Next: \ref blocking_read_write + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/start_stop_abort.dox b/3rdparty/portaudio/doc/src/tutorial/start_stop_abort.dox new file mode 100644 index 0000000000..6d4f777523 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/start_stop_abort.dox @@ -0,0 +1,35 @@ +/** @page start_stop_abort Starting, Stopping and Aborting a Stream +@ingroup tutorial + +@section tut_startstop1 Starting, Stopping and Aborting a Stream + +PortAudio will not start playing back audio until you start the stream. After calling Pa_StartStream(), PortAudio will start calling your callback function to perform the audio processing. + +@code + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; +@endcode + +You can communicate with your callback routine through the data structure you passed in on the open call, or through global variables, or using other interprocess communication techniques, but please be aware that your callback function may be called at interrupt time when your foreground process is least expecting it. So avoid sharing complex data structures that are easily corrupted like double linked lists, and avoid using locks such as mutexs as this may cause your callback function to block and therefore drop audio. Such techniques may even cause deadlock on some platforms. + +PortAudio will continue to call your callback and process audio until you stop the stream. This can be done in one of several ways, but, before we do so, we'll want to see that some of our audio gets processed by sleeping for a few seconds. This is easy to do with Pa_Sleep(), which is used by many of the examples in the patests/ directory for exactly this purpose. Note that, for a variety of reasons, you can not rely on this function for accurate scheduling, so your stream may not run for exactly the same amount of time as you expect, but it's good enough for our example. + +@code + /* Sleep for several seconds. */ + Pa_Sleep(NUM_SECONDS*1000); +@endcode + +Now we need to stop playback. There are several ways to do this, the simplest of which is to call Pa_StopStream(): + +@code + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; +@endcode + +Pa_StopStream() is designed to make sure that the buffers you've processed in your callback are all played, which may cause some delay. Alternatively, you could call Pa_AbortStream(). On some platforms, aborting the stream is much faster and may cause some data processed by your callback not to be played. + +Another way to stop the stream is to return either paComplete, or paAbort from your callback. paComplete ensures that the last buffer is played whereas paAbort stops the stream as soon as possible. If you stop the stream using this technique, you will need to call Pa_StopStream() before starting the stream again. + +Previous: \ref open_default_stream | Next: \ref terminating_portaudio + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/terminating_portaudio.dox b/3rdparty/portaudio/doc/src/tutorial/terminating_portaudio.dox new file mode 100644 index 0000000000..67f74f6bc7 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/terminating_portaudio.dox @@ -0,0 +1,20 @@ +/** @page terminating_portaudio Closing a Stream and Terminating PortAudio +@ingroup tutorial + +When you are done with a stream, you should close it to free up resources: + +@code + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; +@endcode + +We've already mentioned this in \ref initializing_portaudio, but in case you forgot, be sure to terminate PortAudio when you are done: + +@code + err = Pa_Terminate( ); + if( err != paNoError ) goto error; +@endcode + +Previous: \ref start_stop_abort | Next: \ref utility_functions + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/tutorial_start.dox b/3rdparty/portaudio/doc/src/tutorial/tutorial_start.dox new file mode 100644 index 0000000000..b80bc236e5 --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/tutorial_start.dox @@ -0,0 +1,56 @@ +/** @page tutorial_start PortAudio Tutorials +@ingroup tutorial + +These tutorials takes you through a hands-on example of using PortAudio to make sound. If you'd prefer to start with a top-down overview of the PortAudio API, check out the @ref api_overview. + +@section tut_start1 Downloading + +First thing you need to do is download the PortAudio source code either as a tarball from the website, or from the Subversion Repository. + +@section tut_start2 Compiling + +Once you've downloaded PortAudio you'll need to compile it, which of course, depends on your environment: + + - Windows + - \ref compile_windows + - \ref compile_windows_mingw + - \ref compile_windows_asio_msvc + - \ref compile_cmake + - Mac OS X + - \ref compile_mac_coreaudio + - POSIX + - \ref compile_linux + +Many platforms with GCC/make can use the simple ./configure && make combination and simply use the resulting libraries in their code. + +@section tut_start3 Programming with PortAudio + +Below are the steps to writing a PortAudio application: + + - Write a callback function that will be called by PortAudio when audio processing is needed. + - Initialize the PA library and open a stream for audio I/O. + - Start the stream. Your callback function will be now be called repeatedly by PA in the background. + - In your callback you can read audio data from the inputBuffer and/or write data to the outputBuffer. + - Stop the stream by returning 1 from your callback, or by calling a stop function. + - Close the stream and terminate the library. + +In addition to this "Callback" architecture, V19 also supports a "Blocking I/O" model which uses read and write calls which may be more familiar to non-audio programmers. Note that at this time, not all APIs support this functionality. + +In this tutorial, we'll show how to use the callback architecture to play a sawtooth wave. Much of the tutorial is taken from the file paex_saw.c, which is part of the PortAudio distribution. When you're done with this tutorial, you'll be armed with the basic knowledge you need to write an audio program. If you need more sample code, look in the "examples" and "test" directory of the PortAudio distribution. Another great source of info is the portaudio.h Doxygen page, which documents the entire V19 API. Also see the page for tips on programming PortAudio on the PortAudio wiki. + +If you are upgrading from V18, you may want to look at the Proposed Enhancements to PortAudio, which describes the differences between V18 and V19. + +- \ref writing_a_callback +- \ref initializing_portaudio +- \ref open_default_stream +- \ref start_stop_abort +- \ref terminating_portaudio +- \ref utility_functions +- \ref querying_devices +- \ref blocking_read_write + +Once you have a basic understanding of how to use PortAudio, you might be interested in \ref exploring. + +Next: \ref writing_a_callback + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/utility_functions.dox b/3rdparty/portaudio/doc/src/tutorial/utility_functions.dox new file mode 100644 index 0000000000..c06bf3bcaf --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/utility_functions.dox @@ -0,0 +1,69 @@ +/** @page utility_functions Utility Functions +@ingroup tutorial + +In addition to the functions described elsewhere in this tutorial, PortAudio provides a number of Utility functions that are useful in a variety of circumstances. +You'll want to read the portaudio.h reference, which documents the entire V19 API for details, but we'll try to cover the basics here. + +@section tut_util2 Version Information + +PortAudio offers two functions to determine the PortAudio Version. This is most useful when you are using PortAudio as a dynamic library, but it may also be useful at other times. + +@code +int Pa_GetVersion (void) +const char * Pa_GetVersionText (void) +@endcode + +@section tut_util3 Error Text + +PortAudio allows you to get error text from an error number. + +@code +const char * Pa_GetErrorText (PaError errorCode) +@endcode + +@section tut_util4 Stream State + +PortAudio Streams exist in 3 states: Active, Stopped, and Callback Stopped. If a stream is in callback stopped state, you'll need to stop it before you can start it again. If you need to query the state of a PortAudio stream, there are two functions for doing so: + +@code +PaError Pa_IsStreamStopped (PaStream *stream) +PaError Pa_IsStreamActive (PaStream *stream) +@endcode + +@section tut_util5 Stream Info + +If you need to retrieve info about a given stream, such as latency, and sample rate info, there's a function for that too: + +@code +const PaStreamInfo * Pa_GetStreamInfo (PaStream *stream) +@endcode + +@section tut_util6 Stream Time + +If you need to synchronise other activities such as display updates or MIDI output with the PortAudio callback you need to know the current time according to the same timebase used by the stream callback timestamps. + +@code +PaTime Pa_GetStreamTime (PaStream *stream) +@endcode + +@section tut_util6CPU Usage + +To determine how much CPU is being used by the callback, use these: + +@code +double Pa_GetStreamCpuLoad (PaStream *stream) +@endcode + +@section tut_util7 Other utilities + +These functions allow you to determine the size of a sample from its format and sleep for a given amount of time. The sleep function should not be used for precise timing or synchronization because it makes few guarantees about the exact length of time it waits. It is most useful for testing. + +@code +PaError Pa_GetSampleSize (PaSampleFormat format) +void Pa_Sleep (long msec) +@endcode + + +Previous: \ref terminating_portaudio | Next: \ref querying_devices + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/doc/src/tutorial/writing_a_callback.dox b/3rdparty/portaudio/doc/src/tutorial/writing_a_callback.dox new file mode 100644 index 0000000000..f0a43ccd0e --- /dev/null +++ b/3rdparty/portaudio/doc/src/tutorial/writing_a_callback.dox @@ -0,0 +1,66 @@ +/** @page writing_a_callback Writing a Callback Function +@ingroup tutorial + +To write a program using PortAudio, you must include the "portaudio.h" include file. You may wish to read "portaudio.h" because it contains a complete description of the PortAudio functions and constants. Alternatively, you could browse the [http://www.portaudio.com/docs/v19-doxydocs/portaudio_8h.html "portaudio.h" Doxygen page] +@code +#include "portaudio.h" +@endcode +The next task is to write your own "callback" function. The "callback" is a function that is called by the PortAudio engine whenever it has captured audio data, or when it needs more audio data for output. + +Before we begin, it's important to realize that the callback is a delicate place. This is because some systems perform the callback in a special thread, or interrupt handler, and it is rarely treated the same as the rest of your code. In addition, if you want your audio to reach the speakers on time, you'll need to make sure whatever code you run in the callback runs quickly. What is safe or not safe will vary from platform to platform, but as a rule of thumb, don't do anything like allocating or freeing memory, reading or writing files, printf(), or anything else that might take an unbounded amount of time or rely on the OS or require a context switch. Ed: is this still true?: Also do not call any PortAudio functions in the callback except for Pa_StreamTime() and Pa_GetCPULoad(). + +Your callback function must return an int and accept the exact parameters specified in this typedef: + +@code +typedef int PaStreamCallback( const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) ; +@endcode +Here is an example callback function from the test file "patests/patest_saw.c". It calculates a simple left and right sawtooth signal and writes it to the output buffer. Notice that in this example, the signals are of float data type. The signals must be between -1.0 and +1.0. You can also use 16 bit integers or other formats which are specified during setup, but floats are easiest to work with. You can pass a pointer to your data structure through PortAudio which will appear as userData. + +@code +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; +} +@endcode + +Previous: \ref tutorial_start | Next: \ref initializing_portaudio + +*/ \ No newline at end of file diff --git a/3rdparty/portaudio/include/pa_mac_core.h b/3rdparty/portaudio/include/pa_mac_core.h index f7a90f08ff..1d615feed4 100644 --- a/3rdparty/portaudio/include/pa_mac_core.h +++ b/3rdparty/portaudio/include/pa_mac_core.h @@ -124,6 +124,19 @@ AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s ); */ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input ); + +/** Retrieve the range of legal native buffer sizes for the specificed device, in sample frames. + + @param device The global index of the PortAudio device about which the query is being made. + @param minBufferSizeFrames A pointer to the location which will receive the minimum buffer size value. + @param maxBufferSizeFrames A pointer to the location which will receive the maximum buffer size value. + + @see kAudioDevicePropertyBufferFrameSizeRange in the CoreAudio SDK. + */ +PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device, + long *minBufferSizeFrames, long *maxBufferSizeFrames ); + + /** * Flags */ diff --git a/3rdparty/portaudio/include/pa_win_wdmks.h b/3rdparty/portaudio/include/pa_win_wdmks.h new file mode 100644 index 0000000000..9fe9284bd1 --- /dev/null +++ b/3rdparty/portaudio/include/pa_win_wdmks.h @@ -0,0 +1,106 @@ +#ifndef PA_WIN_WDMKS_H +#define PA_WIN_WDMKS_H +/* + * $Id: pa_win_wdmks.h 1812 2012-02-14 09:32:57Z robiwan $ + * PortAudio Portable Real-Time Audio Library + * WDM/KS specific extensions + * + * Copyright (c) 1999-2007 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief WDM Kernel Streaming-specific PortAudio API extension header file. +*/ + + +#include "portaudio.h" + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + typedef struct PaWinWDMKSInfo{ + unsigned long size; /**< sizeof(PaWinWDMKSInfo) */ + PaHostApiTypeId hostApiType; /**< paWDMKS */ + unsigned long version; /**< 1 */ + + /* The number of packets to use for WaveCyclic devices, range is [2, 8]. Set to zero for default value of 2. */ + unsigned noOfPackets; + } PaWinWDMKSInfo; + + typedef enum PaWDMKSType + { + Type_kNotUsed, + Type_kWaveCyclic, + Type_kWaveRT, + Type_kCnt, + } PaWDMKSType; + + typedef enum PaWDMKSSubType + { + SubType_kUnknown, + SubType_kNotification, + SubType_kPolled, + SubType_kCnt, + } PaWDMKSSubType; + + typedef struct PaWinWDMKSDeviceInfo { + wchar_t filterPath[MAX_PATH]; /**< KS filter path in Unicode! */ + wchar_t topologyPath[MAX_PATH]; /**< Topology filter path in Unicode! */ + PaWDMKSType streamingType; + GUID deviceProductGuid; /**< The product GUID of the device (if supported) */ + } PaWinWDMKSDeviceInfo; + + typedef struct PaWDMKSDirectionSpecificStreamInfo + { + PaDeviceIndex device; + unsigned channels; /**< No of channels the device is opened with */ + unsigned framesPerHostBuffer; /**< No of frames of the device buffer */ + int endpointPinId; /**< Endpoint pin ID (on topology filter if topologyName is not empty) */ + int muxNodeId; /**< Only valid for input */ + PaWDMKSSubType streamingSubType; /**< Not known until device is opened for streaming */ + } PaWDMKSDirectionSpecificStreamInfo; + + typedef struct PaWDMKSSpecificStreamInfo { + PaWDMKSDirectionSpecificStreamInfo input; + PaWDMKSDirectionSpecificStreamInfo output; + } PaWDMKSSpecificStreamInfo; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_DS_H */ diff --git a/3rdparty/portaudio/src/SConscript b/3rdparty/portaudio/src/SConscript index 5cc915216d..84ab1338b8 100644 --- a/3rdparty/portaudio/src/SConscript +++ b/3rdparty/portaudio/src/SConscript @@ -1,220 +1,220 @@ -import os.path, copy, sys - -def checkSymbol(conf, header, library=None, symbol=None, autoAdd=True, critical=False, pkgName=None): - """ Check for symbol in library, optionally look only for header. - @param conf: Configure instance. - @param header: The header file where the symbol is declared. - @param library: The library in which the symbol exists, if None it is taken to be the standard C library. - @param symbol: The symbol to look for, if None only the header will be looked up. - @param autoAdd: Automatically link with this library if check is positive. - @param critical: Raise on error? - @param pkgName: Optional name of pkg-config entry for library, to determine build parameters. - @return: True/False - """ - origEnv = conf.env.Copy() # Copy unmodified environment so we can restore it upon error - env = conf.env - if library is None: - library = "c" # Standard library - autoAdd = False - - if pkgName is not None: - origLibs = copy.copy(env.get("LIBS", None)) - - try: env.ParseConfig("pkg-config --silence-errors %s --cflags --libs" % pkgName) - except: pass - else: - # I see no other way of checking that the parsing succeeded, if it did add no more linking parameters - if env.get("LIBS", None) != origLibs: - autoAdd = False - - try: - if not conf.CheckCHeader(header, include_quotes="<>"): - raise ConfigurationError("missing header %s" % header) - if symbol is not None and not conf.CheckLib(library, symbol, language="C", autoadd=autoAdd): - raise ConfigurationError("missing symbol %s in library %s" % (symbol, library)) - except ConfigurationError: - conf.env = origEnv - if not critical: - return False - raise - - return True - -import SCons.Errors - -# Import common variables - -# Could use '#' to refer to top-level SConstruct directory, but looks like env.SConsignFile doesn't interpret this at least :( -sconsDir = os.path.abspath(os.path.join("build", "scons")) - -try: - Import("Platform", "Posix", "ConfigurationError", "ApiVer") -except SCons.Errors.UserError: - # The common objects must be exported first - SConscript(os.path.join(sconsDir, "SConscript_common")) - Import("Platform", "Posix", "ConfigurationError", "ApiVer") - -Import("env") - -# This will be manipulated -env = env.Copy() - -# We operate with a set of needed libraries and optional libraries, the latter stemming from host API implementations. -# For libraries of both types we record a set of values that is used to look for the library in question, during -# configuration. If the corresponding library for a host API implementation isn't found, the implementation is left out. -neededLibs = [] -optionalImpls = {} -if Platform in Posix: - env.Append(CPPPATH=os.path.join("os", "unix")) - neededLibs += [("pthread", "pthread.h", "pthread_create"), ("m", "math.h", "sin")] - if env["useALSA"]: - optionalImpls["ALSA"] = ("asound", "alsa/asoundlib.h", "snd_pcm_open") - if env["useJACK"]: - optionalImpls["JACK"] = ("jack", "jack/jack.h", "jack_client_new") - if env["useOSS"]: - # TODO: It looks like the prefix for soundcard.h depends on the platform - optionalImpls["OSS"] = ("oss", "sys/soundcard.h", None) - if Platform == 'netbsd': - optionalImpls["OSS"] = ("ossaudio", "sys/soundcard.h", "_oss_ioctl") - if env["useASIHPI"]: - optionalImpls["ASIHPI"] = ("hpi", "asihpi/hpi.h", "HPI_SubSysCreate") - if env["useCOREAUDIO"]: - optionalImpls["COREAUDIO"] = ("CoreAudio", "CoreAudio/CoreAudio.h", None) -else: - raise ConfigurationError("unknown platform %s" % Platform) - -if Platform == "darwin": - env.Append(LINKFLAGS="-framework CoreFoundation -framework CoreServices -framework CoreAudio -framework AudioToolBox -framework AudioUnit") -elif Platform == "cygwin": - env.Append(LIBS=["winmm"]) -elif Platform == "irix": - neededLibs += [("audio", "dmedia/audio.h", "alOpenPort"), ("dmedia", "dmedia/dmedia.h", "dmGetUST")] - env.Append(CPPDEFINES=["PA_USE_SGI"]) - -def CheckCTypeSize(context, tp): - """ Check size of C type. - @param context: A configuration context. - @param tp: The type to check. - @return: Size of type, in bytes. - """ - context.Message("Checking the size of C type %s..." % tp) - ret = context.TryRun(""" -#include - -int main() { - printf("%%d", sizeof(%s)); - return 0; -} -""" % tp, ".c") - if not ret[0]: - context.Result(" Couldn't obtain size of type %s!" % tp) - return None - - assert ret[1] - sz = int(ret[1]) - context.Result("%d" % sz) - return sz - -""" -if sys.byteorder == "little": - env.Append(CPPDEFINES=["PA_LITTLE_ENDIAN"]) -elif sys.byteorder == "big": - env.Append(CPPDEFINES=["PA_BIG_ENDIAN"]) -else: - raise ConfigurationError("unknown byte order: %s" % sys.byteorder) -""" -if env["enableDebugOutput"]: - env.Append(CPPDEFINES=["PA_ENABLE_DEBUG_OUTPUT"]) - -# Start configuration - -# Use an absolute path for conf_dir, otherwise it gets created both relative to current directory and build directory -conf = env.Configure(log_file=os.path.join(sconsDir, "sconf.log"), custom_tests={"CheckCTypeSize": CheckCTypeSize}, - conf_dir=os.path.join(sconsDir, ".sconf_temp")) -conf.env.Append(CPPDEFINES=["SIZEOF_SHORT=%d" % conf.CheckCTypeSize("short")]) -conf.env.Append(CPPDEFINES=["SIZEOF_INT=%d" % conf.CheckCTypeSize("int")]) -conf.env.Append(CPPDEFINES=["SIZEOF_LONG=%d" % conf.CheckCTypeSize("long")]) -if checkSymbol(conf, "time.h", "rt", "clock_gettime"): - conf.env.Append(CPPDEFINES=["HAVE_CLOCK_GETTIME"]) -if checkSymbol(conf, "time.h", symbol="nanosleep"): - conf.env.Append(CPPDEFINES=["HAVE_NANOSLEEP"]) -if conf.CheckCHeader("sys/soundcard.h"): - conf.env.Append(CPPDEFINES=["HAVE_SYS_SOUNDCARD_H"]) -if conf.CheckCHeader("linux/soundcard.h"): - conf.env.Append(CPPDEFINES=["HAVE_LINUX_SOUNDCARD_H"]) -if conf.CheckCHeader("machine/soundcard.h"): - conf.env.Append(CPPDEFINES=["HAVE_MACHINE_SOUNDCARD_H"]) - -# Look for needed libraries and link with them -for lib, hdr, sym in neededLibs: - checkSymbol(conf, hdr, lib, sym, critical=True) -# Look for host API libraries, if a library isn't found disable corresponding host API implementation. -for name, val in optionalImpls.items(): - lib, hdr, sym = val - if checkSymbol(conf, hdr, lib, sym, critical=False, pkgName=name.lower()): - conf.env.Append(CPPDEFINES=["PA_USE_%s=1" % name.upper()]) - else: - del optionalImpls[name] - -# Configuration finished -env = conf.Finish() - -# PA infrastructure -CommonSources = [os.path.join("common", f) for f in "pa_allocation.c pa_converters.c pa_cpuload.c pa_dither.c pa_front.c \ - pa_process.c pa_stream.c pa_trace.c pa_debugprint.c pa_ringbuffer.c".split()] -CommonSources.append(os.path.join("hostapi", "skeleton", "pa_hostapi_skeleton.c")) - -# Host APIs implementations -ImplSources = [] -if Platform in Posix: - ImplSources += [os.path.join("os", "unix", f) for f in "pa_unix_hostapis.c pa_unix_util.c".split()] - -if "ALSA" in optionalImpls: - ImplSources.append(os.path.join("hostapi", "alsa", "pa_linux_alsa.c")) -if "JACK" in optionalImpls: - ImplSources.append(os.path.join("hostapi", "jack", "pa_jack.c")) -if "OSS" in optionalImpls: - ImplSources.append(os.path.join("hostapi", "oss", "pa_unix_oss.c")) -if "ASIHPI" in optionalImpls: - ImplSources.append(os.path.join("hostapi", "asihpi", "pa_linux_asihpi.c")) -if "COREAUDIO" in optionalImpls: - ImplSources.append([os.path.join("hostapi", "coreaudio", f) for f in """ - pa_mac_core.c pa_mac_core_blocking.c pa_mac_core_utilities.c - """.split()]) - - -sources = CommonSources + ImplSources - -sharedLibEnv = env.Copy() -if Platform in Posix: - # Add soname to library, this is so a reference is made to the versioned library in programs linking against libportaudio.so - if Platform != 'darwin': - sharedLibEnv.AppendUnique(SHLINKFLAGS="-Wl,-soname=libportaudio.so.%d" % int(ApiVer.split(".")[0])) -sharedLib = sharedLibEnv.SharedLibrary(target="portaudio", source=sources) - -staticLib = env.StaticLibrary(target="portaudio", source=sources) - -if Platform in Posix: - prefix = env["prefix"] - includeDir = os.path.join(prefix, "include") - libDir = os.path.join(prefix, "lib") - -testNames = ["patest_sine", "paqa_devs", "paqa_errs", "patest1", "patest_buffer", "patest_callbackstop", "patest_clip", \ - "patest_dither", "patest_hang", "patest_in_overflow", "patest_latency", "patest_leftright", "patest_longsine", \ - "patest_many", "patest_maxsines", "patest_multi_sine", "patest_out_underflow", "patest_pink", "patest_prime", \ - "patest_read_record", "patest_record", "patest_ringmix", "patest_saw", "patest_sine8", "patest_sine", \ - "patest_sine_time", "patest_start_stop", "patest_stop", "patest_sync", "patest_toomanysines", \ - "patest_underflow", "patest_wire", "patest_write_sine", "pa_devs", "pa_fuzz", "pa_minlat", \ - "patest_sine_channelmaps",] - -# The test directory ("bin") should be in the top-level PA directory -tests = [env.Program(target=os.path.join("#", "bin", name), source=[os.path.join("#", "test", name + ".c"), - staticLib]) for name in testNames] - -# Detect host APIs -hostApis = [] -for cppdef in env["CPPDEFINES"]: - if cppdef.startswith("PA_USE_"): - hostApis.append(cppdef[7:-2]) - -Return("sources", "sharedLib", "staticLib", "tests", "env", "hostApis") +import os.path, copy, sys + +def checkSymbol(conf, header, library=None, symbol=None, autoAdd=True, critical=False, pkgName=None): + """ Check for symbol in library, optionally look only for header. + @param conf: Configure instance. + @param header: The header file where the symbol is declared. + @param library: The library in which the symbol exists, if None it is taken to be the standard C library. + @param symbol: The symbol to look for, if None only the header will be looked up. + @param autoAdd: Automatically link with this library if check is positive. + @param critical: Raise on error? + @param pkgName: Optional name of pkg-config entry for library, to determine build parameters. + @return: True/False + """ + origEnv = conf.env.Copy() # Copy unmodified environment so we can restore it upon error + env = conf.env + if library is None: + library = "c" # Standard library + autoAdd = False + + if pkgName is not None: + origLibs = copy.copy(env.get("LIBS", None)) + + try: env.ParseConfig("pkg-config --silence-errors %s --cflags --libs" % pkgName) + except: pass + else: + # I see no other way of checking that the parsing succeeded, if it did add no more linking parameters + if env.get("LIBS", None) != origLibs: + autoAdd = False + + try: + if not conf.CheckCHeader(header, include_quotes="<>"): + raise ConfigurationError("missing header %s" % header) + if symbol is not None and not conf.CheckLib(library, symbol, language="C", autoadd=autoAdd): + raise ConfigurationError("missing symbol %s in library %s" % (symbol, library)) + except ConfigurationError: + conf.env = origEnv + if not critical: + return False + raise + + return True + +import SCons.Errors + +# Import common variables + +# Could use '#' to refer to top-level SConstruct directory, but looks like env.SConsignFile doesn't interpret this at least :( +sconsDir = os.path.abspath(os.path.join("build", "scons")) + +try: + Import("Platform", "Posix", "ConfigurationError", "ApiVer") +except SCons.Errors.UserError: + # The common objects must be exported first + SConscript(os.path.join(sconsDir, "SConscript_common")) + Import("Platform", "Posix", "ConfigurationError", "ApiVer") + +Import("env") + +# This will be manipulated +env = env.Copy() + +# We operate with a set of needed libraries and optional libraries, the latter stemming from host API implementations. +# For libraries of both types we record a set of values that is used to look for the library in question, during +# configuration. If the corresponding library for a host API implementation isn't found, the implementation is left out. +neededLibs = [] +optionalImpls = {} +if Platform in Posix: + env.Append(CPPPATH=os.path.join("os", "unix")) + neededLibs += [("pthread", "pthread.h", "pthread_create"), ("m", "math.h", "sin")] + if env["useALSA"]: + optionalImpls["ALSA"] = ("asound", "alsa/asoundlib.h", "snd_pcm_open") + if env["useJACK"]: + optionalImpls["JACK"] = ("jack", "jack/jack.h", "jack_client_new") + if env["useOSS"]: + # TODO: It looks like the prefix for soundcard.h depends on the platform + optionalImpls["OSS"] = ("oss", "sys/soundcard.h", None) + if Platform == 'netbsd': + optionalImpls["OSS"] = ("ossaudio", "sys/soundcard.h", "_oss_ioctl") + if env["useASIHPI"]: + optionalImpls["ASIHPI"] = ("hpi", "asihpi/hpi.h", "HPI_SubSysCreate") + if env["useCOREAUDIO"]: + optionalImpls["COREAUDIO"] = ("CoreAudio", "CoreAudio/CoreAudio.h", None) +else: + raise ConfigurationError("unknown platform %s" % Platform) + +if Platform == "darwin": + env.Append(LINKFLAGS="-framework CoreFoundation -framework CoreServices -framework CoreAudio -framework AudioToolBox -framework AudioUnit") +elif Platform == "cygwin": + env.Append(LIBS=["winmm"]) +elif Platform == "irix": + neededLibs += [("audio", "dmedia/audio.h", "alOpenPort"), ("dmedia", "dmedia/dmedia.h", "dmGetUST")] + env.Append(CPPDEFINES=["PA_USE_SGI"]) + +def CheckCTypeSize(context, tp): + """ Check size of C type. + @param context: A configuration context. + @param tp: The type to check. + @return: Size of type, in bytes. + """ + context.Message("Checking the size of C type %s..." % tp) + ret = context.TryRun(""" +#include + +int main() { + printf("%%d", sizeof(%s)); + return 0; +} +""" % tp, ".c") + if not ret[0]: + context.Result(" Couldn't obtain size of type %s!" % tp) + return None + + assert ret[1] + sz = int(ret[1]) + context.Result("%d" % sz) + return sz + +""" +if sys.byteorder == "little": + env.Append(CPPDEFINES=["PA_LITTLE_ENDIAN"]) +elif sys.byteorder == "big": + env.Append(CPPDEFINES=["PA_BIG_ENDIAN"]) +else: + raise ConfigurationError("unknown byte order: %s" % sys.byteorder) +""" +if env["enableDebugOutput"]: + env.Append(CPPDEFINES=["PA_ENABLE_DEBUG_OUTPUT"]) + +# Start configuration + +# Use an absolute path for conf_dir, otherwise it gets created both relative to current directory and build directory +conf = env.Configure(log_file=os.path.join(sconsDir, "sconf.log"), custom_tests={"CheckCTypeSize": CheckCTypeSize}, + conf_dir=os.path.join(sconsDir, ".sconf_temp")) +conf.env.Append(CPPDEFINES=["SIZEOF_SHORT=%d" % conf.CheckCTypeSize("short")]) +conf.env.Append(CPPDEFINES=["SIZEOF_INT=%d" % conf.CheckCTypeSize("int")]) +conf.env.Append(CPPDEFINES=["SIZEOF_LONG=%d" % conf.CheckCTypeSize("long")]) +if checkSymbol(conf, "time.h", "rt", "clock_gettime"): + conf.env.Append(CPPDEFINES=["HAVE_CLOCK_GETTIME"]) +if checkSymbol(conf, "time.h", symbol="nanosleep"): + conf.env.Append(CPPDEFINES=["HAVE_NANOSLEEP"]) +if conf.CheckCHeader("sys/soundcard.h"): + conf.env.Append(CPPDEFINES=["HAVE_SYS_SOUNDCARD_H"]) +if conf.CheckCHeader("linux/soundcard.h"): + conf.env.Append(CPPDEFINES=["HAVE_LINUX_SOUNDCARD_H"]) +if conf.CheckCHeader("machine/soundcard.h"): + conf.env.Append(CPPDEFINES=["HAVE_MACHINE_SOUNDCARD_H"]) + +# Look for needed libraries and link with them +for lib, hdr, sym in neededLibs: + checkSymbol(conf, hdr, lib, sym, critical=True) +# Look for host API libraries, if a library isn't found disable corresponding host API implementation. +for name, val in optionalImpls.items(): + lib, hdr, sym = val + if checkSymbol(conf, hdr, lib, sym, critical=False, pkgName=name.lower()): + conf.env.Append(CPPDEFINES=["PA_USE_%s=1" % name.upper()]) + else: + del optionalImpls[name] + +# Configuration finished +env = conf.Finish() + +# PA infrastructure +CommonSources = [os.path.join("common", f) for f in "pa_allocation.c pa_converters.c pa_cpuload.c pa_dither.c pa_front.c \ + pa_process.c pa_stream.c pa_trace.c pa_debugprint.c pa_ringbuffer.c".split()] +CommonSources.append(os.path.join("hostapi", "skeleton", "pa_hostapi_skeleton.c")) + +# Host APIs implementations +ImplSources = [] +if Platform in Posix: + ImplSources += [os.path.join("os", "unix", f) for f in "pa_unix_hostapis.c pa_unix_util.c".split()] + +if "ALSA" in optionalImpls: + ImplSources.append(os.path.join("hostapi", "alsa", "pa_linux_alsa.c")) +if "JACK" in optionalImpls: + ImplSources.append(os.path.join("hostapi", "jack", "pa_jack.c")) +if "OSS" in optionalImpls: + ImplSources.append(os.path.join("hostapi", "oss", "pa_unix_oss.c")) +if "ASIHPI" in optionalImpls: + ImplSources.append(os.path.join("hostapi", "asihpi", "pa_linux_asihpi.c")) +if "COREAUDIO" in optionalImpls: + ImplSources.append([os.path.join("hostapi", "coreaudio", f) for f in """ + pa_mac_core.c pa_mac_core_blocking.c pa_mac_core_utilities.c + """.split()]) + + +sources = CommonSources + ImplSources + +sharedLibEnv = env.Copy() +if Platform in Posix: + # Add soname to library, this is so a reference is made to the versioned library in programs linking against libportaudio.so + if Platform != 'darwin': + sharedLibEnv.AppendUnique(SHLINKFLAGS="-Wl,-soname=libportaudio.so.%d" % int(ApiVer.split(".")[0])) +sharedLib = sharedLibEnv.SharedLibrary(target="portaudio", source=sources) + +staticLib = env.StaticLibrary(target="portaudio", source=sources) + +if Platform in Posix: + prefix = env["prefix"] + includeDir = os.path.join(prefix, "include") + libDir = os.path.join(prefix, "lib") + +testNames = ["patest_sine", "paqa_devs", "paqa_errs", "patest1", "patest_buffer", "patest_callbackstop", "patest_clip", \ + "patest_dither", "patest_hang", "patest_in_overflow", "patest_latency", "patest_leftright", "patest_longsine", \ + "patest_many", "patest_maxsines", "patest_multi_sine", "patest_out_underflow", "patest_pink", "patest_prime", \ + "patest_read_record", "patest_record", "patest_ringmix", "patest_saw", "patest_sine8", "patest_sine", \ + "patest_sine_time", "patest_start_stop", "patest_stop", "patest_sync", "patest_toomanysines", \ + "patest_underflow", "patest_wire", "patest_write_sine", "pa_devs", "pa_fuzz", "pa_minlat", \ + "patest_sine_channelmaps",] + +# The test directory ("bin") should be in the top-level PA directory +tests = [env.Program(target=os.path.join("#", "bin", name), source=[os.path.join("#", "test", name + ".c"), + staticLib]) for name in testNames] + +# Detect host APIs +hostApis = [] +for cppdef in env["CPPDEFINES"]: + if cppdef.startswith("PA_USE_"): + hostApis.append(cppdef[7:-2]) + +Return("sources", "sharedLib", "staticLib", "tests", "env", "hostApis") diff --git a/3rdparty/portaudio/src/common/pa_trace.c b/3rdparty/portaudio/src/common/pa_trace.c index 24305003f7..bf1ad443ec 100644 --- a/3rdparty/portaudio/src/common/pa_trace.c +++ b/3rdparty/portaudio/src/common/pa_trace.c @@ -1,5 +1,5 @@ /* - * $Id: pa_trace.c 1339 2008-02-15 07:50:33Z rossb $ + * $Id: pa_trace.c 1812 2012-02-14 09:32:57Z robiwan $ * Portable Audio I/O Library Trace Facility * Store trace information in real-time for later printing. * @@ -46,12 +46,16 @@ #include #include +#include #include +#include #include "pa_trace.h" +#include "pa_util.h" +#include "pa_debugprint.h" #if PA_TRACE_REALTIME_EVENTS -static char *traceTextArray[PA_MAX_TRACE_RECORDS]; +static char const *traceTextArray[PA_MAX_TRACE_RECORDS]; static int traceIntArray[PA_MAX_TRACE_RECORDS]; static int traceIndex = 0; static int traceBlock = 0; @@ -94,4 +98,133 @@ void PaUtil_AddTraceMessage( const char *msg, int data ) } } +/************************************************************************/ +/* High performance log alternative */ +/************************************************************************/ + +typedef unsigned long long PaUint64; + +typedef struct __PaHighPerformanceLog +{ + unsigned magik; + int writePtr; + int readPtr; + int size; + double refTime; + char* data; +} PaHighPerformanceLog; + +static const unsigned kMagik = 0xcafebabe; + +#define USEC_PER_SEC (1000000ULL) + +int PaUtil_InitializeHighSpeedLog( LogHandle* phLog, unsigned maxSizeInBytes ) +{ + PaHighPerformanceLog* pLog = (PaHighPerformanceLog*)PaUtil_AllocateMemory(sizeof(PaHighPerformanceLog)); + if (pLog == 0) + { + return paInsufficientMemory; + } + assert(phLog != 0); + *phLog = pLog; + + pLog->data = (char*)PaUtil_AllocateMemory(maxSizeInBytes); + if (pLog->data == 0) + { + PaUtil_FreeMemory(pLog); + return paInsufficientMemory; + } + pLog->magik = kMagik; + pLog->size = maxSizeInBytes; + pLog->refTime = PaUtil_GetTime(); + return paNoError; +} + +void PaUtil_ResetHighSpeedLogTimeRef( LogHandle hLog ) +{ + PaHighPerformanceLog* pLog = (PaHighPerformanceLog*)hLog; + assert(pLog->magik == kMagik); + pLog->refTime = PaUtil_GetTime(); +} + +typedef struct __PaLogEntryHeader +{ + int size; + double timeStamp; +} PaLogEntryHeader; + +#ifdef __APPLE__ +#define _vsnprintf vsnprintf +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + + +int PaUtil_AddHighSpeedLogMessage( LogHandle hLog, const char* fmt, ... ) +{ + va_list l; + int n = 0; + PaHighPerformanceLog* pLog = (PaHighPerformanceLog*)hLog; + if (pLog != 0) + { + PaLogEntryHeader* pHeader; + char* p; + int maxN; + assert(pLog->magik == kMagik); + pHeader = (PaLogEntryHeader*)( pLog->data + pLog->writePtr ); + p = (char*)( pHeader + 1 ); + maxN = pLog->size - pLog->writePtr - 2 * sizeof(PaLogEntryHeader); + + pHeader->timeStamp = PaUtil_GetTime() - pLog->refTime; + if (maxN > 0) + { + if (maxN > 32) + { + va_start(l, fmt); + n = _vsnprintf(p, min(1024, maxN), fmt, l); + va_end(l); + } + else { + n = sprintf(p, "End of log..."); + } + n = ((n + sizeof(unsigned)) & ~(sizeof(unsigned)-1)) + sizeof(PaLogEntryHeader); + pHeader->size = n; +#if 0 + PaUtil_DebugPrint("%05u.%03u: %s\n", pHeader->timeStamp/1000, pHeader->timeStamp%1000, p); +#endif + pLog->writePtr += n; + } + } + return n; +} + +void PaUtil_DumpHighSpeedLog( LogHandle hLog, const char* fileName ) +{ + FILE* f = (fileName != NULL) ? fopen(fileName, "w") : stdout; + unsigned localWritePtr; + PaHighPerformanceLog* pLog = (PaHighPerformanceLog*)hLog; + assert(pLog->magik == kMagik); + localWritePtr = pLog->writePtr; + while (pLog->readPtr != localWritePtr) + { + const PaLogEntryHeader* pHeader = (const PaLogEntryHeader*)( pLog->data + pLog->readPtr ); + const char* p = (const char*)( pHeader + 1 ); + const PaUint64 ts = (const PaUint64)( pHeader->timeStamp * USEC_PER_SEC ); + assert(pHeader->size < (1024+sizeof(unsigned)+sizeof(PaLogEntryHeader))); + fprintf(f, "%05u.%03u: %s\n", (unsigned)(ts/1000), (unsigned)(ts%1000), p); + pLog->readPtr += pHeader->size; + } + if (f != stdout) + { + fclose(f); + } +} + +void PaUtil_DiscardHighSpeedLog( LogHandle hLog ) +{ + PaHighPerformanceLog* pLog = (PaHighPerformanceLog*)hLog; + assert(pLog->magik == kMagik); + PaUtil_FreeMemory(pLog->data); + PaUtil_FreeMemory(pLog); +} + #endif /* TRACE_REALTIME_EVENTS */ diff --git a/3rdparty/portaudio/src/common/pa_trace.h b/3rdparty/portaudio/src/common/pa_trace.h index b11509e0ec..612dbf327d 100644 --- a/3rdparty/portaudio/src/common/pa_trace.h +++ b/3rdparty/portaudio/src/common/pa_trace.h @@ -1,7 +1,7 @@ #ifndef PA_TRACE_H #define PA_TRACE_H /* - * $Id: pa_trace.h 1339 2008-02-15 07:50:33Z rossb $ + * $Id: pa_trace.h 1812 2012-02-14 09:32:57Z robiwan $ * Portable Audio I/O Library Trace Facility * Store trace information in real-time for later printing. * @@ -84,13 +84,29 @@ extern "C" void PaUtil_ResetTraceMessages(); void PaUtil_AddTraceMessage( const char *msg, int data ); void PaUtil_DumpTraceMessages(); - + +/* Alternative interface */ + +typedef void* LogHandle; + +int PaUtil_InitializeHighSpeedLog(LogHandle* phLog, unsigned maxSizeInBytes); +void PaUtil_ResetHighSpeedLogTimeRef(LogHandle hLog); +int PaUtil_AddHighSpeedLogMessage(LogHandle hLog, const char* fmt, ...); +void PaUtil_DumpHighSpeedLog(LogHandle hLog, const char* fileName); +void PaUtil_DiscardHighSpeedLog(LogHandle hLog); + #else #define PaUtil_ResetTraceMessages() /* noop */ #define PaUtil_AddTraceMessage(msg,data) /* noop */ #define PaUtil_DumpTraceMessages() /* noop */ +#define PaUtil_InitializeHighSpeedLog(phLog, maxSizeInBytes) (0) +#define PaUtil_ResetHighSpeedLogTimeRef(hLog) +#define PaUtil_AddHighSpeedLogMessage(...) (0) +#define PaUtil_DumpHighSpeedLog(hLog, fileName) +#define PaUtil_DiscardHighSpeedLog(hLog) + #endif diff --git a/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c b/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c index 4dda3bb62e..796b612f23 100644 --- a/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c +++ b/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c @@ -1,5 +1,5 @@ /* - * $Id: pa_linux_alsa.c 1798 2011-12-08 19:43:29Z alan_horstmann $ + * $Id: pa_linux_alsa.c 1822 2012-03-28 16:14:49Z dmitrykos $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * ALSA implementation by Joshua Haberman and Arve Knudsen @@ -7,6 +7,7 @@ * Copyright (c) 2002 Joshua Haberman * Copyright (c) 2005-2009 Arve Knudsen * Copyright (c) 2008 Kevin Kofler + * Copyright (c) 2011 Dmitry Kostjuchenko * * Based on the Open Source API proposed by Ross Bencina * Copyright (c) 1999-2002 Ross Bencina, Phil Burk @@ -79,10 +80,20 @@ #include "pa_linux_alsa.h" +/* Add missing define (for compatibility with older ALSA versions). */ #ifndef SND_PCM_TSTAMP_ENABLE -#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP + #define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP #endif +/* Combine version elements into a single (unsigned) integer */ +#define ALSA_VERSION_INT(major, minor, subminor) ((major << 16) | (minor << 8) | subminor) + +/* Specifies that hardware audio sample needs byte-swapping into platfom native value representation. */ +#define paSwapEndian ((PaSampleFormat) 0x40000000) /**< @see PaSampleFormat */ + +/* Remove paSwapEndian and paNonInterleaved flags to get pure format value. */ +#define PA_ALSA_TO_FORMAT(X) ((X) & ~(paSwapEndian|paNonInterleaved)) + /* Defines Alsa function types and pointers to these functions. */ #define _PA_DEFINE_FUNC(x) typedef typeof(x) x##_ft; static x##_ft *alsa_##x = 0 @@ -186,6 +197,9 @@ _PA_DEFINE_FUNC(snd_pcm_info_set_subdevice); _PA_DEFINE_FUNC(snd_pcm_info_set_stream); _PA_DEFINE_FUNC(snd_pcm_info_get_name); _PA_DEFINE_FUNC(snd_pcm_info_get_card); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevices_count); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevice_name); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevices_avail); #define alsa_snd_pcm_info_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_info) _PA_DEFINE_FUNC(snd_ctl_pcm_next_device); @@ -219,6 +233,7 @@ _PA_DEFINE_FUNC(snd_pcm_status_get_delay); #define alsa_snd_pcm_status_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_status) _PA_DEFINE_FUNC(snd_card_next); +_PA_DEFINE_FUNC(snd_asoundlib_version); _PA_DEFINE_FUNC(snd_strerror); _PA_DEFINE_FUNC(snd_output_stdio_attach); @@ -244,96 +259,96 @@ static void *g_AlsaLib = NULL; int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_rate(pcm, params, (*val), (*dir))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_rate(pcm, params, (*val), (*dir))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_buffer_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_buffer_size(pcm, params, (*val))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_buffer_size(pcm, params, (*val))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_period_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_period_size(pcm, params, (*val), (*dir))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_period_size(pcm, params, (*val), (*dir))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_min) (const snd_pcm_hw_params_t *params, unsigned int *val) { - (*val) = 1; - return 0; + (*val) = 1; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_max) (const snd_pcm_hw_params_t *params, unsigned int *val) { - (*val) = 2; - return 0; + (*val) = 2; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 2; - return 0; + (*val) = 2; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 8; - return 0; + (*val) = 8; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_min) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) { - (*frames) = 64; - return 0; + (*frames) = 64; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) { - (*frames) = 512; - return 0; + (*frames) = 512; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_buffer_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { - int ret; - int dir = 0; - snd_pcm_uframes_t pmax = 0; - unsigned int pcnt = 0; + int ret; + int dir = 0; + snd_pcm_uframes_t pmax = 0; + unsigned int pcnt = 0; - if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max)(params, &pmax, &dir)) < 0) - return ret; - if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max)(params, &pcnt, &dir)) < 0) - return ret; + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max)(params, &pmax, &dir)) < 0) + return ret; + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max)(params, &pcnt, &dir)) < 0) + return ret; - (*val) = pmax * pcnt; - return 0; + (*val) = pmax * pcnt; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 44100; - return 0; + (*val) = 44100; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 44100; - return 0; + (*val) = 44100; + return 0; } #endif // PA_ALSA_DYNAMIC @@ -345,22 +360,22 @@ static int PaAlsa_LoadLibrary() { #ifdef PA_ALSA_DYNAMIC - PA_DEBUG(( "%s: loading ALSA library file - %s\n", __FUNCTION__, g_AlsaLibName )); + PA_DEBUG(( "%s: loading ALSA library file - %s\n", __FUNCTION__, g_AlsaLibName )); - dlerror(); + dlerror(); g_AlsaLib = dlopen(g_AlsaLibName, (RTLD_NOW|RTLD_GLOBAL)); if (g_AlsaLib == NULL) { - PA_DEBUG(( "%s: failed dlopen() ALSA library file - %s, error: %s\n", __FUNCTION__, g_AlsaLibName, dlerror() )); - return 0; + PA_DEBUG(( "%s: failed dlopen() ALSA library file - %s, error: %s\n", __FUNCTION__, g_AlsaLibName, dlerror() )); + return 0; } PA_DEBUG(( "%s: loading ALSA API\n", __FUNCTION__ )); #define _PA_LOAD_FUNC(x) do { \ - alsa_##x = dlsym(g_AlsaLib, #x); \ - if (alsa_##x == NULL) { \ - PA_DEBUG(( "%s: symbol [%s] not found in - %s, error: %s\n", __FUNCTION__, #x, g_AlsaLibName, dlerror() )); }\ + alsa_##x = dlsym(g_AlsaLib, #x); \ + if (alsa_##x == NULL) { \ + PA_DEBUG(( "%s: symbol [%s] not found in - %s, error: %s\n", __FUNCTION__, #x, g_AlsaLibName, dlerror() )); }\ } while(0) #else @@ -464,6 +479,9 @@ _PA_LOAD_FUNC(snd_pcm_info_set_subdevice); _PA_LOAD_FUNC(snd_pcm_info_set_stream); _PA_LOAD_FUNC(snd_pcm_info_get_name); _PA_LOAD_FUNC(snd_pcm_info_get_card); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevices_count); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevice_name); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevices_avail); _PA_LOAD_FUNC(snd_ctl_pcm_next_device); _PA_LOAD_FUNC(snd_ctl_pcm_info); @@ -494,34 +512,35 @@ _PA_LOAD_FUNC(snd_pcm_status_get_trigger_tstamp); _PA_LOAD_FUNC(snd_pcm_status_get_delay); _PA_LOAD_FUNC(snd_card_next); +_PA_LOAD_FUNC(snd_asoundlib_version); _PA_LOAD_FUNC(snd_strerror); _PA_LOAD_FUNC(snd_output_stdio_attach); #undef _PA_LOAD_FUNC #ifdef PA_ALSA_DYNAMIC - PA_DEBUG(( "%s: loaded ALSA API - ok\n", __FUNCTION__ )); + PA_DEBUG(( "%s: loaded ALSA API - ok\n", __FUNCTION__ )); #define _PA_VALIDATE_LOAD_REPLACEMENT(x)\ - do {\ - if (alsa_##x == NULL)\ - {\ - alsa_##x = &_PA_LOCAL_IMPL(x);\ - PA_DEBUG(( "%s: replacing [%s] with local implementation\n", __FUNCTION__, #x ));\ - }\ - } while (0) + do {\ + if (alsa_##x == NULL)\ + {\ + alsa_##x = &_PA_LOCAL_IMPL(x);\ + PA_DEBUG(( "%s: replacing [%s] with local implementation\n", __FUNCTION__, #x ));\ + }\ + } while (0) - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_rate_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_buffer_size_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_period_size_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_buffer_size_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_rate_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_buffer_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_period_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_buffer_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_max); #undef _PA_LOCAL_IMPL #undef _PA_VALIDATE_LOAD_REPLACEMENT @@ -545,14 +564,14 @@ static void PaAlsa_CloseLibrary() { #ifdef PA_ALSA_DYNAMIC dlclose(g_AlsaLib); - g_AlsaLib = NULL; + g_AlsaLib = NULL; #endif } /* Check return value of ALSA function, and map it to PaError */ #define ENSURE_(expr, code) \ do { \ - int __pa_unsure_error_id;\ + int __pa_unsure_error_id;\ if( UNLIKELY( (__pa_unsure_error_id = (expr)) < 0 ) ) \ { \ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ @@ -570,10 +589,10 @@ static void PaAlsa_CloseLibrary() #define ASSERT_CALL_(expr, success) \ do {\ - int __pa_assert_error_id;\ - __pa_assert_error_id = (expr);\ - assert( success == __pa_assert_error_id );\ - } while (0) + int __pa_assert_error_id;\ + __pa_assert_error_id = (expr);\ + assert( success == __pa_assert_error_id );\ + } while (0) static int numPeriods_ = 4; static int busyRetries_ = 100; @@ -600,6 +619,8 @@ typedef struct void *nonMmapBuffer; unsigned int nonMmapBufferSize; PaDeviceIndex device; /* Keep the device index */ + int deviceIsPlug; /* Distinguish plug types from direct 'hw:' devices */ + int useReventFix; /* Alsa older than 1.0.16, plug devices need a fix */ snd_pcm_t *pcm; snd_pcm_uframes_t bufferSize; @@ -659,6 +680,7 @@ typedef struct PaAlsaHostApiRepresentation PaUtilAllocationGroup *allocations; PaHostApiIndex hostApiIndex; + PaUint32 alsaLibVersion; /* Retrieved from the library at run-time */ } PaAlsaHostApiRepresentation; @@ -699,6 +721,7 @@ static double GetStreamCpuLoad( PaStream* stream ); static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); +static PaUint32 PaAlsaVersionNum(void); /* Callback prototypes */ static void *CallbackThreadFunc( void *userData ); @@ -715,6 +738,9 @@ static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; } +static void CheckAndReplaceConverterForSwapEndian(PaAlsaStream *stream, PaSampleFormat hostInputSampleFormat, + PaSampleFormat hostOutputSampleFormat); + /** Uncommented because AlsaErrorHandler is unused for anything good yet. If AlsaErrorHandler is to be used, do not forget to register this callback in PaAlsa_Initialize, and unregister in Terminate. */ @@ -735,6 +761,7 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); alsaHostApi->hostApiIndex = hostApiIndex; + alsaHostApi->alsaLibVersion = PaAlsaVersionNum(); *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; (*hostApi)->info.structVersion = 1; @@ -944,6 +971,24 @@ static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ) deviceInfo->defaultSampleRate = -1.; } + +/* Retrieve the version of the runtime Alsa-lib, as a single number equivalent to + * SND_LIB_VERSION. Only a version string is available ("a.b.c") so this has to be converted. + * Assume 'a' and 'b' are single digits only. + */ +static PaUint32 PaAlsaVersionNum(void) +{ + char* verStr; + PaUint32 verNum; + + verStr = (char*) alsa_snd_asoundlib_version(); + verNum = ALSA_VERSION_INT( atoi(verStr), atoi(verStr + 2), atoi(verStr + 4) ); + PA_DEBUG(( "ALSA version (build): " SND_LIB_VERSION_STR "\nALSA version (runtime): %s\n", verStr )); + + return verNum; +} + + /* Helper struct */ typedef struct { @@ -982,11 +1027,11 @@ HwDevInfo predefinedNames[] = { { "AndroidPlayback_ExtraDockSpeaker_normal", NULL, 0, 1, 0 }, { "AndroidPlayback_TvOut_normal", NULL, 0, 1, 0 }, - { "AndroidRecord_Microphone", NULL, 0, 0, 1 }, + { "AndroidRecord_Microphone", NULL, 0, 0, 1 }, { "AndroidRecord_Earpiece_normal", NULL, 0, 0, 1 }, - { "AndroidRecord_Speaker_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Speaker_normal", NULL, 0, 0, 1 }, { "AndroidRecord_Headset_normal", NULL, 0, 0, 1 }, - { "AndroidRecord_Bluetooth_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Bluetooth_normal", NULL, 0, 0, 1 }, { "AndroidRecord_Speaker_Headset_normal", NULL, 0, 0, 1 }, { NULL, NULL, 0, 1, 0 } @@ -1061,8 +1106,7 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, ret = alsa_snd_pcm_open( pcmp, name, stream, mode ); if( -EBUSY != ret ) { - PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n", - __FUNCTION__, tries )); + PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n", __FUNCTION__, tries )); } } if( -EBUSY == ret ) @@ -1071,8 +1115,8 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, } else { - if (ret < 0) - PA_DEBUG(( "%s: Opened device '%s' ptr[%p] - result: [%d:%s]\n", __FUNCTION__, name, *pcmp, ret, alsa_snd_strerror(ret) )); + if (ret < 0) + PA_DEBUG(( "%s: Opened device '%s' ptr[%p] - result: [%d:%s]\n", __FUNCTION__, name, *pcmp, ret, alsa_snd_strerror(ret) )); } return ret; @@ -1086,7 +1130,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d snd_pcm_t *pcm = NULL; PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; - PA_DEBUG(( "%s: filling device info for: %s\n", __FUNCTION__, deviceName->name )); + PA_DEBUG(( "%s: Filling device info for: %s\n", __FUNCTION__, deviceName->name )); /* Zero fields */ InitializeDeviceInfo( baseDeviceInfo ); @@ -1096,8 +1140,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d /* Query capture */ if( deviceName->hasCapture && - OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 ) - >= 0 ) + OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 ) >= 0 ) { if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError ) { @@ -1109,8 +1152,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d /* Query playback */ if( deviceName->hasPlayback && - OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 ) - >= 0 ) + OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 ) >= 0 ) { if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError ) { @@ -1121,10 +1163,10 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d } baseDeviceInfo->structVersion = 2; - baseDeviceInfo->hostApi = alsaApi->hostApiIndex; - baseDeviceInfo->name = deviceName->name; - devInfo->alsaName = deviceName->alsaName; - devInfo->isPlug = deviceName->isPlug; + baseDeviceInfo->hostApi = alsaApi->hostApiIndex; + baseDeviceInfo->name = deviceName->name; + devInfo->alsaName = deviceName->alsaName; + devInfo->isPlug = deviceName->isPlug; /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. * Should now be safe to add device info, unless the device supports neither capture nor playback @@ -1132,14 +1174,14 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 ) { /* Make device default if there isn't already one or it is the ALSA "default" device */ - if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxInputChannels > 0 ) + if( (baseApi->info.defaultInputDevice == paNoDevice || + !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxInputChannels > 0 ) { baseApi->info.defaultInputDevice = *devIdx; PA_DEBUG(("Default input device: %s\n", deviceName->name)); } - if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) + if( (baseApi->info.defaultOutputDevice == paNoDevice || + !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) { baseApi->info.defaultOutputDevice = *devIdx; PA_DEBUG(("Default output device: %s\n", deviceName->name)); @@ -1150,7 +1192,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d } else { - PA_DEBUG(( "%s: skipped device: %s, all channels - 0\n", __FUNCTION__, deviceName->name )); + PA_DEBUG(( "%s: Skipped device: %s, all channels == 0\n", __FUNCTION__, deviceName->name )); } end: @@ -1171,7 +1213,6 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) snd_pcm_info_t *pcmInfo; int res; int blocking = SND_PCM_NONBLOCK; - char alsaCardName[50]; #ifdef PA_ENABLE_DEBUG_OUTPUT PaTime startTime = PaUtil_GetTime(); #endif @@ -1196,87 +1237,130 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) alsa_snd_pcm_info_alloca( &pcmInfo ); while( alsa_snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) { + char alsaCardNameId[64] = { 0 }; char *cardName; int devIdx = -1; snd_ctl_t *ctl; - char buf[50]; - snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); + /* Make card name */ + snprintf( alsaCardNameId, sizeof (alsaCardNameId)-1, "hw:%d", cardIdx ); - /* Acquire name of card */ - if( alsa_snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) + /* Try opening card */ + if( alsa_snd_ctl_open( &ctl, alsaCardNameId, 0 ) < 0 ) { /* Unable to open card :( */ - PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardName )); + PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardNameId )); continue; } alsa_snd_ctl_card_info( ctl, cardInfo ); PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, alsa_snd_ctl_card_info_get_name( cardInfo )) ); + PA_DEBUG(( "%s: Open card: id[%s] name[%s]\n", __FUNCTION__, alsaCardNameId, cardName )); + + /* Iterate devices */ while( alsa_snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) { char *alsaDeviceName, *deviceName; size_t len; int hasPlayback = 0, hasCapture = 0; - snprintf( buf, sizeof (buf), "hw:%d,%d", cardIdx, devIdx ); + int subDevIdx, subDevCount = 1; - /* Obtain info about this particular device */ + PA_DEBUG(( "%s: - idx = %d:\n", __FUNCTION__, devIdx )); + + /* Make this device current */ alsa_snd_pcm_info_set_device( pcmInfo, devIdx ); - alsa_snd_pcm_info_set_subdevice( pcmInfo, 0 ); - alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + + /* Iterate sub-devices */ + for( subDevIdx = 0; subDevIdx < subDevCount; ++subDevIdx ) { - hasCapture = 1; + char buf[64] = { 0 }; + + /* Make this sub-device current */ + alsa_snd_pcm_info_set_subdevice( pcmInfo, subDevIdx ); + + /* Test for Capture capability */ + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + { + hasCapture = 1; + } + + /* Test for Playback capability */ + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + { + hasPlayback = 1; + } + + /* If neither supported, such device is stub or failed */ + if( !hasPlayback && !hasCapture ) + { + continue; + } + + /* Get sub-device count */ + if( subDevIdx == 0 ) + { + subDevCount = alsa_snd_pcm_info_get_subdevices_count( pcmInfo ); + PA_DEBUG(( "%s: sub-devices: %d/%d\n", __FUNCTION__, alsa_snd_pcm_info_get_subdevices_avail( pcmInfo ), subDevCount)); + } + + PA_DEBUG(( "%s: - sub: %d\n", __FUNCTION__, subDevIdx )); + if( hasCapture ) PA_DEBUG(( "%s: - cap: CAPTURE\n", __FUNCTION__ )); + if( hasPlayback ) PA_DEBUG(( "%s: - cap: PLAYBACK\n", __FUNCTION__ )); + + /* Make name Id */ + snprintf( buf, sizeof(buf)-1, ( subDevCount > 1 ? "hw:%d,%d,%d" : "hw:%d,%d" ), cardIdx, devIdx, subDevIdx ); + + /* Make name */ + if( subDevCount <= 1 ) + { + const char *snd_deviceName = alsa_snd_pcm_info_get_name( pcmInfo ); + + len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_deviceName, buf ) + 1; + PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); + snprintf( deviceName, len, "%s: %s (%s)", cardName, snd_deviceName, buf ); + } + else + { + const char *snd_deviceName = alsa_snd_pcm_info_get_name( pcmInfo ); + const char *snd_subDeviceName = alsa_snd_pcm_info_get_subdevice_name( pcmInfo ); + + len = snprintf( NULL, 0, "%s: %s (%s) {%s}", cardName, snd_deviceName, buf, snd_subDeviceName ) + 1; + PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); + snprintf( deviceName, len, "%s: %s (%s) {%s}", cardName, snd_deviceName, buf, snd_subDeviceName ); + } + + ++numDeviceNames; + if( !hwDevInfos || numDeviceNames > maxDeviceNames ) + { + maxDeviceNames *= 2; + PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), paInsufficientMemory ); + } + + PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); + + hwDevInfos[ numDeviceNames - 1 ].alsaName = alsaDeviceName; + hwDevInfos[ numDeviceNames - 1 ].name = deviceName; + hwDevInfos[ numDeviceNames - 1 ].isPlug = 0; + hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback; + hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture; + + PA_DEBUG(( "%s: Registered device: id[%s] name[%s]\n", __FUNCTION__, alsaDeviceName, deviceName )); } - - alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - { - hasPlayback = 1; - } - - if( !hasPlayback && !hasCapture ) - { - /* Error */ - continue; - } - - /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, alsa_snd_pcm_info_get_name( pcmInfo ), buf ) + 1; - PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - snprintf( deviceName, len, "%s: %s (%s)", cardName, - alsa_snd_pcm_info_get_name( pcmInfo ), buf ); - - ++numDeviceNames; - if( !hwDevInfos || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), - paInsufficientMemory ); - } - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); - - hwDevInfos[ numDeviceNames - 1 ].alsaName = alsaDeviceName; - hwDevInfos[ numDeviceNames - 1 ].name = deviceName; - hwDevInfos[ numDeviceNames - 1 ].isPlug = 0; - hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback; - hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture; } alsa_snd_ctl_close( ctl ); } /* Iterate over plugin devices */ - if( NULL == (*alsa_snd_config) ) { /* alsa_snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ ENSURE_( alsa_snd_config_update(), paUnanticipatedHostError ); PA_DEBUG(( "Updating snd_config\n" )); } - assert( *alsa_snd_config ); + assert( *alsa_snd_config ); if( (res = alsa_snd_config_search( *alsa_snd_config, "pcm", &topNode )) >= 0 ) { snd_config_iterator_t i, next; @@ -1309,36 +1393,34 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } PA_DEBUG(( "%s: Found plugin [%s] of type [%s]\n", __FUNCTION__, idStr, tpStr )); - PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 6 ), paInsufficientMemory ); + PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 6 ), paInsufficientMemory ); strcpy( alsaDeviceName, idStr ); - PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 1 ), paInsufficientMemory ); + + PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 1 ), paInsufficientMemory ); strcpy( deviceName, idStr ); ++numDeviceNames; if( !hwDevInfos || numDeviceNames > maxDeviceNames ) { maxDeviceNames *= 2; - PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), - paInsufficientMemory ); + PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), paInsufficientMemory ); } predefined = FindDeviceName( alsaDeviceName ); hwDevInfos[numDeviceNames - 1].alsaName = alsaDeviceName; - hwDevInfos[numDeviceNames - 1].name = deviceName; - hwDevInfos[numDeviceNames - 1].isPlug = 1; + hwDevInfos[numDeviceNames - 1].name = deviceName; + hwDevInfos[numDeviceNames - 1].isPlug = 1; if( predefined ) { hwDevInfos[numDeviceNames - 1].hasPlayback = predefined->hasPlayback; - hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture; + hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture; } else { hwDevInfos[numDeviceNames - 1].hasPlayback = 1; - hwDevInfos[numDeviceNames - 1].hasCapture = 1; + hwDevInfos[numDeviceNames - 1].hasCapture = 1; } } } @@ -1346,12 +1428,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, alsa_snd_strerror( res ) )); /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); + PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); /* allocate all device info structs in a contiguous block */ - PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); + PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); /* Loop over list of cards, filling in info. If a device is deemed unavailable (can't get name), * it's ignored. @@ -1361,7 +1441,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) * (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes * for this. */ - PA_DEBUG(( "%s: filling device info for %d devices\n", __FUNCTION__, numDeviceNames )); + PA_DEBUG(( "%s: Filling device info for %d devices\n", __FUNCTION__, numDeviceNames )); for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) { PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i]; @@ -1384,8 +1464,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) continue; } - PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, - &devIdx ) ); + PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) ); } free( hwDevInfos ); @@ -1449,32 +1528,58 @@ static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) alsa_snd_pcm_hw_params_any( pcm, hwParams ); - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0 ) available |= paFloat32; - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0 ) available |= paInt32; -#ifdef PA_LITTLE_ENDIAN - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0 ) available |= paInt24; -#elif defined PA_BIG_ENDIAN - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) + else + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0 ) available |= paInt24; -#endif - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0 ) available |= paInt16; - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0 ) available |= paUInt8; - if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0 ) available |= paInt8; return available; } +/* Check if format is available in native endianness, if not apply paSwapEndian flag for futher + processing by Alsa swapping converters. +*/ +static void CheckAndApplyEndianSwapToFormat( PaSampleFormat *format, snd_pcm_t *pcm ) +{ + snd_pcm_hw_params_t *hwParams; + alsa_snd_pcm_hw_params_alloca( &hwParams ); + + alsa_snd_pcm_hw_params_any( pcm, hwParams ); + + if( *format & paInt24 ) + { +#ifdef PA_LITTLE_ENDIAN + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) < 0 ) + { + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0 ) + *format |= paSwapEndian; + } +#elif defined PA_BIG_ENDIAN + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) < 0 ) + { + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0 ) + *format |= paSwapEndian; + } +#endif + } +} + /* Output to console all formats supported by device */ static void LogAllAvailableFormats( snd_pcm_t *pcm ) { @@ -1484,7 +1589,7 @@ static void LogAllAvailableFormats( snd_pcm_t *pcm ) alsa_snd_pcm_hw_params_any( pcm, hwParams ); - PA_DEBUG(( " --- Supported Formats ---\n" )); + PA_DEBUG(( " --- Supported Formats ---\n" )); if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) PA_DEBUG(( "SND_PCM_FORMAT_S8\n" )); @@ -1599,7 +1704,7 @@ static void LogAllAvailableFormats( snd_pcm_t *pcm ) static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) { - switch( paFormat ) + switch( PA_ALSA_TO_FORMAT(paFormat) ) { case paFloat32: return SND_PCM_FORMAT_FLOAT; @@ -1609,11 +1714,10 @@ static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) case paInt24: #ifdef PA_LITTLE_ENDIAN - return SND_PCM_FORMAT_S24_3LE; + return ( paFormat & paSwapEndian ? SND_PCM_FORMAT_S24_3BE : SND_PCM_FORMAT_S24_3LE ); #elif defined PA_BIG_ENDIAN - return SND_PCM_FORMAT_S24_3BE; + return ( paFormat & paSwapEndian ? SND_PCM_FORMAT_S24_3LE : SND_PCM_FORMAT_S24_3BE ); #endif - case paInt32: return SND_PCM_FORMAT_S32; @@ -1716,7 +1820,10 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const /* See if we can find a best possible match */ availableFormats = GetAvailableFormats( pcm ); - PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); + PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( PA_ALSA_TO_FORMAT(availableFormats), parameters->sampleFormat ) ); + + /* Append endiannes conversion flag */ + CheckAndApplyEndianSwapToFormat( &hostFormat, pcm ); /* Some specific hardware (reported: Audio8 DJ) can fail with assertion during this step. */ ENSURE_( alsa_snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); @@ -1801,9 +1908,11 @@ error: return result; } + static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) { + PaSampleFormat availableFormats; PaError result = paNoError; PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat = paNoError; assert( params->channelCount > 0 ); @@ -1816,19 +1925,30 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device ); self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels : devInfo->minOutputChannels ); + self->deviceIsPlug = devInfo->isPlug; } else { /* We're blissfully unaware of the minimum channelCount */ self->numHostChannels = params->channelCount; + /* Check if device name does not start with hw: to determine if it is a 'plug' device */ + if( strncmp( "hw:", ((PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo)->deviceString, 3 ) != 0 ) + self->deviceIsPlug = 1; /* An Alsa plug device, not a direct hw device */ } + if( self->deviceIsPlug && alsaApi->alsaLibVersion < ALSA_VERSION_INT( 1, 0, 16 ) ) + self->useReventFix = 1; /* Prior to Alsa1.0.16, plug devices may stutter without this fix */ self->device = params->device; PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) ); self->nfds = alsa_snd_pcm_poll_descriptors_count( self->pcm ); - PA_ENSURE( hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ) ); + /* Get host sample format */ + availableFormats = GetAvailableFormats( self->pcm ); + PA_ENSURE( hostSampleFormat = PaUtil_SelectClosestAvailableFormat( PA_ALSA_TO_FORMAT(availableFormats), userSampleFormat ) ); + + /* Append endiannes conversion flag */ + CheckAndApplyEndianSwapToFormat( &hostSampleFormat, self->pcm ); self->hostSampleFormat = hostSampleFormat; self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); @@ -1850,7 +1970,7 @@ error: /* Log all available formats. */ if ( hostSampleFormat == paSampleFormatNotSupported ) - { + { LogAllAvailableFormats( self->pcm ); PA_DEBUG(( "%s: Please provide the log output to PortAudio developers, your hardware does not have any sample format implemented yet.\n", __FUNCTION__ )); } @@ -2014,12 +2134,12 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel } if (alsa_snd_pcm_hw_params_get_buffer_size != NULL) { - ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); - } - else - { - self->bufferSize = bufSz; - } + ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); + } + else + { + self->bufferSize = bufSz; + } /* Latency in seconds */ *latency = self->bufferSize / sampleRate; @@ -2145,7 +2265,7 @@ static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frame */ static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align) { - return ((v - (align ? v % align : 0))); + return ((v - (align ? v % align : 0))); } /** Align value in forward direction. @@ -2155,8 +2275,8 @@ static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align) */ static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align) { - unsigned long remainder = (align ? (v % align) : 0); - return (remainder != 0 ? v + (align - remainder) : v); + unsigned long remainder = (align ? (v % align) : 0); + return (remainder != 0 ? v + (align - remainder) : v); } /** Get size of host buffer maintained from the number of user frames, sample rate and suggested latency. Minimum double buffering @@ -2168,8 +2288,8 @@ static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align) */ static unsigned long PaAlsa_GetFramesPerHostBuffer(unsigned long userFramesPerBuffer, PaTime suggestedLatency, double sampleRate) { - unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) ); - return frames; + unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) ); + return frames; } /** Determine size per host buffer. @@ -2239,7 +2359,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo #endif - { + { unsigned numPeriods = numPeriods_, maxPeriods = 0, minPeriods = numPeriods_; /* It may be that the device only supports 2 periods for instance */ @@ -2356,7 +2476,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo framesPerHostBuffer = 2048; #endif PA_DEBUG(( "%s: suggested host buffer period = %lu \n", __FUNCTION__, framesPerHostBuffer )); - } + } { /* Get min/max period sizes and adjust our chosen */ @@ -2377,10 +2497,10 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo framesPerHostBuffer = ((minmax_diff == 2) ? max - 1 : max); } - PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min )); - PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max )); - PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer )); - PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) )); + PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min )); + PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max )); + PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer )); + PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) )); /* Try setting period size */ dir = 0; @@ -2764,11 +2884,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hostOutputSampleFormat = stream->playback.hostSampleFormat | (!stream->playback.hostInterleaved ? paNonInterleaved : 0); PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - numInputChannels, inputSampleFormat, hostInputSampleFormat, - numOutputChannels, outputSampleFormat, hostOutputSampleFormat, + numInputChannels, inputSampleFormat, (hostInputSampleFormat & ~paSwapEndian), + numOutputChannels, outputSampleFormat, (hostOutputSampleFormat & ~paSwapEndian), sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer, hostBufferSizeMode, callback, userData ) ); + /* Some drivers may work only in Big-Endian format (like Audio4DJ), check it and replace + original converter with our specific with swapping capability. + */ + CheckAndReplaceConverterForSwapEndian(stream, hostInputSampleFormat, hostOutputSampleFormat); + /* Ok, buffer processor is initialized, now we can deduce it's latency */ if( numInputChannels > 0 ) stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)( @@ -3607,6 +3732,16 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st *shouldPoll = 0; } + else /* (A zero revent occurred) */ + /* Work around an issue with Alsa older than 1.0.16 using some plugins (eg default with plug + dmix) where + * POLLIN or POLLOUT are zeroed by Alsa-lib if _mmap_avail() is a few frames short of avail_min at period + * boundary, possibly due to erratic dma interrupts at period boundary? Treat as a valid event. + */ + if( self->useReventFix ) + { + self->ready = 1; + *shouldPoll = 0; + } error: return result; @@ -3769,8 +3904,8 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr /* not else ! */ if (timeouts >= 2048) /* audio device not working, shall return error to notify waiters */ { - *framesAvail = 0; /* no frames available for processing */ - xrun = 1; /* try recovering device */ + *framesAvail = 0; /* no frames available for processing */ + xrun = 1; /* try recovering device */ PA_DEBUG(( "%s: poll timed out\n", __FUNCTION__, timeouts )); goto end;/*PA_ENSURE( paTimedOut );*/ @@ -4148,7 +4283,7 @@ static void *CallbackThreadFunc( void *userData ) int xrun = 0; #ifdef PTHREAD_CANCELED - pthread_testcancel(); + pthread_testcancel(); #endif /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively @@ -4550,3 +4685,648 @@ PaError PaAlsa_SetRetriesBusy( int retries ) busyRetries_ = retries; return paNoError; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* CONVERTERS (Swapping SRC/DST) */ +/* -------------------------------------------------------------------------- */ + +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } + +static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */ + +/* -------------------------------------------------------------------------- */ + +static void Copy_24_To_24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + dest[2] = src[0]; + dest[1] = src[1]; + dest[0] = src[2]; + + src += sourceStride * 3; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameters */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = 0; + dest[0] = (unsigned char)(*src - 128); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(*src - 128); + dest[1] = 0; + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = 0; + dest[0] = (*src); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (*src); + dest[1] = 0; + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaInt16 *src = (PaInt16*) sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt16 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + temp = *src; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = (unsigned char)(temp); + dest[0] = (unsigned char)(temp >> 8); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp); + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaInt32 *src = (PaInt32*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW */ +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(*src >> 8); + dest[1] = (unsigned char)(*src >> 16); + dest[0] = (unsigned char)(*src >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(*src >> 24); + dest[1] = (unsigned char)(*src >> 16); + dest[0] = (unsigned char)(*src >> 8); +#endif + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = (double)(*src) * 2147483647.0; + temp = (PaInt32) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + + temp = (PaInt32) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + temp = (PaInt32) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + + temp = (PaInt32) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Float32_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + float *dest = (float*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + *dest = (float) ((double)temp * const_1_div_2147483648_); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int32_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt32 *dest = (PaInt32*) destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt16 *dest = (PaInt16*)destinationBuffer; + + PaInt16 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[2] is discarded */ + temp = (((PaInt16)src[1])); + temp = temp | (PaInt16)(((PaInt16)src[0]) << 8); +#elif defined(PA_BIG_ENDIAN) + /* src[0] is discarded */ + temp = (PaInt16)(((PaInt16)src[2]) << 8); + temp = temp | (((PaInt16)src[1])); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt16 *dest = (PaInt16*)destinationBuffer; + + PaInt32 temp, dither; + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (PaInt16) (((temp >> 1) + dither) >> 15); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = src[0]; +#elif defined(PA_BIG_ENDIAN) + /* src[2] is discarded */ + /* src[1] is discarded */ + *dest = src[2]; +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + PaInt32 temp, dither; + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (signed char) (((temp >> 1) + dither) >> 23); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_UInt8_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = (unsigned char)(src[0] + 128); +#elif defined(PA_BIG_ENDIAN) + *dest = (unsigned char)(src[2] + 128); + /* src[1] is discarded */ + /* src[2] is discarded */ +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void X_To_X_Stub( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ + + assert(0 && "input/output format needs additional swapping converter"); +} + +/* -------------------------------------------------------------------------- */ + +static PaUtilConverterTable paAlsaSwapConverters = { + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ + + Float32_To_Int24_Swap, /* PaUtilConverter *Float32_To_Int24; */ + Float32_To_Int24_Dither_Swap, /* PaUtilConverter *Float32_To_Int24_Dither; */ + Float32_To_Int24_Clip_Swap, /* PaUtilConverter *Float32_To_Int24_Clip; */ + Float32_To_Int24_DitherClip_Swap, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Int32_To_Float32; */ + Int32_To_Int24_Swap, /* PaUtilConverter *Int32_To_Int24; */ + X_To_X_Stub,/*TO-DO*//* PaUtilConverter *Int32_To_Int24_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int16_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_UInt8_Dither; */ + + Int24_To_Float32_Swap, /* PaUtilConverter *Int24_To_Float32; */ + Int24_To_Int32_Swap, /* PaUtilConverter *Int24_To_Int32; */ + Int24_To_Int16_Swap, /* PaUtilConverter *Int24_To_Int16; */ + Int24_To_Int16_Dither_Swap, /* PaUtilConverter *Int24_To_Int16_Dither; */ + Int24_To_Int8_Swap, /* PaUtilConverter *Int24_To_Int8; */ + Int24_To_Int8_Dither_Swap, /* PaUtilConverter *Int24_To_Int8_Dither; */ + Int24_To_UInt8_Swap, /* PaUtilConverter *Int24_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int24_To_UInt8_Dither; */ + + X_To_X_Stub, /* PaUtilConverter *Int16_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int32; */ + Int16_To_Int24_Swap, /* PaUtilConverter *Int16_To_Int24; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_UInt8_Dither; */ + + X_To_X_Stub, /* PaUtilConverter *Int8_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_Int32; */ + Int8_To_Int24_Swap, /* PaUtilConverter *Int8_To_Int24 */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_UInt8; */ + + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int32; */ + UInt8_To_Int24_Swap, /* PaUtilConverter *UInt8_To_Int24; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int8; */ + + X_To_X_Stub, /* PaUtilConverter *Copy_8_To_8; */ + X_To_X_Stub, /* PaUtilConverter *Copy_16_To_16; */ + Copy_24_To_24_Swap, /* PaUtilConverter *Copy_24_To_24; */ + X_To_X_Stub /* PaUtilConverter *Copy_32_To_32; */ +}; +void CheckAndReplaceConverterForSwapEndian(PaAlsaStream *stream, PaSampleFormat hostInputSampleFormat, + PaSampleFormat hostOutputSampleFormat) +{ + PaUtilConverter **cv_org = (PaUtilConverter **)&paConverters, + **cv_swp = (PaUtilConverter **)&paAlsaSwapConverters; + + int i, i_max = sizeof(PaUtilConverterTable)/sizeof(PaUtilConverter *); + + if (hostInputSampleFormat & paSwapEndian) + { + for (i = 0; i < i_max; ++i) + { + if (cv_org[i] == stream->bufferProcessor.inputConverter) + { + stream->bufferProcessor.inputConverter = cv_swp[i]; + break; + } + } + } + + if (hostOutputSampleFormat & paSwapEndian) + { + for (i = 0; i < i_max; ++i) + { + if (cv_org[i] == stream->bufferProcessor.outputConverter) + { + stream->bufferProcessor.outputConverter = cv_swp[i]; + break; + } + } + } +} diff --git a/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp b/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp index 28eee9dbb5..3ecc2170ae 100644 --- a/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp +++ b/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp @@ -1,5 +1,5 @@ /* - * $Id: pa_asio.cpp 1778 2011-11-10 13:59:53Z rossb $ + * $Id: pa_asio.cpp 1825 2012-04-02 07:58:04Z rbencina $ * Portable Audio I/O Library for ASIO Drivers * * Author: Stephane Letz @@ -1031,8 +1031,6 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaAsioHostApiRepresentation *asioHostApi; PaAsioDeviceInfo *deviceInfoArray; char **names; - PaAsioDriverInfo paAsioDriverInfo; - asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) ); if( !asioHostApi ) { @@ -1040,6 +1038,8 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex goto error; } + memset( asioHostApi, 0, sizeof(PaAsioHostApiRepresentation) ); /* ensure all fields are zeroed. especially asioHostApi->allocations */ + /* We initialize COM ourselves here and uninitialize it in Terminate(). This should be the only COM initialization needed in this module. @@ -1142,6 +1142,13 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex for( i=0; i < driverCount; ++i ) { + /* Due to the headless design of the ASIO API, drivers are free to write over data given to them (like M-Audio + drivers f.i.). This is an attempt to overcome that. */ + union _tag_local { + PaAsioDriverInfo info; + char _padding[4096]; + } paAsioDriver; + PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i])); @@ -1176,7 +1183,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex /* Attempt to load the asio driver... */ - if( LoadAsioDriver( asioHostApi, names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError ) + if( LoadAsioDriver( asioHostApi, names[i], &paAsioDriver.info, asioHostApi->systemSpecific ) == paNoError ) { PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo; @@ -1186,15 +1193,15 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex 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)); + PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriver.info.inputChannelCount)); + PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriver.info.outputChannelCount)); + PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", i, paAsioDriver.info.bufferMinSize)); + PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", i, paAsioDriver.info.bufferMaxSize)); + PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriver.info.bufferPreferredSize)); + PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", i, paAsioDriver.info.bufferGranularity)); - deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount; - deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount; + deviceInfo->maxInputChannels = paAsioDriver.info.inputChannelCount; + deviceInfo->maxOutputChannels = paAsioDriver.info.outputChannelCount; deviceInfo->defaultSampleRate = 0.; bool foundDefaultSampleRate = false; @@ -1222,13 +1229,13 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex */ double defaultLowLatency = - paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate; + paAsioDriver.info.bufferPreferredSize / deviceInfo->defaultSampleRate; deviceInfo->defaultLowInputLatency = defaultLowLatency; deviceInfo->defaultLowOutputLatency = defaultLowLatency; double defaultHighLatency = - paAsioDriverInfo.bufferMaxSize / deviceInfo->defaultSampleRate; + paAsioDriver.info.bufferMaxSize / deviceInfo->defaultSampleRate; if( defaultHighLatency < defaultLowLatency ) defaultHighLatency = defaultLowLatency; /* just in case the driver returns something strange */ @@ -1249,10 +1256,10 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex 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->minBufferSize = paAsioDriver.info.bufferMinSize; + asioDeviceInfo->maxBufferSize = paAsioDriver.info.bufferMaxSize; + asioDeviceInfo->preferredBufferSize = paAsioDriver.info.bufferPreferredSize; + asioDeviceInfo->bufferGranularity = paAsioDriver.info.bufferGranularity; asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory( @@ -3046,7 +3053,7 @@ previousIndex = index; paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency; */ -/* Disabled! Stopping and re-starting the stream causes an input overflow / output undeflow. S.Fischer */ +/* Disabled! Stopping and re-starting the stream causes an input overflow / output underflow. S.Fischer */ #if 0 // detect underflows by checking inter-callback time > 2 buffer period static double previousTime = -1; @@ -3498,6 +3505,7 @@ static PaError IsStreamActive( PaStream *s ) static PaTime GetStreamTime( PaStream *s ) { (void) s; /* unused parameter */ + return (double)timeGetTime() * .001; } diff --git a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c index 55ce9f0c0e..339e2afa33 100644 --- a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c +++ b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c @@ -204,8 +204,40 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input ) return channelName; } + +PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device, + long *minBufferSizeFrames, long *maxBufferSizeFrames ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + + result = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio ); + + if( result == paNoError ) + { + PaDeviceIndex hostApiDeviceIndex; + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDeviceIndex, device, hostApi ); + if( result == paNoError ) + { + PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi; + AudioDeviceID macCoreDeviceId = macCoreHostApi->devIds[hostApiDeviceIndex]; + AudioValueRange audioRange; + UInt32 propSize = sizeof( audioRange ); + + // return the size range for the output scope unless we only have inputs + Boolean isInput = 0; + if( macCoreHostApi->inheritedHostApiRep.deviceInfos[hostApiDeviceIndex]->maxOutputChannels == 0 ) + isInput = 1; + + result = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) ); - + *minBufferSizeFrames = audioRange.mMinimum; + *maxBufferSizeFrames = audioRange.mMaximum; + } + } + + return result; +} AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s ) @@ -1596,12 +1628,19 @@ static UInt32 CalculateOptimalBufferSize( PaMacAUHAL *auhalHostApi, resultBufferSizeFrames = MAX( resultBufferSizeFrames, (UInt32) variableLatencyFrames ); } + // can't have zero frames. code to round up to next user buffer requires non-zero + resultBufferSizeFrames = MAX( resultBufferSizeFrames, 1 ); + if( requestedFramesPerBuffer != paFramesPerBufferUnspecified ) { // make host buffer the next highest integer multiple of user frames per buffer UInt32 n = (resultBufferSizeFrames + requestedFramesPerBuffer - 1) / requestedFramesPerBuffer; resultBufferSizeFrames = n * requestedFramesPerBuffer; + + // FIXME: really we should be searching for a multiple of requestedFramesPerBuffer + // that is >= suggested latency and also fits within device buffer min/max + }else{ VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", resultBufferSizeFrames ) ); diff --git a/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c b/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c index 11dd701cf2..24c322bbee 100644 --- a/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c +++ b/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_ds.c 1794 2011-11-24 18:11:33Z rossb $ + * $Id: pa_win_ds.c 1824 2012-04-02 07:45:18Z rbencina $ * Portable Audio I/O Library DirectSound implementation * * Authors: Phil Burk, Robert Marsanyi & Ross Bencina @@ -480,7 +480,7 @@ static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidV else { newItems[i].lpGUID = &newItems[i].guid; - memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );; + memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) ); } newItems[i].pnpInterface = guidVector->items[i].pnpInterface; } @@ -1167,6 +1167,8 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde goto error; } + memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */ + result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult ); if( result != paNoError ) { @@ -1306,7 +1308,7 @@ error: TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs ); TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs ); - Terminate( winDsHostApi ); + Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi ); return result; } @@ -1703,9 +1705,9 @@ static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames, unsigned long suggestedOutputLatencyFrames, double sampleRate, unsigned long userFramesPerBuffer ) { - unsigned long minimumPollingPeriodFrames = sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS; - unsigned long maximumPollingPeriodFrames = sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS; - unsigned long pollingJitterFrames = sampleRate * PA_DS_POLLING_JITTER_SECONDS; + unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS); + unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS); + unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS); if( userFramesPerBuffer == paFramesPerBufferUnspecified ) { @@ -1771,9 +1773,9 @@ static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames, unsigned long *pollingPeriodFrames, double sampleRate, unsigned long userFramesPerBuffer ) { - unsigned long minimumPollingPeriodFrames = sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS; - unsigned long maximumPollingPeriodFrames = sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS; - unsigned long pollingJitterFrames = sampleRate * PA_DS_POLLING_JITTER_SECONDS; + unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS); + unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS); + unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS); *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 ); @@ -2471,6 +2473,8 @@ static int TimeSlice( PaWinDsStream *stream ) framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes; /* Check for underflow */ + /* FIXME QueryOutputSpace should not adjust underflow count as a side effect. + A query function should be a const operator on the stream and return a flag on underflow. */ if( stream->outputUnderflowCount != previousUnderflowCount ) stream->callbackFlags |= paOutputUnderflow; @@ -2536,7 +2540,7 @@ static int TimeSlice( PaWinDsStream *stream ) { /* We don't currently add outputLatency here because it appears to produce worse - results than non adding it. Need to do more testing to verify this. + results than not adding it. Need to do more testing to verify this. */ /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */ timeInfo.outputBufferDacTime = timeInfo.currentTime; @@ -2729,7 +2733,7 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) LARGE_INTEGER dueTime; int timerPeriodMs; - timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND; + timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND); if( timerPeriodMs < 1 ) timerPeriodMs = 1; @@ -2953,7 +2957,7 @@ static PaError StartStream( PaStream *s ) if( stream->streamRepresentation.streamCallback ) { TIMECAPS timecaps; - int timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND; + int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND); if( timerPeriodMs < 1 ) timerPeriodMs = 1; @@ -2967,7 +2971,7 @@ static PaError StartStream( PaStream *s ) if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 ) ) { /* aim for resolution 4 times higher than polling rate */ - stream->systemTimerResolutionPeriodMs = (stream->pollingPeriodSeconds * MSECS_PER_SECOND) / 4; + stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25); if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin ) stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin; if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax ) diff --git a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c index 0172e47144..3cb5ecc739 100644 --- a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c +++ b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c @@ -1065,6 +1065,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd result = paInsufficientMemory; goto error; } + + memset( paWasapi, 0, sizeof(PaWasapiHostApiRepresentation) ); /* ensure all fields are zeroed. especially paWasapi->allocations */ result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult ); if( result != paNoError ) @@ -1248,7 +1250,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd goto error; } if (value.pwszVal) - wcstombs(deviceName, value.pwszVal, MAX_STR_LEN-1); + WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), deviceName, MAX_STR_LEN-1, 0, 0); else _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i); deviceInfo->name = deviceName; @@ -2211,11 +2213,15 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu if (framesPerLatency == 0) framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec); - //! Exclusive Input stream renders data in 6 packets, we must set then the size of - //! single packet, total buffer size, e.g. required latency will be PacketSize * 6 + // Exclusive Input stream renders data in 6 packets, we must set then the size of + // single packet, total buffer size, e.g. required latency will be PacketSize * 6 if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)) { - framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER; + // Do it only for Polling mode + if ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0) + { + framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER; + } } // Calculate aligned period @@ -3460,7 +3466,7 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames ) // Limit desired to amount of requested frames desired = available; - if (desired > frames) + if ((UINT32)desired > frames) desired = frames; // Get pointers to read regions @@ -4795,13 +4801,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // output if (stream->bufferMode == paUtilFixedHostBufferSize) { - if (frames >= stream->out.framesPerBuffer) + while (frames >= stream->out.framesPerBuffer) { if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK) { LogHostError(hr); goto thread_error; } + + frames -= stream->out.framesPerBuffer; } } else diff --git a/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c index 869cc4dac8..4ab761a94d 100644 --- a/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c +++ b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c @@ -1,66 +1,70 @@ /* - * $Id: pa_win_wdmks.c 1606 2011-02-17 15:56:04Z rob_bielik $ - * 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. - * - * 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. - */ +* $Id: pa_win_wdmks.c 1820 2012-02-25 07:43:10Z robiwan $ +* PortAudio Windows WDM-KS interface +* +* Author: Andrew Baldwin, Robert Bielik (WaveRT) +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ +* The text above constitutes the entire PortAudio license; however, +* the PortAudio community also makes the following non-binding requests: +* +* Any person wishing to distribute modifications to the Software is +* requested to send the modifications to the original developer so that +* they can be incorporated into the canonical version. It is also +* requested that these non-binding requests be included along with the +* license above. +*/ /** @file - @ingroup hostapi_src - @brief Portaudio WDM-KS host API. +@ingroup hostapi_src +@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 +@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, Vista, Win7) +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 +#if (defined(_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment( lib, "setupapi.lib" ) +#endif + /* Debugging/tracing support */ #define PA_LOGE_ #define PA_LOGL_ #ifdef __GNUC__ - #include - #define _WIN32_WINNT 0x0501 - #define WINVER 0x0501 +#include +#define _WIN32_WINNT 0x0501 +#define WINVER 0x0501 #endif #include /* strlen() */ @@ -74,70 +78,95 @@ #include "pa_process.h" #include "portaudio.h" #include "pa_debugprint.h" +#include "pa_memorybarrier.h" +#include "pa_ringbuffer.h" +#include "pa_trace.h" +#include "pa_win_waveformat.h" + +#include "pa_win_wdmks.h" #include #include #include +#include + +#ifdef _MSC_VER +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +/* The PA_HP_TRACE macro is used in RT parts, so it can be switched off without affecting +the rest of the debug tracing */ +#if 1 +#define PA_HP_TRACE(x) PaUtil_AddHighSpeedLogMessage x ; +#else +#define PA_HP_TRACE(x) +#endif + +/* A define that selects whether the resulting pin names are chosen from pin category +instead of the available pin names, who sometimes can be quite cheesy, like "Volume control". +Default is to use the pin category. +*/ +#ifndef PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES +#define PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES 1 +#endif + #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) +#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 -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment( lib, "setupapi.lib" ) -#endif - -/* use CreateThread for CYGWIN, _beginthreadex for all others */ -#ifndef __CYGWIN__ -#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) +/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */ +#if !defined(__CYGWIN__) && !defined(_WIN32_WCE) +#define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex +#define PA_THREAD_FUNC static unsigned WINAPI #else -#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) -#endif - -/* use ExitThread for CYGWIN, _endthreadex for all others */ -#ifndef __CYGWIN__ -#define EXIT_THREAD _endthreadex(0) -#else -#define EXIT_THREAD ExitThread(0) +#define CREATE_THREAD_FUNCTION CreateThread +#define PA_THREAD_FUNC static DWORD WINAPI #endif #ifdef _MSC_VER - //#define NOMMIDS - #define DYNAMIC_GUID(data) {data} - //#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) +#define NOMMIDS +#define DYNAMIC_GUID(data) {data} +#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) #endif +#include #include #include + +/* Note that Windows SDK V6.0A or later is needed for WaveRT specific structs to be present in + ksmedia.h. Also make sure that the SDK include path is before other include paths (that may contain + an "old" ksmedia.h), so the proper ksmedia.h is used */ #include -#include + #include #include @@ -146,17 +175,77 @@ typedef KSDDKAPI DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, P extern HMODULE DllKsUser; extern KSCREATEPIN* FunctionKsCreatePin; +/* These definitions allows the use of AVRT.DLL on Vista and later OSs */ +extern HMODULE DllAvRt; +typedef HANDLE WINAPI AVSETMMTHREADCHARACTERISTICS(LPCSTR, LPDWORD TaskIndex); +typedef BOOL WINAPI AVREVERTMMTHREADCHARACTERISTICS(HANDLE); +typedef enum _PA_AVRT_PRIORITY +{ + PA_AVRT_PRIORITY_LOW = -1, + PA_AVRT_PRIORITY_NORMAL, + PA_AVRT_PRIORITY_HIGH, + PA_AVRT_PRIORITY_CRITICAL +} PA_AVRT_PRIORITY, *PPA_AVRT_PRIORITY; +typedef BOOL WINAPI AVSETMMTHREADPRIORITY(HANDLE, PA_AVRT_PRIORITY); +extern AVSETMMTHREADCHARACTERISTICS* FunctionAvSetMmThreadCharacteristics; +extern AVREVERTMMTHREADCHARACTERISTICS* FunctionAvRevertMmThreadCharacteristics; +extern AVSETMMTHREADPRIORITY* FunctionAvSetMmThreadPriority; + +/* An unspecified channel count (-1) is not treated correctly, so we replace it with +* an arbitrarily large number */ +#define MAXIMUM_NUMBER_OF_CHANNELS 256 + /* Forward definition to break circular type reference between pin and filter */ struct __PaWinWdmFilter; typedef struct __PaWinWdmFilter PaWinWdmFilter; +struct __PaWinWdmPin; +typedef struct __PaWinWdmPin PaWinWdmPin; + +struct __PaWinWdmStream; +typedef struct __PaWinWdmStream PaWinWdmStream; + +/* Function prototype for getting audio position */ +typedef PaError (*FunctionGetPinAudioPosition)(PaWinWdmPin*, unsigned long*); + +/* Function prototype for memory barrier */ +typedef void (*FunctionMemoryBarrier)(void); + +struct __PaProcessThreadInfo; +typedef struct __PaProcessThreadInfo PaProcessThreadInfo; + +typedef PaError (*FunctionPinHandler)(PaProcessThreadInfo* pInfo, unsigned eventIndex); + +typedef enum __PaStreamStartEnum +{ + StreamStart_kOk, + StreamStart_kFailed, + StreamStart_kCnt +} PaStreamStartEnum; + +/* Multiplexed input structure. +* Very often several physical inputs are multiplexed through a MUX node (represented in the topology filter) */ +typedef struct __PaWinWdmMuxedInput +{ + wchar_t friendlyName[MAX_PATH]; + ULONG muxPinId; + ULONG muxNodeId; + ULONG endpointPinId; +} PaWinWdmMuxedInput; + /* The Pin structure - * A pin is an input or output node, e.g. for audio flow */ -typedef struct __PaWinWdmPin +* A pin is an input or output node, e.g. for audio flow */ +struct __PaWinWdmPin { HANDLE handle; + PaWinWdmMuxedInput** inputs; + unsigned inputCount; + wchar_t friendlyName[MAX_PATH]; + PaWinWdmFilter* parentFilter; + PaWDMKSSubType pinKsSubType; unsigned long pinId; + unsigned long endpointPinId; /* For output pins */ KSPIN_CONNECT* pinConnect; unsigned long pinConnectSize; KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx; @@ -168,26 +257,46 @@ typedef struct __PaWinWdmPin unsigned long frameSize; int maxChannels; unsigned long formats; - int bestSampleRate; -} -PaWinWdmPin; + int defaultSampleRate; + ULONG *positionRegister; /* WaveRT */ + ULONG hwLatency; /* WaveRT */ + FunctionMemoryBarrier fnMemBarrier; /* WaveRT */ + FunctionGetPinAudioPosition fnAudioPosition; /* WaveRT */ + FunctionPinHandler fnEventHandler; + FunctionPinHandler fnSubmitHandler; +}; /* The Filter structure - * A filter has a number of pins and a "friendly name" */ +* A filter has a number of pins and a "friendly name" */ struct __PaWinWdmFilter { HANDLE handle; + PaWinWDMKSDeviceInfo devInfo; /* This will hold information that is exposed in PaDeviceInfo */ + + DWORD deviceNode; int pinCount; PaWinWdmPin** pins; - TCHAR filterName[MAX_PATH]; - TCHAR friendlyName[MAX_PATH]; - int maxInputChannels; - int maxOutputChannels; - unsigned long formats; + PaWinWdmFilter* topologyFilter; + wchar_t friendlyName[MAX_PATH]; + int validPinCount; int usageCount; - int bestSampleRate; + KSMULTIPLE_ITEM* connections; + KSMULTIPLE_ITEM* nodes; + int filterRefCount; }; + +typedef struct __PaWinWdmDeviceInfo +{ + PaDeviceInfo inheritedDeviceInfo; + char compositeName[MAX_PATH]; /* Composite name consists of pin name + device name in utf8 */ + PaWinWdmFilter* filter; + unsigned long pin; + int muxPosition; /* Used only for input devices */ + int endpointPinId; +} +PaWinWdmDeviceInfo; + /* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */ typedef struct __PaWinWdmHostApiRepresentation { @@ -196,63 +305,114 @@ typedef struct __PaWinWdmHostApiRepresentation PaUtilStreamInterface blockingStreamInterface; PaUtilAllocationGroup* allocations; - PaWinWdmFilter** filters; - int filterCount; + int deviceCount; } PaWinWdmHostApiRepresentation; -typedef struct __PaWinWdmDeviceInfo -{ - PaDeviceInfo inheritedDeviceInfo; - PaWinWdmFilter* filter; -} -PaWinWdmDeviceInfo; - typedef struct __DATAPACKET { KSSTREAM_HEADER Header; OVERLAPPED Signal; } DATAPACKET; +typedef struct __PaIOPacket +{ + DATAPACKET* packet; + unsigned startByte; + unsigned lengthBytes; +} PaIOPacket; + +typedef struct __PaWinWdmIOInfo +{ + PaWinWdmPin* pPin; + char* hostBuffer; + unsigned hostBufferSize; + unsigned framesPerBuffer; + unsigned bytesPerFrame; + unsigned bytesPerSample; + unsigned noOfPackets; /* Only used in WaveCyclic */ + HANDLE *events; /* noOfPackets handles (WaveCyclic) 1 (WaveRT) */ + DATAPACKET *packets; /* noOfPackets packets (WaveCyclic) 2 (WaveRT) */ + /* WaveRT polled mode */ + unsigned lastPosition; + unsigned pollCntr; +} PaWinWdmIOInfo; + /* PaWinWdmStream - a stream data structure specifically for this implementation */ -typedef struct __PaWinWdmStream +struct __PaWinWdmStream { PaUtilStreamRepresentation streamRepresentation; + PaWDMKSSpecificStreamInfo hostApiStreamInfo; /* This holds info that is exposed through PaStreamInfo */ PaUtilCpuLoadMeasurer cpuLoadMeasurer; PaUtilBufferProcessor bufferProcessor; - PaWinWdmPin* recordingPin; - PaWinWdmPin* playbackPin; - char* hostBuffer; - unsigned long framesPerHostIBuffer; - unsigned long framesPerHostOBuffer; - int bytesPerInputFrame; - int bytesPerOutputFrame; +#if PA_TRACE_REALTIME_EVENTS + LogHandle hLog; +#endif + + PaUtilAllocationGroup* allocGroup; + PaWinWdmIOInfo capture; + PaWinWdmIOInfo render; 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 */ + HANDLE eventAbort; + HANDLE eventStreamStart[StreamStart_kCnt]; /* 0 = OK, 1 = Failed */ + PaError threadResult; PaStreamFlags streamFlags; + + /* Capture ring buffer */ + PaUtilRingBuffer ringBuffer; + char* ringBufferData; + /* These values handle the case where the user wants to use fewer - * channels than the device has */ + * channels than the device has */ int userInputChannels; int deviceInputChannels; int userOutputChannels; int deviceOutputChannels; - int inputSampleSize; - int outputSampleSize; -} -PaWinWdmStream; +}; -#include +/* Gather all processing variables in a struct */ +struct __PaProcessThreadInfo +{ + PaWinWdmStream *stream; + PaStreamCallbackTimeInfo ti; + PaStreamCallbackFlags underover; + int cbResult; + volatile int pending; + volatile int priming; + volatile int pinsStarted; + unsigned long timeout; + unsigned captureHead; + unsigned captureTail; + unsigned renderHead; + unsigned renderTail; + PaIOPacket capturePackets[4]; + PaIOPacket renderPackets[4]; +}; + +/* Used for transferring device infos during scanning / rescanning */ +typedef struct __PaWinWDMScanDeviceInfosResults +{ + PaDeviceInfo **deviceInfos; + PaDeviceIndex defaultInputDevice; + PaDeviceIndex defaultOutputDevice; +} PaWinWDMScanDeviceInfosResults; + +static const unsigned cPacketsArrayMask = 3; HMODULE DllKsUser = NULL; KSCREATEPIN* FunctionKsCreatePin = NULL; +HMODULE DllAvRt = NULL; +AVSETMMTHREADCHARACTERISTICS* FunctionAvSetMmThreadCharacteristics = NULL; +AVREVERTMMTHREADCHARACTERISTICS* FunctionAvRevertMmThreadCharacteristics = NULL; +AVSETMMTHREADPRIORITY* FunctionAvSetMmThreadPriority = NULL; + /* prototypes for functions declared in this file */ #ifdef __cplusplus @@ -260,7 +420,7 @@ extern "C" { #endif /* __cplusplus */ -PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); #ifdef __cplusplus } @@ -268,37 +428,48 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd /* 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); + 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); + 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); + 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); + unsigned long pinId, + const GUID* const guidPropertySet, + unsigned long property, + void* value, + unsigned long valueCount, + unsigned long* byteCount); static PaError WdmGetPinPropertyMulti(HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - KSMULTIPLE_ITEM** ksMultipleItem); + unsigned long pinId, + const GUID* const guidPropertySet, + unsigned long property, + KSMULTIPLE_ITEM** ksMultipleItem); +static PaError WdmGetPropertyMulti(HANDLE handle, + const GUID* const guidPropertySet, + unsigned long property, + KSMULTIPLE_ITEM** ksMultipleItem); + +static PaError WdmSetMuxNodeProperty(HANDLE handle, + ULONG nodeId, + ULONG pinId); + /** Pin management functions */ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error); @@ -309,49 +480,47 @@ static PaError PinInstantiate(PaWinWdmPin* pin); static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state); static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format); static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format); +/* WaveRT support */ +static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult); +static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier); +static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin); +static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle); +static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle); +static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay); +static PaError PinGetAudioPositionDirect(PaWinWdmPin* pPin, ULONG* pPosition); +static PaError PinGetAudioPositionViaIOCTL(PaWinWdmPin* pPin, ULONG* pPosition); /* Filter management functions */ -static PaWinWdmFilter* FilterNew( - TCHAR* filterName, - TCHAR* friendlyName, - PaError* error); +static PaWinWdmFilter* FilterNew(PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error); +static PaError FilterInitializePins(PaWinWdmFilter* filter); 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); +static void FilterAddRef(PaWinWdmFilter* filter); +static PaWinWdmPin* FilterCreatePin( + PaWinWdmFilter* filter, + int pinId, + const WAVEFORMATEX* wfex, + PaError* error); +static PaError FilterUse(PaWinWdmFilter* filter); +static void FilterRelease(PaWinWdmFilter* filter); + +/* Hot plug functions */ +static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1, + const PaWinWdmDeviceInfo* pDev2); /* Interface functions */ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); static PaError IsFormatSupported( - struct PaUtilHostApiRepresentation *hostApi, +struct PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate ); + +static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **newDeviceInfos, int *newDeviceCount ); +static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *deviceInfos, int deviceCount ); +static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, int deviceCount ); + static PaError OpenStream( - struct PaUtilHostApiRepresentation *hostApi, +struct PaUtilHostApiRepresentation *hostApi, PaStream** s, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, @@ -369,27 +538,108 @@ 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 ); + PaStream* stream, + void *buffer, + unsigned long frames ); static PaError WriteStream( - PaStream* stream, - const void *buffer, - unsigned long frames ); + 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 PaWinWdmFilter** BuildFilterList(int* filterCount, int* noOfPaDevices, PaError* result); 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); +PA_THREAD_FUNC ProcessingThread(void*); + +/* Pin handler functions */ +static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex); + +static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex); + +static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex); + +static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex); +static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex); /* Function bodies */ +#if defined(_DEBUG) && defined(PA_ENABLE_DEBUG_OUTPUT) +#define PA_WDMKS_SET_TREF +static PaTime tRef = 0; + +static void PaWinWdmDebugPrintf(const char* fmt, ...) +{ + va_list list; + char buffer[1024]; + PaTime t = PaUtil_GetTime() - tRef; + va_start(list, fmt); + _vsnprintf(buffer, 1023, fmt, list); + va_end(list); + PaUtil_DebugPrint("%6.3lf: %s", t, buffer); +} + +#ifdef PA_DEBUG +#undef PA_DEBUG +#define PA_DEBUG(x) PaWinWdmDebugPrintf x ; +#endif +#endif + +static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1, + const PaWinWdmDeviceInfo* pDev2) +{ + if (pDev1 == NULL || pDev2 == NULL) + return FALSE; + + if (pDev1 == pDev2) + return TRUE; + + if (strcmp(pDev1->compositeName, pDev2->compositeName) == 0) + return TRUE; + + return FALSE; +} + +static BOOL IsEarlierThanVista() +{ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (GetVersionEx(&osvi) && osvi.dwMajorVersion<6) + { + return TRUE; + } + return FALSE; +} + + + +static void MemoryBarrierDummy(void) +{ + /* Do nothing */ +} + +static void MemoryBarrierRead(void) +{ + PaUtil_ReadMemoryBarrier(); +} + +static void MemoryBarrierWrite(void) +{ + PaUtil_WriteMemoryBarrier(); +} + static unsigned long GetWfexSize(const WAVEFORMATEX* wfex) { if( wfex->wFormatTag == WAVE_FORMAT_PCM ) @@ -402,90 +652,67 @@ static unsigned long GetWfexSize(const WAVEFORMATEX* wfex) } } +static void PaWinWDM_SetLastErrorInfo(long errCode, const char* fmt, ...) +{ + va_list list; + char buffer[1024]; + va_start(list, fmt); + _vsnprintf(buffer, 1023, fmt, list); + va_end(list); + PaUtil_SetLastHostErrorInfo(paWDMKS, errCode, buffer); +} + /* 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) + 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; + unsigned long dummyBytesReturned = 0; + BOOL bRes; if( !bytesReturned ) { - /* User a dummy as the caller hasn't supplied one */ + /* Use 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 ) + bRes = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount, outBuffer, outBufferCount, bytesReturned, NULL); + if (!bRes) { - 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 + unsigned long error = GetLastError(); + if ( !(((error == ERROR_INSUFFICIENT_BUFFER ) || ( error == ERROR_MORE_DATA )) && + ( ioctlNumber == IOCTL_KS_PROPERTY ) && + ( outBufferCount == 0 ) ) ) { + PaWinWDM_SetLastErrorInfo(result, "WdmSyncIoctl: DeviceIoControl GLE = 0x%08X", error); 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) + 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 ); + ksProperty = (KSPROPERTY*)_alloca( propertyCount ); if( !ksProperty ) { return paInsufficientMemory; @@ -502,33 +729,32 @@ static PaError WdmGetPropertySimple(HANDLE handle, } result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - ksProperty, - propertyCount, - value, - valueCount, - NULL); + 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) + 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 ); + ksProperty = (KSPROPERTY*)_alloca( propertyCount ); if( !ksProperty ) { return paInsufficientMemory; @@ -544,25 +770,25 @@ static PaError WdmSetPropertySimple( } result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - ksProperty, - propertyCount, - value, - valueCount, - NULL); + 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) + HANDLE handle, + unsigned long pinId, + const GUID* const guidPropertySet, + unsigned long property, + void* value, + unsigned long valueCount, + unsigned long *byteCount) { PaError result; @@ -574,23 +800,23 @@ static PaError WdmGetPinPropertySimple( ksPProp.Reserved = 0; result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp, - sizeof(KSP_PIN), - value, - valueCount, - NULL); + handle, + IOCTL_KS_PROPERTY, + &ksPProp, + sizeof(KSP_PIN), + value, + valueCount, + byteCount); return result; } static PaError WdmGetPinPropertyMulti( - HANDLE handle, - unsigned long pinId, - const GUID* const guidPropertySet, - unsigned long property, - KSMULTIPLE_ITEM** ksMultipleItem) + HANDLE handle, + unsigned long pinId, + const GUID* const guidPropertySet, + unsigned long property, + KSMULTIPLE_ITEM** ksMultipleItem) { PaError result; unsigned long multipleItemSize = 0; @@ -603,13 +829,13 @@ static PaError WdmGetPinPropertyMulti( ksPProp.Reserved = 0; result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp.Property, - sizeof(KSP_PIN), - NULL, - 0, - &multipleItemSize); + handle, + IOCTL_KS_PROPERTY, + &ksPProp.Property, + sizeof(KSP_PIN), + NULL, + 0, + &multipleItemSize); if( result != paNoError ) { return result; @@ -622,13 +848,13 @@ static PaError WdmGetPinPropertyMulti( } result = WdmSyncIoctl( - handle, - IOCTL_KS_PROPERTY, - &ksPProp, - sizeof(KSP_PIN), - (void*)*ksMultipleItem, - multipleItemSize, - NULL); + handle, + IOCTL_KS_PROPERTY, + &ksPProp, + sizeof(KSP_PIN), + (void*)*ksMultipleItem, + multipleItemSize, + NULL); if( result != paNoError ) { @@ -638,6 +864,398 @@ static PaError WdmGetPinPropertyMulti( return result; } +static PaError WdmGetPropertyMulti(HANDLE handle, + const GUID* const guidPropertySet, + unsigned long property, + KSMULTIPLE_ITEM** ksMultipleItem) +{ + PaError result; + unsigned long multipleItemSize = 0; + KSPROPERTY ksProp; + + ksProp.Set = *guidPropertySet; + ksProp.Id = property; + ksProp.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl( + handle, + IOCTL_KS_PROPERTY, + &ksProp, + sizeof(KSPROPERTY), + NULL, + 0, + &multipleItemSize); + if( result != paNoError ) + { + return result; + } + + *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize ); + if( !*ksMultipleItem ) + { + return paInsufficientMemory; + } + + result = WdmSyncIoctl( + handle, + IOCTL_KS_PROPERTY, + &ksProp, + sizeof(KSPROPERTY), + (void*)*ksMultipleItem, + multipleItemSize, + NULL); + + if( result != paNoError ) + { + PaUtil_FreeMemory( ksMultipleItem ); + } + + return result; +} + +static PaError WdmSetMuxNodeProperty(HANDLE handle, + ULONG nodeId, + ULONG pinId) +{ + PaError result = paNoError; + KSNODEPROPERTY prop; + prop.Property.Set = KSPROPSETID_Audio; + prop.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE; + prop.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY; + prop.NodeId = nodeId; + prop.Reserved = 0; + + result = WdmSyncIoctl(handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSNODEPROPERTY), &pinId, sizeof(ULONG), NULL); + + return result; +} + +/* Used when traversing topology for outputs */ +static const KSTOPOLOGY_CONNECTION* GetConnectionTo(const KSTOPOLOGY_CONNECTION* pFrom, PaWinWdmFilter* filter, int muxIdx) +{ + unsigned i; + const KSTOPOLOGY_CONNECTION* retval = NULL; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + (void)muxIdx; + PA_DEBUG(("GetConnectionTo: Checking %u connections... (pFrom = %p)", filter->connections->Count, pFrom)); + for (i = 0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + if (pConn == pFrom) + continue; + + if (pConn->FromNode == pFrom->ToNode) + { + retval = pConn; + break; + } + } + PA_DEBUG(("GetConnectionTo: Returning %p\n", retval)); + return retval; +} + +/* Used when traversing topology for inputs */ +static const KSTOPOLOGY_CONNECTION* GetConnectionFrom(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter, int muxIdx) +{ + unsigned i; + const KSTOPOLOGY_CONNECTION* retval = NULL; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + int muxCntr = 0; + PA_DEBUG(("GetConnectionFrom: Checking %u connections... (pTo = %p)\n", filter->connections->Count, pTo)); + for (i = 0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + if (pConn == pTo) + continue; + + if (pConn->ToNode == pTo->FromNode) + { + if (muxIdx >= 0) + { + if (muxCntr < muxIdx) + { + ++muxCntr; + continue; + } + } + retval = pConn; + break; + } + } + PA_DEBUG(("GetConnectionFrom: Returning %p\n", retval)); + return retval; +} + +static ULONG GetNumberOfConnectionsTo(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter) +{ + ULONG retval = 0; + unsigned i; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + PA_DEBUG(("GetNumberOfConnectionsTo: Checking %u connections...", filter->connections->Count)); + for (i = 0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + if (pConn->ToNode == pTo->FromNode && + (pTo->FromNode != KSFILTER_NODE || pConn->ToNodePin == pTo->FromNodePin)) + { + ++retval; + } + } + return retval; +} + +typedef const KSTOPOLOGY_CONNECTION *(*TFnGetConnection)(const KSTOPOLOGY_CONNECTION*, PaWinWdmFilter*, int); + +static const KSTOPOLOGY_CONNECTION* FindStartConnectionFrom(ULONG startPin, PaWinWdmFilter* filter) +{ + unsigned i; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + PA_DEBUG(("FindStartConnectionFrom: Checking %u connections...", filter->connections->Count)); + for (i = 0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + if (pConn->ToNode == KSFILTER_NODE && pConn->ToNodePin == startPin) + { + return pConn; + } + } + + assert(FALSE); + return 0; +} + +static const KSTOPOLOGY_CONNECTION* FindStartConnectionTo(ULONG startPin, PaWinWdmFilter* filter) +{ + unsigned i; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + PA_DEBUG(("FindStartConnectionTo: Checking %u connections...", filter->connections->Count)); + for (i = 0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + if (pConn->FromNode == KSFILTER_NODE && pConn->FromNodePin == startPin) + { + return pConn; + } + } + + assert(FALSE); + return 0; +} + +static ULONG GetConnectedPin(ULONG startPin, BOOL forward, PaWinWdmFilter* filter, int muxPosition, ULONG *muxInputPinId, ULONG *muxNodeId) +{ + const KSTOPOLOGY_CONNECTION *conn = NULL; + TFnGetConnection fnGetConnection = forward ? GetConnectionTo : GetConnectionFrom ; + while (1) + { + if (conn == NULL) + { + conn = forward ? FindStartConnectionTo(startPin, filter) : FindStartConnectionFrom(startPin, filter); + } + else + { + conn = fnGetConnection(conn, filter, -1); + } + + /* Handling case of erroneous connection list */ + if (conn == NULL) + { + break; + } + + if (forward ? conn->ToNode == KSFILTER_NODE : conn->FromNode == KSFILTER_NODE) + { + return forward ? conn->ToNodePin : conn->FromNodePin; + } + else + { + PA_DEBUG(("GetConnectedPin: count=%d, forward=%d, muxPosition=%d\n", filter->nodes->Count, forward, muxPosition)); + if (filter->nodes->Count > 0 && !forward && muxPosition >= 0) + { + const GUID* nodes = (const GUID*)(filter->nodes + 1); + if (IsEqualGUID(&nodes[conn->FromNode], &KSNODETYPE_MUX)) + { + ULONG nConn = GetNumberOfConnectionsTo(conn, filter); + conn = fnGetConnection(conn, filter, muxPosition); + if (conn == NULL) + { + break; + } + if (muxInputPinId != 0) + { + *muxInputPinId = conn->ToNodePin; + } + if (muxNodeId != 0) + { + *muxNodeId = conn->ToNode; + } + } + } + } + } + return KSFILTER_NODE; +} + +static void DumpConnectionsAndNodes(PaWinWdmFilter* filter) +{ + unsigned i; + const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1); + const GUID* nodes = (const GUID*)(filter->nodes + 1); + + PA_DEBUG(("DumpConnectionsAndNodes: connections=%d, nodes=%d\n", filter->connections->Count, filter->nodes->Count)); + + for (i=0; i < filter->connections->Count; ++i) + { + const KSTOPOLOGY_CONNECTION* pConn = connections + i; + PA_DEBUG((" Connection: %u - FromNode=%u,FromPin=%u -> ToNode=%u,ToPin=%u\n", + i, + pConn->FromNode, pConn->FromNodePin, + pConn->ToNode, pConn->ToNodePin + )); + } + + for (i=0; i < filter->nodes->Count; ++i) + { + const GUID* pConn = nodes + i; + PA_DEBUG((" Node: %d - {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", + i, + pConn->Data1, pConn->Data2, pConn->Data3, + pConn->Data4[0], pConn->Data4[1], + pConn->Data4[2], pConn->Data4[3], + pConn->Data4[4], pConn->Data4[5], + pConn->Data4[6], pConn->Data4[7] + )); + } + +} + +typedef struct __PaUsbTerminalGUIDToName +{ + USHORT usbGUID; + wchar_t name[64]; +} PaUsbTerminalGUIDToName; + +static const PaUsbTerminalGUIDToName kNames[] = +{ + /* Types copied from: http://msdn.microsoft.com/en-us/library/ff537742(v=vs.85).aspx */ + /* Input terminal types */ + { 0x0201, L"Microphone" }, + { 0x0202, L"Desktop Microphone" }, + { 0x0203, L"Personal Microphone" }, + { 0x0204, L"Omni Directional Microphone" }, + { 0x0205, L"Microphone Array" }, + { 0x0206, L"Processing Microphone Array" }, + /* Output terminal types */ + { 0x0301, L"Speakers" }, + { 0x0302, L"Headphones" }, + { 0x0303, L"Head Mounted Display Audio" }, + { 0x0304, L"Desktop Speaker" }, + { 0x0305, L"Room Speaker" }, + { 0x0306, L"Communication Speaker" }, + { 0x0307, L"LFE Speakers" }, + /* External terminal types */ + { 0x0601, L"Analog" }, + { 0x0602, L"Digital" }, + { 0x0603, L"Line" }, + { 0x0604, L"Audio" }, + { 0x0605, L"SPDIF" }, +}; + +static const unsigned kNamesCnt = sizeof(kNames)/sizeof(PaUsbTerminalGUIDToName); + +static int PaUsbTerminalGUIDToNameCmp(const void* lhs, const void* rhs) +{ + const PaUsbTerminalGUIDToName* pL = (const PaUsbTerminalGUIDToName*)lhs; + const PaUsbTerminalGUIDToName* pR = (const PaUsbTerminalGUIDToName*)rhs; + return ((int)(pL->usbGUID) - (int)(pR->usbGUID)); +} + +static PaError GetNameFromCategory(const GUID* pGUID, BOOL input, wchar_t* name, unsigned length) +{ + PaError result = paUnanticipatedHostError; + USHORT usbTerminalGUID = (USHORT)(pGUID->Data1 - 0xDFF219E0); + + if (input && usbTerminalGUID >= 0x301 && usbTerminalGUID < 0x400) + { + /* Output terminal name for an input !? Set it to Line! */ + usbTerminalGUID = 0x603; + } + if (!input && usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x300) + { + /* Input terminal name for an output !? Set it to Line! */ + usbTerminalGUID = 0x603; + } + if (usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x713) + { + PaUsbTerminalGUIDToName s = { usbTerminalGUID }; + const PaUsbTerminalGUIDToName* ptr = bsearch( + &s, + kNames, + kNamesCnt, + sizeof(PaUsbTerminalGUIDToName), + PaUsbTerminalGUIDToNameCmp + ); + if (ptr != 0) + { + PA_DEBUG(("GetNameFromCategory: USB GUID %04X -> '%S'\n", usbTerminalGUID, ptr->name)); + + if (name != NULL && length > 0) + { + int n = _snwprintf(name, length, L"%s", ptr->name); + if (usbTerminalGUID >= 0x601 && usbTerminalGUID < 0x700) + { + _snwprintf(name + n, length - n, L" %s", (input ? L"In":L"Out")); + } + } + result = paNoError; + } + } + else + { + PaWinWDM_SetLastErrorInfo(result, "GetNameFromCategory: usbTerminalGUID = %04X ", usbTerminalGUID); + } + return result; +} + +static BOOL IsFrequencyWithinRange(const KSDATARANGE_AUDIO* range, int frequency) +{ + if (frequency < (int)range->MinimumSampleFrequency) + return FALSE; + if (frequency > (int)range->MaximumSampleFrequency) + return FALSE; + return TRUE; +} + +static BOOL IsBitsWithinRange(const KSDATARANGE_AUDIO* range, int noOfBits) +{ + if (noOfBits < (int)range->MinimumBitsPerSample) + return FALSE; + if (noOfBits > (int)range->MaximumBitsPerSample) + return FALSE; + return TRUE; +} + +/* Note: Somewhat different order compared to WMME implementation, as we want to focus on fidelity first */ +static const int defaultSampleRateSearchOrder[] = +{ 44100, 48000, 88200, 96000, 192000, 32000, 24000, 22050, 16000, 12000, 11025, 9600, 8000 }; +static const int defaultSampleRateSearchOrderCount = sizeof(defaultSampleRateSearchOrder)/sizeof(defaultSampleRateSearchOrder[0]); + +static int DefaultSampleFrequencyIndex(const KSDATARANGE_AUDIO* range) +{ + int i; + + for(i=0; i < defaultSampleRateSearchOrderCount; ++i) + { + int currentFrequency = defaultSampleRateSearchOrder[i]; + + if (IsFrequencyWithinRange(range, currentFrequency)) + { + return i; + } + } + + return -1; +} /* Create a new pin object belonging to a filter @@ -652,9 +1270,11 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa KSMULTIPLE_ITEM* item = NULL; KSIDENTIFIER* identifier; KSDATARANGE* dataRange; + const ULONG streamingId = (parentFilter->devInfo.streamingType == Type_kWaveRT) ? KSINTERFACE_STANDARD_LOOPED_STREAMING : KSINTERFACE_STANDARD_STREAMING; + int defaultSampleRateIndex = defaultSampleRateSearchOrderCount; PA_LOGE_; - PA_DEBUG(("Creating pin %d:\n",pinId)); + PA_DEBUG(("PinNew: Creating pin %d:\n",pinId)); /* Allocate the new PIN object */ pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) ); @@ -681,7 +1301,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa /* Configure the connect structure with default values */ pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard; - pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; + pin->pinConnect->Interface.Id = streamingId; pin->pinConnect->Interface.Flags = 0; pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard; pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; @@ -707,15 +1327,16 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa &KSPROPSETID_Pin, KSPROPERTY_PIN_COMMUNICATION, &pin->communication, - sizeof(KSPIN_COMMUNICATION)); + sizeof(KSPIN_COMMUNICATION), + NULL); if( result != paNoError ) goto error; if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/ - (pin->communication != KSPIN_COMMUNICATION_SINK) && - (pin->communication != KSPIN_COMMUNICATION_BOTH) ) + (pin->communication != KSPIN_COMMUNICATION_SINK) && + (pin->communication != KSPIN_COMMUNICATION_BOTH) ) { - PA_DEBUG(("Not source/sink\n")); + PA_DEBUG(("PinNew: Not source/sink\n")); result = paInvalidDevice; goto error; } @@ -727,7 +1348,8 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa &KSPROPSETID_Pin, KSPROPERTY_PIN_DATAFLOW, &pin->dataFlow, - sizeof(KSPIN_DATAFLOW)); + sizeof(KSPIN_DATAFLOW), + NULL); if( result != paNoError ) goto error; @@ -749,8 +1371,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa 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 ) ) + if( IsEqualGUID(&identifier[i].Set, &KSINTERFACESETID_Standard) && ( identifier[i].Id == streamingId ) ) { result = paNoError; break; @@ -759,7 +1380,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa if( result != paNoError ) { - PA_DEBUG(("No standard streaming\n")); + PA_DEBUG(("PinNew: No %s streaming\n", streamingId==KSINTERFACE_STANDARD_LOOPED_STREAMING?"looped":"standard")); goto error; } @@ -784,8 +1405,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa 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 ) ) + if( IsEqualGUID(&identifier[i].Set, &KSMEDIUMSETID_Standard) && ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) ) { result = paNoError; break; @@ -818,53 +1438,66 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa result = paUnanticipatedHostError; dataRange = pin->dataRanges; pin->maxChannels = 0; - pin->bestSampleRate = 0; + pin->defaultSampleRate = 0; pin->formats = 0; - for( i = 0; i dataRangesItem->Count; i++) + PA_DEBUG(("PinNew: Checking %u no of dataranges...\n", pin->dataRangesItem->Count)); + for( i = 0; i < pin->dataRangesItem->Count; i++) { - PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat)))); + PA_DEBUG(("PinNew: 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 ) ) ) ) ) + IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) || + IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) || + IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_WILDCARD) || + IsEqualGUID(&dataRange->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) ) { + int defaultIndex; 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 ) + if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels == (ULONG) -1 ) { - pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels; - /*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/ + pin->maxChannels = MAXIMUM_NUMBER_OF_CHANNELS; } + else if( (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels ) + { + pin->maxChannels = (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels; + } + PA_DEBUG(("PinNew: MaxChannel: %d\n",pin->maxChannels)); + /* Record the formats (bit depths) that are supported */ - if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 ) + if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 8) ) + { + pin->formats |= paInt8; + PA_DEBUG(("PinNew: Format PCM 8 bit supported\n")); + } + if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 16) ) { pin->formats |= paInt16; - PA_DEBUG(("Format 16 bit supported\n")); + PA_DEBUG(("PinNew: Format PCM 16 bit supported\n")); } - if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 ) + if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 24) ) { pin->formats |= paInt24; - PA_DEBUG(("Format 24 bit supported\n")); + PA_DEBUG(("PinNew: Format PCM 24 bit supported\n")); } - if( ( pin->bestSampleRate != 48000) && - (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) && - (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) ) + if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 32) ) { - pin->bestSampleRate = 48000; - PA_DEBUG(("48kHz supported\n")); + if (IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + pin->formats |= paFloat32; + PA_DEBUG(("PinNew: Format IEEE float 32 bit supported\n")); + } + else + { + pin->formats |= paInt32; + PA_DEBUG(("PinNew: Format PCM 32 bit supported\n")); + } } - else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) && - (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) && - (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) ) + + defaultIndex = DefaultSampleFrequencyIndex((KSDATARANGE_AUDIO*)dataRange); + if (defaultIndex >= 0 && defaultIndex < defaultSampleRateIndex) { - pin->bestSampleRate = 44100; - PA_DEBUG(("44.1kHz supported\n")); - } - else - { - pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency; + defaultSampleRateIndex = defaultIndex; } } dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize); @@ -873,6 +1506,19 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa if( result != paNoError ) goto error; + /* If none of the frequencies searched for are present, there's something seriously wrong */ + if (defaultSampleRateIndex == defaultSampleRateSearchOrderCount) + { + PA_DEBUG(("PinNew: No default sample rate found, skipping pin!\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "PinNew: No default sample rate found"); + result = paUnanticipatedHostError; + goto error; + } + + /* Set the default sample rate */ + pin->defaultSampleRate = defaultSampleRateSearchOrder[defaultSampleRateIndex]; + PA_DEBUG(("PinNew: Default sample rate = %d Hz\n", pin->defaultSampleRate)); + /* Get instance information */ result = WdmGetPinPropertySimple( parentFilter->handle, @@ -880,11 +1526,413 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa &KSPROPSETID_Pin, KSPROPERTY_PIN_CINSTANCES, &pin->instances, - sizeof(KSPIN_CINSTANCES)); + sizeof(KSPIN_CINSTANCES), + NULL); if( result != paNoError ) goto error; + /* If WaveRT, check if pin supports notification mode */ + if (parentFilter->devInfo.streamingType == Type_kWaveRT) + { + BOOL bSupportsNotification = FALSE; + if (PinQueryNotificationSupport(pin, &bSupportsNotification) == paNoError) + { + pin->pinKsSubType = bSupportsNotification ? SubType_kNotification : SubType_kPolled; + } + } + + /* Query pin name (which means we need to traverse to non IRP pin, via physical connection to topology filter pin, through + its nodes to the endpoint pin, and get that ones name... phew...) */ + PA_DEBUG(("PinNew: Finding topology pin...\n")); + + { + ULONG topoPinId = GetConnectedPin(pinId, (pin->dataFlow == KSPIN_DATAFLOW_IN), parentFilter, -1, NULL, NULL); + const wchar_t kInputName[] = L"Input"; + const wchar_t kOutputName[] = L"Output"; + + if (topoPinId != KSFILTER_NODE) + { + /* Get physical connection for topo pin */ + unsigned long cbBytes = 0; + PA_DEBUG(("PinNew: Getting physical connection...\n")); + result = WdmGetPinPropertySimple(parentFilter->handle, + topoPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PHYSICALCONNECTION, + 0, + 0, + &cbBytes + ); + + if (result != paNoError) + { + /* No physical connection -> there is no topology filter! So we get the name of the pin! */ + PA_DEBUG(("PinNew: No physical connection! Getting the pin name\n")); + result = WdmGetPinPropertySimple(parentFilter->handle, + topoPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_NAME, + pin->friendlyName, + MAX_PATH, + NULL); + if (result != paNoError) + { + GUID category = {0}; + + /* Get pin category information */ + result = WdmGetPinPropertySimple(parentFilter->handle, + topoPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_CATEGORY, + &category, + sizeof(GUID), + NULL); + + if (result == paNoError) + { + result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH); + } + } + + /* Make sure pin gets a name here... */ + if (wcslen(pin->friendlyName) == 0) + { + wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName); +#ifdef UNICODE + PA_DEBUG(("PinNew: Setting pin friendly name to '%s'\n", pin->friendlyName)); +#else + PA_DEBUG(("PinNew: Setting pin friendly name to '%S'\n", pin->friendlyName)); +#endif + } + + /* This is then == the endpoint pin */ + pin->endpointPinId = (pin->dataFlow == KSPIN_DATAFLOW_IN) ? pinId : topoPinId; + } + else + { + KSPIN_PHYSICALCONNECTION* pc = (KSPIN_PHYSICALCONNECTION*)PaUtil_AllocateMemory(cbBytes + 2); + PA_DEBUG(("PinNew: Physical connection found!\n")); + if (pc == NULL) + { + result = paInsufficientMemory; + goto error; + } + result = WdmGetPinPropertySimple(parentFilter->handle, + topoPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PHYSICALCONNECTION, + pc, + cbBytes, + NULL + ); + if (result == paNoError) + { + wchar_t symbLinkName[MAX_PATH]; + wcsncpy(symbLinkName, pc->SymbolicLinkName, MAX_PATH); + if (symbLinkName[1] == TEXT('?')) + { + symbLinkName[1] = TEXT('\\'); + } + + if (pin->parentFilter->topologyFilter == NULL) + { + PA_DEBUG(("PinNew: Creating topology filter '%S'\n", symbLinkName)); + + pin->parentFilter->topologyFilter = FilterNew(Type_kNotUsed, 0, symbLinkName, L"", &result); + if (pin->parentFilter->topologyFilter == NULL) + { + PA_DEBUG(("PinNew: Failed creating topology filter\n")); + result = paUnanticipatedHostError; + PaWinWDM_SetLastErrorInfo(result, "Failed to create topology filter '%S'", symbLinkName); + goto error; + } + + /* Copy info so we have it in device info */ + wcsncpy(pin->parentFilter->devInfo.topologyPath, symbLinkName, MAX_PATH); + } + else + { + /* Must be the same */ + assert(wcscmp(symbLinkName, pin->parentFilter->topologyFilter->devInfo.filterPath) == 0); + } + + PA_DEBUG(("PinNew: Opening topology filter...")); + + result = FilterUse(pin->parentFilter->topologyFilter); + if (result == paNoError) + { + unsigned long endpointPinId; + + if (pin->dataFlow == KSPIN_DATAFLOW_IN) + { + /* The "endpointPinId" is what WASAPI looks at for pin names */ + GUID category = {0}; + + PA_DEBUG(("PinNew: Checking for output endpoint pin id...\n")); + + endpointPinId = GetConnectedPin(pc->Pin, TRUE, pin->parentFilter->topologyFilter, -1, NULL, NULL); + + if (endpointPinId == KSFILTER_NODE) + { + result = paUnanticipatedHostError; + PaWinWDM_SetLastErrorInfo(result, "Failed to get endpoint pin ID on topology filter!"); + goto error; + } + + PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId)); + + /* Get pin category information */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_CATEGORY, + &category, + sizeof(GUID), + NULL); + + if (result == paNoError) + { +#if !PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES + wchar_t pinName[MAX_PATH]; + + PA_DEBUG(("PinNew: Getting pin name property...")); + + /* Ok, try pin name also, and favor that if available */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_NAME, + pinName, + MAX_PATH, + NULL); + + if (result == paNoError && wcslen(pinName)>0) + { + wcsncpy(pin->friendlyName, pinName, MAX_PATH); + } + else +#endif + { + result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH); + } + + if (wcslen(pin->friendlyName) == 0) + { + wcscpy(pin->friendlyName, L"Output"); + } +#ifdef UNICODE + PA_DEBUG(("PinNew: Pin name '%s'\n", pin->friendlyName)); +#else + PA_DEBUG(("PinNew: Pin name '%S'\n", pin->friendlyName)); +#endif + } + + /* Set endpoint pin ID (this is the topology INPUT pin, since portmixer will always traverse the + filter in audio streaming direction, see http://msdn.microsoft.com/en-us/library/windows/hardware/ff536331(v=vs.85).aspx + for more information) + */ + pin->endpointPinId = pc->Pin; + } + else + { + unsigned muxCount = 0; + int muxPos = 0; + /* Max 64 multiplexer inputs... sanity check :) */ + for (i = 0; i < 64; ++i) + { + ULONG muxNodeIdTest = (unsigned)-1; + PA_DEBUG(("PinNew: Checking for input endpoint pin id (%d)...\n", i)); + + endpointPinId = GetConnectedPin(pc->Pin, + FALSE, + pin->parentFilter->topologyFilter, + (int)i, + NULL, + &muxNodeIdTest); + + + if (endpointPinId == KSFILTER_NODE) + { + /* We're done */ + PA_DEBUG(("PinNew: Done with inputs.\n", endpointPinId)); + break; + } + else + { + /* The "endpointPinId" is what WASAPI looks at for pin names */ + GUID category = {0}; + + PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId)); + + /* Get pin category information */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_CATEGORY, + &category, + sizeof(GUID), + NULL); + + if (result == paNoError) + { + if (muxNodeIdTest == (unsigned)-1) + { + /* Ok, try pin name, and favor that if available */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_NAME, + pin->friendlyName, + MAX_PATH, + NULL); + + if (result != paNoError) + { + result = GetNameFromCategory(&category, TRUE, pin->friendlyName, MAX_PATH); + } + break; + } + else + { + result = GetNameFromCategory(&category, TRUE, NULL, 0); + + if (result == paNoError) + { + ++muxCount; + } + } + } + else + { + PA_DEBUG(("PinNew: Failed to get pin category")); + } + } + } + + if (muxCount == 0) + { + pin->endpointPinId = endpointPinId; + /* Make sure we get a name for the pin */ + if (wcslen(pin->friendlyName) == 0) + { + wcscpy(pin->friendlyName, kInputName); + } +#ifdef UNICODE + PA_DEBUG(("PinNew: Input friendly name '%s'\n", pin->friendlyName)); +#else + PA_DEBUG(("PinNew: Input friendly name '%S'\n", pin->friendlyName)); +#endif + } + else // muxCount > 0 + { + PA_DEBUG(("PinNew: Setting up %u inputs\n", muxCount)); + + /* Now we redo the operation once known how many multiplexer positions there are */ + pin->inputs = (PaWinWdmMuxedInput**)PaUtil_AllocateMemory(muxCount * sizeof(PaWinWdmMuxedInput*)); + if (pin->inputs == NULL) + { + FilterRelease(pin->parentFilter->topologyFilter); + result = paInsufficientMemory; + goto error; + } + pin->inputCount = muxCount; + + for (i = 0; i < muxCount; ++muxPos) + { + PA_DEBUG(("PinNew: Setting up input %u...\n", i)); + + if (pin->inputs[i] == NULL) + { + pin->inputs[i] = (PaWinWdmMuxedInput*)PaUtil_AllocateMemory(sizeof(PaWinWdmMuxedInput)); + if (pin->inputs[i] == NULL) + { + FilterRelease(pin->parentFilter->topologyFilter); + result = paInsufficientMemory; + goto error; + } + } + + endpointPinId = GetConnectedPin(pc->Pin, + FALSE, + pin->parentFilter->topologyFilter, + muxPos, + &pin->inputs[i]->muxPinId, + &pin->inputs[i]->muxNodeId); + + if (endpointPinId != KSFILTER_NODE) + { + /* The "endpointPinId" is what WASAPI looks at for pin names */ + GUID category = {0}; + + /* Set input endpoint ID */ + pin->inputs[i]->endpointPinId = endpointPinId; + + /* Get pin category information */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_CATEGORY, + &category, + sizeof(GUID), + NULL); + + if (result == paNoError) + { + /* Try pin name first, and if that is not defined, use category instead */ + result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle, + endpointPinId, + &KSPROPSETID_Pin, + KSPROPERTY_PIN_NAME, + pin->inputs[i]->friendlyName, + MAX_PATH, + NULL); + + if (result != paNoError) + { + result = GetNameFromCategory(&category, TRUE, pin->inputs[i]->friendlyName, MAX_PATH); + if (result != paNoError) + { + /* Only specify name, let name hash in ScanDeviceInfos fix postfix enumerators */ + wcscpy(pin->inputs[i]->friendlyName, kInputName); + } + } +#ifdef UNICODE + PA_DEBUG(("PinNew: Input (%u) friendly name '%s'\n", i, pin->inputs[i]->friendlyName)); +#else + PA_DEBUG(("PinNew: Input (%u) friendly name '%S'\n", i, pin->inputs[i]->friendlyName)); +#endif + ++i; + } + } + else + { + /* Should never come here! */ + assert(FALSE); + } + } + } + } + } + } + PaUtil_FreeMemory(pc); + } + } + else + { + PA_DEBUG(("PinNew: No topology pin id found. Bad...\n")); + /* No TOPO pin id ??? This is bad. Ok, so we just say it is an input or output... */ + wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName); + } + } + + /* Release topology filter if it has been used */ + if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL) + { + PA_DEBUG(("PinNew: Releasing topology filter...\n")); + FilterRelease(pin->parentFilter->topologyFilter); + } + /* Success */ *error = paNoError; PA_DEBUG(("Pin created successfully\n")); @@ -892,12 +1940,19 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa return pin; error: + PA_DEBUG(("PinNew: Error %d\n", result)); /* Error cleanup */ + PaUtil_FreeMemory( item ); if( pin ) { + if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL) + { + FilterRelease(pin->parentFilter->topologyFilter); + } + PaUtil_FreeMemory( pin->pinConnect ); PaUtil_FreeMemory( pin->dataRangesItem ); PaUtil_FreeMemory( pin ); @@ -912,6 +1967,7 @@ Safely free all resources associated with the pin */ static void PinFree(PaWinWdmPin* pin) { + unsigned i; PA_LOGE_; if( pin ) { @@ -924,6 +1980,14 @@ static void PinFree(PaWinWdmPin* pin) { PaUtil_FreeMemory( pin->dataRangesItem ); } + if( pin->inputs ) + { + for (i = 0; i < pin->inputCount; ++i) + { + PaUtil_FreeMemory( pin->inputs[i] ); + } + PaUtil_FreeMemory( pin->inputs ); + } PaUtil_FreeMemory( pin ); } PA_LOGL_; @@ -957,22 +2021,21 @@ Set the state of this (instantiated) pin */ static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state) { - PaError result; + PaError result = paNoError; + KSPROPERTY prop; PA_LOGE_; + prop.Set = KSPROPSETID_Connection; + prop.Id = KSPROPERTY_CONNECTION_STATE; + prop.Flags = KSPROPERTY_TYPE_SET; + 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); + result = WdmSyncIoctl(pin->handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSPROPERTY), &state, sizeof(KSSTATE), NULL); + PA_LOGL_; return result; } @@ -1000,41 +2063,56 @@ static PaError PinInstantiate(PaWinWdmPin* pin) &pin->handle ); - PA_DEBUG(("Pin create result = %x\n",createResult)); + PA_DEBUG(("Pin create result = 0x%08x\n",createResult)); if( createResult != ERROR_SUCCESS ) { FilterRelease(pin->parentFilter); pin->handle = NULL; - return paInvalidDevice; + switch (createResult) + { + case ERROR_INVALID_PARAMETER: + /* First case when pin actually don't support the format */ + return paSampleFormatNotSupported; + case ERROR_BAD_COMMAND: + /* Case when pin is occupied (by another application) */ + return paDeviceUnavailable; + default: + /* All other cases */ + return paInvalidDevice; + } } - result = WdmGetPropertySimple( - pin->handle, - &KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_ALLOCATORFRAMING, - &ksaf, - sizeof(ksaf), - NULL, - 0); - - if( result != paNoError ) + if (pin->parentFilter->devInfo.streamingType == Type_kWaveCyclic) { + /* Framing size query only valid for WaveCyclic devices */ result = WdmGetPropertySimple( pin->handle, &KSPROPSETID_Connection, - KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, - &ksafex, - sizeof(ksafex), + KSPROPERTY_CONNECTION_ALLOCATORFRAMING, + &ksaf, + sizeof(ksaf), NULL, 0); - if( result == paNoError ) + + if( result != paNoError ) { - pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize; + 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; } - } - else - { - pin->frameSize = ksaf.FrameSize; } PA_LOGL_; @@ -1042,30 +2120,6 @@ static PaError PinInstantiate(PaWinWdmPin* pin) 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; @@ -1107,65 +2161,285 @@ static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format unsigned long count; GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) ); PaError result = paInvalidDevice; + const WAVEFORMATEXTENSIBLE* pFormatExt = (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) ? (const WAVEFORMATEXTENSIBLE*)format : 0; PA_LOGE_; - if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE ) + if( pFormatExt != 0 ) { - guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat; + guid = pFormatExt->SubFormat; } dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges; - for(count = 0; countdataRangesItem->Count; count++) + for(count = 0; + countdataRangesItem->Count; + count++, + dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize)) /* Need to update dataRange here, due to 'continue' !! */ { - if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) || - ( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) )) + /* Check major format*/ + if (!(IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_AUDIO) || + IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_WILDCARD))) { - /* This is an audio or wildcard datarange... */ - if(( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) || - ( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) )) + continue; + } + + /* This is an audio or wildcard datarange... */ + if (! (IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_WILDCARD) || + IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_PCM) || + IsEqualGUID(&(dataRange->DataRange.SubFormat), &guid) )) + { + continue; + } + + /* Check specifier... */ + if (! (IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WILDCARD) || + IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) ) + { + continue; + } + + 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 != (ULONG)-1 && + dataRange->MaximumChannels < format->nChannels ) + { + result = paInvalidChannelCount; + continue; + } + + if (pFormatExt != 0) + { + if ( dataRange->MinimumBitsPerSample > pFormatExt->Samples.wValidBitsPerSample ) { - 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; - } + result = paSampleFormatNotSupported; + continue; + } + if ( dataRange->MaximumBitsPerSample < pFormatExt->Samples.wValidBitsPerSample ) + { + result = paSampleFormatNotSupported; + continue; } } - dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize); + else + { + 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! */ + result = paNoError; + break; + } + + PA_LOGL_; + return result; +} + +static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult) +{ + PaError result = paNoError; + KSPROPERTY propIn; + + propIn.Set = KSPROPSETID_RtAudio; + propIn.Id = 8; /* = KSPROPERTY_RTAUDIO_QUERY_NOTIFICATION_SUPPORT */ + propIn.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY, + &propIn, + sizeof(KSPROPERTY), + pbResult, + sizeof(BOOL), + NULL); + + if (result != paNoError) + { + PA_DEBUG(("Failed PinQueryNotificationSupport\n")); + } + + return result; + +} + +static PaError PinGetBufferWithNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier) +{ + PaError result = paNoError; + KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION propIn; + KSRTAUDIO_BUFFER propOut; + + propIn.BaseAddress = 0; + propIn.NotificationCount = 2; + propIn.RequestedBufferSize = *pRequestedBufSize; + propIn.Property.Set = KSPROPSETID_RtAudio; + propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER_WITH_NOTIFICATION; + propIn.Property.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY, + &propIn, + sizeof(KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION), + &propOut, + sizeof(KSRTAUDIO_BUFFER), + NULL); + + if (result == paNoError) + { + *pBuffer = propOut.BufferAddress; + *pRequestedBufSize = propOut.ActualBufferSize; + *pbCallMemBarrier = propOut.CallMemoryBarrier; + } + else + { + PA_DEBUG(("Failed to get buffer with notification\n")); + } + + return result; +} + +static PaError PinGetBufferWithoutNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier) +{ + PaError result = paNoError; + KSRTAUDIO_BUFFER_PROPERTY propIn; + KSRTAUDIO_BUFFER propOut; + + propIn.BaseAddress = NULL; + propIn.RequestedBufferSize = *pRequestedBufSize; + propIn.Property.Set = KSPROPSETID_RtAudio; + propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER; + propIn.Property.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY, + &propIn, + sizeof(KSRTAUDIO_BUFFER_PROPERTY), + &propOut, + sizeof(KSRTAUDIO_BUFFER), + NULL); + + if (result == paNoError) + { + *pBuffer = propOut.BufferAddress; + *pRequestedBufSize = propOut.ActualBufferSize; + *pbCallMemBarrier = propOut.CallMemoryBarrier; + } + else + { + PA_DEBUG(("Failed to get buffer without notification\n")); + } + + return result; +} + +/* greatest common divisor - PGCD in French */ +static unsigned long PaWinWDMGCD( unsigned long a, unsigned long b ) +{ + return (b==0) ? a : PaWinWDMGCD( b, a%b); +} + + +/* This function will handle getting the cyclic buffer from a WaveRT driver. Certain WaveRT drivers needs to have +requested buffer size on multiples of 128 bytes: + +*/ +static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier) +{ + PaError result = paNoError; + + while (1) + { + if (pPin->pinKsSubType != SubType_kPolled) + { + /* In case of unknown (or notification), we try both modes */ + result = PinGetBufferWithNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier); + if (result == paNoError) + { + PA_DEBUG(("PinGetBuffer: SubType_kNotification\n")); + pPin->pinKsSubType = SubType_kNotification; + break; + } + } + + result = PinGetBufferWithoutNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier); + if (result == paNoError) + { + PA_DEBUG(("PinGetBuffer: SubType_kPolled\n")); + pPin->pinKsSubType = SubType_kPolled; + break; + } + + /* Check if requested size is on a 128 byte boundary */ + if (((*pRequestedBufSize) % 128UL) == 0) + { + PA_DEBUG(("Buffer size on 128 byte boundary, still fails :(\n")); + /* Ok, can't do much more */ + break; + } + else + { + /* Compute LCM so we know which sizes are on a 128 byte boundary */ + const unsigned gcd = PaWinWDMGCD(128UL, pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign); + const unsigned lcm = (128UL * pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign) / gcd; + DWORD dwOldSize = *pRequestedBufSize; + + /* Align size to (next larger) LCM byte boundary, and then we try again. Note that LCM is not necessarily a + power of 2. */ + *pRequestedBufSize = ((*pRequestedBufSize + lcm - 1) / lcm) * lcm; + + PA_DEBUG(("Adjusting buffer size from %u to %u bytes (128 byte boundary, LCM=%u)\n", dwOldSize, *pRequestedBufSize, lcm)); + } + } + + return result; +} + +static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin) +{ + PaError result = paNoError; + KSRTAUDIO_HWREGISTER_PROPERTY propIn; + KSRTAUDIO_HWREGISTER propOut; + + PA_LOGE_; + + propIn.BaseAddress = NULL; + propIn.Property.Set = KSPROPSETID_RtAudio; + propIn.Property.Id = KSPROPERTY_RTAUDIO_POSITIONREGISTER; + propIn.Property.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY, + &propIn, + sizeof(KSRTAUDIO_HWREGISTER_PROPERTY), + &propOut, + sizeof(KSRTAUDIO_HWREGISTER), + NULL); + + if (result == paNoError) + { + pPin->positionRegister = (ULONG*)propOut.Register; + } + else + { + PA_DEBUG(("Failed to register position register\n")); } PA_LOGL_; @@ -1173,16 +2447,151 @@ static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format return result; } -/** - * Create a new filter object - */ -static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error) +static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle) { - PaWinWdmFilter* filter; - PaError result; - int pinId; - int valid; + PaError result = paNoError; + KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop; + PA_LOGE_; + + prop.NotificationEvent = handle; + prop.Property.Set = KSPROPSETID_RtAudio; + prop.Property.Id = KSPROPERTY_RTAUDIO_REGISTER_NOTIFICATION_EVENT; + prop.Property.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, + IOCTL_KS_PROPERTY, + &prop, + sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY), + &prop, + sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY), + NULL); + + if (result != paNoError) { + PA_DEBUG(("Failed to register notification handle 0x%08X\n", handle)); + } + + PA_LOGL_; + + return result; +} + +static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle) +{ + PaError result = paNoError; + KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop; + + PA_LOGE_; + + if (handle != NULL) + { + prop.NotificationEvent = handle; + prop.Property.Set = KSPROPSETID_RtAudio; + prop.Property.Id = KSPROPERTY_RTAUDIO_UNREGISTER_NOTIFICATION_EVENT; + prop.Property.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, + IOCTL_KS_PROPERTY, + &prop, + sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY), + &prop, + sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY), + NULL); + + if (result != paNoError) { + PA_DEBUG(("Failed to unregister notification handle 0x%08X\n", handle)); + } + } + PA_LOGL_; + + return result; +} + +static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay) +{ + PaError result = paNoError; + KSPROPERTY propIn; + KSRTAUDIO_HWLATENCY propOut; + + PA_LOGE_; + + propIn.Set = KSPROPSETID_RtAudio; + propIn.Id = KSPROPERTY_RTAUDIO_HWLATENCY; + propIn.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY, + &propIn, + sizeof(KSPROPERTY), + &propOut, + sizeof(KSRTAUDIO_HWLATENCY), + NULL); + + if (result == paNoError) + { + *pFifoSize = propOut.FifoSize; + *pChipsetDelay = propOut.ChipsetDelay; + *pCodecDelay = propOut.CodecDelay; + } + else + { + PA_DEBUG(("Failed to retrieve hardware FIFO size!\n")); + } + + PA_LOGL_; + + return result; +} + +/* This one is used for WaveRT */ +static PaError PinGetAudioPositionDirect(PaWinWdmPin* pPin, ULONG* pPosition) +{ + *pPosition = (*pPin->positionRegister); + return paNoError; +} + +/* This one also, but in case the driver hasn't implemented memory mapped access to the position register */ +static PaError PinGetAudioPositionViaIOCTL(PaWinWdmPin* pPin, ULONG* pPosition) +{ + PaError result = paNoError; + KSPROPERTY propIn; + KSAUDIO_POSITION propOut; + + PA_LOGE_; + + propIn.Set = KSPROPSETID_Audio; + propIn.Id = KSPROPERTY_AUDIO_POSITION; + propIn.Flags = KSPROPERTY_TYPE_GET; + + result = WdmSyncIoctl(pPin->handle, + IOCTL_KS_PROPERTY, + &propIn, sizeof(KSPROPERTY), + &propOut, sizeof(KSAUDIO_POSITION), + NULL); + + if (result == paNoError) + { + *pPosition = (ULONG)(propOut.PlayOffset); + } + else + { + PA_DEBUG(("Failed to get audio position!\n")); + } + + PA_LOGL_; + + return result; + +} + +/***********************************************************************************************/ + +/** +* Create a new filter object. +*/ +static PaWinWdmFilter* FilterNew( PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error ) +{ + PaWinWdmFilter* filter = 0; + PaError result; /* Allocate the new filter object */ filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) ); @@ -1192,14 +2601,24 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError goto error; } + PA_DEBUG(("FilterNew: Creating filter '%S'\n", friendlyName)); + + /* Set type flag */ + filter->devInfo.streamingType = type; + + /* Store device node */ + filter->deviceNode = devNode; + /* Zero the filter object - done by AllocateMemory */ /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */ /* Copy the filter name */ - _tcsncpy(filter->filterName, filterName, MAX_PATH); + wcsncpy(filter->devInfo.filterPath, filterName, MAX_PATH); /* Copy the friendly name */ - _tcsncpy(filter->friendlyName, friendlyName, MAX_PATH); + wcsncpy(filter->friendlyName, friendlyName, MAX_PATH); + + PA_DEBUG(("FilterNew: Opening filter...\n", friendlyName)); /* Open the filter handle */ result = FilterUse(filter); @@ -1216,14 +2635,106 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError &KSPROPSETID_Pin, KSPROPERTY_PIN_CTYPES, &filter->pinCount, - sizeof(filter->pinCount) - ); + sizeof(filter->pinCount), + NULL); if( result != paNoError) { goto error; } + /* Get connections & nodes for filter */ + result = WdmGetPropertyMulti( + filter->handle, + &KSPROPSETID_Topology, + KSPROPERTY_TOPOLOGY_CONNECTIONS, + &filter->connections); + + if( result != paNoError) + { + goto error; + } + + result = WdmGetPropertyMulti( + filter->handle, + &KSPROPSETID_Topology, + KSPROPERTY_TOPOLOGY_NODES, + &filter->nodes); + + if( result != paNoError) + { + goto error; + } + + /* For debugging purposes */ + DumpConnectionsAndNodes(filter); + + /* Get product GUID (it might not be supported) */ + { + KSCOMPONENTID compId; + if (WdmGetPropertySimple(filter->handle, &KSPROPSETID_General, KSPROPERTY_GENERAL_COMPONENTID, &compId, sizeof(KSCOMPONENTID), NULL, 0) == paNoError) + { + filter->devInfo.deviceProductGuid = compId.Product; + } + } + + /* This section is not executed for topology filters */ + if (type != Type_kNotUsed) + { + /* Initialize the pins */ + result = FilterInitializePins(filter); + + if( result != paNoError) + { + goto error; + } + } + + /* Close the filter handle for now + * It will be opened later when needed */ + FilterRelease(filter); + + *error = paNoError; + return filter; + +error: + PA_DEBUG(("FilterNew: Error %d\n", result)); + /* + Error cleanup + */ + FilterFree(filter); + + *error = result; + return NULL; +} + +/** +* Add reference to filter +*/ +static void FilterAddRef( PaWinWdmFilter* filter ) +{ + if (filter != 0) + { + filter->filterRefCount++; + } +} + + +/** +* Initialize the pins of the filter. This is separated from FilterNew because this might fail if there is another +* process using the pin(s). +*/ +PaError FilterInitializePins( PaWinWdmFilter* filter ) +{ + PaError result = paNoError; + int pinId; + + if (filter->devInfo.streamingType == Type_kNotUsed) + return paNoError; + + if (filter->pins != NULL) + return paNoError; + /* Allocate pointer array to hold the pins */ filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount ); if( !filter->pins ) @@ -1233,11 +2744,6 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError } /* 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 */ @@ -1248,83 +2754,75 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError 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; - } + ++filter->validPinCount; } } - if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0)) + if (filter->validPinCount == 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; + return paNoError; error: - /* - Error cleanup - */ - if( filter ) + + if (filter->pins) { - for( pinId = 0; pinId < filter->pinCount; pinId++ ) - PinFree(filter->pins[pinId]); + for (pinId = 0; pinId < filter->pinCount; ++pinId) + { + if (filter->pins[pinId]) + { + PaUtil_FreeMemory(filter->pins[pinId]); + filter->pins[pinId] = 0; + } + } PaUtil_FreeMemory( filter->pins ); - if( filter->handle ) - CloseHandle( filter->handle ); - PaUtil_FreeMemory( filter ); + filter->pins = 0; } - *error = result; - return NULL; + + return result; } + /** - * Free a previously created filter - */ +* 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->filterRefCount > 0) + { + /* Ok, a stream has a ref count to this filter */ + return; + } + + if (filter->topologyFilter) + { + FilterFree(filter->topologyFilter); + filter->topologyFilter = 0; + } + if ( filter->pins ) + { + for( pinId = 0; pinId < filter->pinCount; pinId++ ) + PinFree(filter->pins[pinId]); + PaUtil_FreeMemory( filter->pins ); + filter->pins = 0; + } + if( filter->connections ) + { + PaUtil_FreeMemory(filter->connections); + filter->connections = 0; + } + if( filter->nodes ) + { + PaUtil_FreeMemory(filter->nodes); + filter->nodes = 0; + } if( filter->handle ) CloseHandle( filter->handle ); PaUtil_FreeMemory( filter ); @@ -1333,8 +2831,8 @@ static void FilterFree(PaWinWdmFilter* filter) } /** - * Reopen the filter handle if necessary so it can be used - **/ +* Reopen the filter handle if necessary so it can be used +**/ static PaError FilterUse(PaWinWdmFilter* filter) { assert( filter ); @@ -1343,8 +2841,8 @@ static PaError FilterUse(PaWinWdmFilter* filter) if( filter->handle == NULL ) { /* Open the filter */ - filter->handle = CreateFile( - filter->filterName, + filter->handle = CreateFileW( + filter->devInfo.filterPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, @@ -1363,14 +2861,20 @@ static PaError FilterUse(PaWinWdmFilter* filter) } /** - * Release the filter handle if nobody is using it - **/ +* Release the filter handle if nobody is using it +**/ static void FilterRelease(PaWinWdmFilter* filter) { assert( filter ); assert( filter->usageCount > 0 ); PA_LOGE_; + /* Check first topology filter, if used */ + if (filter->topologyFilter != NULL && filter->topologyFilter->handle != NULL) + { + FilterRelease(filter->topologyFilter); + } + filter->usageCount--; if( filter->usageCount == 0 ) { @@ -1384,200 +2888,85 @@ static void FilterRelease(PaWinWdmFilter* filter) } /** - * 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 - * Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which - * have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these - * devices initialise a PaWinWdmFilter structure by calling our NewFilter() - * function. We enumerate devices twice, once to count how many there are, - * and once to initialize the PaWinWdmFilter structures. - */ -static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) +* Create a render or playback pin using the supplied format +**/ +static PaWinWdmPin* FilterCreatePin(PaWinWdmFilter* filter, + int pinId, + const WAVEFORMATEX* wfex, + PaError* error) { PaError result = paNoError; + PaWinWdmPin* pin = NULL; + assert( filter ); + assert( pinId < filter->pinCount ); + pin = filter->pins[pinId]; + assert( pin ); + result = PinSetFormat(pin,wfex); + if( result == paNoError ) + { + result = PinInstantiate(pin); + } + *error = result; + return result == paNoError ? pin : 0; +} + +static const wchar_t kUsbPrefix[] = L"\\\\?\\USB"; + +static BOOL IsUSBDevice(const wchar_t* devicePath) +{ + /* Alex Lessard pointed out that different devices might present the device path with + lower case letters. */ + return (_wcsnicmp(devicePath, kUsbPrefix, sizeof(kUsbPrefix)/sizeof(kUsbPrefix[0]) ) == 0); +} + +/* This should make it more language tolerant, I hope... */ +static const wchar_t kUsbNamePrefix[] = L"USB Audio"; + +static BOOL IsNameUSBAudioDevice(const wchar_t* friendlyName) +{ + return (_wcsnicmp(friendlyName, kUsbNamePrefix, sizeof(kUsbNamePrefix)/sizeof(kUsbNamePrefix[0])) == 0); +} + +typedef enum _tag_EAlias +{ + Alias_kRender = (1<<0), + Alias_kCapture = (1<<1), + Alias_kRealtime = (1<<2), +} EAlias; + +/* Trim whitespace from string */ +static void TrimString(wchar_t* str, size_t length) +{ + wchar_t* s = str; + wchar_t* e = 0; + + /* Find start of string */ + while (iswspace(*s)) ++s; + e=s+min(length,wcslen(s))-1; + + /* Find end of string */ + while(e>s && iswspace(*e)) --e; + ++e; + + length = e - s; + memmove(str, s, length * sizeof(wchar_t)); + str[length] = 0; +} + +/** +* Build the list of available filters +* Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which +* have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these +* devices initialise a PaWinWdmFilter structure by calling our NewFilter() +* function. We enumerate devices twice, once to count how many there are, +* and once to initialize the PaWinWdmFilter structures. +* +* Vista and later: Also check KSCATEGORY_REALTIME for WaveRT devices. +*/ +//PaError BuildFilterList( PaWinWdmHostApiRepresentation* wdmHostApi, int* noOfPaDevices ) +PaWinWdmFilter** BuildFilterList( int* pFilterCount, int* pNoOfPaDevices, PaError* pResult ) +{ + PaWinWdmFilter** ppFilters = NULL; HDEVINFO handle = NULL; int device; int invalidDevices; @@ -1588,26 +2977,32 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) 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; + SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)interfaceDetailsArray; + const GUID* category = (const GUID*)&KSCATEGORY_AUDIO; + const GUID* alias_render = (const GUID*)&KSCATEGORY_RENDER; + const GUID* alias_capture = (const GUID*)&KSCATEGORY_CAPTURE; + const GUID* category_realtime = (const GUID*)&KSCATEGORY_REALTIME; + DWORD aliasFlags; + PaWDMKSType streamingType; + int filterCount = 0; + int noOfPaDevices = 0; PA_LOGE_; - devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + assert(pFilterCount != NULL); + assert(pNoOfPaDevices != NULL); + assert(pResult != NULL); + + devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); + *pFilterCount = 0; + *pNoOfPaDevices = 0; /* Open a handle to search for devices (filters) */ handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if( handle == NULL ) + if( handle == INVALID_HANDLE_VALUE ) { - return paUnanticipatedHostError; + *pResult = paUnanticipatedHostError; + return NULL; } PA_DEBUG(("Setup called\n")); @@ -1625,7 +3020,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) break; /* No more devices */ /* Check this one has the render or capture alias */ - hasAlias = 0; + aliasFlags = 0; noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData); PA_DEBUG(("noError = %d\n",noError)); if(noError) @@ -1633,7 +3028,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) { PA_DEBUG(("Device %d has render alias\n",device)); - hasAlias |= 1; /* Has render alias */ + aliasFlags |= Alias_kRender; /* Has render alias */ } else { @@ -1646,29 +3041,29 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) { PA_DEBUG(("Device %d has capture alias\n",device)); - hasAlias |= 2; /* Has capture alias */ + aliasFlags |= Alias_kCapture; /* Has capture alias */ } else { PA_DEBUG(("Device %d has no capture alias\n",device)); } } - if(!hasAlias) + if(!aliasFlags) invalidDevices++; /* This was not a valid capture or render audio device */ - } /* Remember how many there are */ - wdmHostApi->filterCount = device-invalidDevices; + 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 ) + ppFilters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * filterCount); + if( ppFilters == 0 ) { if(handle != NULL) SetupDiDestroyDeviceInfoList(handle); - return paInsufficientMemory; + *pResult = paInsufficientMemory; + return NULL; } /* Now create filter objects for each interface found */ @@ -1681,20 +3076,21 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) aliasData.Reserved = 0; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); devInfoData.Reserved = 0; + streamingType = Type_kWaveCyclic; noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData); if( !noError ) break; /* No more devices */ /* Check this one has the render or capture alias */ - hasAlias = 0; + aliasFlags = 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 */ + aliasFlags |= Alias_kRender; /* Has render alias */ } } noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData); @@ -1703,48 +3099,114 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED))) { PA_DEBUG(("Device %d has capture alias\n",device)); - hasAlias |= 2; /* Has capture alias */ + aliasFlags |= Alias_kCapture; /* Has capture alias */ } } - if(!hasAlias) + if(!aliasFlags) + { continue; /* This was not a valid capture or render audio device */ + } + else + { + /* Check if filter is WaveRT, if not it is a WaveCyclic */ + noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,category_realtime,&aliasData); + if (noError) + { + PA_DEBUG(("Device %d has realtime alias\n",device)); + aliasFlags |= Alias_kRealtime; + streamingType = Type_kWaveRT; + } + } - noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData); + noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData); if( noError ) { + DWORD type; + WCHAR friendlyName[MAX_PATH] = {0}; + DWORD sizeFriendlyName; + PaWinWdmFilter* newFilter = 0; + + PaError result = paNoError; /* 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) + + if (IsEarlierThanVista() && IsUSBDevice(devInterfaceDetails->DevicePath)) { - 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 + /* XP and USB audio device needs to look elsewhere, otherwise it'll only be a "USB Audio Device". Not + very literate. */ + if (!SetupDiGetDeviceRegistryPropertyW(handle, + &devInfoData, + SPDRP_LOCATION_INFORMATION, + &type, + (BYTE*)friendlyName, + sizeof(friendlyName), + NULL)) { friendlyName[0] = 0; } } - newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result); + + if (friendlyName[0] == 0 || IsNameUSBAudioDevice(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 hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE); + if(hkey!=INVALID_HANDLE_VALUE) + { + noError = RegQueryValueExW(hkey,L"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; + } + } + } + + TrimString(friendlyName, sizeFriendlyName); + + newFilter = FilterNew(streamingType, + devInfoData.DevInst, + devInterfaceDetails->DevicePath, + friendlyName, + &result); + if( result == paNoError ) { - PA_DEBUG(("Filter created\n")); - wdmHostApi->filters[slot] = newFilter; + int pin; + unsigned filterIOs = 0; + + /* Increment number of "devices" */ + for (pin = 0; pin < newFilter->pinCount; ++pin) + { + PaWinWdmPin* pPin = newFilter->pins[pin]; + if (pPin == NULL) + continue; + + filterIOs += max(1, pPin->inputCount); + } + + noOfPaDevices += filterIOs; + + PA_DEBUG(("Filter (%s) created with %d valid pins (total I/Os: %u)\n", ((newFilter->devInfo.streamingType==Type_kWaveRT)?"WaveRT":"WaveCyclic"), newFilter->validPinCount, filterIOs)); + + assert(slot < filterCount); + + ppFilters[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--; + * we must reduce the count by one */ + filterCount--; } } } @@ -1753,21 +3215,467 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi) if(handle != NULL) SetupDiDestroyDeviceInfoList(handle); + *pFilterCount = filterCount; + *pNoOfPaDevices = noOfPaDevices; + + return ppFilters; +} + +typedef struct PaNameHashIndex +{ + unsigned index; + unsigned count; + ULONG hash; + struct PaNameHashIndex *next; +} PaNameHashIndex; + +typedef struct PaNameHashObject +{ + PaNameHashIndex* list; + PaUtilAllocationGroup* allocGroup; +} PaNameHashObject; + +static ULONG GetNameHash(const wchar_t* str, const BOOL input) +{ + /* This is to make sure that a name that exists as both input & output won't get the same hash value */ + const ULONG fnv_prime = (input ? 0x811C9DD7 : 0x811FEB0B); + ULONG hash = 0; + for(; *str != 0; str++) + { + hash *= fnv_prime; + hash ^= (*str); + } + assert(hash != 0); + return hash; +} + +static PaError CreateHashEntry(PaNameHashObject* obj, const wchar_t* name, const BOOL input) +{ + ULONG hash = GetNameHash(name, input); + PaNameHashIndex * pLast = NULL; + PaNameHashIndex * p = obj->list; + while (p != 0) + { + if (p->hash == hash) + { + break; + } + pLast = p; + p = p->next; + } + if (p == NULL) + { + p = (PaNameHashIndex*)PaUtil_GroupAllocateMemory(obj->allocGroup, sizeof(PaNameHashIndex)); + if (p == NULL) + { + return paInsufficientMemory; + } + p->hash = hash; + p->count = 1; + if (pLast != 0) + { + assert(pLast->next == 0); + pLast->next = p; + } + if (obj->list == 0) + { + obj->list = p; + } + } + else + { + ++p->count; + } return paNoError; } +static PaError InitNameHashObject(PaNameHashObject* obj, PaWinWdmFilter* pFilter) +{ + int i; + + obj->allocGroup = PaUtil_CreateAllocationGroup(); + if (obj->allocGroup == NULL) + { + return paInsufficientMemory; + } + + for (i = 0; i < pFilter->pinCount; ++i) + { + unsigned m; + PaWinWdmPin* pin = pFilter->pins[i]; + + if (pin == NULL) + continue; + + for (m = 0; m < max(1, pin->inputCount); ++m) + { + const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT); + const wchar_t* name = (pin->inputs == NULL) ? pin->friendlyName : pin->inputs[m]->friendlyName; + + PaError result = CreateHashEntry(obj, name, isInput); + + if (result != paNoError) + { + return result; + } + } + } + return paNoError; +} + +static void DeinitNameHashObject(PaNameHashObject* obj) +{ + assert(obj != 0); + PaUtil_FreeAllAllocations(obj->allocGroup); + PaUtil_DestroyAllocationGroup(obj->allocGroup); + memset(obj, 0, sizeof(PaNameHashObject)); +} + +static unsigned GetNameIndex(PaNameHashObject* obj, const wchar_t* name, const BOOL input) +{ + ULONG hash = GetNameHash(name, input); + PaNameHashIndex* p = obj->list; + while (p != NULL) + { + if (p->hash == hash) + { + if (p->count > 1) + { + return (++p->index); + } + else + { + return 0; + } + } + + p = p->next; + } + // Should never get here!! + assert(FALSE); + return 0; +} + +static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount ) +{ + PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; + PaError result = paNoError; + PaWinWdmFilter** ppFilters = 0; + PaWinWDMScanDeviceInfosResults *outArgument = 0; + int filterCount = 0; + int totalDeviceCount = 0; + int idxDevice = 0; + + ppFilters = BuildFilterList( &filterCount, &totalDeviceCount, &result ); + if( result != paNoError ) + { + goto error; + } + + if( totalDeviceCount > 0 ) + { + PaWinWdmDeviceInfo *deviceInfoArray = 0; + int idxFilter; + int i; + + /* Allocate the out param for all the info we need */ + outArgument = (PaWinWDMScanDeviceInfosResults *) PaUtil_GroupAllocateMemory( + wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults) ); + if( !outArgument ) + { + result = paInsufficientMemory; + goto error; + } + + outArgument->defaultInputDevice = paNoDevice; + outArgument->defaultOutputDevice = paNoDevice; + + outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + wdmHostApi->allocations, sizeof(PaDeviceInfo*) * totalDeviceCount ); + if( !outArgument->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory( + wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * totalDeviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + /* Make sure all items in array */ + for( i = 0 ; i < totalDeviceCount; ++i ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = 0; + outArgument->deviceInfos[ i ] = deviceInfo; + } + + idxDevice = 0; + for (idxFilter = 0; idxFilter < filterCount; ++idxFilter) + { + PaNameHashObject nameHash = {0}; + PaWinWdmFilter* pFilter = ppFilters[idxFilter]; + if( pFilter == NULL ) + continue; + + if (InitNameHashObject(&nameHash, pFilter) != paNoError) + { + DeinitNameHashObject(&nameHash); + continue; + } + + for (i = 0; i < pFilter->pinCount; ++i) + { + unsigned m; + ULONG nameIndex = 0; + ULONG nameIndexHash = 0; + PaWinWdmPin* pin = pFilter->pins[i]; + + if (pin == NULL) + continue; + + for (m = 0; m < max(1, pin->inputCount); ++m) + { + PaWinWdmDeviceInfo *wdmDeviceInfo = (PaWinWdmDeviceInfo *)outArgument->deviceInfos[idxDevice]; + PaDeviceInfo *deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo; + wchar_t localCompositeName[MAX_PATH]; + unsigned nameIndex = 0; + const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT); + + wdmDeviceInfo->filter = pFilter; + + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = wdmDeviceInfo->compositeName; + /* deviceInfo->hostApiSpecificDeviceInfo = &pFilter->devInfo; */ + + wdmDeviceInfo->pin = pin->pinId; + + /* Get the name of the "device" */ + if (pin->inputs == NULL) + { + wcsncpy(localCompositeName, pin->friendlyName, MAX_PATH); + wdmDeviceInfo->muxPosition = -1; + wdmDeviceInfo->endpointPinId = pin->endpointPinId; + } + else + { + PaWinWdmMuxedInput* input = pin->inputs[m]; + wcsncpy(localCompositeName, input->friendlyName, MAX_PATH); + wdmDeviceInfo->muxPosition = (int)m; + wdmDeviceInfo->endpointPinId = input->endpointPinId; + } + + { + /* Get base length */ + size_t n = wcslen(localCompositeName); + + /* Check if there are more entries with same name (which might very well be the case), if there + are, the name will be postfixed with an index. */ + nameIndex = GetNameIndex(&nameHash, localCompositeName, isInput); + if (nameIndex > 0) + { + /* This name has multiple instances, so we post fix with a number */ + n += _snwprintf(localCompositeName + n, MAX_PATH - n, L" %u", nameIndex); + } + /* Postfix with filter name */ + _snwprintf(localCompositeName + n, MAX_PATH - n, L" (%s)", pFilter->friendlyName); + } + + /* Convert wide char string to utf-8 */ + WideCharToMultiByte(CP_UTF8, 0, localCompositeName, -1, wdmDeviceInfo->compositeName, MAX_PATH, NULL, NULL); + + /* NB! WDM/KS has no concept of a full-duplex device, each pin is either an input or and output */ + if (isInput) + { + /* INPUT ! */ + deviceInfo->maxInputChannels = pin->maxChannels; + deviceInfo->maxOutputChannels = 0; + + if (outArgument->defaultInputDevice == paNoDevice) + { + outArgument->defaultInputDevice = idxDevice; + } + } + else + { + /* OUTPUT ! */ + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = pin->maxChannels; + + if (outArgument->defaultOutputDevice == paNoDevice) + { + outArgument->defaultOutputDevice = idxDevice; + } + } + + /* 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 + **/ + switch (pFilter->devInfo.streamingType) + { + case Type_kWaveCyclic: + if (IsEarlierThanVista()) + { + /* XP doesn't tolerate low latency, unless the Process Priority Class is set to REALTIME_PRIORITY_CLASS + through SetPriorityClass, then 10 ms is quite feasible. However, one should then bear in mind that ALL of + the process is running in REALTIME_PRIORITY_CLASS, which might not be appropriate for an application with + a GUI . In this case it is advisable to separate the audio engine in another process and use IPC to communicate + with it. */ + deviceInfo->defaultLowInputLatency = 0.02; + deviceInfo->defaultLowOutputLatency = 0.02; + } + else + { + /* This is a conservative estimate. Most WaveCyclic drivers will limit the available latency, but f.i. my Edirol + PCR-A30 can reach 3 ms latency easily... */ + deviceInfo->defaultLowInputLatency = 0.01; + deviceInfo->defaultLowOutputLatency = 0.01; + } + deviceInfo->defaultHighInputLatency = (4096.0/48000.0); + deviceInfo->defaultHighOutputLatency = (4096.0/48000.0); + deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate); + break; + case Type_kWaveRT: + /* This is also a conservative estimate, based on WaveRT polled mode. In polled mode, the latency will be dictated + by the buffer size given by the driver. */ + deviceInfo->defaultLowInputLatency = 0.01; + deviceInfo->defaultLowOutputLatency = 0.01; + deviceInfo->defaultHighInputLatency = 0.04; + deviceInfo->defaultHighOutputLatency = 0.04; + deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate); + break; + default: + assert(0); + break; + } + + /* Add reference to filter */ + FilterAddRef(wdmDeviceInfo->filter); + + assert(idxDevice < totalDeviceCount); + ++idxDevice; + } + } + + /* If no one has add ref'd the filter, drop it */ + if (pFilter->filterRefCount == 0) + { + FilterFree(pFilter); + } + + /* Deinitialize name hash object */ + DeinitNameHashObject(&nameHash); + } + } + + *scanResults = outArgument; + *newDeviceCount = idxDevice; + return result; + +error: + result = DisposeDeviceInfos(hostApi, outArgument, totalDeviceCount); + + return result; +} + +static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount ) +{ + PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; + + hostApi->info.deviceCount = 0; + hostApi->info.defaultInputDevice = paNoDevice; + hostApi->info.defaultOutputDevice = paNoDevice; + + /* Free any old memory which might be in the device info */ + if( hostApi->deviceInfos ) + { + PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory( + wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults)); + localScanResults->deviceInfos = hostApi->deviceInfos; + + DisposeDeviceInfos(hostApi, &localScanResults, hostApi->info.deviceCount); + + hostApi->deviceInfos = NULL; + } + + if( scanResults != NULL ) + { + PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults; + + if( deviceCount > 0 ) + { + /* use the array allocated in ScanDeviceInfos() as our deviceInfos */ + hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos; + + hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice; + hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice; + + hostApi->info.deviceCount = deviceCount; + } + + PaUtil_GroupFreeMemory( wdmHostApi->allocations, scanDeviceInfosResults ); + } + + return paNoError; + +} + +static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount ) +{ + PaWinWdmHostApiRepresentation *winDsHostApi = (PaWinWdmHostApiRepresentation*)hostApi; + + if( scanResults != NULL ) + { + PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults; + + if( scanDeviceInfosResults->deviceInfos ) + { + int i; + for (i = 0; i < deviceCount; ++i) + { + PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)scanDeviceInfosResults->deviceInfos[i]; + if (pDevice->filter != 0) + { + FilterFree(pDevice->filter); + } + } + + PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */ + PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos ); + } + + PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults ); + } + + return paNoError; + +} + PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) { PaError result = paNoError; - int i, deviceCount; + int deviceCount = 0; + void *scanResults = 0; PaWinWdmHostApiRepresentation *wdmHostApi; - PaWinWdmDeviceInfo *deviceInfoArray; - PaWinWdmFilter* pFilter; - PaWinWdmDeviceInfo *wdmDeviceInfo; - PaDeviceInfo *deviceInfo; PA_LOGE_; +#ifdef PA_WDMKS_SET_TREF + tRef = PaUtil_GetTime(); +#endif + /* Attempt to load the KSUSER.DLL without which we cannot create pins We will unload this on termination @@ -1779,6 +3687,18 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd goto error; } + /* Attempt to load AVRT.DLL, if we can't, then we'll just use time critical prio instead... */ + if(DllAvRt == NULL) + { + DllAvRt = LoadLibrary(TEXT("avrt.dll")); + if (DllAvRt != NULL) + { + FunctionAvSetMmThreadCharacteristics = (AVSETMMTHREADCHARACTERISTICS*)GetProcAddress(DllAvRt,"AvSetMmThreadCharacteristicsA"); + FunctionAvRevertMmThreadCharacteristics = (AVREVERTMMTHREADCHARACTERISTICS*)GetProcAddress(DllAvRt, "AvRevertMmThreadCharacteristics"); + FunctionAvSetMmThreadPriority = (AVSETMMTHREADPRIORITY*)GetProcAddress(DllAvRt, "AvSetMmThreadPriority"); + } + } + FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin"); if(FunctionKsCreatePin == NULL) goto error; @@ -1797,129 +3717,50 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd 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"; + + /* these are all updated by CommitDeviceInfos() */ + (*hostApi)->info.deviceCount = 0; (*hostApi)->info.defaultInputDevice = paNoDevice; (*hostApi)->info.defaultOutputDevice = paNoDevice; + (*hostApi)->deviceInfos = 0; - if( deviceCount > 0 ) + result = ScanDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount); + if (result != paNoError) { - (*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; - } + goto error; } - (*hostApi)->info.deviceCount = deviceCount; + CommitDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount); (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; - + /* In preparation for hotplug + (*hostApi)->ScanDeviceInfos = ScanDeviceInfos; + (*hostApi)->CommitDeviceInfos = CommitDeviceInfos; + (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos; + */ PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + 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 ); + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); PA_LOGL_; return result; error: - if( DllKsUser != NULL ) - { - FreeLibrary( DllKsUser ); - DllKsUser = NULL; - } + Terminate( (PaUtilHostApiRepresentation*)wdmHostApi ); - if( wdmHostApi ) - { - PaUtil_FreeMemory( wdmHostApi->filters ); - if( wdmHostApi->allocations ) - { - PaUtil_FreeAllAllocations( wdmHostApi->allocations ); - PaUtil_DestroyAllocationGroup( wdmHostApi->allocations ); - } - PaUtil_FreeMemory( wdmHostApi ); - } PA_LOGL_; return result; } @@ -1928,85 +3769,42 @@ error: static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) { PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; - int i; PA_LOGE_; - if( wdmHostApi->filters ) + /* Do not unload the libraries */ + if( DllKsUser != NULL ) { - for( i=0; ifilterCount; i++) + FreeLibrary( DllKsUser ); + DllKsUser = NULL; + } + + if( DllAvRt != NULL ) + { + FreeLibrary( DllAvRt ); + DllAvRt = NULL; + } + + if( wdmHostApi) + { + PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory( + wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults)); + localScanResults->deviceInfos = hostApi->deviceInfos; + DisposeDeviceInfos(hostApi, localScanResults, hostApi->info.deviceCount); + + if( wdmHostApi->allocations ) { - if( wdmHostApi->filters[i] != NULL ) - { - FilterFree( wdmHostApi->filters[i] ); - wdmHostApi->filters[i] = NULL; - } + PaUtil_FreeAllAllocations( wdmHostApi->allocations ); + PaUtil_DestroyAllocationGroup( wdmHostApi->allocations ); } + PaUtil_FreeMemory( wdmHostApi ); } - 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 ) + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) { int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; @@ -2014,49 +3812,114 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, PaWinWdmFilter* pFilter; int result = paFormatIsSupported; WAVEFORMATEXTENSIBLE wfx; + PaWinWaveFormatChannelMask channelMask; PA_LOGE_; if( inputParameters ) { + PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; + PaWinWdmPin* pin; + unsigned fmt; + unsigned long testFormat = 0; + unsigned validBits = 0; + 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 */ + this implementation doesn't support any custom sample formats */ if( inputSampleFormat & paCustomFormat ) + { + PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom input format not supported"); return paSampleFormatNotSupported; + } /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ + paUseHostApiSpecificDeviceSpecification */ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported"); return paInvalidDevice; + } /* check that input device can support inputChannelCount */ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + { + PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "IsFormatSupported: Invalid input channel count"); return paInvalidChannelCount; + } /* validate inputStreamInfo */ if( inputParameters->hostApiSpecificStreamInfo ) + { + PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported"); return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + + pFilter = pDeviceInfo->filter; + pin = pFilter->pins[pDeviceInfo->pin]; + + /* Find out the testing format */ + for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1) + { + if ((fmt & pin->formats) != 0) + { + /* Found a matching format! */ + testFormat = fmt; + break; + } + } + if (testFormat == 0) + { + PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: no testformat found!"); + return paUnanticipatedHostError; + } + + /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and + valid bits = 24 (instead of 24 bit samples) */ + if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24) + { + PA_DEBUG(("IsFormatSupported (capture): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)")); + testFormat = paInt32; + validBits = 24; + } /* Check that the input format is supported */ - FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount); + channelMask = PaWin_DefaultChannelMask(inputChannelCount); + PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx, + inputChannelCount, + testFormat, + PaWin_SampleFormatToLinearWaveFormatTag(testFormat), + sampleRate, + channelMask ); + if (validBits != 0) + { + wfx.Samples.wValidBitsPerSample = validBits; + } - pFilter = wdmHostApi->filters[inputParameters->device]; - result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx); + result = PinIsFormatSupported(pin, (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); + PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx, + inputChannelCount, + testFormat, + PaWin_SampleFormatToLinearWaveFormatTag(testFormat), + sampleRate); + + if (validBits != 0) + { + wfx.Samples.wValidBitsPerSample = validBits; + } + + result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx); if( result != paNoError ) - return result; + { + PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: sr=%u,ch=%u,bits=%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample); + return result; + } } } else @@ -2066,44 +3929,109 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, if( outputParameters ) { + PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; + PaWinWdmPin* pin; + unsigned fmt; + unsigned long testFormat = 0; + unsigned validBits = 0; + 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 */ + this implementation doesn't support any custom sample formats */ if( outputSampleFormat & paCustomFormat ) + { + PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom output format not supported"); return paSampleFormatNotSupported; + } /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ + paUseHostApiSpecificDeviceSpecification */ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported"); return paInvalidDevice; + } /* check that output device can support outputChannelCount */ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + { + PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count"); return paInvalidChannelCount; + } /* validate outputStreamInfo */ if( outputParameters->hostApiSpecificStreamInfo ) + { + PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported"); return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + + pFilter = pDeviceInfo->filter; + pin = pFilter->pins[pDeviceInfo->pin]; + + /* Find out the testing format */ + for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1) + { + if ((fmt & pin->formats) != 0) + { + /* Found a matching format! */ + testFormat = fmt; + break; + } + } + if (testFormat == 0) + { + PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: no testformat found!"); + return paUnanticipatedHostError; + } + + /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and + valid bits = 24 (instead of 24 bit samples) */ + if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24) + { + PA_DEBUG(("IsFormatSupported (render): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)")); + testFormat = paInt32; + validBits = 24; + } /* Check that the output format is supported */ - FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount); + channelMask = PaWin_DefaultChannelMask(outputChannelCount); + PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx, + outputChannelCount, + testFormat, + PaWin_SampleFormatToLinearWaveFormatTag(testFormat), + sampleRate, + channelMask ); - pFilter = wdmHostApi->filters[outputParameters->device]; - result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx); + if (validBits != 0) + { + wfx.Samples.wValidBitsPerSample = validBits; + } + + result = PinIsFormatSupported(pin, (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); + PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx, + outputChannelCount, + testFormat, + PaWin_SampleFormatToLinearWaveFormatTag(testFormat), + sampleRate); + + if (validBits != 0) + { + wfx.Samples.wValidBitsPerSample = validBits; + } + + result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx); if( result != paNoError ) - return result; + { + PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: %u,%u,%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample); + return result; + } } } @@ -2113,43 +4041,149 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, } /* - IMPLEMENT ME: + IMPLEMENT ME: - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary + - 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 + - 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. + 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 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 + - 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 */ + { + PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "No input or output channels defined"); + result = paSampleFormatNotSupported; /* Not right error */ + } PA_LOGL_; return result; } +static void ResetStreamEvents(PaWinWdmStream* stream) +{ + unsigned i; + ResetEvent(stream->eventAbort); + ResetEvent(stream->eventStreamStart[StreamStart_kOk]); + ResetEvent(stream->eventStreamStart[StreamStart_kFailed]); + + for (i=0; icapture.noOfPackets; ++i) + { + if (stream->capture.events && stream->capture.events[i]) + { + ResetEvent(stream->capture.events[i]); + } + } + + for (i=0; irender.noOfPackets; ++i) + { + if (stream->render.events && stream->render.events[i]) + { + ResetEvent(stream->render.events[i]); + } + } +} + +static void CloseStreamEvents(PaWinWdmStream* stream) +{ + unsigned i; + PaWinWdmIOInfo* ios[2] = { &stream->capture, &stream->render }; + + if (stream->eventAbort) + { + CloseHandle(stream->eventAbort); + stream->eventAbort = 0; + } + if (stream->eventStreamStart[StreamStart_kOk]) + { + CloseHandle(stream->eventStreamStart[StreamStart_kOk]); + } + if (stream->eventStreamStart[StreamStart_kFailed]) + { + CloseHandle(stream->eventStreamStart[StreamStart_kFailed]); + } + + for (i = 0; i < 2; ++i) + { + unsigned j; + /* Unregister notification handles for WaveRT */ + if (ios[i]->pPin && ios[i]->pPin->parentFilter->devInfo.streamingType == Type_kWaveRT && + ios[i]->pPin->pinKsSubType == SubType_kNotification && + ios[i]->events != 0) + { + PinUnregisterNotificationHandle(ios[i]->pPin, ios[i]->events[0]); + } + + for (j=0; j < ios[i]->noOfPackets; ++j) + { + if (ios[i]->events && ios[i]->events[j]) + { + CloseHandle(ios[i]->events[j]); + ios[i]->events[j] = 0; + } + } + } +} + +static unsigned NextPowerOf2(unsigned val) +{ + val--; + val = (val >> 1) | val; + val = (val >> 2) | val; + val = (val >> 4) | val; + val = (val >> 8) | val; + val = (val >> 16) | val; + return ++val; +} + +static PaError ValidateSpecificStreamParameters( + const PaStreamParameters *streamParameters, + const PaWinWDMKSInfo *streamInfo) +{ + if( streamInfo ) + { + if( streamInfo->size != sizeof( PaWinWDMKSInfo ) + || streamInfo->version != 1 ) + { + PA_DEBUG(("Stream parameters: size or version not correct")); + return paIncompatibleHostApiSpecificStreamInfo; + } + + if (streamInfo->noOfPackets != 0 && + (streamInfo->noOfPackets < 2 || streamInfo->noOfPackets > 8)) + { + PA_DEBUG(("Stream parameters: noOfPackets %u out of range [2,8]", streamInfo->noOfPackets)); + return paIncompatibleHostApiSpecificStreamInfo; + } + + } + + 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 ) + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerUserBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) { PaError result = paNoError; PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi; @@ -2158,13 +4192,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 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)); + PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerUserBuffer)); if( inputParameters ) { @@ -2172,24 +4204,33 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, inputSampleFormat = inputParameters->sampleFormat; /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ + paUseHostApiSpecificDeviceSpecification */ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(in) not supported"); return paInvalidDevice; + } /* check that input device can support stream->userInputChannels */ if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + { + PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid input channel count"); return paInvalidChannelCount; + } /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - + result = ValidateSpecificStreamParameters(inputParameters, inputParameters->hostApiSpecificStreamInfo); + if(result != paNoError) + { + PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (in)"); + return result; /* this implementation doesn't use custom stream info */ + } } else { userInputChannels = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + inputSampleFormat = hostInputSampleFormat = paInt16; /* Supress 'uninitialised var' warnings. */ } if( outputParameters ) @@ -2198,29 +4239,41 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, outputSampleFormat = outputParameters->sampleFormat; /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ + paUseHostApiSpecificDeviceSpecification */ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(out) not supported"); return paInvalidDevice; + } /* check that output device can support stream->userInputChannels */ if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + { + PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count"); return paInvalidChannelCount; + } /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - + result = ValidateSpecificStreamParameters( outputParameters, outputParameters->hostApiSpecificStreamInfo ); + if (result != paNoError) + { + PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (out)"); + return result; /* this implementation doesn't use custom stream info */ + } } else { userOutputChannels = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ + outputSampleFormat = hostOutputSampleFormat = paInt16; /* Supress 'uninitialized var' warnings. */ } /* validate platform specific flags */ if( (streamFlags & paPlatformSpecificFlags) != 0 ) + { + PaWinWDM_SetLastErrorInfo(paInvalidFlag, "Invalid flag supplied"); return paInvalidFlag; /* unexpected platform specific flag */ + } stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) ); if( !stream ) @@ -2228,18 +4281,33 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, result = paInsufficientMemory; goto error; } + + /* Create allocation group */ + stream->allocGroup = PaUtil_CreateAllocationGroup(); + if( !stream->allocGroup ) + { + result = paInsufficientMemory; + goto error; + } + /* Zero the stream object */ /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */ if( streamCallback ) { PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &wdmHostApi->callbackStreamInterface, streamCallback, userData ); + &wdmHostApi->callbackStreamInterface, streamCallback, userData ); } else { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &wdmHostApi->blockingStreamInterface, streamCallback, userData ); + /* PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &wdmHostApi->blockingStreamInterface, streamCallback, userData ); */ + + /* We don't support the blocking API yet */ + PA_DEBUG(("Blocking API not supported yet!\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Blocking API not supported yet"); + result = paUnanticipatedHostError; + goto error; } PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); @@ -2247,315 +4315,767 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* Instantiate the input pin if necessary */ if(userInputChannels > 0) { + PaWinWdmFilter* pFilter; + PaWinWdmDeviceInfo* pDeviceInfo; + PaWinWdmPin* pPin; + unsigned validBitsPerSample = 0; + PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userInputChannels ); + result = paSampleFormatNotSupported; - pFilter = wdmHostApi->filters[inputParameters->device]; + pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; + pFilter = pDeviceInfo->filter; + pPin = pFilter->pins[pDeviceInfo->pin]; + 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 ) + hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, inputSampleFormat ); + if (hostInputSampleFormat == paSampleFormatNotSupported) { - /* 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; + result = paUnanticipatedHostError; + PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (input)", pPin->formats, inputSampleFormat); + goto error; + } + else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostInputSampleFormat == paInt24) + { + /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a + 128 byte boundary (see PinGetBuffer) */ + hostInputSampleFormat = paInt32; + /* But we'll tell the driver that it's 24 bit in 32 bit container */ + validBitsPerSample = 24; + } - if( result != paNoError ) + while (hostInputSampleFormat <= paUInt8) + { + unsigned channelsToProbe = stream->userInputChannels; + /* 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 + * The following loop tests this case + **/ + while (1) + { + PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx, + channelsToProbe, + hostInputSampleFormat, + PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat), + sampleRate, + channelMask ); + stream->capture.bytesPerFrame = wfx.Format.nBlockAlign; + if (validBitsPerSample != 0) + { + wfx.Samples.wValidBitsPerSample = validBitsPerSample; + } + stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result); + stream->deviceInputChannels = channelsToProbe; + + if( result != paNoError && result != paDeviceUnavailable ) { /* 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); + PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx, + channelsToProbe, + hostInputSampleFormat, + PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat), + sampleRate); + if (validBitsPerSample != 0) + { + wfx.Samples.wValidBitsPerSample = validBitsPerSample; + } + stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result); } + + if (result == paDeviceUnavailable) goto occupied; + + if (result == paNoError) + { + /* We're done */ + break; + } + + if (channelsToProbe < (unsigned)pPin->maxChannels) + { + /* Go to next multiple of 2 */ + channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels); + continue; + } + + break; } + + if (result == paNoError) + { + /* We're done */ + break; + } + + /* Go to next format in line with lower resolution */ + hostInputSampleFormat <<= 1; } - if(stream->recordingPin == NULL) + if(stream->capture.pPin == NULL) { + PaWinWDM_SetLastErrorInfo(result, "Failed to create capture pin: sr=%u,ch=%u,bits=%u,align=%u", + wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign); goto error; } - switch(hostInputSampleFormat) + /* Select correct mux input on MUX node of topology filter */ + if (pDeviceInfo->muxPosition >= 0) { - case paInt16: stream->inputSampleSize = 2; break; - case paInt24: stream->inputSampleSize = 3; break; - case paInt32: - case paFloat32: stream->inputSampleSize = 4; break; + assert(pPin->parentFilter->topologyFilter != NULL); + + result = FilterUse(pPin->parentFilter->topologyFilter); + if (result != paNoError) + { + PaWinWDM_SetLastErrorInfo(result, "Failed to open topology filter"); + goto error; + } + + result = WdmSetMuxNodeProperty(pPin->parentFilter->topologyFilter->handle, + pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId, + pPin->inputs[pDeviceInfo->muxPosition]->muxPinId); + + FilterRelease(pPin->parentFilter->topologyFilter); + + if(result != paNoError) + { + PaWinWDM_SetLastErrorInfo(result, "Failed to set topology mux node"); + goto error; + } } - stream->recordingPin->frameSize /= stream->bytesPerInputFrame; - PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize)); + stream->capture.bytesPerSample = stream->capture.bytesPerFrame / stream->deviceInputChannels; + stream->capture.pPin->frameSize /= stream->capture.bytesPerFrame; + PA_DEBUG(("Capture pin frames: %d\n",stream->capture.pPin->frameSize)); } else { - stream->recordingPin = NULL; - stream->bytesPerInputFrame = 0; + stream->capture.pPin = NULL; + stream->capture.bytesPerFrame = 0; } /* Instantiate the output pin if necessary */ if(userOutputChannels > 0) { + PaWinWdmFilter* pFilter; + PaWinWdmDeviceInfo* pDeviceInfo; + PaWinWdmPin* pPin; + unsigned validBitsPerSample = 0; + PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userOutputChannels ); + result = paSampleFormatNotSupported; - pFilter = wdmHostApi->filters[outputParameters->device]; + pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; + pFilter = pDeviceInfo->filter; + pPin = pFilter->pins[pDeviceInfo->pin]; + stream->userOutputChannels = userOutputChannels; - if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0) + hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, outputSampleFormat ); + if (hostOutputSampleFormat == paSampleFormatNotSupported) { - hostOutputSampleFormat = outputSampleFormat; - FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels); - stream->bytesPerOutputFrame = wfx.Format.nBlockAlign; - stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result); - stream->deviceOutputChannels = stream->userOutputChannels; + result = paUnanticipatedHostError; + PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (output)", pPin->formats, hostOutputSampleFormat); + goto error; + } + else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostOutputSampleFormat == paInt24) + { + /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a + 128 byte boundary (see PinGetBuffer) */ + hostOutputSampleFormat = paInt32; + /* But we'll tell the driver that it's 24 bit in 32 bit container */ + validBitsPerSample = 24; } - 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 ) + while (hostOutputSampleFormat <= paUInt8) { + unsigned channelsToProbe = stream->userOutputChannels; /* 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 ) + * 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 + * The following loop tests this case + **/ + while (1) { - 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 ) + PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx, + channelsToProbe, + hostOutputSampleFormat, + PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat), + sampleRate, + channelMask ); + stream->render.bytesPerFrame = wfx.Format.nBlockAlign; + if (validBitsPerSample != 0) { - /* 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); + wfx.Samples.wValidBitsPerSample = validBitsPerSample; } + stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result); + stream->deviceOutputChannels = channelsToProbe; + + if( result != paNoError && result != paDeviceUnavailable ) + { + PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx, + channelsToProbe, + hostOutputSampleFormat, + PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat), + sampleRate); + if (validBitsPerSample != 0) + { + wfx.Samples.wValidBitsPerSample = validBitsPerSample; + } + stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result); + } + + if (result == paDeviceUnavailable) goto occupied; + + if (result == paNoError) + { + /* We're done */ + break; + } + + if (channelsToProbe < (unsigned)pPin->maxChannels) + { + /* Go to next multiple of 2 */ + channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels); + continue; + } + + break; + }; + + if (result == paNoError) + { + /* We're done */ + break; } + + /* Go to next format in line with lower resolution */ + hostOutputSampleFormat <<= 1; } - if(stream->playbackPin == NULL) + if(stream->render.pPin == NULL) { + PaWinWDM_SetLastErrorInfo(result, "Failed to create render pin: sr=%u,ch=%u,bits=%u,align=%u", + wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign); 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)); + stream->render.bytesPerSample = stream->render.bytesPerFrame / stream->deviceOutputChannels; + stream->render.pPin->frameSize /= stream->render.bytesPerFrame; + PA_DEBUG(("Render pin frames: %d\n",stream->render.pPin->frameSize)); } else { - stream->playbackPin = NULL; - stream->bytesPerOutputFrame = 0; + stream->render.pPin = NULL; + stream->render.bytesPerFrame = 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) + stream->capture.framesPerBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001); + if(stream->capture.framesPerBuffer > (unsigned long)sampleRate) { /* Upper limit is 1 second */ - stream->framesPerHostIBuffer = (unsigned long)sampleRate; + stream->capture.framesPerBuffer = (unsigned long)sampleRate; } - else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize) + else if(stream->capture.framesPerBuffer < stream->capture.pPin->frameSize) { - stream->framesPerHostIBuffer = stream->recordingPin->frameSize; + stream->capture.framesPerBuffer = stream->capture.pPin->frameSize; + } + PA_DEBUG(("Input frames chosen:%ld\n",stream->capture.framesPerBuffer)); + + /* Setup number of packets to use */ + stream->capture.noOfPackets = 2; + + if (inputParameters->hostApiSpecificStreamInfo) + { + PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)inputParameters->hostApiSpecificStreamInfo; + + if (stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic && + pInfo->noOfPackets != 0) + { + stream->capture.noOfPackets = pInfo->noOfPackets; + } } - 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) + stream->render.framesPerBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001); + if(stream->render.framesPerBuffer > (unsigned long)sampleRate) { /* Upper limit is 1 second */ - stream->framesPerHostOBuffer = (unsigned long)sampleRate; + stream->render.framesPerBuffer = (unsigned long)sampleRate; } - else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize) + else if(stream->render.framesPerBuffer < stream->render.pPin->frameSize) { - stream->framesPerHostOBuffer = stream->playbackPin->frameSize; + stream->render.framesPerBuffer = stream->render.pPin->frameSize; + } + PA_DEBUG(("Output frames chosen:%ld\n",stream->render.framesPerBuffer)); + + /* Setup number of packets to use */ + stream->render.noOfPackets = 2; + + if (outputParameters->hostApiSpecificStreamInfo) + { + PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)outputParameters->hostApiSpecificStreamInfo; + + if (stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic && + pInfo->noOfPackets != 0) + { + stream->render.noOfPackets = pInfo->noOfPackets; + } } - PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer)); } - /* Host buffer size is bounded to the largest of the input and output - frame sizes */ - + /* Host buffer size is bound 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 ); + stream->userInputChannels, inputSampleFormat, hostInputSampleFormat, + stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerUserBuffer, + max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer), + paUtilBoundedHostBufferSize, + streamCallback, userData ); if( result != paNoError ) + { + PaWinWDM_SetLastErrorInfo(result, "PaUtil_InitializeBufferProcessor failed: ich=%u, isf=%u, hisf=%u, och=%u, osf=%u, hosf=%u, sr=%lf, flags=0x%X, fpub=%u, fphb=%u", + stream->userInputChannels, inputSampleFormat, hostInputSampleFormat, + stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerUserBuffer, + max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer)); goto error; + } + + /* Allocate/get all the buffers for host I/O */ + if (stream->userInputChannels > 0) + { + stream->streamRepresentation.streamInfo.inputLatency = stream->capture.framesPerBuffer / sampleRate; + + switch (stream->capture.pPin->parentFilter->devInfo.streamingType) + { + case Type_kWaveCyclic: + { + unsigned size = stream->capture.noOfPackets * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame; + /* Allocate input host buffer */ + stream->capture.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size); + PA_DEBUG(("Input buffer allocated (size = %u)\n", size)); + if( !stream->capture.hostBuffer ) + { + PA_DEBUG(("Cannot allocate host input buffer!\n")); + PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate input buffer"); + result = paInsufficientMemory; + goto error; + } + stream->capture.hostBufferSize = size; + PA_DEBUG(("Input buffer start = %p (size=%u)\n",stream->capture.hostBuffer, stream->capture.hostBufferSize)); + stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveCyclic; + stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveCyclic; + } + break; + case Type_kWaveRT: + { + const DWORD dwTotalSize = 2 * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame; + DWORD dwRequestedSize = dwTotalSize; + BOOL bCallMemoryBarrier = FALSE; + ULONG hwFifoLatency = 0; + ULONG dummy; + result = PinGetBuffer(stream->capture.pPin, (void**)&stream->capture.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier); + if (!result) + { + PA_DEBUG(("Input buffer start = %p, size = %u\n", stream->capture.hostBuffer, dwRequestedSize)); + if (dwRequestedSize != dwTotalSize) + { + PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize)); + /* Recalculate to what the driver has given us */ + stream->capture.framesPerBuffer = dwRequestedSize / (2 * stream->capture.bytesPerFrame); + } + stream->capture.hostBufferSize = dwRequestedSize; + + if (stream->capture.pPin->pinKsSubType == SubType_kPolled) + { + stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTPolled; + stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTPolled; + } + else + { + stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTEvent; + stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTEvent; + } + + stream->capture.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierRead : MemoryBarrierDummy; + } + else + { + PA_DEBUG(("Failed to get input buffer (WaveRT)\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get input buffer (WaveRT)"); + result = paUnanticipatedHostError; + goto error; + } + + /* Get latency */ + result = PinGetHwLatency(stream->capture.pPin, &hwFifoLatency, &dummy, &dummy); + if (result == paNoError) + { + stream->capture.pPin->hwLatency = hwFifoLatency; + + /* Add HW latency into total input latency */ + stream->streamRepresentation.streamInfo.inputLatency += ((hwFifoLatency / stream->capture.bytesPerFrame) / sampleRate); + } + else + { + PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n")); + stream->capture.pPin->hwLatency = 0; + } + } + break; + default: + /* Undefined wave type!! */ + assert(0); + result = paInternalError; + PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType); + goto error; + } + } + else + { + stream->capture.hostBuffer = 0; + } + + if (stream->userOutputChannels > 0) + { + stream->streamRepresentation.streamInfo.outputLatency = stream->render.framesPerBuffer / sampleRate; + + switch (stream->render.pPin->parentFilter->devInfo.streamingType) + { + case Type_kWaveCyclic: + { + unsigned size = stream->render.noOfPackets * stream->render.framesPerBuffer * stream->render.bytesPerFrame; + /* Allocate output device buffer */ + stream->render.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size); + PA_DEBUG(("Output buffer allocated (size = %u)\n", size)); + if( !stream->render.hostBuffer ) + { + PA_DEBUG(("Cannot allocate host output buffer!\n")); + PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate output buffer"); + result = paInsufficientMemory; + goto error; + } + stream->render.hostBufferSize = size; + PA_DEBUG(("Output buffer start = %p (size=%u)\n",stream->render.hostBuffer, stream->render.hostBufferSize)); + + stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveCyclic; + stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveCyclic; + } + break; + case Type_kWaveRT: + { + const DWORD dwTotalSize = 2 * stream->render.framesPerBuffer * stream->render.bytesPerFrame; + DWORD dwRequestedSize = dwTotalSize; + BOOL bCallMemoryBarrier = FALSE; + ULONG hwFifoLatency = 0; + ULONG dummy; + result = PinGetBuffer(stream->render.pPin, (void**)&stream->render.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier); + if (!result) + { + PA_DEBUG(("Output buffer start = %p, size = %u, membarrier = %u\n", stream->render.hostBuffer, dwRequestedSize, bCallMemoryBarrier)); + if (dwRequestedSize != dwTotalSize) + { + PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize)); + /* Recalculate to what the driver has given us */ + stream->render.framesPerBuffer = dwRequestedSize / (2 * stream->render.bytesPerFrame); + } + stream->render.hostBufferSize = dwRequestedSize; + + if (stream->render.pPin->pinKsSubType == SubType_kPolled) + { + stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTPolled; + stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTPolled; + } + else + { + stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTEvent; + stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTEvent; + } + + stream->render.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierWrite : MemoryBarrierDummy; + } + else + { + PA_DEBUG(("Failed to get output buffer (with notification)\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get output buffer (with notification)"); + result = paUnanticipatedHostError; + goto error; + } + + /* Get latency */ + result = PinGetHwLatency(stream->render.pPin, &hwFifoLatency, &dummy, &dummy); + if (result == paNoError) + { + stream->render.pPin->hwLatency = hwFifoLatency; + + /* Add HW latency into total output latency */ + stream->streamRepresentation.streamInfo.outputLatency += ((hwFifoLatency / stream->render.bytesPerFrame) / sampleRate); + } + else + { + PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n")); + stream->render.pPin->hwLatency = 0; + } + } + break; + default: + /* Undefined wave type!! */ + assert(0); + result = paInternalError; + PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType); + goto error; + } + } + else + { + stream->render.hostBuffer = 0; + } - 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)); + PA_DEBUG(("BytesPerInputFrame = %d\n",stream->capture.bytesPerFrame)); + PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->render.bytesPerFrame)); - /* 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 ) + /* memset(stream->hostBuffer,0,size); */ + + /* Abort */ + stream->eventAbort = CreateEvent(NULL, TRUE, FALSE, NULL); + if (stream->eventAbort == 0) + { + result = paInsufficientMemory; + goto error; + } + stream->eventStreamStart[0] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (stream->eventStreamStart[0] == 0) + { + result = paInsufficientMemory; + goto error; + } + stream->eventStreamStart[1] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (stream->eventStreamStart[1] == 0) { - 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; + const unsigned bufferSizeInBytes = stream->capture.framesPerBuffer * stream->capture.bytesPerFrame; + const unsigned ringBufferFrameSize = NextPowerOf2( 1024 + 2 * max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer) ); - 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; + stream->capture.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(HANDLE)); + if (stream->capture.events == NULL) + { + result = paInsufficientMemory; + goto error; + } + + stream->capture.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(DATAPACKET)); + if (stream->capture.packets == NULL) + { + result = paInsufficientMemory; + goto error; + } + + switch(stream->capture.pPin->parentFilter->devInfo.streamingType) + { + case Type_kWaveCyclic: + { + /* WaveCyclic case */ + unsigned i; + for (i = 0; i < stream->capture.noOfPackets; ++i) + { + /* Set up the packets */ + DATAPACKET *p = stream->capture.packets + i; + + /* Record event */ + stream->capture.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + + p->Signal.hEvent = stream->capture.events[i]; + p->Header.Data = stream->capture.hostBuffer + (i*bufferSizeInBytes); + p->Header.FrameExtent = bufferSizeInBytes; + p->Header.DataUsed = 0; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + } + } + break; + case Type_kWaveRT: + { + /* Set up the "packets" */ + DATAPACKET *p = stream->capture.packets + 0; + + /* Record event: WaveRT has a single event for 2 notification per buffer */ + stream->capture.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL); + + p->Header.Data = stream->capture.hostBuffer; + p->Header.FrameExtent = bufferSizeInBytes; + p->Header.DataUsed = 0; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + + ++p; + p->Header.Data = stream->capture.hostBuffer + bufferSizeInBytes; + p->Header.FrameExtent = bufferSizeInBytes; + p->Header.DataUsed = 0; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + + if (stream->capture.pPin->pinKsSubType == SubType_kNotification) + { + result = PinRegisterNotificationHandle(stream->capture.pPin, stream->capture.events[0]); + + if (result != paNoError) + { + PA_DEBUG(("Failed to register capture notification handle\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register capture notification handle"); + result = paUnanticipatedHostError; + goto error; + } + } + + result = PinRegisterPositionRegister(stream->capture.pPin); + + if (result != paNoError) + { + PA_DEBUG(("Failed to register capture position register, using PinGetAudioPositionViaIOCTL\n")); + stream->capture.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTL; + } + else + { + stream->capture.pPin->fnAudioPosition = PinGetAudioPositionDirect; + } + } + break; + default: + /* Undefined wave type!! */ + assert(0); + result = paInternalError; + PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType); + goto error; + } + + /* Setup the input ring buffer here */ + stream->ringBufferData = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, ringBufferFrameSize * stream->capture.bytesPerFrame); + if (stream->ringBufferData == NULL) + { + result = paInsufficientMemory; + goto error; + } + PaUtil_InitializeRingBuffer(&stream->ringBuffer, stream->capture.bytesPerFrame, ringBufferFrameSize, stream->ringBufferData); } 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; + const unsigned bufferSizeInBytes = stream->render.framesPerBuffer * stream->render.bytesPerFrame; + + stream->render.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(HANDLE)); + if (stream->render.events == NULL) + { + result = paInsufficientMemory; + goto error; + } + + stream->render.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(DATAPACKET)); + if (stream->render.packets == NULL) + { + result = paInsufficientMemory; + goto error; + } + + switch(stream->render.pPin->parentFilter->devInfo.streamingType) + { + case Type_kWaveCyclic: + { + /* WaveCyclic case */ + unsigned i; + for (i = 0; i < stream->render.noOfPackets; ++i) + { + /* Set up the packets */ + DATAPACKET *p = stream->render.packets + i; + + /* Playback event */ + stream->render.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + + /* In this case, we just use the packets as ptr to the device buffer */ + p->Signal.hEvent = stream->render.events[i]; + p->Header.Data = stream->render.hostBuffer + (i*bufferSizeInBytes); + p->Header.FrameExtent = bufferSizeInBytes; + p->Header.DataUsed = bufferSizeInBytes; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + } + } + break; + case Type_kWaveRT: + { + /* WaveRT case */ + + /* Set up the "packets" */ + DATAPACKET *p = stream->render.packets; + + /* The only playback event */ + stream->render.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL); + + /* In this case, we just use the packets as ptr to the device buffer */ + p->Header.Data = stream->render.hostBuffer; + p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame; + p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + + ++p; + p->Header.Data = stream->render.hostBuffer + stream->render.framesPerBuffer*stream->render.bytesPerFrame; + p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame; + p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame; + p->Header.Size = sizeof(p->Header); + p->Header.PresentationTime.Numerator = 1; + p->Header.PresentationTime.Denominator = 1; + + if (stream->render.pPin->pinKsSubType == SubType_kNotification) + { + result = PinRegisterNotificationHandle(stream->render.pPin, stream->render.events[0]); + + if (result != paNoError) + { + PA_DEBUG(("Failed to register rendering notification handle\n")); + PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register rendering notification handle"); + result = paUnanticipatedHostError; + goto error; + } + } + + result = PinRegisterPositionRegister(stream->render.pPin); + + if (result != paNoError) + { + PA_DEBUG(("Failed to register rendering position register, using PinGetAudioPositionViaIOCTL\n")); + stream->render.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTL; + } + else + { + stream->render.pPin->fnAudioPosition = PinGetAudioPositionDirect; + } + } + break; + default: + /* Undefined wave type!! */ + assert(0); + result = paInternalError; + PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType); + goto error; + } } stream->streamStarted = 0; @@ -2565,45 +5085,93 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->streamFlags = streamFlags; stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; + /* Increase ref count on filters in use, so that a CommitDeviceInfos won't delete them */ + if (stream->capture.pPin != 0) + { + FilterAddRef(stream->capture.pPin->parentFilter); + } + if (stream->render.pPin != 0) + { + FilterAddRef(stream->render.pPin->parentFilter); + } + + /* Ok, now update our host API specific stream info */ + if (stream->userInputChannels) + { + PaWinWdmDeviceInfo *pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; + + stream->hostApiStreamInfo.input.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), inputParameters->device); + stream->hostApiStreamInfo.input.channels = stream->deviceInputChannels; + stream->hostApiStreamInfo.input.muxNodeId = -1; + if (stream->capture.pPin->inputs) + { + stream->hostApiStreamInfo.input.muxNodeId = stream->capture.pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId; + } + stream->hostApiStreamInfo.input.endpointPinId = pDeviceInfo->endpointPinId; + stream->hostApiStreamInfo.input.framesPerHostBuffer = stream->capture.framesPerBuffer; + stream->hostApiStreamInfo.input.streamingSubType = stream->capture.pPin->pinKsSubType; + } + else + { + stream->hostApiStreamInfo.input.device = paNoDevice; + } + if (stream->userOutputChannels) + { + stream->hostApiStreamInfo.output.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), outputParameters->device); + stream->hostApiStreamInfo.output.channels = stream->deviceOutputChannels; + stream->hostApiStreamInfo.output.framesPerHostBuffer = stream->render.framesPerBuffer; + stream->hostApiStreamInfo.output.endpointPinId = stream->render.pPin->endpointPinId; + stream->hostApiStreamInfo.output.streamingSubType = stream->render.pPin->pinKsSubType; + } + else + { + stream->hostApiStreamInfo.output.device = paNoDevice; + } + /*stream->streamRepresentation.streamInfo.hostApiTypeId = paWDMKS; + stream->streamRepresentation.streamInfo.hostApiSpecificStreamInfo = &stream->hostApiStreamInfo;*/ + stream->streamRepresentation.streamInfo.structVersion = 2; + *s = (PaStream*)stream; PA_LOGL_; return result; +occupied: + /* Ok, someone else is hogging the pin, bail out */ + assert (result == paDeviceUnavailable); + PaWinWDM_SetLastErrorInfo(result, "Device is occupied"); + error: - size = 5; - while(size--) + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + + CloseStreamEvents(stream); + + if (stream->allocGroup) { - if(stream->events[size] != NULL) - { - CloseHandle(stream->events[size]); - stream->events[size] = NULL; - } + PaUtil_FreeAllAllocations(stream->allocGroup); + PaUtil_DestroyAllocationGroup(stream->allocGroup); + stream->allocGroup = 0; } - if(stream->hostBuffer) - PaUtil_FreeMemory( stream->hostBuffer ); - if(stream->playbackPin) - PinClose(stream->playbackPin); - if(stream->recordingPin) - PinClose(stream->recordingPin); + if(stream->render.pPin) + PinClose(stream->render.pPin); + if(stream->capture.pPin) + PinClose(stream->capture.pPin); - if( stream ) - PaUtil_FreeMemory( 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. +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_; @@ -2612,22 +5180,33 @@ static PaError CloseStream( PaStream* s ) 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); + CloseStreamEvents(stream); + + if (stream->allocGroup) + { + PaUtil_FreeAllAllocations(stream->allocGroup); + PaUtil_DestroyAllocationGroup(stream->allocGroup); + stream->allocGroup = 0; + } + + if(stream->render.pPin) + { + PinClose(stream->render.pPin); + } + if(stream->capture.pPin) + { + PinClose(stream->capture.pPin); + } + + if (stream->render.pPin) + { + FilterFree(stream->render.pPin->parentFilter); + } + if (stream->capture.pPin) + { + FilterFree(stream->capture.pPin->parentFilter); + } PaUtil_FreeMemory( stream ); @@ -2638,25 +5217,57 @@ static PaError CloseStream( PaStream* s ) /* Write the supplied packet to the pin Asynchronous -Should return false on success +Should return paNoError on success */ -static BOOL PinWrite(HANDLE h, DATAPACKET* p) +static PaError PinWrite(HANDLE h, DATAPACKET* p) { + PaError result = paNoError; unsigned long cbReturned = 0; - return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0, - &p->Header,p->Header.Size,&cbReturned,&p->Signal); + BOOL fRes = DeviceIoControl(h, + IOCTL_KS_WRITE_STREAM, + NULL, + 0, + &p->Header, + p->Header.Size, + &cbReturned, + &p->Signal); + if (!fRes) + { + unsigned long error = GetLastError(); + if (error != ERROR_IO_PENDING) + { + result = paInternalError; + } + } + return result; } /* Read to the supplied packet from the pin Asynchronous -Should return false on success +Should return paNoError on success */ -static BOOL PinRead(HANDLE h, DATAPACKET* p) +static PaError PinRead(HANDLE h, DATAPACKET* p) { + PaError result = paNoError; unsigned long cbReturned = 0; - return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0, - &p->Header,p->Header.Size,&cbReturned,&p->Signal); + BOOL fRes = DeviceIoControl(h, + IOCTL_KS_READ_STREAM, + NULL, + 0, + &p->Header, + p->Header.Size, + &cbReturned, + &p->Signal); + if (!fRes) + { + unsigned long error = GetLastError(); + if (error != ERROR_IO_PENDING) + { + result = paInternalError; + } + } + return result; } /* @@ -2722,365 +5333,763 @@ static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples) } } -static DWORD WINAPI ProcessingThread(LPVOID pParam) +/* +Increase the priority of the calling thread to RT +*/ +static HANDLE BumpThreadPriority() +{ + HANDLE hThread = GetCurrentThread(); + DWORD dwTask = 0; + HANDLE hAVRT = NULL; + + /* If we have access to AVRT.DLL (Vista and later), use it */ + if (FunctionAvSetMmThreadCharacteristics != NULL) + { + hAVRT = FunctionAvSetMmThreadCharacteristics("Pro Audio", &dwTask); + if (hAVRT != NULL && hAVRT != INVALID_HANDLE_VALUE) + { + BOOL bret = FunctionAvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_CRITICAL); + if (!bret) + { + PA_DEBUG(("Set mm thread prio to critical failed!\n")); + } + else + { + return hAVRT; + } + } + else + { + PA_DEBUG(("Set mm thread characteristic to 'Pro Audio' failed, reverting to SetThreadPriority\n")); + } + } + + /* For XP and earlier, or if AvSetMmThreadCharacteristics fails (MMCSS disabled ?) */ + if (timeBeginPeriod(1) != TIMERR_NOERROR) { + PA_DEBUG(("timeBeginPeriod(1) failed!\n")); + } + + if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) { + PA_DEBUG(("SetThreadPriority failed!\n")); + } + + return hAVRT; +} + +/* +Decrease the priority of the calling thread to normal +*/ +static void DropThreadPriority(HANDLE hAVRT) +{ + HANDLE hThread = GetCurrentThread(); + + if (hAVRT != NULL) + { + FunctionAvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_NORMAL); + FunctionAvRevertMmThreadCharacteristics(hAVRT); + return; + } + + SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL); + timeEndPeriod(1); +} + +static PaError PreparePinForStart(PaWinWdmPin* pin) { - 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; + result = PinSetState(pin, KSSTATE_ACQUIRE); + if (result != paNoError) + { + goto error; + } + result = PinSetState(pin, KSSTATE_PAUSE); + if (result != paNoError) + { + goto error; + } + return result; + +error: + PinSetState(pin, KSSTATE_STOP); + return result; +} + +static PaError PreparePinsForStart(PaProcessThreadInfo* pInfo) +{ + PaError result = paNoError; + /* Submit buffers */ + if (pInfo->stream->capture.pPin) + { + if ((result = PreparePinForStart(pInfo->stream->capture.pPin)) != paNoError) + { + goto error; + } + + if (pInfo->stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic) + { + unsigned i; + for(i=0; i < pInfo->stream->capture.noOfPackets; ++i) + { + if ((result = PinRead(pInfo->stream->capture.pPin->handle, pInfo->stream->capture.packets + i)) != paNoError) + { + goto error; + } + ++pInfo->pending; + } + } + else + { + pInfo->pending = 2; + } + } + + if(pInfo->stream->render.pPin) + { + if ((result = PreparePinForStart(pInfo->stream->render.pPin)) != paNoError) + { + goto error; + } + + pInfo->priming += pInfo->stream->render.noOfPackets; + ++pInfo->pending; + SetEvent(pInfo->stream->render.events[0]); + if (pInfo->stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic) + { + unsigned i; + for(i=1; i < pInfo->stream->render.noOfPackets; ++i) + { + SetEvent(pInfo->stream->render.events[i]); + ++pInfo->pending; + } + } + } + +error: + PA_DEBUG(("PreparePinsForStart = %d\n", result)); + return result; +} + +static PaError StartPin(PaWinWdmPin* pin) +{ + return PinSetState(pin, KSSTATE_RUN); +} + +static PaError StartPins(PaProcessThreadInfo* pInfo) +{ + PaError result = paNoError; + /* Start the pins as synced as possible */ + if (pInfo->stream->capture.pPin) + { + result = StartPin(pInfo->stream->capture.pPin); + } + if(pInfo->stream->render.pPin) + { + result = StartPin(pInfo->stream->render.pPin); + } + PA_DEBUG(("StartPins = %d\n", result)); + return result; +} + + +static PaError StopPin(PaWinWdmPin* pin) +{ + PinSetState(pin, KSSTATE_PAUSE); + PinSetState(pin, KSSTATE_STOP); + return paNoError; +} + + +static PaError StopPins(PaProcessThreadInfo* pInfo) +{ + PaError result = paNoError; + if(pInfo->stream->render.pPin) + { + StopPin(pInfo->stream->render.pPin); + } + if(pInfo->stream->capture.pPin) + { + StopPin(pInfo->stream->capture.pPin); + } + return result; +} + +typedef void (*TSetInputFrameCount)(PaUtilBufferProcessor*, unsigned long); +typedef void (*TSetInputChannel)(PaUtilBufferProcessor*, unsigned int, void *, unsigned int); +static const TSetInputFrameCount fnSetInputFrameCount[2] = { PaUtil_SetInputFrameCount, PaUtil_Set2ndInputFrameCount }; +static const TSetInputChannel fnSetInputChannel[2] = { PaUtil_SetInputChannel, PaUtil_Set2ndInputChannel }; + +static PaError PaDoProcessing(PaProcessThreadInfo* pInfo) +{ + PaError result = paNoError; + int i, framesProcessed = 0, doChannelCopy = 0; + ring_buffer_size_t inputFramesAvailable = PaUtil_GetRingBufferReadAvailable(&pInfo->stream->ringBuffer); + + /* Do necessary buffer processing (which will invoke user callback if necessary) */ + if (pInfo->cbResult == paContinue && + (pInfo->renderHead != pInfo->renderTail || inputFramesAvailable)) + { + unsigned processFullDuplex = pInfo->stream->capture.pPin && pInfo->stream->render.pPin && (!pInfo->priming); + + PA_HP_TRACE((pInfo->stream->hLog, "DoProcessing: InputFrames=%u", inputFramesAvailable)); + + PaUtil_BeginCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer ); + + pInfo->ti.currentTime = PaUtil_GetTime(); + + PaUtil_BeginBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->ti, pInfo->underover); + pInfo->underover = 0; /* Reset the (under|over)flow status */ + + if (pInfo->renderTail != pInfo->renderHead) + { + DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet; + + assert(packet != 0); + assert(packet->Header.Data != 0); + + PaUtil_SetOutputFrameCount(&pInfo->stream->bufferProcessor, pInfo->stream->render.framesPerBuffer); + + for(i=0;istream->userOutputChannels;i++) + { + /* Only write the user output channels. Leave the rest blank */ + PaUtil_SetOutputChannel(&pInfo->stream->bufferProcessor, + i, + ((unsigned char*)(packet->Header.Data))+(i*pInfo->stream->render.bytesPerSample), + pInfo->stream->deviceOutputChannels); + } + + /* We will do a copy to the other channels after the data has been written */ + doChannelCopy = ( pInfo->stream->userOutputChannels == 1 ); + } + + if (inputFramesAvailable && (!pInfo->stream->userOutputChannels || inputFramesAvailable >= (int)pInfo->stream->render.framesPerBuffer)) + { + unsigned wrapCntr = 0; + char* data[2] = {0}; + ring_buffer_size_t size[2] = {0}; + + /* If full-duplex, we just extract output buffer number of frames */ + if (pInfo->stream->userOutputChannels) + { + inputFramesAvailable = min(inputFramesAvailable, (int)pInfo->stream->render.framesPerBuffer); + } + + inputFramesAvailable = PaUtil_GetRingBufferReadRegions(&pInfo->stream->ringBuffer, + inputFramesAvailable, + &data[0], + &size[0], + &data[1], + &size[1]); + + for (wrapCntr = 0; wrapCntr < 2; ++wrapCntr) + { + if (size[wrapCntr] == 0) + break; + + fnSetInputFrameCount[wrapCntr](&pInfo->stream->bufferProcessor, size[wrapCntr]); + for(i=0;istream->userInputChannels;i++) + { + /* Only read as many channels as the user wants */ + fnSetInputChannel[wrapCntr](&pInfo->stream->bufferProcessor, + i, + ((unsigned char*)(data[wrapCntr]))+(i*pInfo->stream->capture.bytesPerSample), + pInfo->stream->deviceInputChannels); + } + } + } + else + { + /* We haven't consumed anything from the ring buffer... */ + inputFramesAvailable = 0; + /* If we have full-duplex, this is at startup, so mark no-input! */ + if (pInfo->stream->userOutputChannels>0 && pInfo->stream->userInputChannels>0) + { + PA_HP_TRACE((pInfo->stream->hLog, "Input startup, marking no input.")); + PaUtil_SetNoInput(&pInfo->stream->bufferProcessor); + } + } + + if (processFullDuplex) /* full duplex */ + { + /* Only call the EndBufferProcessing function when the total input frames == total output frames */ + const unsigned long totalInputFrameCount = pInfo->stream->bufferProcessor.hostInputFrameCount[0] + pInfo->stream->bufferProcessor.hostInputFrameCount[1]; + const unsigned long totalOutputFrameCount = pInfo->stream->bufferProcessor.hostOutputFrameCount[0] + pInfo->stream->bufferProcessor.hostOutputFrameCount[1]; + + if(totalInputFrameCount == totalOutputFrameCount && totalOutputFrameCount != 0) + { + framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult); + } + else + { + framesProcessed = 0; + } + } + else + { + framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult); + } + + PA_HP_TRACE((pInfo->stream->hLog, "Frames processed: %u %s", framesProcessed, (pInfo->priming ? "(priming)":""))); + + if( doChannelCopy ) + { + DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet; + /* Copy the first output channel to the other channels */ + switch (pInfo->stream->render.bytesPerSample) + { + case 2: + DuplicateFirstChannelInt16(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer); + break; + case 3: + DuplicateFirstChannelInt24(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer); + break; + case 4: + DuplicateFirstChannelInt32(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer); + break; + default: + assert(0); /* Unsupported format! */ + break; + } + } + PaUtil_EndCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer, framesProcessed ); + + if (inputFramesAvailable) + { + PaUtil_AdvanceRingBufferReadIndex(&pInfo->stream->ringBuffer, inputFramesAvailable); + } + + if (pInfo->renderTail != pInfo->renderHead) + { + if (!pInfo->stream->streamStop) + { + result = pInfo->stream->render.pPin->fnSubmitHandler(pInfo, pInfo->renderTail); + if (result != paNoError) + { + PA_HP_TRACE((pInfo->stream->hLog, "Capture submit handler failed with result %d", result)); + return result; + } + } + pInfo->renderTail++; + if (!pInfo->pinsStarted && pInfo->priming == 0) + { + /* We start the pins here to allow "prime time" */ + if ((result = StartPins(pInfo)) == paNoError) + { + PA_HP_TRACE((pInfo->stream->hLog, "Starting pins!")); + pInfo->pinsStarted = 1; + } + } + } + } + + return result; +} + +static VOID CALLBACK TimerAPCWaveRTPolledMode( + LPVOID lpArgToCompletionRoutine, + DWORD dwTimerLowValue, + DWORD dwTimerHighValue) +{ + HANDLE* pHandles = (HANDLE*)lpArgToCompletionRoutine; + if (pHandles[0]) SetEvent(pHandles[0]); + if (pHandles[1]) SetEvent(pHandles[1]); +} + +static DWORD GetCurrentTimeInMillisecs() +{ + return timeGetTime(); +} + +PA_THREAD_FUNC ProcessingThread(void* pParam) +{ + PaError result = paNoError; + HANDLE hAVRT = NULL; + HANDLE hTimer = NULL; + HANDLE *handleArray = NULL; + HANDLE timerEventHandles[2] = {0}; + unsigned noOfHandles = 0; + unsigned captureEvents = 0; + unsigned renderEvents = 0; + unsigned timerPeriod = 0; + DWORD timeStamp[2] = {0}; + + PaProcessThreadInfo info; + memset(&info, 0, sizeof(PaProcessThreadInfo)); + info.stream = (PaWinWdmStream*)pParam; + + info.stream->threadResult = paNoError; PA_LOGE_; - ti.inputBufferAdcTime = 0.0; - ti.currentTime = 0.0; - ti.outputBufferDacTime = 0.0; + info.ti.inputBufferAdcTime = 0.0; + info.ti.currentTime = 0.0; + info.ti.outputBufferDacTime = 0.0; - /* Get double buffering going */ + PA_DEBUG(("In buffer len: %.3f ms\n",(2000*info.stream->capture.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate)); + PA_DEBUG(("Out buffer len: %.3f ms\n",(2000*info.stream->render.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate)); + info.timeout = (DWORD)max( + (2000*info.stream->render.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5), + (2000*info.stream->capture.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5)); + info.timeout = max(info.timeout*8, 100); + timerPeriod = info.timeout; + PA_DEBUG(("Timeout = %ld ms\n",info.timeout)); - /* Submit buffers */ - if(stream->playbackPin) + /* Allocate handle array */ + handleArray = (HANDLE*)PaUtil_AllocateMemory((info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1) * sizeof(HANDLE)); + + /* Setup handle array for WFMO */ + if (info.stream->capture.pPin != 0) { - 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) + handleArray[noOfHandles++] = info.stream->capture.events[0]; + if (info.stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic) { - PA_DEBUG(("ss1:pending=%d ",pending)); + unsigned i; + for(i=1; i < info.stream->capture.noOfPackets; ++i) + { + handleArray[noOfHandles++] = info.stream->capture.events[i]; + } } - wait = WaitForMultipleObjects(5, stream->events, FALSE, 0); - if( wait == WAIT_TIMEOUT ) + captureEvents = noOfHandles; + renderEvents = noOfHandles; + } + + if (info.stream->render.pPin != 0) + { + handleArray[noOfHandles++] = info.stream->render.events[0]; + if (info.stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic) { - /* No (under|over)flow has ocurred */ - wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout); - eventSignaled = wait - WAIT_OBJECT_0; + unsigned i; + for(i=1; i < info.stream->render.noOfPackets; ++i) + { + handleArray[noOfHandles++] = info.stream->render.events[i]; + } + } + renderEvents = noOfHandles; + } + handleArray[noOfHandles++] = info.stream->eventAbort; + assert(noOfHandles <= (info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1)); + + /* Prepare render and capture pins */ + if ((result = PreparePinsForStart(&info)) != paNoError) + { + PA_DEBUG(("Failed to prepare device(s)!\n")); + goto error; + } + + /* Init high speed logger */ + if (PaUtil_InitializeHighSpeedLog(&info.stream->hLog, 1000000) != paNoError) + { + PA_DEBUG(("Failed to init high speed logger!\n")); + goto error; + } + + /* Heighten priority here */ + hAVRT = BumpThreadPriority(); + + /* If input only, we start the pins immediately */ + if (info.stream->render.pPin == 0) + { + if ((result = StartPins(&info)) != paNoError) + { + PA_DEBUG(("Failed to start device(s)!\n")); + goto error; + } + info.pinsStarted = 1; + } + + /* Handle WaveRT polled mode */ + { + const unsigned fs = (unsigned)info.stream->streamRepresentation.streamInfo.sampleRate; + if (info.stream->capture.pPin != 0 && info.stream->capture.pPin->pinKsSubType == SubType_kPolled) + { + timerEventHandles[0] = info.stream->capture.events[0]; + timerPeriod = min(timerPeriod, (1000*info.stream->capture.framesPerBuffer)/fs); + } + + if (info.stream->render.pPin != 0 && info.stream->render.pPin->pinKsSubType == SubType_kPolled) + { + timerEventHandles[1] = info.stream->render.events[0]; + timerPeriod = min(timerPeriod, (1000*info.stream->render.framesPerBuffer)/fs); + } + + if (timerEventHandles[0] || timerEventHandles[1]) + { + LARGE_INTEGER dueTime = {0}; + + timerPeriod=max(timerPeriod/5,1); + PA_DEBUG(("Timer event handles=0x%04X,0x%04X period=%u ms", timerEventHandles[0], timerEventHandles[1], timerPeriod)); + hTimer = CreateWaitableTimer(0, FALSE, NULL); + if (hTimer == NULL) + { + result = paUnanticipatedHostError; + goto error; + } + /* invoke first timeout immediately */ + if (!SetWaitableTimer(hTimer, &dueTime, timerPeriod, TimerAPCWaveRTPolledMode, timerEventHandles, FALSE)) + { + result = paUnanticipatedHostError; + goto error; + } + PA_DEBUG(("Waitable timer started, period = %u ms\n", timerPeriod)); + } + } + + /* Mark stream as active */ + info.stream->streamActive = 1; + info.stream->threadResult = paNoError; + + /* Up and running... */ + SetEvent(info.stream->eventStreamStart[StreamStart_kOk]); + + /* Take timestamp here */ + timeStamp[0] = timeStamp[1] = GetCurrentTimeInMillisecs(); + + while(!info.stream->streamAbort) + { + unsigned doProcessing = 1; + unsigned wait = WaitForMultipleObjects(noOfHandles, handleArray, FALSE, 0); + unsigned eventSignalled = wait - WAIT_OBJECT_0; + DWORD dwCurrentTime = 0; + + if (wait == WAIT_FAILED) + { + PA_DEBUG(("Wait failed = %ld! \n",wait)); + break; + } + if (wait == WAIT_TIMEOUT) + { + wait = WaitForMultipleObjectsEx(noOfHandles, handleArray, FALSE, 50, TRUE); + eventSignalled = wait - WAIT_OBJECT_0; } else { - eventSignaled = wait - WAIT_OBJECT_0; - if( eventSignaled < 2 ) + if (eventSignalled < captureEvents) { - underover |= paInputOverflow; - PA_DEBUG(("Input overflow\n")); + if (PaUtil_GetRingBufferWriteAvailable(&info.stream->ringBuffer) == 0) + { + PA_HP_TRACE((info.stream->hLog, "!!!!! Input overflow !!!!!")); + info.underover |= paInputOverflow; + } } - else if(( eventSignaled < 4 )&&(!priming)) + else if (eventSignalled < renderEvents) { - underover |= paOutputUnderflow; - PA_DEBUG(("Output underflow\n")); + if (!info.priming && info.renderHead - info.renderTail > 1) + { + PA_HP_TRACE((info.stream->hLog, "!!!!! Output underflow !!!!!")); + info.underover |= paOutputUnderflow; + } } } - if(stream->streamStop) + /* Get event time */ + dwCurrentTime = GetCurrentTimeInMillisecs(); + + /* Since we can mix capture/render devices between WaveCyclic, WaveRT polled and WaveRT notification (3x3 combinations), + we can't rely on the timeout of WFMO to check for device timeouts, we need to keep tally. */ + if (info.stream->capture.pPin && (dwCurrentTime - timeStamp[0]) >= info.timeout) { - PA_DEBUG(("ss2:wait=%ld",wait)); - } - if(wait == WAIT_FAILED) - { - PA_DEBUG(("Wait fail = %ld! ",wait)); + PA_DEBUG(("Timeout for capture device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[0]))); + result = paTimedOut; break; } - if(wait == WAIT_TIMEOUT) + if (info.stream->render.pPin && (dwCurrentTime - timeStamp[1]) >= info.timeout) { + PA_DEBUG(("Timeout for render device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[1]))); + result = paTimedOut; + break; + } + + if (wait == WAIT_IO_COMPLETION) + { + /* Waitable timer has fired! */ + PA_HP_TRACE((info.stream->hLog, "WAIT_IO_COMPLETION")); 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--; + if (wait == WAIT_TIMEOUT) + { + continue; } 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) + if (eventSignalled < captureEvents) { - PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0); - if( stream->userOutputChannels == 1 ) + if (info.stream->capture.pPin->fnEventHandler(&info, eventSignalled) == paNoError) { - /* 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++) + timeStamp[0] = dwCurrentTime; + + /* Since we use the ring buffer, we can submit the buffers directly */ + if (!info.stream->streamStop) { - /* 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); + result = info.stream->capture.pPin->fnSubmitHandler(&info, info.captureTail); + if (result != paNoError) + { + PA_HP_TRACE((info.stream->hLog, "Capture submit handler failed with result %d", result)); + break; + } + } + ++info.captureTail; + /* If full-duplex, let _only_ render event trigger processing. We still need the stream stop + handling working, so let that be processed anyways... */ + if (info.stream->userOutputChannels > 0) + { + doProcessing = 0; } } } - if(emptyRecordbuf) + else if (eventSignalled < renderEvents) { - 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); - } + timeStamp[1] = dwCurrentTime; + eventSignalled -= captureEvents; + info.stream->render.pPin->fnEventHandler(&info, eventSignalled); } - - if (stream->recordingPin && stream->playbackPin) /* full duplex */ + else { - /* Only call the EndBufferProcessing function when the total input frames == total output frames */ + assert(info.stream->streamAbort); + PA_HP_TRACE((info.stream->hLog, "Stream abort!")); + continue; + } + } - 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; - } - } - else + /* Handle processing */ + if (doProcessing) + { + result = PaDoProcessing(&info); + if (result != paNoError) { - framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult); + PA_HP_TRACE((info.stream->hLog, "PaDoProcessing failed!")); + break; } + } - 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 + if(info.stream->streamStop && info.cbResult != paComplete) { - fillPlaybuf = 0; - emptyRecordbuf = 0; + PA_HP_TRACE((info.stream->hLog, "Stream stop! pending=%d",info.pending)); + info.cbResult = paComplete; /* Stop, but play remaining buffers */ } - - /* - if(cbResult != paContinue) + + if(info.pending<=0) { - 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...;")); + PA_HP_TRACE((info.stream->hLog, "pending==0 finished...")); break; } - if((!stream->playbackPin)&&(cbResult!=paContinue)) + if((!info.stream->render.pPin)&&(info.cbResult!=paContinue)) { - PA_DEBUG(("record only cbResult=%d...;",cbResult)); + PA_HP_TRACE((info.stream->hLog, "record only cbResult=%d...",info.cbResult)); break; } } - PA_DEBUG(("Finished thread")); + PA_DEBUG(("Finished processing loop\n")); - /* Finished, either normally or aborted */ - if(stream->playbackPin) + info.stream->threadResult = result; + goto bailout; + +error: + PA_DEBUG(("Error starting processing thread\n")); + /* Set the "error" event together with result */ + info.stream->threadResult = result; + SetEvent(info.stream->eventStreamStart[StreamStart_kFailed]); + +bailout: + if (hTimer) { - 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); + PA_DEBUG(("Waitable timer stopped\n", timerPeriod)); + CancelWaitableTimer(hTimer); + CloseHandle(hTimer); + hTimer = 0; } - stream->streamActive = 0; - - if((!stream->streamStop)&&(!stream->streamAbort)) + if (info.pinsStarted) { - /* 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 ); + StopPins(&info); } - stream->streamStop = 0; - stream->streamAbort = 0; - /* Reset process priority if necessary */ - if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS) + /* Lower prio here */ + DropThreadPriority(hAVRT); + + if (handleArray != NULL) { - SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority); - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; + PaUtil_FreeMemory(handleArray); } +#if PA_TRACE_REALTIME_EVENTS + if (info.stream->hLog) + { + PA_DEBUG(("Dumping highspeed trace...\n")); + PaUtil_DumpHighSpeedLog(info.stream->hLog, "hp_trace.log"); + PaUtil_DiscardHighSpeedLog(info.stream->hLog); + info.stream->hLog = 0; + } +#endif + info.stream->streamActive = 0; + + if((!info.stream->streamStop)&&(!info.stream->streamAbort)) + { + /* Invoke the user stream finished callback */ + /* Only do it from here if not being stopped/aborted by user */ + if( info.stream->streamRepresentation.streamFinishedCallback != 0 ) + info.stream->streamRepresentation.streamFinishedCallback( info.stream->streamRepresentation.userData ); + } + info.stream->streamStop = 0; + info.stream->streamAbort = 0; + PA_LOGL_; - EXIT_THREAD; 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]); - } - } + + ResetStreamEvents(stream); 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 */ + * 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));*/ + PA_DEBUG(("Class ret = %d;",ret));*/ - stream->streamStarted = 1; - stream->streamThread = (HANDLE)_beginthreadex(NULL, 0, ProcessingThread, stream, 0, &dwID); + stream->streamThread = CREATE_THREAD_FUNCTION (NULL, 0, ProcessingThread, stream, CREATE_SUSPENDED, &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; + ResumeThread(stream->streamThread); + + switch (WaitForMultipleObjects(2, stream->eventStreamStart, FALSE, 5000)) + { + case WAIT_OBJECT_0 + StreamStart_kOk: + PA_DEBUG(("Processing thread started!\n")); + result = paNoError; + /* streamActive is set in processing thread */ + stream->streamStarted = 1; + break; + case WAIT_OBJECT_0 + StreamStart_kFailed: + PA_DEBUG(("Processing thread start failed! (result=%d)\n", stream->threadResult)); + result = stream->threadResult; + /* Wait for the stream to really exit */ + WaitForSingleObject(stream->streamThread, 200); + CloseHandle(stream->streamThread); + stream->streamThread = 0; + break; + case WAIT_TIMEOUT: + default: + result = paTimedOut; + PaWinWDM_SetLastErrorInfo(result, "Failed to start processing thread (timeout)!"); + break; + } end: PA_LOGL_; @@ -3098,29 +6107,39 @@ static PaError StopStream( PaStream *s ) if(stream->streamActive) { + DWORD dwExitCode; doCb = 1; stream->streamStop = 1; - while(stream->streamActive) + if (GetExitCodeThread(stream->streamThread, &dwExitCode) && dwExitCode == STILL_ACTIVE) { - PA_DEBUG(("W.")); - Sleep(10); /* Let thread sleep for 10 msec */ + if (WaitForSingleObject(stream->streamThread, INFINITE) != WAIT_OBJECT_0) + { + PA_DEBUG(("StopStream: stream thread terminated\n")); + TerminateThread(stream->streamThread, -1); + result = paTimedOut; + } + } + else + { + PA_DEBUG(("StopStream: GECT says not active, but streamActive is not false ??")); + result = paUnanticipatedHostError; + PaWinWDM_SetLastErrorInfo(result, "StopStream: GECT says not active, but streamActive = %d", stream->streamActive); + } + } + else + { + if (stream->threadResult != paNoError) + { + PA_DEBUG(("StopStream: Stream not active (%d)\n", stream->threadResult)); + result = stream->threadResult; + stream->threadResult = paNoError; } } - PA_DEBUG(("Terminating thread")); - if(stream->streamStarted && stream->streamThread) - { - TerminateThread(stream->streamThread,0); - stream->streamThread = NULL; - } - + CloseHandle(stream->streamThread); + stream->streamThread = 0; stream->streamStarted = 0; - - if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS) - { - SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority); - stream->oldProcessPriority = REALTIME_PRIORITY_CLASS; - } + stream->streamActive = 0; if(doCb) { @@ -3128,7 +6147,7 @@ static PaError StopStream( PaStream *s ) /* 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->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); } PA_LOGL_; @@ -3147,27 +6166,20 @@ static PaError AbortStream( PaStream *s ) { doCb = 1; stream->streamAbort = 1; - SetEvent(stream->events[4]); /* Signal immediately */ - while(stream->streamActive) + SetEvent(stream->eventAbort); /* Signal immediately */ + if (WaitForSingleObject(stream->streamThread, 10000) != WAIT_OBJECT_0) { - Sleep(10); + TerminateThread(stream->streamThread, -1); + result = paTimedOut; + + PA_DEBUG(("AbortStream: stream thread terminated\n")); } + assert(!stream->streamActive); } - - if(stream->streamStarted && stream->streamThread) - { - TerminateThread(stream->streamThread,0); - stream->streamThread = NULL; - } - + CloseHandle(stream->streamThread); + 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 */ @@ -3236,13 +6248,32 @@ static double GetStreamCpuLoad( PaStream* s ) /* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. +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, + 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 paInternalError; +} + + +static PaError WriteStream( PaStream* s, + const void *buffer, unsigned long frames ) { PaWinWdmStream *stream = (PaWinWdmStream*)s; @@ -3256,26 +6287,7 @@ static PaError ReadStream( PaStream* s, /* 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; + return paInternalError; } @@ -3305,4 +6317,296 @@ static signed long GetStreamWriteAvailable( PaStream* s ) /* IMPLEMENT ME, see portaudio.h for required behavior*/ PA_LOGL_; return 0; -} \ No newline at end of file +} + +/***************************************************************************************/ +/* Event and submit handlers for WaveCyclic */ +/***************************************************************************************/ + +static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + ring_buffer_size_t frameCount; + DATAPACKET* packet = pInfo->stream->capture.packets + eventIndex; + + assert( eventIndex < pInfo->stream->capture.noOfPackets ); + + if (packet->Header.DataUsed == 0) + { + PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture bogus event: idx=%u (DataUsed=%u)", eventIndex, packet->Header.DataUsed)); + + /* Bogus event, reset! This is to handle the behavior of this USB mic: http://shop.xtz.se/measurement-system/microphone-to-dirac-live-room-correction-suite + on startup of streaming, where it erroneously sets the event without the corresponding buffer being filled (DataUsed == 0) */ + ResetEvent(packet->Signal.hEvent); + return -1; + } + + pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet; + + frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pInfo->stream->capture.framesPerBuffer); + + PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture event: idx=%u (frames=%u)", eventIndex, frameCount)); + ++pInfo->captureHead; + --pInfo->pending; + return paNoError; +} + +static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + PaError result = paNoError; + DATAPACKET* packet = pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet; + pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0; + assert(packet != 0); + PA_HP_TRACE((pInfo->stream->hLog, "Capture submit: %u", eventIndex)); + packet->Header.DataUsed = 0; /* Reset for reuse */ + ResetEvent(packet->Signal.hEvent); + result = PinRead(pInfo->stream->capture.pPin->handle, packet); + ++pInfo->pending; + return result; +} + +static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + assert( eventIndex < pInfo->stream->render.noOfPackets ); + + pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask].packet = pInfo->stream->render.packets + eventIndex; + PA_HP_TRACE((pInfo->stream->hLog, "<<< Render event : idx=%u head=%u", eventIndex, pInfo->renderHead)); + ++pInfo->renderHead; + --pInfo->pending; + return paNoError; +} + +static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + PaError result = paNoError; + DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet; + pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0; + assert(packet != 0); + + PA_HP_TRACE((pInfo->stream->hLog, "Render submit : %u idx=%u", pInfo->renderTail, (unsigned)(packet - pInfo->stream->render.packets))); + ResetEvent(packet->Signal.hEvent); + result = PinWrite(pInfo->stream->render.pPin->handle, packet); + /* Reset event, just in case we have an analogous situation to capture (see PaPinCaptureSubmitHandler_WaveCyclic) */ + ++pInfo->pending; + if (pInfo->priming) + { + --pInfo->priming; + } + return result; +} + +/***************************************************************************************/ +/* Event and submit handlers for WaveRT */ +/***************************************************************************************/ + +static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + unsigned long pos; + unsigned realInBuf; + unsigned frameCount; + PaWinWdmIOInfo* pCapture = &pInfo->stream->capture; + const unsigned halfInputBuffer = pCapture->hostBufferSize >> 1; + PaWinWdmPin* pin = pCapture->pPin; + DATAPACKET* packet = 0; + + /* Get hold of current ADC position */ + pin->fnAudioPosition(pin, &pos); + /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from + where it should be, i.e. at beginning or half buffer position. Why? No idea.) */ + + pos %= pCapture->hostBufferSize; + /* Then realInBuf will point to "other" half of double buffer */ + realInBuf = pos < halfInputBuffer ? 1U : 0U; + + packet = pInfo->stream->capture.packets + realInBuf; + + /* Call barrier (or dummy) */ + pin->fnMemBarrier(); + + /* Put it in queue */ + frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pCapture->framesPerBuffer); + + pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet; + + PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRT): idx=%u head=%u (pos = %4.1lf%%, frames=%u)", realInBuf, pInfo->captureHead, (pos * 100.0 / pCapture->hostBufferSize), frameCount)); + + ++pInfo->captureHead; + --pInfo->pending; + + return paNoError; +} + +static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + unsigned long pos; + unsigned bytesToRead; + PaWinWdmIOInfo* pCapture = &pInfo->stream->capture; + const unsigned halfInputBuffer = pCapture->hostBufferSize>>1; + PaWinWdmPin* pin = pInfo->stream->capture.pPin; + + /* Get hold of current ADC position */ + pin->fnAudioPosition(pin, &pos); + /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from + where it should be, i.e. at beginning or half buffer position. Why? No idea.) */ + /* Compensate for HW FIFO to get to last read buffer position */ + pos += pin->hwLatency; + pos %= pCapture->hostBufferSize; + /* Need to align position on frame boundary */ + pos &= ~(pCapture->bytesPerFrame - 1); + + /* Call barrier (or dummy) */ + pin->fnMemBarrier(); + + /* Put it in "queue" */ + bytesToRead = (pCapture->hostBufferSize + pos - pCapture->lastPosition) % pCapture->hostBufferSize; + if (bytesToRead > 0) + { + unsigned frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, + pCapture->hostBuffer + pCapture->lastPosition, + bytesToRead / pCapture->bytesPerFrame); + + pCapture->lastPosition = (pCapture->lastPosition + frameCount * pCapture->bytesPerFrame) % pCapture->hostBufferSize; + + PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRTPolled): pos = %4.1lf%%, framesRead=%u", (pos * 100.0 / pCapture->hostBufferSize), frameCount)); + ++pInfo->captureHead; + --pInfo->pending; + } + return paNoError; +} + +static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0; + ++pInfo->pending; + return paNoError; +} + +static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0; + ++pInfo->pending; + return paNoError; +} + +static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + unsigned long pos; + unsigned realOutBuf; + PaWinWdmIOInfo* pRender = &pInfo->stream->render; + const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1; + PaWinWdmPin* pin = pInfo->stream->render.pPin; + PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask]; + + /* Get hold of current DAC position */ + pin->fnAudioPosition(pin, &pos); + /* Compensate for HW FIFO to get to last read buffer position */ + pos += pin->hwLatency; + /* Wrap it */ + pos %= pRender->hostBufferSize; + /* And align it, not sure its really needed though */ + pos &= ~(pRender->bytesPerFrame - 1); + /* Then realOutBuf will point to "other" half of double buffer */ + realOutBuf = pos < halfOutputBuffer ? 1U : 0U; + + if (pInfo->priming) + { + realOutBuf = pInfo->renderHead & 0x1; + } + ioPacket->packet = pInfo->stream->render.packets + realOutBuf; + ioPacket->startByte = realOutBuf * halfOutputBuffer; + ioPacket->lengthBytes = halfOutputBuffer; + + PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRT) : idx=%u head=%u (pos = %4.1lf%%)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize) )); + + ++pInfo->renderHead; + --pInfo->pending; + return paNoError; +} + +static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + unsigned long pos; + unsigned realOutBuf; + unsigned bytesToWrite; + + PaWinWdmIOInfo* pRender = &pInfo->stream->render; + const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1; + PaWinWdmPin* pin = pInfo->stream->render.pPin; + PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask]; + + /* Get hold of current DAC position */ + pin->fnAudioPosition(pin, &pos); + /* Compensate for HW FIFO to get to last read buffer position */ + pos += pin->hwLatency; + /* Wrap it */ + pos %= pRender->hostBufferSize; + /* And align it, not sure its really needed though */ + pos &= ~(pRender->bytesPerFrame - 1); + + if (pInfo->priming) + { + realOutBuf = pInfo->renderHead & 0x1; + ioPacket->packet = pInfo->stream->render.packets + realOutBuf; + ioPacket->startByte = realOutBuf * halfOutputBuffer; + ioPacket->lengthBytes = halfOutputBuffer; + ++pInfo->renderHead; + --pInfo->pending; + } + else + { + bytesToWrite = (pRender->hostBufferSize + pos - pRender->lastPosition) % pRender->hostBufferSize; + ++pRender->pollCntr; + if (bytesToWrite >= halfOutputBuffer) + { + realOutBuf = (pos < halfOutputBuffer) ? 1U : 0U; + ioPacket->packet = pInfo->stream->render.packets + realOutBuf; + pRender->lastPosition = realOutBuf ? 0U : halfOutputBuffer; + ioPacket->startByte = realOutBuf * halfOutputBuffer; + ioPacket->lengthBytes = halfOutputBuffer; + ++pInfo->renderHead; + --pInfo->pending; + PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRTPolled) : idx=%u head=%u (pos = %4.1lf%%, cnt=%u)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize), pRender->pollCntr)); + pRender->pollCntr = 0; + } + } + return paNoError; +} + +static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + PaWinWdmPin* pin = pInfo->stream->render.pPin; + pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0; + /* Call barrier (if needed) */ + pin->fnMemBarrier(); + PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRT) : submit=%u", pInfo->renderTail)); + ++pInfo->pending; + if (pInfo->priming) + { + --pInfo->priming; + if (pInfo->priming) + { + PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)")); + SetEvent(pInfo->stream->render.events[0]); + } + } + return paNoError; +} + +static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex) +{ + PaWinWdmPin* pin = pInfo->stream->render.pPin; + pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0; + /* Call barrier (if needed) */ + pin->fnMemBarrier(); + PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRTPolled) : submit=%u", pInfo->renderTail)); + ++pInfo->pending; + if (pInfo->priming) + { + --pInfo->priming; + if (pInfo->priming) + { + PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)")); + SetEvent(pInfo->stream->render.events[0]); + } + } + return paNoError; +} diff --git a/3rdparty/portaudio/src/hostapi/wdmks/readme.txt b/3rdparty/portaudio/src/hostapi/wdmks/readme.txt index 2a366f237e..b3faf33856 100644 --- a/3rdparty/portaudio/src/hostapi/wdmks/readme.txt +++ b/3rdparty/portaudio/src/hostapi/wdmks/readme.txt @@ -3,12 +3,15 @@ Notes about WDM-KS host API Status history -------------- +16th January 2011: +Added support for WaveRT device API (Vista and later) for even lesser +latency support. + 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. + with 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 @@ -71,7 +74,7 @@ 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, +PortAudio, you must define PA_USE_WDMKS=1 in 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 @@ -79,4 +82,4 @@ 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 +with G++(CPP).