Fix api name functions for C, replace map with array.
authorStephen Sinclair <radarsat1@gmail.com>
Wed, 8 Aug 2018 19:01:18 +0000 (15:01 -0400)
committerStephen Sinclair <radarsat1@gmail.com>
Wed, 8 Aug 2018 23:24:39 +0000 (19:24 -0400)
RtAudio.cpp
RtAudio.h
rtaudio_c.cpp
rtaudio_c.h
tests/apinames.cpp

index 07a63144d375a745ff4dfc35e75ffd2e935c4bb7..c2c376016b03717bc63ba4dd28d129e52990d056 100644 (file)
@@ -98,57 +98,76 @@ std::string RtAudio :: getVersion( void )
   return RTAUDIO_VERSION;
 }
 
-// Define API names.
-// TODO: replace with initializer list in C++11.
+// 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.
-// Have to maintain a separate list of API enum identifiers since map
-// doesn't preserve insertion order.
-static std::pair< RtAudio::ApiNameMap, std::vector<RtAudio::Api> > init_ApiNames()
-{
-  RtAudio::ApiNameMap names;
-  std::vector<RtAudio::Api> apis;
+RtAudio::Api rtaudio_compiled_apis[] = {
 #if defined(__UNIX_JACK__)
-  names["jack"] = std::pair<RtAudio::Api, std::string>(RtAudio::UNIX_JACK, "Jack");
-  apis.push_back(RtAudio::UNIX_JACK);
+  RtAudio::UNIX_JACK,
 #endif
 #if defined(__LINUX_PULSE__)
-  names["pulse"] = std::pair<RtAudio::Api, std::string>(RtAudio::LINUX_PULSE, "Pulse");
-  apis.push_back(RtAudio::LINUX_PULSE);
+  RtAudio::LINUX_PULSE,
 #endif
 #if defined(__LINUX_ALSA__)
-  names["alsa"] = std::pair<RtAudio::Api, std::string>(RtAudio::LINUX_ALSA, "ALSA");
-  apis.push_back(RtAudio::LINUX_ALSA);
+  RtAudio::LINUX_ALSA,
 #endif
 #if defined(__LINUX_OSS__)
-  names["oss"] = std::pair<RtAudio::Api, std::string>(RtAudio::LINUX_OSS, "OSS");
-  apis.push_back(RtAudio::LINUX_OSS);
+  RtAudio::LINUX_OSS,
 #endif
 #if defined(__WINDOWS_ASIO__)
-  names["asio"] = std::pair<RtAudio::Api, std::string>(RtAudio::WINDOWS_ASIO, "ASIO");
-  apis.push_back(RtAudio::WINDOWS_ASIO);
+  RtAudio::WINDOWS_ASIO,
 #endif
 #if defined(__WINDOWS_WASAPI__)
-  names["wasapi"] = std::pair<RtAudio::Api, std::string>(RtAudio::WINDOWS_WASAPI, "WASAPI");
-  apis.push_back(RtAudio::WINDOWS_WASAPI);
+  RtAudio::WINDOWS_WASAPI,
 #endif
 #if defined(__WINDOWS_DS__)
-  names["ds"] = std::pair<RtAudio::Api, std::string>(RtAudio::WINDOWS_DS, "DirectSound");
-  apis.push_back(RtAudio::WINDOWS_DS);
+  RtAudio::WINDOWS_DS,
 #endif
 #if defined(__MACOSX_CORE__)
-  names["core"] = std::pair<RtAudio::Api, std::string>(RtAudio::MACOSX_CORE, "CoreAudio");
-  apis.push_back(RtAudio::MACOSX_CORE);
+  RtAudio::MACOSX_CORE,
 #endif
 #if defined(__RTAUDIO_DUMMY__)
-  names["dummy"] = std::pair<RtAudio::Api, std::string>(RtAudio::RTAUDIO_DUMMY, "Dummy");
-  apis.push_back(RtAudio::RTAUDIO_DUMMY);
+  RtAudio::RTAUDIO_DUMMY,
 #endif
-  return std::make_pair(names, apis);
+  RtAudio::UNSPECIFIED,
+};
+const unsigned int rtaudio_num_compiled_apis =
+  sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1;
 }
 
