Merge pull request #185 from jleben/fix-alsa-get-device-count-error
authorgaryscavone <garyscavone@users.noreply.github.com>
Wed, 13 Feb 2019 15:34:30 +0000 (10:34 -0500)
committerGitHub <noreply@github.com>
Wed, 13 Feb 2019 15:34:30 +0000 (10:34 -0500)
Alsa API: Fix use of invalid card handle

31 files changed:
.gitattributes [new file with mode: 0644]
.gitignore
.travis.yml [new file with mode: 0644]
CMakeLists.txt
ChangeLog [new file with mode: 0644]
Makefile.am
README.md [new file with mode: 0644]
RtAudio.cpp
RtAudio.h
cmake/RtAudioConfigUninstall.cmake.in [new file with mode: 0644]
configure.ac
contrib/go/rtaudio/rtaudio.go [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_stub.cpp [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_stub.h [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_test.go [new file with mode: 0644]
doc/doxygen/compiling.txt
doc/doxygen/probe.txt
include/FunctionDiscoveryKeys_devpkey.h [deleted file]
include/asio.h
include/asiosys.h
include/functiondiscoverykeys_devpkey.h [new file with mode: 0644]
include/ginclude.h
install.txt
readme [deleted file]
rtaudio.pc.in
rtaudio_c.cpp [new file with mode: 0644]
rtaudio_c.h [new file with mode: 0644]
tests/CMakeLists.txt
tests/Makefile.am
tests/apinames.cpp [new file with mode: 0644]
tests/audioprobe.cpp

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..f49e821
--- /dev/null
@@ -0,0 +1,4 @@
+# make sure that .gitignore, .travis.yml,... are not part of a
+# source-package generated via 'git archive'
+.git*          export-ignore
+/.*            export-ignore
index a93909f7b257b9fbacae5382c59737a22c43b9fd..13550deb40534a116ed62856a4d0180ef9274932 100644 (file)
@@ -1,5 +1,6 @@
 Makefile
 *.o
+build
 /config
 /configure
 /Makefile.in
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..838f834
--- /dev/null
@@ -0,0 +1,77 @@
+language: cpp
+sudo: false  # docker VM
+matrix:
+  include:
+  - os: linux
+    env: HOST="" API="alsa"
+    compiler: gcc
+  - os: linux
+    env: HOST="" API="alsa"
+    compiler: clang
+  - os: linux
+    env: HOST="" API="jack"
+    compiler: gcc
+  - os: linux
+    env: HOST="" API="jack"
+    compiler: clang
+  - os: linux
+    env: HOST="" API="pulse"
+    compiler: gcc
+  - os: linux
+    env: HOST="" API="pulse"
+    compiler: clang
+  - os: linux
+    env: HOST="" API="oss"
+    compiler: gcc
+  - os: linux
+    env: HOST="" API="oss"
+    compiler: clang
+  - os: linux
+    env: HOST="--host=i686-w64-mingw32" API="dsound" CPPFLAGS="-Wno-unused-function"
+    compiler: gcc
+  - os: linux
+    env: HOST="--host=x86_64-w64-mingw32" API="dsound" CPPFLAGS="-Wno-unused-function"
+    compiler: gcc
+  - os: linux
+    env: HOST="--host=i686-w64-mingw32" API="asio" CPPFLAGS="-Wno-unused-function -Wno-unused-but-set-variable"
+    compiler: gcc
+  - os: linux
+    env: HOST="--host=x86_64-w64-mingw32" API="asio" CPPFLAGS="-Wno-unused-function -Wno-unused-but-set-variable"
+    compiler: gcc
+  - os: linux
+    env: HOST="--host=i686-w64-mingw32" API="wasapi" CPPFLAGS="-Wno-unused-function"
+    compiler: gcc
+  - os: linux
+    env: HOST="--host=x86_64-w64-mingw32" API="wasapi" CPPFLAGS="-Wno-unused-function"
+    compiler: gcc
+  # jack and asound not found on ARM gnueabihf
+  # - os: linux
+  #   env: HOST="--host=arm-linux-gnueabihf" API="alsa"
+  #   compiler: gcc
+  # - os: linux
+  #   env: HOST="--host=arm-linux-gnueabihf" API="jack"
+  #   compiler: gcc
+  - os: osx
+    env: HOST="" API="core"
+    compiler: gcc
+  - os: osx
+    env: HOST="" API="core"
+    compiler: clang
+install:
+- if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get install libasound2-dev libjack-dev libpulse-dev doxygen g++-mingw-w64-i686 g++-mingw-w64-x86-64 g++-arm-linux-gnueabihf oss4-dev; fi
+- if [ $TRAVIS_OS_NAME = osx ]; then brew install doxygen || (brew update && brew install doxygen); fi
+- if [ -n "$HOST" ]; then unset CXX; unset CC; fi
+script: ./autogen.sh --enable-debug --with-$API $HOST && make
+after_script:
+- make check
+- make distcheck
+- make install
+# ALSA: no access to /dev/snd/seq
+# JACK: Jack server not running
+# - tests/midiprobe
+notifications:
+  email:
+    recipients:
+      - radarsat1@gmail.com
+    on_success: never
+    on_failure: change
index 812d4d307666e7fa9ab37ccc6de609f19d36e6d5..89b537279f668cbdc24c71ab211666087f8b6666 100644 (file)
-cmake_minimum_required(VERSION 2.8.10)
-project(RtAudio)
+# Set minimum CMake required version for this project.
+cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
 
-include(CTest)
-include(CheckFunctionExists)
+# Define a C++ project.
+project(RtAudio LANGUAGES CXX)
+
+# Check for Jack (any OS)
+find_library(JACK_LIB jack)
+find_package(PkgConfig)
+pkg_check_modules(jack jack)
+if(JACK_LIB OR jack_FOUND)
+  set(HAVE_JACK TRUE)
+endif()
+
+# Check for Pulse (any OS)
+pkg_check_modules(pulse libpulse-simple)
+
+# Check for known non-Linux unix-likes
+if (CMAKE_SYSTEM_NAME MATCHES "kNetBSD.*|NetBSD.*")
+  message(STATUS "NetBSD detected, using OSS")
+  set(xBSD ON)
+elseif(UNIX AND NOT APPLE)
+  set(LINUX ON)
+endif()
+
+# Necessary for Windows
+if(WIN32)
+  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+  set(CMAKE_DEBUG_POSTFIX "d")
+endif()
 
+# Build Options
+option(BUILD_SHARED_LIBS "Compile library shared lib." TRUE)
+option(BUILD_STATIC_LIBS "Compile library static lib." TRUE)
+option(BUILD_TESTING "Compile test programs." TRUE)
 option(BUILD_PYTHON "Build PyRtAudio python bindings" OFF)
-option(AUDIO_WINDOWS_DS "Build Windows DirectSound API" OFF)
-option(AUDIO_WINDOWS_ASIO "Build Windows ASIO API" OFF)
-option(AUDIO_WINDOWS_WASAPI "Build Windows WASAPI API" OFF)
-option(AUDIO_LINUX_OSS "Build Linux OSS API" OFF)
-option(AUDIO_LINUX_ALSA "Build Linux ALSA API" OFF)
-option(AUDIO_LINUX_PULSE "Build Linux PulseAudio API" OFF)
-option(AUDIO_UNIX_JACK "Build Unix JACK audio server API" OFF)
-option(AUDIO_OSX_CORE "Build Mac OSX CoreAudio API" OFF)
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (Release,Debug)")
 
-if (CMAKE_BUILD_TYPE STREQUAL "Debug")
-    add_definitions(-D__RTAUDIO_DEBUG__)
-endif ()
+# API Options
+option(RTAUDIO_API_DS "Build DirectSound API" OFF)
+option(RTAUDIO_API_ASIO "Build ASIO API" OFF)
+option(RTAUDIO_API_WASAPI "Build WASAPI API" ${WIN32})
+option(RTAUDIO_API_OSS "Build OSS4 API" ${xBSD})
+option(RTAUDIO_API_ALSA "Build ALSA API" ${LINUX})
+option(RTAUDIO_API_PULSE "Build PulseAudio API" ${pulse_FOUND})
+option(RTAUDIO_API_JACK "Build JACK audio server API" ${HAVE_JACK})
+option(RTAUDIO_API_CORE "Build CoreAudio API" ${APPLE})
 
+# Check for functions
+include(CheckFunctionExists)
 check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
-
 if (HAVE_GETTIMEOFDAY)
     add_definitions(-DHAVE_GETTIMEOFDAY)
 endif ()
 
+# Add -Wall if possible
 if (CMAKE_COMPILER_IS_GNUCXX)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
 endif (CMAKE_COMPILER_IS_GNUCXX)
 
-set(rtaudio_SOURCES RtAudio.cpp)
+# Add debug flags
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+  add_definitions(-D__RTAUDIO_DEBUG__)
+  if (CMAKE_COMPILER_IS_GNUCXX)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+  endif (CMAKE_COMPILER_IS_GNUCXX)
+endif ()
 
+# Read libtool version info from configure.ac
+set(R "m4_define\\(\\[lt_([a-z]+)\\], ([0-9]+)\\)")
+file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" CONFIGAC
+  REGEX ${R})
+foreach(_S ${CONFIGAC})
+  string(REGEX REPLACE ${R} "\\1" k ${_S})
+  string(REGEX REPLACE ${R} "\\2" v ${_S})
+  set(SO_${k} ${v})
+endforeach()
+math(EXPR SO_current_minus_age "${SO_current} - ${SO_age}")
+set(SO_VER "${SO_current_minus_age}")
+set(FULL_VER "${SO_current_minus_age}.${SO_revision}.${SO_age}")
+
+# Read package version info from configure.ac
+set(R "AC_INIT\\(RtAudio, ([0-9\\.]+),.*\\)")
+file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" CONFIGAC
+  REGEX ${R})
+string(REGEX REPLACE ${R} "\\1" PACKAGE_VERSION ${CONFIGAC})
+
+# Init variables
+set(rtaudio_SOURCES RtAudio.cpp RtAudio.h rtaudio_c.cpp rtaudio_c.h)
 set(LINKLIBS)
-if (CMAKE_SYSTEM_NAME MATCHES "kNetBSD.*|NetBSD.*")
-    message(STATUS "NetBSD detected, using OSS")
-    find_package(Threads REQUIRED CMAKE_THREAD_PREFER_PTHREAD)
-    list(APPEND LINKLIBS ossaudio ${CMAKE_THREAD_LIBS_INIT})
-    set(AUDIO_LINUX_OSS ON)
-elseif (UNIX AND NOT APPLE)
-    if (NOT AUDIO_LINUX_PULSE AND NOT AUDIO_LINUX_ALSA AND NOT AUDIO_LINUX_OSS AND NOT AUDIO_UNIX_JACK)
-        set(AUDIO_LINUX_ALSA ON)
-    endif()
-
-    if (AUDIO_LINUX_PULSE)
-        find_library(PULSE_LIB pulse)
-        find_library(PULSESIMPLE_LIB pulse-simple)
-        find_package(Threads REQUIRED CMAKE_THREAD_PREFER_PTHREAD)
-        list(APPEND LINKLIBS ${PULSE_LIB} ${PULSESIMPLE_LIB} ${CMAKE_THREAD_LIBS_INIT})
-        add_definitions(-D__LINUX_PULSE__)
-        message(STATUS "Using Linux PulseAudio")
-    endif (AUDIO_LINUX_PULSE)
-    if (AUDIO_LINUX_ALSA)
-        find_package(ALSA)
-        find_package(Threads REQUIRED CMAKE_THREAD_PREFER_PTHREAD)
-        if (NOT ALSA_FOUND)
-            message(FATAL_ERROR "ALSA API requested but no ALSA dev libraries found")
-        endif()
-        include_directories(${ALSA_INCLUDE_DIR})
-        list(APPEND LINKLIBS ${ALSA_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
-        add_definitions(-D__LINUX_ALSA__)
-        message(STATUS "Using Linux ALSA")
-    endif (AUDIO_LINUX_ALSA)
-endif ()
+set(PKGCONFIG_REQUIRES)
+set(API_DEFS)
+set(API_LIST)
+
+# Tweak API-specific configuration.
+
+# Jack
+if (RTAUDIO_API_JACK AND jack_FOUND)
+  set(NEED_PTHREAD ON)
+  list(APPEND PKGCONFIG_REQUIRES "jack")
+  list(APPEND API_DEFS "-D__UNIX_JACK__")
+  list(APPEND API_LIST "jack")
+  if(jack_FOUND)
+    list(APPEND LINKLIBS ${jack_LIBRARIES})
+    list(APPEND INCDIRS ${jack_INCLUDEDIR})
+  else()
+    list(APPEND LINKLIBS ${JACK_LIB})
+  endif()
+endif()
+
+# ALSA
+if (RTAUDIO_API_ALSA)
+  set(NEED_PTHREAD ON)
+  find_package(ALSA)
+  if (NOT ALSA_FOUND)
+    message(FATAL_ERROR "ALSA API requested but no ALSA dev libraries found")
+  endif()
+  list(APPEND INCDIRS ${ALSA_INCLUDE_DIR})
+  list(APPEND LINKLIBS ${ALSA_LIBRARY})
+  list(APPEND PKGCONFIG_REQUIRES "alsa")
+  list(APPEND API_DEFS "-D__LINUX_ALSA__")
+  list(APPEND API_LIST "alsa")
+endif()
+
+# OSS
+if (RTAUDIO_OSS)
+  set(NEED_PTHREAD ON)
+  find_library(OSSAUDIO_LIB ossaudio)
+  if (OSSAUDIO_LIB)
+    list(APPEND LINKLIBS ossaudio)
+    # Note: not an error on some systems
+  endif()
+  list(APPEND API_DEFS "-D__LINUX_OSS__")
+  list(APPEND API_LIST "oss")
+endif()
+
+# Pulse
+if (RTAUDIO_API_PULSE)
+  set(NEED_PTHREAD ON)
+  find_library(PULSE_LIB pulse)
+  find_library(PULSESIMPLE_LIB pulse-simple)
+  list(APPEND LINKLIBS ${PULSE_LIB} ${PULSESIMPLE_LIB})
+  list(APPEND PKGCONFIG_REQUIRES "libpulse-simple")
+  list(APPEND API_DEFS "-D__LINUX_PULSE__")
+  list(APPEND API_LIST "pulse")
+endif()
+
+# CoreAudio
+if (RTAUDIO_API_CORE)
+  find_library(COREAUDIO_LIB CoreAudio)
+  find_library(COREFOUNDATION_LIB CoreFoundation)
+  list(APPEND LINKLIBS ${COREAUDIO_LIB} ${COREFOUNDATION_LIB})
+  list(APPEND API_DEFS "-D__MACOSX_CORE__")
+  list(APPEND API_LIST "core")
+endif()
+
+# ASIO
+if (RTAUDIO_API_ASIO)
+  set(NEED_WIN32LIBS ON)
+  include_directories(include)
+  list(APPEND rtaudio_SOURCES
+    include/asio.cpp
+    include/asiodrivers.cpp
+    include/asiolist.cpp
+    include/iasiothiscallresolver.cpp)
+  list(APPEND API_DEFS "-D__WINDOWS_ASIO__")
+  list(APPEND API_LIST "asio")
+endif()
 
-if (APPLE)
-    if (NOT AUDIO_OSX_CORE AND NOT AUDIO_UNIX_JACK)
-        set(AUDIO_OSX_CORE ON)
-    endif()
-
-    if (AUDIO_OSX_CORE)
-        find_library(COREAUDIO_LIB CoreAudio)
-        find_library(COREFOUNDATION_LIB CoreFoundation)
-        list(APPEND LINKLIBS ${COREAUDIO_LIB} ${COREFOUNDATION_LIB})
-        add_definitions(-D__MACOSX_CORE__)
-        message(STATUS "Using OSX CoreAudio")
-    endif (AUDIO_OSX_CORE)
-endif (APPLE)
-
-# JACK supported on many Unices
-if (UNIX)
-    if (AUDIO_UNIX_JACK)
-        find_library(JACK_LIB jack)
-        list(APPEND LINKLIBS ${JACK_LIB})
-        add_definitions(-D__UNIX_JACK__)
-        message(STATUS "Using JACK")
-    endif (AUDIO_UNIX_JACK)
-endif (UNIX)
-
-if (WIN32)
-    if (NOT AUDIO_WINDOWS_DS AND NOT AUDIO_WINDOWS_ASIO AND NOT AUDIO_WINDOWS_WASAPI)
-        set(AUDIO_WINDOWS_WASAPI ON)
-    endif()
-
-    include_directories(include)
-    list(APPEND LINKLIBS winmm ole32)
-
-    if (AUDIO_WINDOWS_DS)
-        add_definitions(-D__WINDOWS_DS__)
-        message(STATUS "Using Windows DirectSound")
-        list(APPEND LINKLIBS dsound)
-    endif (AUDIO_WINDOWS_DS)
-    if (AUDIO_WINDOWS_WASAPI)
-        add_definitions(-D__WINDOWS_WASAPI__)
-        message(STATUS "Using Windows WASAPI")
-        list(APPEND LINKLIBS uuid ksuser)
-    endif (AUDIO_WINDOWS_WASAPI)
-    if (AUDIO_WINDOWS_ASIO)
-        list(APPEND rtaudio_SOURCES
-            include/asio.cpp
-            include/asiodrivers.cpp
-            include/asiolist.cpp
-            include/iasiothiscallresolver.cpp)
-        add_definitions(-D__WINDOWS_ASIO__)
-        message(STATUS "Using Windows ASIO")
-    endif (AUDIO_WINDOWS_ASIO)
-endif (WIN32)
+# DSound
+if (RTAUDIO_API_DS)
+  set(NEED_WIN32LIBS ON)
+  list(APPEND LINKLIBS dsound)
+  list(APPEND API_DEFS "-D__WINDOWS_DS__")
+  list(APPEND API_LIST "ds")
+endif()
 
+# WASAPI
+if (RTAUDIO_API_WASAPI)
+  include_directories(include)
+  set(NEED_WIN32LIBS ON)
+  list(APPEND LINKLIBS ksuser mfplat mfuuid wmcodecdspuuid)
+  list(APPEND API_DEFS "-D__WINDOWS_WASAPI__")
+  list(APPEND API_LIST "wasapi")
+endif()
+
+# Windows libs
+if (NEED_WIN32LIBS)
+  list(APPEND LINKLIBS winmm ole32)
+endif()
+
+# pthread
+if (NEED_PTHREAD)
+  find_package(Threads REQUIRED
+    CMAKE_THREAD_PREFER_PTHREAD
+    THREADS_PREFER_PTHREAD_FLAG)
+  list(APPEND LINKLIBS Threads::Threads)
+endif()
+
+# Create library targets.
 cmake_policy(SET CMP0042 OLD)
-add_library(rtaudio SHARED ${rtaudio_SOURCES})
-add_library(rtaudio_static STATIC ${rtaudio_SOURCES})
+set(LIB_TARGETS)
+if(BUILD_SHARED_LIBS)
+  add_library(rtaudio SHARED ${rtaudio_SOURCES})
+  list(APPEND LIB_TARGETS rtaudio)
+
+  # Add headers destination for install rule.
+  set_target_properties(rtaudio PROPERTIES PUBLIC_HEADER RtAudio.h
+    SOVERSION ${SO_VER}
+    VERSION ${FULL_VER})
+
+  # Set include paths, populate target interface.
+  target_include_directories(rtaudio PRIVATE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    ${INCDIRS})
+
+  # Set compile-time definitions
+  target_compile_definitions(rtaudio PRIVATE ${API_DEFS})
+  target_compile_definitions(rtaudio PRIVATE RTAUDIO_EXPORT)
+
+  target_link_libraries(rtaudio ${LINKLIBS})
+endif()
+
+if(BUILD_STATIC_LIBS)
+  add_library(rtaudio_static STATIC ${rtaudio_SOURCES})
+  list(APPEND LIB_TARGETS rtaudio_static)
+
+  # Add headers destination for install rule.
+  set_target_properties(rtaudio_static PROPERTIES PUBLIC_HEADER RtAudio.h
+    SOVERSION ${SO_VER}
+    VERSION ${FULL_VER})
+
+  # Set include paths, populate target interface.
+  target_include_directories(rtaudio_static PRIVATE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    ${INCDIRS})
+
+  # Set compile-time definitions
+  target_compile_definitions(rtaudio_static PRIVATE ${API_DEFS})
 
-target_link_libraries(rtaudio ${LINKLIBS})
+  target_link_libraries(rtaudio_static ${LINKLIBS})
+endif()
 
+# Set standard installation directories.
+include(GNUInstallDirs)
+
+# Subdirs
 if (BUILD_TESTING)
-    add_subdirectory(tests)
+  include(CTest)
+  add_subdirectory(tests)
 endif (BUILD_TESTING)
 
-install(TARGETS rtaudio
-      LIBRARY DESTINATION lib
-      ARCHIVE DESTINATION lib
-      RUNTIME DESTINATION bin)
+# Message
+string(REPLACE ";" " " apilist "${API_LIST}")
+message(STATUS "Compiling with support for: ${apilist}")
 
-install(
-    FILES RtAudio.h
-    DESTINATION include)
+# PkgConfig file
+string(REPLACE ";" " " req "${PKGCONFIG_REQUIRES}")
+string(REPLACE ";" " " api "${API_DEFS}")
+set(prefix ${CMAKE_INSTALL_PREFIX})
+configure_file("rtaudio.pc.in" "rtaudio.pc" @ONLY)
+
+# Add install rule.
+install(TARGETS ${LIB_TARGETS}
+        EXPORT RtAudioTargets
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+# Store the package in the user registry.
+export(PACKAGE RtAudio)
+
+# Set installation path for CMake files.
+if(WIN32)
+    set(RTAUDIO_CMAKE_DESTINATION cmake)
+else()
+    set(RTAUDIO_CMAKE_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/RtAudio)
+endif()
+
+# Create CMake configuration export file.
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/RtAudioConfig.cmake "include(\${CMAKE_CURRENT_LIST_DIR}/RtAudioTargets.cmake)")
+
+# Install CMake configuration export file.
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/RtAudioConfig.cmake
+        DESTINATION ${RTAUDIO_CMAKE_DESTINATION})
+
+# Export library target (build-tree).
+export(EXPORT RtAudioTargets
+       NAMESPACE RtAudio::)
+
+# Export library target (install-tree).
+install(EXPORT RtAudioTargets
+        DESTINATION ${RTAUDIO_CMAKE_DESTINATION}
+        NAMESPACE RtAudio::)
+
+# Configure uninstall target.
+configure_file(
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/RtAudioConfigUninstall.cmake.in"
+    "${CMAKE_CURRENT_BINARY_DIR}/RtAudioConfigUninstall.cmake" @ONLY)
+
+# Create uninstall target.
+add_custom_target(uninstall
+    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/RtAudioConfigUninstall.cmake)
 
 install(
-    FILES rtaudio.pc
-    DESTINATION lib/pkgconfig)
+    FILES ${CMAKE_CURRENT_BINARY_DIR}/rtaudio.pc
+    DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..6282a76
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,10 @@
+30.04.2018
+      * Additions and fixes for realtime operation: (Tim E. Real of MusE)
+        - Added realtime operation to Pulse driver.
+        - Fixed ALSA realtime support. Attributes are once again all set
+           in probeDeviceOpen().
+        - Fixed OSS realtime support. Same mods as done to ALSA driver.
+          OSS untested, but should work, it's the same code.
+        - A diagnostic message (streamed to cerr) in each of the callback
+           handlers informs the user if realtime is really running.
+
index 00927e8b17068784db2035b2620bf511f15bd312..718873c4f031ff452c077fea33438347653a6766 100644 (file)
@@ -3,14 +3,30 @@ if MAKE_DOC
 SUBDIRS += doc
 endif
 
+AM_CXXFLAGS = @visibility@
+
 lib_LTLIBRARIES = %D%/librtaudio.la
+%C%_librtaudio_la_CXXFLAGS = -DRTAUDIO_EXPORT
 %C%_librtaudio_la_LDFLAGS = -no-undefined -export-dynamic -version-info @SO_VERSION@
 %C%_librtaudio_la_SOURCES = \
-  %D%/RtAudio.cpp
+  %D%/RtAudio.cpp \
+  %D%/rtaudio_c.cpp
+
+if ASIO
+%C%_librtaudio_la_SOURCES += \
+       include/asio.cpp \
+       include/asiodrivers.cpp \
+       include/asiolist.cpp \
+       include/iasiothiscallresolver.cpp
+
+# due to warning in asiolist.cpp
+%C%_librtaudio_la_CXXFLAGS += -Wno-error=unused-but-set-variable
+endif
 
 rtaudio_incdir = $(includedir)/rtaudio
 rtaudio_inc_HEADERS = \
-  %D%/RtAudio.h
+  %D%/RtAudio.h \
+  %D%/rtaudio_c.h
 
 pkgconfigdatadir = $(libdir)/pkgconfig
 pkgconfigdata_DATA = rtaudio.pc
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..536c661
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+# RtAudio
+
+[![Build Status](https://travis-ci.org/thestk/rtaudio.svg?branch=master)](https://travis-ci.org/thestk/rtaudio)
+
+A set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO and WASAPI) operating systems.
+
+By Gary P. Scavone, 2001-2017 (and many other developers!)
+
+This distribution of RtAudio contains the following:
+
+- doc:      RtAudio documentation (see doc/html/index.html)
+- tests:    example RtAudio programs
+- include:  header and source files necessary for ASIO, DS & OSS compilation
+- tests/Windows: Visual C++ .net test program workspace and projects
+
+## Overview
+
+RtAudio is a set of C++ classes that provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X and Windows (DirectSound, ASIO and WASAPI) operating systems.  RtAudio significantly simplifies the process of interacting with computer audio hardware.  It was designed with the following objectives:
+
+  - object-oriented C++ design
+  - simple, common API across all supported platforms
+  - only one source and one header file for easy inclusion in programming projects
+  - allow simultaneous multi-api support
+  - support dynamic connection of devices
+  - provide extensive audio device parameter control
+  - allow audio device capability probing
+  - automatic internal conversion for data format, channel number compensation, (de)interleaving, and byte-swapping
+
+RtAudio incorporates the concept of audio streams, which represent audio output (playback) and/or input (recording).  Available audio devices and their capabilities can be enumerated and then specified when opening a stream.  Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance.  See the \ref apinotes section for information specific to each of the supported audio APIs.
+
+## Further Reading
+
+For complete documentation on RtAudio, see the doc directory of the distribution or surf to http://www.music.mcgill.ca/~gary/rtaudio/.
+
+
+## Legal and ethical:
+
+The RtAudio license is similar to the MIT License.
+
+    RtAudio: a set of realtime audio i/o C++ classes
+    Copyright (c) 2001-2017 Gary P. Scavone
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    Any person wishing to distribute modifications to the Software is
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
+
+    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.
index 8c119a3462700235695b520f2a7a6ab5eb228c76..9c7f1546e56e4fafb1d9bc193159242caf1cd269 100644 (file)
@@ -1,4 +1,4 @@
-/************************************************************************/
+/************************************************************************/
 /*! \class RtAudio
     \brief Realtime audio i/o C++ classes.
 
@@ -98,39 +98,95 @@ std::string RtAudio :: getVersion( void )
   return RTAUDIO_VERSION;
 }
 
-void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis )
-{
-  apis.clear();
+// Define API names and display names.
+// Must be in same order as API enum.
+extern "C" {
+const char* rtaudio_api_names[][2] = {
+  { "unspecified" , "Unknown" },
+  { "alsa"        , "ALSA" },
+  { "pulse"       , "Pulse" },
+  { "oss"         , "OpenSoundSystem" },
+  { "jack"        , "Jack" },
+  { "core"        , "CoreAudio" },
+  { "wasapi"      , "WASAPI" },
+  { "asio"        , "ASIO" },
+  { "ds"          , "DirectSound" },
+  { "dummy"       , "Dummy" },
+};
+const unsigned int rtaudio_num_api_names = 
+  sizeof(rtaudio_api_names)/sizeof(rtaudio_api_names[0]);
 
-  // The order here will control the order of RtAudio's API search in
-  // the constructor.
+// The order here will control the order of RtAudio's API search in
+// the constructor.
+extern "C" const RtAudio::Api rtaudio_compiled_apis[] = {
 #if defined(__UNIX_JACK__)
-  apis.push_back( UNIX_JACK );
-#endif
-#if defined(__LINUX_ALSA__)
-  apis.push_back( LINUX_ALSA );
+  RtAudio::UNIX_JACK,
 #endif
 #if defined(__LINUX_PULSE__)
-  apis.push_back( LINUX_PULSE );
+  RtAudio::LINUX_PULSE,
+#endif
+#if defined(__LINUX_ALSA__)
+  RtAudio::LINUX_ALSA,
 #endif
 #if defined(__LINUX_OSS__)
-  apis.push_back( LINUX_OSS );
+  RtAudio::LINUX_OSS,
 #endif
 #if defined(__WINDOWS_ASIO__)
-  apis.push_back( WINDOWS_ASIO );
+  RtAudio::WINDOWS_ASIO,
 #endif
 #if defined(__WINDOWS_WASAPI__)
-  apis.push_back( WINDOWS_WASAPI );
+  RtAudio::WINDOWS_WASAPI,
 #endif
 #if defined(__WINDOWS_DS__)
-  apis.push_back( WINDOWS_DS );
+  RtAudio::WINDOWS_DS,
 #endif
 #if defined(__MACOSX_CORE__)
-  apis.push_back( MACOSX_CORE );
+  RtAudio::MACOSX_CORE,
 #endif
 #if defined(__RTAUDIO_DUMMY__)
-  apis.push_back( RTAUDIO_DUMMY );
+  RtAudio::RTAUDIO_DUMMY,
 #endif
+  RtAudio::UNSPECIFIED,
+};
+extern "C" const unsigned int rtaudio_num_compiled_apis =
+  sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1;
+}
+
+// This is a compile-time check that rtaudio_num_api_names == RtAudio::NUM_APIS.
+// If the build breaks here, check that they match.
+template<bool b> class StaticAssert { private: StaticAssert() {} };
+template<> class StaticAssert<true>{ public: StaticAssert() {} };
+class StaticAssertions { StaticAssertions() {
+  StaticAssert<rtaudio_num_api_names == RtAudio::NUM_APIS>();
+}};
+
+void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis )
+{
+  apis = std::vector<RtAudio::Api>(rtaudio_compiled_apis,
+                                   rtaudio_compiled_apis + rtaudio_num_compiled_apis);
+}
+
+std::string RtAudio :: getApiName( RtAudio::Api api )
+{
+  if (api < 0 || api >= RtAudio::NUM_APIS)
+    return "";
+  return rtaudio_api_names[api][0];
+}
+
+std::string RtAudio :: getApiDisplayName( RtAudio::Api api )
+{
+  if (api < 0 || api >= RtAudio::NUM_APIS)
+    return "Unknown";
+  return rtaudio_api_names[api][1];
+}
+
+RtAudio::Api RtAudio :: getCompiledApiByName( const std::string &name )
+{
+  unsigned int i=0;
+  for (i = 0; i < rtaudio_num_compiled_apis; ++i)
+    if (name == rtaudio_api_names[rtaudio_compiled_apis[i]][0])
+      return rtaudio_compiled_apis[i];
+  return RtAudio::UNSPECIFIED;
 }
 
 void RtAudio :: openRtApi( RtAudio::Api api )
@@ -1485,6 +1541,10 @@ void RtApiCore :: startStream( void )
     return;
   }
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   OSStatus result = noErr;
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -1973,7 +2033,7 @@ unsigned int RtApiJack :: getDeviceCount( void )
   const char **ports;
   std::string port, previousPort;
   unsigned int nChannels = 0, nDevices = 0;
-  ports = jack_get_ports( client, NULL, NULL, 0 );
+  ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
     size_t iColon = 0;
@@ -2012,7 +2072,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   const char **ports;
   std::string port, previousPort;
   unsigned int nPorts = 0, nDevices = 0;
-  ports = jack_get_ports( client, NULL, NULL, 0 );
+  ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
     size_t iColon = 0;
@@ -2047,7 +2107,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   // Count the available ports containing the client name as device
   // channels.  Jack "input ports" equal RtAudio output channels.
   unsigned int nChannels = 0;
-  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
+  ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput );
   if ( ports ) {
     while ( ports[ nChannels ] ) nChannels++;
     free( ports );
@@ -2056,7 +2116,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
 
   // Jack "output ports" equal RtAudio input channels.
   nChannels = 0;
-  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
+  ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
   if ( ports ) {
     while ( ports[ nChannels ] ) nChannels++;
     free( ports );
@@ -2130,7 +2190,7 @@ static void jackShutdown( void *infoPointer )
 
 static int jackXrun( void *infoPointer )
 {
-  JackHandle *handle = (JackHandle *) infoPointer;
+  JackHandle *handle = *((JackHandle **) infoPointer);
 
   if ( handle->ports[0] ) handle->xrun[0] = true;
   if ( handle->ports[1] ) handle->xrun[1] = true;
@@ -2168,7 +2228,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   const char **ports;
   std::string port, previousPort, deviceName;
   unsigned int nPorts = 0, nDevices = 0;
-  ports = jack_get_ports( client, NULL, NULL, 0 );
+  ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
     size_t iColon = 0;
@@ -2192,22 +2252,24 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     return FAILURE;
   }
 
-  // Count the available ports containing the client name as device
-  // channels.  Jack "input ports" equal RtAudio output channels.
-  unsigned int nChannels = 0;
   unsigned long flag = JackPortIsInput;
   if ( mode == INPUT ) flag = JackPortIsOutput;
-  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
-  if ( ports ) {
-    while ( ports[ nChannels ] ) nChannels++;
-    free( ports );
-  }
 
-  // Compare the jack ports for specified client to the requested number of channels.
-  if ( nChannels < (channels + firstChannel) ) {
-    errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
-    errorText_ = errorStream_.str();
-    return FAILURE;
+  if ( ! (options && (options->flags & RTAUDIO_JACK_DONT_CONNECT)) ) {
+    // Count the available ports containing the client name as device
+    // channels.  Jack "input ports" equal RtAudio output channels.
+    unsigned int nChannels = 0;
+    ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag );
+    if ( ports ) {
+      while ( ports[ nChannels ] ) nChannels++;
+      free( ports );
+    }
+    // Compare the jack ports for specified client to the requested number of channels.
+    if ( nChannels < (channels + firstChannel) ) {
+      errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
 
   // Check the jack server sample rate.
@@ -2221,7 +2283,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.sampleRate = jackRate;
 
   // Get the latency of the JACK port.
-  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag );
   if ( ports[ firstChannel ] ) {
     // Added by Ge Wang
     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);
@@ -2334,7 +2396,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   else {
     stream_.mode = mode;
     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
-    jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
+    jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle );
     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
   }
 
@@ -2441,6 +2503,10 @@ void RtApiJack :: startStream( void )
     return;
   }
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
   int result = jack_activate( handle->client );
   if ( result ) {
@@ -2453,7 +2519,7 @@ void RtApiJack :: startStream( void )
   // Get the list of available ports.
   if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) {
     result = 1;
-    ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
+    ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
     if ( ports == NULL) {
       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
       goto unlock;
@@ -2477,7 +2543,7 @@ void RtApiJack :: startStream( void )
 
   if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) {
     result = 1;
-    ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
+    ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
     if ( ports == NULL) {
       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
       goto unlock;
@@ -3163,8 +3229,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
   if ( result != ASE_OK ) {
     // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges
-    // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver
-    // in that case, let's be naïve and try that instead
+    // but only accept the preferred buffer size as parameter for ASIOCreateBuffers (e.g. Creative's ASIO driver).
+    // In that case, let's be naïve and try that instead.
     *bufferSize = preferSize;
     stream_.bufferSize = *bufferSize;
     result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
@@ -3320,6 +3386,10 @@ void RtApiAsio :: startStream()
     return;
   }
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
   ASIOError result = ASIOStart();
   if ( result != ASE_OK ) {
@@ -3687,11 +3757,33 @@ static const char* getAsioErrorString( ASIOError result )
 #ifndef INITGUID
   #define INITGUID
 #endif
+
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfplay.h>
+#include <mftransform.h>
+#include <wmcodecdsp.h>
+
 #include <audioclient.h>
 #include <avrt.h>
 #include <mmdeviceapi.h>
 #include <functiondiscoverykeys_devpkey.h>
 
+#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT
+  #define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72)
+#endif
+
+#ifndef MFSTARTUP_NOSOCKET
+  #define MFSTARTUP_NOSOCKET 0x1
+#endif
+
+#ifdef _MSC_VER
+  #pragma comment( lib, "ksuser" )
+  #pragma comment( lib, "mfplat.lib" )
+  #pragma comment( lib, "mfuuid.lib" )
+  #pragma comment( lib, "wmcodecdspuuid" )
+#endif
+
 //=============================================================================
 
 #define SAFE_RELEASE( objectPtr )\
@@ -3750,7 +3842,7 @@ public:
     }
 
     // "in" index can end on the "out" index but cannot begin at it
-    if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {
+    if ( inIndex_ < relOutIndex && inIndexEnd > relOutIndex ) {
       return false; // not enough space between "in" index and "out" index
     }
 
@@ -3811,7 +3903,7 @@ public:
     }
 
     // "out" index can begin at and end on the "in" index
-    if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {
+    if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) {
       return false; // not enough space between "out" index and "in" index
     }
 
@@ -3865,145 +3957,194 @@ private:
 //-----------------------------------------------------------------------------
 
 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate
-// between HW and the user. The convertBufferWasapi function is used to perform this conversion
-// between HwIn->UserIn and UserOut->HwOut during the stream callback loop.
-// This sample rate converter works best with conversions between one rate and its multiple.
-void convertBufferWasapi( char* outBuffer,
-                          const char* inBuffer,
-                          const unsigned int& channelCount,
-                          const unsigned int& inSampleRate,
-                          const unsigned int& outSampleRate,
-                          const unsigned int& inSampleCount,
-                          unsigned int& outSampleCount,
-                          const RtAudioFormat& format )
-{
-  // calculate the new outSampleCount and relative sampleStep
-  float sampleRatio = ( float ) outSampleRate / inSampleRate;
-  float sampleRatioInv = ( float ) 1 / sampleRatio;
-  float sampleStep = 1.0f / sampleRatio;
-  float inSampleFraction = 0.0f;
-
-  outSampleCount = ( unsigned int ) std::roundf( inSampleCount * sampleRatio );
-
-  // if inSampleRate is a multiple of outSampleRate (or vice versa) there's no need to interpolate
-  if ( floor( sampleRatio ) == sampleRatio || floor( sampleRatioInv ) == sampleRatioInv )
+// between HW and the user. The WasapiResampler class is used to perform this conversion between
+// HwIn->UserIn and UserOut->HwOut during the stream callback loop.
+class WasapiResampler
+{
+public:
+  WasapiResampler( bool isFloat, unsigned int bitsPerSample, unsigned int channelCount,
+                   unsigned int inSampleRate, unsigned int outSampleRate )
+    : _bytesPerSample( bitsPerSample / 8 )
+    , _channelCount( channelCount )
+    , _sampleRatio( ( float ) outSampleRate / inSampleRate )
+    , _transformUnk( NULL )
+    , _transform( NULL )
+    , _mediaType( NULL )
+    , _inputMediaType( NULL )
+    , _outputMediaType( NULL )
+
+    #ifdef __IWMResamplerProps_FWD_DEFINED__
+      , _resamplerProps( NULL )
+    #endif
   {
-    // frame-by-frame, copy each relative input sample into it's corresponding output sample
-    for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
-    {
-      unsigned int inSample = ( unsigned int ) inSampleFraction;
+    // 1. Initialization
 
-      switch ( format )
-      {
-        case RTAUDIO_SINT8:
-          memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );
-          break;
-        case RTAUDIO_SINT16:
-          memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );
-          break;
-        case RTAUDIO_SINT24:
-          memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );
-          break;
-        case RTAUDIO_SINT32:
-          memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );
-          break;
-        case RTAUDIO_FLOAT32:
-          memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );
-          break;
-        case RTAUDIO_FLOAT64:
-          memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );
-          break;
-      }
+    MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET );
 
-      // jump to next in sample
-      inSampleFraction += sampleStep;
-    }
+    // 2. Create Resampler Transform Object
+
+    CoCreateInstance( CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER,
+                      IID_IUnknown, ( void** ) &_transformUnk );
+
+    _transformUnk->QueryInterface( IID_PPV_ARGS( &_transform ) );
+
+    #ifdef __IWMResamplerProps_FWD_DEFINED__
+      _transformUnk->QueryInterface( IID_PPV_ARGS( &_resamplerProps ) );
+      _resamplerProps->SetHalfFilterLength( 60 ); // best conversion quality
+    #endif
+
+    // 3. Specify input / output format
+
+    MFCreateMediaType( &_mediaType );
+    _mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
+    _mediaType->SetGUID( MF_MT_SUBTYPE, isFloat ? MFAudioFormat_Float : MFAudioFormat_PCM );
+    _mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channelCount );
+    _mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSampleRate );
+    _mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, _bytesPerSample * channelCount );
+    _mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * inSampleRate );
+    _mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample );
+    _mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE );
+
+    MFCreateMediaType( &_inputMediaType );
+    _mediaType->CopyAllItems( _inputMediaType );
+
+    _transform->SetInputType( 0, _inputMediaType, 0 );
+
+    MFCreateMediaType( &_outputMediaType );
+    _mediaType->CopyAllItems( _outputMediaType );
+
+    _outputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, outSampleRate );
+    _outputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * outSampleRate );
+
+    _transform->SetOutputType( 0, _outputMediaType, 0 );
+
+    // 4. Send stream start messages to Resampler
+
+    _transform->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, 0 );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0 );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0 );
   }
-  else // else interpolate
+
+  ~WasapiResampler()
   {
-    // frame-by-frame, copy each relative input sample into it's corresponding output sample
-    for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
+    // 8. Send stream stop messages to Resampler
+
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0 );
+    _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_STREAMING, 0 );
+
+    // 9. Cleanup
+
+    MFShutdown();
+
+    SAFE_RELEASE( _transformUnk );
+    SAFE_RELEASE( _transform );
+    SAFE_RELEASE( _mediaType );
+    SAFE_RELEASE( _inputMediaType );
+    SAFE_RELEASE( _outputMediaType );
+
+    #ifdef __IWMResamplerProps_FWD_DEFINED__
+      SAFE_RELEASE( _resamplerProps );
+    #endif
+  }
+
+  void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount )
+  {
+    unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount;
+    if ( _sampleRatio == 1 )
     {
-      unsigned int inSample = ( unsigned int ) inSampleFraction;
-      float inSampleDec = inSampleFraction - inSample;
-      unsigned int frameInSample = inSample * channelCount;
-      unsigned int frameOutSample = outSample * channelCount;
+      // no sample rate conversion required
+      memcpy( outBuffer, inBuffer, inputBufferSize );
+      outSampleCount = inSampleCount;
+      return;
+    }
 
-      switch ( format )
-      {
-        case RTAUDIO_SINT8:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            char fromSample = ( ( char* ) inBuffer )[ frameInSample + channel ];
-            char toSample = ( ( char* ) inBuffer )[ frameInSample + channelCount + channel ];
-            char sampleDiff = ( char ) ( ( toSample - fromSample ) * inSampleDec );
-            ( ( char* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-        case RTAUDIO_SINT16:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            short fromSample = ( ( short* ) inBuffer )[ frameInSample + channel ];
-            short toSample = ( ( short* ) inBuffer )[ frameInSample + channelCount + channel ];
-            short sampleDiff = ( short ) ( ( toSample - fromSample ) * inSampleDec );
-            ( ( short* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-        case RTAUDIO_SINT24:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            int fromSample = ( ( S24* ) inBuffer )[ frameInSample + channel ].asInt();
-            int toSample = ( ( S24* ) inBuffer )[ frameInSample + channelCount + channel ].asInt();
-            int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );
-            ( ( S24* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-        case RTAUDIO_SINT32:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            int fromSample = ( ( int* ) inBuffer )[ frameInSample + channel ];
-            int toSample = ( ( int* ) inBuffer )[ frameInSample + channelCount + channel ];
-            int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );
-            ( ( int* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-        case RTAUDIO_FLOAT32:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            float fromSample = ( ( float* ) inBuffer )[ frameInSample + channel ];
-            float toSample = ( ( float* ) inBuffer )[ frameInSample + channelCount + channel ];
-            float sampleDiff = ( toSample - fromSample ) * inSampleDec;
-            ( ( float* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-        case RTAUDIO_FLOAT64:
-        {
-          for ( unsigned int channel = 0; channel < channelCount; channel++ )
-          {
-            double fromSample = ( ( double* ) inBuffer )[ frameInSample + channel ];
-            double toSample = ( ( double* ) inBuffer )[ frameInSample + channelCount + channel ];
-            double sampleDiff = ( toSample - fromSample ) * inSampleDec;
-            ( ( double* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;
-          }
-          break;
-        }
-      }
+    unsigned int outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount );
+
+    IMFMediaBuffer* rInBuffer;
+    IMFSample* rInSample;
+    BYTE* rInByteBuffer = NULL;
+
+    // 5. Create Sample object from input data
+
+    MFCreateMemoryBuffer( inputBufferSize, &rInBuffer );
+
+    rInBuffer->Lock( &rInByteBuffer, NULL, NULL );
+    memcpy( rInByteBuffer, inBuffer, inputBufferSize );
+    rInBuffer->Unlock();
+    rInByteBuffer = NULL;
+
+    rInBuffer->SetCurrentLength( inputBufferSize );
+
+    MFCreateSample( &rInSample );
+    rInSample->AddBuffer( rInBuffer );
+
+    // 6. Pass input data to Resampler
+
+    _transform->ProcessInput( 0, rInSample, 0 );
+
+    SAFE_RELEASE( rInBuffer );
+    SAFE_RELEASE( rInSample );
+
+    // 7. Perform sample rate conversion
+
+    IMFMediaBuffer* rOutBuffer = NULL;
+    BYTE* rOutByteBuffer = NULL;
+
+    MFT_OUTPUT_DATA_BUFFER rOutDataBuffer;
+    DWORD rStatus;
+    DWORD rBytes = outputBufferSize; // maximum bytes accepted per ProcessOutput
+
+    // 7.1 Create Sample object for output data
+
+    memset( &rOutDataBuffer, 0, sizeof rOutDataBuffer );
+    MFCreateSample( &( rOutDataBuffer.pSample ) );
+    MFCreateMemoryBuffer( rBytes, &rOutBuffer );
+    rOutDataBuffer.pSample->AddBuffer( rOutBuffer );
+    rOutDataBuffer.dwStreamID = 0;
+    rOutDataBuffer.dwStatus = 0;
+    rOutDataBuffer.pEvents = NULL;
+
+    // 7.2 Get output data from Resampler
 
-      // jump to next in sample
-      inSampleFraction += sampleStep;
+    if ( _transform->ProcessOutput( 0, 1, &rOutDataBuffer, &rStatus ) == MF_E_TRANSFORM_NEED_MORE_INPUT )
+    {
+      outSampleCount = 0;
+      SAFE_RELEASE( rOutBuffer );
+      SAFE_RELEASE( rOutDataBuffer.pSample );
+      return;
     }
+
+    // 7.3 Write output data to outBuffer
+
+    SAFE_RELEASE( rOutBuffer );
+    rOutDataBuffer.pSample->ConvertToContiguousBuffer( &rOutBuffer );
+    rOutBuffer->GetCurrentLength( &rBytes );
+
+    rOutBuffer->Lock( &rOutByteBuffer, NULL, NULL );
+    memcpy( outBuffer, rOutByteBuffer, rBytes );
+    rOutBuffer->Unlock();
+    rOutByteBuffer = NULL;
+
+    outSampleCount = rBytes / _bytesPerSample / _channelCount;
+    SAFE_RELEASE( rOutBuffer );
+    SAFE_RELEASE( rOutDataBuffer.pSample );
   }
-}
+
+private:
+  unsigned int _bytesPerSample;
+  unsigned int _channelCount;
+  float _sampleRatio;
+
+  IUnknown* _transformUnk;
+  IMFTransform* _transform;
+  IMFMediaType* _mediaType;
+  IMFMediaType* _inputMediaType;
+  IMFMediaType* _outputMediaType;
+
+  #ifdef __IWMResamplerProps_FWD_DEFINED__
+    IWMResamplerProps* _resamplerProps;
+  #endif
+};
 
 //-----------------------------------------------------------------------------
 
@@ -4041,10 +4182,9 @@ RtApiWasapi::RtApiWasapi()
                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),
                          ( void** ) &deviceEnumerator_ );
 
-  if ( FAILED( hr ) ) {
-    errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";
-    error( RtAudioError::DRIVER_ERROR );
-  }
+  // If this runs on an old Windows, it will fail. Ignore and proceed.
+  if ( FAILED( hr ) )
+    deviceEnumerator_ = NULL;
 }
 
 //-----------------------------------------------------------------------------
@@ -4071,6 +4211,9 @@ unsigned int RtApiWasapi::getDeviceCount( void )
   IMMDeviceCollection* captureDevices = NULL;
   IMMDeviceCollection* renderDevices = NULL;
 
+  if ( !deviceEnumerator_ )
+    return 0;
+
   // Count capture devices
   errorText_.clear();
   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
@@ -4420,6 +4563,10 @@ void RtApiWasapi::startStream( void )
     return;
   }
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   // update stream state
   stream_.state = STREAM_RUNNING;
 
@@ -4459,26 +4606,6 @@ void RtApiWasapi::stopStream( void )
   // Wait for the last buffer to play before stopping.
   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
 
-  // stop capture client if applicable
-  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
-    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";
-      error( RtAudioError::DRIVER_ERROR );
-      return;
-    }
-  }
-
-  // stop render client if applicable
-  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
-    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";
-      error( RtAudioError::DRIVER_ERROR );
-      return;
-    }
-  }
-
   // close thread handle
   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
@@ -4509,26 +4636,6 @@ void RtApiWasapi::abortStream( void )
     Sleep( 1 );
   }
 
-  // stop capture client if applicable
-  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
-    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";
-      error( RtAudioError::DRIVER_ERROR );
-      return;
-    }
-  }
-
-  // stop render client if applicable
-  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
-    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";
-      error( RtAudioError::DRIVER_ERROR );
-      return;
-    }
-  }
-
   // close thread handle
   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";
@@ -4596,7 +4703,7 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     goto Exit;
   }
 
-  // determine whether index falls within capture or render devices
+  // if device index falls within capture devices
   if ( device >= renderDeviceCount ) {
     if ( mode != INPUT ) {
       errorType = RtAudioError::INVALID_USE;
@@ -4616,28 +4723,66 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
                               NULL, ( void** ) &captureAudioClient );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device audio client.";
       goto Exit;
     }
 
     hr = captureAudioClient->GetMixFormat( &deviceFormat );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device mix format.";
       goto Exit;
     }
 
     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
   }
-  else {
-    if ( mode != OUTPUT ) {
-      errorType = RtAudioError::INVALID_USE;
-      errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";
+
+  // if device index falls within render devices and is configured for loopback
+  if ( device < renderDeviceCount && mode == INPUT )
+  {
+    // if renderAudioClient is not initialised, initialise it now
+    IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
+    if ( !renderAudioClient )
+    {
+      probeDeviceOpen( device, OUTPUT, channels, firstChannel, sampleRate, format, bufferSize, options );
+    }
+
+    // retrieve captureAudioClient from devicePtr
+    IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
+
+    hr = renderDevices->Item( device, &devicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";
+      goto Exit;
+    }
+
+    hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
+                              NULL, ( void** ) &captureAudioClient );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client.";
       goto Exit;
     }
 
-    // retrieve renderAudioClient from devicePtr
+    hr = captureAudioClient->GetMixFormat( &deviceFormat );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format.";
+      goto Exit;
+    }
+
+    stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
+    captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
+  }
+
+  // if device index falls within render devices and is configured for output
+  if ( device < renderDeviceCount && mode == OUTPUT )
+  {
+    // if renderAudioClient is already initialised, don't initialise it again
     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
+    if ( renderAudioClient )
+    {
+      methodResult = SUCCESS;
+      goto Exit;
+    }
 
     hr = renderDevices->Item( device, &devicePtr );
     if ( FAILED( hr ) ) {
@@ -4648,13 +4793,13 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
                               NULL, ( void** ) &renderAudioClient );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client.";
       goto Exit;
     }
 
     hr = renderAudioClient->GetMixFormat( &deviceFormat );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format.";
       goto Exit;
     }
 
@@ -4690,7 +4835,8 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // Set flags for buffer conversion.
   stream_.doConvertBuffer[mode] = false;
   if ( stream_.userFormat != stream_.deviceFormat[mode] ||
-       stream_.nUserChannels != stream_.nDeviceChannels )
+       stream_.nUserChannels[0] != stream_.nDeviceChannels[0] ||
+       stream_.nUserChannels[1] != stream_.nDeviceChannels[1] )
     stream_.doConvertBuffer[mode] = true;
   else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
             stream_.nUserChannels[mode] > 1 )
@@ -4783,6 +4929,8 @@ void RtApiWasapi::wasapiThread()
   float renderSrRatio = 0.0f;
   WasapiBuffer captureBuffer;
   WasapiBuffer renderBuffer;
+  WasapiResampler* captureResampler = NULL;
+  WasapiResampler* renderResampler = NULL;
 
   // declare local stream variables
   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
@@ -4791,7 +4939,8 @@ void RtApiWasapi::wasapiThread()
   unsigned int bufferFrameCount = 0;
   unsigned int numFramesPadding = 0;
   unsigned int convBufferSize = 0;
-  bool callbackPushed = false;
+  bool loopbackEnabled = stream_.device[INPUT] == stream_.device[OUTPUT];
+  bool callbackPushed = true;
   bool callbackPulled = false;
   bool callbackStopped = false;
   int callbackResult = 0;
@@ -4801,14 +4950,15 @@ void RtApiWasapi::wasapiThread()
   unsigned int convBuffSize = 0;
   unsigned int deviceBuffSize = 0;
 
-  errorText_.clear();
+  std::string errorText;
   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
 
   // Attempt to assign "Pro Audio" characteristic to thread
   HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );
   if ( AvrtDll ) {
     DWORD taskIndex = 0;
-    TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
+    TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr =
+      ( TAvSetMmThreadCharacteristicsPtr ) (void(*)()) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );
     FreeLibrary( AvrtDll );
   }
@@ -4817,112 +4967,119 @@ void RtApiWasapi::wasapiThread()
   if ( captureAudioClient ) {
     hr = captureAudioClient->GetMixFormat( &captureFormat );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
+      errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
       goto Exit;
     }
 
-    captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );
+    // init captureResampler
+    captureResampler = new WasapiResampler( stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT64,
+                                            formatBytes( stream_.deviceFormat[INPUT] ) * 8, stream_.nDeviceChannels[INPUT],
+                                            captureFormat->nSamplesPerSec, stream_.sampleRate );
 
-    // initialize capture stream according to desire buffer size
-    float desiredBufferSize = stream_.bufferSize * captureSrRatio;
-    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );
+    captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );
 
     if ( !captureClient ) {
       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
-                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
-                                           desiredBufferPeriod,
-                                           desiredBufferPeriod,
+                                           loopbackEnabled ? AUDCLNT_STREAMFLAGS_LOOPBACK : AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                           0,
+                                           0,
                                            captureFormat,
                                            NULL );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";
         goto Exit;
       }
 
       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),
                                            ( void** ) &captureClient );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";
         goto Exit;
       }
 
-      // configure captureEvent to trigger on every available capture buffer
-      captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
-      if ( !captureEvent ) {
-        errorType = RtAudioError::SYSTEM_ERROR;
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";
-        goto Exit;
+      // don't configure captureEvent if in loopback mode
+      if ( !loopbackEnabled )
+      {
+        // configure captureEvent to trigger on every available capture buffer
+        captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+        if ( !captureEvent ) {
+          errorType = RtAudioError::SYSTEM_ERROR;
+          errorText = "RtApiWasapi::wasapiThread: Unable to create capture event.";
+          goto Exit;
+        }
+
+        hr = captureAudioClient->SetEventHandle( captureEvent );
+        if ( FAILED( hr ) ) {
+          errorText = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";
+          goto Exit;
+        }
+
+        ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;
       }
 
-      hr = captureAudioClient->SetEventHandle( captureEvent );
+      ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
+
+      // reset the capture stream
+      hr = captureAudioClient->Reset();
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
         goto Exit;
       }
 
-      ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
-      ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;
+      // start the capture stream
+      hr = captureAudioClient->Start();
+      if ( FAILED( hr ) ) {
+        errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
+        goto Exit;
+      }
     }
 
     unsigned int inBufferSize = 0;
     hr = captureAudioClient->GetBufferSize( &inBufferSize );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";
+      errorText = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";
       goto Exit;
     }
 
     // scale outBufferSize according to stream->user sample rate ratio
-    unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];
+    unsigned int outBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];
     inBufferSize *= stream_.nDeviceChannels[INPUT];
 
     // set captureBuffer size
     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );
-
-    // reset the capture stream
-    hr = captureAudioClient->Reset();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
-      goto Exit;
-    }
-
-    // start the capture stream
-    hr = captureAudioClient->Start();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
-      goto Exit;
-    }
   }
 
   // start render stream if applicable
   if ( renderAudioClient ) {
     hr = renderAudioClient->GetMixFormat( &renderFormat );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
+      errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
       goto Exit;
     }
 
-    renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );
+    // init renderResampler
+    renderResampler = new WasapiResampler( stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT64,
+                                           formatBytes( stream_.deviceFormat[OUTPUT] ) * 8, stream_.nDeviceChannels[OUTPUT],
+                                           stream_.sampleRate, renderFormat->nSamplesPerSec );
 
-    // initialize render stream according to desire buffer size
-    float desiredBufferSize = stream_.bufferSize * renderSrRatio;
-    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );
+    renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );
 
     if ( !renderClient ) {
       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
-                                          desiredBufferPeriod,
-                                          desiredBufferPeriod,
+                                          0,
+                                          0,
                                           renderFormat,
                                           NULL );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";
         goto Exit;
       }
 
       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),
                                           ( void** ) &renderClient );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";
         goto Exit;
       }
 
@@ -4930,69 +5087,75 @@ void RtApiWasapi::wasapiThread()
       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
       if ( !renderEvent ) {
         errorType = RtAudioError::SYSTEM_ERROR;
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to create render event.";
         goto Exit;
       }
 
       hr = renderAudioClient->SetEventHandle( renderEvent );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to set render event handle.";
         goto Exit;
       }
 
       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;
       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;
+
+      // reset the render stream
+      hr = renderAudioClient->Reset();
+      if ( FAILED( hr ) ) {
+        errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
+        goto Exit;
+      }
+
+      // start the render stream
+      hr = renderAudioClient->Start();
+      if ( FAILED( hr ) ) {
+        errorText = "RtApiWasapi::wasapiThread: Unable to start render stream.";
+        goto Exit;
+      }
     }
 
     unsigned int outBufferSize = 0;
     hr = renderAudioClient->GetBufferSize( &outBufferSize );
     if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";
+      errorText = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";
       goto Exit;
     }
 
     // scale inBufferSize according to user->stream sample rate ratio
-    unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];
+    unsigned int inBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];
     outBufferSize *= stream_.nDeviceChannels[OUTPUT];
 
     // set renderBuffer size
     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );
-
-    // reset the render stream
-    hr = renderAudioClient->Reset();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
-      goto Exit;
-    }
-
-    // start the render stream
-    hr = renderAudioClient->Start();
-    if ( FAILED( hr ) ) {
-      errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";
-      goto Exit;
-    }
   }
 
-  if ( stream_.mode == INPUT ) {
-    convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
+  // malloc buffer memory
+  if ( stream_.mode == INPUT )
+  {
+    using namespace std; // for ceilf
+    convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
   }
-  else if ( stream_.mode == OUTPUT ) {
-    convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
+  else if ( stream_.mode == OUTPUT )
+  {
+    convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
   }
-  else if ( stream_.mode == DUPLEX ) {
-    convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
-                             ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
+  else if ( stream_.mode == DUPLEX )
+  {
+    convBuffSize = std::max( ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
+                             ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
   }
 
-  convBuffer = ( char* ) malloc( convBuffSize );
-  stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );
+  convBuffSize *= 2; // allow overflow for *SrRatio remainders
+  convBuffer = ( char* ) calloc( convBuffSize, 1 );
+  stream_.deviceBuffer = ( char* ) calloc( deviceBuffSize, 1 );
   if ( !convBuffer || !stream_.deviceBuffer ) {
     errorType = RtAudioError::MEMORY_ERROR;
-    errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
+    errorText = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
     goto Exit;
   }
 
@@ -5005,23 +5168,43 @@ void RtApiWasapi::wasapiThread()
       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count
       //                          Convert callback buffer to user format
 
-      if ( captureAudioClient ) {
-        // Pull callback buffer from inputBuffer
-        callbackPulled = captureBuffer.pullBuffer( convBuffer,
-                                                   ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],
-                                                   stream_.deviceFormat[INPUT] );
+      if ( captureAudioClient )
+      {
+        int samplesToPull = ( unsigned int ) floorf( stream_.bufferSize * captureSrRatio );
+        if ( captureSrRatio != 1 )
+        {
+          // account for remainders
+          samplesToPull--;
+        }
+
+        convBufferSize = 0;
+        while ( convBufferSize < stream_.bufferSize )
+        {
+          // Pull callback buffer from inputBuffer
+          callbackPulled = captureBuffer.pullBuffer( convBuffer,
+                                                     samplesToPull * stream_.nDeviceChannels[INPUT],
+                                                     stream_.deviceFormat[INPUT] );
+
+          if ( !callbackPulled )
+          {
+            break;
+          }
 
-        if ( callbackPulled ) {
           // Convert callback buffer to user sample rate
-          convertBufferWasapi( stream_.deviceBuffer,
-                               convBuffer,
-                               stream_.nDeviceChannels[INPUT],
-                               captureFormat->nSamplesPerSec,
-                               stream_.sampleRate,
-                               ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),
-                               convBufferSize,
-                               stream_.deviceFormat[INPUT] );
+          unsigned int deviceBufferOffset = convBufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
+          unsigned int convSamples = 0;
+
+          captureResampler->Convert( stream_.deviceBuffer + deviceBufferOffset,
+                                     convBuffer,
+                                     samplesToPull,
+                                     convSamples );
 
+          convBufferSize += convSamples;
+          samplesToPull = 1; // now pull one sample at a time until we have stream_.bufferSize samples
+        }
+
+        if ( callbackPulled )
+        {
           if ( stream_.doConvertBuffer[INPUT] ) {
             // Convert callback buffer to user format
             convertBuffer( stream_.userBuffer[INPUT],
@@ -5062,12 +5245,12 @@ void RtApiWasapi::wasapiThread()
           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );
           if ( !threadHandle ) {
             errorType = RtAudioError::THREAD_ERROR;
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";
             goto Exit;
           }
           else if ( !CloseHandle( threadHandle ) ) {
             errorType = RtAudioError::THREAD_ERROR;
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";
             goto Exit;
           }
 
@@ -5078,12 +5261,12 @@ void RtApiWasapi::wasapiThread()
           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );
           if ( !threadHandle ) {
             errorType = RtAudioError::THREAD_ERROR;
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";
             goto Exit;
           }
           else if ( !CloseHandle( threadHandle ) ) {
             errorType = RtAudioError::THREAD_ERROR;
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";
             goto Exit;
           }
 
@@ -5098,24 +5281,32 @@ void RtApiWasapi::wasapiThread()
     // 2. Convert callback buffer to stream sample rate and channel count
     // 3. Push callback buffer into outputBuffer
 
-    if ( renderAudioClient && callbackPulled ) {
-      if ( stream_.doConvertBuffer[OUTPUT] ) {
-        // Convert callback buffer to stream format
-        convertBuffer( stream_.deviceBuffer,
-                       stream_.userBuffer[OUTPUT],
-                       stream_.convertInfo[OUTPUT] );
+    if ( renderAudioClient && callbackPulled )
+    {
+      // if the last call to renderBuffer.PushBuffer() was successful
+      if ( callbackPushed || convBufferSize == 0 )
+      {
+        if ( stream_.doConvertBuffer[OUTPUT] )
+        {
+          // Convert callback buffer to stream format
+          convertBuffer( stream_.deviceBuffer,
+                         stream_.userBuffer[OUTPUT],
+                         stream_.convertInfo[OUTPUT] );
 
-      }
+        }
+        else {
+          // no further conversion, simple copy userBuffer to deviceBuffer
+          memcpy( stream_.deviceBuffer,
+                  stream_.userBuffer[OUTPUT],
+                  stream_.bufferSize * stream_.nUserChannels[OUTPUT] * formatBytes( stream_.userFormat ) );
+        }
 
-      // Convert callback buffer to stream sample rate
-      convertBufferWasapi( convBuffer,
-                           stream_.deviceBuffer,
-                           stream_.nDeviceChannels[OUTPUT],
-                           stream_.sampleRate,
-                           renderFormat->nSamplesPerSec,
-                           stream_.bufferSize,
-                           convBufferSize,
-                           stream_.deviceFormat[OUTPUT] );
+        // Convert callback buffer to stream sample rate
+        renderResampler->Convert( convBuffer,
+                                  stream_.deviceBuffer,
+                                  stream_.bufferSize,
+                                  convBufferSize );
+      }
 
       // Push callback buffer into outputBuffer
       callbackPushed = renderBuffer.pushBuffer( convBuffer,
@@ -5136,7 +5327,7 @@ void RtApiWasapi::wasapiThread()
     if ( captureAudioClient ) {
       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event
       if ( !callbackPulled ) {
-        WaitForSingleObject( captureEvent, INFINITE );
+        WaitForSingleObject( loopbackEnabled ? renderEvent : captureEvent, INFINITE );
       }
 
       // Get capture buffer from stream
@@ -5144,7 +5335,7 @@ void RtApiWasapi::wasapiThread()
                                      &bufferFrameCount,
                                      &captureFlags, NULL, NULL );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";
         goto Exit;
       }
 
@@ -5157,7 +5348,7 @@ void RtApiWasapi::wasapiThread()
           // Release capture buffer
           hr = captureClient->ReleaseBuffer( bufferFrameCount );
           if ( FAILED( hr ) ) {
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
             goto Exit;
           }
         }
@@ -5166,7 +5357,7 @@ void RtApiWasapi::wasapiThread()
           // Inform WASAPI that capture was unsuccessful
           hr = captureClient->ReleaseBuffer( 0 );
           if ( FAILED( hr ) ) {
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
             goto Exit;
           }
         }
@@ -5176,7 +5367,7 @@ void RtApiWasapi::wasapiThread()
         // Inform WASAPI that capture was unsuccessful
         hr = captureClient->ReleaseBuffer( 0 );
         if ( FAILED( hr ) ) {
-          errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+          errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
           goto Exit;
         }
       }
@@ -5198,13 +5389,13 @@ void RtApiWasapi::wasapiThread()
       // Get render buffer from stream
       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";
         goto Exit;
       }
 
       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );
       if ( FAILED( hr ) ) {
-        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";
+        errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";
         goto Exit;
       }
 
@@ -5213,7 +5404,7 @@ void RtApiWasapi::wasapiThread()
       if ( bufferFrameCount != 0 ) {
         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );
         if ( FAILED( hr ) ) {
-          errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";
+          errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";
           goto Exit;
         }
 
@@ -5226,7 +5417,7 @@ void RtApiWasapi::wasapiThread()
           // Release render buffer
           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );
           if ( FAILED( hr ) ) {
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
             goto Exit;
           }
         }
@@ -5235,7 +5426,7 @@ void RtApiWasapi::wasapiThread()
           // Inform WASAPI that render was unsuccessful
           hr = renderClient->ReleaseBuffer( 0, 0 );
           if ( FAILED( hr ) ) {
-            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+            errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
             goto Exit;
           }
         }
@@ -5245,7 +5436,7 @@ void RtApiWasapi::wasapiThread()
         // Inform WASAPI that render was unsuccessful
         hr = renderClient->ReleaseBuffer( 0, 0 );
         if ( FAILED( hr ) ) {
-          errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+          errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
           goto Exit;
         }
       }
@@ -5253,7 +5444,10 @@ void RtApiWasapi::wasapiThread()
 
     // if the callback buffer was pushed renderBuffer reset callbackPulled flag
     if ( callbackPushed ) {
+      // unsetting the callbackPulled flag lets the stream know that
+      // the audio device is ready for another callback output buffer.
       callbackPulled = false;
+
       // tick stream time
       RtApi::tickStreamTime();
     }
@@ -5266,16 +5460,19 @@ Exit:
   CoTaskMemFree( renderFormat );
 
   free ( convBuffer );
+  delete renderResampler;
+  delete captureResampler;
 
   CoUninitialize();
 
   // update stream state
   stream_.state = STREAM_STOPPED;
 
-  if ( errorText_.empty() )
-    return;
-  else
+  if ( !errorText.empty() )
+  {
+    errorText_ = errorText;
     error( errorType );
+  }
 }
 
 //******************** End of __WINDOWS_WASAPI__ *********************//
@@ -5291,6 +5488,8 @@ Exit:
 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
 // Changed device query structure for RtAudio 4.0.7, January 2010
 
+#include <windows.h>
+#include <process.h>
 #include <mmsystem.h>
 #include <mmreg.h>
 #include <dsound.h>
@@ -6170,6 +6369,10 @@ void RtApiDs :: startStream()
     return;
   }
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   DsHandle *handle = (DsHandle *) stream_.apiHandle;
 
   // Increase scheduler frequency on lesser windows (a side-effect of
@@ -7331,10 +7534,12 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     if ( result == 0 ) {
       if ( nDevices == device ) {
         strcpy( name, "default" );
+        snd_ctl_close( chandle );
         goto foundDevice;
       }
       nDevices++;
     }
+    snd_ctl_close( chandle );
 
     if ( nDevices == 0 ) {
       // This should not happen because a check is made before this function is called.
@@ -7734,30 +7939,41 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     pthread_attr_t attr;
     pthread_attr_init( &attr );
     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
-
-#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
-      // We previously attempted to increase the audio callback priority
-      // to SCHED_RR here via the attributes.  However, while no errors
-      // were reported in doing so, it did not work.  So, now this is
-      // done in the alsaCallbackHandler function.
       stream_.callbackInfo.doRealtime = true;
+      struct sched_param param;
       int priority = options->priority;
       int min = sched_get_priority_min( SCHED_RR );
       int max = sched_get_priority_max( SCHED_RR );
       if ( priority < min ) priority = min;
       else if ( priority > max ) priority = max;
-      stream_.callbackInfo.priority = priority;
+      param.sched_priority = priority;
+
+      // Set the policy BEFORE the priority. Otherwise it fails.
+      pthread_attr_setschedpolicy(&attr, SCHED_RR);
+      pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+      // This is definitely required. Otherwise it fails.
+      pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+      pthread_attr_setschedparam(&attr, &param);
     }
+    else
+      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
 #endif
 
     stream_.callbackInfo.isRunning = true;
     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
     pthread_attr_destroy( &attr );
     if ( result ) {
-      stream_.callbackInfo.isRunning = false;
-      errorText_ = "RtApiAlsa::error creating callback thread!";
-      goto error;
+      // Failed. Try instead with default attributes.
+      result = pthread_create( &stream_.callbackInfo.thread, NULL, alsaCallbackHandler, &stream_.callbackInfo );
+      if ( result ) {
+        stream_.callbackInfo.isRunning = false;
+        errorText_ = "RtApiAlsa::error creating callback thread!";
+        goto error;
+      }
     }
   }
 
@@ -7853,6 +8069,10 @@ void RtApiAlsa :: startStream()
 
   MUTEX_LOCK( &stream_.mutex );
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   int result = 0;
   snd_pcm_state_t state;
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
@@ -8172,11 +8392,11 @@ static void *alsaCallbackHandler( void *ptr )
   RtApiAlsa *object = (RtApiAlsa *) info->object;
   bool *isRunning = &info->isRunning;
 
-#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
   if ( info->doRealtime ) {
-    pthread_t tID = pthread_self();     // ID of this thread
-    sched_param prio = { info->priority }; // scheduling priority of thread
-    pthread_setschedparam( tID, SCHED_RR, &prio );
+    std::cerr << "RtAudio alsa: " << 
+             (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
+             "running realtime scheduling" << std::endl;
   }
 #endif
 
@@ -8259,7 +8479,15 @@ static void *pulseaudio_callback( void * user )
   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );
   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
   volatile bool *isRunning = &cbi->isRunning;
-
+  
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
+  if (cbi->doRealtime) {
+    std::cerr << "RtAudio pulse: " << 
+             (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
+             "running realtime scheduling" << std::endl;
+  }
+#endif
+  
   while ( *isRunning ) {
     pthread_testcancel();
     context->callbackEvent();
@@ -8416,6 +8644,10 @@ void RtApiPulse::startStream( void )
 
   MUTEX_LOCK( &stream_.mutex );
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   stream_.state = STREAM_RUNNING;
 
   pah->runnable = true;
@@ -8644,15 +8876,56 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
 
   if ( !stream_.callbackInfo.isRunning ) {
     stream_.callbackInfo.object = this;
+    
+    stream_.state = STREAM_STOPPED;
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority (optional).  The higher priority will only take affect
+    // if the program is run as root or suid. Note, under Linux
+    // processes with CAP_SYS_NICE privilege, a user can change
+    // scheduling policy and priority (thus need not be root). See
+    // POSIX "capabilities".
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
+    if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      stream_.callbackInfo.doRealtime = true;
+      struct sched_param param;
+      int priority = options->priority;
+      int min = sched_get_priority_min( SCHED_RR );
+      int max = sched_get_priority_max( SCHED_RR );
+      if ( priority < min ) priority = min;
+      else if ( priority > max ) priority = max;
+      param.sched_priority = priority;
+      
+      // Set the policy BEFORE the priority. Otherwise it fails.
+      pthread_attr_setschedpolicy(&attr, SCHED_RR);
+      pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+      // This is definitely required. Otherwise it fails.
+      pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+      pthread_attr_setschedparam(&attr, &param);
+    }
+    else
+      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
+
     stream_.callbackInfo.isRunning = true;
-    if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {
-      errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
-      goto error;
+    int result = pthread_create( &pah->thread, &attr, pulseaudio_callback, (void *)&stream_.callbackInfo);
+    pthread_attr_destroy(&attr);
+    if(result != 0) {
+      // Failed. Try instead with default attributes.
+      result = pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo);
+      if(result != 0) {
+        stream_.callbackInfo.isRunning = false;
+        errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
+        goto error;
+      }
     }
   }
 
-  stream_.state = STREAM_STOPPED;
-  return true;
+  return SUCCESS;
  
  error:
   if ( pah && stream_.callbackInfo.isRunning ) {
@@ -8673,6 +8946,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     stream_.deviceBuffer = 0;
   }
 
+  stream_.state = STREAM_CLOSED;
   return FAILURE;
 }
 
@@ -9234,8 +9508,9 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     pthread_attr_t attr;
     pthread_attr_init( &attr );
     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
-#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      stream_.callbackInfo.doRealtime = true;
       struct sched_param param;
       int priority = options->priority;
       int min = sched_get_priority_min( SCHED_RR );
@@ -9243,8 +9518,13 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
       if ( priority < min ) priority = min;
       else if ( priority > max ) priority = max;
       param.sched_priority = priority;
-      pthread_attr_setschedparam( &attr, &param );
-      pthread_attr_setschedpolicy( &attr, SCHED_RR );
+      
+      // Set the policy BEFORE the priority. Otherwise it fails.
+      pthread_attr_setschedpolicy(&attr, SCHED_RR);
+      pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+      // This is definitely required. Otherwise it fails.
+      pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+      pthread_attr_setschedparam(&attr, &param);
     }
     else
       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
