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.

659 lines
20KB

  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 GPL.txt file
  16. */
  17. #ifndef HAVE_JUCE
  18. # error This file should not be compiled if Juce is disabled
  19. #endif
  20. #include "CarlaEngineInternal.hpp"
  21. #include "CarlaBackendUtils.hpp"
  22. #include "RtLinkedList.hpp"
  23. #include "juce_audio_devices.h"
  24. using namespace juce;
  25. CARLA_BACKEND_START_NAMESPACE
  26. #if 0
  27. } // Fix editor indentation
  28. #endif
  29. // -------------------------------------------------------------------------------------------------------------------
  30. // Global static data
  31. static const char** gRetNames = nullptr;
  32. static OwnedArray<AudioIODeviceType> gJuceDeviceTypes;
  33. struct JuceCleanup {
  34. JuceCleanup() noexcept {}
  35. ~JuceCleanup()
  36. {
  37. if (gRetNames != nullptr)
  38. {
  39. for (int i=0; gRetNames[i] != nullptr; ++i)
  40. delete[] gRetNames[i];
  41. delete[] gRetNames;
  42. gRetNames = nullptr;
  43. }
  44. gJuceDeviceTypes.clear(true);
  45. }
  46. };
  47. // -------------------------------------------------------------------------------------------------------------------
  48. // Cleanup
  49. static void initJuceDevicesIfNeeded()
  50. {
  51. static const JuceCleanup sJuceCleanup;
  52. static AudioDeviceManager sDeviceManager;
  53. static bool needsInit = true;
  54. if (! needsInit)
  55. return;
  56. needsInit = false;
  57. sDeviceManager.createAudioDeviceTypes(gJuceDeviceTypes);
  58. // remove JACK from device list
  59. for (int i=0, count=gJuceDeviceTypes.size(); i < count; ++i)
  60. {
  61. if (gJuceDeviceTypes[i]->getTypeName() != "JACK")
  62. continue;
  63. gJuceDeviceTypes.remove(i, true);
  64. }
  65. }
  66. // -------------------------------------------------------------------------------------------------------------------
  67. // Juce Engine
  68. class CarlaEngineJuce : public CarlaEngine,
  69. public AudioIODeviceCallback
  70. {
  71. public:
  72. CarlaEngineJuce(AudioIODeviceType* const devType)
  73. : CarlaEngine(),
  74. AudioIODeviceCallback(),
  75. fDeviceType(devType)
  76. {
  77. carla_debug("CarlaEngineJuce::CarlaEngineJuce(%p)", devType);
  78. // just to make sure
  79. pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
  80. }
  81. ~CarlaEngineJuce() override
  82. {
  83. carla_debug("CarlaEngineJuce::~CarlaEngineJuce()");
  84. }
  85. // -------------------------------------
  86. bool init(const char* const clientName) override
  87. {
  88. CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
  89. carla_debug("CarlaEngineJuce::init(\"%s\")", clientName);
  90. if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
  91. {
  92. setLastError("Invalid process mode");
  93. return false;
  94. }
  95. String deviceName;
  96. if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
  97. {
  98. deviceName = pData->options.audioDevice;
  99. }
  100. else
  101. {
  102. const int defaultIndex(fDeviceType->getDefaultDeviceIndex(false));
  103. StringArray deviceNames(fDeviceType->getDeviceNames());
  104. if (defaultIndex >= 0 && defaultIndex < deviceNames.size())
  105. deviceName = deviceNames[defaultIndex];
  106. }
  107. if (deviceName.isEmpty())
  108. {
  109. setLastError("Audio device has not been selected yet and a default one is not available");
  110. return false;
  111. }
  112. fDevice = fDeviceType->createDevice(deviceName, deviceName);
  113. if (fDevice == nullptr)
  114. {
  115. setLastError("Failed to create device");
  116. return false;
  117. }
  118. StringArray inputNames(fDevice->getInputChannelNames());
  119. StringArray outputNames(fDevice->getOutputChannelNames());
  120. BigInteger inputChannels;
  121. inputChannels.setRange(0, inputNames.size(), true);
  122. BigInteger outputChannels;
  123. outputChannels.setRange(0, outputNames.size(), true);
  124. String error = fDevice->open(inputChannels, outputChannels, pData->options.audioSampleRate, static_cast<int>(pData->options.audioBufferSize));
  125. if (error.isNotEmpty())
  126. {
  127. setLastError(error.toUTF8());
  128. fDevice = nullptr;
  129. return false;
  130. }
  131. pData->bufferSize = static_cast<uint32_t>(fDevice->getCurrentBufferSizeSamples());
  132. pData->sampleRate = fDevice->getCurrentSampleRate();
  133. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  134. {
  135. pData->audio.inCount = 2;
  136. pData->audio.outCount = 2;
  137. }
  138. else
  139. {
  140. pData->audio.inCount = 0;
  141. pData->audio.outCount = 0;
  142. }
  143. pData->audio.create(pData->bufferSize);
  144. fDevice->start(this);
  145. CarlaEngine::init(clientName);
  146. pData->audio.isReady = true;
  147. patchbayRefresh();
  148. return true;
  149. }
  150. bool close() override
  151. {
  152. carla_debug("CarlaEngineJuce::close()");
  153. pData->audio.isReady = false;
  154. bool hasError = !CarlaEngine::close();
  155. if (fDevice != nullptr)
  156. {
  157. if (fDevice->isPlaying())
  158. fDevice->stop();
  159. if (fDevice->isOpen())
  160. fDevice->close();
  161. fDevice = nullptr;
  162. }
  163. return !hasError;
  164. }
  165. bool isRunning() const noexcept override
  166. {
  167. return fDevice != nullptr && fDevice->isPlaying();
  168. }
  169. bool isOffline() const noexcept override
  170. {
  171. return false;
  172. }
  173. EngineType getType() const noexcept override
  174. {
  175. return kEngineTypeJuce;
  176. }
  177. const char* getCurrentDriverName() const noexcept override
  178. {
  179. return fDeviceType->getTypeName().toRawUTF8();
  180. }
  181. // -------------------------------------------------------------------
  182. // Patchbay
  183. bool patchbayRefresh() override
  184. {
  185. CARLA_SAFE_ASSERT_RETURN(pData->audio.isReady, false);
  186. //fUsedMidiPorts.clear();
  187. if (pData->graph.isRack)
  188. patchbayRefreshRack();
  189. else
  190. patchbayRefreshPatchbay();
  191. return true;
  192. }
  193. void patchbayRefreshRack()
  194. {
  195. RackGraph* const rack(pData->graph.rack);
  196. rack->connections.clear();
  197. char strBuf[STR_MAX+1];
  198. strBuf[STR_MAX] = '\0';
  199. // Main
  200. {
  201. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_CARLA, PATCHBAY_ICON_CARLA, -1, 0.0f, getName());
  202. 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");
  203. 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");
  204. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT1, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out1");
  205. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT2, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out2");
  206. 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");
  207. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_OUT, PATCHBAY_PORT_TYPE_MIDI, 0.0f, "midi-out");
  208. }
  209. String deviceName(fDevice->getName());
  210. if (deviceName.isNotEmpty())
  211. deviceName = deviceName.dropLastCharacters(deviceName.fromFirstOccurrenceOf(", ", true, false).length());
  212. // Audio In
  213. {
  214. StringArray inputNames(fDevice->getInputChannelNames());
  215. if (deviceName.isNotEmpty())
  216. std::snprintf(strBuf, STR_MAX, "Capture (%s)", deviceName.toRawUTF8());
  217. else
  218. std::strncpy(strBuf, "Capture", STR_MAX);
  219. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_AUDIO_IN, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  220. for (int i=0, count=inputNames.size(); i<count; ++i)
  221. callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_AUDIO_IN, static_cast<int>(i), PATCHBAY_PORT_TYPE_AUDIO, 0.0f, inputNames[i].toRawUTF8());
  222. }
  223. // Audio Out
  224. {
  225. StringArray outputNames(fDevice->getOutputChannelNames());
  226. if (deviceName.isNotEmpty())
  227. std::snprintf(strBuf, STR_MAX, "Playback (%s)", deviceName.toRawUTF8());
  228. else
  229. std::strncpy(strBuf, "Playback", STR_MAX);
  230. callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_AUDIO_OUT, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  231. for (int i=0, count=outputNames.size(); i<count; ++i)
  232. 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, outputNames[i].toRawUTF8());
  233. }
  234. // TODO - MIDI
  235. // Connections
  236. rack->audio.mutex.lock();
  237. for (LinkedList<uint>::Itenerator it = rack->audio.connectedIn1.begin(); it.valid(); it.next())
  238. {
  239. const uint& portId(it.getValue());
  240. //CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioInCount);
  241. ConnectionToId connectionToId;
  242. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_AUDIO_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN1);
  243. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  244. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  245. rack->connections.list.append(connectionToId);
  246. }
  247. for (LinkedList<uint>::Itenerator it = rack->audio.connectedIn2.begin(); it.valid(); it.next())
  248. {
  249. const uint& portId(it.getValue());
  250. //CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioInCount);
  251. ConnectionToId connectionToId;
  252. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_AUDIO_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_IN2);
  253. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  254. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  255. rack->connections.list.append(connectionToId);
  256. }
  257. for (LinkedList<uint>::Itenerator it = rack->audio.connectedOut1.begin(); it.valid(); it.next())
  258. {
  259. const uint& portId(it.getValue());
  260. //CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioOutCount);
  261. ConnectionToId connectionToId;
  262. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT1, RACK_GRAPH_GROUP_AUDIO_OUT, portId);
  263. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  264. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  265. rack->connections.list.append(connectionToId);
  266. }
  267. for (LinkedList<uint>::Itenerator it = rack->audio.connectedOut2.begin(); it.valid(); it.next())
  268. {
  269. const uint& portId(it.getValue());
  270. //CARLA_SAFE_ASSERT_CONTINUE(portId < fAudioOutCount);
  271. ConnectionToId connectionToId;
  272. connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_AUDIO_OUT2, RACK_GRAPH_GROUP_AUDIO_OUT, portId);
  273. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  274. callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  275. rack->connections.list.append(connectionToId);
  276. }
  277. rack->audio.mutex.unlock();
  278. // TODO - MIDI
  279. }
  280. void patchbayRefreshPatchbay() noexcept
  281. {
  282. }
  283. // -------------------------------------------------------------------
  284. protected:
  285. void audioDeviceIOCallback(const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) override
  286. {
  287. // assert juce buffers
  288. CARLA_SAFE_ASSERT_RETURN(numInputChannels >= 0, runPendingRtEvents());
  289. CARLA_SAFE_ASSERT_RETURN(numOutputChannels > 0, runPendingRtEvents());
  290. CARLA_SAFE_ASSERT_RETURN(outputChannelData != nullptr, runPendingRtEvents());
  291. CARLA_SAFE_ASSERT_RETURN(numSamples == static_cast<int>(pData->bufferSize), runPendingRtEvents());
  292. if (! pData->audio.isReady)
  293. return runPendingRtEvents();
  294. // initialize juce output
  295. for (int i=0; i < numOutputChannels; ++i)
  296. FloatVectorOperations::clear(outputChannelData[i], numSamples);
  297. // initialize input events
  298. carla_zeroStruct<EngineEvent>(pData->events.in, kMaxEngineEventInternalCount);
  299. // TODO - get events from juce
  300. if (pData->graph.isRack)
  301. {
  302. pData->processRackFull(inputChannelData, static_cast<uint32_t>(numInputChannels),
  303. outputChannelData, static_cast<uint32_t>(numOutputChannels),
  304. static_cast<uint32_t>(numSamples), false);
  305. }
  306. else
  307. {
  308. }
  309. // output events
  310. {
  311. // TODO
  312. //fMidiOutEvents...
  313. }
  314. runPendingRtEvents();
  315. return;
  316. }
  317. void audioDeviceAboutToStart(AudioIODevice* /*device*/) override
  318. {
  319. }
  320. void audioDeviceStopped() override
  321. {
  322. }
  323. void audioDeviceError(const String& errorMessage) override
  324. {
  325. callback(ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0f, errorMessage.toRawUTF8());
  326. }
  327. // -------------------------------------------------------------------
  328. bool connectRackMidiInPort(const char* const /*portName*/) override
  329. {
  330. return false;
  331. }
  332. bool connectRackMidiOutPort(const char* const /*portName*/) override
  333. {
  334. return false;
  335. }
  336. bool disconnectRackMidiInPort(const char* const /*portName*/) override
  337. {
  338. return false;
  339. }
  340. bool disconnectRackMidiOutPort(const char* const /*portName*/) override
  341. {
  342. return false;
  343. }
  344. // -------------------------------------
  345. private:
  346. ScopedPointer<AudioIODevice> fDevice;
  347. AudioIODeviceType* const fDeviceType;
  348. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJuce)
  349. };
  350. // -----------------------------------------
  351. CarlaEngine* CarlaEngine::newJuce(const AudioApi api)
  352. {
  353. initJuceDevicesIfNeeded();
  354. String juceApi;
  355. switch (api)
  356. {
  357. case AUDIO_API_NULL:
  358. case AUDIO_API_OSS:
  359. case AUDIO_API_PULSE:
  360. break;
  361. case AUDIO_API_JACK:
  362. juceApi = "JACK";
  363. break;
  364. case AUDIO_API_ALSA:
  365. juceApi = "ALSA";
  366. break;
  367. case AUDIO_API_CORE:
  368. juceApi = "CoreAudio";
  369. break;
  370. case AUDIO_API_ASIO:
  371. juceApi = "ASIO";
  372. break;
  373. case AUDIO_API_DS:
  374. juceApi = "DirectSound";
  375. break;
  376. }
  377. if (juceApi.isEmpty())
  378. return nullptr;
  379. AudioIODeviceType* deviceType = nullptr;
  380. for (int i=0, count=gJuceDeviceTypes.size(); i < count; ++i)
  381. {
  382. deviceType = gJuceDeviceTypes[i];
  383. if (deviceType == nullptr || deviceType->getTypeName() == juceApi)
  384. break;
  385. }
  386. if (deviceType == nullptr)
  387. return nullptr;
  388. deviceType->scanForDevices();
  389. return new CarlaEngineJuce(deviceType);
  390. }
  391. uint CarlaEngine::getJuceApiCount()
  392. {
  393. initJuceDevicesIfNeeded();
  394. return static_cast<uint>(gJuceDeviceTypes.size());
  395. }
  396. const char* CarlaEngine::getJuceApiName(const uint uindex)
  397. {
  398. initJuceDevicesIfNeeded();
  399. const int index(static_cast<int>(uindex));
  400. CARLA_SAFE_ASSERT_RETURN(index < gJuceDeviceTypes.size(), nullptr);
  401. AudioIODeviceType* const deviceType(gJuceDeviceTypes[index]);
  402. CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);
  403. return deviceType->getTypeName().toRawUTF8();
  404. }
  405. const char* const* CarlaEngine::getJuceApiDeviceNames(const uint uindex)
  406. {
  407. initJuceDevicesIfNeeded();
  408. const int index(static_cast<int>(uindex));
  409. CARLA_SAFE_ASSERT_RETURN(index < gJuceDeviceTypes.size(), nullptr);
  410. AudioIODeviceType* const deviceType(gJuceDeviceTypes[index]);
  411. CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);
  412. deviceType->scanForDevices();
  413. StringArray deviceNames(deviceType->getDeviceNames());
  414. const int deviceNameCount(deviceNames.size());
  415. if (deviceNameCount <= 0)
  416. return nullptr;
  417. if (gRetNames != nullptr)
  418. {
  419. for (int i=0; gRetNames[i] != nullptr; ++i)
  420. delete[] gRetNames[i];
  421. delete[] gRetNames;
  422. }
  423. gRetNames = new const char*[deviceNameCount+1];
  424. for (int i=0; i < deviceNameCount; ++i)
  425. gRetNames[i] = carla_strdup(deviceNames[i].toRawUTF8());
  426. gRetNames[deviceNameCount] = nullptr;
  427. return gRetNames;
  428. }
  429. const EngineDriverDeviceInfo* CarlaEngine::getJuceDeviceInfo(const uint uindex, const char* const deviceName)
  430. {
  431. initJuceDevicesIfNeeded();
  432. const int index(static_cast<int>(uindex));
  433. CARLA_SAFE_ASSERT_RETURN(index < gJuceDeviceTypes.size(), nullptr);
  434. AudioIODeviceType* const deviceType(gJuceDeviceTypes[index]);
  435. CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);
  436. deviceType->scanForDevices();
  437. ScopedPointer<AudioIODevice> device(deviceType->createDevice(deviceName, deviceName));
  438. if (device == nullptr)
  439. return nullptr;
  440. static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
  441. static uint32_t dummyBufferSizes[11] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
  442. static double dummySampleRates[14] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };
  443. // reset
  444. devInfo.hints = ENGINE_DRIVER_DEVICE_VARIABLE_BUFFER_SIZE | ENGINE_DRIVER_DEVICE_VARIABLE_SAMPLE_RATE;
  445. // cleanup
  446. if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
  447. {
  448. delete[] devInfo.bufferSizes;
  449. devInfo.bufferSizes = nullptr;
  450. }
  451. if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
  452. {
  453. delete[] devInfo.sampleRates;
  454. devInfo.sampleRates = nullptr;
  455. }
  456. if (device->hasControlPanel())
  457. devInfo.hints |= ENGINE_DRIVER_DEVICE_HAS_CONTROL_PANEL;
  458. Array<int> juceBufferSizes = device->getAvailableBufferSizes();
  459. if (int bufferSizesCount = juceBufferSizes.size())
  460. {
  461. uint32_t* const bufferSizes(new uint32_t[bufferSizesCount+1]);
  462. for (int i=0; i < bufferSizesCount; ++i)
  463. bufferSizes[i] = static_cast<uint32_t>(juceBufferSizes[i]);
  464. bufferSizes[bufferSizesCount] = 0;
  465. devInfo.bufferSizes = bufferSizes;
  466. }
  467. else
  468. {
  469. devInfo.bufferSizes = dummyBufferSizes;
  470. }
  471. Array<double> juceSampleRates = device->getAvailableSampleRates();
  472. if (int sampleRatesCount = juceSampleRates.size())
  473. {
  474. double* const sampleRates(new double[sampleRatesCount+1]);
  475. for (int i=0; i < sampleRatesCount; ++i)
  476. sampleRates[i] = juceSampleRates[i];
  477. sampleRates[sampleRatesCount] = 0.0;
  478. devInfo.sampleRates = sampleRates;
  479. }
  480. else
  481. {
  482. devInfo.sampleRates = dummySampleRates;
  483. }
  484. return &devInfo;
  485. }
  486. // -----------------------------------------
  487. CARLA_BACKEND_END_NAMESPACE