-const RtAudio::ApiNameMap RtAudio::apiNames(init_ApiNames().first);
-const std::vector<RtAudio::Api> RtAudio::compiledApis(init_ApiNames().second);
+// TODO: replace this with something nicer (C++11)
+static const std::vector<RtAudio::Api> init_compiledApis() {
+  std::vector<RtAudio::Api> apis;
+  for (unsigned int i=0; i<rtaudio_num_compiled_apis; i++)
+    apis.push_back(rtaudio_compiled_apis[i]);
+  return apis;
+}
+const std::vector<RtAudio::Api> RtAudio::compiledApis(init_compiledApis());
+
+// 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 )
 {
@@ -160,32 +179,32 @@ const std::vector<RtAudio::Api>& RtAudio :: getCompiledApi()
   return compiledApis;
 }
 
-static const std::string unknown_api_name = "";
-static const std::string unknown_api_display_name = "Unknown";
-
-const std::string& RtAudio :: getCompiledApiName( RtAudio::Api api )
+const std::string RtAudio :: getCompiledApiName( RtAudio::Api api )
 {
-  ApiNameMap::const_iterator it;
-  for (it = apiNames.begin(); it != apiNames.end(); it++)
-    if (it->second.first == api)
-      return it->first;
-  return unknown_api_name;
+  if (api < 0 || api > RtAudio::NUM_APIS
+      || (std::find(RtAudio::compiledApis.begin(),
+                    RtAudio::compiledApis.end(), api) == RtAudio::compiledApis.end()))
+    return "";
+  return rtaudio_api_names[api][0];
 }
 
-const std::string& RtAudio :: getCompiledApiDisplayName( RtAudio::Api api )
+const std::string RtAudio :: getCompiledApiDisplayName( RtAudio::Api api )
 {
-  ApiNameMap::const_iterator it;
-  for (it = apiNames.begin(); it != apiNames.end(); it++)
-    if (it->second.first == api)
-      return it->second.second;
-  return unknown_api_display_name;
+  if (api < 0 || api > RtAudio::NUM_APIS
+      || (std::find(RtAudio::compiledApis.begin(),
+                    RtAudio::compiledApis.end(), api) == RtAudio::compiledApis.end())) 
+    return "Unknown";
+  return rtaudio_api_names[api][1];
 }
 
 RtAudio::Api RtAudio :: getCompiledApiByName( const std::string &name )
 {
-  if (apiNames.find(name) == apiNames.end())
-    return RtAudio::UNSPECIFIED;
-  return apiNames.at(name).first;
+  unsigned int i=0;
+  std::vector<RtAudio::Api>::const_iterator it;
+  for (it = compiledApis.begin(); it != compiledApis.end(); ++it, ++i)
+    if (name == rtaudio_api_names[*it][0])
+      return *it;
+  return RtAudio::UNSPECIFIED;
 }
 
 void RtAudio :: openRtApi( RtAudio::Api api )
index 3927ae19e7ac3e2e5d5da3246c7dd79c9ab273f7..976ada06133fb0a470bca0c29553263034460709 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
@@ -61,7 +61,6 @@
 #include <vector>
 #include <stdexcept>
 #include <iostream>
-#include <map>
 
 /*! \typedef typedef unsigned long RtAudioFormat;
     \brief RtAudio data format type.
@@ -286,12 +285,10 @@ 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. */
   };
 