@@ -9256,9 +9536,13 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
     pthread_attr_destroy( &attr );
     if ( result ) {
-      stream_.callbackInfo.isRunning = false;
-      errorText_ = "RtApiOss::error creating callback thread!";
-      goto error;
+      // Failed. Try instead with default attributes.
+      result = pthread_create( &stream_.callbackInfo.thread, NULL, ossCallbackHandler, &stream_.callbackInfo );
+      if ( result ) {
+        stream_.callbackInfo.isRunning = false;
+        errorText_ = "RtApiOss::error creating callback thread!";
+        goto error;
+      }
     }
   }
 
@@ -9285,6 +9569,7 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     stream_.deviceBuffer = 0;
   }
 
+  stream_.state = STREAM_CLOSED;
   return FAILURE;
 }
 
@@ -9347,6 +9632,10 @@ void RtApiOss :: startStream()
 
   MUTEX_LOCK( &stream_.mutex );
 
+  #if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+  #endif
+
   stream_.state = STREAM_RUNNING;
 
   // No need to do anything else here ... OSS automatically starts
@@ -9614,6 +9903,14 @@ static void *ossCallbackHandler( void *ptr )
   RtApiOss *object = (RtApiOss *) info->object;
   bool *isRunning = &info->isRunning;
 
+#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
+  if (info->doRealtime) {
+    std::cerr << "RtAudio oss: " << 
+             (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
+             "running realtime scheduling" << std::endl;
+  }
+#endif
+
   while ( *isRunning == true ) {
     pthread_testcancel();
     object->callbackEvent();
index a9fa6504a6c9069fd9c402c60ee0a81bd6a137f4..4bb75545a29dcd7a72602f3f634ff03618c97654 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
 
 #define RTAUDIO_VERSION "5.0.0"
 
+#if defined _WIN32 || defined __CYGWIN__
+  #if defined(RTAUDIO_EXPORT)
+    #define RTAUDIO_DLL_PUBLIC __declspec(dllexport)
+  #else
+    #define RTAUDIO_DLL_PUBLIC
+  #endif
+#else
+  #if __GNUC__ >= 4
+    #define RTAUDIO_DLL_PUBLIC __attribute__( (visibility( "default" )) )
+  #else
+    #define RTAUDIO_DLL_PUBLIC
+  #endif
+#endif
+
 #include <string>
 #include <vector>
 #include <stdexcept>
@@ -102,7 +116,7 @@ static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/mi
     Certain audio APIs offer a number of parameters that influence the
     I/O latency of a stream.  By default, RtAudio will attempt to set
     these parameters internally for robust (glitch-free) performance
-    (though some APIs, like Windows Direct Sound, make this difficult).
+    (though some APIs, like Windows DirectSound, make this difficult).
     By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
     function, internal stream settings will be influenced in an attempt
     to minimize stream latency, though possibly at the expense of stream
@@ -200,7 +214,7 @@ typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer,
 */
 /************************************************************************/
 
-class RtAudioError : public std::runtime_error
+class RTAUDIO_DLL_PUBLIC RtAudioError : public std::runtime_error
 {
  public:
   //! Defined RtAudioError types.
@@ -260,7 +274,7 @@ typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string
 
 class RtApi;
 
-class RtAudio
+class RTAUDIO_DLL_PUBLIC RtAudio
 {
  public:
 
@@ -274,8 +288,9 @@ class RtAudio
     MACOSX_CORE,    /*!< Macintosh OS-X Core Audio API. */
     WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */
     WINDOWS_ASIO,   /*!< The Steinberg Audio Stream I/O API. */
-    WINDOWS_DS,     /*!< The Microsoft Direct Sound API. */
-    RTAUDIO_DUMMY   /*!< A compilable but non-functional API. */
+    WINDOWS_DS,     /*!< The Microsoft DirectSound API. */
+    RTAUDIO_DUMMY,  /*!< A compilable but non-functional API. */
+    NUM_APIS        /*!< Number of values in this enum. */
   };
 
   //! The public device information structure for returning queried values.
@@ -288,7 +303,7 @@ class RtAudio
     bool isDefaultOutput;         /*!< true if this is the default output device. */
     bool isDefaultInput;          /*!< true if this is the default input device. */
     std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
-    unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */
+    unsigned int preferredSampleRate; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */
     RtAudioFormat nativeFormats;  /*!< Bit mask of supported data formats. */
 
     // Default constructor.
@@ -333,7 +348,7 @@ class RtAudio
     Certain audio APIs offer a number of parameters that influence the
     I/O latency of a stream.  By default, RtAudio will attempt to set
     these parameters internally for robust (glitch-free) performance
-    (though some APIs, like Windows Direct Sound, make this difficult).
+    (though some APIs, like Windows DirectSound, make this difficult).
     By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
     function, internal stream settings will be influenced in an attempt
     to minimize stream latency, though possibly at the expense of stream
@@ -387,6 +402,29 @@ class RtAudio
   */
   static void getCompiledApi( std::vector<RtAudio::Api> &apis );
 
+  //! Return the name of a specified compiled audio API.
+  /*!
+    This obtains a short lower-case name used for identification purposes.
+    This value is guaranteed to remain identical across library versions.
+    If the API is unknown, this function will return the empty string.
+  */
+  static std::string getApiName( RtAudio::Api api );
+
+  //! Return the display name of a specified compiled audio API.
+  /*!
+    This obtains a long name used for display purposes.
+    If the API is unknown, this function will return the empty string.
+  */
+  static std::string getApiDisplayName( RtAudio::Api api );
+
+  //! Return the compiled audio API having the given name.
+  /*!
+    A case insensitive comparison will check the specified name
+    against the list of compiled APIs, and return the one which
+    matches. On failure, the function returns UNSPECIFIED.
+  */
+  static RtAudio::Api getCompiledApiByName( const std::string &name );
+
   //! The class constructor.
   /*!
     The constructor performs minor initialization tasks.  An exception
@@ -583,6 +621,7 @@ class RtAudio
   #endif
   #include <windows.h>
   #include <process.h>
+  #include <stdint.h>
 
   typedef uintptr_t ThreadHandle;
   typedef CRITICAL_SECTION StreamMutex;
@@ -651,7 +690,6 @@ class S24 {
     return *this;
   }
 
-  S24( const S24& v ) { *this = v; }
   S24( const double& d ) { *this = (int) d; }
   S24( const float& f ) { *this = (int) f; }
   S24( const signed short& s ) { *this = (int) s; }
@@ -671,7 +709,7 @@ class S24 {
 
 #include <sstream>
 
-class RtApi
+class RTAUDIO_DLL_PUBLIC RtApi
 {
 public:
 
@@ -864,7 +902,6 @@ public:
   void startStream( void );
   void stopStream( void );
   void abortStream( void );
-  long getStreamLatency( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
@@ -900,7 +937,6 @@ public:
   void startStream( void );
   void stopStream( void );
   void abortStream( void );
-  long getStreamLatency( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
@@ -935,7 +971,6 @@ public:
   void startStream( void );
   void stopStream( void );
   void abortStream( void );
-  long getStreamLatency( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
@@ -973,7 +1008,6 @@ public:
   void startStream( void );
   void stopStream( void );
   void abortStream( void );
-  long getStreamLatency( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
@@ -1003,7 +1037,7 @@ class RtApiWasapi : public RtApi
 {
 public:
   RtApiWasapi();
-  ~RtApiWasapi();
+  virtual ~RtApiWasapi();
 
   RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
   unsigned int getDeviceCount( void );
diff --git a/cmake/RtAudioConfigUninstall.cmake.in b/cmake/RtAudioConfigUninstall.cmake.in
new file mode 100644 (file)
index 0000000..db894b3
--- /dev/null
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
+endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
+  if(EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+  else(EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
+  endif(EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
index cc1c30b3f1a90bf2a753e764becce87637dbfb9c..1a2314c032f6a5d3c916bd51b23517c479be6e6e 100644 (file)
@@ -27,51 +27,52 @@ m4_define([lt_current_minus_age], [m4_eval(lt_current - lt_age)])
 
 SO_VERSION=lt_version_info
 AC_SUBST(SO_VERSION)
+AC_SUBST(api)
+AC_SUBST(req)
+AC_SUBST(visibility)
+
+api=""
+req=""
+use_asio=""
+
+
+# configure flags
+AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug],[enable various debug output])])
+AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack], [choose JACK server support])])
+AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa], [choose native ALSA API support (linux only)])])
+AC_ARG_WITH(pulse, [AS_HELP_STRING([--with-pulse], [choose PulseAudio API support (unixes)])])
+AC_ARG_WITH(oss, [AS_HELP_STRING([--with-oss], [choose OSS API support (unixes)])])
+AC_ARG_WITH(core, [AS_HELP_STRING([--with-core], [choose CoreAudio API support (mac only)])])
+AC_ARG_WITH(asio, [AS_HELP_STRING([--with-asio], [choose ASIO API support (win32 only)])])
+AC_ARG_WITH(ds, [AS_HELP_STRING([--with-ds], [choose DirectSound API support (win32 only)])])
+AC_ARG_WITH(wasapi, [AS_HELP_STRING([--with-wasapi], [choose Windows Audio Session API support (win32 only)])])
 
 # Check version number coherency between RtAudio.h and configure.ac
 AC_MSG_CHECKING([that version numbers are coherent])
-AC_RUN_IFELSE(
-   [AC_LANG_PROGRAM([#include <string.h>
-                     `grep "define RTAUDIO_VERSION" $srcdir/RtAudio.h`],
-                    [return strcmp(RTAUDIO_VERSION, PACKAGE_VERSION);])],
-   [AC_MSG_RESULT([yes])],
-   [AC_MSG_FAILURE([testing RTAUDIO_VERSION==PACKAGE_VERSION failed, check that RtAudio.h defines RTAUDIO_VERSION as "$PACKAGE_VERSION" or that the first line of configure.ac has been updated.])])
-
+RTAUDIO_VERSION=`sed -n 's/#define RTAUDIO_VERSION "\(.*\)"/\1/p' $srcdir/RtAudio.h`
+AS_IF([test "x$RTAUDIO_VERSION" != "x$PACKAGE_VERSION"],[
+   AC_MSG_RESULT([no])
+   AC_MSG_FAILURE([testing RTAUDIO_VERSION==PACKAGE_VERSION failed, check that RtAudio.h defines RTAUDIO_VERSION as "$PACKAGE_VERSION" or that the first line of configure.ac has been updated.])
+   ],[
+   AC_MSG_RESULT([yes])
+])
 # Enable some nice automake features if they are available
 m4_ifdef([AM_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 # Fill GXX with something before test.
-AC_SUBST( GXX, ["no"] )
-
-dnl Check for pkg-config program, used for configuring some libraries.
-m4_define_default([PKG_PROG_PKG_CONFIG],
-[AC_MSG_CHECKING([pkg-config])
-AC_MSG_RESULT([no])])
-
-PKG_PROG_PKG_CONFIG
-
-dnl If the pkg-config autoconf support isn't installed, define its
-dnl autoconf macro to disable any packages depending on it.
-m4_define_default([PKG_CHECK_MODULES],
-[AC_MSG_CHECKING([$1])
-AC_MSG_RESULT([no])
-$4])
-
-# Check for debug
-AC_MSG_CHECKING(whether to compile debug version)
-AC_ARG_ENABLE(debug,
-  [AS_HELP_STRING([--enable-debug],[enable various debug output])],
-  [: ${CXXFLAGS="-g -O0"};] [AC_DEFINE([__RTAUDIO_DEBUG__])],
-  [: ${CXXFLAGS="-O3"}])
+GXX="no"
+# if the user did not provide any CXXFLAGS, we can override them
+AS_IF([test "x$CXXFLAGS" = "x" ], [override_cxx=yes], [override_cxx=no])
+AS_IF([test "x$CFLAGS" = "x" ], [override_c=yes], [override_c=no])
 
 # Checks for programs.
 AC_PROG_CXX(g++ CC c++ cxx)
 AM_PROG_AR
 AC_PATH_PROG(AR, ar, no)
-if [[ $AR = "no" ]] ; then
-    AC_MSG_ERROR("Could not find ar - needed to create a library");
-fi
+AS_IF([test "x${AR}" = "xno" ], [
+    AC_MSG_ERROR([Could not find ar - needed to create a library])
+])
 
 # Initialize libtool
 LT_INIT([win32-dll])
@@ -82,20 +83,47 @@ AC_HEADER_STDC
 AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
 
 # Check compiler and use -Wall if gnu
-if test x"$GXX" = "xyes"; then
-  CXXFLAGS="${CXXFLAGS} -Wall -Wextra"
-  # Add -Werror in debug mode
-  if test x"${enable_debug+set}" = xset; then
-    CXXFLAGS="${CXXFLAGS} -Werror"
-  fi
-fi
+AS_IF([test "x${GXX}" = "xyes" ], [
+  CXXFLAGS="-Wall -Wextra ${CXXFLAGS}"
+  AS_IF([ test "x${enable_debug}" = "xyes" ], [
+    # Add -Werror in debug mode (except unused-function)
+    CXXFLAGS="-Werror -Wno-error=unused-function ${CXXFLAGS}"
+  ], [
+    # hide private symbols in non-debug mode
+    visibility="-fvisibility=hidden"
+  ])
+])
+
+# Check for debug
+AC_MSG_CHECKING([whether to compile debug version])
+debugflags=""
+AS_CASE([${enable_debug}],
+  [ yes ], [
+    AC_MSG_RESULT([yes])
+    AC_DEFINE([__RTAUDIO_DEBUG__])
+    debugflags="${debugflags} -g -O0"
+    object_path=Debug
+  ],
+  [ no ], [
+    AC_MSG_RESULT([no!])
+    debugflags="${debugflags} -O3"
+  ], [
+    AC_MSG_RESULT([no])
+  ])
+
+# For debugging and optimization ... overwrite default because it has both -g and -O2
+AS_IF([test "x$debugflags" != x],
+  AS_IF([test "x$override_cxx" = "xyes" ], CXXFLAGS="$CXXFLAGS $debugflags", CXXFLAGS="$debugflags $CXXFLAGS")
+  AS_IF([test "x$override_c" = "xyes" ], CFLAGS="$CFLAGS $debugflags", CFLAGS="$debugflags $CFLAGS")
+  )
+
 
 # Checks for functions
 AC_CHECK_FUNC(gettimeofday, [cppflag="$cppflag -DHAVE_GETTIMEOFDAY"], )
 
 # Checks for doxygen
 AC_CHECK_PROG( DOXYGEN, [doxygen], [doxygen] )
-AM_CONDITIONAL( MAKE_DOC, [test "x${DOXYGEN}" != x] )
+AM_CONDITIONAL( MAKE_DOC, [test "x${DOXYGEN}" != x ] )
 
 # Copy doc files to build dir if necessary
 AC_CONFIG_LINKS( [doc/release.txt:doc/release.txt] )
@@ -119,125 +147,170 @@ AC_CONFIG_LINKS( [doc/images/ccrma.gif:doc/images/ccrma.gif] )
 # Checks for package options and external software
 AC_CANONICAL_HOST
 
-AC_SUBST( api, [""] )
-AC_SUBST( req, [""] )
-AC_MSG_CHECKING(for audio API)
-
-AC_ARG_WITH(jack, [  --with-jack = choose JACK server support (mac and linux only)])
-AS_IF([test "x$with_jack" == "xyes"], [
-  api="$api -D__UNIX_JACK__"
-  AC_MSG_RESULT(using JACK)
-  AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))])
-
-case $host in
-  *-*-netbsd*)
-  AS_IF([test "$api" == ""], [
-    AC_MSG_RESULT(using OSS)
-    api="$api -D__LINUX_OSS__"
-    LIBS="$LIBS -lossaudio"
-    AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))])
-  ;;
-
-  *-*-freebsd*)
-  AS_IF([test "$api" == ""], [
-    AC_MSG_RESULT(using OSS)
-    api="$api -D__LINUX_OSS__"
-    LIBS="$LIBS -lossaudio"
-    AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))])
-  ;;
-
-  *-*-linux*)
-  # Look for ALSA flag
-  AC_ARG_WITH(alsa, [  --with-alsa = choose native ALSA API support (linux only)])
-  AS_IF([test "x$with_alsa" == "xyes"], [
-    api="$api -D__LINUX_ALSA__"
-    req="$req alsa"
-    AC_MSG_RESULT(using ALSA)
-    AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))])
-
-  # Look for PULSE flag
-  AC_ARG_WITH(pulse, [  --with-pulse = choose PulseAudio API support (linux only)])
-  AS_IF([test "x$with_pulse" == "xyes"], [
-    api="$api -D__LINUX_PULSE__"
-    req="$req libpulse-simple"
-    AC_MSG_RESULT(using PulseAudio)
-    AC_CHECK_LIB(pulse-simple, pa_simple_flush, , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!))])
-
-  # Look for OSS flag
-  AC_ARG_WITH(oss, [  --with-oss = choose OSS API support (unixes)])
-  AS_IF([test "x$with_oss" == "xyes"], [
-    api="$api -D__LINUX_OSS__"
-    AC_MSG_RESULT(using OSS)])
-
-  # If no audio api flags specified, use ALSA
-  if [test "$api" == "";] then
-    AC_MSG_RESULT(using ALSA)
-    AC_SUBST( api, [-D__LINUX_ALSA__] )
-    req="$req alsa"
-    AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))
-  fi
-
-  AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
-  ;;
-
-  *-apple*)
-  # Look for Core flag
-  AC_ARG_WITH(core, [  --with-core = choose CoreAudio API support (mac only)])
-  AS_IF([test "x$with_core" == "xyes"], [
-    api="$api -D__MACOSX_CORE__"
-    AC_MSG_RESULT(using CoreAudio)
-    AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] )
-    LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" ])
-
-  # If no audio api flags specified, use CoreAudio
-  if [test "$api" == ""; ] then
-    AC_SUBST( api, [-D__MACOSX_CORE__] )
-    AC_MSG_RESULT(using CoreAudio)
-    AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
-      [],
-      [AC_MSG_ERROR(CoreAudio header files not found!)] )
-    AC_SUBST( LIBS, ["-framework CoreAudio -framework CoreFoundation"] )
-  fi
-
-  AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
-  ;;
-
-  *-mingw32*)
-  AC_ARG_WITH(asio, [  --with-asio = choose ASIO API support (windoze only)])
-  AS_IF([test "x$with_asio" == "xyes"], [
-    api="$api -D__WINDOWS_ASIO__"
-    AC_MSG_RESULT(using ASIO)
-    AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) ])
-
-  # Look for DirectSound flag
-  AC_ARG_WITH(ds, [  --with-ds = choose DirectSound API support (windoze only)])
-  AS_IF([test "x$with_ds" == "xyes"], [
-    api="$api -D__WINDOWS_DS__"
-    AC_MSG_RESULT(using DirectSound)
-    LIBS="-ldsound -lwinmm $LIBS" ])
-
-  # Look for WASAPI flag
-  AC_ARG_WITH(wasapi, [  --with-wasapi = choose Windows Audio Session API support (windoze only)])
-  AS_IF([test "x$with_wasapi" == "xyes"], [
-    api="$api -D__WINDOWS_WASAPI__"
-    AC_MSG_RESULT(using WASAPI)
-    LIBS="-lwinmm -luuid -lksuser $LIBS" ])
-
-  # If no audio api flags specified, use DS
-  if [test "$api" == "";] then
-    AC_SUBST( api, [-D__WINDOWS_DS__] )
-    AC_MSG_RESULT(using DirectSound)
-    LIBS="-ldsound -lwinmm $LIBS"
-  fi
-
-  LIBS="-lole32 $LIBS"
-  ;;
-
-  *)
-  # Default case for unknown realtime systems.
-  AC_MSG_ERROR(Unknown system type for realtime support!)
-  ;;
-esac
+# Aggregate options into a single string.
+AS_IF([test "x$with_jack"   = "xyes"], [systems="$systems jack"])
+AS_IF([test "x$with_alsa"   = "xyes"], [systems="$systems alsa"])
+AS_IF([test "x$with_pulse"  = "xyes"], [systems="$systems pulse"])
+AS_IF([test "x$with_oss"    = "xyes"], [systems="$systems oss"])
+AS_IF([test "x$with_core"   = "xyes"], [systems="$systems core"])
+AS_IF([test "x$with_asio"   = "xyes"], [systems="$systems asio"])
+AS_IF([test "x$with_dsound" = "xyes"], [systems="$systems dsound"])
+AS_IF([test "x$with_wasapi" = "xyes"], [systems="$systems wasapi"])
+required=" $systems "
+
+# If none, assign defaults if any are known for this OS.
+# User must specified with-* options for any unknown OS.
+AS_IF([test "x$systems" = "x"],
+  AS_CASE([$host],
+    [*-*-netbsd*],   [systems="oss"],
+    [*-*-freebsd*],  [systems="oss"],
+    [*-*-linux*],    [systems="alsa pulse jack oss"],
+    [*-apple*],      [systems="core jack"],
+    [*-mingw32*],    [systems="asio dsound wasapi jack"],
+    [*-mingw64*],    [systems="asio dsound wasapi jack"],
+    [*-msys*],       [systems="asio dsound wasapi jack"],
+  ))
+
+# If any were specifically requested disabled, do it.
+AS_IF([test "x$with_jack"   = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v jack`])
+AS_IF([test "x$with_alsa"   = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v alsa`])
+AS_IF([test "x$with_pulse"  = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v pulse`])
+AS_IF([test "x$with_oss"    = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v oss`])
+AS_IF([test "x$with_core"   = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v core`])
+AS_IF([test "x$with_asio"   = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v asio`])
+AS_IF([test "x$with_dsound" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v dsound`])
+AS_IF([test "x$with_wasapi" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v wasapi`])
+systems=" `echo $systems|tr \\\\n ' '` "
+
+# For each audio system, check if it is selected and found.
+# Note: Order specified above is not necessarily respected. However,
+# *actual* priority is set at run-time, see RtAudio::openRtApi.
+# One AS_CASE per system, since they are not mutually-exclusive.
+
+AS_CASE(["$systems"], [*" alsa "*], [
+  AC_CHECK_LIB(asound, snd_pcm_open,
+    [api="$api -D__LINUX_ALSA__"
+     req="$req alsa"
+     need_pthread=yes
+     found="$found ALSA"
+     LIBS="-lasound $LIBS"],
+    AS_CASE(["$required"], [*" alsa "*],
+      AC_MSG_ERROR([ALSA support requires the asound library!])))
+])
+
+AS_CASE(["$systems"], [*" pulse "*], [
+  AC_CHECK_LIB(pulse-simple, pa_simple_flush,
+    [api="$api -D__LINUX_PULSE__"
+     req="$req libpulse-simple"
+     need_pthread=yes
+     found="$found PulseAudio"
+     LIBS="-lpulse-simple $LIBS"],
+    AS_CASE(["$required"], [*" pulse "*],
+      AC_MSG_ERROR([PulseAudio support requires the pulse-simple library!])))
+])
+
+AS_CASE(["$systems"], [*" oss "*], [
+  # libossaudio not required on some platforms (e.g. linux) so we
+  # don't break things if it's not found, but issue a warning when we
+  # are not sure (i.e. not on linux)
+  AS_CASE([$host], [*-*-linux*], [], [*], [need_ossaudio=yes])
+  AC_CHECK_LIB(ossaudio, main, [have_ossaudio=true],
+    AS_CASE(["$required"], [*" oss "*],
+      AS_IF([test "x$need_ossaudio" = xyes],
+        AC_MSG_WARN([RtAudio may require the ossaudio library]))))
+
+  # linux systems may have soundcard.h but *not* have OSS4 installed,
+  # we have to actually check if it exports OSS4 symbols
+  AC_CHECK_DECL(SNDCTL_SYSINFO,
+    [api="$api -D__LINUX_OSS__"
+     need_pthread=yes
+     found="$found OSS"],
+     AS_CASE(["$required"], [*" oss "*],
+       AC_MSG_ERROR([sys/soundcard.h not found]))
+    [],
+    [#include <sys/soundcard.h>])
+])
+
+AS_CASE(["$systems"], [*" jack "*], [
+  AC_CHECK_LIB(jack, jack_client_open,
+    [api="$api -D__UNIX_JACK__"
+     req="$req jack"
+     need_pthread=yes
+     found="$found JACK"
+     LIBS="-ljack $LIBS"],
+    AS_CASE(["$required"], [*" jack "*],
+      AC_MSG_ERROR([JACK support requires the jack library!])))
+])
+
+AS_CASE(["$systems"], [*" core "*], [
+  AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
+    [api="$api -D__MACOSX_CORE__"
+     need_pthread=yes
+     found="$found CoreAudio",
+     LIBS="$LIBS -framework CoreAudio -framework CoreFoundation"],
+    AS_CASE(["$required"], [*" core "*],
+      AC_MSG_ERROR([CoreAudio header files not found!])))
+])
+
+AS_CASE(["$systems"], [*" asio "*], [
+  api="$api -D__WINDOWS_ASIO__"
+  use_asio=yes
+  CPPFLAGS="-I$srcdir/include $CPPFLAGS"
+  need_ole32=yes
+  found="$found ASIO"
+])
+
+AS_CASE(["$systems"], [*" dsound "*], [
+  AC_CHECK_HEADERS(windows.h)
+  AC_CHECK_HEADERS(mmsystem.h mmreg.h dsound.h, [], [],
+[#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif])
+  AS_IF([test "x$ac_cv_header_windows_h" = xyes \
+      && test "x$ac_cv_header_mmsystem_h" = xyes \
+      && test "x$ac_cv_header_mmreg_h" = xyes \
+      && test "x$ac_cv_header_dsound_h" = xyes],
+    [api="$api -D__WINDOWS_DS__"
+     need_ole32=yes
+     found="$found DirectSound"
+     LIBS="-ldsound -lwinmm $LIBS"])
+])
+
+AS_CASE(["$systems"], [*" wasapi "*], [
+  AC_CHECK_HEADERS(windows.h)
+  AC_CHECK_HEADERS(audioclient.h avrt.h mmdeviceapi.h, [], [],
+[#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif])
+  AS_IF([test "x$ac_cv_header_windows_h" = xyes \
+      && test "x$ac_cv_header_audioclient_h" = xyes \
+      && test "x$ac_cv_header_avrt_h" = xyes \
+      && test "x$ac_cv_header_mmdeviceapi_h" = xyes],
+    [api="$api -D__WINDOWS_WASAPI__"
+     CPPFLAGS="-I$srcdir/include $CPPFLAGS"
+     need_ole32=yes
+     found="$found WASAPI"
+     LIBS="-lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid $LIBS"])
+])
+
+AS_IF([test -n "$need_ole32"], [LIBS="-lole32 $LIBS"])
+
+AS_IF([test -n "$need_pthread"],[
+  AC_MSG_CHECKING([for pthread])
+  AC_CHECK_LIB(pthread, pthread_create, ,
+    AC_MSG_ERROR([RtAudio requires the pthread library!]))])
+
+AC_MSG_CHECKING([for audio API])
+
+# Error case: no known realtime systems found.
+AS_IF([test x"$api" = "x"], [
+  AC_MSG_RESULT([none])
+  AC_MSG_ERROR([No known system type found for realtime support!])
+], [
+  AC_MSG_RESULT([$found])
+])
+
+AM_CONDITIONAL( ASIO, [test "x${use_asio}" = "xyes" ])
 
 CPPFLAGS="$CPPFLAGS $api"
 
diff --git a/contrib/go/rtaudio/rtaudio.go b/contrib/go/rtaudio/rtaudio.go
new file mode 100644 (file)
index 0000000..49815a3
--- /dev/null
@@ -0,0 +1,563 @@
+package rtaudio
+
+/*
+
+#cgo CXXFLAGS: -g
+#cgo LDFLAGS: -lstdc++ -g
+
+#cgo linux CXXFLAGS: -D__LINUX_ALSA__
+#cgo linux LDFLAGS: -lm -lasound -pthread
+
+#cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
+#cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
+
+#cgo jack CXXFLAGS: -D__UNIX_JACK__
+#cgo jack LDFLAGS: -ljack
+
+#cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
+#cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
+
+#cgo darwin CXXFLAGS: -D__MACOSX_CORE__
+#cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "rtaudio_stub.h"
+
+extern int goCallback(void *out, void *in, unsigned int nFrames,
+       double stream_time, rtaudio_stream_status_t status, void *userdata);
+
+static inline void cgoRtAudioOpenStream(rtaudio_t audio,
+       rtaudio_stream_parameters_t *output_params,
+       rtaudio_stream_parameters_t *input_params,
+       rtaudio_format_t format,
+       unsigned int sample_rate,
+       unsigned int *buffer_frames,
+       int cb_id,
+       rtaudio_stream_options_t *options) {
+               rtaudio_open_stream(audio, output_params, input_params,
+                       format, sample_rate, buffer_frames,
+                       goCallback, (void *)(uintptr_t)cb_id, options, NULL);
+}
+*/
+import "C"
+import (
+       "errors"
+       "sync"
+       "time"
+       "unsafe"
+)
+
+// API is an enumeration of available compiled APIs. Supported API include
+// Alsa/PulseAudio/OSS, Jack, CoreAudio, WASAPI/ASIO/DS and dummy API.
+type API C.rtaudio_api_t
+
+const (
+       // APIUnspecified looks for a working compiled API.
+       APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED
+       // APILinuxALSA uses the Advanced Linux Sound Architecture API.
+       APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA
+       // APILinuxPulse uses the Linux PulseAudio API.
+       APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE
+       // APILinuxOSS uses the Linux Open Sound System API.
+       APILinuxOSS = C.RTAUDIO_API_LINUX_OSS
+       // APIUnixJack uses the Jack Low-Latency Audio Server API.
+       APIUnixJack = C.RTAUDIO_API_UNIX_JACK
+       // APIMacOSXCore uses Macintosh OS-X Core Audio API.
+       APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE
+       // APIWindowsWASAPI uses the Microsoft WASAPI API.
+       APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI
+       // APIWindowsASIO uses the Steinberg Audio Stream I/O API.
+       APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO
+       // APIWindowsDS uses the Microsoft DirectSound API.
+       APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
+       // APIDummy is a compilable but non-functional API.
+       APIDummy = C.RTAUDIO_API_DUMMY
+)
+
+func (api API) String() string {
+       switch api {
+       case APIUnspecified:
+               return "unspecified"
+       case APILinuxALSA:
+               return "alsa"
+       case APILinuxPulse:
+               return "pulse"
+       case APILinuxOSS:
+               return "oss"
+       case APIUnixJack:
+               return "jack"
+       case APIMacOSXCore:
+               return "coreaudio"
+       case APIWindowsWASAPI:
+               return "wasapi"
+       case APIWindowsASIO:
+               return "asio"
+       case APIWindowsDS:
+               return "directsound"
+       case APIDummy:
+               return "dummy"
+       }
+       return "?"
+}
+
+// StreamStatus defines over- or underflow flags in the audio callback.
+type StreamStatus C.rtaudio_stream_status_t
+
+const (
+       // StatusInputOverflow indicates that data was discarded because of an
+       // overflow condition at the driver.
+       StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW
+       // StatusOutputUnderflow indicates that the output buffer ran low, likely
+       // producing a break in the output sound.
+       StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW
+)
+
+// Version returns current RtAudio library version string.
+func Version() string {
+       return C.GoString(C.rtaudio_version())
+}
+
+// CompiledAPI determines the available compiled audio APIs.
+func CompiledAPI() (apis []API) {
+       capis := (*[1 << 27]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api()))
+       for i := 0; ; i++ {
+               api := capis[i]
+               if api == C.RTAUDIO_API_UNSPECIFIED {
+                       break
+               }
+               apis = append(apis, API(api))
+       }
+       return apis
+}
+
+// DeviceInfo is the public device information structure for returning queried values.
+type DeviceInfo struct {
+       Name              string
+       Probed            bool
+       NumOutputChannels int
+       NumInputChannels  int
+       NumDuplexChannels int
+       IsDefaultOutput   bool
+       IsDefaultInput    bool
+
+       //rtaudio_format_t native_formats;
+
+       PreferredSampleRate uint
+       SampleRates         []int
+}
+
+// StreamParams is the structure for specifying input or output stream parameters.
+type StreamParams struct {
+       DeviceID     uint
+       NumChannels  uint
+       FirstChannel uint
+}
+
+// StreamFlags is a set of RtAudio stream option flags.
+type StreamFlags C.rtaudio_stream_flags_t
+
+const (
+       // FlagsNoninterleaved is set to use non-interleaved buffers (default = interleaved).
+       FlagsNoninterleaved = C.RTAUDIO_FLAGS_NONINTERLEAVED
+       // FlagsMinimizeLatency when set attempts to configure stream parameters for lowest possible latency.
+       FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY
+       // FlagsHogDevice when set attempts to grab device for exclusive use.
+       FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE
+       // FlagsScheduleRealtime is set in attempt to select realtime scheduling (round-robin) for the callback thread.
+       FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME
+       // FlagsAlsaUseDefault is set to use the "default" PCM device (ALSA only).
+       FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT
+)
+
+// StreamOptions is the structure for specifying stream options.
+type StreamOptions struct {
+       Flags      StreamFlags
+       NumBuffers uint
+       Priotity   int
+       Name       string
+}
+
+// RtAudio is a "controller" used to select an available audio i/o interface.
+type RtAudio interface {
+       Destroy()
+       CurrentAPI() API
+       Devices() ([]DeviceInfo, error)
+       DefaultOutputDevice() int
+       DefaultInputDevice() int
+
+       Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
+       Close()
+       Start() error
+       Stop() error
+       Abort() error
+
+       IsOpen() bool
+       IsRunning() bool
+
+       Latency() (int, error)
+       SampleRate() (uint, error)
+       Time() (time.Duration, error)
+       SetTime(time.Duration) error
+
+       ShowWarnings(bool)
+}
+
+type rtaudio struct {
+       audio          C.rtaudio_t
+       cb             Callback
+       inputChannels  int
+       outputChannels int
+       format         Format
+}
+
+var _ RtAudio = &rtaudio{}
+
+// Create a new RtAudio instance using the given API.
+func Create(api API) (RtAudio, error) {
+       audio := C.rtaudio_create(C.rtaudio_api_t(api))
+       if C.rtaudio_error(audio) != nil {
+               return nil, errors.New(C.GoString(C.rtaudio_error(audio)))
+       }
+       return &rtaudio{audio: audio}, nil
+}
+
+func (audio *rtaudio) Destroy() {
+       C.rtaudio_destroy(audio.audio)
+}
+
+func (audio *rtaudio) CurrentAPI() API {
+       return API(C.rtaudio_current_api(audio.audio))
+}
+
+func (audio *rtaudio) DefaultInputDevice() int {
+       return int(C.rtaudio_get_default_input_device(audio.audio))
+}
+
+func (audio *rtaudio) DefaultOutputDevice() int {
+       return int(C.rtaudio_get_default_output_device(audio.audio))
+}
+
+func (audio *rtaudio) Devices() ([]DeviceInfo, error) {
+       n := C.rtaudio_device_count(audio.audio)
+       devices := []DeviceInfo{}
+       for i := C.int(0); i < n; i++ {
+               cinfo := C.rtaudio_get_device_info(audio.audio, i)
+               if C.rtaudio_error(audio.audio) != nil {
+                       return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+               }
+               sr := []int{}
+               for _, r := range cinfo.sample_rates {
+                       if r == 0 {
+                               break
+                       }
+                       sr = append(sr, int(r))
+               }
+               devices = append(devices, DeviceInfo{
+                       Name:                C.GoString(&cinfo.name[0]),
+                       Probed:              cinfo.probed != 0,
+                       NumInputChannels:    int(cinfo.input_channels),
+                       NumOutputChannels:   int(cinfo.output_channels),
+                       NumDuplexChannels:   int(cinfo.duplex_channels),
+                       IsDefaultOutput:     cinfo.is_default_output != 0,
+                       IsDefaultInput:      cinfo.is_default_input != 0,
+                       PreferredSampleRate: uint(cinfo.preferred_sample_rate),
+                       SampleRates:         sr,
+               })
+               // TODO: formats
+       }
+       return devices, nil
+}
+
+// Format defines RtAudio data format type.
+type Format int
+
+const (
+       // FormatInt8 uses 8-bit signed integer.
+       FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8
+       // FormatInt16 uses 16-bit signed integer.
+       FormatInt16 = C.RTAUDIO_FORMAT_SINT16
+       // FormatInt24 uses 24-bit signed integer.
+       FormatInt24 = C.RTAUDIO_FORMAT_SINT24
+       // FormatInt32 uses 32-bit signed integer.
+       FormatInt32 = C.RTAUDIO_FORMAT_SINT32
+       // FormatFloat32 uses 32-bit floating point values normalized between (-1..1).
+       FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32
+       // FormatFloat64 uses 64-bit floating point values normalized between (-1..1).
+       FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64
+)
+
+// Buffer is a common interface for audio buffers of various data format types.
+type Buffer interface {
+       Len() int
+       Int8() []int8
+       Int16() []int16
+       Int24() []Int24
+       Int32() []int32
+       Float32() []float32
+       Float64() []float64
+}
+
+// Int24 is a helper type to convert int32 values to int24 and back.
+type Int24 [3]byte
+
+// Set Int24 value using the least significant bytes of the given number n.
+func (i *Int24) Set(n int32) {
+       (*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16)
+}
+
+// Get Int24 value as int32.
+func (i Int24) Get() int32 {
+       n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16
+       if n&0x800000 != 0 {
+               n |= ^0xffffff
+       }
+       return n
+}
+
+type buffer struct {
+       format      Format
+       length      int
+       numChannels int
+       ptr         unsafe.Pointer
+}
+
+func (b *buffer) Len() int {
+       if b.ptr == nil {
+               return 0
+       }
+       return b.length
+}
+
+func (b *buffer) Int8() []int8 {
+       if b.format != FormatInt8 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+func (b *buffer) Int16() []int16 {
+       if b.format != FormatInt16 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 29]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+func (b *buffer) Int24() []Int24 {
+       if b.format != FormatInt24 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 28]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+func (b *buffer) Int32() []int32 {
+       if b.format != FormatInt32 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 27]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+func (b *buffer) Float32() []float32 {
+       if b.format != FormatFloat32 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 27]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+func (b *buffer) Float64() []float64 {
+       if b.format != FormatFloat64 {
+               return nil
+       }
+       if b.ptr == nil {
+               return nil
+       }
+       return (*[1 << 23]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
+}
+
+// Callback is a client-defined function that will be invoked when input data
+// is available and/or output data is needed.
+type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int
+
+var (
+       mu     sync.Mutex
+       audios = map[int]*rtaudio{}
+)
+
+func registerAudio(a *rtaudio) int {
+       mu.Lock()
+       defer mu.Unlock()
+       for i := 0; ; i++ {
+               if _, ok := audios[i]; !ok {
+                       audios[i] = a
+                       return i
+               }
+       }
+}
+
+func unregisterAudio(a *rtaudio) {
+       mu.Lock()
+       defer mu.Unlock()
+       for i := 0; i < len(audios); i++ {
+               if audios[i] == a {
+                       delete(audios, i)
+                       return
+               }
+       }
+}
+
+func findAudio(k int) *rtaudio {
+       mu.Lock()
+       defer mu.Unlock()
+       return audios[k]
+}
+
+//export goCallback
+func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double,
+       status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int {
+
+       k := int(uintptr(userdata))
+       audio := findAudio(k)
+       dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0))
+       inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in}
+       outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out}
+       return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status)))
+}
+
+func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
+       frames uint, cb Callback, opts *StreamOptions) error {
+       var (
+               cInPtr   *C.rtaudio_stream_parameters_t
+               cOutPtr  *C.rtaudio_stream_parameters_t
+               cOptsPtr *C.rtaudio_stream_options_t
+               cIn      C.rtaudio_stream_parameters_t
+               cOut     C.rtaudio_stream_parameters_t
+               cOpts    C.rtaudio_stream_options_t
+       )
+
+       audio.inputChannels = 0
+       audio.outputChannels = 0
+       if out != nil {
+               audio.outputChannels = int(out.NumChannels)
+               cOut.device_id = C.uint(out.DeviceID)
+               cOut.num_channels = C.uint(out.NumChannels)
+               cOut.first_channel = C.uint(out.FirstChannel)
+               cOutPtr = &cOut
+       }
+       if in != nil {
+               audio.inputChannels = int(in.NumChannels)
+               cIn.device_id = C.uint(in.DeviceID)
+               cIn.num_channels = C.uint(in.NumChannels)
+               cIn.first_channel = C.uint(in.FirstChannel)
+               cInPtr = &cIn
+       }
+       if opts != nil {
+               cOpts.flags = C.rtaudio_stream_flags_t(opts.Flags)
+               cOpts.num_buffers = C.uint(opts.NumBuffers)
+               cOpts.priority = C.int(opts.Priotity)
+               cOptsPtr = &cOpts
+       }
+       framesCount := C.uint(frames)
+       audio.format = format
+       audio.cb = cb
+
+       k := registerAudio(audio)
+       C.cgoRtAudioOpenStream(audio.audio, cOutPtr, cInPtr,
+               C.rtaudio_format_t(format), C.uint(sampleRate), &framesCount, C.int(k), cOptsPtr)
+       if C.rtaudio_error(audio.audio) != nil {
+               return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return nil
+}
+
+func (audio *rtaudio) Close() {
+       unregisterAudio(audio)
+       C.rtaudio_close_stream(audio.audio)
+}
+
+func (audio *rtaudio) Start() error {
+       C.rtaudio_start_stream(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return nil
+}
+
+func (audio *rtaudio) Stop() error {
+       C.rtaudio_stop_stream(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return nil
+}
+
+func (audio *rtaudio) Abort() error {
+       C.rtaudio_abort_stream(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return nil
+}
+
+func (audio *rtaudio) IsOpen() bool {
+       return C.rtaudio_is_stream_open(audio.audio) != 0
+}
+
+func (audio *rtaudio) IsRunning() bool {
+       return C.rtaudio_is_stream_running(audio.audio) != 0
+}
+
+func (audio *rtaudio) Latency() (int, error) {
+       latency := C.rtaudio_get_stream_latency(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return int(latency), nil
+}
+
+func (audio *rtaudio) SampleRate() (uint, error) {
+       sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return uint(sampleRate), nil
+}
+
+func (audio *rtaudio) Time() (time.Duration, error) {
+       sec := C.rtaudio_get_stream_time(audio.audio)
+       if C.rtaudio_error(audio.audio) != nil {
+               return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
+}
+
+func (audio *rtaudio) SetTime(t time.Duration) error {
+       sec := float64(t) * 1000000.0 / float64(time.Microsecond)
+       C.rtaudio_set_stream_time(audio.audio, C.double(sec))
+       if C.rtaudio_error(audio.audio) != nil {
+               return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
+       }
+       return nil
+}
+
+func (audio *rtaudio) ShowWarnings(show bool) {
+       if show {
+               C.rtaudio_show_warnings(audio.audio, 1)
+       } else {
+               C.rtaudio_show_warnings(audio.audio, 0)
+       }
+}
diff --git a/contrib/go/rtaudio/rtaudio_stub.cpp b/contrib/go/rtaudio/rtaudio_stub.cpp
new file mode 100644 (file)
index 0000000..1db9da1
--- /dev/null
@@ -0,0 +1,4 @@
+#include "../../../RtAudio.h"
+
+#include "../../../RtAudio.cpp"
+#include "../../../rtaudio_c.cpp"
diff --git a/contrib/go/rtaudio/rtaudio_stub.h b/contrib/go/rtaudio/rtaudio_stub.h
new file mode 100644 (file)
index 0000000..7778f10
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../rtaudio_c.h"
diff --git a/contrib/go/rtaudio/rtaudio_test.go b/contrib/go/rtaudio/rtaudio_test.go
new file mode 100644 (file)
index 0000000..32475cf
--- /dev/null
@@ -0,0 +1,71 @@
+package rtaudio
+
+import (
+       "log"
+       "math"
+       "time"
+)
+
+func ExampleCompiledAPI() {
+       log.Println("RtAudio version: ", Version())
+       for _, api := range CompiledAPI() {
+               log.Println("Compiled API: ", api)
+       }
+}
+
+func ExampleRtAudio_Devices() {
+       audio, err := Create(APIUnspecified)
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer audio.Destroy()
+       devices, err := audio.Devices()
+       if err != nil {
+               log.Fatal(err)
+       }
+       for _, d := range devices {
+               log.Printf("Audio device: %#v\n", d)
+       }
+}
+
+func ExampleRtAudio_Open() {
+       const (
+               sampleRate = 44100
+               bufSz      = 512
+               freq       = 440.0
+       )
+       phase := 0.0
+       audio, err := Create(APIUnspecified)
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer audio.Destroy()
+
+       params := StreamParams{
+               DeviceID:     uint(audio.DefaultOutputDevice()),
+               NumChannels:  2,
+               FirstChannel: 0,
+       }
+       options := StreamOptions{
+               Flags: FlagsAlsaUseDefault,
+       }
+       cb := func(out, in Buffer, dur time.Duration, status StreamStatus) int {
+               samples := out.Float32()
+               for i := 0; i < len(samples)/2; i++ {
+                       sample := float32(math.Sin(2 * math.Pi * phase))
+                       phase += freq / sampleRate
+
+                       samples[i*2] = sample
+                       samples[i*2+1] = sample
+               }
+               return 0
+       }
+       err = audio.Open(&params, nil, FormatFloat32, sampleRate, bufSz, cb, &options)
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer audio.Close()
+       audio.Start()
+       defer audio.Stop()
+       time.Sleep(3 * time.Second)
+}
index 29dcbd471afbf460e5d442521f0a818f166df6af..e5c4a9e203e05dbcc940e44ff5079e9e77804065 100644 (file)
@@ -61,7 +61,7 @@ In order to compile RtAudio for a specific OS and audio API, it is necessary to
 </TR>
 <TR>
   <TD>Windows</TD>
-  <TD>Direct Sound</TD>
+  <TD>DirectSound</TD>
   <TD>RtApiDs</TD>
   <TD>__WINDOWS_DS__</TD>
   <TD><TT>dsound.lib (ver. 5.0 or higher), multithreaded</TT></TD>
@@ -80,8 +80,8 @@ In order to compile RtAudio for a specific OS and audio API, it is necessary to
   <TD>WASAPI</TD>
   <TD>RtApiWasapi</TD>
   <TD>__WINDOWS_WASAPI__</TD>
-  <TD>MinGW: <TT>FunctionDiscoveryKeys_devpkey.h, lksuser, luuid, lwinmm, lole32</TT></TD>
-  <TD>MinGW: <TT>g++ -Wall -D__WINDOWS_WASAPI__ -Iinclude -o audioprobe audioprobe.cpp RtAudio.cpp -lole32 -lwinmm -lksuser -luuid</TT></TD>
+  <TD>MinGW: <TT>FunctionDiscoveryKeys_devpkey.h, lksuser, lmfplat, lmfuuid, lwmcodecdspuuid, lwinmm, lole32</TT></TD>
+  <TD>MinGW: <TT>g++ -Wall -D__WINDOWS_WASAPI__ -Iinclude -o audioprobe audioprobe.cpp RtAudio.cpp -lole32 -lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid</TT></TD>
 </TR>
 </TABLE>
 <P>
index 975ff0eda069d7df9744c5d8eef5d63b3d1af97a..69970009f29f02715625a2912cf0421ca3c28351 100644 (file)
@@ -45,7 +45,7 @@ The RtAudio::DeviceInfo structure is defined in RtAudio.h and provides a variety
     bool isDefaultOutput;                  // true if this is the default output device.
     bool isDefaultInput;                   // true if this is the default input device.
     std::vector<unsigned int> sampleRates; // Supported sample rates.
-    unsigned int preferredSampleRate;      // Preferred sample rate, eg. for WASAPI the system sample rate.
+    unsigned int preferredSampleRate;      // Preferred sample rate, e.g. for WASAPI the system sample rate.
     RtAudioFormat nativeFormats;           // Bit mask of supported data formats.
   };
 \endcode
diff --git a/include/FunctionDiscoveryKeys_devpkey.h b/include/FunctionDiscoveryKeys_devpkey.h
deleted file mode 100644 (file)
index 854244d..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-#pragma once
-
-/*++
-
-Copyright (c) Microsoft Corporation.  All rights reserved.
-
-Module Name:
-
-    devpkey.h
-
-Abstract:
-
-    Defines property keys for the Plug and Play Device Property API.
-
-Author:
-
-    Jim Cavalaris (jamesca) 10-14-2003
-
-Environment:
-
-    User-mode only.
-
-Revision History:
-
-    14-October-2003     jamesca
-
-        Creation and initial implementation.
-
-    20-June-2006        dougb
-
-        Copied Jim's version replaced "DEFINE_DEVPROPKEY(DEVPKEY_" with "DEFINE_PROPERTYKEY(PKEY_"
-    
---*/
-
-//#include <devpropdef.h>
-
-//
-// _NAME
-//
-
-DEFINE_PROPERTYKEY(PKEY_NAME,                          0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10);    // DEVPROP_TYPE_STRING
-
-//
-// Device properties
-// These PKEYs correspond to the old setupapi SPDRP_XXX properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc,             0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_HardwareIds,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_CompatibleIds,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 4);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Service,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 6);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Class,                  0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 9);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ClassGuid,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 10);    // DEVPROP_TYPE_GUID
-DEFINE_PROPERTYKEY(PKEY_Device_Driver,                 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 11);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ConfigFlags,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 12);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Manufacturer,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_LocationInfo,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 15);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_PDOName,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 16);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Capabilities,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 17);    // DEVPROP_TYPE_UNINT32
-DEFINE_PROPERTYKEY(PKEY_Device_UINumber,               0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 18);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_UpperFilters,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 19);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_LowerFilters,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 20);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BusTypeGuid,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 21);    // DEVPROP_TYPE_GUID
-DEFINE_PROPERTYKEY(PKEY_Device_LegacyBusType,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 22);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_BusNumber,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 23);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName,         0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Security,               0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 25);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR
-DEFINE_PROPERTYKEY(PKEY_Device_SecuritySDS,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 26);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DevType,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 27);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Exclusive,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 28);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Characteristics,        0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 29);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Address,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 30);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_UINumberDescFormat,     0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 31);    // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_PowerData,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 32);    // DEVPROP_TYPE_BINARY
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicy,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 33);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyDefault,   0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 34);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyOverride,  0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 35);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_InstallState,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 36);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_LocationPaths,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 37);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BaseContainerId,        0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 38);    // DEVPROP_TYPE_GUID
-
-//
-// Device properties
-// These PKEYs correspond to a device's status and problem code
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DevNodeStatus,          0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 2);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_ProblemCode,            0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 3);     // DEVPROP_TYPE_UINT32
-
-//
-// Device properties
-// These PKEYs correspond to device relations
-//
-DEFINE_PROPERTYKEY(PKEY_Device_EjectionRelations,      0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 4);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalRelations,       0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 5);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_PowerRelations,         0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 6);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BusRelations,           0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 7);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Parent,                 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Children,               0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 9);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Siblings,               0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 10);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_TransportRelations,     0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 11);    // DEVPROP_TYPE_STRING_LIST
-
-//
-// Other Device properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_Reported,               0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 2);     // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_Legacy,                 0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 3);     // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,             0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256);   // DEVPROP_TYPE_STRING
-
-DEFINE_PROPERTYKEY(PKEY_Device_ContainerId,            0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2);     // DEVPROP_TYPE_GUID
-
-DEFINE_PROPERTYKEY(PKEY_Device_ModelId,                0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 2);     // DEVPROP_TYPE_GUID
-
-DEFINE_PROPERTYKEY(PKEY_Device_FriendlyNameAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 3);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_ManufacturerAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 4);     // DEVPROP_TYPE_UINT32
-
-DEFINE_PROPERTYKEY(PKEY_Device_PresenceNotForDevice,   0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 5);     // DEVPROP_TYPE_BOOLEAN
-
-
-DEFINE_PROPERTYKEY(PKEY_Numa_Proximity_Domain,         0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 1);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_DHP_Rebalance_Policy,   0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 2);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Numa_Node,              0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 3);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_BusReportedDeviceDesc,  0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4);     // DEVPROP_TYPE_STRING
-
-DEFINE_PROPERTYKEY(PKEY_Device_InstallInProgress,      0x83da6326, 0x97a6, 0x4088, 0x94, 0x53, 0xa1, 0x92, 0x3f, 0x57, 0x3b, 0x29, 9);     // DEVPROP_TYPE_BOOLEAN
-
-//
-// Device driver properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DriverDate,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2);      // DEVPROP_TYPE_FILETIME
-DEFINE_PROPERTYKEY(PKEY_Device_DriverVersion,          0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverDesc,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 4);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfPath,          0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 5);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSection,       0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 6);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSectionExt,    0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 7);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_MatchingDeviceId,       0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 8);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverProvider,         0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 9);      // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverPropPageProvider, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 10);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverCoInstallers,     0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 11);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerTags,     0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 12);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerExceptions, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 13); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverRank,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 14);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_DriverLogoLevel,        0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 15);     // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_NoConnectSound,         0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 17);     // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_GenericDriverInstalled, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 18);     // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_AdditionalSoftwareRequested, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 19);// DEVPROP_TYPE_BOOLEAN
-
-//
-// Device safe-removal properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequired,    0xafd97640,  0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 2);    // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequiredOverride, 0xafd97640,  0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 3);// DEVPROP_TYPE_BOOLEAN
-
-
-//
-// Device properties that were set by the driver package that was installed
-// on the device.
-//
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_Model,                  0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 2);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_VendorWebSite,          0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 3);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_DetailedDescription,    0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 4);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_DocumentationLink,      0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 5);     // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_Icon,                   0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 6);     // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_BrandingIcon,           0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 7);     // DEVPROP_TYPE_STRING_LIST
-
-//
-// Device setup class properties
-// These PKEYs correspond to the old setupapi SPCRP_XXX properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_UpperFilters,      0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 19);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_LowerFilters,      0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 20);    // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Security,          0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 25);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_SecuritySDS,       0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 26);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_DevType,           0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 27);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Exclusive,         0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 28);    // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Characteristics,   0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 29);    // DEVPROP_TYPE_UINT32
-
-//
-// Device setup class properties
-// These PKEYs correspond to registry values under the device class GUID key
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Name,              0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 2);  // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassName,         0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 3);  // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Icon,              0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 4);  // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassInstaller,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 5);  // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_PropPageProvider,  0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 6);  // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoInstallClass,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 7);  // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoDisplayClass,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 8);  // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_SilentInstall,     0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 9);  // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoUseClass,        0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 10); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_DefaultService,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 11); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_IconPath,          0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 12); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Other Device setup class properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassCoInstallers, 0x713d1703, 0xa2e2, 0x49f5, 0x92, 0x14, 0x56, 0x47, 0x2e, 0xf3, 0xda, 0x5c, 2); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Device interface properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName,  0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Enabled,       0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 3); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_ClassGuid,     0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 4); // DEVPROP_TYPE_GUID
-
-//
-// Device interface class properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceInterfaceClass_DefaultInterface,  0x14c83a99, 0x0b3f, 0x44b7, 0xbe, 0x4c, 0xa1, 0x78, 0xd3, 0x99, 0x05, 0x64, 2); // DEVPROP_TYPE_STRING
-
-
-
-
index 8ec811f8fe08f5a1ecd0f62bf30e4a8792867bd0..c74199f02bc2619ae48cc00945248d3376297eb0 100644 (file)
@@ -3,12 +3,14 @@
 \r
 /*\r
        Steinberg Audio Stream I/O API\r
-       (c) 1997 - 2005, Steinberg Media Technologies GmbH\r
+       (c) 1997 - 2013, Steinberg Media Technologies GmbH\r
 \r
-       ASIO Interface Specification v 2.1\r
+       ASIO Interface Specification v 2.3\r
 \r
        2005 - Added support for DSD sample data (in cooperation with Sony)\r
-\r
+       2012 - Added support for drop out detection\r
+               \r
+       \r
 \r
        basic concept is an i/o synchronous double-buffer scheme:\r
        \r
@@ -916,13 +918,19 @@ enum
        kAsioCanInputMeter,\r
        kAsioCanOutputGain,\r
        kAsioCanOutputMeter,\r
-\r
+       kAsioOptionalOne,\r
+       \r
        //      DSD support\r
        //      The following extensions are required to allow switching\r
        //      and control of the DSD subsystem.\r
        kAsioSetIoFormat                        = 0x23111961,           /* ASIOIoFormat * in params.                    */\r
        kAsioGetIoFormat                        = 0x23111983,           /* ASIOIoFormat * in params.                    */\r
        kAsioCanDoIoFormat                      = 0x23112004,           /* ASIOIoFormat * in params.                    */\r
