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 41KB

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
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
9 years ago
11 years ago
10 years ago
10 years ago
10 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
11 years ago
11 years ago
11 years ago
11 years ago
11 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
11 years ago
11 years ago
11 years ago
11 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328
  1. /*
  2. * Carla Plugin Host
  3. * Copyright (C) 2011-2020 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 "CarlaEngineGraph.hpp"
  18. #include "CarlaEngineInit.hpp"
  19. #include "CarlaEngineInternal.hpp"
  20. #include "CarlaBackendUtils.hpp"
  21. #include "CarlaMathUtils.hpp"
  22. #include "CarlaStringList.hpp"
  23. #include "RtLinkedList.hpp"
  24. #include "jackbridge/JackBridge.hpp"
  25. #if defined(__clang__)
  26. # pragma clang diagnostic push
  27. # pragma clang diagnostic ignored "-Wconversion"
  28. # pragma clang diagnostic ignored "-Wdeprecated-copy"
  29. # pragma clang diagnostic ignored "-Weffc++"
  30. # pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
  31. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  32. # pragma GCC diagnostic push
  33. # pragma GCC diagnostic ignored "-Wconversion"
  34. # pragma GCC diagnostic ignored "-Weffc++"
  35. # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
  36. #endif
  37. #include "rtaudio/RtAudio.h"
  38. #include "rtmidi/RtMidi.h"
  39. #if defined(__clang__)
  40. # pragma clang diagnostic pop
  41. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  42. # pragma GCC diagnostic pop
  43. #endif
  44. CARLA_BACKEND_START_NAMESPACE
  45. // -------------------------------------------------------------------------------------------------------------------
  46. // Global static data
  47. static CharStringListPtr gDeviceNames;
  48. static std::vector<RtAudio::Api> gRtAudioApis;
  49. // -------------------------------------------------------------------------------------------------------------------
  50. static void initRtAudioAPIsIfNeeded()
  51. {
  52. static bool needsInit = true;
  53. if (! needsInit)
  54. return;
  55. needsInit = false;
  56. // get APIs in a local var, and pass wanted ones into gRtAudioApis
  57. std::vector<RtAudio::Api> apis;
  58. RtAudio::getCompiledApi(apis);
  59. for (std::vector<RtAudio::Api>::const_iterator it = apis.begin(), end=apis.end(); it != end; ++it)
  60. {
  61. const RtAudio::Api& api(*it);
  62. if (api == RtAudio::UNIX_JACK)
  63. {
  64. #if defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  65. if ( ! jackbridge_is_ok())
  66. continue;
  67. #else
  68. /* NOTE
  69. * RtMidi doesn't have a native MIDI backend for these OSes,
  70. * Using RtAudio JACK funcitonality is only useful when we need to access the native MIDI APIs.
  71. * (JACK audio + ALSA MIDI, or JACK audio + CoreMidi, or JACK audio + Windows MIDI)
  72. * Because RtMidi has no native MIDI support outside of win/mac/linux, we skip these RtAudio APIs.
  73. * Those OSes can use Carla's JACK support directly, which is much better than RtAudio classes.
  74. */
  75. continue;
  76. #endif
  77. }
  78. gRtAudioApis.push_back(api);
  79. }
  80. }
  81. static const char* getRtAudioApiName(const RtAudio::Api api) noexcept
  82. {
  83. switch (api)
  84. {
  85. case RtAudio::UNSPECIFIED:
  86. return "Unspecified";
  87. case RtAudio::LINUX_ALSA:
  88. return "ALSA";
  89. case RtAudio::LINUX_OSS:
  90. return "OSS";
  91. case RtAudio::UNIX_PULSE:
  92. return "PulseAudio";
  93. case RtAudio::UNIX_JACK:
  94. #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
  95. return "JACK with ALSA-MIDI";
  96. #elif defined(CARLA_OS_MAC)
  97. return "JACK with CoreMidi";
  98. #elif defined(CARLA_OS_WIN)
  99. return "JACK with WinMM";
  100. #else
  101. return "JACK (RtAudio)";
  102. #endif
  103. case RtAudio::MACOSX_CORE:
  104. return "CoreAudio";
  105. case RtAudio::WINDOWS_ASIO:
  106. return "ASIO";
  107. case RtAudio::WINDOWS_DS:
  108. return "DirectSound";
  109. case RtAudio::WINDOWS_WASAPI:
  110. return "WASAPI";
  111. case RtAudio::RTAUDIO_DUMMY:
  112. return "Dummy";
  113. }
  114. carla_stderr("CarlaBackend::getRtAudioApiName(%i) - invalid API", api);
  115. return nullptr;
  116. }
  117. static RtMidi::Api getMatchedAudioMidiAPI(const RtAudio::Api rtApi) noexcept
  118. {
  119. switch (rtApi)
  120. {
  121. case RtAudio::UNSPECIFIED:
  122. return RtMidi::UNSPECIFIED;
  123. case RtAudio::LINUX_ALSA:
  124. case RtAudio::LINUX_OSS:
  125. return RtMidi::LINUX_ALSA;
  126. case RtAudio::UNIX_PULSE:
  127. case RtAudio::UNIX_JACK:
  128. #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
  129. return RtMidi::LINUX_ALSA;
  130. #elif defined(CARLA_OS_MAC)
  131. return RtMidi::MACOSX_CORE;
  132. #elif defined(CARLA_OS_WIN)
  133. return RtMidi::WINDOWS_MM;
  134. #else
  135. return RtMidi::RTMIDI_DUMMY;
  136. #endif
  137. case RtAudio::MACOSX_CORE:
  138. return RtMidi::MACOSX_CORE;
  139. case RtAudio::WINDOWS_ASIO:
  140. case RtAudio::WINDOWS_DS:
  141. case RtAudio::WINDOWS_WASAPI:
  142. return RtMidi::WINDOWS_MM;
  143. case RtAudio::RTAUDIO_DUMMY:
  144. return RtMidi::RTMIDI_DUMMY;
  145. }
  146. return RtMidi::UNSPECIFIED;
  147. }
  148. // -------------------------------------------------------------------------------------------------------------------
  149. // RtAudio Engine
  150. class CarlaEngineRtAudio : public CarlaEngine
  151. {
  152. public:
  153. CarlaEngineRtAudio(const RtAudio::Api api)
  154. : CarlaEngine(),
  155. fAudio(api),
  156. fAudioInterleaved(false),
  157. fAudioInCount(0),
  158. fAudioOutCount(0),
  159. fLastEventTime(0),
  160. fDeviceName(),
  161. fAudioIntBufIn(nullptr),
  162. fAudioIntBufOut(nullptr),
  163. fMidiIns(),
  164. fMidiInEvents(),
  165. fMidiOuts(),
  166. fMidiOutMutex(),
  167. fMidiOutVector(EngineMidiEvent::kDataSize)
  168. {
  169. carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
  170. // just to make sure
  171. pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
  172. }
  173. ~CarlaEngineRtAudio() override
  174. {
  175. CARLA_SAFE_ASSERT(fAudioInCount == 0);
  176. CARLA_SAFE_ASSERT(fAudioOutCount == 0);
  177. CARLA_SAFE_ASSERT(fLastEventTime == 0);
  178. carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
  179. }
  180. // -------------------------------------
  181. bool init(const char* const clientName) override
  182. {
  183. CARLA_SAFE_ASSERT_RETURN(fAudioInCount == 0, false);
  184. CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
  185. CARLA_SAFE_ASSERT_RETURN(fLastEventTime == 0, false);
  186. CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
  187. carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
  188. if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
  189. {
  190. setLastError("Invalid process mode");
  191. return false;
  192. }
  193. const bool isDummy(fAudio.getCurrentApi() == RtAudio::RtAudio::RTAUDIO_DUMMY);
  194. bool deviceSet = false;
  195. RtAudio::StreamParameters iParams, oParams;
  196. if (isDummy)
  197. {
  198. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  199. {
  200. setLastError("Cannot use dummy driver in Rack mode");
  201. return false;
  202. }
  203. fDeviceName = "Dummy";
  204. }
  205. else
  206. {
  207. const uint devCount(fAudio.getDeviceCount());
  208. if (devCount == 0)
  209. {
  210. setLastError("No audio devices available for this driver");
  211. return false;
  212. }
  213. if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
  214. {
  215. for (uint i=0; i < devCount; ++i)
  216. {
  217. RtAudio::DeviceInfo devInfo(fAudio.getDeviceInfo(i));
  218. if (devInfo.probed && devInfo.outputChannels > 0 && devInfo.name == pData->options.audioDevice)
  219. {
  220. deviceSet = true;
  221. fDeviceName = devInfo.name.c_str();
  222. iParams.deviceId = i;
  223. oParams.deviceId = i;
  224. iParams.nChannels = devInfo.inputChannels;
  225. oParams.nChannels = devInfo.outputChannels;
  226. break;
  227. }
  228. }
  229. }
  230. if (! deviceSet)
  231. {
  232. iParams.deviceId = fAudio.getDefaultInputDevice();
  233. oParams.deviceId = fAudio.getDefaultOutputDevice();
  234. iParams.nChannels = fAudio.getDeviceInfo(iParams.deviceId).inputChannels;
  235. oParams.nChannels = fAudio.getDeviceInfo(oParams.deviceId).outputChannels;
  236. carla_stdout("No device set, using %i inputs and %i outputs", iParams.nChannels, oParams.nChannels);
  237. }
  238. if (oParams.nChannels == 0 && pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  239. {
  240. setLastError("Current audio setup has no outputs, cannot continue");
  241. return false;
  242. }
  243. iParams.nChannels = carla_fixedValue(0U, 128U, iParams.nChannels);
  244. oParams.nChannels = carla_fixedValue(0U, 128U, oParams.nChannels);
  245. fAudioInterleaved = fAudio.getCurrentApi() == RtAudio::UNIX_PULSE;
  246. }
  247. RtAudio::StreamOptions rtOptions;
  248. rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_SCHEDULE_REALTIME;
  249. rtOptions.numberOfBuffers = pData->options.audioTripleBuffer ? 3 : 2;
  250. rtOptions.streamName = clientName;
  251. rtOptions.priority = 85;
  252. if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA && ! deviceSet)
  253. rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
  254. if (! fAudioInterleaved)
  255. rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
  256. uint bufferFrames = pData->options.audioBufferSize;
  257. try {
  258. fAudio.openStream(oParams.nChannels > 0 ? &oParams : nullptr,
  259. iParams.nChannels > 0 ? &iParams : nullptr,
  260. RTAUDIO_FLOAT32, pData->options.audioSampleRate, &bufferFrames,
  261. carla_rtaudio_process_callback, this, &rtOptions,
  262. carla_rtaudio_buffer_size_callback);
  263. }
  264. catch (const RtAudioError& e) {
  265. setLastError(e.what());
  266. return false;
  267. }
  268. if (! pData->init(clientName))
  269. {
  270. close();
  271. setLastError("Failed to init internal data");
  272. return false;
  273. }
  274. pData->bufferSize = bufferFrames;
  275. pData->sampleRate = isDummy ? 44100.0 : fAudio.getStreamSampleRate();
  276. pData->initTime(pData->options.transportExtra);
  277. fAudioInCount = iParams.nChannels;
  278. fAudioOutCount = oParams.nChannels;
  279. fLastEventTime = 0;
  280. if (fAudioInCount > 0)
  281. fAudioIntBufIn = new float[fAudioInCount*bufferFrames];
  282. if (fAudioOutCount > 0)
  283. fAudioIntBufOut = new float[fAudioOutCount*bufferFrames];
  284. pData->graph.create(fAudioInCount, fAudioOutCount, 0, 0);
  285. try {
  286. fAudio.startStream();
  287. }
  288. catch (const RtAudioError& e)
  289. {
  290. close();
  291. setLastError(e.what());
  292. return false;
  293. }
  294. patchbayRefresh(true, false, false);
  295. if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
  296. refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
  297. callback(true, true,
  298. ENGINE_CALLBACK_ENGINE_STARTED,
  299. 0,
  300. pData->options.processMode,
  301. pData->options.transportMode,
  302. static_cast<int>(pData->bufferSize),
  303. static_cast<float>(pData->sampleRate),
  304. getCurrentDriverName());
  305. return true;
  306. }
  307. bool close() override
  308. {
  309. carla_debug("CarlaEngineRtAudio::close()");
  310. bool hasError = false;
  311. // stop stream first
  312. if (fAudio.isStreamOpen() && fAudio.isStreamRunning())
  313. {
  314. try {
  315. fAudio.stopStream();
  316. }
  317. catch (const RtAudioError& e)
  318. {
  319. setLastError(e.what());
  320. hasError = true;
  321. }
  322. }
  323. // clear engine data
  324. CarlaEngine::close();
  325. pData->graph.destroy();
  326. for (LinkedList<MidiInPort>::Itenerator it = fMidiIns.begin2(); it.valid(); it.next())
  327. {
  328. static MidiInPort fallback = { nullptr, { '\0' } };
  329. MidiInPort& inPort(it.getValue(fallback));
  330. CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
  331. inPort.port->cancelCallback();
  332. inPort.port->closePort();
  333. delete inPort.port;
  334. }
  335. fMidiIns.clear();
  336. fMidiInEvents.clear();
  337. fMidiOutMutex.lock();
  338. for (LinkedList<MidiOutPort>::Itenerator it = fMidiOuts.begin2(); it.valid(); it.next())
  339. {
  340. static MidiOutPort fallback = { nullptr, { '\0' } };
  341. MidiOutPort& outPort(it.getValue(fallback));
  342. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  343. outPort.port->closePort();
  344. delete outPort.port;
  345. }
  346. fMidiOuts.clear();
  347. fMidiOutMutex.unlock();
  348. fAudioInCount = 0;
  349. fAudioOutCount = 0;
  350. fLastEventTime = 0;
  351. fDeviceName.clear();
  352. if (fAudioIntBufIn != nullptr)
  353. {
  354. delete[] fAudioIntBufIn;
  355. fAudioIntBufIn = nullptr;
  356. }
  357. if (fAudioIntBufOut != nullptr)
  358. {
  359. delete[] fAudioIntBufOut;
  360. fAudioIntBufOut = nullptr;
  361. }
  362. // close stream
  363. if (fAudio.isStreamOpen())
  364. fAudio.closeStream();
  365. return !hasError;
  366. }
  367. bool isRunning() const noexcept override
  368. {
  369. return fAudio.isStreamOpen();
  370. }
  371. bool isOffline() const noexcept override
  372. {
  373. return false;
  374. }
  375. EngineType getType() const noexcept override
  376. {
  377. return kEngineTypeRtAudio;
  378. }
  379. const char* getCurrentDriverName() const noexcept override
  380. {
  381. return CarlaBackend::getRtAudioApiName(fAudio.getCurrentApi());
  382. }
  383. // -------------------------------------------------------------------
  384. // Patchbay
  385. template<class Graph>
  386. bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
  387. {
  388. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  389. char strBuf[STR_MAX+1U];
  390. strBuf[STR_MAX] = '\0';
  391. ExternalGraph& extGraph(graph->extGraph);
  392. // ---------------------------------------------------------------
  393. // clear last ports
  394. extGraph.clear();
  395. // ---------------------------------------------------------------
  396. // fill in new ones
  397. // Audio In
  398. for (uint i=0; i < fAudioInCount; ++i)
  399. {
  400. std::snprintf(strBuf, STR_MAX, "capture_%i", i+1);
  401. PortNameToId portNameToId;
  402. portNameToId.setData(kExternalGraphGroupAudioIn, i+1, strBuf, "");
  403. extGraph.audioPorts.ins.append(portNameToId);
  404. }
  405. // Audio Out
  406. for (uint i=0; i < fAudioOutCount; ++i)
  407. {
  408. std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
  409. PortNameToId portNameToId;
  410. portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
  411. extGraph.audioPorts.outs.append(portNameToId);
  412. }
  413. // MIDI In
  414. try
  415. {
  416. RtMidiIn midiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-in");
  417. for (uint i=0, count = midiIn.getPortCount(); i < count; ++i)
  418. {
  419. PortNameToId portNameToId;
  420. portNameToId.setData(kExternalGraphGroupMidiIn, i+1, midiIn.getPortName(i).c_str(), "");
  421. extGraph.midiPorts.ins.append(portNameToId);
  422. }
  423. } CARLA_SAFE_EXCEPTION("RtMidiIn discovery");
  424. // MIDI Out
  425. try
  426. {
  427. RtMidiOut midiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-out");
  428. for (uint i=0, count = midiOut.getPortCount(); i < count; ++i)
  429. {
  430. PortNameToId portNameToId;
  431. portNameToId.setData(kExternalGraphGroupMidiOut, i+1, midiOut.getPortName(i).c_str(), "");
  432. extGraph.midiPorts.outs.append(portNameToId);
  433. }
  434. } CARLA_SAFE_EXCEPTION("RtMidiOut discovery");
  435. // ---------------------------------------------------------------
  436. // now refresh
  437. if (sendHost || sendOSC)
  438. graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
  439. // ---------------------------------------------------------------
  440. // add midi connections
  441. for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
  442. {
  443. static const MidiInPort fallback = { nullptr, { '\0' } };
  444. const MidiInPort& inPort(it.getValue(fallback));
  445. CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
  446. const uint portId(extGraph.midiPorts.getPortId(true, inPort.name));
  447. CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.ins.count());
  448. ConnectionToId connectionToId;
  449. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupMidiIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiIn);
  450. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  451. extGraph.connections.list.append(connectionToId);
  452. callback(sendHost, sendOSC,
  453. ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
  454. connectionToId.id,
  455. 0, 0, 0, 0.0f,
  456. strBuf);
  457. }
  458. fMidiOutMutex.lock();
  459. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
  460. {
  461. static const MidiOutPort fallback = { nullptr, { '\0' } };
  462. const MidiOutPort& outPort(it.getValue(fallback));
  463. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  464. const uint portId(extGraph.midiPorts.getPortId(false, outPort.name));
  465. CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.outs.count());
  466. ConnectionToId connectionToId;
  467. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiOut, kExternalGraphGroupMidiOut, portId);
  468. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  469. extGraph.connections.list.append(connectionToId);
  470. callback(sendHost, sendOSC,
  471. ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
  472. connectionToId.id,
  473. 0, 0, 0, 0.0f,
  474. strBuf);
  475. }
  476. fMidiOutMutex.unlock();
  477. return true;
  478. }
  479. bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
  480. {
  481. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
  482. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  483. return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
  484. if (sendHost)
  485. pData->graph.setUsingExternalHost(external);
  486. if (sendOSC)
  487. pData->graph.setUsingExternalOSC(external);
  488. if (external)
  489. return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
  490. return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
  491. }
  492. // -------------------------------------------------------------------
  493. protected:
  494. void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer,
  495. uint nframes, double streamTime, RtAudioStreamStatus status)
  496. {
  497. const PendingRtEventsRunner prt(this, nframes, true);
  498. if (status & RTAUDIO_INPUT_OVERFLOW)
  499. ++pData->xruns;
  500. if (status & RTAUDIO_OUTPUT_UNDERFLOW)
  501. ++pData->xruns;
  502. // get buffers from RtAudio
  503. const float* const insPtr = (const float*)inputBuffer;
  504. /* */ float* const outsPtr = (float*)outputBuffer;
  505. // assert rtaudio buffers
  506. CARLA_SAFE_ASSERT_RETURN(outputBuffer != nullptr,);
  507. // set rtaudio buffers as non-interleaved
  508. const float* inBuf[fAudioInCount];
  509. /* */ float* outBuf[fAudioOutCount];
  510. if (fAudioInterleaved)
  511. {
  512. // FIXME - this looks completely wrong!
  513. float* inBuf2[fAudioInCount];
  514. for (uint i=0, count=fAudioInCount; i<count; ++i)
  515. {
  516. inBuf [i] = fAudioIntBufIn + (nframes*i);
  517. inBuf2[i] = fAudioIntBufIn + (nframes*i);
  518. }
  519. for (uint i=0, count=fAudioOutCount; i<count; ++i)
  520. outBuf[i] = fAudioIntBufOut + (nframes*i);
  521. // init input
  522. for (uint i=0; i<nframes; ++i)
  523. for (uint j=0; j<fAudioInCount; ++j)
  524. inBuf2[j][i] = insPtr[i*fAudioInCount+j];
  525. // clear output
  526. carla_zeroFloats(fAudioIntBufOut, fAudioOutCount*nframes);
  527. }
  528. else
  529. {
  530. for (uint i=0; i < fAudioInCount; ++i)
  531. inBuf[i] = insPtr+(nframes*i);
  532. for (uint i=0; i < fAudioOutCount; ++i)
  533. outBuf[i] = outsPtr+(nframes*i);
  534. // clear output
  535. carla_zeroFloats(outsPtr, nframes*fAudioOutCount);
  536. }
  537. // initialize events
  538. carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
  539. carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
  540. if (fMidiInEvents.mutex.tryLock())
  541. {
  542. uint32_t engineEventIndex = 0;
  543. fMidiInEvents.splice();
  544. for (LinkedList<RtMidiEvent>::Itenerator it = fMidiInEvents.data.begin2(); it.valid(); it.next())
  545. {
  546. static const RtMidiEvent fallback = { 0, 0, { 0 } };
  547. const RtMidiEvent& midiEvent(it.getValue(fallback));
  548. CARLA_SAFE_ASSERT_CONTINUE(midiEvent.size > 0);
  549. EngineEvent& engineEvent(pData->events.in[engineEventIndex++]);
  550. if (midiEvent.time < pData->timeInfo.frame)
  551. {
  552. engineEvent.time = 0;
  553. }
  554. else if (midiEvent.time >= pData->timeInfo.frame + nframes)
  555. {
  556. carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame);
  557. engineEvent.time = static_cast<uint32_t>(pData->timeInfo.frame) + nframes - 1;
  558. }
  559. else
  560. engineEvent.time = static_cast<uint32_t>(midiEvent.time - pData->timeInfo.frame);
  561. engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data, 0);
  562. if (engineEventIndex >= kMaxEngineEventInternalCount)
  563. break;
  564. }
  565. fMidiInEvents.data.clear();
  566. fMidiInEvents.mutex.unlock();
  567. }
  568. pData->graph.process(pData, inBuf, outBuf, nframes);
  569. fMidiOutMutex.lock();
  570. if (fMidiOuts.count() > 0)
  571. {
  572. uint8_t size = 0;
  573. uint8_t mdata[3] = { 0, 0, 0 };
  574. uint8_t mdataTmp[EngineMidiEvent::kDataSize];
  575. const uint8_t* mdataPtr;
  576. for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
  577. {
  578. const EngineEvent& engineEvent(pData->events.out[i]);
  579. /**/ if (engineEvent.type == kEngineEventTypeNull)
  580. {
  581. break;
  582. }
  583. else if (engineEvent.type == kEngineEventTypeControl)
  584. {
  585. const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
  586. size = ctrlEvent.convertToMidiData(engineEvent.channel, mdata);
  587. mdataPtr = mdata;
  588. }
  589. else if (engineEvent.type == kEngineEventTypeMidi)
  590. {
  591. const EngineMidiEvent& midiEvent(engineEvent.midi);
  592. size = midiEvent.size;
  593. CARLA_SAFE_ASSERT_CONTINUE(size > 0);
  594. if (size > EngineMidiEvent::kDataSize)
  595. {
  596. CARLA_SAFE_ASSERT_CONTINUE(midiEvent.dataExt != nullptr);
  597. mdataPtr = midiEvent.dataExt;
  598. }
  599. else
  600. {
  601. // set first byte
  602. mdataTmp[0] = static_cast<uint8_t>(midiEvent.data[0] | (engineEvent.channel & MIDI_CHANNEL_BIT));
  603. // copy rest
  604. carla_copy<uint8_t>(mdataTmp+1, midiEvent.data+1, size-1U);
  605. // done
  606. mdataPtr = mdataTmp;
  607. }
  608. }
  609. else
  610. {
  611. continue;
  612. }
  613. if (size > 0)
  614. {
  615. fMidiOutVector.assign(mdataPtr, mdataPtr + size);
  616. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
  617. {
  618. static MidiOutPort fallback = { nullptr, { '\0' } };
  619. MidiOutPort& outPort(it.getValue(fallback));
  620. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  621. outPort.port->sendMessage(&fMidiOutVector);
  622. }
  623. }
  624. }
  625. }
  626. fMidiOutMutex.unlock();
  627. if (fAudioInterleaved)
  628. {
  629. for (uint i=0; i < nframes; ++i)
  630. for (uint j=0; j<fAudioOutCount; ++j)
  631. outsPtr[i*fAudioOutCount+j] = outBuf[j][i];
  632. }
  633. return; // unused
  634. (void)streamTime;
  635. }
  636. void handleBufferSizeCallback(const uint newBufferSize)
  637. {
  638. carla_stdout("bufferSize callback %u %u", pData->bufferSize, newBufferSize);
  639. if (pData->bufferSize == newBufferSize)
  640. return;
  641. if (fAudioInCount > 0)
  642. {
  643. delete[] fAudioIntBufIn;
  644. fAudioIntBufIn = new float[fAudioInCount*newBufferSize];
  645. }
  646. if (fAudioOutCount > 0)
  647. {
  648. delete[] fAudioIntBufOut;
  649. fAudioIntBufOut = new float[fAudioOutCount*newBufferSize];
  650. }
  651. pData->bufferSize = newBufferSize;
  652. bufferSizeChanged(newBufferSize);
  653. }
  654. void handleMidiCallback(double timeStamp, std::vector<uchar>* const message)
  655. {
  656. const size_t messageSize(message->size());
  657. if (messageSize == 0 || messageSize > EngineMidiEvent::kDataSize)
  658. return;
  659. timeStamp /= 2;
  660. if (timeStamp > 0.95)
  661. timeStamp = 0.95;
  662. else if (timeStamp < 0.0)
  663. timeStamp = 0.0;
  664. RtMidiEvent midiEvent;
  665. midiEvent.time = pData->timeInfo.frame + uint64_t(timeStamp * (double)pData->bufferSize);
  666. if (midiEvent.time < fLastEventTime)
  667. midiEvent.time = fLastEventTime;
  668. else
  669. fLastEventTime = midiEvent.time;
  670. midiEvent.size = static_cast<uint8_t>(messageSize);
  671. size_t i=0;
  672. for (; i < messageSize; ++i)
  673. midiEvent.data[i] = message->at(i);
  674. for (; i < EngineMidiEvent::kDataSize; ++i)
  675. midiEvent.data[i] = 0;
  676. fMidiInEvents.append(midiEvent);
  677. }
  678. // -------------------------------------------------------------------
  679. bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
  680. {
  681. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  682. carla_debug("CarlaEngineRtAudio::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
  683. switch (connectionType)
  684. {
  685. case kExternalGraphConnectionAudioIn1:
  686. case kExternalGraphConnectionAudioIn2:
  687. case kExternalGraphConnectionAudioOut1:
  688. case kExternalGraphConnectionAudioOut2:
  689. return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
  690. case kExternalGraphConnectionMidiInput: {
  691. CarlaString newRtMidiPortName;
  692. newRtMidiPortName += getName();
  693. newRtMidiPortName += ":";
  694. newRtMidiPortName += portName;
  695. RtMidiIn* rtMidiIn;
  696. try {
  697. rtMidiIn = new RtMidiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer(), 512);
  698. } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiIn", false);
  699. rtMidiIn->ignoreTypes();
  700. rtMidiIn->setCallback(carla_rtmidi_callback, this);
  701. bool found = false;
  702. uint rtMidiPortIndex;
  703. for (uint i=0, count=rtMidiIn->getPortCount(); i < count; ++i)
  704. {
  705. if (rtMidiIn->getPortName(i) == portName)
  706. {
  707. found = true;
  708. rtMidiPortIndex = i;
  709. break;
  710. }
  711. }
  712. if (! found)
  713. {
  714. delete rtMidiIn;
  715. return false;
  716. }
  717. try {
  718. rtMidiIn->openPort(rtMidiPortIndex, portName);
  719. }
  720. catch(...) {
  721. delete rtMidiIn;
  722. return false;
  723. };
  724. MidiInPort midiPort;
  725. midiPort.port = rtMidiIn;
  726. std::strncpy(midiPort.name, portName, STR_MAX);
  727. midiPort.name[STR_MAX] = '\0';
  728. fMidiIns.append(midiPort);
  729. return true;
  730. } break;
  731. case kExternalGraphConnectionMidiOutput: {
  732. CarlaString newRtMidiPortName;
  733. newRtMidiPortName += getName();
  734. newRtMidiPortName += ":";
  735. newRtMidiPortName += portName;
  736. RtMidiOut* rtMidiOut;
  737. try {
  738. rtMidiOut = new RtMidiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer());
  739. } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiOut", false);
  740. bool found = false;
  741. uint rtMidiPortIndex;
  742. for (uint i=0, count=rtMidiOut->getPortCount(); i < count; ++i)
  743. {
  744. if (rtMidiOut->getPortName(i) == portName)
  745. {
  746. found = true;
  747. rtMidiPortIndex = i;
  748. break;
  749. }
  750. }
  751. if (! found)
  752. {
  753. delete rtMidiOut;
  754. return false;
  755. }
  756. try {
  757. rtMidiOut->openPort(rtMidiPortIndex, portName);
  758. }
  759. catch(...) {
  760. delete rtMidiOut;
  761. return false;
  762. };
  763. MidiOutPort midiPort;
  764. midiPort.port = rtMidiOut;
  765. std::strncpy(midiPort.name, portName, STR_MAX);
  766. midiPort.name[STR_MAX] = '\0';
  767. const CarlaMutexLocker cml(fMidiOutMutex);
  768. fMidiOuts.append(midiPort);
  769. return true;
  770. } break;
  771. }
  772. return false;
  773. }
  774. bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
  775. {
  776. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  777. carla_debug("CarlaEngineRtAudio::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
  778. switch (connectionType)
  779. {
  780. case kExternalGraphConnectionAudioIn1:
  781. case kExternalGraphConnectionAudioIn2:
  782. case kExternalGraphConnectionAudioOut1:
  783. case kExternalGraphConnectionAudioOut2:
  784. return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
  785. case kExternalGraphConnectionMidiInput:
  786. for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
  787. {
  788. static MidiInPort fallback = { nullptr, { '\0' } };
  789. MidiInPort& inPort(it.getValue(fallback));
  790. CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
  791. if (std::strncmp(inPort.name, portName, STR_MAX) != 0)
  792. continue;
  793. inPort.port->cancelCallback();
  794. inPort.port->closePort();
  795. delete inPort.port;
  796. fMidiIns.remove(it);
  797. return true;
  798. }
  799. break;
  800. case kExternalGraphConnectionMidiOutput: {
  801. const CarlaMutexLocker cml(fMidiOutMutex);
  802. for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
  803. {
  804. static MidiOutPort fallback = { nullptr, { '\0' } };
  805. MidiOutPort& outPort(it.getValue(fallback));
  806. CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
  807. if (std::strncmp(outPort.name, portName, STR_MAX) != 0)
  808. continue;
  809. outPort.port->closePort();
  810. delete outPort.port;
  811. fMidiOuts.remove(it);
  812. return true;
  813. }
  814. } break;
  815. }
  816. return false;
  817. }
  818. // -------------------------------------------------------------------
  819. private:
  820. RtAudio fAudio;
  821. // useful info
  822. bool fAudioInterleaved;
  823. uint fAudioInCount;
  824. uint fAudioOutCount;
  825. uint64_t fLastEventTime;
  826. // current device name
  827. CarlaString fDeviceName;
  828. // temp buffer for interleaved audio
  829. float* fAudioIntBufIn;
  830. float* fAudioIntBufOut;
  831. struct MidiInPort {
  832. RtMidiIn* port;
  833. char name[STR_MAX+1];
  834. };
  835. struct MidiOutPort {
  836. RtMidiOut* port;
  837. char name[STR_MAX+1];
  838. };
  839. struct RtMidiEvent {
  840. uint64_t time; // needs to compare to internal time
  841. uint8_t size;
  842. uint8_t data[EngineMidiEvent::kDataSize];
  843. };
  844. struct RtMidiEvents {
  845. CarlaMutex mutex;
  846. RtLinkedList<RtMidiEvent>::Pool dataPool;
  847. RtLinkedList<RtMidiEvent> data;
  848. RtLinkedList<RtMidiEvent> dataPending;
  849. RtMidiEvents()
  850. : mutex(),
  851. dataPool(512, 512),
  852. data(dataPool),
  853. dataPending(dataPool) {}
  854. ~RtMidiEvents()
  855. {
  856. clear();
  857. }
  858. void append(const RtMidiEvent& event)
  859. {
  860. mutex.lock();
  861. dataPending.append(event);
  862. mutex.unlock();
  863. }
  864. void clear()
  865. {
  866. mutex.lock();
  867. data.clear();
  868. dataPending.clear();
  869. mutex.unlock();
  870. }
  871. void splice()
  872. {
  873. if (dataPending.count() > 0)
  874. dataPending.moveTo(data, true /* append */);
  875. }
  876. };
  877. LinkedList<MidiInPort> fMidiIns;
  878. RtMidiEvents fMidiInEvents;
  879. LinkedList<MidiOutPort> fMidiOuts;
  880. CarlaMutex fMidiOutMutex;
  881. std::vector<uint8_t> fMidiOutVector;
  882. #define handlePtr ((CarlaEngineRtAudio*)userData)
  883. static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, uint nframes, double streamTime, RtAudioStreamStatus status, void* userData)
  884. {
  885. handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
  886. return 0;
  887. }
  888. static bool carla_rtaudio_buffer_size_callback(unsigned int bufferSize, void* userData)
  889. {
  890. handlePtr->handleBufferSizeCallback(bufferSize);
  891. return true;
  892. }
  893. static void carla_rtmidi_callback(double timeStamp, std::vector<uchar>* message, void* userData)
  894. {
  895. handlePtr->handleMidiCallback(timeStamp, message);
  896. }
  897. #undef handlePtr
  898. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
  899. };
  900. // -----------------------------------------
  901. namespace EngineInit {
  902. CarlaEngine* newRtAudio(const AudioApi api)
  903. {
  904. initRtAudioAPIsIfNeeded();
  905. RtAudio::Api rtApi = RtAudio::UNSPECIFIED;
  906. switch (api)
  907. {
  908. case AUDIO_API_NULL:
  909. rtApi = RtAudio::RTAUDIO_DUMMY;
  910. break;
  911. case AUDIO_API_JACK:
  912. rtApi = RtAudio::UNIX_JACK;
  913. break;
  914. case AUDIO_API_OSS:
  915. rtApi = RtAudio::LINUX_OSS;
  916. break;
  917. case AUDIO_API_ALSA:
  918. rtApi = RtAudio::LINUX_ALSA;
  919. break;
  920. case AUDIO_API_PULSEAUDIO:
  921. rtApi = RtAudio::UNIX_PULSE;
  922. break;
  923. case AUDIO_API_COREAUDIO:
  924. rtApi = RtAudio::MACOSX_CORE;
  925. break;
  926. case AUDIO_API_ASIO:
  927. rtApi = RtAudio::WINDOWS_ASIO;
  928. break;
  929. case AUDIO_API_DIRECTSOUND:
  930. rtApi = RtAudio::WINDOWS_DS;
  931. break;
  932. case AUDIO_API_WASAPI:
  933. rtApi = RtAudio::WINDOWS_WASAPI;
  934. break;
  935. }
  936. return new CarlaEngineRtAudio(rtApi);
  937. }
  938. uint getRtAudioApiCount()
  939. {
  940. initRtAudioAPIsIfNeeded();
  941. return static_cast<uint>(gRtAudioApis.size());
  942. }
  943. const char* getRtAudioApiName(const uint index)
  944. {
  945. initRtAudioAPIsIfNeeded();
  946. CARLA_SAFE_ASSERT_RETURN(index < gRtAudioApis.size(), nullptr);
  947. return CarlaBackend::getRtAudioApiName(gRtAudioApis[index]);
  948. }
  949. const char* const* getRtAudioApiDeviceNames(const uint index)
  950. {
  951. initRtAudioAPIsIfNeeded();
  952. if (index >= gRtAudioApis.size())
  953. return nullptr;
  954. const RtAudio::Api& api(gRtAudioApis[index]);
  955. CarlaStringList devNames;
  956. try {
  957. RtAudio rtAudio(api);
  958. const uint devCount(rtAudio.getDeviceCount());
  959. if (devCount == 0)
  960. return nullptr;
  961. for (uint i=0; i < devCount; ++i)
  962. {
  963. RtAudio::DeviceInfo devInfo(rtAudio.getDeviceInfo(i));
  964. if (devInfo.probed && devInfo.outputChannels > 0 /*&& (devInfo.nativeFormats & RTAUDIO_FLOAT32) != 0*/)
  965. devNames.append(devInfo.name.c_str());
  966. }
  967. } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device names", nullptr);
  968. gDeviceNames = devNames.toCharStringListPtr();
  969. return gDeviceNames;
  970. }
  971. const EngineDriverDeviceInfo* getRtAudioDeviceInfo(const uint index, const char* const deviceName)
  972. {
  973. initRtAudioAPIsIfNeeded();
  974. if (index >= gRtAudioApis.size())
  975. return nullptr;
  976. static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
  977. static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
  978. static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };
  979. // reset
  980. devInfo.hints = 0x0;
  981. // cleanup
  982. if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
  983. {
  984. delete[] devInfo.bufferSizes;
  985. devInfo.bufferSizes = nullptr;
  986. }
  987. if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
  988. {
  989. delete[] devInfo.sampleRates;
  990. devInfo.sampleRates = nullptr;
  991. }
  992. const RtAudio::Api& api(gRtAudioApis[index]);
  993. if (api == RtAudio::UNIX_JACK)
  994. {
  995. devInfo.bufferSizes = nullptr;
  996. devInfo.sampleRates = nullptr;
  997. return &devInfo;
  998. }
  999. RtAudio::DeviceInfo rtAudioDevInfo;
  1000. try {
  1001. RtAudio rtAudio(api);
  1002. const uint devCount(rtAudio.getDeviceCount());
  1003. if (devCount == 0)
  1004. return nullptr;
  1005. uint i;
  1006. for (i=0; i < devCount; ++i)
  1007. {
  1008. rtAudioDevInfo = rtAudio.getDeviceInfo(i);
  1009. if (rtAudioDevInfo.name == deviceName)
  1010. break;
  1011. }
  1012. if (i == devCount)
  1013. rtAudioDevInfo = rtAudio.getDeviceInfo(rtAudio.getDefaultOutputDevice());
  1014. } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device discovery", nullptr);
  1015. // a few APIs can do triple buffer
  1016. switch (api)
  1017. {
  1018. case RtAudio::LINUX_ALSA:
  1019. case RtAudio::LINUX_OSS:
  1020. case RtAudio::WINDOWS_DS:
  1021. devInfo.hints |= ENGINE_DRIVER_DEVICE_CAN_TRIPLE_BUFFER;
  1022. break;
  1023. default:
  1024. break;
  1025. }
  1026. // always use default buffer sizes
  1027. devInfo.bufferSizes = dummyBufferSizes;
  1028. // valid sample rates
  1029. if (const size_t sampleRatesCount = rtAudioDevInfo.sampleRates.size())
  1030. {
  1031. double* const sampleRates(new double[sampleRatesCount+1]);
  1032. for (size_t i=0; i < sampleRatesCount; ++i)
  1033. sampleRates[i] = rtAudioDevInfo.sampleRates[i];
  1034. sampleRates[sampleRatesCount] = 0.0;
  1035. devInfo.sampleRates = sampleRates;
  1036. }
  1037. else
  1038. {
  1039. devInfo.sampleRates = dummySampleRates;
  1040. }
  1041. return &devInfo;
  1042. }
  1043. }
  1044. // -----------------------------------------
  1045. CARLA_BACKEND_END_NAMESPACE