Merge remote-tracking branch 'upstream/pr/141'
authorStephen Sinclair <radarsat1@gmail.com>
Tue, 16 Oct 2018 13:09:53 +0000 (15:09 +0200)
committerStephen Sinclair <radarsat1@gmail.com>
Tue, 16 Oct 2018 13:09:53 +0000 (15:09 +0200)
18 files changed:
.travis.yml
CMakeLists.txt
Makefile.am
RtAudio.cpp
RtAudio.h
cmake/RtAudioConfigUninstall.cmake.in [new file with mode: 0644]
configure.ac
contrib/go/rtaudio/rtaudio.go
doc/doxygen/compiling.txt
doc/doxygen/probe.txt
install.txt
rtaudio.pc.in
rtaudio_c.cpp
rtaudio_c.h
tests/CMakeLists.txt
tests/Makefile.am
tests/apinames.cpp [new file with mode: 0644]
tests/audioprobe.cpp

index 2c687a74b42952802cec4d2d218fc973eb6d9c0d..838f834e269c206b52f817ef244fa802f08a6551 100644 (file)
@@ -26,12 +26,6 @@ matrix:
   - os: linux
     env: HOST="" API="oss"
     compiler: clang
-  - os: linux
-    env: HOST="--host=i686-w64-mingw32" API="winmm" CPPFLAGS="-Wno-unused-function"
-    compiler: gcc
-  - os: linux
-    env: HOST="--host=x86_64-w64-mingw32" API="winmm" CPPFLAGS="-Wno-unused-function"
-    compiler: gcc
   - os: linux
     env: HOST="--host=i686-w64-mingw32" API="dsound" CPPFLAGS="-Wno-unused-function"
     compiler: gcc
index d5a00c7e9c2341c6d42743b2b760860b9151176b..d220bc7500f8b943ecbfeb503281e1941411d4a0 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 rtaudio_c.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()
 
-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)
+# 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()
 
+# 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)
+  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)
-set(PACKAGE_VERSION 5.0.0)
-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})
 
-target_link_libraries(rtaudio ${LINKLIBS})
+  # Set compile-time definitions
+  target_compile_definitions(rtaudio_static PRIVATE ${API_DEFS})
 
+  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)
 
+# Message
+string(REPLACE ";" " " apilist "${API_LIST}")
+message(STATUS "Compiling with support for: ${apilist}")
+
+# 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)
 
-install(TARGETS rtaudio
-      LIBRARY DESTINATION lib)
+# 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})
 
-install(TARGETS rtaudio_static
-      ARCHIVE DESTINATION lib)
+# Store the package in the user registry.
+export(PACKAGE RtAudio)
 
-install(
-    FILES RtAudio.h
-    DESTINATION include)
+# 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 ${CMAKE_CURRENT_BINARY_DIR}/rtaudio.pc
-    DESTINATION lib/pkgconfig)
+    DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
index f83007ccf19474c54b969ab1db0b31b05043a481..2a050354a19646ee5dd607ca7c19c2a91f8e368c 100644 (file)
@@ -6,6 +6,7 @@ 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 \
index e1462f4ddba67f283eb454ca7cb1a119c3b382f5..d23524d4024fd81662faf66ec3bf8bc13da8ed4f 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 );
+  RtAudio::UNIX_JACK,
 #endif
 #if defined(__LINUX_PULSE__)
-  apis.push_back( LINUX_PULSE );
+  RtAudio::LINUX_PULSE,
 #endif
 #if defined(__LINUX_ALSA__)
-  apis.push_back( 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 )
@@ -1973,7 +2029,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 +2068,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 +2103,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 +2112,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 );
@@ -2168,7 +2224,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 +2248,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 +2279,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);
@@ -2453,7 +2511,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 +2535,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 +3221,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 );
@@ -3687,11 +3745,32 @@ 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>
-#include <sstream>
+
+#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
 
 //=============================================================================
 
@@ -3865,6 +3944,198 @@ private:
 
 //-----------------------------------------------------------------------------
 
+// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate
+// 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
+  {
+    // 1. Initialization
+
+    MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET );
+
+    // 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 );
+  }
+
+  ~WasapiResampler()
+  {
+    // 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 )
+    {
+      // no sample rate conversion required
+      memcpy( outBuffer, inBuffer, inputBufferSize );
+      outSampleCount = inSampleCount;
+      return;
+    }
+
+    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
+
+    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
+};
+
+//-----------------------------------------------------------------------------
+
 // A structure to hold various information related to the WASAPI implementation.
 struct WasapiHandle
 {
@@ -3899,10 +4170,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;
 }
 
 //-----------------------------------------------------------------------------
@@ -3929,6 +4199,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 );
@@ -4130,11 +4403,14 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
     info.duplexChannels = 0;
   }
 
-  // sample rates (WASAPI only supports the one native sample rate)
-  info.preferredSampleRate = deviceFormat->nSamplesPerSec;
-
+  // sample rates
   info.sampleRates.clear();
-  info.sampleRates.push_back( deviceFormat->nSamplesPerSec );
+
+  // allow support for all sample rates as we have a built-in sample rate converter
+  for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {
+    info.sampleRates.push_back( SAMPLE_RATES[i] );
+  }
+  info.preferredSampleRate = deviceFormat->nSamplesPerSec;
 
   // native format
   info.nativeFormats = 0;
@@ -4411,7 +4687,6 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   WAVEFORMATEX* deviceFormat = NULL;
   unsigned int bufferBytes;
   stream_.state = STREAM_STOPPED;
-  RtAudio::DeviceInfo deviceInfo;
 
   // create API Handle if not already created
   if ( !stream_.apiHandle )
@@ -4452,20 +4727,6 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     goto Exit;
   }
 
-  deviceInfo = getDeviceInfo( device );
-
-  // validate sample rate
-  if ( sampleRate != deviceInfo.preferredSampleRate )
-  {
-    errorType = RtAudioError::INVALID_USE;
-    std::stringstream ss;
-    ss << "RtApiWasapi::probeDeviceOpen: " << sampleRate
-       << "Hz sample rate not supported. This device only supports "
-       << deviceInfo.preferredSampleRate << "Hz.";
-    errorText_ = ss.str();
-    goto Exit;
-  }
-
   // determine whether index falls within capture or render devices
   if ( device >= renderDeviceCount ) {
     if ( mode != INPUT ) {
@@ -4549,7 +4810,7 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.nUserChannels[mode] = channels;
   stream_.channelOffset[mode] = firstChannel;
   stream_.userFormat = format;
-  stream_.deviceFormat[mode] = deviceInfo.nativeFormats;
+  stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;
 
   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
     stream_.userInterleaved = false;
@@ -4560,7 +4821,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 )
@@ -4649,8 +4911,12 @@ void RtApiWasapi::wasapiThread()
 
   WAVEFORMATEX* captureFormat = NULL;
   WAVEFORMATEX* renderFormat = NULL;
+  float captureSrRatio = 0.0f;
+  float renderSrRatio = 0.0f;
   WasapiBuffer captureBuffer;
   WasapiBuffer renderBuffer;
+  WasapiResampler* captureResampler = NULL;
+  WasapiResampler* renderResampler = NULL;
 
   // declare local stream variables
   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
@@ -4658,11 +4924,15 @@ void RtApiWasapi::wasapiThread()
   unsigned long captureFlags = 0;
   unsigned int bufferFrameCount = 0;
   unsigned int numFramesPadding = 0;
-  bool callbackPushed = false;
+  unsigned int convBufferSize = 0;
+  bool callbackPushed = true;
   bool callbackPulled = false;
   bool callbackStopped = false;
   int callbackResult = 0;
 
+  // convBuffer is used to store converted buffers between WASAPI and the user
+  char* convBuffer = NULL;
+  unsigned int convBuffSize = 0;
   unsigned int deviceBuffSize = 0;
 
   errorText_.clear();
@@ -4685,8 +4955,16 @@ void RtApiWasapi::wasapiThread()
       goto Exit;
     }
 