+       \r
+       // Extension for drop out detection\r
+       kAsioCanReportOverload                  = 0x24042012,   /* return ASE_SUCCESS if driver can detect and report overloads */\r
+       \r
+       kAsioGetInternalBufferSamples   = 0x25042012    /* ASIOInternalBufferInfo * in params. Deliver size of driver internal buffering, return ASE_SUCCESS if supported */\r
 };\r
 \r
 typedef struct ASIOInputMonitor\r
@@ -1003,6 +1011,14 @@ typedef struct ASIOIoFormat_s
        char                            future[512-sizeof(ASIOIoFormatType)];\r
 } ASIOIoFormat;\r
 \r
+// Extension for drop detection\r
+// Note: Refers to buffering that goes beyond the double buffer e.g. used by USB driver designs\r
+typedef struct ASIOInternalBufferInfo\r
+{\r
+       long inputSamples;                      // size of driver's internal input buffering which is included in getLatencies\r
+       long outputSamples;                     // size of driver's internal output buffering which is included in getLatencies\r
+} ASIOInternalBufferInfo;\r
+\r
 \r
 ASIOError ASIOOutputReady(void);\r
 /* Purpose:\r
index 37f7a48a170e6eb4bfd8a4f45cfacd7b07ec6336..003cf1af7e38b90a5eda09dec6d50f3b7b2d19d1 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __asiosys__\r
        #define __asiosys__\r
 \r
-       #ifdef WIN32\r
+       #if defined(_WIN32) || defined(_WIN64)\r
                #undef MAC \r
                #define PPC 0\r
                #define WINDOWS 1\r
diff --git a/include/functiondiscoverykeys_devpkey.h b/include/functiondiscoverykeys_devpkey.h
new file mode 100644 (file)
index 0000000..854244d
--- /dev/null
@@ -0,0 +1,212 @@
+#pragma once
+
+/*++
+
+Copyright (c) Microsoft Corporation.  All rights reserved.
+
+Module Name:
+
+    devpkey.h
+
+Abstract:
+
+    Defines property keys for the Plug and Play Device Property API.
+
+Author:
+
+    Jim Cavalaris (jamesca) 10-14-2003
+
+Environment:
+
+    User-mode only.
+
+Revision History:
+
+    14-October-2003     jamesca
+
+        Creation and initial implementation.
+
+    20-June-2006        dougb
+
+        Copied Jim's version replaced "DEFINE_DEVPROPKEY(DEVPKEY_" with "DEFINE_PROPERTYKEY(PKEY_"
+    
+--*/
+
+//#include <devpropdef.h>
+
+//
+// _NAME
+//
+
+DEFINE_PROPERTYKEY(PKEY_NAME,                          0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10);    // DEVPROP_TYPE_STRING
+
+//
+// Device properties
+// These PKEYs correspond to the old setupapi SPDRP_XXX properties
+//
+DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc,             0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_HardwareIds,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_CompatibleIds,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 4);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_Service,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 6);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_Class,                  0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 9);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_ClassGuid,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 10);    // DEVPROP_TYPE_GUID
+DEFINE_PROPERTYKEY(PKEY_Device_Driver,                 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 11);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_ConfigFlags,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 12);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_Manufacturer,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_LocationInfo,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 15);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_PDOName,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 16);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_Capabilities,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 17);    // DEVPROP_TYPE_UNINT32
+DEFINE_PROPERTYKEY(PKEY_Device_UINumber,               0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 18);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_UpperFilters,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 19);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_LowerFilters,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 20);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_BusTypeGuid,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 21);    // DEVPROP_TYPE_GUID
+DEFINE_PROPERTYKEY(PKEY_Device_LegacyBusType,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 22);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_BusNumber,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 23);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName,         0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_Security,               0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 25);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR
+DEFINE_PROPERTYKEY(PKEY_Device_SecuritySDS,            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 26);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DevType,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 27);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_Exclusive,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 28);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_Characteristics,        0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 29);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_Address,                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 30);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_UINumberDescFormat,     0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 31);    // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_PowerData,              0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 32);    // DEVPROP_TYPE_BINARY
+DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicy,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 33);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyDefault,   0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 34);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyOverride,  0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 35);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_InstallState,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 36);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_LocationPaths,          0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 37);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_BaseContainerId,        0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 38);    // DEVPROP_TYPE_GUID
+
+//
+// Device properties
+// These PKEYs correspond to a device's status and problem code
+//
+DEFINE_PROPERTYKEY(PKEY_Device_DevNodeStatus,          0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 2);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_ProblemCode,            0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 3);     // DEVPROP_TYPE_UINT32
+
+//
+// Device properties
+// These PKEYs correspond to device relations
+//
+DEFINE_PROPERTYKEY(PKEY_Device_EjectionRelations,      0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 4);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_RemovalRelations,       0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 5);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_PowerRelations,         0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 6);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_BusRelations,           0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 7);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_Parent,                 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_Children,               0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 9);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_Siblings,               0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 10);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_TransportRelations,     0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 11);    // DEVPROP_TYPE_STRING_LIST
+
+//
+// Other Device properties
+//
+DEFINE_PROPERTYKEY(PKEY_Device_Reported,               0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 2);     // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_Device_Legacy,                 0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 3);     // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,             0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256);   // DEVPROP_TYPE_STRING
+
+DEFINE_PROPERTYKEY(PKEY_Device_ContainerId,            0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2);     // DEVPROP_TYPE_GUID
+
+DEFINE_PROPERTYKEY(PKEY_Device_ModelId,                0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 2);     // DEVPROP_TYPE_GUID
+
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyNameAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 3);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_ManufacturerAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 4);     // DEVPROP_TYPE_UINT32
+
+DEFINE_PROPERTYKEY(PKEY_Device_PresenceNotForDevice,   0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 5);     // DEVPROP_TYPE_BOOLEAN
+
+
+DEFINE_PROPERTYKEY(PKEY_Numa_Proximity_Domain,         0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 1);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_DHP_Rebalance_Policy,   0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 2);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_Numa_Node,              0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 3);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_BusReportedDeviceDesc,  0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4);     // DEVPROP_TYPE_STRING
+
+DEFINE_PROPERTYKEY(PKEY_Device_InstallInProgress,      0x83da6326, 0x97a6, 0x4088, 0x94, 0x53, 0xa1, 0x92, 0x3f, 0x57, 0x3b, 0x29, 9);     // DEVPROP_TYPE_BOOLEAN
+
+//
+// Device driver properties
+//
+DEFINE_PROPERTYKEY(PKEY_Device_DriverDate,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2);      // DEVPROP_TYPE_FILETIME
+DEFINE_PROPERTYKEY(PKEY_Device_DriverVersion,          0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverDesc,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 4);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverInfPath,          0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 5);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSection,       0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 6);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSectionExt,    0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 7);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_MatchingDeviceId,       0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 8);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverProvider,         0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 9);      // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverPropPageProvider, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 10);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverCoInstallers,     0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 11);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerTags,     0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 12);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerExceptions, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 13); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_Device_DriverRank,             0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 14);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_DriverLogoLevel,        0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 15);     // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_Device_NoConnectSound,         0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 17);     // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_Device_GenericDriverInstalled, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 18);     // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_Device_AdditionalSoftwareRequested, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 19);// DEVPROP_TYPE_BOOLEAN
+
+//
+// Device safe-removal properties
+//
+DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequired,    0xafd97640,  0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 2);    // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequiredOverride, 0xafd97640,  0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 3);// DEVPROP_TYPE_BOOLEAN
+
+
+//
+// Device properties that were set by the driver package that was installed
+// on the device.
+//
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_Model,                  0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 2);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_VendorWebSite,          0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 3);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_DetailedDescription,    0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 4);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_DocumentationLink,      0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 5);     // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_Icon,                   0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 6);     // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_DrvPkg_BrandingIcon,           0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 7);     // DEVPROP_TYPE_STRING_LIST
+
+//
+// Device setup class properties
+// These PKEYs correspond to the old setupapi SPCRP_XXX properties
+//
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_UpperFilters,      0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 19);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_LowerFilters,      0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 20);    // DEVPROP_TYPE_STRING_LIST
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_Security,          0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 25);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_SecuritySDS,       0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 26);    // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_DevType,           0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 27);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_Exclusive,         0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 28);    // DEVPROP_TYPE_UINT32
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_Characteristics,   0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 29);    // DEVPROP_TYPE_UINT32
+
+//
+// Device setup class properties
+// These PKEYs correspond to registry values under the device class GUID key
+//
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_Name,              0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 2);  // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassName,         0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 3);  // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_Icon,              0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 4);  // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassInstaller,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 5);  // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_PropPageProvider,  0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 6);  // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoInstallClass,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 7);  // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoDisplayClass,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 8);  // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_SilentInstall,     0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 9);  // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoUseClass,        0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 10); // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_DefaultService,    0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 11); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_IconPath,          0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 12); // DEVPROP_TYPE_STRING_LIST
+
+//
+// Other Device setup class properties
+//
+DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassCoInstallers, 0x713d1703, 0xa2e2, 0x49f5, 0x92, 0x14, 0x56, 0x47, 0x2e, 0xf3, 0xda, 0x5c, 2); // DEVPROP_TYPE_STRING_LIST
+
+//
+// Device interface properties
+//
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName,  0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Enabled,       0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 3); // DEVPROP_TYPE_BOOLEAN
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_ClassGuid,     0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 4); // DEVPROP_TYPE_GUID
+
+//
+// Device interface class properties
+//
+DEFINE_PROPERTYKEY(PKEY_DeviceInterfaceClass_DefaultInterface,  0x14c83a99, 0x0b3f, 0x44b7, 0xbe, 0x4c, 0xa1, 0x78, 0xd3, 0x99, 0x05, 0x64, 2); // DEVPROP_TYPE_STRING
+
+
+
+
index b627dc2e7652f4ace60eea85c6679cca51ab56df..d6dda0dec9edeebb5d7a93111a6a251032e0967f 100644 (file)
@@ -8,7 +8,7 @@
        //\r
        #define ASIO_BIG_ENDIAN 1\r
        #define ASIO_CPU_MIPS 1\r
