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:
invertego 2023-10-08 07:50:30 -07:00 committed by GitHub
parent 07b3bdd04f
commit 963561c7cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 5463 additions and 704 deletions

View File

@ -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()

View File

@ -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 \

View File

@ -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)

View 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)

View File

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/PortAudioCppTargets.cmake")
check_required_components(PortAudioCpp)

View 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()

View 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
# )

View 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}

View 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
)

View 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")

View File

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

View File

@ -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)

View 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

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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"
>

View File

@ -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];

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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
View 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 */

View File

@ -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;

View File

@ -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)";

View File

@ -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)

View File

@ -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 );

View File

@ -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;
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View 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 );
}

View 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

View 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 );
}

View 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

View 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

View File

@ -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, &params, sampleRate, FALSE);
MakeWaveFormatFromParams(outWavexU, &params, 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, &params, sampleRate, TRUE);
MakeWaveFormatFromParams(outWavexU, &params, 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, &params, outWavex, FALSE)) == S_OK)
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, 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, &params, outWavex, TRUE)) == S_OK)
if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, 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)
{

View File

@ -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 */

View File

@ -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;
}

View File

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

View 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;
}

View 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 */

View File

@ -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)

View File

@ -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. */

View 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;
}

View 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;
}

View File

@ -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:

View File

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

View File

@ -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",