+    // 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 );
+
+    captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );
+
     // initialize capture stream according to desire buffer size
-    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / captureFormat->nSamplesPerSec );
+    float desiredBufferSize = stream_.bufferSize * captureSrRatio;
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );
 
     if ( !captureClient ) {
       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
@@ -4733,7 +5011,7 @@ void RtApiWasapi::wasapiThread()
     }
 
     // scale outBufferSize according to stream->user sample rate ratio
-    unsigned int outBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[INPUT];
+    unsigned int outBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];
     inBufferSize *= stream_.nDeviceChannels[INPUT];
 
     // set captureBuffer size
@@ -4762,8 +5040,16 @@ void RtApiWasapi::wasapiThread()
       goto Exit;
     }
 
+    // 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 );
+
+    renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );
+
     // initialize render stream according to desire buffer size
-    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / renderFormat->nSamplesPerSec );
+    float desiredBufferSize = stream_.bufferSize * renderSrRatio;
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );
 
     if ( !renderClient ) {
       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
@@ -4810,7 +5096,7 @@ void RtApiWasapi::wasapiThread()
     }
 
     // scale inBufferSize according to user->stream sample rate ratio
-    unsigned int inBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[OUTPUT];
+    unsigned int inBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];
     outBufferSize *= stream_.nDeviceChannels[OUTPUT];
 
     // set renderBuffer size
@@ -4831,20 +5117,30 @@ void RtApiWasapi::wasapiThread()
     }
   }
 
-  if ( stream_.mode == INPUT ) {
-    using namespace std; // for roundf
+  // 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 ) {
+  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 ) {
+  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] ) );
   }
 
+  convBuffSize *= 2; // allow overflow for *SrRatio remainders
+  convBuffer = ( char* ) malloc( convBuffSize );
   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );
-  if ( !stream_.deviceBuffer ) {
+  if ( !convBuffer || !stream_.deviceBuffer ) {
     errorType = RtAudioError::MEMORY_ERROR;
     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
     goto Exit;
@@ -4856,15 +5152,46 @@ void RtApiWasapi::wasapiThread()
       // Callback Input
       // ==============
       // 1. Pull callback buffer from inputBuffer
-      // 2. If 1. was successful: Convert callback buffer to user format
+      // 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( stream_.deviceBuffer,
-                                                   ( unsigned int ) stream_.bufferSize * 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;
+          }
+
+          // Convert callback buffer to user sample rate
+          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 ( callbackPulled )
+        {
           if ( stream_.doConvertBuffer[INPUT] ) {
             // Convert callback buffer to user format
             convertBuffer( stream_.userBuffer[INPUT],
@@ -4938,20 +5265,33 @@ void RtApiWasapi::wasapiThread()
     // Callback Output
     // ===============
     // 1. Convert callback buffer to stream format
-    // 2. Push callback buffer into outputBuffer
+    // 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] );
 
+        }
+
+        // Convert callback buffer to stream sample rate
+        renderResampler->Convert( convBuffer,
+                                  stream_.deviceBuffer,
+                                  stream_.bufferSize,
+                                  convBufferSize );
       }
 
       // Push callback buffer into outputBuffer
-      callbackPushed = renderBuffer.pushBuffer( stream_.deviceBuffer,
-                                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT],
+      callbackPushed = renderBuffer.pushBuffer( convBuffer,
+                                                convBufferSize * stream_.nDeviceChannels[OUTPUT],
                                                 stream_.deviceFormat[OUTPUT] );
     }
     else {
@@ -5085,7 +5425,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();
     }
@@ -5097,15 +5440,17 @@ Exit:
   CoTaskMemFree( captureFormat );
   CoTaskMemFree( renderFormat );
 
+  free ( convBuffer );
+  delete renderResampler;
+  delete captureResampler;
+
   CoUninitialize();
 
+  if ( !errorText_.empty() )
+    error( errorType );
+
   // update stream state
   stream_.state = STREAM_STOPPED;
-
-  if ( errorText_.empty() )
-    return;
-  else
-    error( errorType );
 }
 
 //******************** End of __WINDOWS_WASAPI__ *********************//
