You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rtaudio.cpp 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. #include <map>
  2. #include <algorithm>
  3. #pragma GCC diagnostic push
  4. #ifndef __clang__
  5. #pragma GCC diagnostic ignored "-Wsuggest-override"
  6. #endif
  7. #include <rtaudio/RtAudio.h>
  8. #pragma GCC diagnostic pop
  9. #include <rtaudio.hpp>
  10. #include <audio.hpp>
  11. #include <string.hpp>
  12. #include <math.hpp>
  13. #include <system.hpp>
  14. namespace rack {
  15. static const std::map<RtAudio::Api, std::string> RTAUDIO_API_NAMES = {
  16. {RtAudio::LINUX_ALSA, "ALSA"},
  17. {RtAudio::UNIX_JACK, "JACK"},
  18. {RtAudio::LINUX_PULSE, "PulseAudio"},
  19. {RtAudio::LINUX_OSS, "OSS"},
  20. {RtAudio::WINDOWS_WASAPI, "WASAPI"},
  21. {RtAudio::WINDOWS_ASIO, "ASIO"},
  22. {RtAudio::WINDOWS_DS, "DirectSound"},
  23. {RtAudio::MACOSX_CORE, "CoreAudio"},
  24. {RtAudio::RTAUDIO_DUMMY, "Dummy"},
  25. {RtAudio::UNSPECIFIED, "Unspecified"},
  26. };
  27. struct RtAudioDevice : audio::Device {
  28. RtAudio::Api api;
  29. int deviceId;
  30. RtAudio* rtAudio;
  31. RtAudio::DeviceInfo deviceInfo;
  32. RtAudio::StreamParameters inputParameters;
  33. RtAudio::StreamParameters outputParameters;
  34. RtAudio::StreamOptions options;
  35. int blockSize = 0;
  36. float sampleRate = 0;
  37. RtAudioDevice(RtAudio::Api api, int deviceId) {
  38. this->api = api;
  39. this->deviceId = deviceId;
  40. // Create RtAudio object
  41. INFO("Creating RtAudio %s device", RTAUDIO_API_NAMES.at(api).c_str());
  42. rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) {
  43. WARN("RtAudio error %d: %s", type, errorText.c_str());
  44. });
  45. rtAudio->showWarnings(false);
  46. try {
  47. // Query device ID
  48. deviceInfo = rtAudio->getDeviceInfo(deviceId);
  49. if (!deviceInfo.probed) {
  50. throw Exception("Failed to query RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  51. }
  52. openStream();
  53. }
  54. catch (Exception& e) {
  55. delete rtAudio;
  56. throw;
  57. }
  58. }
  59. ~RtAudioDevice() {
  60. closeStream();
  61. delete rtAudio;
  62. }
  63. void openStream() {
  64. // Open new device
  65. if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) {
  66. throw Exception("RtAudio %s device %d has 0 inputs and 0 outputs", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  67. }
  68. inputParameters = RtAudio::StreamParameters();
  69. inputParameters.deviceId = deviceId;
  70. inputParameters.nChannels = deviceInfo.inputChannels;
  71. inputParameters.firstChannel = 0;
  72. outputParameters = RtAudio::StreamParameters();
  73. outputParameters.deviceId = deviceId;
  74. outputParameters.nChannels = deviceInfo.outputChannels;
  75. outputParameters.firstChannel = 0;
  76. options = RtAudio::StreamOptions();
  77. // options.flags |= RTAUDIO_MINIMIZE_LATENCY;
  78. options.flags |= RTAUDIO_SCHEDULE_REALTIME;
  79. options.numberOfBuffers = 2;
  80. options.streamName = "VCV Rack";
  81. int32_t closestSampleRate = deviceInfo.preferredSampleRate;
  82. if (sampleRate > 0) {
  83. // Find the closest sample rate to the requested one.
  84. for (int32_t sr : deviceInfo.sampleRates) {
  85. if (std::fabs(sr - sampleRate) < std::fabs(closestSampleRate - sampleRate)) {
  86. closestSampleRate = sr;
  87. }
  88. }
  89. }
  90. if (blockSize <= 0) {
  91. // DirectSound should use a higher default block size
  92. if (api == RtAudio::WINDOWS_DS)
  93. blockSize = 1024;
  94. else
  95. blockSize = 256;
  96. }
  97. INFO("Opening RtAudio %s device %d: %s (%d in, %d out, %d sample rate, %d block size)", RTAUDIO_API_NAMES.at(api).c_str(), deviceId, deviceInfo.name.c_str(), inputParameters.nChannels, outputParameters.nChannels, closestSampleRate, blockSize);
  98. if (rtAudio->openStream(
  99. outputParameters.nChannels > 0 ? &outputParameters : NULL,
  100. inputParameters.nChannels > 0 ? &inputParameters : NULL,
  101. RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize,
  102. &rtAudioCallback, this, &options)) {
  103. throw Exception("Failed to open RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  104. }
  105. try {
  106. INFO("Starting RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  107. if (rtAudio->startStream()) {
  108. throw Exception("Failed to start RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  109. }
  110. // Update sample rate to actual value
  111. sampleRate = rtAudio->getStreamSampleRate();
  112. onStartStream();
  113. }
  114. catch (Exception& e) {
  115. rtAudio->closeStream();
  116. throw;
  117. }
  118. }
  119. void closeStream() {
  120. if (rtAudio->isStreamRunning()) {
  121. INFO("Stopping RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  122. rtAudio->stopStream();
  123. }
  124. if (rtAudio->isStreamOpen()) {
  125. INFO("Closing RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
  126. rtAudio->closeStream();
  127. }
  128. onStopStream();
  129. }
  130. std::string getName() override {
  131. return deviceInfo.name;
  132. }
  133. int getNumInputs() override {
  134. return inputParameters.nChannels;
  135. }
  136. int getNumOutputs() override {
  137. return outputParameters.nChannels;
  138. }
  139. std::set<float> getSampleRates() override {
  140. std::set<float> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end());
  141. return sampleRates;
  142. }
  143. float getSampleRate() override {
  144. return sampleRate;
  145. }
  146. void setSampleRate(float sampleRate) override {
  147. if (sampleRate == this->sampleRate)
  148. return;
  149. closeStream();
  150. this->sampleRate = sampleRate;
  151. openStream();
  152. }
  153. std::set<int> getBlockSizes() override {
  154. std::set<int> blockSizes;
  155. // 32 to 4096
  156. for (int i = 5; i <= 12; i++) {
  157. blockSizes.insert(1 << i);
  158. }
  159. return blockSizes;
  160. }
  161. int getBlockSize() override {
  162. return blockSize;
  163. }
  164. void setBlockSize(int blockSize) override {
  165. if (blockSize == this->blockSize)
  166. return;
  167. closeStream();
  168. this->blockSize = blockSize;
  169. openStream();
  170. }
  171. static int rtAudioCallback(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
  172. // fprintf(stderr, ".");
  173. // fflush(stderr);
  174. RtAudioDevice* that = (RtAudioDevice*) userData;
  175. assert(that);
  176. system::setThreadName("RtAudio");
  177. int inputStride = that->getNumInputs();
  178. int outputStride = that->getNumOutputs();
  179. try {
  180. that->processBuffer((const float*) inputBuffer, inputStride, (float*) outputBuffer, outputStride, nFrames);
  181. }
  182. catch (Exception& e) {
  183. // Log nothing to avoid spamming the log.
  184. }
  185. return 0;
  186. }
  187. };
  188. struct RtAudioDriver : audio::Driver {
  189. RtAudio::Api api;
  190. // deviceId -> Device
  191. std::map<int, RtAudioDevice*> devices;
  192. RtAudio* rtAudio = NULL;
  193. std::vector<RtAudio::DeviceInfo> deviceInfos;
  194. RtAudioDriver(RtAudio::Api api) {
  195. this->api = api;
  196. INFO("Creating RtAudio %s driver", RTAUDIO_API_NAMES.at(api).c_str());
  197. rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) {
  198. WARN("RtAudio error %d: %s", type, errorText.c_str());
  199. });
  200. rtAudio->showWarnings(false);
  201. // Cache DeviceInfos for performance and stability (especially for ASIO).
  202. if (api == RtAudio::WINDOWS_WASAPI || api == RtAudio::WINDOWS_ASIO || api == RtAudio::WINDOWS_DS) {
  203. int count = rtAudio->getDeviceCount();
  204. for (int deviceId = 0; deviceId < count; deviceId++) {
  205. RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
  206. INFO("Found RtAudio %s device %d: %s (%d in, %d out)", RTAUDIO_API_NAMES.at(api).c_str(), deviceId, deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
  207. deviceInfos.push_back(deviceInfo);
  208. }
  209. delete rtAudio;
  210. rtAudio = NULL;
  211. }
  212. }
  213. ~RtAudioDriver() {
  214. assert(devices.empty());
  215. if (rtAudio)
  216. delete rtAudio;
  217. }
  218. std::string getName() override {
  219. return RTAUDIO_API_NAMES.at(api);
  220. }
  221. std::vector<int> getDeviceIds() override {
  222. int count = 0;
  223. if (rtAudio) {
  224. count = rtAudio->getDeviceCount();
  225. }
  226. else {
  227. count = deviceInfos.size();
  228. }
  229. std::vector<int> deviceIds;
  230. for (int i = 0; i < count; i++)
  231. deviceIds.push_back(i);
  232. return deviceIds;
  233. }
  234. std::string getDeviceName(int deviceId) override {
  235. if (rtAudio) {
  236. int count = rtAudio->getDeviceCount();
  237. if (0 <= deviceId && deviceId < count) {
  238. RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
  239. return deviceInfo.name;
  240. }
  241. }
  242. else {
  243. if (0 <= deviceId && deviceId < (int) deviceInfos.size())
  244. return deviceInfos[deviceId].name;
  245. }
  246. return "";
  247. }
  248. int getDeviceNumInputs(int deviceId) override {
  249. if (rtAudio) {
  250. int count = rtAudio->getDeviceCount();
  251. if (0 <= deviceId && deviceId < count) {
  252. RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
  253. return deviceInfo.inputChannels;
  254. }
  255. }
  256. else {
  257. if (0 <= deviceId && deviceId < (int) deviceInfos.size())
  258. return deviceInfos[deviceId].inputChannels;
  259. }
  260. return 0;
  261. }
  262. int getDeviceNumOutputs(int deviceId) override {
  263. if (rtAudio) {
  264. int count = rtAudio->getDeviceCount();
  265. if (0 <= deviceId && deviceId < count) {
  266. RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
  267. return deviceInfo.outputChannels;
  268. }
  269. }
  270. else {
  271. if (0 <= deviceId && deviceId < (int) deviceInfos.size())
  272. return deviceInfos[deviceId].outputChannels;
  273. }
  274. return 0;
  275. }
  276. audio::Device* subscribe(int deviceId, audio::Port* port) override {
  277. RtAudioDevice* device;
  278. auto it = devices.find(deviceId);
  279. if (it == devices.end()) {
  280. // ASIO only allows one device to be used simultaneously
  281. if (api == RtAudio::WINDOWS_ASIO && devices.size() >= 1)
  282. throw Exception("ASIO driver only allows one audio device to be used simultaneously");
  283. // Can throw Exception
  284. device = new RtAudioDevice(api, deviceId);
  285. devices[deviceId] = device;
  286. }
  287. else {
  288. device = it->second;
  289. }
  290. device->subscribe(port);
  291. return device;
  292. }
  293. void unsubscribe(int deviceId, audio::Port* port) override {
  294. auto it = devices.find(deviceId);
  295. if (it == devices.end())
  296. return;
  297. RtAudioDevice* device = it->second;
  298. device->unsubscribe(port);
  299. if (device->subscribed.empty()) {
  300. devices.erase(it);
  301. delete device;
  302. }
  303. }
  304. };
  305. void rtaudioInit() {
  306. std::vector<RtAudio::Api> apis;
  307. RtAudio::getCompiledApi(apis);
  308. // I don't like the order returned by getCompiledApi(), so reorder it here.
  309. std::vector<RtAudio::Api> orderedApis = {
  310. RtAudio::LINUX_ALSA,
  311. RtAudio::LINUX_PULSE,
  312. RtAudio::UNIX_JACK,
  313. RtAudio::LINUX_OSS,
  314. RtAudio::WINDOWS_WASAPI,
  315. RtAudio::WINDOWS_ASIO,
  316. RtAudio::WINDOWS_DS,
  317. RtAudio::MACOSX_CORE,
  318. };
  319. for (RtAudio::Api api : orderedApis) {
  320. auto it = std::find(apis.begin(), apis.end(), api);
  321. if (it != apis.end()) {
  322. RtAudioDriver* driver = new RtAudioDriver(api);
  323. audio::addDriver((int) api, driver);
  324. }
  325. }
  326. }
  327. } // namespace rack