-  //! Map string identifiers for APIs to enum identifiers and display names
-  typedef std::map< std::string, std::pair<RtAudio::Api, std::string> > ApiNameMap;
-
   //! The public device information structure for returning queried values.
   struct DeviceInfo {
     bool probed;                  /*!< true if the device capabilities were successfully probed. */
@@ -407,7 +404,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
     the enumerated list values.  Note that there can be more than one
     API compiled for certain operating systems.
   */
-  static const std::vector<RtAudio::Api>& getCompiledApi();
+  static const std::vector<RtAudio::Api>& getCompiledApis();
 
   //! Return the name of a specified compiled audio API.
   /*!
@@ -416,7 +413,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
     If the API is unknown or not compiled, this function will return
     the empty string.
   */
-  static const std::string& getCompiledApiName( RtAudio::Api api );
+  static const std::string getCompiledApiName( RtAudio::Api api );
 
   //! Return the display name of a specified compiled audio API.
   /*!
@@ -424,7 +421,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
     If the API is unknown or not compiled, this function will return
     the empty string.
   */
-  static const std::string& getCompiledApiDisplayName( RtAudio::Api api );
+  static const std::string getCompiledApiDisplayName( RtAudio::Api api );
 
   //! Return the compiled audio API having the given name.
   /*!
@@ -618,9 +615,6 @@ class RTAUDIO_DLL_PUBLIC RtAudio
 
  protected:
 
-  //! Storage for API name map
-  static const ApiNameMap apiNames;
-
   //! Storage for compiled API list
   static const std::vector<RtAudio::Api> compiledApis;
 
index ec849414a4cdd6e860dccea76daa237018ac238c..7ae1bec4c36e4308ff65f715cd041e4ec7a7003b 100644 (file)
@@ -15,50 +15,24 @@ 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" 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_compiled_api_name(rtaudio_api_t api) {
-    const std::string &name = RtAudio::getCompiledApiName((RtAudio::Api)api);
-    return name.empty() ? NULL : name.c_str();
+    if (api < 0 || api >= RTAUDIO_API_NUM)
+        return NULL;
+    return rtaudio_api_names[api][0];
 }
 
 const char *rtaudio_compiled_api_display_name(rtaudio_api_t api)
 {
-    const std::string &name = RtAudio::getCompiledApiDisplayName((RtAudio::Api)api);
-    return name.empty() ? NULL : name.c_str();
+    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) {
index a13ea79410f9d43679d8e144ad39266d215f6369..550f480453cc08997e994dca1c3f5bf713d61592 100644 (file)
@@ -68,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
index 81a3d7dd153570303391308a47047170e82dd870..9dc634c408405e629433c92834b5bf3203a39ae1 100644 (file)
 #include <cstdlib>
 #include <iostream>
 
-int main() {
+int test_cpp() {
     std::vector<RtAudio::Api> apis;
     RtAudio::getCompiledApi( apis );
 
     // ensure the known APIs return valid names
-    std::cout << "API names by identifier:\n";
+    std::cout << "API names by identifier (C++):\n";
     for ( size_t i = 0; i < apis.size() ; ++i ) {
-        const std::string &name = RtAudio::getCompiledApiName(apis[i]);
+        const std::string name = RtAudio::getCompiledApiName(apis[i]);
         if (name.empty()) {
             std::cerr << "Invalid name for API " << (int)apis[i] << "\n";
             exit(1);
         }
-        const std::string &displayName = RtAudio::getCompiledApiDisplayName(apis[i]);
+        const std::string displayName = RtAudio::getCompiledApiDisplayName(apis[i]);
         if (displayName.empty()) {
             std::cerr << "Invalid display name for API " << (int)apis[i] << "\n";
             exit(1);
@@ -36,12 +36,12 @@ int main() {
 
     // ensure unknown APIs return the empty string
     {
-        const std::string &name = RtAudio::getCompiledApiName((RtAudio::Api)-1);
+        const std::string name = RtAudio::getCompiledApiName((RtAudio::Api)-1);
         if (!name.empty()) {
             std::cerr << "Bad string for invalid API '" << name << "'\n";
             exit(1);
         }
-        const std::string &displayName = RtAudio::getCompiledApiDisplayName((RtAudio::Api)-1);
+        const std::string displayName = RtAudio::getCompiledApiDisplayName((RtAudio::Api)-1);
         if (displayName!="Unknown") {
             std::cerr << "Bad display string for invalid API '" << displayName << "'\n";
             exit(1);
@@ -49,7 +49,7 @@ int main() {
     }
 
     // try getting API identifier by name
-    std::cout << "API identifiers by name:\n";
+    std::cout << "API identifiers by name (C++):\n";
     for ( size_t i = 0; i < apis.size() ; ++i ) {
         std::string name = RtAudio::getCompiledApiName(apis[i]);
         if ( RtAudio::getCompiledApiByName(name) != apis[i] ) {
@@ -79,3 +79,79 @@ int main() {
 
     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_compiled_api_name(apis[i]);
+        if (name.empty()) {
+            std::cerr << "Invalid name for API " << (int)apis[i] << "\n";
+            exit(1);
+        }
+        const std::string displayName = rtaudio_compiled_api_display_name(apis[i]);
+        if (displayName.empty()) {
+            std::cerr << "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_compiled_api_name((rtaudio_api_t)-1);
+        const std::string name(s?s:"");
+        if (!name.empty()) {
+            std::cerr << "Bad string for invalid API '" << name << "'\n";
+            exit(1);
+        }
+        s = rtaudio_compiled_api_display_name((rtaudio_api_t)-1);
+        const std::string displayName(s?s:"");
+        if (displayName!="Unknown") {
+            std::cerr << "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_compiled_api_name(apis[i]);
+        std::string name(s?s:"");
+        if ( rtaudio_compiled_api_by_name(name.c_str()) != apis[i] ) {
+            std::cerr << "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::cerr << "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::cerr << "Bad identifier for unknown API name\n";
+            exit( 1 );
+        }
+    }
+
+    return 0;
+}
+
+int main()
+{
+    test_cpp();
+    test_c();
+}