@@ -5121,6 +5466,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>
@@ -7562,7 +7909,7 @@ 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 ) {
       stream_.callbackInfo.doRealtime = true;
       struct sched_param param;
@@ -8011,7 +8358,7 @@ 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 ) {
     std::cerr << "RtAudio alsa: " << 
              (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
@@ -8099,7 +8446,7 @@ static void *pulseaudio_callback( void * user )
   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
   volatile bool *isRunning = &cbi->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 (cbi->doRealtime) {
     std::cerr << "RtAudio pulse: " << 
              (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
@@ -8502,7 +8849,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     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;
@@ -9123,7 +9470,7 @@ 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;
@@ -9514,7 +9861,7 @@ static void *ossCallbackHandler( void *ptr )
   RtApiOss *object = (RtApiOss *) 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) {
     std::cerr << "RtAudio oss: " << 
              (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << 
index 34a2534859fab7dcd85d4434d472209a09d1bbe4..25c2076b30f4b9aa3c7d116f01e44e641b3f06a6 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
 #define RTAUDIO_VERSION "5.0.0"
 
 #if defined _WIN32 || defined __CYGWIN__
-  #define RTAUDIO_DLL_PUBLIC
+  #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" )) )
@@ -285,7 +289,8 @@ class RTAUDIO_DLL_PUBLIC RtAudio
     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. */
+    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.
@@ -298,7 +303,7 @@ class RTAUDIO_DLL_PUBLIC 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.
@@ -397,6 +402,29 @@ class RTAUDIO_DLL_PUBLIC 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
@@ -1013,7 +1041,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 286b7d93fdabbea5aac8111dcb2d419580b0d7ed..266ff488fd4fd70c479a978fe055981f6369f261 100644 (file)
@@ -38,9 +38,9 @@ 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 (mac and linux only)])])
+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 (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)])])
@@ -86,8 +86,8 @@ AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
 AS_IF([test "x${GXX}" = "xyes" ], [
   CXXFLAGS="-Wall -Wextra ${CXXFLAGS}"
   AS_IF([ test "x${enable_debug}" = "xyes" ], [
-    # Add -Werror in debug mode
-    CXXFLAGS="-Werror ${CXXFLAGS}"
+    # 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"
@@ -147,114 +147,150 @@ AC_CONFIG_LINKS( [doc/images/ccrma.gif:doc/images/ccrma.gif] )
 # Checks for package options and external software
 AC_CANONICAL_HOST
 
-AC_MSG_CHECKING([for audio API])
+# 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 ds"])
+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 ds 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 ' '` "
 
-AS_IF([test "x$with_jack" = "xyes"], [
-  AC_MSG_RESULT([using JACK])
-  AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR([JACK support requires the jack library!]))
-  api="$api -D__UNIX_JACK__"
-  req="$req jack"
+# 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([$host],
-  [*-*-netbsd*],
-  AS_IF([test "x$api" = "x"], [
-    AC_MSG_RESULT([using OSS])
-    api="$api -D__LINUX_OSS__"
-    AC_CHECK_LIB(ossaudio, main, , AC_MSG_ERROR([RtAudio requires the ossaudio library]))
-    AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR([RtAudio requires the pthread library!]))
-  ]),
-  [*-*-freebsd*],
-  AS_IF([test "x$api" = "x"], [
-    AC_MSG_RESULT([using OSS])
-    api="$api -D__LINUX_OSS__"
-    AC_CHECK_LIB(ossaudio, main, , AC_MSG_ERROR([RtAudio requires the ossaudio library]))
-    AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR([RtAudio requires the pthread library!]))
-  ]),
-  [*-*-linux*], [
-  # Look for ALSA flag
-  AS_IF([test "x$with_alsa" = "xyes"], [
-    AC_MSG_RESULT([using ALSA])
-    api="$api -D__LINUX_ALSA__"
-    req="$req alsa"
-    AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR([ALSA support requires the asound library!]))
-  ])
-  # Look for PULSE flag
-  AS_IF([test "x$with_pulse" = "xyes"], [
-    AC_MSG_RESULT([using PulseAudio])
-    api="$api -D__LINUX_PULSE__"
-    req="$req libpulse-simple"
-    AC_CHECK_LIB(pulse-simple, pa_simple_flush, , 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]))))
 
-  # Look for OSS flag
-  AS_IF([test "x$with_oss" = "xyes"], [
-    AC_MSG_RESULT([using OSS])
-    api="$api -D__LINUX_OSS__"
-  ])
+  # 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>])
+])
 
-  # If no audio api flags specified, use ALSA
-  AS_IF([test "x$api" = "x" ], [
-    AC_MSG_RESULT([using ALSA])
-    api="${api} -D__LINUX_ALSA__"
-    req="${req} alsa"
-    AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR([ALSA support requires the asound library!]))
-  ])
-  AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR([RtAudio requires the pthread library!]))
-  ],
-  [*-apple*],[
-  # Look for Core flag
-  AS_IF([test "x$with_core" = "xyes"], [
-    AC_MSG_RESULT([using CoreAudio])
-    api="$api -D__MACOSX_CORE__"
-    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
-  AS_IF([test "x$api" = "x" ], [
-    AC_MSG_RESULT([using CoreAudio])
-    api="${api} -D__MACOSX_CORE__"
-    AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
-      [],
-      [AC_MSG_ERROR([CoreAudio header files not found!])] )
-    LIBS="LIBS -framework CoreAudio -framework CoreFoundation"
-  ])
-  AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR([RtAudio requires the pthread library!]))
-  ],
-  [*-mingw32*],[
-  AS_IF([test "x$with_asio" = "xyes" ], [
-    AC_MSG_RESULT([using ASIO])
-    api="$api -D__WINDOWS_ASIO__"
-    use_asio=yes
-    CPPFLAGS="-I$srcdir/include $CPPFLAGS"
-  ])
-  # Look for DirectSound flag
-  AS_IF([test "x$with_ds" = "xyes" ], [
-    AC_MSG_RESULT([using DirectSound])
-    api="$api -D__WINDOWS_DS__"
-    LIBS="-ldsound -lwinmm $LIBS"
-  ])
-  # Look for WASAPI flag
-  AS_IF([test "x$with_wasapi" = "xyes"], [
-    AC_MSG_RESULT([using WASAPI])
-    api="$api -D__WINDOWS_WASAPI__"
-    LIBS="-lwinmm -luuid -lksuser $LIBS"
-    CPPFLAGS="-I$srcdir/include $CPPFLAGS"
-  ])
-  # If no audio api flags specified, use DS
-  AS_IF([test "x$api" = "x" ], [
-    AC_MSG_RESULT([using DirectSound])
-    api="$api -D__WINDOWS_DS__"
-    LIBS="-ldsound -lwinmm $LIBS"
-  ])
-  LIBS="-lole32 $LIBS"
-  ],[
+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"], [*" ds "*], [
+  AC_CHECK_HEADERS(mmsystem.h mmreg.h dsound.h,
+    [api="$api -D__WINDOWS_DS__"
+     need_ole32=yes
+     found="$found DirectSound"
+     LIBS="-ldsound -lwinmm $LIBS"])
+])
+
+AS_CASE(["$systems"], [*" wasapi "*], [
+  AC_CHECK_HEADERS(windows.h audioclient.h avrt.h mmdeviceapi.h,
+    [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])
-  # Default case for unknown realtime systems.
-  AC_MSG_ERROR([Unknown system type for realtime support!])
-  ]
-)
+  AC_MSG_ERROR([No known system type found for realtime support!])
+], [
+  AC_MSG_RESULT([$found])
+])
 
 AM_CONDITIONAL( ASIO, [test "x${use_asio}" = "xyes" ])
 
index a0baf31b6d3f3553a5a8260e8f68222c8c613f0e..f487b14d652253a1f5c2f777e760585da1dd46ba 100644 (file)
@@ -15,7 +15,7 @@ package rtaudio
 #cgo jack LDFLAGS: -ljack
 
 #cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
-#cgo windows LDFLAGS: -lm -luuid -lksuser -lwinmm -lole32 -static
+#cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
 
 #cgo darwin CXXFLAGS: -D__MACOSX_CORE__
 #cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
index 29dcbd471afbf460e5d442521f0a818f166df6af..819ba9c66d75fec7d1cda96c93c21b3a8551f067 100644 (file)
@@ -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
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
 
index 762778060ebd0bc12c423284c6e694bc963dc336..0e81090dfede61890e45f2b1e01748cdad16b658 100644 (file)
@@ -1,4 +1,4 @@
-prefix=@CMAKE_INSTALL_PREFIX@
+prefix=@prefix@
 exec_prefix=${prefix}
 libdir=${exec_prefix}/lib
 includedir=${prefix}/include/rtaudio        
index 699d2ce760d4fdd31b63d96a7b992f25d1e006ca..da3ab24ebc255896d27b3fef231f3c7de4e8f570 100644 (file)
@@ -15,40 +15,33 @@ struct rtaudio {
   char errmsg[MAX_ERROR_MESSAGE_LENGTH];
 };
 
-static const rtaudio_api_t compiled_api[] = {
-#if defined(__UNIX_JACK__)
-    RTAUDIO_API_UNIX_JACK,
-#endif
-#if defined(__LINUX_ALSA__)
-    RTAUDIO_API_LINUX_ALSA,
-#endif
-#if defined(__LINUX_PULSE__)
-    RTAUDIO_API_LINUX_PULSE,
-#endif
-#if defined(__LINUX_OSS__)
-    RTAUDIO_API_LINUX_OSS,
-#endif
-#if defined(__WINDOWS_ASIO__)
-    RTAUDIO_API_WINDOWS_ASIO,
-#endif
-#if defined(__WINDOWS_WASAPI__)
-    RTAUDIO_API_WINDOWS_WASAPI,
-#endif
-#if defined(__WINDOWS_DS__)
-    RTAUDIO_API_WINDOWS_DS,
-#endif
-#if defined(__MACOSX_CORE__)
-    RTAUDIO_API_MACOSX_CORE,
-#endif
-#if defined(__RTAUDIO_DUMMY__)
-    RTAUDIO_API_DUMMY,
-#endif
-    RTAUDIO_API_UNSPECIFIED,
-};
-
 const char *rtaudio_version() { return RTAUDIO_VERSION; }
 
-const rtaudio_api_t *rtaudio_compiled_api() { return compiled_api; }
+extern "C" const rtaudio_api_t rtaudio_compiled_apis[]; // casting from RtAudio::Api[]
+extern "C" const unsigned int rtaudio_num_compiled_apis;
+const rtaudio_api_t *rtaudio_compiled_api() { return rtaudio_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) {
index 05015a96cf35647ec737fd31b252a823e36cf7fa..a366117b0aa8b9a93da62ec0f29cc946c82d8fb6 100644 (file)
@@ -2,8 +2,12 @@
 #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
 
@@ -64,6 +68,7 @@ typedef enum rtaudio_api {
   RTAUDIO_API_WINDOWS_ASIO,
   RTAUDIO_API_WINDOWS_DS,
   RTAUDIO_API_DUMMY,
+  RTAUDIO_API_NUM,
 } rtaudio_api_t;
 
 #define NUM_SAMPLE_RATES 16
@@ -102,6 +107,9 @@ typedef struct rtaudio *rtaudio_t;
 
 RTAUDIOAPI const char *rtaudio_version(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);
 
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..c83d3a5f45600cb7bd75895b363e45e0707d813f 100644 (file)
@@ -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 {