-#elif defined WIN32\r
+#elif defined(_WIN32) || defined(_WIN64)\r
        #undef BEOS \r
        #undef MAC \r
        #undef SGI\r
index f63cf38661f23641942c483162e22af46f65519f..ea4e121ebee4330712fffa0fd1c83c7348e6b708 100644 (file)
@@ -28,7 +28,7 @@ A few options can be passed to configure (or the autogen.sh script), including:
 
 Typing "./configure --help" will display all the available options.  Note that you can provide more than one "--with-" flag to the configure script to enable multiple API support.
 
-If you wish to use a different compiler than that selected by configure, specify that compiler in the command line (ex. to use CC):
+If you wish to use a different compiler than that selected by configure, specify that compiler in the command line (e.g. to use CC):
 
   ./configure CXX=CC
 
diff --git a/readme b/readme
deleted file mode 100644 (file)
index fcd7063..0000000
--- a/readme
+++ /dev/null
@@ -1,61 +0,0 @@
-RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO and WASAPI) operating systems.
-
-By Gary P. Scavone, 2001-2017 (and many other developers!)
-
-This distribution of RtAudio contains the following:
-
-doc:      RtAudio documentation (see doc/html/index.html)
-tests:    example RtAudio programs
-include:  header and source files necessary for ASIO, DS & OSS compilation
-tests/Windows: Visual C++ .net test program workspace and projects
-
-OVERVIEW:
-
-RtAudio is a set of C++ classes that provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X and Windows (DirectSound, ASIO and WASAPI) operating systems.  RtAudio significantly simplifies the process of interacting with computer audio hardware.  It was designed with the following objectives:
-
-  - object-oriented C++ design
-  - simple, common API across all supported platforms
-  - only one source and one header file for easy inclusion in programming projects
-  - allow simultaneous multi-api support
-  - support dynamic connection of devices
-  - provide extensive audio device parameter control
-  - allow audio device capability probing
-  - automatic internal conversion for data format, channel number compensation, (de)interleaving, and byte-swapping
-
-RtAudio incorporates the concept of audio streams, which represent audio output (playback) and/or input (recording).  Available audio devices and their capabilities can be enumerated and then specified when opening a stream.  Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance.  See the \ref apinotes section for information specific to each of the supported audio APIs.
-
-FURTHER READING:
-
-For complete documentation on RtAudio, see the doc directory of the distribution or surf to http://www.music.mcgill.ca/~gary/rtaudio/.
-
-
-LEGAL AND ETHICAL:
-
-The RtAudio license is similar to the MIT License.
-
-    RtAudio: a set of realtime audio i/o C++ classes
-    Copyright (c) 2001-2017 Gary P. Scavone
-
-    Permission is hereby granted, free of charge, to any person
-    obtaining a copy of this software and associated documentation files
-    (the "Software"), to deal in the Software without restriction,
-    including without limitation the rights to use, copy, modify, merge,
-    publish, distribute, sublicense, and/or sell copies of the Software,
-    and to permit persons to whom the Software is furnished to do so,
-    subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be
-    included in all copies or substantial portions of the Software.
-
-    Any person wishing to distribute modifications to the Software is
-    asked to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.  This is,
-    however, not a binding provision of this license.
-
-    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.
index e0aa401457f2a8827f9c99ba02f399887fd3340e..0e81090dfede61890e45f2b1e01748cdad16b658 100644 (file)
@@ -9,4 +9,4 @@ Version: @PACKAGE_VERSION@
 Requires: @req@ 
 Libs: -L${libdir} -lrtaudio
 Libs.private: -lpthread
