Audio plugin host https://kx.studio/carla
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.

CarlaEngineRtAudio.cpp 37KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. /*
  2. * Carla Plugin Host
  3. * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaEngineInternal.hpp"
  18. #include "CarlaBackendUtils.hpp"
  19. #include "CarlaMathUtils.hpp"
  20. #include "CarlaStringList.hpp"
  21. #include "RtLinkedList.hpp"
  22. #include "jackbridge/JackBridge.hpp"
  23. #include "rtaudio/RtAudio.h"
  24. #include "rtmidi/RtMidi.h"
  25. CARLA_BACKEND_START_NAMESPACE
  26. #if 0
  27. } // Fix editor indentation
  28. #endif
  29. // -------------------------------------------------------------------------------------------------------------------
  30. // Global static data
  31. static CharStringListPtr gDeviceNames;
  32. static std::vector<RtAudio::Api> gRtAudioApis;
  33. // -------------------------------------------------------------------------------------------------------------------
  34. static void initRtAudioAPIsIfNeeded()
  35. {
  36. static bool needsInit = true;
  37. if (! needsInit)
  38. return;
  39. needsInit = false;
  40. RtAudio::getCompiledApi(gRtAudioApis);
  41. std::vector<RtAudio::Api>::iterator it;
  42. // remove JACK if not available
  43. if (! jackbridge_is_ok())
  44. {
  45. it = std::find(gRtAudioApis.begin(), gRtAudioApis.end(), RtAudio::UNIX_JACK);
  46. if (it != gRtAudioApis.end()) gRtAudioApis.erase(it);
  47. }
  48. #ifdef HAVE_JUCE
  49. // prefer juce to handle some APIs
  50. it = std::find(gRtAudioApis.begin(), gRtAudioApis.end(), RtAudio::LINUX_ALSA);
  51. if (it != gRtAudioApis.end()) gRtAudioApis.erase(it);
  52. it = std::find(gRtAudioApis.begin(), gRtAudioApis.end(), RtAudio::MACOSX_CORE);
  53. if (it != gRtAudioApis.end()) gRtAudioApis.erase(it);
  54. it = std::find(gRtAudioApis.begin(), gRtAudioApis.end(), RtAudio::WINDOWS_ASIO);
  55. if (it != gRtAudioApis.end()) gRtAudioApis.erase(it);
  56. it = std::find(gRtAudioApis.begin(), gRtAudioApis.end(), RtAudio::WINDOWS_DS);
  57. if (it != gRtAudioApis.end()) gRtAudioApis.erase(it);
  58. #endif
  59. }
  60. static const char* getRtAudioApiName(const RtAudio::Api api) noexcept
  61. {
  62. switch (api)
  63. {
  64. case RtAudio::UNSPECIFIED:
  65. return "Unspecified";
  66. case RtAudio::LINUX_ALSA:
  67. return "ALSA";
  68. case RtAudio::LINUX_PULSE:
  69. return "PulseAudio";
  70. case RtAudio::LINUX_OSS:
  71. return "OSS";
  72. case RtAudio::UNIX_JACK:
  73. #if defined(CARLA_OS_WIN)
  74. return "JACK with WinMM";
  75. #elif defined(CARLA_OS_MAC)
  76. return "JACK with CoreMidi";
  77. #elif defined(CARLA_OS_LINUX)
  78. return "JACK with ALSA-MIDI";
  79. #else
  80. return "JACK (RtAudio)";
  81. #endif
  82. case RtAudio::MACOSX_CORE:
  83. return "CoreAudio";
  84. case RtAudio::WINDOWS_ASIO:
  85. return "ASIO";
  86. case RtAudio::WINDOWS_DS:
  87. return "DirectSound";
  88. case RtAudio::RTAUDIO_DUMMY:
  89. return "Dummy";
  90. }
  91. carla_stderr("CarlaBackend::getRtAudioApiName(%i) - invalid API", api);
  92. return nullptr;
  93. }
  94. static RtMidi::Api getMatchedAudioMidiAPI(const RtAudio::Api rtApi) noexcept
  95. {
  96. switch (rtApi)
  97. {
  98. case RtAudio::UNSPECIFIED:
  99. return RtMidi::UNSPECIFIED;
  100. case RtAudio::LINUX_ALSA:
  101. case RtAudio::LINUX_OSS:
  102. case RtAudio::LINUX_PULSE:
  103. return RtMidi::LINUX_ALSA;
  104. case RtAudio::UNIX_JACK:
  105. #if defined(CARLA_OS_WIN)
  106. return RtMidi::WINDOWS_MM;
  107. #elif defined(CARLA_OS_MAC)
  108. return RtMidi::MACOSX_CORE;
  109. #elif defined(CARLA_OS_LINUX)
  110. return RtMidi::LINUX_ALSA;
  111. #else
  112. return RtMidi::UNIX_JACK;
  113. #endif
  114. case RtAudio::MACOSX_CORE:
  115. return RtMidi::MACOSX_CORE;
  116. case RtAudio::WINDOWS_ASIO:
  117. case RtAudio::WINDOWS_DS:
  118. return RtMidi::WINDOWS_MM;
  119. case RtAudio::RTAUDIO_DUMMY:
  120. return RtMidi::RTMIDI_DUMMY;
  121. }
  122. return RtMidi::UNSPECIFIED;
  123. }
  124. // -------------------------------------------------------------------------------------------------------------------
  125. // RtAudio Engine
  126. class CarlaEngineRtAudio : public CarlaEngine
  127. {
  128. public:
  129. CarlaEngineRtAudio(const RtAudio::Api api)
  130. : CarlaEngine(),
  131. fAudio(api),
  132. fAudioInCount(0),
  133. fAudioOutCount(0),
  134. fLastEventTime(0),
  135. fMidiOutVector(3)
  136. {
  137. carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
  138. // just to make sure
  139. pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
  140. }
  141. ~CarlaEngineRtAudio() override
  142. {
  143. CARLA_SAFE_ASSERT(fAudioInCount == 0);
  144. CARLA_SAFE_ASSERT(fAudioOutCount == 0);
  145. CARLA_SAFE_ASSERT(fLastEventTime == 0);
  146. carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
  147. }
  148. // -------------------------------------
  149. bool init(const char* const clientName) override
  150. {
  151. CARLA_SAFE_ASSERT_RETURN(fAudioInCount == 0, false);
  152. CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
  153. CARLA_SAFE_ASSERT_RETURN(fLastEventTime == 0, false);
  154. CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
  155. carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
  156. if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
  157. {
  158. setLastError("Invalid process mode");
  159. return false;
  160. }
  161. const uint devCount(fAudio.getDeviceCount());
  162. if (devCount == 0)
  163. {
  164. setLastError("No audio devices available for this driver");
  165. return false;
  166. }
  167. RtAudio::StreamParameters iParams, oParams;
  168. bool deviceSet = false;
  169. if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
  170. {
  171. for (uint i=0; i < devCount; ++i)
  172. {
  173. RtAudio::DeviceInfo devInfo(fAudio.getDeviceInfo(i));
  174. if (devInfo.probed && devInfo.outputChannels > 0 && devInfo.name == pData->options.audioDevice)
  175. {
  176. deviceSet = true;
  177. fDeviceName = devInfo.name.c_str();
  178. iParams.deviceId = i;
  179. oParams.deviceId = i;
  180. iParams.nChannels = devInfo.inputChannels;
  181. oParams.nChannels = devInfo.outputChannels;
  182. break;
  183. }
  184. }
  185. }
  186. if (! deviceSet)
  187. {
  188. iParams.deviceId = fAudio.getDefaultInputDevice();
  189. oParams.deviceId = fAudio.getDefaultOutputDevice();
  190. iParams.nChannels = fAudio.getDeviceInfo(iParams.deviceId).inputChannels;
  191. oParams.nChannels = fAudio.getDeviceInfo(oParams.deviceId).outputChannels;
  192. carla_stdout("No device set, using %i inputs and %i outputs", iParams.nChannels, oParams.nChannels);
  193. }
  194. if (oParams.nChannels == 0)
  195. {
  196. setLastError("Current audio setup has no outputs, cannot continue");
  197. return false;
  198. }
  199. RtAudio::StreamOptions rtOptions;
  200. rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME | RTAUDIO_NONINTERLEAVED;
  201. rtOptions.streamName = clientName;
  202. rtOptions.priority = 85;
  203. if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA && ! deviceSet)
  204. rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
  205. uint bufferFrames = pData->options.audioBufferSize;
  206. try {
  207. fAudio.openStream(&oParams, (iParams.nChannels > 0) ? &iParams : nullptr, RTAUDIO_FLOAT32, pData->options.audioSampleRate, &bufferFrames, carla_rtaudio_process_callback, this, &rtOptions);
  208. }
  209. catch (const RtError& e) {
  210. setLastError(e.what());
  211. return false;
  212. }
  213. pData->bufferSize = bufferFrames;
  214. pData->sampleRate = fAudio.getStreamSampleRate();
  215. fAudioInCount = iParams.nChannels;
  216. fAudioOutCount = oParams.nChannels;
  217. fLastEventTime = 0;
  218. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  219. {
  220. pData->audio.inCount = 2;
  221. pData->audio.outCount = 2;
  222. }
  223. else
  224. {
  225. pData->audio.inCount = 0;
  226. pData->audio.outCount = 0;
  227. }
  228. pData->audio.create(pData->bufferSize);
  229. try {
  230. fAudio.startStream();
  231. }
  232. catch (const RtError& e)
  233. {
  234. close();
  235. setLastError(e.what());
  236. return false;
  237. }
  238. CarlaEngine::init(clientName);
  239. pData->audio.isReady = true;
  240. patchbayRefresh();
  241. return true;
  242. }
  243. bool close() override
  244. {
  245. CARLA_SAFE_ASSERT(fAudioOutCount != 0);
  246. carla_debug("CarlaEngineRtAudio::close()");
  247. pData->audio.isReady = false;
  248. bool hasError = !CarlaEngine::close();
  249. if (fAudio.isStreamOpen())
  250. {
  251. if (fAudio.isStreamRunning())
  252. {
  253. try {
  254. fAudio.stopStream();
  255. }
  256. catch (const RtError& e)
  257. {
  258. if (! hasError)
  259. {
  260. setLastError(e.what());
  261. hasError = true;
  262. }
  263. }
  264. }
  265. fAudio.closeStream();
  266. }
  267. for (LinkedList<MidiInPort>::Itenerator it = fMidiIns.begin(); it.valid(); it.next())
  268. {
  269. MidiInPort& inPort(it.getValue());
  270. CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
  271. inPort.port->cancelCallback();
  272. inPort.port->closePort();
  273. delete inPort.port;
  274. }
  275. fMidiIns.clear();
  276. fMidiInEvents.clear();
  277. fMidiOutMutex.lock();
  278. for (LinkedList<MidiOutPort>::Itenerator it = fMidiOuts.begin(); it.valid(); it.next())
  279. {
  280. MidiOutPort& outPort(it.getValue());
  281. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  282. outPort.port->closePort();
  283. delete outPort.port;
  284. }
  285. fMidiOuts.clear();
  286. fMidiOutMutex.unlock();
  287. fAudioInCount = 0;
  288. fAudioOutCount = 0;
  289. fLastEventTime = 0;
  290. fDeviceName.clear();
  291. return !hasError;
  292. }
  293. bool isRunning() const noexcept override
  294. {
  295. return fAudio.isStreamRunning();
  296. }
  297. bool isOffline() const noexcept override
  298. {
  299. return false;
  300. }
  301. EngineType getType() const noexcept override
  302. {
  303. return kEngineTypeRtAudio;
  304. }
  305. const char* getCurrentDriverName() const noexcept override
  306. {
  307. return CarlaBackend::getRtAudioApiName(fAudio.getCurrentApi());
  308. }
  309. // -------------------------------------------------------------------
  310. // Patchbay
  311. bool patchbayRefresh() override
  312. {
  313. CARLA_SAFE_ASSERT_RETURN(pData->audio.isReady, false);
  314. if (pData->graph.isRack)
  315. patchbayRefreshRack();
  316. else
  317. patchbayRefreshPatchbay();
  318. return true;
  319. }
  320. void patchbayRefreshRack()
  321. {
  322. RackGraph* const rack(pData->graph.rack);
  323. rack->connections.clear();
  324. char strBuf[STR_MAX+1];
  325. strBuf[STR_MAX] = '\0';
  326. // Main
  327. {
  328. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_CARLA, PATCHBAY_ICON_CARLA, -1, 0.0f, getName());
  329. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN1, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in1");
  330. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN2, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in2");
  331. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT1, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out1");
  332. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT2, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out2");
  333. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_IN, PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, "midi-in");
  334. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_OUT, PATCHBAY_PORT_TYPE_MIDI, 0.0f, "midi-out");
  335. }
  336. // Audio In
  337. {
  338. if (fDeviceName.isNotEmpty())
  339. std::snprintf(strBuf, STR_MAX, "Capture (%s)", fDeviceName.buffer());
  340. else
  341. std::strncpy(strBuf, "Capture", STR_MAX);
  342. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_AUDIO_IN, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  343. for (uint i=0; i < fAudioInCount; ++i)
  344. {
  345. std::snprintf(strBuf, STR_MAX, "capture_%i", i+1);
  346. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_AUDIO_IN, static_cast<int>(i), PATCHBAY_PORT_TYPE_AUDIO, 0.0f, strBuf);
  347. }
  348. }
  349. // Audio Out
  350. {
  351. if (fDeviceName.isNotEmpty())
  352. std::snprintf(strBuf, STR_MAX, "Playback (%s)", fDeviceName.buffer());
  353. else
  354. std::strncpy(strBuf, "Playback", STR_MAX);
  355. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_AUDIO_OUT, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  356. for (uint i=0; i < fAudioOutCount; ++i)
  357. {
  358. std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
  359. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_AUDIO_OUT, static_cast<int>(i), PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, strBuf);
  360. }
  361. }
  362. // MIDI In
  363. {
  364. RtMidiIn midiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery");
  365. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_MIDI_IN, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Readable MIDI ports");
  366. for (uint i=0, count = midiIn.getPortCount(); i < count; ++i)
  367. {
  368. std::string portName(midiIn.getPortName(i));
  369. std::snprintf(strBuf, STR_MAX, "Readable MIDI ports:%s", portName.c_str());
  370. PortNameToId portNameToId;
  371. portNameToId.setData(RACK_GRAPH_GROUP_MIDI_IN, i, portName.c_str(), strBuf);
  372. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, portNameToId.group, static_cast<int>(portNameToId.port), PATCHBAY_PORT_TYPE_MIDI, 0.0f, portNameToId.name);
  373. rack->midi.ins.append(portNameToId);
  374. }
  375. }
  376. // MIDI Out
  377. {
  378. RtMidiOut midiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery");
  379. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_MIDI_OUT, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Writable MIDI ports");
  380. for (uint i=0, count = midiOut.getPortCount(); i < count; ++i)
  381. {
  382. std::string portName(midiOut.getPortName(i));
  383. std::snprintf(strBuf, STR_MAX, "Writable MIDI ports:%s", portName.c_str());
  384. PortNameToId portNameToId;
  385. portNameToId.setData(RACK_GRAPH_GROUP_MIDI_OUT, i, portName.c_str(), strBuf);
  386. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, portNameToId.group, static_cast<int>(portNameToId.port), PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, portNameToId.name);
  387. rack->midi.outs.append(portNameToId);
  388. }
  389. }
  390. // Connections
  391. rack->audio.mutex.lock();
  392. for (LinkedList<uint>::Itenerator it = rack->audio.connectedIn1.begin(); it.valid(); it.next())
  393. {
  394. const uint& portId(it.getValue());
  395. CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioInCount);
  396. ConnectionToId connectionToId;
  397. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_AUDIO_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN1);
  398. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  399. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  400. rack->connections.list.append(connectionToId);
  401. }
  402. for (LinkedList<uint>::Itenerator it = rack->audio.connectedIn2.begin(); it.valid(); it.next())
  403. {
  404. const uint& portId(it.getValue());
  405. CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioInCount);
  406. ConnectionToId connectionToId;
  407. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_AUDIO_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN2);
  408. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  409. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  410. rack->connections.list.append(connectionToId);
  411. }
  412. for (LinkedList<uint>::Itenerator it = rack->audio.connectedOut1.begin(); it.valid(); it.next())
  413. {
  414. const uint& portId(it.getValue());
  415. CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioOutCount);
  416. ConnectionToId connectionToId;
  417. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT1, RACK_GRAPH_GROUP_AUDIO_OUT, portId);
  418. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  419. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  420. rack->connections.list.append(connectionToId);
  421. }
  422. for (LinkedList<uint>::Itenerator it = rack->audio.connectedOut2.begin(); it.valid(); it.next())
  423. {
  424. const uint& portId(it.getValue());
  425. CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioOutCount);
  426. ConnectionToId connectionToId;
  427. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT2, RACK_GRAPH_GROUP_AUDIO_OUT, portId);
  428. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  429. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  430. rack->connections.list.append(connectionToId);
  431. }
  432. rack->audio.mutex.unlock();
  433. for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin(); it.valid(); it.next())
  434. {
  435. const MidiInPort& inPort(it.getValue());
  436. const uint portId(rack->midi.getPortId(true, inPort.name));
  437. CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.ins.count());
  438. ConnectionToId connectionToId;
  439. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_MIDI_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_IN);
  440. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  441. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  442. rack->connections.list.append(connectionToId);
  443. }
  444. fMidiOutMutex.lock();
  445. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin(); it.valid(); it.next())
  446. {
  447. const MidiOutPort& outPort(it.getValue());
  448. const uint portId(rack->midi.getPortId(false, outPort.name));
  449. CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.outs.count());
  450. ConnectionToId connectionToId;
  451. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_OUT, RACK_GRAPH_GROUP_MIDI_OUT, portId);
  452. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  453. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  454. rack->connections.list.append(connectionToId);
  455. }
  456. fMidiOutMutex.unlock();
  457. }
  458. void patchbayRefreshPatchbay() noexcept
  459. {
  460. }
  461. // -------------------------------------------------------------------
  462. protected:
  463. void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer, uint nframes, double streamTime, RtAudioStreamStatus status)
  464. {
  465. // get buffers from RtAudio
  466. const float* const insPtr = (const float*)inputBuffer;
  467. float* const outsPtr = (float*)outputBuffer;
  468. // assert rtaudio buffers
  469. CARLA_SAFE_ASSERT_RETURN(outputBuffer != nullptr, runPendingRtEvents());
  470. CARLA_SAFE_ASSERT_RETURN(pData->bufferSize == nframes, runPendingRtEvents());
  471. if (! pData->audio.isReady)
  472. return runPendingRtEvents();
  473. // initialize rtaudio input
  474. const float* inBuf[fAudioInCount];
  475. for (uint i=0; i < fAudioInCount; ++i)
  476. inBuf[i] = insPtr+(nframes*i);
  477. // initialize rtaudio output
  478. float* outBuf[fAudioOutCount];
  479. for (uint i=0; i < fAudioOutCount; ++i)
  480. outBuf[i] = outsPtr+(nframes*i);
  481. FLOAT_CLEAR(outsPtr, nframes*fAudioOutCount);
  482. // initialize events
  483. carla_zeroStruct<EngineEvent>(pData->events.in, kMaxEngineEventInternalCount);
  484. carla_zeroStruct<EngineEvent>(pData->events.out, kMaxEngineEventInternalCount);
  485. if (fMidiInEvents.mutex.tryLock())
  486. {
  487. uint32_t engineEventIndex = 0;
  488. fMidiInEvents.splice();
  489. for (LinkedList<RtMidiEvent>::Itenerator it = fMidiInEvents.data.begin(); it.valid(); it.next())
  490. {
  491. const RtMidiEvent& midiEvent(it.getValue());
  492. EngineEvent& engineEvent(pData->events.in[engineEventIndex++]);
  493. if (midiEvent.time < pData->timeInfo.frame)
  494. {
  495. engineEvent.time = 0;
  496. }
  497. else if (midiEvent.time >= pData->timeInfo.frame + nframes)
  498. {
  499. carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame);
  500. engineEvent.time = static_cast<uint32_t>(pData->timeInfo.frame) + nframes - 1;
  501. }
  502. else
  503. engineEvent.time = static_cast<uint32_t>(midiEvent.time - pData->timeInfo.frame);
  504. engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data);
  505. if (engineEventIndex >= kMaxEngineEventInternalCount)
  506. break;
  507. }
  508. fMidiInEvents.data.clear();
  509. fMidiInEvents.mutex.unlock();
  510. }
  511. if (pData->graph.isRack)
  512. {
  513. pData->processRackFull(inBuf, fAudioInCount, outBuf, fAudioOutCount, nframes, false);
  514. }
  515. else
  516. {
  517. }
  518. fMidiOutMutex.lock();
  519. if (fMidiOuts.count() > 0)
  520. {
  521. uint8_t size = 0;
  522. uint8_t data[3] = { 0, 0, 0 };
  523. const uint8_t* dataPtr = data;
  524. for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
  525. {
  526. const EngineEvent& engineEvent(pData->events.out[i]);
  527. if (engineEvent.type == kEngineEventTypeNull)
  528. break;
  529. else if (engineEvent.type == kEngineEventTypeControl)
  530. {
  531. const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
  532. ctrlEvent.convertToMidiData(engineEvent.channel, size, data);
  533. dataPtr = data;
  534. }
  535. else if (engineEvent.type == kEngineEventTypeMidi)
  536. {
  537. const EngineMidiEvent& midiEvent(engineEvent.midi);
  538. size = midiEvent.size;
  539. if (size > EngineMidiEvent::kDataSize && midiEvent.dataExt != nullptr)
  540. dataPtr = midiEvent.dataExt;
  541. else
  542. dataPtr = midiEvent.data;
  543. }
  544. else
  545. {
  546. continue;
  547. }
  548. if (size > 0)
  549. {
  550. fMidiOutVector.assign(dataPtr, dataPtr + size);
  551. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin(); it.valid(); it.next())
  552. {
  553. MidiOutPort& outPort(it.getValue());
  554. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  555. outPort.port->sendMessage(&fMidiOutVector);
  556. }
  557. }
  558. }
  559. }
  560. fMidiOutMutex.unlock();
  561. runPendingRtEvents();
  562. return;
  563. // unused
  564. (void)streamTime;
  565. (void)status;
  566. }
  567. void handleMidiCallback(double timeStamp, std::vector<uchar>* const message)
  568. {
  569. if (! pData->audio.isReady)
  570. return;
  571. const size_t messageSize(message->size());
  572. if (messageSize == 0 || messageSize > EngineMidiEvent::kDataSize)
  573. return;
  574. timeStamp /= 2;
  575. if (timeStamp > 0.95)
  576. timeStamp = 0.95;
  577. else if (timeStamp < 0.0)
  578. timeStamp = 0.0;
  579. RtMidiEvent midiEvent;
  580. midiEvent.time = pData->timeInfo.frame + uint64_t(timeStamp * (double)pData->bufferSize);
  581. if (midiEvent.time < fLastEventTime)
  582. midiEvent.time = fLastEventTime;
  583. else
  584. fLastEventTime = midiEvent.time;
  585. midiEvent.size = static_cast<uint8_t>(messageSize);
  586. size_t i=0;
  587. for (; i < messageSize; ++i)
  588. midiEvent.data[i] = message->at(i);
  589. for (; i < EngineMidiEvent::kDataSize; ++i)
  590. midiEvent.data[i] = 0;
  591. fMidiInEvents.append(midiEvent);
  592. }
  593. // -------------------------------------------------------------------
  594. bool connectRackMidiInPort(const char* const portName) override
  595. {
  596. CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false);
  597. carla_debug("CarlaEngineRtAudio::connectRackMidiInPort(\"%s\")", portName);
  598. RackGraph* const rack(pData->graph.rack);
  599. CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false);
  600. CarlaString newRtMidiPortName;
  601. newRtMidiPortName += getName();
  602. newRtMidiPortName += ":";
  603. newRtMidiPortName += portName;
  604. RtMidiIn* const rtMidiIn(new RtMidiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer(), 512));
  605. rtMidiIn->ignoreTypes();
  606. rtMidiIn->setCallback(carla_rtmidi_callback, this);
  607. bool found = false;
  608. uint rtMidiPortIndex;
  609. for (uint i=0, count=rtMidiIn->getPortCount(); i < count; ++i)
  610. {
  611. if (rtMidiIn->getPortName(i) == portName)
  612. {
  613. found = true;
  614. rtMidiPortIndex = i;
  615. break;
  616. }
  617. }
  618. if (! found)
  619. {
  620. delete rtMidiIn;
  621. return false;
  622. }
  623. try {
  624. rtMidiIn->openPort(rtMidiPortIndex, portName);
  625. }
  626. catch(...) {
  627. delete rtMidiIn;
  628. return false;
  629. };
  630. MidiInPort midiPort;
  631. midiPort.port = rtMidiIn;
  632. std::strncpy(midiPort.name, portName, STR_MAX);
  633. midiPort.name[STR_MAX] = '\0';
  634. fMidiIns.append(midiPort);
  635. return true;
  636. }
  637. bool connectRackMidiOutPort(const char* const portName) override
  638. {
  639. CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false);
  640. carla_debug("CarlaEngineRtAudio::connectRackMidiOutPort(\"%s\")", portName);
  641. RackGraph* const rack(pData->graph.rack);
  642. CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false);
  643. CarlaString newRtMidiPortName;
  644. newRtMidiPortName += getName();
  645. newRtMidiPortName += ":";
  646. newRtMidiPortName += portName;
  647. RtMidiOut* const rtMidiOut(new RtMidiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer()));
  648. bool found = false;
  649. uint rtMidiPortIndex;
  650. for (uint i=0, count=rtMidiOut->getPortCount(); i < count; ++i)
  651. {
  652. if (rtMidiOut->getPortName(i) == portName)
  653. {
  654. found = true;
  655. rtMidiPortIndex = i;
  656. break;
  657. }
  658. }
  659. if (! found)
  660. {
  661. delete rtMidiOut;
  662. return false;
  663. }
  664. try {
  665. rtMidiOut->openPort(rtMidiPortIndex, portName);
  666. }
  667. catch(...) {
  668. delete rtMidiOut;
  669. return false;
  670. };
  671. MidiOutPort midiPort;
  672. midiPort.port = rtMidiOut;
  673. std::strncpy(midiPort.name, portName, STR_MAX);
  674. midiPort.name[STR_MAX] = '\0';
  675. const CarlaMutexLocker cml(fMidiOutMutex);
  676. fMidiOuts.append(midiPort);
  677. return true;
  678. }
  679. bool disconnectRackMidiInPort(const char* const portName) override
  680. {
  681. CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false);
  682. carla_debug("CarlaEngineRtAudio::disconnectRackMidiInPort(\"%s\")", portName);
  683. RackGraph* const rack(pData->graph.rack);
  684. CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false);
  685. for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin(); it.valid(); it.next())
  686. {
  687. MidiInPort& inPort(it.getValue());
  688. CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
  689. if (std::strcmp(inPort.name, portName) != 0)
  690. continue;
  691. inPort.port->cancelCallback();
  692. inPort.port->closePort();
  693. delete inPort.port;
  694. fMidiIns.remove(it);
  695. return true;
  696. }
  697. return false;
  698. }
  699. bool disconnectRackMidiOutPort(const char* const portName) override
  700. {
  701. CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false);
  702. carla_debug("CarlaEngineRtAudio::disconnectRackMidiOutPort(\"%s\")", portName);
  703. RackGraph* const rack(pData->graph.rack);
  704. CARLA_SAFE_ASSERT_RETURN(rack->midi.outs.count() > 0, false);
  705. const CarlaMutexLocker cml(fMidiOutMutex);
  706. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin(); it.valid(); it.next())
  707. {
  708. MidiOutPort& outPort(it.getValue());
  709. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  710. if (std::strcmp(outPort.name, portName) != 0)
  711. continue;
  712. outPort.port->closePort();
  713. delete outPort.port;
  714. fMidiOuts.remove(it);
  715. return true;
  716. }
  717. return false;
  718. }
  719. // -------------------------------------------------------------------
  720. private:
  721. RtAudio fAudio;
  722. // useful info
  723. uint fAudioInCount;
  724. uint fAudioOutCount;
  725. uint64_t fLastEventTime;
  726. // current device name
  727. CarlaString fDeviceName;
  728. struct MidiInPort {
  729. RtMidiIn* port;
  730. char name[STR_MAX+1];
  731. };
  732. struct MidiOutPort {
  733. RtMidiOut* port;
  734. char name[STR_MAX+1];
  735. };
  736. struct RtMidiEvent {
  737. uint64_t time; // needs to compare to internal time
  738. uint8_t size;
  739. uint8_t data[EngineMidiEvent::kDataSize];
  740. };
  741. struct RtMidiEvents {
  742. CarlaMutex mutex;
  743. RtLinkedList<RtMidiEvent>::Pool dataPool;
  744. RtLinkedList<RtMidiEvent> data;
  745. RtLinkedList<RtMidiEvent> dataPending;
  746. RtMidiEvents()
  747. : dataPool(512, 512),
  748. data(dataPool),
  749. dataPending(dataPool) {}
  750. ~RtMidiEvents()
  751. {
  752. clear();
  753. }
  754. void append(const RtMidiEvent& event)
  755. {
  756. mutex.lock();
  757. dataPending.append(event);
  758. mutex.unlock();
  759. }
  760. void clear()
  761. {
  762. mutex.lock();
  763. data.clear();
  764. dataPending.clear();
  765. mutex.unlock();
  766. }
  767. void splice()
  768. {
  769. dataPending.spliceAppendTo(data);
  770. }
  771. };
  772. LinkedList<MidiInPort> fMidiIns;
  773. RtMidiEvents fMidiInEvents;
  774. LinkedList<MidiOutPort> fMidiOuts;
  775. CarlaMutex fMidiOutMutex;
  776. std::vector<uint8_t> fMidiOutVector;
  777. #define handlePtr ((CarlaEngineRtAudio*)userData)
  778. static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, uint nframes, double streamTime, RtAudioStreamStatus status, void* userData)
  779. {
  780. handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
  781. return 0;
  782. }
  783. static void carla_rtmidi_callback(double timeStamp, std::vector<uchar>* message, void* userData)
  784. {
  785. handlePtr->handleMidiCallback(timeStamp, message);
  786. }
  787. #undef handlePtr
  788. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
  789. };
  790. // -----------------------------------------
  791. CarlaEngine* CarlaEngine::newRtAudio(const AudioApi api)
  792. {
  793. initRtAudioAPIsIfNeeded();
  794. RtAudio::Api rtApi(RtAudio::UNSPECIFIED);
  795. switch (api)
  796. {
  797. case AUDIO_API_NULL:
  798. rtApi = RtAudio::RTAUDIO_DUMMY;
  799. break;
  800. case AUDIO_API_JACK:
  801. rtApi = RtAudio::UNIX_JACK;
  802. break;
  803. case AUDIO_API_ALSA:
  804. rtApi = RtAudio::LINUX_ALSA;
  805. break;
  806. case AUDIO_API_OSS:
  807. rtApi = RtAudio::LINUX_OSS;
  808. break;
  809. case AUDIO_API_PULSE:
  810. rtApi = RtAudio::LINUX_PULSE;
  811. break;
  812. case AUDIO_API_CORE:
  813. rtApi = RtAudio::MACOSX_CORE;
  814. break;
  815. case AUDIO_API_ASIO:
  816. rtApi = RtAudio::WINDOWS_ASIO;
  817. break;
  818. case AUDIO_API_DS:
  819. rtApi = RtAudio::WINDOWS_DS;
  820. break;
  821. }
  822. return new CarlaEngineRtAudio(rtApi);
  823. }
  824. uint CarlaEngine::getRtAudioApiCount()
  825. {
  826. initRtAudioAPIsIfNeeded();
  827. return static_cast<uint>(gRtAudioApis.size());
  828. }
  829. const char* CarlaEngine::getRtAudioApiName(const uint index)
  830. {
  831. initRtAudioAPIsIfNeeded();
  832. CARLA_SAFE_ASSERT_RETURN(index < gRtAudioApis.size(), nullptr);
  833. return CarlaBackend::getRtAudioApiName(gRtAudioApis[index]);
  834. }
  835. const char* const* CarlaEngine::getRtAudioApiDeviceNames(const uint index)
  836. {
  837. initRtAudioAPIsIfNeeded();
  838. if (index >= gRtAudioApis.size())
  839. return nullptr;
  840. const RtAudio::Api& api(gRtAudioApis[index]);
  841. RtAudio rtAudio(api);
  842. const uint devCount(rtAudio.getDeviceCount());
  843. if (devCount == 0)
  844. return nullptr;
  845. CarlaStringList devNames;
  846. for (uint i=0; i < devCount; ++i)
  847. {
  848. RtAudio::DeviceInfo devInfo(rtAudio.getDeviceInfo(i));
  849. if (devInfo.probed && devInfo.outputChannels > 0 /*&& (devInfo.nativeFormats & RTAUDIO_FLOAT32) != 0*/)
  850. devNames.append(devInfo.name.c_str());
  851. }
  852. gDeviceNames = devNames.toCharStringListPtr();
  853. return gDeviceNames;
  854. }
  855. const EngineDriverDeviceInfo* CarlaEngine::getRtAudioDeviceInfo(const uint index, const char* const deviceName)
  856. {
  857. initRtAudioAPIsIfNeeded();
  858. if (index >= gRtAudioApis.size())
  859. return nullptr;
  860. const RtAudio::Api& api(gRtAudioApis[index]);
  861. RtAudio rtAudio(api);
  862. const uint devCount(rtAudio.getDeviceCount());
  863. if (devCount == 0)
  864. return nullptr;
  865. uint i;
  866. RtAudio::DeviceInfo rtAudioDevInfo;
  867. for (i=0; i < devCount; ++i)
  868. {
  869. rtAudioDevInfo = rtAudio.getDeviceInfo(i);
  870. if (rtAudioDevInfo.name == deviceName)
  871. break;
  872. }
  873. if (i == devCount)
  874. return nullptr;
  875. static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
  876. static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
  877. static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };
  878. // reset
  879. devInfo.hints = 0x0;
  880. devInfo.bufferSizes = dummyBufferSizes;
  881. // cleanup
  882. if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
  883. {
  884. delete[] devInfo.sampleRates;
  885. devInfo.sampleRates = nullptr;
  886. }
  887. if (size_t sampleRatesCount = rtAudioDevInfo.sampleRates.size())
  888. {
  889. double* const sampleRates(new double[sampleRatesCount+1]);
  890. for (size_t j=0; j < sampleRatesCount; ++j)
  891. sampleRates[j] = rtAudioDevInfo.sampleRates[j];
  892. sampleRates[sampleRatesCount] = 0.0;
  893. devInfo.sampleRates = sampleRates;
  894. }
  895. else
  896. {
  897. devInfo.sampleRates = dummySampleRates;
  898. }
  899. return &devInfo;
  900. }
  901. // -----------------------------------------
  902. CARLA_BACKEND_END_NAMESPACE