mirror of
https://github.com/holub/mame
synced 2025-04-17 22:13:04 +03:00
3rdparty/portaudio: Updated to latest upstream version. (#11604)
Up-to-date with revision 24c8d575e588d557d28f4011becb753421346860. Resolves issues building with Visual Studio. Enabled PortAudio when building with Visual Studio and clang-cl. docs: Removed note about duplicate GUID symbols in PortAudio when built with MSVC.
This commit is contained in:
parent
07b3bdd04f
commit
963561c7cb
65
3rdparty/portaudio/CMakeLists.txt
vendored
65
3rdparty/portaudio/CMakeLists.txt
vendored
@ -15,6 +15,33 @@ else()
|
||||
set(LIBRARY_BUILD_TYPE STATIC)
|
||||
endif()
|
||||
|
||||
option(PA_WARNINGS_ARE_ERRORS "Turn compiler warnings into errors" OFF)
|
||||
if(PA_WARNINGS_ARE_ERRORS)
|
||||
if(MSVC)
|
||||
add_compile_options(/WX
|
||||
# "Grandfathered" warnings that existed before we started enforcement.
|
||||
# Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning.
|
||||
# TODO: fix the offending code so that we don't have to exclude specific warnings anymore.
|
||||
/wd4244 # W2 conversion possible loss of data
|
||||
/wd4267 # W3 conversion possible loss of data
|
||||
/wd4996 # W3 unsafe/deprecated
|
||||
)
|
||||
else()
|
||||
add_compile_options(-Werror
|
||||
# "Grandfathered" warnings that existed before we started enforcement.
|
||||
# Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning.
|
||||
# TODO: fix the offending code so that we don't have to exclude specific warnings anymore.
|
||||
-Wno-error=deprecated-declarations # https://github.com/PortAudio/portaudio/issues/213 https://github.com/PortAudio/portaudio/issues/641
|
||||
-Wno-error=stringop-overflow
|
||||
)
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
# Don't fail on older clang versions that don't recognize the latest warnings in the list above.
|
||||
# Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional.
|
||||
add_compile_options(-Wno-error=unknown-warning-option)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(PortAudio
|
||||
${LIBRARY_BUILD_TYPE}
|
||||
src/common/pa_allocation.c
|
||||
@ -130,6 +157,8 @@ if(WIN32)
|
||||
src/os/win/pa_win_hostapis.c
|
||||
src/os/win/pa_win_util.c
|
||||
src/os/win/pa_win_util.h
|
||||
src/os/win/pa_win_version.c
|
||||
src/os/win/pa_win_version.h
|
||||
src/os/win/pa_win_waveformat.c
|
||||
src/os/win/pa_win_wdmks_utils.h
|
||||
src/os/win/pa_x86_plain_converters.h
|
||||
@ -278,17 +307,13 @@ elseif(UNIX)
|
||||
target_include_directories(PortAudio PRIVATE src/hostapi/coreaudio)
|
||||
set(PORTAUDIO_PUBLIC_HEADERS "${PORTAUDIO_PUBLIC_HEADERS}" include/pa_mac_core.h)
|
||||
|
||||
find_library(COREAUDIO_LIBRARY CoreAudio REQUIRED)
|
||||
find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox REQUIRED)
|
||||
find_library(AUDIOUNIT_LIBRARY AudioUnit REQUIRED)
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED)
|
||||
find_library(CORESERVICES_LIBRARY CoreServices REQUIRED)
|
||||
target_link_libraries(PortAudio PRIVATE
|
||||
"${COREAUDIO_LIBRARY}"
|
||||
"${AUDIOTOOLBOX_LIBRARY}"
|
||||
"${AUDIOUNIT_LIBRARY}"
|
||||
"${COREFOUNDATION_LIBRARY}"
|
||||
"${CORESERVICES_LIBRARY}"
|
||||
target_link_libraries(PortAudio
|
||||
PRIVATE
|
||||
-Wl,-framework,CoreAudio
|
||||
-Wl,-framework,AudioToolbox
|
||||
-Wl,-framework,AudioUnit
|
||||
-Wl,-framework,CoreFoundation
|
||||
-Wl,-framework,CoreServices
|
||||
)
|
||||
target_compile_definitions(PortAudio PUBLIC PA_USE_COREAUDIO=1)
|
||||
set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_COREAUDIO=1")
|
||||
@ -340,6 +365,24 @@ elseif(UNIX)
|
||||
target_compile_definitions(PortAudio PUBLIC PA_USE_AUDIOIO=1)
|
||||
set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_AUDIOIO=1")
|
||||
endif()
|
||||
|
||||
find_package(PulseAudio)
|
||||
cmake_dependent_option(PA_USE_PULSEAUDIO "Enable support for PulseAudio general purpose sound server" ON PulseAudio_FOUND OFF)
|
||||
if(PA_USE_PULSEAUDIO)
|
||||
target_link_libraries(PortAudio PRIVATE PulseAudio::PulseAudio)
|
||||
target_sources(PortAudio PRIVATE
|
||||
src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c
|
||||
src/hostapi/pulseaudio/pa_linux_pulseaudio.c
|
||||
src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c)
|
||||
|
||||
target_compile_definitions(PortAudio PUBLIC PA_USE_PULSEAUDIO=1)
|
||||
set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_PULSEAUDIO=1")
|
||||
set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} libpulse")
|
||||
|
||||
# needed for PortAudioConfig.cmake so `find_package(PortAudio)` works in downstream projects
|
||||
install(FILES cmake/modules/FindPulseAudio.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/portaudio/modules")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
7
3rdparty/portaudio/Makefile.in
vendored
7
3rdparty/portaudio/Makefile.in
vendored
@ -44,7 +44,7 @@ PALIB = libportaudio.la
|
||||
PAINC = include/portaudio.h
|
||||
|
||||
PA_LDFLAGS = $(LDFLAGS) $(SHARED_FLAGS) -rpath $(libdir) -no-undefined \
|
||||
-export-symbols-regex "(Pa|PaMacCore|PaJack|PaAlsa|PaAsio|PaOSS|PaWasapi|PaWasapiWinrt|PaWinMME)_.*" \
|
||||
-export-symbols-regex "(Pa|PaMacCore|PaPulseAudio|PaJack|PaAlsa|PaAsio|PaOSS|PaWasapi|PaWasapiWinrt|PaWinMME)_.*" \
|
||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||
|
||||
COMMON_OBJS = \
|
||||
@ -66,7 +66,7 @@ LOOPBACK_OBJS = \
|
||||
qa/loopback/src/test_audio_analyzer.o \
|
||||
qa/loopback/src/write_wav.o \
|
||||
qa/loopback/src/paqa.o
|
||||
|
||||
|
||||
EXAMPLES = \
|
||||
bin/pa_devs \
|
||||
bin/pa_fuzz \
|
||||
@ -82,7 +82,7 @@ SELFTESTS = \
|
||||
bin/paqa_devs \
|
||||
bin/paqa_errs \
|
||||
bin/paqa_latency
|
||||
|
||||
|
||||
TESTS = \
|
||||
bin/patest1 \
|
||||
bin/patest_buffer \
|
||||
@ -146,6 +146,7 @@ SRC_DIRS = \
|
||||
src/hostapi/coreaudio \
|
||||
src/hostapi/dsound \
|
||||
src/hostapi/jack \
|
||||
src/hostapi/pulseaudio \
|
||||
src/hostapi/oss \
|
||||
src/hostapi/skeleton \
|
||||
src/hostapi/wasapi \
|
||||
|
1
3rdparty/portaudio/README.md
vendored
1
3rdparty/portaudio/README.md
vendored
@ -41,6 +41,7 @@ Please feel free to join. See http://www.portaudio.com for details.
|
||||
src/hostapi/dsound = Windows Direct Sound
|
||||
src/hostapi/jack = JACK Audio Connection Kit
|
||||
src/hostapi/oss = Unix Open Sound System (OSS)
|
||||
src/hostapi/pulseaudio = Sound system for POSIX OSes
|
||||
src/hostapi/wasapi = Windows Vista WASAPI
|
||||
src/hostapi/wdmks = Windows WDM Kernel Streaming
|
||||
src/hostapi/wmme = Windows MultiMedia Extensions (MME)
|
||||
|
122
3rdparty/portaudio/bindings/cpp/CMakeLists.txt
vendored
Normal file
122
3rdparty/portaudio/bindings/cpp/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
cmake_policy(VERSION 3.13)
|
||||
|
||||
project(PortAudioCpp VERSION 19.8 LANGUAGES CXX)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
# Todo (multi-generator): Add support for multiple generators like: - {Debug,
|
||||
# Release} x {Static, Dynamic} x {MT, MD (Windows only)}
|
||||
|
||||
# ##############################################################################
|
||||
# sources and headers
|
||||
# ##############################################################################
|
||||
|
||||
set(portaudiocpp-sources
|
||||
source/portaudiocpp/BlockingStream.cxx
|
||||
source/portaudiocpp/CFunCallbackStream.cxx
|
||||
source/portaudiocpp/CallbackInterface.cxx
|
||||
source/portaudiocpp/CallbackStream.cxx
|
||||
source/portaudiocpp/CppFunCallbackStream.cxx
|
||||
source/portaudiocpp/Device.cxx
|
||||
source/portaudiocpp/DirectionSpecificStreamParameters.cxx
|
||||
source/portaudiocpp/Exception.cxx
|
||||
source/portaudiocpp/HostApi.cxx
|
||||
source/portaudiocpp/InterfaceCallbackStream.cxx
|
||||
source/portaudiocpp/MemFunCallbackStream.cxx
|
||||
source/portaudiocpp/Stream.cxx
|
||||
source/portaudiocpp/StreamParameters.cxx
|
||||
source/portaudiocpp/System.cxx
|
||||
source/portaudiocpp/SystemDeviceIterator.cxx
|
||||
source/portaudiocpp/SystemHostApiIterator.cxx)
|
||||
|
||||
# since we don't GLOBing this variable must be kept up to date otherwise user
|
||||
# installations are broken.
|
||||
set(portaudiocpp-header-files
|
||||
include/portaudiocpp/AutoSystem.hxx
|
||||
include/portaudiocpp/BlockingStream.hxx
|
||||
include/portaudiocpp/CFunCallbackStream.hxx
|
||||
include/portaudiocpp/CallbackInterface.hxx
|
||||
include/portaudiocpp/CallbackStream.hxx
|
||||
include/portaudiocpp/CppFunCallbackStream.hxx
|
||||
include/portaudiocpp/Device.hxx
|
||||
include/portaudiocpp/DirectionSpecificStreamParameters.hxx
|
||||
include/portaudiocpp/Exception.hxx
|
||||
include/portaudiocpp/HostApi.hxx
|
||||
include/portaudiocpp/InterfaceCallbackStream.hxx
|
||||
include/portaudiocpp/MemFunCallbackStream.hxx
|
||||
include/portaudiocpp/PortAudioCpp.hxx
|
||||
include/portaudiocpp/SampleDataFormat.hxx
|
||||
include/portaudiocpp/Stream.hxx
|
||||
include/portaudiocpp/StreamParameters.hxx
|
||||
include/portaudiocpp/System.hxx
|
||||
include/portaudiocpp/SystemDeviceIterator.hxx
|
||||
include/portaudiocpp/SystemHostApiIterator.hxx)
|
||||
|
||||
if(WIN32)
|
||||
find_package(ASIO MODULE)
|
||||
if(ASIO_FOUND)
|
||||
list(APPEND portaudiocpp-sources source/portaudiocpp/AsioDeviceAdapter.cxx)
|
||||
list(APPEND portaudiocpp-header-files
|
||||
include/portaudiocpp/AsioDeviceAdapter.hxx)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ##############################################################################
|
||||
# portaudiocpp-targets
|
||||
# ##############################################################################
|
||||
|
||||
add_library(portaudiocpp ${portaudiocpp-sources})
|
||||
add_library(PortAudio::portaudiocpp ALIAS portaudiocpp) # For subdirectory build
|
||||
|
||||
find_package(PortAudio MODULE REQUIRED)
|
||||
|
||||
target_link_libraries(portaudiocpp PUBLIC PortAudio::portaudio)
|
||||
target_include_directories(
|
||||
portaudiocpp PUBLIC $<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
set_target_properties(portaudiocpp PROPERTIES SOVERSION 2)
|
||||
# Todo (modernize): update the code at least to c++14
|
||||
# target_compile_features(portaudiocpp PUBLIC cxx_std_14)
|
||||
|
||||
# ## Export ###
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(
|
||||
TARGETS portaudiocpp
|
||||
EXPORT PortAudioCppTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
INCLUDES
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/portaudiocpp)
|
||||
|
||||
install(FILES ${portaudiocpp-header-files}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/portaudiocpp)
|
||||
|
||||
install(
|
||||
EXPORT PortAudioCppTargets
|
||||
FILE PortAudioCppTargets.cmake
|
||||
NAMESPACE PortAudio::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PortAudio)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/PortAudioCppConfig.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/PortAudioCppConfig.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PortAudio)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/PortAudioCppConfigVersion.cmake"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PortAudioCppConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/PortAudioCppConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PortAudio)
|
||||
|
||||
#use relative path, since CMAKE can't reconfigure on install with different prefix path
|
||||
set(PC_PREFIX "\${pcfiledir}/../..")
|
||||
configure_file(cmake/portaudiocpp.pc.in "${CMAKE_CURRENT_BINARY_DIR}/portaudiocpp.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/portaudiocpp.pc"
|
||||
CONFIGURATIONS Release RelWithDebInfo
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
5
3rdparty/portaudio/bindings/cpp/cmake/PortAudioCppConfig.cmake.in
vendored
Normal file
5
3rdparty/portaudio/bindings/cpp/cmake/PortAudioCppConfig.cmake.in
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/PortAudioCppTargets.cmake")
|
||||
|
||||
check_required_components(PortAudioCpp)
|
81
3rdparty/portaudio/bindings/cpp/cmake/modules/FindASIO.cmake
vendored
Normal file
81
3rdparty/portaudio/bindings/cpp/cmake/modules/FindASIO.cmake
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
#[=======================================================================[.rst:
|
||||
FindASIO
|
||||
--------
|
||||
|
||||
Finds the ASIO SDK by searching for the SDK ZIP in CMAKE_PREFIX_PATH and
|
||||
CMAKE_CURRENT_BINARY_DIR. Alternatively, you may manually specify the path of
|
||||
the SDK ZIP with the ASIO_SDK_ZIP_PATH variable, which can be used for caching
|
||||
in CI scripts.
|
||||
|
||||
If the ZIP is found, this module extracts it.
|
||||
The ZIP extraction is skipped if the unzipped SDK is found.
|
||||
|
||||
This module provides an `ASIO::host` IMPORT library target for building host
|
||||
applications which use ASIO drivers. If you want to build an ASIO driver, this
|
||||
module may serve as a useful start but you will need to modify it.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
if(NOT WIN32)
|
||||
message(WARNING "ASIO is only supported on Windows.")
|
||||
set(ASIO_FOUND OFF)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB HEADER_FILE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/asiosdk*/common/asio.h"
|
||||
"${CMAKE_PREFIX_PATH}/asiosdk*/common/asio.h"
|
||||
# The old build systems before PortAudio 19.8 used to look for the ASIO SDK
|
||||
# in the same parent directory as the source code repository. This is no
|
||||
# longer advised or documented but kept for backwards compatibility.
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../asiosdk*/common/asio.h"
|
||||
)
|
||||
if(NOT EXISTS "${HEADER_FILE}")
|
||||
# The file(ARCHIVE_EXTRACT) command was added in CMake 3.18, so if using an
|
||||
# older version of CMake, the user needs to extract it themselves.
|
||||
if(CMAKE_VERSION VERSION_LESS 3.18)
|
||||
message(STATUS "ASIO SDK NOT found. Download the ASIO SDK ZIP from "
|
||||
"https://www.steinberg.net/asiosdk and extract it to "
|
||||
"${CMAKE_PREFIX_PATH} or ${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
file(GLOB results
|
||||
"${ASIO_SDK_ZIP_PATH}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/asiosdk*.zip"
|
||||
"${CMAKE_PREFIX_PATH}/asiosdk*.zip"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../asiosdk*.zip"
|
||||
)
|
||||
foreach(f ${results})
|
||||
if(EXISTS "${f}")
|
||||
message(STATUS "Extracting ASIO SDK ZIP archive: ${f}")
|
||||
file(ARCHIVE_EXTRACT INPUT "${f}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
file(GLOB HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/asiosdk*/common/asio.h")
|
||||
endif()
|
||||
|
||||
get_filename_component(HEADER_DIR "${HEADER_FILE}" DIRECTORY)
|
||||
get_filename_component(ASIO_ROOT "${HEADER_DIR}" DIRECTORY)
|
||||
|
||||
if(ASIO_ROOT)
|
||||
set(ASIO_FOUND TRUE)
|
||||
message(STATUS "Found ASIO SDK: ${ASIO_ROOT}")
|
||||
|
||||
if(ASIO_FOUND AND NOT TARGET ASIO::host)
|
||||
add_library(ASIO::host INTERFACE IMPORTED)
|
||||
target_sources(ASIO::host INTERFACE
|
||||
"${ASIO_ROOT}/common/asio.cpp"
|
||||
"${ASIO_ROOT}/host/asiodrivers.cpp"
|
||||
"${ASIO_ROOT}/host/pc/asiolist.cpp"
|
||||
)
|
||||
target_include_directories(ASIO::host INTERFACE
|
||||
"${ASIO_ROOT}/common"
|
||||
"${ASIO_ROOT}/host"
|
||||
"${ASIO_ROOT}/host/pc"
|
||||
)
|
||||
target_link_libraries(ASIO::host INTERFACE ole32 uuid)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "ASIO SDK NOT found")
|
||||
endif()
|
39
3rdparty/portaudio/bindings/cpp/cmake/modules/FindPortAudio.cmake
vendored
Normal file
39
3rdparty/portaudio/bindings/cpp/cmake/modules/FindPortAudio.cmake
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
macro(handle_default)
|
||||
|
||||
endmacro()
|
||||
|
||||
if(TARGET PortAudio::portaudio)
|
||||
# nothing to do
|
||||
return()
|
||||
endif()
|
||||
# search for portaudio as cmake module
|
||||
find_package(PortAudio CONFIG QUIET)
|
||||
if(PortAudio_FOUND)
|
||||
if(TARGET PortAudio::portaudio)
|
||||
return()
|
||||
elseif(TARGET portaudio)
|
||||
# vcpkg and old portaudio installations do not provide the same targets
|
||||
add_library(PortAudio::portaudio ALIAS portaudio)
|
||||
return()
|
||||
else()
|
||||
message(FATAL_ERROR "PortAudio_FOUND but not target PortAudio::portaudio")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# search for portaudio via pkg-config
|
||||
|
||||
message(STATUS "portaudio could not be found via cmake, specify PortAudio_DIR.\n Searching for it via pkg-config")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(portaudio REQUIRED QUIET IMPORTED_TARGET GLOBAL portaudio-2.0)
|
||||
add_library(PortAudio::portaudio ALIAS PkgConfig::portaudio)
|
||||
return()
|
||||
|
||||
# include(FindPackageHandleStandardArgs)
|
||||
# find_package_handle_standard_args(Foo
|
||||
# FOUND_VAR Foo_FOUND
|
||||
# REQUIRED_VARS
|
||||
# Foo_LIBRARY
|
||||
# Foo_INCLUDE_DIR
|
||||
# VERSION_VAR Foo_VERSION
|
||||
# )
|
12
3rdparty/portaudio/bindings/cpp/cmake/portaudiocpp.pc.in
vendored
Normal file
12
3rdparty/portaudio/bindings/cpp/cmake/portaudiocpp.pc.in
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
prefix=@PC_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: PortAudioCpp
|
||||
Description: Portable audio I/O C++ bindings
|
||||
Version: 12
|
||||
Requires: portaudio-2.0
|
||||
|
||||
Libs: -L${libdir} -lportaudiocpp
|
||||
Cflags: -I${includedir}
|
26
3rdparty/portaudio/bindings/cpp/source/portaudiocpp/CMakeLists.txt
vendored
Normal file
26
3rdparty/portaudio/bindings/cpp/source/portaudiocpp/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
find_package(ASIO)
|
||||
if(ASIO_FOUND)
|
||||
set(portaudio-cpp-sources-asio AsioDeviceAdapter.cxx PARENT_SCOPE)
|
||||
else()
|
||||
set(portaudio-cpp-sources-asio PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(portaudio-cpp-sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlockingStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CFunCallbackStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CallbackInterface.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CallbackStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CppFunCallbackStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Device.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectionSpecificStreamParameters.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Exception.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/HostApi.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/InterfaceCallbackStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MemFunCallbackStream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Stream.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StreamParameters.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SystemDeviceIterator.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SystemHostApiIterator.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
147
3rdparty/portaudio/cmake/modules/FindPulseAudio.cmake
vendored
Normal file
147
3rdparty/portaudio/cmake/modules/FindPulseAudio.cmake
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
# Copyright 2008 Matthias Kretz <kretz@kde.org>
|
||||
# Copyright 2009 Marcus Hufgard <Marcus.Hufgard@hufgard.de>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2008 Matthias Kretz <kretz@kde.org>
|
||||
# SPDX-FileCopyrightText: 2009 Marcus Hufgard <Marcus.Hufgard@hufgard.de>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#.rst:
|
||||
# FindPulseAudio
|
||||
# --------------
|
||||
#
|
||||
# This is base on
|
||||
# https://invent.kde.org/frameworks/extra-cmake-modules/-/blob/master/find-modules/FindPulseAudio.cmake
|
||||
#
|
||||
# Try to locate the PulseAudio library.
|
||||
# If found, this will define the following variables:
|
||||
#
|
||||
# ``PulseAudio_FOUND``
|
||||
# True if the system has the PulseAudio library of at least
|
||||
# the minimum version specified by either the version parameter
|
||||
# to find_package() or the variable PulseAudio_MINIMUM_VERSION
|
||||
# ``PulseAudio_INCLUDE_DIRS``
|
||||
# The PulseAudio include directory
|
||||
# ``PulseAudio_LIBRARIES``
|
||||
# The PulseAudio libraries for linking
|
||||
# ``PulseAudio_MAINLOOP_LIBRARY``
|
||||
# The libraries needed to use PulseAudio Mainloop
|
||||
# ``PulseAudio_VERSION``
|
||||
# The version of PulseAudio that was found
|
||||
# ``PulseAudio_INCLUDE_DIR``
|
||||
# Deprecated, use ``PulseAudio_INCLUDE_DIRS``
|
||||
# ``PulseAudio_LIBRARY``
|
||||
# Deprecated, use ``PulseAudio_LIBRARIES``
|
||||
#
|
||||
# If ``PulseAudio_FOUND`` is TRUE, it will also define the following
|
||||
# imported target:
|
||||
#
|
||||
# ``PulseAudio::PulseAudio``
|
||||
# The PulseAudio library
|
||||
#
|
||||
# Since 5.41.0.
|
||||
|
||||
# Support PulseAudio_MINIMUM_VERSION for compatibility:
|
||||
if(NOT PulseAudio_FIND_VERSION)
|
||||
set(PulseAudio_FIND_VERSION "${PulseAudio_MINIMUM_VERSION}")
|
||||
endif()
|
||||
|
||||
# the minimum version of PulseAudio we require
|
||||
if(NOT PulseAudio_FIND_VERSION)
|
||||
set(PulseAudio_FIND_VERSION "1.0.0")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_PulseAudio QUIET libpulse>=${PulseAudio_FIND_VERSION})
|
||||
pkg_check_modules(PC_PulseAudio_MAINLOOP QUIET libpulse-mainloop-glib)
|
||||
|
||||
find_path(PulseAudio_INCLUDE_DIRS pulse/pulseaudio.h
|
||||
HINTS
|
||||
${PC_PulseAudio_INCLUDEDIR}
|
||||
${PC_PulseAudio_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(PulseAudio_LIBRARIES NAMES pulse libpulse
|
||||
HINTS
|
||||
${PC_PulseAudio_LIBDIR}
|
||||
${PC_PulseAudio_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
find_library(PulseAudio_MAINLOOP_LIBRARY
|
||||
NAMES pulse-mainloop pulse-mainloop-glib libpulse-mainloop-glib
|
||||
HINTS
|
||||
${PC_PulseAudio_LIBDIR}
|
||||
${PC_PulseAudio_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Store the version number in the cache,
|
||||
# so we don't have to search every time again:
|
||||
if(PulseAudio_INCLUDE_DIRS AND NOT PulseAudio_VERSION)
|
||||
|
||||
# get PulseAudio's version from its version.h
|
||||
file(STRINGS "${PulseAudio_INCLUDE_DIRS}/pulse/version.h" pulse_version_h
|
||||
REGEX ".*pa_get_headers_version\\(\\).*")
|
||||
string(REGEX REPLACE ".*pa_get_headers_version\\(\\)\ \\(\"([0-9]+\\.[0-9]+\\.[0-9]+)[^\"]*\"\\).*" "\\1"
|
||||
_PulseAudio_VERSION "${pulse_version_h}")
|
||||
|
||||
set(PulseAudio_VERSION "${_PulseAudio_VERSION}"
|
||||
CACHE STRING "Version number of PulseAudio"
|
||||
FORCE)
|
||||
endif()
|
||||
|
||||
# Use the new extended syntax of
|
||||
# find_package_handle_standard_args(),
|
||||
# which also handles version checking:
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(PulseAudio
|
||||
REQUIRED_VARS PulseAudio_LIBRARIES
|
||||
PulseAudio_INCLUDE_DIRS
|
||||
VERSION_VAR PulseAudio_VERSION)
|
||||
|
||||
# Deprecated synonyms
|
||||
set(PulseAudio_INCLUDE_DIR "${PulseAudio_INCLUDE_DIRS}")
|
||||
set(PulseAudio_LIBRARY "${PulseAudio_LIBRARIES}")
|
||||
set(PulseAudio_MAINLOOP_LIBRARY "${PulseAudio_MAINLOOP_LIBRARY}")
|
||||
set(PulseAudio_FOUND "${PulseAudio_FOUND}")
|
||||
|
||||
if(PulseAudio_FOUND AND NOT TARGET PulseAudio::PulseAudio)
|
||||
add_library(PulseAudio::PulseAudio UNKNOWN IMPORTED)
|
||||
set_target_properties(PulseAudio::PulseAudio PROPERTIES
|
||||
IMPORTED_LOCATION "${PulseAudio_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${PulseAudio_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(PulseAudio_INCLUDE_DIRS PulseAudio_INCLUDE_DIR
|
||||
PulseAudio_LIBRARIES PulseAudio_LIBRARY
|
||||
PulseAudio_MAINLOOP_LIBRARY PulseAudio_MAINLOOP_LIBRARY)
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(PulseAudio PROPERTIES
|
||||
URL "https://www.freedesktop.org/wiki/Software/PulseAudio"
|
||||
DESCRIPTION "Sound server, for sound stream routing and mixing")
|
29
3rdparty/portaudio/configure.in
vendored
29
3rdparty/portaudio/configure.in
vendored
@ -41,6 +41,10 @@ AC_ARG_WITH(jack,
|
||||
AS_HELP_STRING([--with-jack], [Enable support for JACK @<:@autodetect@:>@]),
|
||||
[with_jack=$withval])
|
||||
|
||||
AC_ARG_WITH(pulseaudio,
|
||||
AS_HELP_STRING([--with-pulseaudio], [Enable support for PulseAudio @<:@autodetect@:>@]),
|
||||
[with_pulseaudio=$withval])
|
||||
|
||||
AC_ARG_WITH(oss,
|
||||
AS_HELP_STRING([--with-oss], [Enable support for OSS @<:@autodetect@:>@]),
|
||||
[with_oss=$withval])
|
||||
@ -149,10 +153,17 @@ if test "x$with_oss" != "xno"; then
|
||||
AC_CHECK_LIB(ossaudio, _oss_ioctl, have_libossaudio=yes, have_libossaudio=no)
|
||||
fi
|
||||
fi
|
||||
if [[ "x$with_jack" != "xno" ] || [ "x$with_pulseaudio" != "xno" ]]; then
|
||||
PKG_PROG_PKG_CONFIG
|
||||
fi
|
||||
have_jack=no
|
||||
if test "x$with_jack" != "xno"; then
|
||||
PKG_CHECK_MODULES(JACK, jack, have_jack=yes, have_jack=no)
|
||||
fi
|
||||
have_pulse=no
|
||||
if test "x$with_pulseaudio" != "xno"; then
|
||||
PKG_CHECK_MODULES(PULSE, libpulse, have_pulse=yes, have_pulse=no)
|
||||
fi
|
||||
|
||||
|
||||
dnl sizeof checks: we will need a 16-bit and a 32-bit type
|
||||
@ -386,11 +397,26 @@ case "${host_os}" in
|
||||
if [[ "$have_jack" = "yes" ] && [ "$with_jack" != "no" ]] ; then
|
||||
DLL_LIBS="$DLL_LIBS $JACK_LIBS"
|
||||
CFLAGS="$CFLAGS $JACK_CFLAGS"
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/jack/pa_jack.o src/common/pa_ringbuffer.o"
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/jack/pa_jack.o"
|
||||
INCLUDES="$INCLUDES pa_jack.h"
|
||||
AC_DEFINE(PA_USE_JACK,1)
|
||||
fi
|
||||
|
||||
if [[ "$have_pulse" = "yes" ] || [ "$have_jack" = "yes" ]] ; then
|
||||
OTHER_OBJS="$OTHER_OBJS src/common/pa_ringbuffer.o"
|
||||
fi
|
||||
|
||||
if [[ "$have_pulse" = "yes" ] && [ "$with_pulse" != "no" ]] ; then
|
||||
INCLUDES="$INCLUDES pa_linux_pulseaudio.h"
|
||||
DLL_LIBS="$DLL_LIBS $PULSE_LIBS"
|
||||
CFLAGS="$CFLAGS $PULSE_CFLAGS"
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.o"
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio_block.o"
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/pulseaudio/pa_linux_pulseaudio.o"
|
||||
dnl INCLUDES="$INCLUDES pa_pulseaudio.h src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h"
|
||||
AC_DEFINE(PA_USE_PULSEAUDIO,1)
|
||||
fi
|
||||
|
||||
if [[ "$with_oss" != "no" ]] ; then
|
||||
OTHER_OBJS="$OTHER_OBJS src/hostapi/oss/pa_unix_oss.o"
|
||||
if [[ "$have_libossaudio" = "yes" ]] ; then
|
||||
@ -489,6 +515,7 @@ case "$target_os" in
|
||||
AudioIO ..................... $have_audioio
|
||||
OSS ......................... $have_oss
|
||||
JACK ........................ $have_jack
|
||||
PulseAudio .................. $have_pulse
|
||||
])
|
||||
;;
|
||||
esac
|
||||
|
@ -56,6 +56,7 @@ 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_version.c (portaudio\src\os\win)
|
||||
pa_win_coinitialize.c (portaudio\src\os\win)
|
||||
pa_win_waveformat.c (portaudio\src\os\win)
|
||||
pa_x86_plain_converters.c (portaudio\src\os\win)
|
||||
|
79
3rdparty/portaudio/include/pa_linux_pulseaudio.h
vendored
Normal file
79
3rdparty/portaudio/include/pa_linux_pulseaudio.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef PA_LINUX_PULSEAUDIO_H
|
||||
#define PA_LINUX_PULSEAUDIO_H
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PulseAudio-specific extensions
|
||||
*
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 PulseAudio-specific PortAudio API extension header file.
|
||||
*/
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Renames the PulseAudio description for the source that is opened
|
||||
* by PortAudio.
|
||||
*
|
||||
* @param s The PortAudio stream to operate on.
|
||||
* @param streamName The new name/description of the source.
|
||||
*
|
||||
* @return paNoError on success or the error encountered otherwise.
|
||||
*/
|
||||
PaError PaPulseAudio_RenameSource( PaStream *s, const char *streamName );
|
||||
|
||||
/**
|
||||
* Renames the PulseAudio description for the sink that is opened
|
||||
* by PortAudio.
|
||||
*
|
||||
* @param s The PortAudio stream to operate on.
|
||||
* @param streamName The new name/description of the sink.
|
||||
*
|
||||
* @return paNoError on success or the error encountered otherwise.
|
||||
*/
|
||||
PaError PaPulseAudio_RenameSink( PaStream *s, const char *streamName );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
70
3rdparty/portaudio/include/pa_win_wasapi.h
vendored
70
3rdparty/portaudio/include/pa_win_wasapi.h
vendored
@ -82,7 +82,12 @@ typedef enum PaWasapiFlags
|
||||
playback formats that do not match the current configured system settings.
|
||||
this is in particular required for streams not matching the system mixer sample rate.
|
||||
only applies in Shared mode. */
|
||||
paWinWasapiAutoConvert = (1 << 6)
|
||||
paWinWasapiAutoConvert = (1 << 6),
|
||||
|
||||
/* use Passthrough mode for sending encoded audio data in PCM containers to the audio device,
|
||||
refer to Microsoft documentation "Representing Formats for IEC 61937 Transmissions" for more
|
||||
details about data representation and stream configuration */
|
||||
paWinWasapiPassthrough = (1 << 7),
|
||||
}
|
||||
PaWasapiFlags;
|
||||
#define paWinWasapiExclusive (paWinWasapiExclusive)
|
||||
@ -92,6 +97,7 @@ PaWasapiFlags;
|
||||
#define paWinWasapiThreadPriority (paWinWasapiThreadPriority)
|
||||
#define paWinWasapiExplicitSampleFormat (paWinWasapiExplicitSampleFormat)
|
||||
#define paWinWasapiAutoConvert (paWinWasapiAutoConvert)
|
||||
#define paWinWasapiPassthrough (paWinWasapiPassthrough)
|
||||
|
||||
|
||||
/* Stream state.
|
||||
@ -302,6 +308,61 @@ typedef enum PaWasapiStreamOption
|
||||
PaWasapiStreamOption;
|
||||
|
||||
|
||||
/** Passthrough format.
|
||||
|
||||
Format ids are obtained from the Microsoft documentation "Representing Formats for IEC 61937 Transmissions"
|
||||
and are composed by such formula where GUID is the guid of passthrough format:
|
||||
GUID.Data1 << 16 | GUID.Data2.
|
||||
|
||||
@see PaWasapiStreamPassthrough
|
||||
@version Available as of 19.8.0
|
||||
*/
|
||||
typedef enum PaWasapiPassthroughFormat
|
||||
{
|
||||
ePassthroughFormatPcmIec60958 = 0x00000000,
|
||||
ePassthroughFormatDolbyDigital = 0x00920000,
|
||||
ePassthroughFormatMpeg1 = 0x00030cea,
|
||||
ePassthroughFormatMpeg3 = 0x00040cea,
|
||||
ePassthroughFormatMpeg2 = 0x00050cea,
|
||||
ePassthroughFormatAac = 0x00060cea,
|
||||
ePassthroughFormatDts = 0x00080cea,
|
||||
ePassthroughFormatDolbyDigitalPlus = 0x000a0cea,
|
||||
ePassthroughFormatDolbyDigitalPlusAtmos = 0x010a0cea,
|
||||
ePassthroughFormatDtsHd = 0x000b0cea,
|
||||
ePassthroughFormatDtsXE1 = 0x010b0cea,
|
||||
ePassthroughFormatDtsXE2 = 0x030b0cea,
|
||||
ePassthroughFormatDolbyMlp = 0x000c0cea,
|
||||
ePassthroughFormatDolbyMat20 = 0x010c0cea,
|
||||
ePassthroughFormatDolbyMat21 = 0x030c0cea,
|
||||
ePassthroughFormatWmaPro = 0x01640000,
|
||||
ePassthroughFormatAtrac = 0x00080cea,
|
||||
ePassthroughFormatOneBitAudio = 0x00090cea,
|
||||
ePassthroughFormatDst = 0x000d0cea,
|
||||
}
|
||||
PaWasapiPassthroughFormat;
|
||||
|
||||
|
||||
/** Passthrough details.
|
||||
|
||||
Passthrough details provide direct link to the additional members in WAVEFORMATEXTENSIBLE_IEC61937.
|
||||
Passthrough mode allows to pass encoded data inside the PCM containers to the audio device.
|
||||
|
||||
Detailed description about supported formats and examples are provided in Microsoft documentation
|
||||
"Representing Formats for IEC 61937 Transmissions".
|
||||
|
||||
@see paWinWasapiPassthrough
|
||||
@version Available as of 19.8.0
|
||||
*/
|
||||
typedef struct PaWasapiStreamPassthrough
|
||||
{
|
||||
PaWasapiPassthroughFormat formatId;
|
||||
unsigned int encodedSamplesPerSec;
|
||||
unsigned int encodedChannelCount;
|
||||
unsigned int averageBytesPerSec;
|
||||
}
|
||||
PaWasapiStreamPassthrough;
|
||||
|
||||
|
||||
/* Stream descriptor. */
|
||||
typedef struct PaWasapiStreamInfo
|
||||
{
|
||||
@ -347,6 +408,13 @@ typedef struct PaWasapiStreamInfo
|
||||
@version Available as of 19.6.0
|
||||
*/
|
||||
PaWasapiStreamOption streamOption;
|
||||
|
||||
/** Passthrough details.
|
||||
@note paWinWasapiPassthrough flag must be specified in PaWasapiStreamInfo::flags to enable Passthrough mode.
|
||||
@see paWinWasapiPassthrough
|
||||
@version Available as of 19.7.0
|
||||
*/
|
||||
PaWasapiStreamPassthrough passthrough;
|
||||
}
|
||||
PaWasapiStreamInfo;
|
||||
|
||||
|
6
3rdparty/portaudio/include/portaudio.h
vendored
6
3rdparty/portaudio/include/portaudio.h
vendored
@ -151,7 +151,8 @@ typedef enum PaErrorCode
|
||||
paCanNotReadFromAnOutputOnlyStream,
|
||||
paCanNotWriteToAnInputOnlyStream,
|
||||
paIncompatibleStreamHostApi,
|
||||
paBadBufferPtr
|
||||
paBadBufferPtr,
|
||||
paCanNotInitializeRecursively
|
||||
} PaErrorCode;
|
||||
|
||||
|
||||
@ -288,7 +289,8 @@ typedef enum PaHostApiTypeId
|
||||
paJACK=12,
|
||||
paWASAPI=13,
|
||||
paAudioScienceHPI=14,
|
||||
paAudioIO=15
|
||||
paAudioIO=15,
|
||||
paPulseAudio=16
|
||||
} PaHostApiTypeId;
|
||||
|
||||
|
||||
|
4
3rdparty/portaudio/msvc/portaudio.dsp
vendored
4
3rdparty/portaudio/msvc/portaudio.dsp
vendored
@ -240,6 +240,10 @@ SOURCE=..\..\src\os\win\pa_win_util.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\src\os\win\pa_win_version.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\src\os\win\pa_win_waveformat.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
4
3rdparty/portaudio/msvc/portaudio.vcproj
vendored
4
3rdparty/portaudio/msvc/portaudio.vcproj
vendored
@ -1819,6 +1819,10 @@
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\os\win\pa_win_version.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\os\win\pa_win_waveformat.c"
|
||||
>
|
||||
|
9
3rdparty/portaudio/qa/loopback/src/paqa.c
vendored
9
3rdparty/portaudio/qa/loopback/src/paqa.c
vendored
@ -120,8 +120,8 @@ typedef struct LoopbackContext_s
|
||||
volatile int minInputOutputDelta;
|
||||
volatile int maxInputOutputDelta;
|
||||
|
||||
int minFramesPerBuffer;
|
||||
int maxFramesPerBuffer;
|
||||
unsigned long minFramesPerBuffer;
|
||||
unsigned long maxFramesPerBuffer;
|
||||
int primingCount;
|
||||
TestParameters *test;
|
||||
volatile int done;
|
||||
@ -849,7 +849,7 @@ static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *te
|
||||
{
|
||||
double latencyMSec;
|
||||
|
||||
printf( "%4d-%4d | ",
|
||||
printf( "%4lu-%4lu | ",
|
||||
loopbackContext.minFramesPerBuffer,
|
||||
loopbackContext.maxFramesPerBuffer
|
||||
);
|
||||
@ -1351,7 +1351,8 @@ int TestSampleFormatConversion( void )
|
||||
const char charInput[] = { 127, 64, -64, -128 };
|
||||
const unsigned char ucharInput[] = { 255, 128+64, 64, 0 };
|
||||
const short shortInput[] = { 32767, 32768/2, -32768/2, -32768 };
|
||||
const int intInput[] = { 2147483647, 2147483647/2, -1073741824 /*-2147483648/2 doesn't work in msvc*/, -2147483648 };
|
||||
const int int_minus_2147483648 = (-2147483647 - 1); /*"-2147483648" as a signed integer. See PR #814*/
|
||||
const int intInput[] = { 2147483647, 2147483647/2, int_minus_2147483648/2, int_minus_2147483648 };
|
||||
|
||||
float floatOutput[4];
|
||||
short shortOutput[4];
|
||||
|
882
3rdparty/portaudio/qa/paqa_devs.c
vendored
882
3rdparty/portaudio/qa/paqa_devs.c
vendored
File diff suppressed because it is too large
Load Diff
46
3rdparty/portaudio/qa/paqa_errs.c
vendored
46
3rdparty/portaudio/qa/paqa_errs.c
vendored
@ -43,10 +43,13 @@
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* for EXIT_SUCCESS and EXIT_FAILURE */
|
||||
#include <math.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "paqa_macros.h"
|
||||
|
||||
/*--------- Definitions ---------*/
|
||||
#define MODE_INPUT (0)
|
||||
@ -54,6 +57,8 @@
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#define SAMPLE_RATE (44100.0)
|
||||
|
||||
PAQA_INSTANTIATE_GLOBALS
|
||||
|
||||
typedef struct PaQaData
|
||||
{
|
||||
unsigned long framesLeft;
|
||||
@ -63,38 +68,6 @@ typedef struct PaQaData
|
||||
}
|
||||
PaQaData;
|
||||
|
||||
static int gNumPassed = 0; /* Two globals */
|
||||
static int gNumFailed = 0;
|
||||
|
||||
/*------------------- Macros ------------------------------*/
|
||||
/* Print ERROR if it fails. Tally success or failure. Odd */
|
||||
/* do-while wrapper seems to be needed for some compilers. */
|
||||
|
||||
#define EXPECT(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
gNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
|
||||
gNumFailed++; \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define HOPEFOR(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
gNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
|
||||
gNumFailed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
It may be called at interrupt level on some machines so don't do anything
|
||||
@ -393,11 +366,14 @@ int main(void)
|
||||
{
|
||||
PaError result;
|
||||
|
||||
EXPECT(((result = Pa_Initialize()) == paNoError));
|
||||
printf("-----------------------------\n");
|
||||
printf("paqa_errs - PortAudio QA test\n");
|
||||
ASSERT_EQ(paNoError, (result = Pa_Initialize()));
|
||||
TestBadOpens();
|
||||
TestBadActions();
|
||||
error:
|
||||
Pa_Terminate();
|
||||
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed);
|
||||
return 0;
|
||||
|
||||
PAQA_PRINT_RESULT;
|
||||
return PAQA_EXIT_RESULT;
|
||||
}
|
||||
|
8
3rdparty/portaudio/qa/paqa_latency.c
vendored
8
3rdparty/portaudio/qa/paqa_latency.c
vendored
@ -61,8 +61,8 @@ typedef struct
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
char message[20];
|
||||
int minFramesPerBuffer;
|
||||
int maxFramesPerBuffer;
|
||||
unsigned long minFramesPerBuffer;
|
||||
unsigned long maxFramesPerBuffer;
|
||||
int callbackCount;
|
||||
PaTime minDeltaDacTime;
|
||||
PaTime maxDeltaDacTime;
|
||||
@ -170,8 +170,8 @@ PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr,
|
||||
printf("Play for %d seconds.\n", NUM_SECONDS );
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
|
||||
printf(" minFramesPerBuffer = %4d\n", dataPtr->minFramesPerBuffer );
|
||||
printf(" maxFramesPerBuffer = %4d\n", dataPtr->maxFramesPerBuffer );
|
||||
printf(" minFramesPerBuffer = %4lu\n", dataPtr->minFramesPerBuffer );
|
||||
printf(" maxFramesPerBuffer = %4lu\n", dataPtr->maxFramesPerBuffer );
|
||||
printf(" minDeltaDacTime = %f\n", dataPtr->minDeltaDacTime );
|
||||
printf(" maxDeltaDacTime = %f\n", dataPtr->maxDeltaDacTime );
|
||||
|
||||
|
71
3rdparty/portaudio/qa/paqa_macros.h
vendored
Normal file
71
3rdparty/portaudio/qa/paqa_macros.h
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
#ifndef PORTAUDIO_QA_PAQA_MACROS_H
|
||||
#define PORTAUDIO_QA_PAQA_MACROS_H
|
||||
|
||||
extern int paQaNumPassed;
|
||||
extern int paQaNumFailed;
|
||||
|
||||
/* You must use this macro exactly once in each test program. */
|
||||
#define PAQA_INSTANTIATE_GLOBALS\
|
||||
int paQaNumPassed = 0;\
|
||||
int paQaNumFailed = 0;
|
||||
|
||||
/*------------------- Macros ------------------------------*/
|
||||
/* Print ERROR if it fails. Tally success or failure. Odd */
|
||||
/* do-while wrapper seems to be needed for some compilers. */
|
||||
#define ASSERT_TRUE(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if (_exp) {\
|
||||
paQaNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("ERROR at %s:%d, (%s) not true\n", \
|
||||
__FILE__, __LINE__, #_exp ); \
|
||||
paQaNumFailed++; \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_AB(_a, _b, _op, _opn) \
|
||||
do \
|
||||
{ \
|
||||
int mA = (int)(_a); \
|
||||
int mB = (int)(_b); \
|
||||
if (mA _op mB) {\
|
||||
paQaNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("ERROR at %s:%d, (%s) %s (%s), %d %s %d\n", \
|
||||
__FILE__, __LINE__, #_a, #_opn, #_b, mA, #_opn, mB ); \
|
||||
paQaNumFailed++; \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_EQ(_a, _b) ASSERT_AB(_a, _b, ==, !=)
|
||||
#define ASSERT_NE(_a, _b) ASSERT_AB(_a, _b, !=, ==)
|
||||
#define ASSERT_GT(_a, _b) ASSERT_AB(_a, _b, >, <=)
|
||||
#define ASSERT_GE(_a, _b) ASSERT_AB(_a, _b, >=, <)
|
||||
#define ASSERT_LT(_a, _b) ASSERT_AB(_a, _b, <, >=)
|
||||
#define ASSERT_LE(_a, _b) ASSERT_AB(_a, _b, <=, >)
|
||||
|
||||
#define HOPEFOR(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
paQaNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
|
||||
paQaNumFailed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PAQA_PRINT_RESULT \
|
||||
printf("QA Report: %d passed, %d failed.\n", paQaNumPassed, paQaNumFailed )
|
||||
|
||||
#define PAQA_EXIT_RESULT \
|
||||
(((paQaNumFailed > 0) || (paQaNumPassed == 0)) ? EXIT_FAILURE : EXIT_SUCCESS)
|
||||
|
||||
#endif /* PORTAUDIO_QA_PAQA_MACROS_H */
|
47
3rdparty/portaudio/src/common/pa_converters.c
vendored
47
3rdparty/portaudio/src/common/pa_converters.c
vendored
@ -41,8 +41,6 @@
|
||||
|
||||
@brief Conversion function implementations.
|
||||
|
||||
If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
|
||||
|
||||
@todo Consider whether functions which dither but don't clip should exist,
|
||||
V18 automatically enabled clipping whenever dithering was selected. Perhaps
|
||||
we should do the same.
|
||||
@ -343,13 +341,8 @@ static void Float32_To_Int32(
|
||||
while( count-- )
|
||||
{
|
||||
/* REVIEW */
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
float scaled = *src * 0x7FFFFFFF;
|
||||
*dest = lrintf(scaled-0.5f);
|
||||
#else
|
||||
double scaled = *src * 0x7FFFFFFF;
|
||||
*dest = (PaInt32) scaled;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
@ -369,17 +362,11 @@ static void Float32_To_Int32_Dither(
|
||||
while( count-- )
|
||||
{
|
||||
/* REVIEW */
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
float dithered = ((float)*src * (2147483646.0f)) + dither;
|
||||
*dest = lrintf(dithered - 0.5f);
|
||||
#else
|
||||
double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
double dithered = ((double)*src * (2147483646.0)) + dither;
|
||||
*dest = (PaInt32) dithered;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
}
|
||||
@ -399,15 +386,9 @@ static void Float32_To_Int32_Clip(
|
||||
while( count-- )
|
||||
{
|
||||
/* REVIEW */
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
float scaled = *src * 0x7FFFFFFF;
|
||||
PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
|
||||
*dest = lrintf(scaled-0.5f);
|
||||
#else
|
||||
double scaled = *src * 0x7FFFFFFF;
|
||||
PA_CLIP_( scaled, -2147483648., 2147483647. );
|
||||
*dest = (PaInt32) scaled;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
@ -427,19 +408,11 @@ static void Float32_To_Int32_DitherClip(
|
||||
while( count-- )
|
||||
{
|
||||
/* REVIEW */
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
float dithered = ((float)*src * (2147483646.0f)) + dither;
|
||||
PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
|
||||
*dest = lrintf(dithered-0.5f);
|
||||
#else
|
||||
double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
double dithered = ((double)*src * (2147483646.0)) + dither;
|
||||
PA_CLIP_( dithered, -2147483648., 2147483647. );
|
||||
*dest = (PaInt32) dithered;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
@ -601,13 +574,8 @@ static void Float32_To_Int16(
|
||||
|
||||
while( count-- )
|
||||
{
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
float tempf = (*src * (32767.0f)) ;
|
||||
*dest = lrintf(tempf-0.5f);
|
||||
#else
|
||||
short samp = (short) (*src * (32767.0f));
|
||||
*dest = samp;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
@ -631,11 +599,7 @@ static void Float32_To_Int16_Dither(
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
float dithered = (*src * (32766.0f)) + dither;
|
||||
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
*dest = lrintf(dithered-0.5f);
|
||||
#else
|
||||
*dest = (PaInt16) dithered;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
@ -655,11 +619,8 @@ static void Float32_To_Int16_Clip(
|
||||
|
||||
while( count-- )
|
||||
{
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
long samp = lrintf((*src * (32767.0f)) -0.5f);
|
||||
#else
|
||||
long samp = (PaInt32) (*src * (32767.0f));
|
||||
#endif
|
||||
|
||||
PA_CLIP_( samp, -0x8000, 0x7FFF );
|
||||
*dest = (PaInt16) samp;
|
||||
|
||||
@ -687,11 +648,7 @@ static void Float32_To_Int16_DitherClip(
|
||||
float dithered = (*src * (32766.0f)) + dither;
|
||||
PaInt32 samp = (PaInt32) dithered;
|
||||
PA_CLIP_( samp, -0x8000, 0x7FFF );
|
||||
#ifdef PA_USE_C99_LRINTF
|
||||
*dest = lrintf(samp-0.5f);
|
||||
#else
|
||||
*dest = (PaInt16) samp;
|
||||
#endif
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
|
17
3rdparty/portaudio/src/common/pa_front.c
vendored
17
3rdparty/portaudio/src/common/pa_front.c
vendored
@ -154,6 +154,7 @@ static PaUtilHostApiRepresentation **hostApis_ = 0;
|
||||
static int hostApisCount_ = 0;
|
||||
static int defaultHostApiIndex_ = 0;
|
||||
static int initializationCount_ = 0;
|
||||
static int initializing_ = 0;
|
||||
static int deviceCount_ = 0;
|
||||
|
||||
PaUtilStreamRepresentation *firstOpenStream_ = NULL;
|
||||
@ -360,8 +361,21 @@ PaError Pa_Initialize( void )
|
||||
++initializationCount_;
|
||||
result = paNoError;
|
||||
}
|
||||
else if( initializing_ )
|
||||
{
|
||||
// a concurrent initialization is already running
|
||||
PA_DEBUG(("Attempting to re-enter Pa_Initialize(), aborting!\n"));
|
||||
result = paCanNotInitializeRecursively;
|
||||
}
|
||||
else
|
||||
{
|
||||
// set initializing_ here to
|
||||
// let recursive calls execute the if branch above.
|
||||
// This can happen if a driver like FlexAsio itself uses portaudio
|
||||
// and avoids a stack overflow in the user application.
|
||||
// https://github.com/PortAudio/portaudio/issues/766
|
||||
initializing_ = 1;
|
||||
|
||||
PA_VALIDATE_TYPE_SIZES;
|
||||
PA_VALIDATE_ENDIANNESS;
|
||||
|
||||
@ -371,6 +385,8 @@ PaError Pa_Initialize( void )
|
||||
result = InitializeHostApis();
|
||||
if( result == paNoError )
|
||||
++initializationCount_;
|
||||
|
||||
initializing_ = 0;
|
||||
}
|
||||
|
||||
PA_LOGAPI_EXIT_PAERROR( "Pa_Initialize", result );
|
||||
@ -453,6 +469,7 @@ const char *Pa_GetErrorText( PaError errorCode )
|
||||
case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break;
|
||||
case paIncompatibleStreamHostApi: result = "Incompatible stream host API"; break;
|
||||
case paBadBufferPtr: result = "Bad buffer pointer"; break;
|
||||
case paCanNotInitializeRecursively: result = "PortAudio can not be initialized recursively"; break;
|
||||
default:
|
||||
if( errorCode > 0 )
|
||||
result = "Invalid error code (value greater than zero)";
|
||||
|
9
3rdparty/portaudio/src/common/pa_hostapi.h
vendored
9
3rdparty/portaudio/src/common/pa_hostapi.h
vendored
@ -67,7 +67,7 @@ are defaulted to 1.
|
||||
#define PA_USE_SKELETON 1
|
||||
#endif
|
||||
|
||||
#if defined(PA_NO_ASIO) || defined(PA_NO_DS) || defined(PA_NO_WMME) || defined(PA_NO_WASAPI) || defined(PA_NO_WDMKS)
|
||||
#if defined(PA_NO_PULSEAUDIO) || defined(PA_NO_ASIO) || defined(PA_NO_DS) || defined(PA_NO_WMME) || defined(PA_NO_WASAPI) || defined(PA_NO_WDMKS)
|
||||
#error "Portaudio: PA_NO_<APINAME> is no longer supported, please remove definition and use PA_USE_<APINAME> instead"
|
||||
#endif
|
||||
|
||||
@ -132,6 +132,13 @@ are defaulted to 1.
|
||||
#define PA_USE_JACK 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_PULSEAUDIO
|
||||
#define PA_USE_PULSEAUDIO 0
|
||||
#elif (PA_USE_PULSEAUDIO != 0) && (PA_USE_PULSEAUDIO != 1)
|
||||
#undef PA_USE_PULSEAUDIO
|
||||
#define PA_USE_PULSEAUDIO 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_SGI
|
||||
#define PA_USE_SGI 0
|
||||
#elif (PA_USE_SGI != 0) && (PA_USE_SGI != 1)
|
||||
|
6
3rdparty/portaudio/src/common/pa_util.h
vendored
6
3rdparty/portaudio/src/common/pa_util.h
vendored
@ -154,6 +154,12 @@ void PaUtil_InitializeClock( void );
|
||||
|
||||
/** Return the system time in seconds. Used to implement CPU load functions
|
||||
|
||||
@note Do not make assumptions about which underlying clock is used to implement
|
||||
PaUtil_GetTime, or use the current implementation as a guide. Do not use this
|
||||
function when a specific clock is required (e.g. when using platform APIs
|
||||
such as pthreads). If you need to use a specific clock, use a native API that
|
||||
returns that clock.
|
||||
|
||||
@see PaUtil_InitializeClock
|
||||
*/
|
||||
double PaUtil_GetTime( void );
|
||||
|
@ -93,6 +93,7 @@
|
||||
#include "pa_win_waveformat.h"
|
||||
#include "pa_win_wdmks_utils.h"
|
||||
#include "pa_win_coinitialize.h"
|
||||
#include "pa_win_version.h"
|
||||
|
||||
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
|
||||
#pragma comment( lib, "dsound.lib" )
|
||||
@ -328,43 +329,26 @@ typedef struct PaWinDsStream
|
||||
*/
|
||||
static double PaWinDS_GetMinSystemLatencySeconds( void )
|
||||
{
|
||||
/*
|
||||
NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
|
||||
versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
|
||||
Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
|
||||
is faster, for now we just disable the deprecation warning.
|
||||
See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
|
||||
See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
|
||||
*/
|
||||
#pragma warning (disable : 4996) /* use of GetVersionEx */
|
||||
|
||||
double minLatencySeconds;
|
||||
/* Set minimal latency based on whether NT or other OS.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
PaOsVersion version = PaWinUtil_GetOsVersion();
|
||||
|
||||
OSVERSIONINFO osvi;
|
||||
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
||||
GetVersionEx( &osvi );
|
||||
DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
|
||||
DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
|
||||
DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
|
||||
/* Check for NT */
|
||||
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(osvi.dwMajorVersion >= 5)
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
|
||||
}
|
||||
else
|
||||
if(version <= paOsVersionWindows9x)
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
|
||||
}
|
||||
return minLatencySeconds;
|
||||
else if(version == paOsVersionWindowsNT4)
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(version >= paOsVersionWindows2000)
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
|
||||
}
|
||||
|
||||
#pragma warning (default : 4996)
|
||||
return minLatencySeconds;
|
||||
}
|
||||
|
||||
|
||||
|
@ -507,7 +507,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
|
||||
// Add 1 for null terminator.
|
||||
size_t device_name_regex_escaped_size = jack_client_name_size() * 2 + 1;
|
||||
size_t port_regex_size = device_name_regex_escaped_size + strlen(port_regex_suffix);
|
||||
int port_index, client_index, i;
|
||||
unsigned long port_index, client_index, i;
|
||||
double globalSampleRate;
|
||||
regex_t port_regex;
|
||||
unsigned long numClients = 0, numPorts = 0;
|
||||
|
1452
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio.c
vendored
Normal file
1452
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
197
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c
vendored
Normal file
197
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_block.c
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
/*
|
||||
* PulseAudio host to play natively in Linux based systems without
|
||||
* ALSA emulation
|
||||
*
|
||||
* Copyright (c) 2014-2023 Tuukka Pasanen <tuukka.pasanen@ilmi.fi>
|
||||
* Copyright (c) 2016 Sqweek
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 common_src
|
||||
|
||||
@brief PulseAudio implementation of support for a host API.
|
||||
|
||||
This host API implements PulseAudio support for portaudio
|
||||
it has callbackmode and normal write mode support
|
||||
*/
|
||||
|
||||
#include "pa_linux_pulseaudio_block_internal.h"
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
As separate stream interfaces are used for blocking and callback
|
||||
streams, the following functions can be guaranteed to only be called
|
||||
for blocking streams.
|
||||
*/
|
||||
|
||||
PaError PaPulseAudio_ReadStreamBlock( PaStream * s,
|
||||
void *buffer,
|
||||
unsigned long frames )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s;
|
||||
PaPulseAudio_HostApiRepresentation *pulseaudioHostApi =
|
||||
pulseaudioStream->hostapi;
|
||||
PaError ret = 0;
|
||||
uint8_t *readableBuffer = (uint8_t *) buffer;
|
||||
long bufferLeftToRead = (frames * pulseaudioStream->inputFrameSize);
|
||||
|
||||
while( bufferLeftToRead > 0 )
|
||||
{
|
||||
PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped )
|
||||
|
||||
PaPulseAudio_Lock( pulseaudioStream->mainloop );
|
||||
long l_read = PaUtil_ReadRingBuffer( &pulseaudioStream->inputRing, readableBuffer,
|
||||
bufferLeftToRead );
|
||||
readableBuffer += l_read;
|
||||
bufferLeftToRead -= l_read;
|
||||
if( bufferLeftToRead > 0 )
|
||||
pa_threaded_mainloop_wait( pulseaudioStream->mainloop );
|
||||
|
||||
PaPulseAudio_UnLock( pulseaudioStream->mainloop );
|
||||
|
||||
if( bufferLeftToRead > 0 )
|
||||
{
|
||||
/* Sleep small amount of time not burn CPU
|
||||
* we block anyway so this is bearable
|
||||
*/
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
PaError PaPulseAudio_WriteStreamBlock( PaStream * s,
|
||||
const void *buffer,
|
||||
unsigned long frames )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s;
|
||||
int ret = 0;
|
||||
size_t pulseaudioWritable = 0;
|
||||
uint8_t *writableBuffer = (uint8_t *) buffer;
|
||||
long bufferLeftToWrite = (frames * pulseaudioStream->outputFrameSize);
|
||||
pa_operation *pulseaudioOperation = NULL;
|
||||
|
||||
PaUtil_BeginCpuLoadMeasurement( &pulseaudioStream->cpuLoadMeasurer );
|
||||
|
||||
while( bufferLeftToWrite > 0)
|
||||
{
|
||||
PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped )
|
||||
|
||||
PaPulseAudio_Lock( pulseaudioStream->mainloop );
|
||||
pulseaudioWritable = pa_stream_writable_size( pulseaudioStream->outputStream );
|
||||
PaPulseAudio_UnLock( pulseaudioStream->mainloop );
|
||||
|
||||
if( pulseaudioWritable > 0 )
|
||||
{
|
||||
if( bufferLeftToWrite < pulseaudioWritable )
|
||||
{
|
||||
pulseaudioWritable = bufferLeftToWrite;
|
||||
}
|
||||
PaPulseAudio_Lock( pulseaudioStream->mainloop );
|
||||
ret = pa_stream_write( pulseaudioStream->outputStream,
|
||||
writableBuffer,
|
||||
pulseaudioWritable,
|
||||
NULL,
|
||||
0,
|
||||
PA_SEEK_RELATIVE );
|
||||
|
||||
pulseaudioOperation = pa_stream_update_timing_info( pulseaudioStream->outputStream,
|
||||
NULL,
|
||||
NULL );
|
||||
PaPulseAudio_UnLock( pulseaudioStream->mainloop );
|
||||
|
||||
ret = 0;
|
||||
|
||||
if( pulseaudioOperation == NULL )
|
||||
{
|
||||
return paInsufficientMemory;
|
||||
}
|
||||
|
||||
while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING )
|
||||
{
|
||||
ret ++;
|
||||
PA_PULSEAUDIO_IS_ERROR( pulseaudioStream, paStreamIsStopped )
|
||||
|
||||
/* As this shouldn never happen it's error if it does */
|
||||
if( ret >= 10000 )
|
||||
{
|
||||
return paStreamIsStopped;
|
||||
}
|
||||
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
PaPulseAudio_Lock( pulseaudioStream->mainloop );
|
||||
|
||||
pa_operation_unref( pulseaudioOperation );
|
||||
pulseaudioOperation = NULL;
|
||||
|
||||
PaPulseAudio_UnLock( pulseaudioStream->mainloop );
|
||||
|
||||
writableBuffer += pulseaudioWritable;
|
||||
bufferLeftToWrite -= pulseaudioWritable;
|
||||
}
|
||||
|
||||
if( bufferLeftToWrite > 0 )
|
||||
{
|
||||
/* Sleep small amount of time not burn CPU
|
||||
* we block anyway so this is bearable
|
||||
*/
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
}
|
||||
PaUtil_EndCpuLoadMeasurement( &pulseaudioStream->cpuLoadMeasurer,
|
||||
frames );
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
signed long PaPulseAudio_GetStreamReadAvailableBlock( PaStream * s )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) s;
|
||||
|
||||
if( pulseaudioStream->inputStream == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ( PaUtil_GetRingBufferReadAvailable( &pulseaudioStream->inputRing ) /
|
||||
pulseaudioStream->inputFrameSize );
|
||||
}
|
91
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h
vendored
Normal file
91
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_block_internal.h
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
/*
|
||||
* PulseAudio host to play natively in Linux based systems without
|
||||
* ALSA emulation
|
||||
*
|
||||
* Copyright (c) 2014-2023 Tuukka Pasanen <tuukka.pasanen@ilmi.fi>
|
||||
* Copyright (c) 2016 Sqweek
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PA_HOSTAPI_PULSEAUDIO_BLOCK_H_
|
||||
#define _PA_HOSTAPI_PULSEAUDIO_BLOCK_H_
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_allocation.h"
|
||||
#include "pa_hostapi.h"
|
||||
#include "pa_stream.h"
|
||||
#include "pa_cpuload.h"
|
||||
#include "pa_process.h"
|
||||
|
||||
#include "pa_unix_util.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
|
||||
/* PulseAudio headers */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "pa_linux_pulseaudio_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
PaError PaPulseAudio_CloseStreamBlock( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_StartStreamBlock( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_StopStreamBlock( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_AbortStreamBlock( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_ReadStreamBlock( PaStream * stream,
|
||||
void *buffer,
|
||||
unsigned long frames );
|
||||
|
||||
PaError PaPulseAudio_WriteStreamBlock( PaStream * stream,
|
||||
const void *buffer,
|
||||
unsigned long frames );
|
||||
|
||||
signed long PaPulseAudio_GetStreamReadAvailableBlock( PaStream * stream );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif
|
971
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c
vendored
Normal file
971
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c
vendored
Normal file
@ -0,0 +1,971 @@
|
||||
|
||||
/*
|
||||
* PulseAudio host to play natively in Linux based systems without
|
||||
* ALSA emulation
|
||||
*
|
||||
* Copyright (c) 2014-2023 Tuukka Pasanen <tuukka.pasanen@ilmi.fi>
|
||||
* Copyright (c) 2016 Sqweek
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 common_src
|
||||
|
||||
@brief PulseAudio implementation of support for a host API.
|
||||
|
||||
This host API implements PulseAudio support for portaudio
|
||||
it has callback mode and normal write mode support
|
||||
*/
|
||||
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_allocation.h"
|
||||
#include "pa_hostapi.h"
|
||||
#include "pa_stream.h"
|
||||
#include "pa_cpuload.h"
|
||||
#include "pa_process.h"
|
||||
|
||||
#include "pa_unix_util.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
|
||||
#include "pa_linux_pulseaudio_cb_internal.h"
|
||||
|
||||
|
||||
/* PulseAudio headers */
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int PaPulseAudio_updateTimeInfo( pa_stream * s,
|
||||
PaStreamCallbackTimeInfo *timeInfo,
|
||||
int record )
|
||||
{
|
||||
unsigned int isNegative = 0;
|
||||
pa_usec_t pulseaudioStreamTime = 0;
|
||||
pa_usec_t pulseaudioStreamLatency = 0;
|
||||
|
||||
if( pa_stream_get_time( s,
|
||||
&pulseaudioStreamTime ) == -PA_ERR_NODATA )
|
||||
{
|
||||
return -PA_ERR_NODATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeInfo->currentTime = ((PaTime) pulseaudioStreamTime / (PaTime) 1000000);
|
||||
}
|
||||
|
||||
if( pa_stream_get_latency( s,
|
||||
&pulseaudioStreamLatency,
|
||||
&isNegative ) == -PA_ERR_NODATA )
|
||||
{
|
||||
return -PA_ERR_NODATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( record == 0 )
|
||||
{
|
||||
timeInfo->outputBufferDacTime = timeInfo->currentTime + ((PaTime) pulseaudioStreamLatency / (PaTime) 1000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
timeInfo->inputBufferAdcTime = timeInfo->currentTime - ((PaTime) pulseaudioStreamLatency / (PaTime) 1000000);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* locks the Pulse Main loop when not called from it */
|
||||
void PaPulseAudio_Lock( pa_threaded_mainloop *mainloop )
|
||||
{
|
||||
if( !pa_threaded_mainloop_in_thread( mainloop ) ) {
|
||||
pa_threaded_mainloop_lock( mainloop );
|
||||
}
|
||||
}
|
||||
|
||||
/* unlocks the Pulse Main loop when not called from it */
|
||||
void PaPulseAudio_UnLock( pa_threaded_mainloop *mainloop )
|
||||
{
|
||||
if( !pa_threaded_mainloop_in_thread( mainloop ) ) {
|
||||
pa_threaded_mainloop_unlock( mainloop );
|
||||
}
|
||||
}
|
||||
|
||||
void _PaPulseAudio_WriteRingBuffer( PaUtilRingBuffer *ringbuffer,
|
||||
const void *buffer,
|
||||
size_t length )
|
||||
{
|
||||
/*
|
||||
* If there is not enough room. Read from ringbuffer to make
|
||||
* sure that if not full and audio will just underrun
|
||||
*
|
||||
* If you try to read too much and there is no room then this
|
||||
* will fail. But I don't know how to get into that?
|
||||
*/
|
||||
if( PaUtil_GetRingBufferWriteAvailable( ringbuffer ) < length )
|
||||
{
|
||||
uint8_t tmpBuffer[ PULSEAUDIO_BUFFER_SIZE ];
|
||||
PaUtil_ReadRingBuffer( ringbuffer,
|
||||
tmpBuffer,
|
||||
length );
|
||||
}
|
||||
|
||||
PaUtil_WriteRingBuffer( ringbuffer,
|
||||
buffer,
|
||||
length );
|
||||
|
||||
}
|
||||
|
||||
void _PaPulseAudio_Read( PaPulseAudio_Stream *stream,
|
||||
size_t length )
|
||||
{
|
||||
const void *pulseaudioData = NULL;
|
||||
|
||||
/*
|
||||
* Idea behind this is that we fill ringbuffer with data
|
||||
* that comes from input device. When it's available
|
||||
* we'll fill it to callback or blocking reading
|
||||
*/
|
||||
if( pa_stream_peek( stream->inputStream,
|
||||
&pulseaudioData,
|
||||
&length ))
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: Can't read audio!\n",
|
||||
__FUNCTION__) );
|
||||
}
|
||||
else
|
||||
{
|
||||
_PaPulseAudio_WriteRingBuffer( &stream->inputRing, pulseaudioData, length );
|
||||
}
|
||||
|
||||
pa_stream_drop( stream->inputStream );
|
||||
|
||||
pulseaudioData = NULL;
|
||||
|
||||
}
|
||||
|
||||
static int _PaPulseAudio_ProcessAudio(PaPulseAudio_Stream *stream,
|
||||
size_t length)
|
||||
{
|
||||
uint8_t pulseaudioSampleBuffer[PULSEAUDIO_BUFFER_SIZE];
|
||||
size_t hostFramesPerBuffer = stream->bufferProcessor.framesPerHostBuffer;
|
||||
size_t pulseaudioOutputBytes = 0;
|
||||
size_t pulseaudioInputBytes = 0;
|
||||
size_t hostFrameCount = 0;
|
||||
int isOutputCb = 0;
|
||||
int isInputCb = 0;
|
||||
PaStreamCallbackTimeInfo timeInfo;
|
||||
int ret = paContinue;
|
||||
void *bufferData = NULL;
|
||||
size_t pulseaudioOutputWritten = 0;
|
||||
|
||||
/* If there is no specified per host buffer then
|
||||
* just generate one or but correct one in place
|
||||
*/
|
||||
if( hostFramesPerBuffer == paFramesPerBufferUnspecified )
|
||||
{
|
||||
if( !stream->framesPerHostCallback )
|
||||
{
|
||||
/* This just good enough and most
|
||||
* Pulseaudio server and ALSA can handle it
|
||||
*
|
||||
* We should never get here but this is ultimate
|
||||
* backup.
|
||||
*/
|
||||
hostFramesPerBuffer = PAPULSEAUDIO_FRAMESPERBUFFERUNSPEC;
|
||||
|
||||
stream->framesPerHostCallback = hostFramesPerBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
hostFramesPerBuffer = stream->framesPerHostCallback;
|
||||
}
|
||||
}
|
||||
|
||||
if( stream->outputStream )
|
||||
{
|
||||
/* Calculate how many bytes goes to one frame */
|
||||
pulseaudioInputBytes = pulseaudioOutputBytes = (hostFramesPerBuffer * stream->outputFrameSize);
|
||||
|
||||
if( stream->bufferProcessor.streamCallback )
|
||||
{
|
||||
isOutputCb = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we just want to have input but not output (Not Duplex)
|
||||
* Use this calculation
|
||||
*/
|
||||
if( stream->inputStream )
|
||||
{
|
||||
pulseaudioInputBytes = pulseaudioOutputBytes = (hostFramesPerBuffer * stream->inputFrameSize);
|
||||
|
||||
if( stream->bufferProcessor.streamCallback )
|
||||
{
|
||||
isInputCb = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When stopped we should stop feeding or recording right away
|
||||
*/
|
||||
if( stream->isStopped )
|
||||
{
|
||||
return paStreamIsStopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be called before we have reached out
|
||||
* starting Portaudio stream or Portaudio stream
|
||||
* is stopped
|
||||
*/
|
||||
if( !stream->pulseaudioIsActive )
|
||||
{
|
||||
if(stream->outputStream)
|
||||
{
|
||||
bufferData = pulseaudioSampleBuffer;
|
||||
memset( bufferData, 0x00, length);
|
||||
|
||||
pa_stream_write( stream->outputStream,
|
||||
bufferData,
|
||||
length,
|
||||
NULL,
|
||||
0,
|
||||
PA_SEEK_RELATIVE );
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
/* If we have input which is mono and
|
||||
* output which is stereo. We have to copy
|
||||
* mono to monomono which is stereo.
|
||||
* Then just read half and copy
|
||||
*/
|
||||
if( isOutputCb &&
|
||||
stream->outputSampleSpec.channels == 2 &&
|
||||
stream->inputSampleSpec.channels == 1)
|
||||
{
|
||||
pulseaudioInputBytes /= 2;
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
/*
|
||||
* If everything fail like stream vanish or mainloop
|
||||
* is in error state try to handle error
|
||||
*/
|
||||
PA_PULSEAUDIO_IS_ERROR( stream, paStreamIsStopped )
|
||||
|
||||
/* There is only Record stream so
|
||||
* see if we have enough stuff to feed record stream
|
||||
* If not then bail out.
|
||||
*/
|
||||
if( isInputCb &&
|
||||
PaUtil_GetRingBufferReadAvailable(&stream->inputRing) < pulseaudioInputBytes )
|
||||
{
|
||||
if(isOutputCb && (pulseaudioOutputWritten < length) && !stream->missedBytes)
|
||||
{
|
||||
stream->missedBytes = length - pulseaudioOutputWritten;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->missedBytes = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if( pulseaudioOutputWritten >= length)
|
||||
{
|
||||
stream->missedBytes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if( stream->outputStream )
|
||||
{
|
||||
PaPulseAudio_updateTimeInfo( stream->outputStream,
|
||||
&timeInfo,
|
||||
0 );
|
||||
}
|
||||
|
||||
if( stream->inputStream )
|
||||
{
|
||||
PaPulseAudio_updateTimeInfo( stream->inputStream,
|
||||
&timeInfo,
|
||||
1 );
|
||||
}
|
||||
|
||||
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
|
||||
|
||||
/* When doing Portaudio Duplex one has to write and read same amount of data
|
||||
* if not done that way Portaudio will go boo boo and nothing works.
|
||||
* This is why this is done as it's done
|
||||
*
|
||||
* Pulseaudio does not care and this is something that needs small adjusting
|
||||
*/
|
||||
PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
|
||||
&timeInfo,
|
||||
0 );
|
||||
|
||||
/* Read of ther is something to read */
|
||||
if( isInputCb )
|
||||
{
|
||||
PaUtil_ReadRingBuffer( &stream->inputRing,
|
||||
pulseaudioSampleBuffer,
|
||||
pulseaudioInputBytes);
|
||||
|
||||
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
|
||||
0,
|
||||
pulseaudioSampleBuffer,
|
||||
stream->inputSampleSpec.channels );
|
||||
|
||||
PaUtil_SetInputFrameCount( &stream->bufferProcessor,
|
||||
hostFramesPerBuffer );
|
||||
}
|
||||
|
||||
if( isOutputCb )
|
||||
{
|
||||
|
||||
size_t tmpSize = pulseaudioOutputBytes;
|
||||
|
||||
/* Allocate memory to make it faster to output stuff */
|
||||
pa_stream_begin_write( stream->outputStream, &bufferData, &tmpSize );
|
||||
|
||||
/* If bufferData is NULL then output is not ready
|
||||
* and we have to wait for it
|
||||
*/
|
||||
if(!bufferData)
|
||||
{
|
||||
return paNotInitialized;
|
||||
}
|
||||
|
||||
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
|
||||
0,
|
||||
bufferData,
|
||||
stream->outputChannelCount );
|
||||
|
||||
PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
|
||||
hostFramesPerBuffer );
|
||||
|
||||
if( pa_stream_write( stream->outputStream,
|
||||
bufferData,
|
||||
pulseaudioOutputBytes,
|
||||
NULL,
|
||||
0,
|
||||
PA_SEEK_RELATIVE ) )
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: Can't write audio!\n",
|
||||
__FUNCTION__) );
|
||||
}
|
||||
|
||||
pulseaudioOutputWritten += pulseaudioOutputBytes;
|
||||
}
|
||||
|
||||
hostFrameCount =
|
||||
PaUtil_EndBufferProcessing( &stream->bufferProcessor,
|
||||
&ret );
|
||||
|
||||
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer,
|
||||
hostFrameCount );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PaPulseAudio_StreamRecordCb( pa_stream * s,
|
||||
size_t length,
|
||||
void *userdata )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata;
|
||||
|
||||
if( !pulseaudioStream->pulseaudioIsActive )
|
||||
{
|
||||
pulseaudioStream->pulseaudioIsActive = 1;
|
||||
pulseaudioStream->pulseaudioIsStopped= 0;
|
||||
}
|
||||
|
||||
_PaPulseAudio_Read( pulseaudioStream, length );
|
||||
|
||||
/* Let's handle when output happens if Duplex
|
||||
*
|
||||
* Also there is no callback there is no meaning to continue
|
||||
* as we have blocking reading
|
||||
*/
|
||||
if( pulseaudioStream->bufferProcessor.streamCallback )
|
||||
{
|
||||
_PaPulseAudio_ProcessAudio( pulseaudioStream, length );
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_signal( pulseaudioStream->mainloop,
|
||||
0 );
|
||||
}
|
||||
|
||||
void PaPulseAudio_StreamPlaybackCb( pa_stream * s,
|
||||
size_t length,
|
||||
void *userdata )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata;
|
||||
|
||||
if( !pulseaudioStream->inputStream && !pulseaudioStream->pulseaudioIsActive )
|
||||
{
|
||||
pulseaudioStream->pulseaudioIsActive = 1;
|
||||
pulseaudioStream->pulseaudioIsStopped = 0;
|
||||
}
|
||||
|
||||
if( pulseaudioStream->bufferProcessor.streamCallback )
|
||||
{
|
||||
_PaPulseAudio_ProcessAudio( pulseaudioStream, length );
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_signal( pulseaudioStream->mainloop,
|
||||
0 );
|
||||
}
|
||||
|
||||
/* This is left for future use! */
|
||||
static void PaPulseAudio_StreamSuccessCb( pa_stream * s,
|
||||
int success,
|
||||
void *userdata )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata;
|
||||
PA_DEBUG( ("Portaudio %s: %d\n", __FUNCTION__,
|
||||
success) );
|
||||
pa_threaded_mainloop_signal( pulseaudioStream->mainloop,
|
||||
0 );
|
||||
}
|
||||
|
||||
/* This is left for future use! */
|
||||
static void PaPulseAudio_CorkSuccessCb(
|
||||
pa_stream * s,
|
||||
int success,
|
||||
void *userdata
|
||||
)
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata;
|
||||
pa_threaded_mainloop_signal( pulseaudioStream->mainloop,
|
||||
0 );
|
||||
}
|
||||
|
||||
|
||||
/* This is left for future use! */
|
||||
void PaPulseAudio_StreamStartedCb( pa_stream * stream,
|
||||
void *userdata )
|
||||
{
|
||||
PaPulseAudio_Stream *pulseaudioStream = (PaPulseAudio_Stream *) userdata;
|
||||
pa_threaded_mainloop_signal( pulseaudioStream->mainloop,
|
||||
0 );
|
||||
}
|
||||
|
||||
/*
|
||||
When CloseStream() is called, the multi-api layer ensures that
|
||||
the stream has already been stopped or aborted.
|
||||
*/
|
||||
PaError PaPulseAudio_CloseStreamCb( PaStream * s )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s;
|
||||
PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi;
|
||||
pa_operation *pulseaudioOperation = NULL;
|
||||
int waitLoop = 0;
|
||||
int pulseaudioError = 0;
|
||||
|
||||
/* Wait for stream to be stopped */
|
||||
stream->isActive = 0;
|
||||
stream->isStopped = 1;
|
||||
stream->pulseaudioIsActive = 0;
|
||||
stream->pulseaudioIsStopped = 1;
|
||||
|
||||
if( stream->outputStream != NULL
|
||||
&& pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY )
|
||||
{
|
||||
PaPulseAudio_Lock(stream->mainloop);
|
||||
/**
|
||||
* Pause stream so it stops faster
|
||||
*/
|
||||
pulseaudioOperation = pa_stream_cork( stream->outputStream,
|
||||
1,
|
||||
PaPulseAudio_CorkSuccessCb,
|
||||
stream );
|
||||
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
|
||||
while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING )
|
||||
{
|
||||
pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop );
|
||||
waitLoop ++;
|
||||
|
||||
if(waitLoop > 250)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
waitLoop = 0;
|
||||
|
||||
PaPulseAudio_Lock(stream->mainloop);
|
||||
pa_operation_unref( pulseaudioOperation );
|
||||
pulseaudioOperation = NULL;
|
||||
|
||||
pa_stream_disconnect( stream->outputStream );
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
}
|
||||
|
||||
if( stream->inputStream != NULL
|
||||
&& pa_stream_get_state( stream->inputStream ) == PA_STREAM_READY )
|
||||
{
|
||||
PaPulseAudio_Lock( stream->mainloop );
|
||||
|
||||
/**
|
||||
* Pause stream so it stops so it stops faster
|
||||
*/
|
||||
pulseaudioOperation = pa_stream_cork( stream->inputStream,
|
||||
1,
|
||||
PaPulseAudio_CorkSuccessCb,
|
||||
stream );
|
||||
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
|
||||
while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING )
|
||||
{
|
||||
pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop );
|
||||
waitLoop ++;
|
||||
|
||||
if(waitLoop > 250)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
waitLoop = 0;
|
||||
|
||||
PaPulseAudio_Lock( stream->mainloop );
|
||||
pa_operation_unref( pulseaudioOperation );
|
||||
pulseaudioOperation = NULL;
|
||||
|
||||
/* Then we disconnect stream and wait for
|
||||
* Termination
|
||||
*/
|
||||
pa_stream_disconnect( stream->inputStream );
|
||||
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
|
||||
}
|
||||
|
||||
/* Wait for termination for both */
|
||||
while(!waitLoop)
|
||||
{
|
||||
PaPulseAudio_Lock( stream->mainloop );
|
||||
if( stream->inputStream != NULL
|
||||
&& pa_stream_get_state( stream->inputStream ) == PA_STREAM_TERMINATED )
|
||||
{
|
||||
pa_stream_unref( stream->inputStream );
|
||||
stream->inputStream = NULL;
|
||||
}
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
|
||||
PaPulseAudio_Lock( stream->mainloop );
|
||||
if( stream->outputStream != NULL
|
||||
&& pa_stream_get_state(stream->outputStream) == PA_STREAM_TERMINATED )
|
||||
{
|
||||
pa_stream_unref( stream->outputStream );
|
||||
stream->outputStream = NULL;
|
||||
}
|
||||
PaPulseAudio_UnLock( stream->mainloop );
|
||||
|
||||
if((stream->outputStream == NULL
|
||||
&& stream->inputStream == NULL)
|
||||
|| pulseaudioError >= 5000 )
|
||||
{
|
||||
waitLoop = 1;
|
||||
}
|
||||
|
||||
pulseaudioError ++;
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
|
||||
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
|
||||
|
||||
PaUtil_FreeMemory( stream->inputStreamName );
|
||||
PaUtil_FreeMemory( stream->outputStreamName );
|
||||
PaUtil_FreeMemory( stream );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PaError PaPulseAudio_StartStreamCb( PaStream * s )
|
||||
{
|
||||
PaError ret = paNoError;
|
||||
PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) s;
|
||||
int pulseaudioPlaybackStarted = 0;
|
||||
int pulseaudioRecordStarted = 0;
|
||||
pa_stream_state_t pulseaudioState = PA_STREAM_UNCONNECTED;
|
||||
PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi;
|
||||
const char *pulseaudioName = NULL;
|
||||
pa_operation *pulseaudioOperation = NULL;
|
||||
int waitLoop = 0;
|
||||
unsigned int pulseaudioReqFrameSize = (1024 * 2);
|
||||
|
||||
stream->isActive = 0;
|
||||
stream->isStopped = 1;
|
||||
stream->pulseaudioIsActive = 0;
|
||||
stream->pulseaudioIsStopped = 1;
|
||||
stream->missedBytes = 0;
|
||||
|
||||
/* Ready the processor */
|
||||
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
|
||||
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
/* Adjust latencies if that is wanted
|
||||
* https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/LatencyControl/
|
||||
*
|
||||
* tlength is for Playback
|
||||
* fragsize if for Record
|
||||
*/
|
||||
stream->outputBufferAttr.maxlength = (uint32_t)-1;
|
||||
stream->inputBufferAttr.maxlength = (uint32_t)-1;
|
||||
|
||||
stream->outputBufferAttr.tlength = (uint32_t)-1;
|
||||
stream->inputBufferAttr.tlength = (uint32_t)-1;
|
||||
|
||||
stream->outputBufferAttr.fragsize = (uint32_t)-1;
|
||||
stream->inputBufferAttr.fragsize = (uint32_t)-1;
|
||||
|
||||
stream->outputBufferAttr.prebuf = (uint32_t)-1;
|
||||
stream->inputBufferAttr.prebuf = (uint32_t)-1;
|
||||
|
||||
stream->outputBufferAttr.minreq = (uint32_t)-1;
|
||||
stream->inputBufferAttr.minreq = (uint32_t)-1;
|
||||
|
||||
stream->outputUnderflows = 0;
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
pa_stream_flags_t pulseaudioStreamFlags = PA_STREAM_INTERPOLATE_TIMING |
|
||||
PA_STREAM_AUTO_TIMING_UPDATE |
|
||||
PA_STREAM_ADJUST_LATENCY |
|
||||
PA_STREAM_NO_REMIX_CHANNELS |
|
||||
PA_STREAM_NO_REMAP_CHANNELS |
|
||||
PA_STREAM_DONT_MOVE;
|
||||
|
||||
if( stream->inputStream )
|
||||
{
|
||||
/* Default input reads 65,535 bytes setting fragsize
|
||||
* fragments request to smaller chunks of data so it's
|
||||
* easier to get nicer looking timestamps with current
|
||||
* callback system
|
||||
*/
|
||||
stream->inputBufferAttr.fragsize = pa_usec_to_bytes( pulseaudioReqFrameSize,
|
||||
&stream->inputSampleSpec );
|
||||
|
||||
if( stream->inputDevice != paNoDevice)
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: %d (%s)\n", __FUNCTION__, stream->inputDevice,
|
||||
pulseaudioHostApi->pulseaudioDeviceNames[stream->
|
||||
inputDevice]) );
|
||||
}
|
||||
|
||||
pa_stream_set_read_callback( stream->inputStream,
|
||||
PaPulseAudio_StreamRecordCb,
|
||||
stream );
|
||||
|
||||
PaDeviceIndex defaultInputDevice;
|
||||
PaError result = PaUtil_DeviceIndexToHostApiDeviceIndex(
|
||||
&defaultInputDevice,
|
||||
pulseaudioHostApi->inheritedHostApiRep.info.defaultInputDevice,
|
||||
&(pulseaudioHostApi->inheritedHostApiRep) );
|
||||
|
||||
/* NULL means default device */
|
||||
pulseaudioName = NULL;
|
||||
|
||||
/* If default device is not requested then change to wanted device */
|
||||
if( result == paNoError && stream->inputDevice != defaultInputDevice )
|
||||
{
|
||||
pulseaudioName = pulseaudioHostApi->
|
||||
pulseaudioDeviceNames[stream->inputDevice];
|
||||
}
|
||||
|
||||
if ( result == paNoError )
|
||||
{
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
/* Zero means success */
|
||||
if( ! pa_stream_connect_record( stream->inputStream,
|
||||
pulseaudioName,
|
||||
&stream->inputBufferAttr,
|
||||
pulseaudioStreamFlags ) )
|
||||
{
|
||||
pa_stream_set_started_callback( stream->inputStream,
|
||||
PaPulseAudio_StreamStartedCb,
|
||||
stream );
|
||||
}
|
||||
else
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: Can't read audio!\n",
|
||||
__FUNCTION__) );
|
||||
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
for( waitLoop = 0; waitLoop < 100; waitLoop ++ )
|
||||
{
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
pulseaudioState = pa_stream_get_state( stream->inputStream );
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
if( pulseaudioState == PA_STREAM_READY )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( pulseaudioState == PA_STREAM_FAILED ||
|
||||
pulseaudioState == PA_STREAM_TERMINATED )
|
||||
{
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( stream->outputStream )
|
||||
{
|
||||
/* tlength does almost the same as fragsize in record.
|
||||
* See reasoning up there in comments.
|
||||
*
|
||||
* In future this should we tuned when things changed
|
||||
* this just 'good' default
|
||||
*/
|
||||
stream->outputBufferAttr.tlength = pa_usec_to_bytes( pulseaudioReqFrameSize,
|
||||
&stream->outputSampleSpec );
|
||||
|
||||
pa_stream_set_write_callback( stream->outputStream,
|
||||
PaPulseAudio_StreamPlaybackCb,
|
||||
stream );
|
||||
|
||||
/* Just keep on trucking if we are just corked */
|
||||
if( pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY
|
||||
&& pa_stream_is_corked( stream->outputStream ) )
|
||||
{
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
pulseaudioOperation = pa_stream_cork( stream->outputStream,
|
||||
0,
|
||||
PaPulseAudio_CorkSuccessCb,
|
||||
stream );
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING)
|
||||
{
|
||||
pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop );
|
||||
}
|
||||
|
||||
pa_operation_unref( pulseaudioOperation );
|
||||
pulseaudioOperation = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( stream->outputDevice != paNoDevice )
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: %d (%s)\n",
|
||||
__FUNCTION__,
|
||||
stream->outputDevice,
|
||||
pulseaudioHostApi->pulseaudioDeviceNames[stream->
|
||||
outputDevice]) );
|
||||
}
|
||||
|
||||
PaDeviceIndex defaultOutputDevice;
|
||||
PaError result = PaUtil_DeviceIndexToHostApiDeviceIndex( &defaultOutputDevice,
|
||||
pulseaudioHostApi->inheritedHostApiRep.info.defaultOutputDevice,
|
||||
&(pulseaudioHostApi->inheritedHostApiRep) );
|
||||
|
||||
/* NULL means default device */
|
||||
pulseaudioName = NULL;
|
||||
|
||||
/* If default device is not requested then change to wanted device */
|
||||
if( result == paNoError && stream->outputDevice != defaultOutputDevice )
|
||||
{
|
||||
pulseaudioName = pulseaudioHostApi->
|
||||
pulseaudioDeviceNames[stream->outputDevice];
|
||||
}
|
||||
|
||||
if(result == paNoError)
|
||||
{
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
|
||||
if ( ! pa_stream_connect_playback( stream->outputStream,
|
||||
pulseaudioName,
|
||||
&stream->outputBufferAttr,
|
||||
pulseaudioStreamFlags,
|
||||
NULL,
|
||||
NULL ) )
|
||||
{
|
||||
pa_stream_set_underflow_callback( stream->outputStream,
|
||||
PaPulseAudio_StreamUnderflowCb,
|
||||
stream);
|
||||
pa_stream_set_started_callback( stream->outputStream,
|
||||
PaPulseAudio_StreamStartedCb,
|
||||
stream );
|
||||
}
|
||||
else
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: Can't write audio!\n",
|
||||
__FUNCTION__) );
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
for( waitLoop = 0; waitLoop < 100; waitLoop ++ )
|
||||
{
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
pulseaudioState = pa_stream_get_state( stream->outputStream );
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
|
||||
if( pulseaudioState = PA_STREAM_READY )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( pulseaudioState == PA_STREAM_FAILED ||
|
||||
pulseaudioState == PA_STREAM_TERMINATED )
|
||||
{
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !stream->outputStream &&
|
||||
!stream->inputStream )
|
||||
{
|
||||
PA_DEBUG( ("Portaudio %s: Streams not initialized!\n",
|
||||
__FUNCTION__) );
|
||||
goto startstreamcb_error;
|
||||
}
|
||||
|
||||
/* Make sure we pass no error on intialize */
|
||||
ret = paNoError;
|
||||
|
||||
/* Stream is now active */
|
||||
stream->isActive = 1;
|
||||
stream->isStopped = 0;
|
||||
|
||||
/* Allways unlock.. so we don't get locked */
|
||||
startstreamcb_end:
|
||||
return ret;
|
||||
|
||||
error:
|
||||
startstreamcb_error:
|
||||
PA_DEBUG( ("Portaudio %s: Can't start audio!\n",
|
||||
__FUNCTION__) );
|
||||
|
||||
if( pulseaudioPlaybackStarted || pulseaudioRecordStarted )
|
||||
{
|
||||
PaPulseAudio_AbortStreamCb( stream );
|
||||
}
|
||||
|
||||
stream->isActive = 0;
|
||||
stream->isStopped = 1;
|
||||
ret = paNotInitialized;
|
||||
|
||||
goto startstreamcb_end;
|
||||
}
|
||||
|
||||
static PaError RequestStop( PaPulseAudio_Stream * stream,
|
||||
int abort )
|
||||
{
|
||||
PaError ret = paNoError;
|
||||
PaPulseAudio_HostApiRepresentation *pulseaudioHostApi = stream->hostapi;
|
||||
pa_operation *pulseaudioOperation = NULL;
|
||||
|
||||
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
|
||||
|
||||
/* Wait for stream to be stopped */
|
||||
stream->isActive = 0;
|
||||
stream->isStopped = 1;
|
||||
stream->pulseaudioIsActive = 0;
|
||||
stream->pulseaudioIsStopped = 1;
|
||||
|
||||
stream->missedBytes = 0;
|
||||
|
||||
/* Test if there is something that we can play */
|
||||
if( stream->outputStream
|
||||
&& pa_stream_get_state( stream->outputStream ) == PA_STREAM_READY
|
||||
&& !pa_stream_is_corked( stream->outputStream )
|
||||
&& !abort )
|
||||
{
|
||||
pulseaudioOperation = pa_stream_cork( stream->outputStream,
|
||||
1,
|
||||
PaPulseAudio_CorkSuccessCb,
|
||||
stream );
|
||||
|
||||
while( pa_operation_get_state( pulseaudioOperation ) == PA_OPERATION_RUNNING )
|
||||
{
|
||||
pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop );
|
||||
}
|
||||
|
||||
pa_operation_unref( pulseaudioOperation );
|
||||
|
||||
pulseaudioOperation = NULL;
|
||||
}
|
||||
|
||||
requeststop_error:
|
||||
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );
|
||||
stream->isActive = 0;
|
||||
stream->isStopped = 1;
|
||||
stream->pulseaudioIsActive = 0;
|
||||
stream->pulseaudioIsStopped = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PaError PaPulseAudio_StopStreamCb( PaStream * s )
|
||||
{
|
||||
return RequestStop( (PaPulseAudio_Stream *) s,
|
||||
0 );
|
||||
}
|
||||
|
||||
PaError PaPulseAudio_AbortStreamCb( PaStream * s )
|
||||
{
|
||||
return RequestStop( (PaPulseAudio_Stream *) s,
|
||||
1 );
|
||||
}
|
97
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h
vendored
Normal file
97
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_cb_internal.h
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
/*
|
||||
* PulseAudio host to play natively in Linux based systems without
|
||||
* ALSA emulation
|
||||
*
|
||||
* Copyright (c) 2014-2023 Tuukka Pasanen <tuukka.pasanen@ilmi.fi>
|
||||
* Copyright (c) 2016 Sqweek
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PA_HOSTAPI_PULSEAUDIO_CB_H_
|
||||
#define _PA_HOSTAPI_PULSEAUDIO_CB_H_
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_allocation.h"
|
||||
#include "pa_hostapi.h"
|
||||
#include "pa_stream.h"
|
||||
#include "pa_cpuload.h"
|
||||
#include "pa_process.h"
|
||||
|
||||
#include "pa_unix_util.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
|
||||
|
||||
/* PulseAudio headers */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "pa_linux_pulseaudio_internal.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int PaPulseAudio_updateTimeInfo( pa_stream * s,
|
||||
PaStreamCallbackTimeInfo *timeInfo,
|
||||
int record );
|
||||
|
||||
void *PaPulseAudio_processThread( void *userdata );
|
||||
|
||||
PaError PaPulseAudio_CloseStreamCb( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_StartStreamCb( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_StopStreamCb( PaStream * stream );
|
||||
|
||||
PaError PaPulseAudio_AbortStreamCb( PaStream * stream );
|
||||
|
||||
void PaPulseAudio_StreamRecordCb( pa_stream * s,
|
||||
size_t length,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_StreamPlaybackCb( pa_stream * s,
|
||||
size_t length,
|
||||
void *userdata );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif
|
273
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h
vendored
Normal file
273
3rdparty/portaudio/src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
|
||||
/*
|
||||
* PulseAudio host to play natively in Linux based systems without
|
||||
* ALSA emulation
|
||||
*
|
||||
* Copyright (c) 2014-2023 Tuukka Pasanen <tuukka.pasanen@ilmi.fi>
|
||||
* Copyright (c) 2016 Sqweek
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PA_HOSTAPI_PULSEAUDIO_H_
|
||||
#define _PA_HOSTAPI_PULSEAUDIO_H_
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_allocation.h"
|
||||
#include "pa_hostapi.h"
|
||||
#include "pa_stream.h"
|
||||
#include "pa_cpuload.h"
|
||||
#include "pa_process.h"
|
||||
|
||||
#include "pa_unix_util.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
#include "pa_debugprint.h"
|
||||
|
||||
/* PulseAudio headers */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* prototypes for functions declared in this file */
|
||||
|
||||
#define PA_PULSEAUDIO_SET_LAST_HOST_ERROR(errorCode, errorText) \
|
||||
PaUtil_SetLastHostErrorInfo(paInDevelopment, errorCode, errorText)
|
||||
|
||||
#define PAPULSEAUDIO_MAX_DEVICECOUNT 1024
|
||||
#define PAPULSEAUDIO_MAX_DEVICENAME 1024
|
||||
|
||||
/* Default latency values to expose. Chosen by trial and error to be reasonable. */
|
||||
#define PA_PULSEAUDIO_DEFAULT_MIN_LATENCY 0.010
|
||||
#define PA_PULSEAUDIO_DEFAULT_MAX_LATENCY 0.080
|
||||
|
||||
/* Just some value that Pulseaudio can handle */
|
||||
#define PAPULSEAUDIO_FRAMESPERBUFFERUNSPEC 32
|
||||
|
||||
/* Assuming of 2 seconds of 44100 Hz sample rate with FLOAT (4 bytes) and stereo channels (2 channels).
|
||||
You should have pretty good size buffer with this. If output/intput doesn't happens in 2 second we
|
||||
have more trouble than this buffer.
|
||||
@todo change this to something more sophisticated */
|
||||
#define PULSEAUDIO_BUFFER_SIZE (96100 * 4 * 2)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PaUtilHostApiRepresentation inheritedHostApiRep;
|
||||
PaUtilStreamInterface callbackStreamInterface;
|
||||
PaUtilStreamInterface blockingStreamInterface;
|
||||
|
||||
PaUtilAllocationGroup *allocations;
|
||||
|
||||
PaHostApiIndex hostApiIndex;
|
||||
PaDeviceInfo deviceInfoArray[PAPULSEAUDIO_MAX_DEVICECOUNT];
|
||||
char *pulseaudioDeviceNames[PAPULSEAUDIO_MAX_DEVICECOUNT];
|
||||
pa_sample_spec pulseaudioDefaultSampleSpec;
|
||||
|
||||
/* PulseAudio stuff goes here */
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_mainloop_api *mainloopApi;
|
||||
pa_context *context;
|
||||
int deviceCount;
|
||||
pa_time_event *timeEvent;
|
||||
}
|
||||
PaPulseAudio_HostApiRepresentation;
|
||||
|
||||
/* PaPulseAudio_Stream - a stream data structure specifically for this implementation */
|
||||
|
||||
typedef struct PaPulseAudio_Stream
|
||||
{
|
||||
PaUtilStreamRepresentation streamRepresentation;
|
||||
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
|
||||
PaUtilBufferProcessor bufferProcessor;
|
||||
PaPulseAudio_HostApiRepresentation *hostapi;
|
||||
|
||||
unsigned long framesPerHostCallback;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_sample_spec outputSampleSpec;
|
||||
pa_sample_spec inputSampleSpec;
|
||||
pa_stream *outputStream;
|
||||
pa_stream *inputStream;
|
||||
pa_buffer_attr outputBufferAttr;
|
||||
pa_buffer_attr inputBufferAttr;
|
||||
int outputUnderflows;
|
||||
int outputChannelCount;
|
||||
int inputChannelCount;
|
||||
|
||||
size_t maxFramesPerBuffer;
|
||||
size_t maxFramesHostPerBuffer;
|
||||
int outputFrameSize;
|
||||
int inputFrameSize;
|
||||
|
||||
PaDeviceIndex inputDevice;
|
||||
PaDeviceIndex outputDevice;
|
||||
|
||||
char *outputStreamName;
|
||||
char *inputStreamName;
|
||||
|
||||
PaUtilRingBuffer inputRing;
|
||||
|
||||
size_t missedBytes;
|
||||
|
||||
/* Used in communication between threads
|
||||
*
|
||||
* State machine works like this:
|
||||
* When stream is wanted to start with Pa_StartStream
|
||||
* then isActive is 1 if opening of devices goes well
|
||||
* and isStopped is then 0.
|
||||
*
|
||||
* When requested to stop isStopped is 1 on isActive is 0
|
||||
* and nothing should be written to ouput or read from input
|
||||
* anymore
|
||||
*
|
||||
* Pulseaudio does not like this as it creates streams and they
|
||||
* start when they are ready and it can be after we have
|
||||
* exited Pa_StartStream or before if get's kicked up very fast
|
||||
*
|
||||
* pulseaudioIsActive and pulseaudioIsStopped are used to find if
|
||||
* there is stream active or stopped in pulseaudio side. They
|
||||
* live their own life besides isActive and isStopped to make sure
|
||||
* that portaudio will have input and output available before
|
||||
* reading or writing to stream.
|
||||
*/
|
||||
volatile sig_atomic_t isActive;
|
||||
volatile sig_atomic_t isStopped;
|
||||
volatile sig_atomic_t pulseaudioIsActive;
|
||||
volatile sig_atomic_t pulseaudioIsStopped;
|
||||
|
||||
}
|
||||
PaPulseAudio_Stream;
|
||||
|
||||
/* PulseAudio Error checking macro */
|
||||
#define PA_PULSEAUDIO_IS_ERROR(pastream, errorCode) \
|
||||
if( !(pastream) || \
|
||||
!(pastream)->context || \
|
||||
!PA_CONTEXT_IS_GOOD( pa_context_get_state( (pastream)->context ) ) || \
|
||||
( (pastream)->outputStream && \
|
||||
!PA_STREAM_IS_GOOD( pa_stream_get_state( (pastream)->outputStream ) ) ) || \
|
||||
( (pastream)->inputStream && \
|
||||
!PA_STREAM_IS_GOOD( pa_stream_get_state( (pastream)->inputStream ) ) ) ) \
|
||||
{ \
|
||||
if( !(pastream) || \
|
||||
( (pastream)->context && \
|
||||
pa_context_get_state( (pastream)->context ) == PA_CONTEXT_FAILED ) || \
|
||||
( (pastream)->outputStream && \
|
||||
pa_stream_get_state( (pastream)->outputStream ) == PA_STREAM_FAILED ) || \
|
||||
( (pastream)->inputStream && \
|
||||
pa_stream_get_state( (pastream)->inputStream ) == PA_STREAM_FAILED ) ) \
|
||||
{ \
|
||||
return errorCode; \
|
||||
} \
|
||||
} \
|
||||
if( !pastream->isActive || pastream->isStopped ) \
|
||||
{ \
|
||||
return paStreamIsStopped; \
|
||||
}
|
||||
|
||||
void PaPulseAudio_Lock( pa_threaded_mainloop *mainloop );
|
||||
|
||||
void PaPulseAudio_UnLock( pa_threaded_mainloop *mainloop );
|
||||
|
||||
PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation ** hostApi,
|
||||
PaHostApiIndex index );
|
||||
|
||||
void Terminate( struct PaUtilHostApiRepresentation *hostApi );
|
||||
|
||||
|
||||
PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
||||
const PaStreamParameters * inputParameters,
|
||||
const PaStreamParameters * outputParameters,
|
||||
double sampleRate );
|
||||
|
||||
PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
PaStream ** s,
|
||||
const PaStreamParameters * inputParameters,
|
||||
const PaStreamParameters * outputParameters,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
PaStreamFlags streamFlags,
|
||||
PaStreamCallback * streamCallback,
|
||||
void *userData );
|
||||
|
||||
|
||||
PaError IsStreamStopped( PaStream * s );
|
||||
PaError IsStreamActive( PaStream * stream );
|
||||
|
||||
PaTime GetStreamTime( PaStream * stream );
|
||||
double GetStreamCpuLoad( PaStream * stream );
|
||||
|
||||
PaPulseAudio_HostApiRepresentation *PaPulseAudio_New( void );
|
||||
void PaPulseAudio_Free( PaPulseAudio_HostApiRepresentation * ptr );
|
||||
|
||||
int PaPulseAudio_CheckConnection( PaPulseAudio_HostApiRepresentation * ptr );
|
||||
|
||||
void PaPulseAudio_CheckContextStateCb( pa_context * c,
|
||||
void *userdata );
|
||||
void PaPulseAudio_ServerInfoCb( pa_context *c,
|
||||
const pa_server_info *i,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_SinkListCb( pa_context * c,
|
||||
const pa_sink_info * l,
|
||||
int eol,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_SourceListCb( pa_context * c,
|
||||
const pa_source_info * l,
|
||||
int eol,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_StreamStateCb( pa_stream * s,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_StreamStartedCb( pa_stream * s,
|
||||
void *userdata );
|
||||
|
||||
void PaPulseAudio_StreamUnderflowCb( pa_stream * s,
|
||||
void *userdata );
|
||||
|
||||
PaError PaPulseAudio_ConvertPortaudioFormatToPaPulseAudio_( PaSampleFormat portaudiosf,
|
||||
pa_sample_spec * pulseaudiosf
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif
|
@ -44,6 +44,7 @@
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <process.h>
|
||||
#include <assert.h>
|
||||
@ -106,10 +107,11 @@
|
||||
#include "pa_stream.h"
|
||||
#include "pa_cpuload.h"
|
||||
#include "pa_process.h"
|
||||
#include "pa_win_wasapi.h"
|
||||
#include "pa_debugprint.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
#include "pa_win_version.h"
|
||||
#include "pa_win_coinitialize.h"
|
||||
#include "pa_win_wasapi.h"
|
||||
|
||||
#if !defined(NTDDI_VERSION) || (defined(__GNUC__) && (__GNUC__ <= 6) && !defined(__MINGW64__))
|
||||
|
||||
@ -281,9 +283,26 @@ PA_DEFINE_IID(IPart, AE2DE0E4, 5BCA, 4F2D, aa, 46, 5d, 13, f8, fd
|
||||
PA_DEFINE_IID(IKsJackDescription, 4509F757, 2D46, 4637, 8e, 62, ce, 7d, b9, 44, f5, 7b);
|
||||
|
||||
// Media formats:
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM, 0x00000000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_IEC61937_
|
||||
#define _WAVEFORMATEXTENSIBLE_IEC61937_
|
||||
typedef struct {
|
||||
WAVEFORMATEXTENSIBLE FormatExt;
|
||||
DWORD dwEncodedSamplesPerSec;
|
||||
DWORD dwEncodedChannelCount;
|
||||
DWORD dwAverageBytesPerSec;
|
||||
} WAVEFORMATEXTENSIBLE_IEC61937, *PWAVEFORMATEXTENSIBLE_IEC61937;
|
||||
#endif // !_WAVEFORMATEXTENSIBLE_IEC61937_
|
||||
|
||||
typedef union _WAVEFORMATEXTENSIBLE_UNION
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE ext;
|
||||
WAVEFORMATEXTENSIBLE_IEC61937 iec61937;
|
||||
} WAVEFORMATEXTENSIBLE_UNION;
|
||||
|
||||
#ifdef __IAudioClient2_INTERFACE_DEFINED__
|
||||
typedef enum _pa_AUDCLNT_STREAMOPTIONS {
|
||||
@ -534,7 +553,7 @@ typedef struct PaWasapiSubStream
|
||||
#endif
|
||||
IAudioClient *clientProc;
|
||||
|
||||
WAVEFORMATEXTENSIBLE wavex;
|
||||
WAVEFORMATEXTENSIBLE_UNION wavexu;
|
||||
UINT32 bufferSize;
|
||||
REFERENCE_TIME deviceLatency;
|
||||
REFERENCE_TIME period;
|
||||
@ -1164,193 +1183,24 @@ static BOOL IsWow64()
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
typedef enum EWindowsVersion
|
||||
{
|
||||
WINDOWS_UNKNOWN = 0,
|
||||
WINDOWS_VISTA_SERVER2008,
|
||||
WINDOWS_7_SERVER2008R2,
|
||||
WINDOWS_8_SERVER2012,
|
||||
WINDOWS_8_1_SERVER2012R2,
|
||||
WINDOWS_10_SERVER2016,
|
||||
WINDOWS_FUTURE
|
||||
}
|
||||
EWindowsVersion;
|
||||
// Alternative way for checking Windows version (allows to check version on Windows 8.1 and up)
|
||||
#ifndef PA_WINRT
|
||||
static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
|
||||
{
|
||||
typedef ULONGLONG (NTAPI *LPFN_VERSETCONDITIONMASK)(ULONGLONG ConditionMask, DWORD TypeMask, BYTE Condition);
|
||||
typedef BOOL (WINAPI *LPFN_VERIFYVERSIONINFO)(LPOSVERSIONINFOEXA lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
|
||||
|
||||
LPFN_VERSETCONDITIONMASK fnVerSetConditionMask;
|
||||
LPFN_VERIFYVERSIONINFO fnVerifyVersionInfo;
|
||||
OSVERSIONINFOEXA osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
|
||||
DWORDLONG dwlConditionMask;
|
||||
|
||||
fnVerSetConditionMask = (LPFN_VERSETCONDITIONMASK)GetProcAddress(GetModuleHandleA("kernel32"), "VerSetConditionMask");
|
||||
fnVerifyVersionInfo = (LPFN_VERIFYVERSIONINFO)GetProcAddress(GetModuleHandleA("kernel32"), "VerifyVersionInfoA");
|
||||
|
||||
if ((fnVerSetConditionMask == NULL) || (fnVerifyVersionInfo == NULL))
|
||||
return FALSE;
|
||||
|
||||
dwlConditionMask = fnVerSetConditionMask(
|
||||
fnVerSetConditionMask(
|
||||
fnVerSetConditionMask(
|
||||
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
|
||||
VER_MINORVERSION, VER_GREATER_EQUAL),
|
||||
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
|
||||
osvi.dwMajorVersion = wMajorVersion;
|
||||
osvi.dwMinorVersion = wMinorVersion;
|
||||
osvi.wServicePackMajor = wServicePackMajor;
|
||||
|
||||
return (fnVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE);
|
||||
}
|
||||
#endif
|
||||
// Get Windows version
|
||||
// note: We are trying to get Windows version starting from Windows Vista. Earlier OS versions
|
||||
// will fall into WINDOWS_UNKNOWN case.
|
||||
static EWindowsVersion GetWindowsVersion()
|
||||
{
|
||||
#ifndef PA_WINRT
|
||||
static EWindowsVersion version = WINDOWS_UNKNOWN;
|
||||
|
||||
if (version == WINDOWS_UNKNOWN)
|
||||
{
|
||||
DWORD dwMajorVersion = 0xFFFFFFFFU, dwMinorVersion = 0, dwBuild = 0;
|
||||
|
||||
// RTL_OSVERSIONINFOW equals OSVERSIONINFOW but it is missing inb MinGW winnt.h header,
|
||||
// thus use OSVERSIONINFOW for greater portability
|
||||
typedef NTSTATUS (WINAPI *LPFN_RTLGETVERSION)(POSVERSIONINFOW lpVersionInformation);
|
||||
LPFN_RTLGETVERSION fnRtlGetVersion;
|
||||
|
||||
#define NTSTATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
|
||||
// RtlGetVersion must be able to provide true Windows version (Windows 10 may be reported as Windows 8
|
||||
// by GetVersion API)
|
||||
if ((fnRtlGetVersion = (LPFN_RTLGETVERSION)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion")) != NULL)
|
||||
{
|
||||
OSVERSIONINFOW ver = { sizeof(OSVERSIONINFOW), 0, 0, 0, 0, {0} };
|
||||
|
||||
if (fnRtlGetVersion(&ver) == NTSTATUS_SUCCESS)
|
||||
{
|
||||
dwMajorVersion = ver.dwMajorVersion;
|
||||
dwMinorVersion = ver.dwMinorVersion;
|
||||
dwBuild = ver.dwBuildNumber;
|
||||
}
|
||||
|
||||
PRINT(("WASAPI: getting Windows version with RtlGetVersion(): major=%d, minor=%d, build=%d\n", dwMajorVersion, dwMinorVersion, dwBuild));
|
||||
}
|
||||
|
||||
#undef NTSTATUS_SUCCESS
|
||||
|
||||
// fallback to GetVersion if RtlGetVersion is missing
|
||||
if (dwMajorVersion == 0xFFFFFFFFU)
|
||||
{
|
||||
typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
|
||||
LPFN_GETVERSION fnGetVersion;
|
||||
|
||||
if ((fnGetVersion = (LPFN_GETVERSION)GetProcAddress(GetModuleHandleA("kernel32"), "GetVersion")) != NULL)
|
||||
{
|
||||
DWORD dwVersion = fnGetVersion();
|
||||
|
||||
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
||||
|
||||
if (dwVersion < 0x80000000)
|
||||
dwBuild = (DWORD)(HIWORD(dwVersion));
|
||||
|
||||
PRINT(("WASAPI: getting Windows version with GetVersion(): major=%d, minor=%d, build=%d\n", dwMajorVersion, dwMinorVersion, dwBuild));
|
||||
}
|
||||
}
|
||||
|
||||
if (dwMajorVersion != 0xFFFFFFFFU)
|
||||
{
|
||||
switch (dwMajorVersion)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
break; // skip lower
|
||||
case 6:
|
||||
switch (dwMinorVersion)
|
||||
{
|
||||
case 0: version = WINDOWS_VISTA_SERVER2008; break;
|
||||
case 1: version = WINDOWS_7_SERVER2008R2; break;
|
||||
case 2: version = WINDOWS_8_SERVER2012; break;
|
||||
case 3: version = WINDOWS_8_1_SERVER2012R2; break;
|
||||
default: version = WINDOWS_FUTURE; break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (dwMinorVersion)
|
||||
{
|
||||
case 0: version = WINDOWS_10_SERVER2016; break;
|
||||
default: version = WINDOWS_FUTURE; break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
version = WINDOWS_FUTURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// fallback to VerifyVersionInfo if RtlGetVersion and GetVersion are missing
|
||||
else
|
||||
{
|
||||
PRINT(("WASAPI: getting Windows version with VerifyVersionInfo()\n"));
|
||||
|
||||
if (IsWindowsVersionOrGreater(10, 0, 0))
|
||||
version = WINDOWS_10_SERVER2016;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 3, 0))
|
||||
version = WINDOWS_8_1_SERVER2012R2;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 2, 0))
|
||||
version = WINDOWS_8_SERVER2012;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 1, 0))
|
||||
version = WINDOWS_7_SERVER2008R2;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 0, 0))
|
||||
version = WINDOWS_VISTA_SERVER2008;
|
||||
else
|
||||
version = WINDOWS_FUTURE;
|
||||
}
|
||||
|
||||
PRINT(("WASAPI: Windows version = %d\n", version));
|
||||
}
|
||||
|
||||
return version;
|
||||
#else
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10)
|
||||
return WINDOWS_10_SERVER2016;
|
||||
#else
|
||||
return WINDOWS_8_SERVER2012;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static BOOL UseWOW64Workaround()
|
||||
{
|
||||
// note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
|
||||
// method. Windows 7 x64 seems has WOW64 bug fixed.
|
||||
|
||||
return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008));
|
||||
return (IsWow64() && (PaWinUtil_GetOsVersion() == paOsVersionWindowsVistaServer2008));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static UINT32 GetAudioClientVersion()
|
||||
{
|
||||
if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
|
||||
PaOsVersion version = PaWinUtil_GetOsVersion();
|
||||
|
||||
if (version >= paOsVersionWindows10Server2016)
|
||||
return 3;
|
||||
else
|
||||
if (GetWindowsVersion() >= WINDOWS_8_SERVER2012)
|
||||
if (version >= paOsVersionWindows8Server2012)
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
@ -1764,11 +1614,11 @@ static HRESULT ActivateAudioInterface(const PaWasapiDeviceInfo *deviceInfo, cons
|
||||
switch (streamInfo->streamOption)
|
||||
{
|
||||
case eStreamOptionRaw:
|
||||
if (GetWindowsVersion() >= WINDOWS_8_1_SERVER2012R2)
|
||||
if (PaWinUtil_GetOsVersion() >= paOsVersionWindows8_1Server2012R2)
|
||||
audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_RAW;
|
||||
break;
|
||||
case eStreamOptionMatchFormat:
|
||||
if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
|
||||
if (PaWinUtil_GetOsVersion() >= paOsVersionWindows10Server2016)
|
||||
audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
|
||||
break;
|
||||
}
|
||||
@ -2440,7 +2290,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
||||
|
||||
#ifndef PA_WINRT
|
||||
// Fail safely for any Windows version below Windows Vista
|
||||
if (GetWindowsVersion() == WINDOWS_UNKNOWN)
|
||||
if (PaWinUtil_GetOsVersion() < paOsVersionWindowsVistaServer2008)
|
||||
{
|
||||
PRINT(("WASAPI: Unsupported Windows version!\n"));
|
||||
return paNoError;
|
||||
@ -2652,7 +2502,7 @@ int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned
|
||||
if (stream == NULL)
|
||||
return paBadStreamPtr;
|
||||
|
||||
format = (bOutput == TRUE ? &stream->out.wavex : &stream->in.wavex);
|
||||
format = (bOutput == TRUE ? &stream->out.wavexu.ext : &stream->in.wavexu.ext);
|
||||
|
||||
size = min(formatSize, (UINT32)sizeof(*format));
|
||||
memcpy(pFormat, format, size);
|
||||
@ -2856,9 +2706,7 @@ static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
|
||||
// ------------------------------------------------------------------------------------------
|
||||
PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext)
|
||||
{
|
||||
const WAVEFORMATEX *fmt = (WAVEFORMATEX *)fmtext;
|
||||
|
||||
switch (fmt->wFormatTag)
|
||||
switch (fmtext->Format.wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
if (IsEqualGUID(&fmtext->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
|
||||
@ -2869,7 +2717,7 @@ PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext)
|
||||
else
|
||||
if (IsEqualGUID(&fmtext->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
|
||||
{
|
||||
switch (fmt->wBitsPerSample)
|
||||
switch (fmtext->Format.wBitsPerSample)
|
||||
{
|
||||
case 32: return paInt32;
|
||||
case 24: return paInt24;
|
||||
@ -2877,13 +2725,25 @@ PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext)
|
||||
case 8: return paUInt8;
|
||||
}
|
||||
}
|
||||
else
|
||||
// KSDATAFORMAT_SUBTYPE_IEC61937_PCM is a naked format GUID which has different Data1 and Data2
|
||||
// depending on the passthrough format, for the possible values refer Microsoft documentation
|
||||
// "Representing Formats for IEC 61937 Transmissions". Here we check if Data3 and Data4 match
|
||||
// assuming that if they do match then we have KSDATAFORMAT_SUBTYPE_IEC61937_XXX format.
|
||||
// Currently PA WASAPI is using KSDATAFORMAT_SUBTYPE_IEEE_FLOAT and KSDATAFORMAT_SUBTYPE_PCM
|
||||
// therefore this check is reliable in this way.
|
||||
if (!memcmp(&fmtext->SubFormat.Data3, &pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM.Data3,
|
||||
(sizeof(GUID) - offsetof(GUID, Data3))))
|
||||
{
|
||||
return paInt16;
|
||||
}
|
||||
break; }
|
||||
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
return paFloat32;
|
||||
|
||||
case WAVE_FORMAT_PCM: {
|
||||
switch (fmt->wBitsPerSample)
|
||||
switch (fmtext->Format.wBitsPerSample)
|
||||
{
|
||||
case 32: return paInt32;
|
||||
case 24: return paInt24;
|
||||
@ -2897,7 +2757,7 @@ PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
|
||||
static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE_UNION *wavexu, const PaStreamParameters *params,
|
||||
double sampleRate, BOOL packedOnly)
|
||||
{
|
||||
WORD bitsPerSample;
|
||||
@ -2905,6 +2765,8 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
|
||||
DWORD channelMask = 0;
|
||||
BOOL useExtensible = (params->channelCount > 2); // format is always forced for >2 channels format
|
||||
PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
|
||||
WAVEFORMATEXTENSIBLE *wavex = (WAVEFORMATEXTENSIBLE *)wavexu;
|
||||
BOOL passthroughMode = ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiPassthrough));
|
||||
|
||||
// Convert PaSampleFormat to valid data bits
|
||||
if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
|
||||
@ -2917,9 +2779,9 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
|
||||
useExtensible = TRUE;
|
||||
}
|
||||
|
||||
memset(wavex, 0, sizeof(*wavex));
|
||||
memset(wavexu, 0, sizeof(*wavexu));
|
||||
|
||||
old = (WAVEFORMATEX *)wavex;
|
||||
old = (WAVEFORMATEX *)wavex;
|
||||
old->nChannels = (WORD)params->channelCount;
|
||||
old->nSamplesPerSec = (DWORD)sampleRate;
|
||||
old->wBitsPerSample = bitsPerSample;
|
||||
@ -2937,18 +2799,35 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
|
||||
// WAVEFORMATEX
|
||||
if (!useExtensible)
|
||||
{
|
||||
old->wFormatTag = WAVE_FORMAT_PCM;
|
||||
old->wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
// WAVEFORMATEXTENSIBLE
|
||||
else
|
||||
{
|
||||
old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
old->cbSize = (passthroughMode ? (sizeof(WAVEFORMATEXTENSIBLE_IEC61937) - sizeof(WAVEFORMATEX))
|
||||
: (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)));
|
||||
|
||||
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
|
||||
if (passthroughMode)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE_IEC61937 *wavex_61937 = (WAVEFORMATEXTENSIBLE_IEC61937 *)wavexu;
|
||||
|
||||
wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM;
|
||||
wavex->SubFormat.Data1 = (streamInfo->passthrough.formatId >> 16);
|
||||
wavex->SubFormat.Data2 = (USHORT)(streamInfo->passthrough.formatId & 0x0000FFFF);
|
||||
|
||||
wavex_61937->dwEncodedSamplesPerSec = streamInfo->passthrough.encodedSamplesPerSec;
|
||||
wavex_61937->dwEncodedChannelCount = streamInfo->passthrough.encodedChannelCount;
|
||||
wavex_61937->dwAverageBytesPerSec = streamInfo->passthrough.averageBytesPerSec;
|
||||
}
|
||||
else if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
|
||||
{
|
||||
wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
|
||||
}
|
||||
|
||||
wavex->Samples.wValidBitsPerSample = bitsPerSample;
|
||||
|
||||
@ -2995,11 +2874,11 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double sampleRate,
|
||||
const PaStreamParameters *params, WAVEFORMATEXTENSIBLE *outWavex, BOOL packedSampleFormatOnly)
|
||||
const PaStreamParameters *params, WAVEFORMATEXTENSIBLE_UNION *outWavexU, BOOL packedSampleFormatOnly)
|
||||
{
|
||||
HRESULT hr = !S_OK;
|
||||
AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
WAVEFORMATEXTENSIBLE testFormat;
|
||||
WAVEFORMATEXTENSIBLE_UNION testFormat;
|
||||
PaStreamParameters testParams;
|
||||
int i;
|
||||
static const PaSampleFormat bestToWorst[] = { paInt32, paInt24, paFloat32, paInt16 };
|
||||
@ -3012,9 +2891,9 @@ static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double
|
||||
|
||||
if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
|
||||
{
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
|
||||
{
|
||||
(*outWavex) = testFormat;
|
||||
(*outWavexU) = testFormat;
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@ -3026,9 +2905,9 @@ static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double
|
||||
|
||||
if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
|
||||
{
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
|
||||
{
|
||||
(*outWavex) = testFormat;
|
||||
(*outWavexU) = testFormat;
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@ -3043,9 +2922,9 @@ static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double
|
||||
|
||||
if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
|
||||
{
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
|
||||
if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
|
||||
{
|
||||
(*outWavex) = testFormat;
|
||||
(*outWavexU) = testFormat;
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@ -3056,13 +2935,14 @@ static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const PaStreamParameters *_params,
|
||||
AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex, BOOL output)
|
||||
AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE_UNION *outWavexU, BOOL output)
|
||||
{
|
||||
PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)_params->hostApiSpecificStreamInfo;
|
||||
WAVEFORMATEX *sharedClosestMatch = NULL;
|
||||
HRESULT hr = !S_OK;
|
||||
PaStreamParameters params = (*_params);
|
||||
const BOOL explicitFormat = (streamInfo != NULL) && ((streamInfo->flags & paWinWasapiExplicitSampleFormat) == paWinWasapiExplicitSampleFormat);
|
||||
WAVEFORMATEXTENSIBLE *outWavex = (WAVEFORMATEXTENSIBLE *)outWavexU;
|
||||
(void)output;
|
||||
|
||||
/* It was not noticed that 24-bit Input producing no output while device accepts this format.
|
||||
@ -3074,10 +2954,10 @@ static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const P
|
||||
params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
|
||||
|
||||
// Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
|
||||
MakeWaveFormatFromParams(outWavex, ¶ms, sampleRate, FALSE);
|
||||
MakeWaveFormatFromParams(outWavexU, ¶ms, sampleRate, FALSE);
|
||||
|
||||
// If built-in PCM converter requested then shared mode format will always succeed
|
||||
if ((GetWindowsVersion() >= WINDOWS_7_SERVER2008R2) &&
|
||||
if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
|
||||
(shareMode == AUDCLNT_SHAREMODE_SHARED) &&
|
||||
((streamInfo != NULL) && (streamInfo->flags & paWinWasapiAutoConvert)))
|
||||
return paFormatIsSupported;
|
||||
@ -3088,7 +2968,7 @@ static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const P
|
||||
if ((hr != S_OK) && (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
|
||||
{
|
||||
// Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
|
||||
MakeWaveFormatFromParams(outWavex, ¶ms, sampleRate, TRUE);
|
||||
MakeWaveFormatFromParams(outWavexU, ¶ms, sampleRate, TRUE);
|
||||
hr = IAudioClient_IsFormatSupported(client, shareMode, &outWavex->Format, NULL);
|
||||
}
|
||||
|
||||
@ -3136,11 +3016,11 @@ static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const P
|
||||
if ((shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && !explicitFormat)
|
||||
{
|
||||
// Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
|
||||
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, ¶ms, outWavex, FALSE)) == S_OK)
|
||||
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, ¶ms, outWavexU, FALSE)) == S_OK)
|
||||
return paFormatIsSupported;
|
||||
|
||||
// Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
|
||||
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, ¶ms, outWavex, TRUE)) == S_OK)
|
||||
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, ¶ms, outWavexU, TRUE)) == S_OK)
|
||||
return paFormatIsSupported;
|
||||
|
||||
// Log failure
|
||||
@ -3250,7 +3130,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
||||
|
||||
if (inputParameters != NULL)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wavex;
|
||||
WAVEFORMATEXTENSIBLE_UNION wavex;
|
||||
HRESULT hr;
|
||||
PaError answer;
|
||||
AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
|
||||
@ -3276,7 +3156,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
||||
if (outputParameters != NULL)
|
||||
{
|
||||
HRESULT hr;
|
||||
WAVEFORMATEXTENSIBLE wavex;
|
||||
WAVEFORMATEXTENSIBLE_UNION wavex;
|
||||
PaError answer;
|
||||
AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
|
||||
outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
|
||||
@ -3353,11 +3233,11 @@ static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerL
|
||||
if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
|
||||
{
|
||||
(*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),
|
||||
pSub->wavex.Format.nBlockAlign, pAlignFunc);
|
||||
pSub->wavexu.ext.Format.nBlockAlign, pAlignFunc);
|
||||
}
|
||||
|
||||
// Calculate period
|
||||
pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);
|
||||
pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
@ -3388,9 +3268,9 @@ static void _CalculatePeriodicity(PaWasapiSubStream *pSub, BOOL output, REFERENC
|
||||
// Align frames backwards, so device will likely make buffer read ready when we are ready
|
||||
// to read it (our scheduling will wait for amount of millisoconds of frames_per_buffer)
|
||||
alignedFrames = AlignFramesPerBuffer(pSub->params.frames_per_buffer,
|
||||
pSub->wavex.Format.nBlockAlign, ALIGN_BWD);
|
||||
pSub->wavexu.ext.Format.nBlockAlign, ALIGN_BWD);
|
||||
|
||||
userPeriodicity = MakeHnsPeriod(alignedFrames, pSub->wavex.Format.nSamplesPerSec);
|
||||
userPeriodicity = MakeHnsPeriod(alignedFrames, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
|
||||
// Must not be larger than buffer size
|
||||
if (userPeriodicity > pSub->period)
|
||||
@ -3445,7 +3325,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
}
|
||||
|
||||
// Get closest format
|
||||
if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
|
||||
if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavexu, output)) != paFormatIsSupported)
|
||||
{
|
||||
(*pa_error) = error;
|
||||
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
|
||||
@ -3453,10 +3333,10 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
}
|
||||
|
||||
// Check for Mono <<>> Stereo workaround
|
||||
if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
|
||||
if ((params->channelCount == 1) && (pSub->wavexu.ext.Format.nChannels == 2))
|
||||
{
|
||||
// select mixer
|
||||
pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavex, (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
|
||||
pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavexu.ext, (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
|
||||
if (pSub->monoMixer == NULL)
|
||||
{
|
||||
(*pa_error) = paInvalidChannelCount;
|
||||
@ -3470,7 +3350,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
(!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
|
||||
{
|
||||
framesPerLatency = _CalculateFramesPerHostBuffer(userFramesPerBuffer, params->suggestedLatency,
|
||||
pSub->wavex.Format.nSamplesPerSec);
|
||||
pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3479,16 +3359,16 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
#endif
|
||||
|
||||
// Work 1:1 with user buffer (only polling allows to use >1)
|
||||
framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
|
||||
// Force Polling if overall latency is >= 21.33ms as it allows to use 100% CPU in a callback,
|
||||
// or user specified latency parameter.
|
||||
#ifdef PA_WASAPI_FORCE_POLL_IF_LARGE_BUFFER
|
||||
overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);
|
||||
overall = MakeHnsPeriod(framesPerLatency, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
if (overall >= (106667 * 2)/*21.33ms*/)
|
||||
{
|
||||
framesPerLatency = _CalculateFramesPerHostBuffer(userFramesPerBuffer, params->suggestedLatency,
|
||||
pSub->wavex.Format.nSamplesPerSec);
|
||||
pSub->wavexu.Format.nSamplesPerSec);
|
||||
|
||||
// Use Polling interface
|
||||
pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
@ -3503,7 +3383,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
|
||||
// Avoid 0 frames
|
||||
if (framesPerLatency == 0)
|
||||
framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavexu.ext.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
|
||||
@ -3527,7 +3407,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->period = pInfo->DefaultDevicePeriod;
|
||||
|
||||
// Recalculate aligned period
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
|
||||
}
|
||||
}
|
||||
@ -3538,7 +3418,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->period = pInfo->MinimumDevicePeriod;
|
||||
|
||||
// Recalculate aligned period
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);
|
||||
}
|
||||
}
|
||||
@ -3566,7 +3446,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->period = MAX_BUFFER_EVENT_DURATION;
|
||||
|
||||
// Recalculate aligned period
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
|
||||
}
|
||||
}
|
||||
@ -3578,7 +3458,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->period = MAX_BUFFER_POLL_DURATION;
|
||||
|
||||
// Recalculate aligned period
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
|
||||
}
|
||||
}
|
||||
@ -3594,7 +3474,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->streamFlags,
|
||||
pSub->period,
|
||||
eventPeriodicity,
|
||||
&pSub->wavex.Format,
|
||||
&pSub->wavexu.ext.Format,
|
||||
NULL);
|
||||
|
||||
// [Output only] Check if buffer size is the one we requested in Exclusive mode, for UAC1 USB DACs WASAPI
|
||||
@ -3621,7 +3501,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
PRINT(("WASAPI: CreateAudioClient: detected %d times larger buffer than requested, correct to match user latency\n", ratio));
|
||||
|
||||
// Get new aligned frames lowered by calculated ratio
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period / ratio, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period / ratio, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
|
||||
|
||||
// Make sure we are not below the minimum period
|
||||
@ -3648,7 +3528,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->streamFlags,
|
||||
pSub->period,
|
||||
eventPeriodicity,
|
||||
&pSub->wavex.Format,
|
||||
&pSub->wavexu.ext.Format,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
@ -3665,7 +3545,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->period -= (100 * 10000);
|
||||
|
||||
// Recalculate aligned period
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
|
||||
framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
|
||||
_CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
|
||||
|
||||
// Release the previous allocations
|
||||
@ -3688,7 +3568,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->streamFlags,
|
||||
pSub->period,
|
||||
eventPeriodicity,
|
||||
&pSub->wavex.Format,
|
||||
&pSub->wavexu.ext.Format,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@ -3721,7 +3601,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
pSub->streamFlags,
|
||||
pSub->period,
|
||||
eventPeriodicity,
|
||||
&pSub->wavex.Format,
|
||||
&pSub->wavexu.ext.Format,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@ -3738,7 +3618,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
||||
IAudioClient_AddRef(pSub->clientParent);
|
||||
|
||||
// Recalculate buffers count
|
||||
_RecalculateBuffersCount(pSub, userFramesPerBuffer, MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec),
|
||||
_RecalculateBuffersCount(pSub, userFramesPerBuffer, MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec),
|
||||
fullDuplex, output);
|
||||
|
||||
// No error, client is successfully created
|
||||
@ -3766,7 +3646,7 @@ static PaError ActivateAudioClient(PaWasapiStream *stream, BOOL output)
|
||||
LogPaError(result);
|
||||
goto error;
|
||||
}
|
||||
LogWAVEFORMATEXTENSIBLE(&subStream->wavex);
|
||||
LogWAVEFORMATEXTENSIBLE(&subStream->wavexu.ext);
|
||||
|
||||
// Get max possible buffer size to check if it is not less than that we request
|
||||
if (FAILED(hr = IAudioClient_GetBufferSize(subStream->clientParent, &maxBufferSize)))
|
||||
@ -3799,7 +3679,7 @@ static PaError ActivateAudioClient(PaWasapiStream *stream, BOOL output)
|
||||
subStream->framesPerHostCallback : subStream->params.frames_per_buffer);
|
||||
|
||||
// Calculate buffer latency
|
||||
bufferLatency = (PaTime)maxBufferSize / subStream->wavex.Format.nSamplesPerSec;
|
||||
bufferLatency = (PaTime)maxBufferSize / subStream->wavexu.ext.Format.nSamplesPerSec;
|
||||
|
||||
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
|
||||
subStream->latencySeconds = bufferLatency;
|
||||
@ -3945,7 +3825,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
|
||||
|
||||
// Use built-in PCM converter (channel count and sample rate) if requested
|
||||
if ((GetWindowsVersion() >= WINDOWS_7_SERVER2008R2) &&
|
||||
if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
|
||||
(stream->in.shareMode == AUDCLNT_SHAREMODE_SHARED) &&
|
||||
((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiAutoConvert)))
|
||||
stream->in.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
|
||||
@ -3972,7 +3852,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
}
|
||||
|
||||
// Get closest format
|
||||
hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->in.wavex), inputSampleFormat);
|
||||
hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->in.wavexu.ext), inputSampleFormat);
|
||||
|
||||
// Set user-side custom host processor
|
||||
if ((inputStreamInfo != NULL) &&
|
||||
@ -3995,7 +3875,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
if (stream->in.params.blocking == TRUE)
|
||||
{
|
||||
UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2);
|
||||
UINT32 frameSize = stream->in.wavex.Format.nBlockAlign;
|
||||
UINT32 frameSize = stream->in.wavexu.ext.Format.nBlockAlign;
|
||||
|
||||
// buffer
|
||||
if ((stream->in.tailBuffer = PaUtil_AllocateZeroInitializedMemory(sizeof(PaUtilRingBuffer))) == NULL)
|
||||
@ -4083,7 +3963,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
|
||||
|
||||
// Use built-in PCM converter (channel count and sample rate) if requested
|
||||
if ((GetWindowsVersion() >= WINDOWS_7_SERVER2008R2) &&
|
||||
if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
|
||||
(stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) &&
|
||||
((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiAutoConvert)))
|
||||
stream->out.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
|
||||
@ -4105,7 +3985,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
}
|
||||
|
||||
// Get closest format
|
||||
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->out.wavex), outputSampleFormat);
|
||||
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->out.wavexu.ext), outputSampleFormat);
|
||||
|
||||
// Set user-side custom host processor
|
||||
if ((outputStreamInfo != NULL) &&
|
||||
@ -4836,7 +4716,7 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
|
||||
{
|
||||
UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback);
|
||||
|
||||
sleep = GetFramesSleepTime(sleep_frames, stream->in.wavex.Format.nSamplesPerSec);
|
||||
sleep = GetFramesSleepTime(sleep_frames, stream->in.wavexu.ext.Format.nSamplesPerSec);
|
||||
sleep /= 4; // wait only for 1/4 of the buffer
|
||||
|
||||
// WASAPI input provides packets, thus expiring packet will result in bad audio
|
||||
@ -4886,7 +4766,7 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
|
||||
// Save tail into buffer
|
||||
if ((frames == 0) && (available > processed))
|
||||
{
|
||||
UINT32 bytes_processed = processed * stream->in.wavex.Format.nBlockAlign;
|
||||
UINT32 bytes_processed = processed * stream->in.wavexu.ext.Format.nBlockAlign;
|
||||
UINT32 frames_to_save = available - processed;
|
||||
|
||||
PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save);
|
||||
@ -4964,7 +4844,7 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long fram
|
||||
{
|
||||
UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback);
|
||||
|
||||
sleep = GetFramesSleepTime(sleep_frames, stream->out.wavex.Format.nSamplesPerSec);
|
||||
sleep = GetFramesSleepTime(sleep_frames, stream->out.wavexu.ext.Format.nSamplesPerSec);
|
||||
sleep /= 2; // wait only for half of the buffer
|
||||
|
||||
// Avoid busy waiting, schedule next 1 millesecond wait
|
||||
@ -5102,7 +4982,7 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
|
||||
{
|
||||
PaTime pending_time;
|
||||
if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK)
|
||||
pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;
|
||||
pending_time = (PaTime)pending / (PaTime)stream->in.wavexu.ext.Format.nSamplesPerSec;
|
||||
else
|
||||
pending_time = (PaTime)stream->in.latencySeconds;
|
||||
|
||||
@ -5113,7 +4993,7 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
|
||||
{
|
||||
PaTime pending_time;
|
||||
if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK)
|
||||
pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;
|
||||
pending_time = (PaTime)pending / (PaTime)stream->out.wavexu.ext.Format.nSamplesPerSec;
|
||||
else
|
||||
pending_time = (PaTime)stream->out.latencySeconds;
|
||||
|
||||
@ -5748,7 +5628,7 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
|
||||
if (stream->out.monoMixer != NULL)
|
||||
{
|
||||
// Expand buffer
|
||||
UINT32 monoFrames = frames * (stream->out.wavex.Format.wBitsPerSample / 8);
|
||||
UINT32 monoFrames = frames * (stream->out.wavexu.ext.Format.wBitsPerSample / 8);
|
||||
if (monoFrames > stream->out.monoBufferSize)
|
||||
{
|
||||
stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = monoFrames));
|
||||
@ -5812,7 +5692,7 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
|
||||
if (stream->in.monoMixer != NULL)
|
||||
{
|
||||
// expand buffer
|
||||
UINT32 monoFrames = frames * (stream->in.wavex.Format.wBitsPerSample / 8);
|
||||
UINT32 monoFrames = frames * (stream->in.wavexu.ext.Format.wBitsPerSample / 8);
|
||||
if (monoFrames > stream->in.monoBufferSize)
|
||||
{
|
||||
stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = monoFrames));
|
||||
@ -6152,8 +6032,8 @@ static UINT32 ConfigureLoopSleepTimeAndScheduler(PaWasapiStream *stream, ThreadI
|
||||
}
|
||||
|
||||
// Calculate timeout for the next polling attempt
|
||||
sleepTimeIn = GetFramesSleepTime(userFramesIn, stream->in.wavex.Format.nSamplesPerSec);
|
||||
sleepTimeOut = GetFramesSleepTime(userFramesOut, stream->out.wavex.Format.nSamplesPerSec);
|
||||
sleepTimeIn = GetFramesSleepTime(userFramesIn, stream->in.wavexu.ext.Format.nSamplesPerSec);
|
||||
sleepTimeOut = GetFramesSleepTime(userFramesOut, stream->out.wavexu.ext.Format.nSamplesPerSec);
|
||||
|
||||
// WASAPI input packets tend to expire very easily, let's limit sleep time to 2 milliseconds
|
||||
// for all cases. Please propose better solution if any
|
||||
@ -6165,8 +6045,8 @@ static UINT32 ConfigureLoopSleepTimeAndScheduler(PaWasapiStream *stream, ThreadI
|
||||
// Make sure not 0, othervise use ThreadIdleScheduler to bounce between [0, 1] ms to avoid too busy loop
|
||||
if (sleepTime == 0)
|
||||
{
|
||||
sleepTimeIn = GetFramesSleepTimeMicroseconds(userFramesIn, stream->in.wavex.Format.nSamplesPerSec);
|
||||
sleepTimeOut = GetFramesSleepTimeMicroseconds(userFramesOut, stream->out.wavex.Format.nSamplesPerSec);
|
||||
sleepTimeIn = GetFramesSleepTimeMicroseconds(userFramesIn, stream->in.wavexu.ext.Format.nSamplesPerSec);
|
||||
sleepTimeOut = GetFramesSleepTimeMicroseconds(userFramesOut, stream->out.wavexu.ext.Format.nSamplesPerSec);
|
||||
|
||||
sleepTime = GetSleepTime(stream, sleepTimeIn, sleepTimeOut, userFramesOut);
|
||||
|
||||
@ -6476,7 +6356,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||
// convert output mono
|
||||
if (stream->out.monoMixer)
|
||||
{
|
||||
UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
|
||||
UINT32 mono_frames_size = o_processed * (stream->out.wavexu.ext.Format.wBitsPerSample / 8);
|
||||
// expand buffer
|
||||
if (mono_frames_size > stream->out.monoBufferSize)
|
||||
{
|
||||
@ -6500,7 +6380,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||
// convert input mono
|
||||
if (stream->in.monoMixer)
|
||||
{
|
||||
UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
|
||||
UINT32 mono_frames_size = i_processed * (stream->in.wavexu.ext.Format.wBitsPerSample / 8);
|
||||
// expand buffer
|
||||
if (mono_frames_size > stream->in.monoBufferSize)
|
||||
{
|
||||
|
@ -83,6 +83,7 @@ of a device for the duration of active stream using those devices
|
||||
#include "pa_ringbuffer.h"
|
||||
#include "pa_trace.h"
|
||||
#include "pa_win_waveformat.h"
|
||||
#include "pa_win_version.h"
|
||||
|
||||
#include "pa_win_wdmks.h"
|
||||
|
||||
@ -163,12 +164,17 @@ Default is to use the pin category.
|
||||
#define DYNAMIC_GUID(data) {data}
|
||||
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
|
||||
#undef DEFINE_GUID
|
||||
#ifdef DECLSPEC_SELECTANY
|
||||
#define PA_DECLSPEC_SELECTANY DECLSPEC_SELECTANY
|
||||
#else
|
||||
#define PA_DECLSPEC_SELECTANY
|
||||
#endif
|
||||
#if defined(__clang__) || (defined(_MSVC_TRADITIONAL) && !_MSVC_TRADITIONAL) /* clang-cl and new msvc preprocessor: avoid too many arguments error */
|
||||
#define DEFINE_GUID(n, ...) EXTERN_C const GUID n = {__VA_ARGS__}
|
||||
#define DEFINE_GUID(n, ...) EXTERN_C const GUID PA_DECLSPEC_SELECTANY n = {__VA_ARGS__}
|
||||
#define DEFINE_GUID_THUNK(n, ...) DEFINE_GUID(n, __VA_ARGS__)
|
||||
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
#else
|
||||
#define DEFINE_GUID(n, data) EXTERN_C const GUID n = {data}
|
||||
#define DEFINE_GUID(n, data) EXTERN_C const GUID PA_DECLSPEC_SELECTANY n = {data}
|
||||
#define DEFINE_GUID_THUNK(n, data) DEFINE_GUID(n, data)
|
||||
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
#endif /* __clang__, !_MSVC_TRADITIONAL */
|
||||
@ -649,29 +655,9 @@ static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
|
||||
|
||||
static BOOL IsEarlierThanVista()
|
||||
{
|
||||
/*
|
||||
NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
|
||||
versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
|
||||
Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
|
||||
is faster, for now we just disable the deprecation warning.
|
||||
See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
|
||||
See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
|
||||
*/
|
||||
#pragma warning (disable : 4996) /* use of GetVersionEx */
|
||||
|
||||
OSVERSIONINFO osvi;
|
||||
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
||||
if (GetVersionEx(&osvi) && osvi.dwMajorVersion<6)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
#pragma warning (default : 4996)
|
||||
return (PaWinUtil_GetOsVersion() < paOsVersionWindowsVistaServer2008);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void MemoryBarrierDummy(void)
|
||||
{
|
||||
/* Do nothing */
|
||||
|
@ -113,6 +113,7 @@
|
||||
#include "pa_win_wmme.h"
|
||||
#include "pa_win_waveformat.h"
|
||||
#include "pa_win_util.h"
|
||||
#include "pa_win_version.h"
|
||||
|
||||
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
|
||||
#include "pa_win_wdmks_utils.h"
|
||||
@ -321,6 +322,11 @@ static void PaMme_SetLastSystemError( DWORD errorCode )
|
||||
PaMme_SetLastSystemError( errorCode )
|
||||
|
||||
|
||||
static int PaMme_IsWindowsVistaOrGreater() {
|
||||
return (PaWinUtil_GetOsVersion() >= paOsVersionWindowsVistaServer2008);
|
||||
}
|
||||
|
||||
|
||||
/* PaError returning wrappers for some commonly used win32 functions
|
||||
note that we allow passing a null ptr to have no effect.
|
||||
*/
|
||||
@ -901,37 +907,22 @@ error:
|
||||
|
||||
static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
|
||||
{
|
||||
/*
|
||||
NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
|
||||
versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
|
||||
Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
|
||||
is faster, for now we just disable the deprecation warning.
|
||||
See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
|
||||
See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
|
||||
*/
|
||||
#pragma warning (disable : 4996) /* use of GetVersionEx */
|
||||
PaOsVersion version = PaWinUtil_GetOsVersion();
|
||||
|
||||
OSVERSIONINFO osvi;
|
||||
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
||||
GetVersionEx( &osvi );
|
||||
|
||||
/* Check for NT */
|
||||
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
|
||||
{
|
||||
*defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(osvi.dwMajorVersion >= 5)
|
||||
{
|
||||
*defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
|
||||
}
|
||||
else
|
||||
if(version <= paOsVersionWindows9x)
|
||||
{
|
||||
*defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(version == paOsVersionWindowsNT4)
|
||||
{
|
||||
*defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(version >= paOsVersionWindows2000)
|
||||
{
|
||||
*defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
|
||||
}
|
||||
|
||||
*defaultHighLatency = *defaultLowLatency * 2;
|
||||
|
||||
#pragma warning (default : 4996)
|
||||
}
|
||||
|
||||
|
||||
@ -1771,7 +1762,7 @@ static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionH
|
||||
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
|
||||
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
|
||||
unsigned long winMmeSpecificFlags,
|
||||
unsigned long bytesPerHostSample,
|
||||
PaSampleFormat hostSampleFormat,
|
||||
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
|
||||
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
|
||||
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
|
||||
@ -1796,14 +1787,13 @@ static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionH
|
||||
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
|
||||
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
|
||||
unsigned long winMmeSpecificFlags,
|
||||
unsigned long bytesPerHostSample,
|
||||
PaSampleFormat hostSampleFormat,
|
||||
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
|
||||
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
|
||||
{
|
||||
PaError result;
|
||||
MMRESULT mmresult;
|
||||
signed int i, j;
|
||||
PaSampleFormat sampleFormat;
|
||||
int waveFormatTag;
|
||||
|
||||
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
|
||||
@ -1832,9 +1822,7 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
|
||||
((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
|
||||
}
|
||||
|
||||
/* @todo at the moment we only use 16 bit sample format */
|
||||
sampleFormat = paInt16;
|
||||
waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
|
||||
waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( hostSampleFormat, winMmeSpecificFlags );
|
||||
|
||||
for( i = 0; i < (signed int)deviceCount; ++i )
|
||||
{
|
||||
@ -1852,14 +1840,14 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
|
||||
if this fails we fall back to WAVEFORMATEX */
|
||||
|
||||
PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
|
||||
sampleFormat, waveFormatTag, sampleRate, channelMask );
|
||||
hostSampleFormat, waveFormatTag, sampleRate, channelMask );
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* retry with WAVEFORMATEX */
|
||||
|
||||
PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
|
||||
sampleFormat, waveFormatTag, sampleRate );
|
||||
hostSampleFormat, waveFormatTag, sampleRate );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2329,6 +2317,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
|
||||
char throttleProcessingThreadOnOverload = 1;
|
||||
|
||||
/* On Windows Vista and greater, MME will accept any format that can be represented
|
||||
in WAVEFORMATEX and will internally convert if and when necessary.
|
||||
On older Windows versions, the story is less clear, so we restrict ourselves to
|
||||
Int16 for maximum compatibility.
|
||||
*/
|
||||
const PaSampleFormat kNativeFormats = PaMme_IsWindowsVistaOrGreater() ?
|
||||
paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32 : paInt16;
|
||||
|
||||
if( inputParameters )
|
||||
{
|
||||
@ -2356,7 +2351,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
if( result != paNoError ) return result;
|
||||
|
||||
hostInputSampleFormat =
|
||||
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
|
||||
PaUtil_SelectClosestAvailableFormat( kNativeFormats, inputSampleFormat );
|
||||
|
||||
if( inputDeviceCount != 1 ){
|
||||
/* always use direct speakers when using multi-device multichannel mode */
|
||||
@ -2406,7 +2401,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
if( result != paNoError ) return result;
|
||||
|
||||
hostOutputSampleFormat =
|
||||
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
|
||||
PaUtil_SelectClosestAvailableFormat( kNativeFormats, outputSampleFormat );
|
||||
|
||||
if( outputDeviceCount != 1 ){
|
||||
/* always use direct speakers when using multi-device multichannel mode */
|
||||
@ -2549,7 +2544,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
{
|
||||
result = InitializeWaveHandles( winMmeHostApi, &stream->input,
|
||||
winMmeSpecificInputFlags,
|
||||
stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
|
||||
hostInputSampleFormat, sampleRate,
|
||||
inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
@ -2558,7 +2553,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
||||
{
|
||||
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
|
||||
winMmeSpecificOutputFlags,
|
||||
stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
|
||||
hostOutputSampleFormat, sampleRate,
|
||||
outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "pa_hostapi.h"
|
||||
|
||||
PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaAudioIO_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
@ -100,6 +101,10 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
|
||||
PaMacCore_Initialize,
|
||||
#endif
|
||||
|
||||
#if PA_USE_PULSEAUDIO
|
||||
PaPulseAudio_Initialize,
|
||||
#endif
|
||||
|
||||
#if PA_USE_SKELETON
|
||||
PaSkeleton_Initialize,
|
||||
#endif
|
||||
|
242
3rdparty/portaudio/src/os/win/pa_win_version.c
vendored
Normal file
242
3rdparty/portaudio/src/os/win/pa_win_version.c
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* Win32 platform-specific support functions
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2008 Ross Bencina
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 win_src
|
||||
|
||||
@brief Portable implementation of Windows OS version getter.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "pa_win_version.h"
|
||||
#include "pa_debugprint.h"
|
||||
|
||||
// WinRT
|
||||
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
|
||||
#define PA_WINRT
|
||||
#endif
|
||||
|
||||
// Alternative way for checking Windows version, allows to check version of Windows 8.1 and up
|
||||
#ifndef PA_WINRT
|
||||
static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
|
||||
{
|
||||
typedef ULONGLONG (NTAPI *LPFN_VERSETCONDITIONMASK)(ULONGLONG ConditionMask, DWORD TypeMask, BYTE Condition);
|
||||
typedef BOOL (WINAPI *LPFN_VERIFYVERSIONINFO)(LPOSVERSIONINFOEXA lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
|
||||
|
||||
LPFN_VERSETCONDITIONMASK fnVerSetConditionMask;
|
||||
LPFN_VERIFYVERSIONINFO fnVerifyVersionInfo;
|
||||
OSVERSIONINFOEXA osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
|
||||
DWORDLONG dwlConditionMask;
|
||||
|
||||
fnVerSetConditionMask = (LPFN_VERSETCONDITIONMASK)GetProcAddress(GetModuleHandleA("kernel32"), "VerSetConditionMask");
|
||||
fnVerifyVersionInfo = (LPFN_VERIFYVERSIONINFO)GetProcAddress(GetModuleHandleA("kernel32"), "VerifyVersionInfoA");
|
||||
|
||||
if ((fnVerSetConditionMask == NULL) || (fnVerifyVersionInfo == NULL))
|
||||
return FALSE;
|
||||
|
||||
dwlConditionMask = fnVerSetConditionMask(
|
||||
fnVerSetConditionMask(
|
||||
fnVerSetConditionMask(
|
||||
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
|
||||
VER_MINORVERSION, VER_GREATER_EQUAL),
|
||||
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
|
||||
osvi.dwMajorVersion = wMajorVersion;
|
||||
osvi.dwMinorVersion = wMinorVersion;
|
||||
osvi.wServicePackMajor = wServicePackMajor;
|
||||
|
||||
return (fnVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
static PaOsVersion GetOsVersion()
|
||||
{
|
||||
PaOsVersion version = paOsVersionWindowsUnknown;
|
||||
|
||||
#ifndef PA_WINRT
|
||||
DWORD dwMajorVersion = 0xFFFFFFFFU, dwMinorVersion = 0, dwPlatformId = 0, dwBuild = 0;
|
||||
|
||||
// Can be missing in some MinGW distributions
|
||||
#ifndef NT_SUCCESS
|
||||
typedef LONG NTSTATUS;
|
||||
#endif
|
||||
|
||||
#ifndef VER_PLATFORM_WIN32_NT
|
||||
#define VER_PLATFORM_WIN32_NT 2
|
||||
#endif
|
||||
|
||||
// RTL_OSVERSIONINFOW equals OSVERSIONINFOW but it is missing inb MinGW winnt.h header,
|
||||
// thus use OSVERSIONINFOW for greater portability
|
||||
typedef NTSTATUS (WINAPI *LPFN_RTLGETVERSION)(POSVERSIONINFOW lpVersionInformation);
|
||||
LPFN_RTLGETVERSION fnRtlGetVersion;
|
||||
|
||||
#define NTSTATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
|
||||
// RtlGetVersion is able to provide true Windows version (Windows 10 may be reported as Windows 8
|
||||
// by GetVersion API) unless Windows is running app in Compatibility mode when shim is replacing
|
||||
// real Windows version (https://techcommunity.microsoft.com/t5/ask-the-performance-team/demystifying-shims-or-using-the-app-compat-toolkit-to-make-your/ba-p/374947).
|
||||
// In our case we obey Compatibility mode and do not try to bypass it.
|
||||
if ((fnRtlGetVersion = (LPFN_RTLGETVERSION)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion")) != NULL)
|
||||
{
|
||||
OSVERSIONINFOW ver = { sizeof(OSVERSIONINFOW), 0, 0, 0, 0, {0} };
|
||||
|
||||
if (fnRtlGetVersion(&ver) == NTSTATUS_SUCCESS)
|
||||
{
|
||||
dwMajorVersion = ver.dwMajorVersion;
|
||||
dwMinorVersion = ver.dwMinorVersion;
|
||||
dwPlatformId = ver.dwPlatformId;
|
||||
dwBuild = ver.dwBuildNumber;
|
||||
}
|
||||
|
||||
PA_DEBUG(("getting Windows version with RtlGetVersion(): major=%d, minor=%d, build=%d, platform=%d\n",
|
||||
dwMajorVersion, dwMinorVersion, dwBuild, dwPlatformId));
|
||||
}
|
||||
|
||||
#undef NTSTATUS_SUCCESS
|
||||
|
||||
// Fallback to GetVersion if RtlGetVersion is missing
|
||||
if (dwMajorVersion == 0xFFFFFFFFU)
|
||||
{
|
||||
typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
|
||||
LPFN_GETVERSION fnGetVersion;
|
||||
|
||||
if ((fnGetVersion = (LPFN_GETVERSION)GetProcAddress(GetModuleHandleA("kernel32"), "GetVersion")) != NULL)
|
||||
{
|
||||
DWORD dwVersion = fnGetVersion();
|
||||
|
||||
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
||||
dwPlatformId = VER_PLATFORM_WIN32_NT;
|
||||
|
||||
if (dwVersion < 0x80000000)
|
||||
dwBuild = (DWORD)(HIWORD(dwVersion));
|
||||
|
||||
PA_DEBUG(("getting Windows version with GetVersion(): major=%d, minor=%d, build=%d, platform=%d\n",
|
||||
dwMajorVersion, dwMinorVersion, dwBuild, dwPlatformId));
|
||||
}
|
||||
}
|
||||
|
||||
// Version numbers reference:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/operating-system-version
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
|
||||
if (dwMajorVersion != 0xFFFFFFFFU)
|
||||
{
|
||||
switch (dwMajorVersion)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
break; // skip lower
|
||||
case 4:
|
||||
version = (dwPlatformId == VER_PLATFORM_WIN32_NT ? paOsVersionWindowsNT4 : paOsVersionWindows9x);
|
||||
break;
|
||||
case 5:
|
||||
switch (dwMinorVersion)
|
||||
{
|
||||
case 0: version = paOsVersionWindows2000; break;
|
||||
case 1: version = paOsVersionWindowsXP; break;
|
||||
case 2: version = paOsVersionWindowsXPServer2003; break;
|
||||
default: version = paOsVersionWindowsXPServer2003; break; // shall not happen
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (dwMinorVersion)
|
||||
{
|
||||
case 0: version = paOsVersionWindowsVistaServer2008; break;
|
||||
case 1: version = paOsVersionWindows7Server2008R2; break;
|
||||
case 2: version = paOsVersionWindows8Server2012; break;
|
||||
case 3: version = paOsVersionWindows8_1Server2012R2; break;
|
||||
default: version = paOsVersionWindows8_1Server2012R2; break; // shall not happen
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
// note: Windows 11 can be detected by dwBuild >= 22000
|
||||
version = paOsVersionWindows10Server2016;
|
||||
break;
|
||||
default:
|
||||
version = paOsVersionWindowsFuture;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fallback to VerifyVersionInfo if RtlGetVersion and GetVersion are missing
|
||||
else
|
||||
{
|
||||
PA_DEBUG(("getting Windows version with VerifyVersionInfo()\n"));
|
||||
|
||||
if (IsWindowsVersionOrGreater(10, 0, 0))
|
||||
version = paOsVersionWindows10Server2016;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 3, 0))
|
||||
version = paOsVersionWindows8_1Server2012R2;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 2, 0))
|
||||
version = paOsVersionWindows8Server2012;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 1, 0))
|
||||
version = paOsVersionWindows7Server2008R2;
|
||||
else
|
||||
if (IsWindowsVersionOrGreater(6, 0, 0))
|
||||
version = paOsVersionWindowsVistaServer2008;
|
||||
else
|
||||
version = paOsVersionWindowsFuture;
|
||||
}
|
||||
#else
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10)
|
||||
version = paOsVersionWindows10Server2016;
|
||||
#else
|
||||
version = paOsVersionWindows8Server2012;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PA_DEBUG(("Windows version=%d\n", version));
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
PaOsVersion PaWinUtil_GetOsVersion()
|
||||
{
|
||||
static PaOsVersion version = paOsVersionWindowsUnknown;
|
||||
|
||||
if (version == paOsVersionWindowsUnknown)
|
||||
version = GetOsVersion();
|
||||
|
||||
return version;
|
||||
}
|
83
3rdparty/portaudio/src/os/win/pa_win_version.h
vendored
Normal file
83
3rdparty/portaudio/src/os/win/pa_win_version.h
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef PA_WIN_VERSION_H
|
||||
#define PA_WIN_VERSION_H
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* Win32 platform-specific support functions
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2008 Ross Bencina
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
Windows OS version.
|
||||
*/
|
||||
typedef enum PaOsVersion
|
||||
{
|
||||
paOsVersionWindowsUnknown = 0,
|
||||
paOsVersionWindows9x, // Windows 95, Windows 98, Windows ME
|
||||
paOsVersionWindowsNT4,
|
||||
paOsVersionWindows2000,
|
||||
paOsVersionWindowsXP,
|
||||
paOsVersionWindowsXPServer2003,
|
||||
paOsVersionWindowsVistaServer2008,
|
||||
paOsVersionWindows7Server2008R2,
|
||||
paOsVersionWindows8Server2012,
|
||||
paOsVersionWindows8_1Server2012R2,
|
||||
paOsVersionWindows10Server2016,
|
||||
// insert subsequent Windows versions below:
|
||||
// ...
|
||||
// paOsVersionWindowsFuture must be the last in the list
|
||||
paOsVersionWindowsFuture = 1000
|
||||
} PaOsVersion;
|
||||
|
||||
/**
|
||||
Get Windows OS version.
|
||||
@return OS version via PaOsVersion enum.
|
||||
@see PaOsVersion
|
||||
*/
|
||||
PaOsVersion PaWinUtil_GetOsVersion();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* PA_WIN_VERSION_H */
|
2
3rdparty/portaudio/test/CMakeLists.txt
vendored
2
3rdparty/portaudio/test/CMakeLists.txt
vendored
@ -19,6 +19,8 @@ add_test(patest_hang)
|
||||
add_test(patest_in_overflow)
|
||||
if(PA_USE_WASAPI)
|
||||
add_test(patest_jack_wasapi)
|
||||
add_test(patest_wasapi_ac3)
|
||||
add_test(patest_wasapi_eac3)
|
||||
endif()
|
||||
add_test(patest_latency)
|
||||
add_test(patest_leftright)
|
||||
|
2
3rdparty/portaudio/test/patest_latency.c
vendored
2
3rdparty/portaudio/test/patest_latency.c
vendored
@ -93,7 +93,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
int i;
|
||||
unsigned long i;
|
||||
|
||||
(void) inputBuffer; /* Prevent unused variable warning. */
|
||||
|
||||
|
206
3rdparty/portaudio/test/patest_wasapi_ac3.c
vendored
Normal file
206
3rdparty/portaudio/test/patest_wasapi_ac3.c
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
/** @file patest_wasapi_ac3.c
|
||||
@ingroup test_src
|
||||
@brief Use WASAPI-specific interface to send raw AC3 data to a S/PDIF output.
|
||||
@author Ross Bencina <rossb@audiomulch.com>, Jie Ding <gabys999@gmail.com>
|
||||
*/
|
||||
/*
|
||||
* $Id: $
|
||||
* Portable Audio I/O Library
|
||||
* WASAPI ac3 sound output test
|
||||
*
|
||||
* Copyright (c) 2009-2023 Ross Bencina, Jie Ding
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <windows.h> /* required when using pa_win_wasapi.h */
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_win_wasapi.h"
|
||||
|
||||
#define NUM_SECONDS (200)
|
||||
#define SAMPLE_RATE (48000)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#define CHANNEL_COUNT (2)
|
||||
|
||||
#define AC3_FILEPATH "./test_48k.ac3.spdif"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short *buffer;
|
||||
int bufferSampleCount;
|
||||
int playbackIndex;
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
short *out = (short*)outputBuffer;
|
||||
unsigned long i,j;
|
||||
|
||||
(void) timeInfo; /* Prevent unused variable warnings. */
|
||||
(void) statusFlags;
|
||||
(void) inputBuffer;
|
||||
|
||||
/* stream out contents of data->buffer looping at end */
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
for( j = 0; j < CHANNEL_COUNT; ++j ){
|
||||
*out++ = data->buffer[ data->playbackIndex++ ];
|
||||
|
||||
if( data->playbackIndex >= data->bufferSampleCount )
|
||||
data->playbackIndex = 0; /* loop at end of buffer */
|
||||
}
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
PaStreamParameters outputParameters = { 0 };
|
||||
PaWasapiStreamInfo wasapiStreamInfo = { 0 };
|
||||
PaStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int deviceIndex;
|
||||
FILE *fp;
|
||||
const char *fileName = AC3_FILEPATH;
|
||||
data.buffer = NULL;
|
||||
|
||||
if( argc >= 2 )
|
||||
fileName = argv[1];
|
||||
|
||||
printf( "reading spdif ac3 raw stream file %s\n", fileName );
|
||||
|
||||
fp = fopen( fileName, "rb" );
|
||||
if( !fp ){
|
||||
fprintf( stderr, "error opening spdif ac3 file.\n" );
|
||||
return -1;
|
||||
}
|
||||
/* get file size */
|
||||
fseek( fp, 0, SEEK_END );
|
||||
data.bufferSampleCount = ftell( fp ) / sizeof(short);
|
||||
fseek( fp, 0, SEEK_SET );
|
||||
|
||||
/* allocate buffer, read the whole file into memory */
|
||||
data.buffer = (short*)malloc( data.bufferSampleCount * sizeof(short) );
|
||||
if( !data.buffer ){
|
||||
fprintf( stderr, "error allocating buffer.\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
fread( data.buffer, sizeof(short), data.bufferSampleCount, fp );
|
||||
fclose( fp );
|
||||
|
||||
data.playbackIndex = 0;
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex(paWASAPI) )->defaultOutputDevice;
|
||||
if( argc >= 3 ){
|
||||
sscanf( argv[1], "%d", &deviceIndex );
|
||||
}
|
||||
|
||||
printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
|
||||
|
||||
|
||||
outputParameters.device = deviceIndex;
|
||||
outputParameters.channelCount = CHANNEL_COUNT;
|
||||
outputParameters.sampleFormat = paInt16;
|
||||
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
|
||||
outputParameters.hostApiSpecificStreamInfo = &wasapiStreamInfo;
|
||||
|
||||
wasapiStreamInfo.size = sizeof(PaWasapiStreamInfo);
|
||||
wasapiStreamInfo.hostApiType = paWASAPI;
|
||||
wasapiStreamInfo.version = 1;
|
||||
wasapiStreamInfo.flags = paWinWasapiExclusive | paWinWasapiUseChannelMask | paWinWasapiPassthrough;
|
||||
wasapiStreamInfo.channelMask = PAWIN_SPEAKER_STEREO;
|
||||
|
||||
wasapiStreamInfo.passthrough.formatId = ePassthroughFormatDolbyDigital;
|
||||
|
||||
if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported ){
|
||||
printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );
|
||||
}else{
|
||||
printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );
|
||||
}
|
||||
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
NULL, /* no input */
|
||||
&outputParameters,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0,
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
printf("Play for %d seconds.\n", NUM_SECONDS );
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
Pa_Terminate();
|
||||
free( data.buffer );
|
||||
printf("Test finished.\n");
|
||||
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
free( data.buffer );
|
||||
|
||||
fprintf( stderr, "An error occurred while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
208
3rdparty/portaudio/test/patest_wasapi_eac3.c
vendored
Normal file
208
3rdparty/portaudio/test/patest_wasapi_eac3.c
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/** @file patest_wasapi_eac3.c
|
||||
@ingroup test_src
|
||||
@brief Use WASAPI-specific interface to send raw EAC3 data to a S/PDIF output.
|
||||
@author Ross Bencina <rossb@audiomulch.com>, Jie Ding <gabys999@gmail.com>
|
||||
*/
|
||||
/*
|
||||
* $Id: $
|
||||
* Portable Audio I/O Library
|
||||
* WASAPI eac3 sound output test
|
||||
*
|
||||
* Copyright (c) 2009-2023 Ross Bencina, Jie Ding
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <windows.h> /* required when using pa_win_wasapi.h */
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_win_wasapi.h"
|
||||
|
||||
#define NUM_SECONDS (200)
|
||||
#define SAMPLE_RATE (192000)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#define CHANNEL_COUNT (2)
|
||||
|
||||
#define EAC3_FILEPATH "./test_48k.eac3.spdif"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short *buffer;
|
||||
int bufferSampleCount;
|
||||
int playbackIndex;
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
short *out = (short*)outputBuffer;
|
||||
unsigned long i,j;
|
||||
|
||||
(void) timeInfo; /* Prevent unused variable warnings. */
|
||||
(void) statusFlags;
|
||||
(void) inputBuffer;
|
||||
|
||||
/* stream out contents of data->buffer looping at end */
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
for( j = 0; j < CHANNEL_COUNT; ++j ){
|
||||
*out++ = data->buffer[ data->playbackIndex++ ];
|
||||
|
||||
if( data->playbackIndex >= data->bufferSampleCount )
|
||||
data->playbackIndex = 0; /* loop at end of buffer */
|
||||
}
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
PaStreamParameters outputParameters = { 0 };
|
||||
PaWasapiStreamInfo wasapiStreamInfo = { 0 };
|
||||
PaStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int deviceIndex;
|
||||
FILE *fp;
|
||||
const char *fileName = EAC3_FILEPATH;
|
||||
data.buffer = NULL;
|
||||
|
||||
if( argc >= 2 )
|
||||
fileName = argv[1];
|
||||
|
||||
printf( "reading spdif eac3 raw stream file %s\n", fileName );
|
||||
|
||||
fp = fopen( fileName, "rb" );
|
||||
if( !fp ){
|
||||
fprintf( stderr, "error opening spdif eac3 file.\n" );
|
||||
return -1;
|
||||
}
|
||||
/* get file size */
|
||||
fseek( fp, 0, SEEK_END );
|
||||
data.bufferSampleCount = ftell( fp ) / sizeof(short);
|
||||
fseek( fp, 0, SEEK_SET );
|
||||
|
||||
/* allocate buffer, read the whole file into memory */
|
||||
data.buffer = (short*)malloc( data.bufferSampleCount * sizeof(short) );
|
||||
if( !data.buffer ){
|
||||
fprintf( stderr, "error allocating buffer.\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
fread( data.buffer, sizeof(short), data.bufferSampleCount, fp );
|
||||
fclose( fp );
|
||||
|
||||
data.playbackIndex = 0;
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex(paWASAPI) )->defaultOutputDevice;
|
||||
if( argc >= 3 ){
|
||||
sscanf( argv[1], "%d", &deviceIndex );
|
||||
}
|
||||
|
||||
printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
|
||||
|
||||
|
||||
outputParameters.device = deviceIndex;
|
||||
outputParameters.channelCount = CHANNEL_COUNT;
|
||||
outputParameters.sampleFormat = paInt16;
|
||||
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
|
||||
outputParameters.hostApiSpecificStreamInfo = &wasapiStreamInfo;
|
||||
|
||||
wasapiStreamInfo.size = sizeof(PaWasapiStreamInfo);
|
||||
wasapiStreamInfo.hostApiType = paWASAPI;
|
||||
wasapiStreamInfo.version = 1;
|
||||
wasapiStreamInfo.flags = paWinWasapiExclusive | paWinWasapiUseChannelMask | paWinWasapiPassthrough;
|
||||
wasapiStreamInfo.channelMask = PAWIN_SPEAKER_5POINT1; /* set channel mask according to the encoded Dolby stream */
|
||||
|
||||
wasapiStreamInfo.passthrough.formatId = ePassthroughFormatDolbyDigitalPlus;
|
||||
wasapiStreamInfo.passthrough.encodedSamplesPerSec = SAMPLE_RATE / 4;
|
||||
wasapiStreamInfo.passthrough.encodedChannelCount = 6;
|
||||
|
||||
if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported ){
|
||||
printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );
|
||||
}else{
|
||||
printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );
|
||||
}
|
||||
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
NULL, /* no input */
|
||||
&outputParameters,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0,
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
printf("Play for %d seconds.\n", NUM_SECONDS );
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
Pa_Terminate();
|
||||
free( data.buffer );
|
||||
printf("Test finished.\n");
|
||||
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
free( data.buffer );
|
||||
|
||||
fprintf( stderr, "An error occurred while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
@ -758,12 +758,6 @@ to make when generating the Visual Studio project files. This stops warnings
|
||||
from being treated as errors. (MSVC seems to lack options to control which
|
||||
specific warnings are treated as error, which other compilers support.)
|
||||
|
||||
There is an as-yet unresolved issue with duplicate COM GUIDS being defined in
|
||||
the PortAudio library when the target Windows version is set to Windows Vista
|
||||
(6.0) or later. To work around this, add ``NO_USE_PORTAUDIO=1`` to the options
|
||||
passed to make when generating the Visual Studio project files. MAME will be
|
||||
built without support for sound output via PortAudio.
|
||||
|
||||
|
||||
.. _compiling-unusual:
|
||||
|
||||
|
2
makefile
2
makefile
@ -1163,7 +1163,7 @@ endif
|
||||
|
||||
.PHONY: vs2019_clang
|
||||
vs2019_clang: generate
|
||||
$(SILENT) $(GENIE) $(PARAMS) $(TARGET_PARAMS) --vs=clangcl --NO_USE_PORTAUDIO=1 vs2019
|
||||
$(SILENT) $(GENIE) $(PARAMS) $(TARGET_PARAMS) --vs=clangcl vs2019
|
||||
ifdef MSBUILD
|
||||
$(SILENT) msbuild.exe $(PROJECTDIR_WIN)/vs2019-clang/$(PROJECT_NAME).sln $(MSBUILD_PARAMS)
|
||||
endif
|
||||
|
@ -1550,6 +1550,13 @@ project "portaudio"
|
||||
"/wd4456", -- warning C4456: declaration of 'xxx' hides previous local declaration
|
||||
"/wd4312", -- warning C4312: 'type cast': conversion from 'UINT' to 'HWAVEIN' of greater size
|
||||
}
|
||||
if _OPTIONS["vs"]=="clangcl" then
|
||||
buildoptions {
|
||||
"-Wno-implicit-const-int-float-conversion",
|
||||
"-Wno-sometimes-uninitialized",
|
||||
"-Wno-unused-but-set-variable",
|
||||
}
|
||||
end
|
||||
if _OPTIONS["vs"]=="intel-15" then
|
||||
buildoptions {
|
||||
"/Qwd869", -- remark #869: parameter "xxx" was never referenced
|
||||
@ -1641,6 +1648,7 @@ project "portaudio"
|
||||
configuration { }
|
||||
files {
|
||||
MAME_DIR .. "3rdparty/portaudio/src/os/win/pa_win_util.c",
|
||||
MAME_DIR .. "3rdparty/portaudio/src/os/win/pa_win_version.c",
|
||||
MAME_DIR .. "3rdparty/portaudio/src/os/win/pa_win_waveformat.c",
|
||||
MAME_DIR .. "3rdparty/portaudio/src/os/win/pa_win_hostapis.c",
|
||||
MAME_DIR .. "3rdparty/portaudio/src/os/win/pa_win_coinitialize.c",
|
||||
|
Loading…
Reference in New Issue
Block a user