-Cflags: -pthread -I${includedir} @CPPFLAGS@
+Cflags: -pthread -I${includedir} @api@
diff --git a/rtaudio_c.cpp b/rtaudio_c.cpp
new file mode 100644 (file)
index 0000000..ad91bab
--- /dev/null
@@ -0,0 +1,261 @@
+#include "rtaudio_c.h"
+#include "RtAudio.h"
+
+#include <cstring>
+
+#define MAX_ERROR_MESSAGE_LENGTH 512
+
+struct rtaudio {
+  RtAudio *audio;
+
+  rtaudio_cb_t cb;
+  void *userdata;
+
+  int has_error;
+  char errmsg[MAX_ERROR_MESSAGE_LENGTH];
+};
+
+const char *rtaudio_version() { return RTAUDIO_VERSION; }
+
+extern "C" const RtAudio::Api rtaudio_compiled_apis[];
+const rtaudio_api_t *rtaudio_compiled_api() {
+  return (rtaudio_api_t *) &rtaudio_compiled_apis[0];
+}
+
+extern "C" const unsigned int rtaudio_num_compiled_apis;
+unsigned int rtaudio_get_num_compiled_apis(void) {
+  return rtaudio_num_compiled_apis;
+}
+
+extern "C" const char* rtaudio_api_names[][2];
+const char *rtaudio_api_name(rtaudio_api_t api) {
+    if (api < 0 || api >= RTAUDIO_API_NUM)
+        return NULL;
+    return rtaudio_api_names[api][0];
+}
+
+const char *rtaudio_api_display_name(rtaudio_api_t api)
+{
+    if (api < 0 || api >= RTAUDIO_API_NUM)
+        return "Unknown";
+    return rtaudio_api_names[api][1];
+}
+
+rtaudio_api_t rtaudio_compiled_api_by_name(const char *name) {
+    RtAudio::Api api = RtAudio::UNSPECIFIED;
+    if (name) {
+        api = RtAudio::getCompiledApiByName(name);
+    }
+    return (rtaudio_api_t)api;
+}
+
+const char *rtaudio_error(rtaudio_t audio) {
+  if (audio->has_error) {
+    return audio->errmsg;
+  }
+  return NULL;
+}
+
+rtaudio_t rtaudio_create(rtaudio_api_t api) {
+  rtaudio_t audio = new struct rtaudio();
+  try {
+    audio->audio = new RtAudio((RtAudio::Api)api);
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+  return audio;
+}
+
+void rtaudio_destroy(rtaudio_t audio) { delete audio->audio; }
+
+rtaudio_api_t rtaudio_current_api(rtaudio_t audio) {
+  return (rtaudio_api_t)audio->audio->getCurrentApi();
+}
+
+int rtaudio_device_count(rtaudio_t audio) {
+  return audio->audio->getDeviceCount();
+}
+
+rtaudio_device_info_t rtaudio_get_device_info(rtaudio_t audio, int i) {
+  rtaudio_device_info_t result;
+  std::memset(&result, 0, sizeof(result));
+  try {
+    audio->has_error = 0;
+    RtAudio::DeviceInfo info = audio->audio->getDeviceInfo(i);
+    result.probed = info.probed;
+    result.output_channels = info.outputChannels;
+    result.input_channels = info.inputChannels;
+    result.duplex_channels = info.duplexChannels;
+    result.is_default_output = info.isDefaultOutput;
+    result.is_default_input = info.isDefaultInput;
+    result.native_formats = info.nativeFormats;
+    result.preferred_sample_rate = info.preferredSampleRate;
+    strncpy(result.name, info.name.c_str(), sizeof(result.name) - 1);
+    for (unsigned int j = 0; j < info.sampleRates.size(); j++) {
+      if (j < sizeof(result.sample_rates) / sizeof(result.sample_rates[0])) {
+        result.sample_rates[j] = info.sampleRates[j];
+      }
+    }
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+  return result;
+}
+
+unsigned int rtaudio_get_default_output_device(rtaudio_t audio) {
+  return audio->audio->getDefaultOutputDevice();
+}
+
+unsigned int rtaudio_get_default_input_device(rtaudio_t audio) {
+  return audio->audio->getDefaultInputDevice();
+}
+
+static int proxy_cb_func(void *out, void *in, unsigned int nframes, double time,
+                         RtAudioStreamStatus status, void *userdata) {
+  rtaudio_t audio = (rtaudio_t)userdata;
+  return audio->cb(out, in, nframes, time, (rtaudio_stream_status_t)status,
+                   audio->userdata);
+}
+
+int rtaudio_open_stream(rtaudio_t audio,
+                        rtaudio_stream_parameters_t *output_params,
+                        rtaudio_stream_parameters_t *input_params,
+                        rtaudio_format_t format, unsigned int sample_rate,
+                        unsigned int *buffer_frames, rtaudio_cb_t cb,
+                        void *userdata, rtaudio_stream_options_t *options,
+                        rtaudio_error_cb_t /*errcb*/) {
+  try {
+    audio->has_error = 0;
+    RtAudio::StreamParameters *in = NULL;
+    RtAudio::StreamParameters *out = NULL;
+    RtAudio::StreamOptions *opts = NULL;
+
+    RtAudio::StreamParameters inparams;
+    RtAudio::StreamParameters outparams;
+    RtAudio::StreamOptions stream_opts;
+
+    if (input_params != NULL) {
+      inparams.deviceId = input_params->device_id;
+      inparams.nChannels = input_params->num_channels;
+      inparams.firstChannel = input_params->first_channel;
+      in = &inparams;
+    }
+    if (output_params != NULL) {
+      outparams.deviceId = output_params->device_id;
+      outparams.nChannels = output_params->num_channels;
+      outparams.firstChannel = output_params->first_channel;
+      out = &outparams;
+    }
+
+    if (options != NULL) {
+      stream_opts.flags = (RtAudioStreamFlags)options->flags;
+      stream_opts.numberOfBuffers = options->num_buffers;
+      stream_opts.priority = options->priority;
+      if (strlen(options->name) > 0) {
+        stream_opts.streamName = std::string(options->name);
+      }
+      opts = &stream_opts;
+    }
+    audio->cb = cb;
+    audio->userdata = userdata;
+    audio->audio->openStream(out, in, (RtAudioFormat)format, sample_rate,
+                             buffer_frames, proxy_cb_func, (void *)audio, opts,
+                             NULL);
+    return 0;
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+    return -1;
+  }
+}
+
+void rtaudio_close_stream(rtaudio_t audio) { audio->audio->closeStream(); }
+
+int rtaudio_start_stream(rtaudio_t audio) {
+  try {
+    audio->has_error = 0;
+    audio->audio->startStream();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+  return 0;
+}
+
+int rtaudio_stop_stream(rtaudio_t audio) {
+  try {
+    audio->has_error = 0;
+    audio->audio->stopStream();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+  return 0;
+}
+
+int rtaudio_abort_stream(rtaudio_t audio) {
+  try {
+    audio->has_error = 0;
+    audio->audio->abortStream();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+  return 0;
+}
+
+int rtaudio_is_stream_open(rtaudio_t audio) {
+  return !!audio->audio->isStreamOpen();
+}
+
+int rtaudio_is_stream_running(rtaudio_t audio) {
+  return !!audio->audio->isStreamRunning();
+}
+
+double rtaudio_get_stream_time(rtaudio_t audio) {
+  try {
+    audio->has_error = 0;
+    return audio->audio->getStreamTime();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+    return 0;
+  }
+}
+
+void rtaudio_set_stream_time(rtaudio_t audio, double time) {
+  try {
+    audio->has_error = 0;
+    audio->audio->setStreamTime(time);
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+  }
+}
+
+int rtaudio_get_stream_latency(rtaudio_t audio) {
+  try {
+    audio->has_error = 0;
+    return audio->audio->getStreamLatency();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+    return -1;
+  }
+}
+
+unsigned int rtaudio_get_stream_sample_rate(rtaudio_t audio) {
+  try {
+    return audio->audio->getStreamSampleRate();
+  } catch (RtAudioError &err) {
+    audio->has_error = 1;
+    strncpy(audio->errmsg, err.what(), sizeof(audio->errmsg) - 1);
+    return -1;
+  }
+}
+
+void rtaudio_show_warnings(rtaudio_t audio, int show) {
+  audio->audio->showWarnings(!!show);
+}
diff --git a/rtaudio_c.h b/rtaudio_c.h
new file mode 100644 (file)
index 0000000..6d0131a
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef RTAUDIO_C_H
+#define RTAUDIO_C_H
+
+#if defined(RTAUDIO_EXPORT)
+#if defined _WIN32 || defined __CYGWIN__
+#define RTAUDIOAPI __declspec(dllexport)
+#else
+#define RTAUDIOAPI __attribute__((visibility("default")))
+#endif
+#else
+#define RTAUDIOAPI //__declspec(dllimport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long rtaudio_format_t;
+
+#define RTAUDIO_FORMAT_SINT8 0x01
+#define RTAUDIO_FORMAT_SINT16 0x02
+#define RTAUDIO_FORMAT_SINT24 0x04
+#define RTAUDIO_FORMAT_SINT32 0x08
+#define RTAUDIO_FORMAT_FLOAT32 0x10
+#define RTAUDIO_FORMAT_FLOAT64 0x20
+
+typedef unsigned int rtaudio_stream_flags_t;
+
+#define RTAUDIO_FLAGS_NONINTERLEAVED 0x1
+#define RTAUDIO_FLAGS_MINIMIZE_LATENCY 0x2
+#define RTAUDIO_FLAGS_HOG_DEVICE 0x4
+#define RTAUDIO_FLAGS_SCHEDULE_REALTIME 0x8
+#define RTAUDIO_FLAGS_ALSA_USE_DEFAULT 0x10
+
+typedef unsigned int rtaudio_stream_status_t;
+
+#define RTAUDIO_STATUS_INPUT_OVERFLOW 0x1
+#define RTAUDIO_STATUS_OUTPUT_UNDERFLOW 0x2
+
+typedef int (*rtaudio_cb_t)(void *out, void *in, unsigned int nFrames,
+                            double stream_time, rtaudio_stream_status_t status,
+                            void *userdata);
+
+typedef enum rtaudio_error {
+  RTAUDIO_ERROR_WARNING,
+  RTAUDIO_ERROR_DEBUG_WARNING,
+  RTAUDIO_ERROR_UNSPECIFIED,
+  RTAUDIO_ERROR_NO_DEVICES_FOUND,
+  RTAUDIO_ERROR_INVALID_DEVICE,
+  RTAUDIO_ERROR_MEMORY_ERROR,
+  RTAUDIO_ERROR_INVALID_PARAMETER,
+  RTAUDIO_ERROR_INVALID_USE,
+  RTAUDIO_ERROR_DRIVER_ERROR,
+  RTAUDIO_ERROR_SYSTEM_ERROR,
+  RTAUDIO_ERROR_THREAD_ERROR,
+} rtaudio_error_t;
+
+typedef void (*rtaudio_error_cb_t)(rtaudio_error_t err, const char *msg);
+
+typedef enum rtaudio_api {
+  RTAUDIO_API_UNSPECIFIED,
+  RTAUDIO_API_LINUX_ALSA,
+  RTAUDIO_API_LINUX_PULSE,
+  RTAUDIO_API_LINUX_OSS,
+  RTAUDIO_API_UNIX_JACK,
+  RTAUDIO_API_MACOSX_CORE,
+  RTAUDIO_API_WINDOWS_WASAPI,
+  RTAUDIO_API_WINDOWS_ASIO,
+  RTAUDIO_API_WINDOWS_DS,
+  RTAUDIO_API_DUMMY,
+  RTAUDIO_API_NUM,
+} rtaudio_api_t;
+
+#define NUM_SAMPLE_RATES 16
+#define MAX_NAME_LENGTH 512
+typedef struct rtaudio_device_info {
+  int probed;
+  unsigned int output_channels;
+  unsigned int input_channels;
+  unsigned int duplex_channels;
+
+  int is_default_output;
+  int is_default_input;
+
+  rtaudio_format_t native_formats;
+
+  unsigned int preferred_sample_rate;
+  int sample_rates[NUM_SAMPLE_RATES];
+
+  char name[MAX_NAME_LENGTH];
+} rtaudio_device_info_t;
+
+typedef struct rtaudio_stream_parameters {
+  unsigned int device_id;
+  unsigned int num_channels;
+  unsigned int first_channel;
+} rtaudio_stream_parameters_t;
+
+typedef struct rtaudio_stream_options {
+  rtaudio_stream_flags_t flags;
+  unsigned int num_buffers;
+  int priority;
+  char name[MAX_NAME_LENGTH];
+} rtaudio_stream_options_t;
+
+typedef struct rtaudio *rtaudio_t;
+
+RTAUDIOAPI const char *rtaudio_version(void);
+RTAUDIOAPI unsigned int rtaudio_get_num_compiled_apis(void);
+RTAUDIOAPI const rtaudio_api_t *rtaudio_compiled_api(void);
+RTAUDIOAPI const char *rtaudio_api_name(rtaudio_api_t api);
+RTAUDIOAPI const char *rtaudio_api_display_name(rtaudio_api_t api);
+RTAUDIOAPI rtaudio_api_t rtaudio_compiled_api_by_name(const char *name);
+
+RTAUDIOAPI const char *rtaudio_error(rtaudio_t audio);
+
+RTAUDIOAPI rtaudio_t rtaudio_create(rtaudio_api_t api);
+RTAUDIOAPI void rtaudio_destroy(rtaudio_t audio);
+
+RTAUDIOAPI rtaudio_api_t rtaudio_current_api(rtaudio_t audio);
+
+RTAUDIOAPI int rtaudio_device_count(rtaudio_t audio);
+RTAUDIOAPI rtaudio_device_info_t rtaudio_get_device_info(rtaudio_t audio,
+                                                         int i);
+RTAUDIOAPI unsigned int rtaudio_get_default_output_device(rtaudio_t audio);
+RTAUDIOAPI unsigned int rtaudio_get_default_input_device(rtaudio_t audio);
+
+RTAUDIOAPI int
+rtaudio_open_stream(rtaudio_t audio, rtaudio_stream_parameters_t *output_params,
+                    rtaudio_stream_parameters_t *input_params,
+                    rtaudio_format_t format, unsigned int sample_rate,
+                    unsigned int *buffer_frames, rtaudio_cb_t cb,
+                    void *userdata, rtaudio_stream_options_t *options,
+                    rtaudio_error_cb_t errcb);
+RTAUDIOAPI void rtaudio_close_stream(rtaudio_t audio);
+RTAUDIOAPI int rtaudio_start_stream(rtaudio_t audio);
+RTAUDIOAPI int rtaudio_stop_stream(rtaudio_t audio);
+RTAUDIOAPI int rtaudio_abort_stream(rtaudio_t audio);
+
+RTAUDIOAPI int rtaudio_is_stream_open(rtaudio_t audio);
+RTAUDIOAPI int rtaudio_is_stream_running(rtaudio_t audio);
+
+RTAUDIOAPI double rtaudio_get_stream_time(rtaudio_t audio);
+RTAUDIOAPI void rtaudio_set_stream_time(rtaudio_t audio, double time);
+RTAUDIOAPI int rtaudio_get_stream_latency(rtaudio_t audio);
+RTAUDIOAPI unsigned int rtaudio_get_stream_sample_rate(rtaudio_t audio);
+
+RTAUDIOAPI void rtaudio_show_warnings(rtaudio_t audio, int show);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* RTAUDIO_C_H */
index 0fb028fc1876da00c1d1629a04fbf3e87fb577b0..5847027bcdbbe605f0c4fd2e830355cd2d3a406d 100644 (file)
@@ -3,23 +3,28 @@ if (WIN32)
     include_directories(../include)
 endif (WIN32)
 
+list(GET LIB_TARGETS 0 LIBRTAUDIO)
+
 add_executable(audioprobe audioprobe.cpp)
-target_link_libraries(audioprobe rtaudio_static ${LINKLIBS})
+target_link_libraries(audioprobe ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(playsaw playsaw.cpp)
-target_link_libraries(playsaw rtaudio_static ${LINKLIBS})
+target_link_libraries(playsaw ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(playraw playraw.cpp)
-target_link_libraries(playraw rtaudio_static ${LINKLIBS})
+target_link_libraries(playraw ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(record record.cpp)
-target_link_libraries(record rtaudio_static ${LINKLIBS})
+target_link_libraries(record ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(duplex duplex.cpp)
-target_link_libraries(duplex rtaudio_static ${LINKLIBS})
+target_link_libraries(duplex ${LIBRTAUDIO} ${LINKLIBS})
+
+add_executable(apinames apinames.cpp)
+target_link_libraries(apinames ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(testall testall.cpp)
-target_link_libraries(testall rtaudio_static ${LINKLIBS})
+target_link_libraries(testall ${LIBRTAUDIO} ${LINKLIBS})
 
 add_executable(teststops teststops.cpp)
-target_link_libraries(teststops rtaudio_static ${LINKLIBS})
+target_link_libraries(teststops ${LIBRTAUDIO} ${LINKLIBS})
index e39fdde25f9921215d41ce09546460e43355bb81..c8159da4c9bc569d48a492be3dbdb43fb7650ac2 100644 (file)
@@ -1,5 +1,5 @@
 
-noinst_PROGRAMS = audioprobe playsaw playraw record duplex testall teststops
+noinst_PROGRAMS = audioprobe playsaw playraw record duplex apinames testall teststops
 
 AM_CXXFLAGS = -Wall -I$(top_srcdir)
 
@@ -18,6 +18,9 @@ record_LDADD = $(top_builddir)/librtaudio.la
 duplex_SOURCES = duplex.cpp
 duplex_LDADD = $(top_builddir)/librtaudio.la
 
+apinames_SOURCES = apinames.cpp
+apinames_LDADD = $(top_builddir)/librtaudio.la
+
 testall_SOURCES = testall.cpp
 testall_LDADD = $(top_builddir)/librtaudio.la
 
diff --git a/tests/apinames.cpp b/tests/apinames.cpp
new file mode 100644 (file)
index 0000000..c270764
--- /dev/null
@@ -0,0 +1,157 @@
+/******************************************/
+/*
+  apinames.cpp
+  by Jean Pierre Cimalando, 2018.
+
+  This program tests parts of RtAudio related
+  to API names, the conversion from name to API
+  and vice-versa.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <cctype>
+#include <cstdlib>
+#include <iostream>
+
+int test_cpp() {
+    std::vector<RtAudio::Api> apis;
+    RtAudio::getCompiledApi( apis );
+
+    // ensure the known APIs return valid names
+    std::cout << "API names by identifier (C++):\n";
+    for ( size_t i = 0; i < apis.size() ; ++i ) {
+        const std::string name = RtAudio::getApiName(apis[i]);
+        if (name.empty()) {
+            std::cout << "Invalid name for API " << (int)apis[i] << "\n";
+            exit(1);
+        }
+        const std::string displayName = RtAudio::getApiDisplayName(apis[i]);
+        if (displayName.empty()) {
+            std::cout << "Invalid display name for API " << (int)apis[i] << "\n";
+            exit(1);
+        }
+        std::cout << "* " << (int)apis[i] << " '" << name << "': '" << displayName << "'\n";
+    }
+
+    // ensure unknown APIs return the empty string
+    {
+        const std::string name = RtAudio::getApiName((RtAudio::Api)-1);
+        if (!name.empty()) {
+            std::cout << "Bad string for invalid API '" << name << "'\n";
+            exit(1);
+        }
+        const std::string displayName = RtAudio::getApiDisplayName((RtAudio::Api)-1);
+        if (displayName!="Unknown") {
+            std::cout << "Bad display string for invalid API '" << displayName << "'\n";
+            exit(1);
+        }
+    }
+
+    // try getting API identifier by name
+    std::cout << "API identifiers by name (C++):\n";
+    for ( size_t i = 0; i < apis.size() ; ++i ) {
+        std::string name = RtAudio::getApiName(apis[i]);
+        if ( RtAudio::getCompiledApiByName(name) != apis[i] ) {
+            std::cout << "Bad identifier for API '" << name << "'\n";
+            exit( 1 );
+        }
+        std::cout << "* '" << name << "': " << (int)apis[i] << "\n";
+
+        for ( size_t j = 0; j < name.size(); ++j )
+            name[j] = (j & 1) ? toupper(name[j]) : tolower(name[j]);
+        RtAudio::Api api = RtAudio::getCompiledApiByName(name);
+        if ( api != RtAudio::UNSPECIFIED ) {
+            std::cout << "Identifier " << (int)api << " for invalid API '" << name << "'\n";
+            exit( 1 );
+        }
+    }
+
+    // try getting an API identifier by unknown name
+    {
+        RtAudio::Api api;
+        api = RtAudio::getCompiledApiByName("");
+        if ( api != RtAudio::UNSPECIFIED ) {
+            std::cout << "Bad identifier for unknown API name\n";
+            exit( 1 );
+        }
+    }
+
+    return 0;
+}
+
+#include "rtaudio_c.h"
+
+int test_c() {
+    const rtaudio_api_t *apis = rtaudio_compiled_api();
+
+    // ensure the known APIs return valid names
+    std::cout << "API names by identifier (C):\n";
+    for ( size_t i = 0; apis[i] != RTAUDIO_API_UNSPECIFIED; ++i) {
+        const std::string name = rtaudio_api_name(apis[i]);
+        if (name.empty()) {
+            std::cout << "Invalid name for API " << (int)apis[i] << "\n";
+            exit(1);
+        }
+        const std::string displayName = rtaudio_api_display_name(apis[i]);
+        if (displayName.empty()) {
+            std::cout << "Invalid display name for API " << (int)apis[i] << "\n";
+            exit(1);
+        }
+        std::cout << "* " << (int)apis[i] << " '" << name << "': '" << displayName << "'\n";
+    }
+
+    // ensure unknown APIs return the empty string
+    {
+        const char *s = rtaudio_api_name((rtaudio_api_t)-1);
+        const std::string name(s?s:"");
+        if (!name.empty()) {
+            std::cout << "Bad string for invalid API '" << name << "'\n";
+            exit(1);
+        }
+        s = rtaudio_api_display_name((rtaudio_api_t)-1);
+        const std::string displayName(s?s:"");
+        if (displayName!="Unknown") {
+            std::cout << "Bad display string for invalid API '" << displayName << "'\n";
+            exit(1);
+        }
+    }
+
+    // try getting API identifier by name
+    std::cout << "API identifiers by name (C):\n";
+    for ( size_t i = 0; apis[i] != RTAUDIO_API_UNSPECIFIED ; ++i ) {
+        const char *s = rtaudio_api_name(apis[i]);
+        std::string name(s?s:"");
+        if ( rtaudio_compiled_api_by_name(name.c_str()) != apis[i] ) {
+            std::cout << "Bad identifier for API '" << name << "'\n";
+            exit( 1 );
+        }
+        std::cout << "* '" << name << "': " << (int)apis[i] << "\n";
+
+        for ( size_t j = 0; j < name.size(); ++j )
+            name[j] = (j & 1) ? toupper(name[j]) : tolower(name[j]);
+        rtaudio_api_t api = rtaudio_compiled_api_by_name(name.c_str());
+        if ( api != RTAUDIO_API_UNSPECIFIED ) {
+            std::cout << "Identifier " << (int)api << " for invalid API '" << name << "'\n";
+            exit( 1 );
+        }
+    }
+
+    // try getting an API identifier by unknown name
+    {
+        rtaudio_api_t api;
+        api = rtaudio_compiled_api_by_name("");
+        if ( api != RTAUDIO_API_UNSPECIFIED ) {
+            std::cout << "Bad identifier for unknown API name\n";
+            exit( 1 );
+        }
+    }
+
+    return 0;
+}
+
+int main()
+{
+    test_cpp();
+    test_c();
+}
index 1b93908748e3d8dd8ed5d3484e8d39bd223504ea..f2f47d9ad2f61b62e8989ada090d2606a5e6f266 100644 (file)
@@ -17,7 +17,7 @@ int main()
   std::map<int, std::string> apiMap;
   apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
   apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
-  apiMap[RtAudio::WINDOWS_DS] = "Windows Direct Sound";
+  apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound";
   apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI";
   apiMap[RtAudio::UNIX_JACK] = "Jack Client";
   apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
@@ -46,6 +46,7 @@ int main()
     info = audio.getDeviceInfo(i);
 
     std::cout << "\nDevice Name = " << info.name << '\n';
+    std::cout << "Device ID = " << i << '\n';
     if ( info.probed == false )
       std::cout << "Probe Status = UNsuccessful\n";
     else {