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.

1096 lines
32KB

  1. /*
  2. * Carla RtAudio Engine
  3. * Copyright (C) 2012-2013 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. #ifdef WANT_RTAUDIO
  18. #include "CarlaEngineInternal.hpp"
  19. #include "CarlaBackendUtils.hpp"
  20. #include "CarlaMIDI.h"
  21. #include "RtList.hpp"
  22. #include "RtAudio.h"
  23. #include "RtMidi.h"
  24. CARLA_BACKEND_START_NAMESPACE
  25. #if 0
  26. } // Fix editor indentation
  27. #endif
  28. // -------------------------------------------------------------------------------------------------------------------
  29. static const char** gRetNames = nullptr;
  30. static std::vector<RtAudio::Api> gRtAudioApis;
  31. static void initRtApis()
  32. {
  33. static bool initiated = false;
  34. if (! initiated)
  35. {
  36. initiated = true;
  37. RtAudio::getCompiledApi(gRtAudioApis);
  38. }
  39. }
  40. RtMidi::Api getMatchedAudioMidiAPi(const RtAudio::Api rtApi)
  41. {
  42. switch (rtApi)
  43. {
  44. case RtAudio::UNSPECIFIED:
  45. return RtMidi::UNSPECIFIED;
  46. case RtAudio::LINUX_ALSA:
  47. case RtAudio::LINUX_OSS:
  48. case RtAudio::LINUX_PULSE:
  49. return RtMidi::LINUX_ALSA;
  50. case RtAudio::UNIX_JACK:
  51. #if defined(CARLA_OS_WIN)
  52. return RtMidi::WINDOWS_MM;
  53. #elif defined(CARLA_OS_MAC)
  54. return RtMidi::MACOSX_CORE;
  55. #elif defined(CARLA_OS_LINUX)
  56. return RtMidi::LINUX_ALSA;
  57. #else
  58. return RtMidi::UNIX_JACK;
  59. #endif
  60. case RtAudio::MACOSX_CORE:
  61. return RtMidi::MACOSX_CORE;
  62. case RtAudio::WINDOWS_ASIO:
  63. case RtAudio::WINDOWS_DS:
  64. return RtMidi::WINDOWS_MM;
  65. case RtAudio::RTAUDIO_DUMMY:
  66. return RtMidi::RTMIDI_DUMMY;
  67. }
  68. return RtMidi::UNSPECIFIED;
  69. }
  70. // -------------------------------------------------------------------------------------------------------------------
  71. // RtAudio Engine
  72. class CarlaEngineRtAudio : public CarlaEngine
  73. {
  74. public:
  75. CarlaEngineRtAudio(const RtAudio::Api api)
  76. : CarlaEngine(),
  77. fAudio(api),
  78. fAudioIsInterleaved(false),
  79. fAudioIsReady(false),
  80. fAudioInBuf1(nullptr),
  81. fAudioInBuf2(nullptr),
  82. fAudioOutBuf1(nullptr),
  83. fAudioOutBuf2(nullptr),
  84. fMidiIn(getMatchedAudioMidiAPi(api), "Carla"),
  85. fMidiOut(getMatchedAudioMidiAPi(api), "Carla"),
  86. fLastConnectionId(0)
  87. {
  88. carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
  89. // just to make sure
  90. fOptions.forceStereo = true;
  91. fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK;
  92. }
  93. ~CarlaEngineRtAudio() override
  94. {
  95. carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
  96. CARLA_ASSERT(fAudioInBuf1 == nullptr);
  97. CARLA_ASSERT(fAudioInBuf2 == nullptr);
  98. CARLA_ASSERT(fAudioOutBuf1 == nullptr);
  99. CARLA_ASSERT(fAudioOutBuf2 == nullptr);
  100. fUsedPortNames.clear();
  101. fUsedConnections.clear();
  102. if (gRetNames != nullptr)
  103. {
  104. delete[] gRetNames;
  105. gRetNames = nullptr;
  106. }
  107. }
  108. // -------------------------------------
  109. bool init(const char* const clientName) override
  110. {
  111. carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
  112. CARLA_ASSERT(! fAudioIsReady);
  113. CARLA_ASSERT(fAudioInBuf1 == nullptr);
  114. CARLA_ASSERT(fAudioInBuf2 == nullptr);
  115. CARLA_ASSERT(fAudioOutBuf1 == nullptr);
  116. CARLA_ASSERT(fAudioOutBuf2 == nullptr);
  117. CARLA_ASSERT(clientName != nullptr);
  118. if (fAudio.getDeviceCount() == 0)
  119. {
  120. setLastError("No audio devices available for this driver");
  121. return false;
  122. }
  123. fBufferSize = fOptions.rtaudioBufferSize;
  124. // Audio
  125. {
  126. RtAudio::StreamParameters iParams, oParams;
  127. iParams.deviceId = fAudio.getDefaultInputDevice();
  128. oParams.deviceId = fAudio.getDefaultOutputDevice();
  129. iParams.nChannels = 2;
  130. oParams.nChannels = 2;
  131. RtAudio::StreamOptions rtOptions;
  132. rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME;
  133. rtOptions.numberOfBuffers = 2;
  134. rtOptions.streamName = clientName;
  135. rtOptions.priority = 85;
  136. if (fAudio.getCurrentApi() != RtAudio::LINUX_PULSE)
  137. {
  138. rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
  139. fAudioIsInterleaved = false;
  140. if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA)
  141. rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
  142. }
  143. else
  144. fAudioIsInterleaved = true;
  145. try {
  146. fAudio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, fOptions.rtaudioSampleRate, &fBufferSize, carla_rtaudio_process_callback, this, &rtOptions);
  147. }
  148. catch (RtError& e)
  149. {
  150. carla_stderr2("RtAudio::openStream() failed");
  151. if (e.getType() == RtError::SYSTEM_ERROR)
  152. setLastError("Stream cannot be opened with the specified parameters");
  153. else if (e.getType() == RtError::INVALID_USE)
  154. setLastError("Invalid device ID");
  155. else
  156. setLastError("Unknown error");
  157. return false;
  158. }
  159. try {
  160. fAudio.startStream();
  161. }
  162. catch (RtError& e)
  163. {
  164. carla_stderr2("RtAudio::startStream() failed");
  165. setLastError(e.what());
  166. fAudio.closeStream();
  167. return false;
  168. }
  169. fAudioInBuf1 = new float[fBufferSize];
  170. fAudioInBuf2 = new float[fBufferSize];
  171. fAudioOutBuf1 = new float[fBufferSize];
  172. fAudioOutBuf2 = new float[fBufferSize];
  173. fSampleRate = fAudio.getStreamSampleRate();
  174. }
  175. // MIDI
  176. {
  177. fMidiIn.setCallback(carla_rtmidi_callback, this);
  178. fUsedMidiPortIn.clear();
  179. fUsedMidiPortOut.clear();
  180. }
  181. fAudioIsReady = true;
  182. patchbayRefresh();
  183. return CarlaEngine::init(clientName);
  184. }
  185. bool close() override
  186. {
  187. carla_debug("CarlaEngineRtAudio::close()");
  188. CARLA_ASSERT(fAudioIsReady);
  189. CARLA_ASSERT(fAudioInBuf1 != nullptr);
  190. CARLA_ASSERT(fAudioInBuf2 != nullptr);
  191. CARLA_ASSERT(fAudioOutBuf1 != nullptr);
  192. CARLA_ASSERT(fAudioOutBuf2 != nullptr);
  193. CarlaEngine::close();
  194. fAudioIsReady = false;
  195. if (fAudio.isStreamRunning())
  196. {
  197. try {
  198. fAudio.stopStream();
  199. }
  200. catch (...) {}
  201. }
  202. if (fAudio.isStreamOpen())
  203. {
  204. try {
  205. fAudio.closeStream();
  206. }
  207. catch (...) {}
  208. }
  209. fMidiIn.cancelCallback();
  210. disconnectMidiPort(true, false);
  211. disconnectMidiPort(false, false);
  212. if (fAudioInBuf1 != nullptr)
  213. {
  214. delete[] fAudioInBuf1;
  215. fAudioInBuf1 = nullptr;
  216. }
  217. if (fAudioInBuf2 != nullptr)
  218. {
  219. delete[] fAudioInBuf2;
  220. fAudioInBuf2 = nullptr;
  221. }
  222. if (fAudioOutBuf1 != nullptr)
  223. {
  224. delete[] fAudioOutBuf1;
  225. fAudioOutBuf1 = nullptr;
  226. }
  227. if (fAudioOutBuf2 != nullptr)
  228. {
  229. delete[] fAudioOutBuf2;
  230. fAudioOutBuf2 = nullptr;
  231. }
  232. fMidiInEvents.clear();
  233. fMidiOutEvents.clear();
  234. return true;
  235. }
  236. bool isRunning() const override
  237. {
  238. return fAudio.isStreamRunning();
  239. }
  240. bool isOffline() const override
  241. {
  242. return false;
  243. }
  244. EngineType type() const override
  245. {
  246. return kEngineTypeRtAudio;
  247. }
  248. // -------------------------------------------------------------------
  249. // Patchbay
  250. bool patchbayConnect(int portA, int portB) override
  251. {
  252. CARLA_ASSERT(fAudioIsReady);
  253. CARLA_ASSERT(portA > PATCHBAY_PORT_MAX);
  254. CARLA_ASSERT(portB > PATCHBAY_PORT_MAX);
  255. if (! fAudioIsReady)
  256. {
  257. setLastError("Engine not ready");
  258. return false;
  259. }
  260. if (portA < PATCHBAY_PORT_MAX)
  261. {
  262. setLastError("Invalid output port");
  263. return false;
  264. }
  265. if (portB < PATCHBAY_PORT_MAX)
  266. {
  267. setLastError("Invalid input port");
  268. return false;
  269. }
  270. // only allow connections between Carla and other ports
  271. if (portA < 0 && portB < 0)
  272. {
  273. setLastError("Invalid connection");
  274. return false;
  275. }
  276. if (portA >= 0 && portB >= 0)
  277. {
  278. setLastError("Invalid connection");
  279. return false;
  280. }
  281. const int carlaPort = (portA < 0) ? portA : portB;
  282. const int targetPort = (carlaPort == portA) ? portB : portA;
  283. bool makeConnection = false;
  284. switch (carlaPort)
  285. {
  286. case PATCHBAY_PORT_AUDIO_IN1:
  287. case PATCHBAY_PORT_AUDIO_IN2:
  288. case PATCHBAY_PORT_AUDIO_OUT1:
  289. case PATCHBAY_PORT_AUDIO_OUT2:
  290. break;
  291. case PATCHBAY_PORT_MIDI_IN:
  292. CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_IN*1000);
  293. CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_IN*1000 + 999);
  294. disconnectMidiPort(true, true);
  295. for (unsigned int i=0, count=fMidiIn.getPortCount(); i < count; ++i)
  296. {
  297. const char* const portName(fMidiIn.getPortName(i).c_str());
  298. if (getPatchbayPortId(portName) == targetPort)
  299. {
  300. fUsedMidiPortIn = portName;
  301. fMidiIn.openPort(i, "midi-in");
  302. makeConnection = true;
  303. break;
  304. }
  305. }
  306. break;
  307. case PATCHBAY_PORT_MIDI_OUT:
  308. CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000);
  309. CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_OUT*1000 + 999);
  310. disconnectMidiPort(false, true);
  311. for (unsigned int i=0, count=fMidiOut.getPortCount(); i < count; ++i)
  312. {
  313. const char* const portName(fMidiOut.getPortName(i).c_str());
  314. if (getPatchbayPortId(portName) == targetPort)
  315. {
  316. fUsedMidiPortOut = portName;
  317. fMidiOut.openPort(i, "midi-out");
  318. makeConnection = true;
  319. break;
  320. }
  321. }
  322. break;
  323. }
  324. if (makeConnection)
  325. {
  326. setLastError("Invalid conenction");
  327. return false;
  328. }
  329. ConnectionToId connectionToId;
  330. connectionToId.id = fLastConnectionId;
  331. connectionToId.portOut = portA;
  332. connectionToId.portIn = portB;
  333. fUsedConnections.append(connectionToId);
  334. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, portA, portB, nullptr);
  335. fLastConnectionId++;
  336. return true;
  337. }
  338. bool patchbayDisconnect(int connectionId) override
  339. {
  340. CARLA_ASSERT(fAudioIsReady);
  341. if (! fAudioIsReady)
  342. {
  343. setLastError("Engine not ready");
  344. return false;
  345. }
  346. for (int i=0, count=fUsedConnections.count(); i < count; ++i)
  347. {
  348. const ConnectionToId& connection(fUsedConnections.at(i));
  349. if (connection.id == connectionId)
  350. {
  351. const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
  352. if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
  353. {
  354. fMidiOut.closePort();
  355. fUsedMidiPortOut.clear();
  356. }
  357. else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
  358. {
  359. fMidiIn.closePort();
  360. fUsedMidiPortIn.clear();
  361. }
  362. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  363. fUsedConnections.takeAt(i);
  364. }
  365. }
  366. return true;
  367. }
  368. void patchbayRefresh() override
  369. {
  370. CARLA_ASSERT(fAudioIsReady);
  371. if (! fAudioIsReady)
  372. return;
  373. fLastConnectionId = 0;
  374. fUsedPortNames.clear();
  375. fUsedConnections.clear();
  376. // Main
  377. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_CARLA, 0, 0.0f, "Carla");
  378. {
  379. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in1");
  380. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in2");
  381. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out1");
  382. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out2");
  383. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_IN, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, "midi-in");
  384. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_OUT, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, "midi-out");
  385. }
  386. // Audio In
  387. {
  388. // TODO
  389. }
  390. // Audio Out
  391. {
  392. // TODO
  393. }
  394. // MIDI In
  395. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, 0, 0.0f, "Readable MIDI ports");
  396. {
  397. const unsigned int portCount = fMidiIn.getPortCount();
  398. for (unsigned int i=0; i < portCount; ++i)
  399. {
  400. PortNameToId portNameToId;
  401. portNameToId.portId = PATCHBAY_GROUP_MIDI_IN*1000 + i;
  402. portNameToId.name = fMidiIn.getPortName(i).c_str();
  403. fUsedPortNames.append(portNameToId);
  404. const char* const portName(portNameToId.name.toUtf8().constData());
  405. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, portName);
  406. }
  407. }
  408. // MIDI Out
  409. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, 0, 0.0f, "Writable MIDI ports");
  410. {
  411. const unsigned int portCount = fMidiOut.getPortCount();
  412. for (unsigned int i=0; i < portCount; ++i)
  413. {
  414. PortNameToId portNameToId;
  415. portNameToId.portId = PATCHBAY_GROUP_MIDI_OUT*1000 + i;
  416. portNameToId.name = fMidiOut.getPortName(i).c_str();
  417. fUsedPortNames.append(portNameToId);
  418. const char* const portName(portNameToId.name.toUtf8().constData());
  419. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, portName);
  420. }
  421. }
  422. // Connections
  423. if (! fUsedMidiPortIn.isEmpty())
  424. {
  425. ConnectionToId connectionToId;
  426. connectionToId.id = fLastConnectionId;
  427. connectionToId.portOut = getPatchbayPortId(fUsedMidiPortIn);
  428. connectionToId.portIn = PATCHBAY_PORT_MIDI_IN;
  429. fUsedConnections.append(connectionToId);
  430. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
  431. fLastConnectionId++;
  432. }
  433. if (! fUsedMidiPortOut.isEmpty())
  434. {
  435. ConnectionToId connectionToId;
  436. connectionToId.id = fLastConnectionId;
  437. connectionToId.portOut = PATCHBAY_PORT_MIDI_OUT;
  438. connectionToId.portIn = getPatchbayPortId(fUsedMidiPortOut);
  439. fUsedConnections.append(connectionToId);
  440. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
  441. fLastConnectionId++;
  442. }
  443. }
  444. // -------------------------------------------------------------------
  445. protected:
  446. void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status)
  447. {
  448. // get buffers from RtAudio
  449. float* insPtr = (float*)inputBuffer;
  450. float* outsPtr = (float*)outputBuffer;
  451. // assert buffers
  452. CARLA_ASSERT(insPtr != nullptr);
  453. CARLA_ASSERT(outsPtr != nullptr);
  454. if (! fAudioIsReady)
  455. {
  456. carla_zeroFloat(outsPtr, nframes*2);
  457. return proccessPendingEvents();
  458. }
  459. if (kData->curPluginCount == 0)
  460. {
  461. // pass-through
  462. if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
  463. carla_copyFloat(outsPtr, insPtr, nframes*2);
  464. return proccessPendingEvents();
  465. }
  466. // initialize audio input
  467. if (fAudioIsInterleaved)
  468. {
  469. for (unsigned int i=0; i < nframes*2; ++i)
  470. {
  471. if (i % 2 == 0)
  472. fAudioInBuf1[i/2] = insPtr[i];
  473. else
  474. fAudioInBuf2[i/2] = insPtr[i];
  475. }
  476. }
  477. else
  478. {
  479. carla_copyFloat(fAudioInBuf1, insPtr, nframes);
  480. carla_copyFloat(fAudioInBuf2, insPtr+nframes, nframes);
  481. }
  482. // initialize audio output
  483. carla_zeroFloat(fAudioOutBuf1, fBufferSize);
  484. carla_zeroFloat(fAudioOutBuf2, fBufferSize);
  485. // initialize input events
  486. carla_zeroMem(kData->rack.in, sizeof(EngineEvent)*RACK_EVENT_COUNT);
  487. if (fMidiInEvents.mutex.tryLock())
  488. {
  489. uint32_t engineEventIndex = 0;
  490. fMidiInEvents.splice();
  491. while (! fMidiInEvents.data.isEmpty())
  492. {
  493. const RtMidiEvent& midiEvent = fMidiInEvents.data.getFirst(true);
  494. EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
  495. engineEvent->clear();
  496. const uint8_t midiStatus = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
  497. const uint8_t midiChannel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);
  498. engineEvent->channel = midiChannel;
  499. if (midiEvent.time < fTimeInfo.frame)
  500. engineEvent->time = 0;
  501. else if (midiEvent.time >= fTimeInfo.frame + nframes)
  502. {
  503. engineEvent->time = fTimeInfo.frame + nframes-1;
  504. carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent->time, fTimeInfo.frame);
  505. }
  506. else
  507. engineEvent->time = midiEvent.time - fTimeInfo.frame;
  508. //carla_stdout("Got midi, time %f vs %i", midiEvent.time, engineEvent->time);
  509. if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus))
  510. {
  511. const uint8_t midiControl = midiEvent.data[1];
  512. engineEvent->type = kEngineEventTypeControl;
  513. if (MIDI_IS_CONTROL_BANK_SELECT(midiControl))
  514. {
  515. const uint8_t midiBank = midiEvent.data[2];
  516. engineEvent->ctrl.type = kEngineControlEventTypeMidiBank;
  517. engineEvent->ctrl.param = midiBank;
  518. engineEvent->ctrl.value = 0.0;
  519. }
  520. else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF)
  521. {
  522. engineEvent->ctrl.type = kEngineControlEventTypeAllSoundOff;
  523. engineEvent->ctrl.param = 0;
  524. engineEvent->ctrl.value = 0.0;
  525. }
  526. else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF)
  527. {
  528. engineEvent->ctrl.type = kEngineControlEventTypeAllNotesOff;
  529. engineEvent->ctrl.param = 0;
  530. engineEvent->ctrl.value = 0.0;
  531. }
  532. else
  533. {
  534. const uint8_t midiValue = midiEvent.data[2];
  535. engineEvent->ctrl.type = kEngineControlEventTypeParameter;
  536. engineEvent->ctrl.param = midiControl;
  537. engineEvent->ctrl.value = double(midiValue)/127.0;
  538. }
  539. }
  540. else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus))
  541. {
  542. const uint8_t midiProgram = midiEvent.data[1];
  543. engineEvent->type = kEngineEventTypeControl;
  544. engineEvent->ctrl.type = kEngineControlEventTypeMidiProgram;
  545. engineEvent->ctrl.param = midiProgram;
  546. engineEvent->ctrl.value = 0.0;
  547. }
  548. else
  549. {
  550. engineEvent->type = kEngineEventTypeMidi;
  551. engineEvent->midi.data[0] = midiStatus;
  552. engineEvent->midi.data[1] = midiEvent.data[1];
  553. engineEvent->midi.data[2] = midiEvent.data[2];
  554. engineEvent->midi.size = midiEvent.size;
  555. }
  556. if (engineEventIndex >= RACK_EVENT_COUNT)
  557. break;
  558. }
  559. fMidiInEvents.mutex.unlock();
  560. }
  561. // create audio buffers
  562. float* inBuf[2] = { fAudioInBuf1, fAudioInBuf2 };
  563. float* outBuf[2] = { fAudioOutBuf1, fAudioOutBuf2 };
  564. processRack(inBuf, outBuf, nframes);
  565. // output audio
  566. if (fAudioIsInterleaved)
  567. {
  568. for (unsigned int i=0; i < nframes*2; ++i)
  569. {
  570. if (i % 2 == 0)
  571. outsPtr[i] = fAudioOutBuf1[i/2];
  572. else
  573. outsPtr[i] = fAudioOutBuf2[i/2];
  574. }
  575. }
  576. else
  577. {
  578. carla_copyFloat(outsPtr, fAudioOutBuf1, nframes);
  579. carla_copyFloat(outsPtr+nframes, fAudioOutBuf2, nframes);
  580. }
  581. // output events
  582. {
  583. // TODO
  584. //fMidiOut.sendMessage();
  585. }
  586. proccessPendingEvents();
  587. return;
  588. // unused
  589. (void)streamTime;
  590. (void)status;
  591. }
  592. void handleMidiCallback(double timeStamp, std::vector<unsigned char>* const message)
  593. {
  594. const size_t messageSize = message->size();
  595. static uint32_t lastTime = 0;
  596. if (messageSize == 0 || messageSize > 4)
  597. return;
  598. timeStamp /= 2;
  599. if (timeStamp > 0.95)
  600. timeStamp = 0.95;
  601. RtMidiEvent midiEvent;
  602. midiEvent.time = fTimeInfo.frame + (timeStamp*(double)fBufferSize);
  603. carla_stdout("Put midi, frame:%09i/%09i, stamp:%g", fTimeInfo.frame, midiEvent.time, timeStamp);
  604. if (midiEvent.time < lastTime)
  605. midiEvent.time = lastTime;
  606. else
  607. lastTime = midiEvent.time;
  608. if (messageSize == 1)
  609. {
  610. midiEvent.data[0] = message->at(0);
  611. midiEvent.data[1] = 0;
  612. midiEvent.data[2] = 0;
  613. midiEvent.data[3] = 0;
  614. midiEvent.size = 1;
  615. }
  616. else if (messageSize == 2)
  617. {
  618. midiEvent.data[0] = message->at(0);
  619. midiEvent.data[1] = message->at(1);
  620. midiEvent.data[2] = 0;
  621. midiEvent.data[3] = 0;
  622. midiEvent.size = 2;
  623. }
  624. else if (messageSize == 3)
  625. {
  626. midiEvent.data[0] = message->at(0);
  627. midiEvent.data[1] = message->at(1);
  628. midiEvent.data[2] = message->at(2);
  629. midiEvent.data[3] = 0;
  630. midiEvent.size = 3;
  631. }
  632. else
  633. {
  634. midiEvent.data[0] = message->at(0);
  635. midiEvent.data[1] = message->at(1);
  636. midiEvent.data[2] = message->at(2);
  637. midiEvent.data[3] = message->at(3);
  638. midiEvent.size = 4;
  639. }
  640. fMidiInEvents.append(midiEvent);
  641. }
  642. // -------------------------------------
  643. void disconnectMidiPort(const bool isInput, const bool doPatchbay)
  644. {
  645. carla_debug("CarlaEngineRtAudio::disconnectMidiPort(%s, %s)", bool2str(isInput), bool2str(doPatchbay));
  646. if (isInput)
  647. {
  648. if (! fUsedMidiPortIn.isEmpty())
  649. {
  650. fUsedMidiPortIn.clear();
  651. fMidiIn.closePort();
  652. }
  653. }
  654. else
  655. {
  656. if (! fUsedMidiPortOut.isEmpty())
  657. {
  658. fUsedMidiPortOut.clear();
  659. fMidiIn.closePort();
  660. }
  661. }
  662. if (! doPatchbay)
  663. return;
  664. for (int i=0, count=fUsedConnections.count(); i < count; ++i)
  665. {
  666. const ConnectionToId& connection(fUsedConnections.at(i));
  667. const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
  668. if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
  669. {
  670. if (isInput)
  671. continue;
  672. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  673. fUsedConnections.takeAt(i);
  674. break;
  675. }
  676. else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
  677. {
  678. if (! isInput)
  679. continue;
  680. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  681. fUsedConnections.takeAt(i);
  682. break;
  683. }
  684. }
  685. }
  686. int getPatchbayPortId(const QString& name)
  687. {
  688. carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\")", name.toUtf8().constData());
  689. for (int i=0, count=fUsedPortNames.count(); i < count; ++i)
  690. {
  691. carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\") VS \"%s\"", name.toUtf8().constData(), fUsedPortNames[i].name.toUtf8().constData());
  692. if (fUsedPortNames[i].name == name)
  693. return fUsedPortNames[i].portId;
  694. }
  695. return PATCHBAY_PORT_MAX;
  696. }
  697. // -------------------------------------
  698. private:
  699. RtAudio fAudio;
  700. bool fAudioIsInterleaved;
  701. bool fAudioIsReady;
  702. float* fAudioInBuf1;
  703. float* fAudioInBuf2;
  704. float* fAudioOutBuf1;
  705. float* fAudioOutBuf2;
  706. RtMidiIn fMidiIn;
  707. RtMidiOut fMidiOut;
  708. enum PatchbayGroupIds {
  709. PATCHBAY_GROUP_CARLA = -1,
  710. PATCHBAY_GROUP_AUDIO_IN = 0,
  711. PATCHBAY_GROUP_AUDIO_OUT = 1,
  712. PATCHBAY_GROUP_MIDI_IN = 2,
  713. PATCHBAY_GROUP_MIDI_OUT = 3,
  714. PATCHBAY_GROUP_MAX = 4
  715. };
  716. enum PatchbayPortIds {
  717. PATCHBAY_PORT_AUDIO_IN1 = -1,
  718. PATCHBAY_PORT_AUDIO_IN2 = -2,
  719. PATCHBAY_PORT_AUDIO_OUT1 = -3,
  720. PATCHBAY_PORT_AUDIO_OUT2 = -4,
  721. PATCHBAY_PORT_MIDI_IN = -5,
  722. PATCHBAY_PORT_MIDI_OUT = -6,
  723. PATCHBAY_PORT_MAX = -7
  724. };
  725. struct PortNameToId {
  726. int portId;
  727. QString name;
  728. };
  729. struct ConnectionToId {
  730. int id;
  731. int portOut;
  732. int portIn;
  733. };
  734. int fLastConnectionId;
  735. QList<PortNameToId> fUsedPortNames;
  736. QList<ConnectionToId> fUsedConnections;
  737. QString fUsedMidiPortIn;
  738. QString fUsedMidiPortOut;
  739. struct RtMidiEvent {
  740. uint32_t time;
  741. unsigned char data[4];
  742. unsigned char size;
  743. };
  744. struct RtMidiEvents {
  745. CarlaMutex mutex;
  746. RtList<RtMidiEvent>::Pool dataPool;
  747. RtList<RtMidiEvent> data;
  748. RtList<RtMidiEvent> dataPending;
  749. RtMidiEvents()
  750. : dataPool(512, 512),
  751. data(&dataPool),
  752. dataPending(&dataPool) {}
  753. ~RtMidiEvents()
  754. {
  755. clear();
  756. }
  757. void append(const RtMidiEvent& event)
  758. {
  759. mutex.lock();
  760. dataPending.append(event);
  761. mutex.unlock();
  762. }
  763. void clear()
  764. {
  765. mutex.lock();
  766. data.clear();
  767. dataPending.clear();
  768. mutex.unlock();
  769. }
  770. void splice()
  771. {
  772. dataPending.spliceAppend(data, true);
  773. }
  774. };
  775. RtMidiEvents fMidiInEvents;
  776. RtMidiEvents fMidiOutEvents;
  777. #define handlePtr ((CarlaEngineRtAudio*)userData)
  778. static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int 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<unsigned char>* 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(RtAudioApi api)
  792. {
  793. RtAudio::Api rtApi(RtAudio::UNSPECIFIED);
  794. switch (api)
  795. {
  796. case RTAUDIO_DUMMY:
  797. rtApi = RtAudio::RTAUDIO_DUMMY;
  798. break;
  799. case RTAUDIO_LINUX_ALSA:
  800. rtApi = RtAudio::LINUX_ALSA;
  801. break;
  802. case RTAUDIO_LINUX_PULSE:
  803. rtApi = RtAudio::LINUX_PULSE;
  804. break;
  805. case RTAUDIO_LINUX_OSS:
  806. rtApi = RtAudio::LINUX_OSS;
  807. break;
  808. case RTAUDIO_UNIX_JACK:
  809. rtApi = RtAudio::UNIX_JACK;
  810. break;
  811. case RTAUDIO_MACOSX_CORE:
  812. rtApi = RtAudio::MACOSX_CORE;
  813. break;
  814. case RTAUDIO_WINDOWS_ASIO:
  815. rtApi = RtAudio::WINDOWS_ASIO;
  816. break;
  817. case RTAUDIO_WINDOWS_DS:
  818. rtApi = RtAudio::WINDOWS_DS;
  819. break;
  820. }
  821. return new CarlaEngineRtAudio(rtApi);
  822. }
  823. size_t CarlaEngine::getRtAudioApiCount()
  824. {
  825. initRtApis();
  826. return gRtAudioApis.size();
  827. }
  828. const char* CarlaEngine::getRtAudioApiName(const unsigned int index)
  829. {
  830. initRtApis();
  831. if (index < gRtAudioApis.size())
  832. {
  833. const RtAudio::Api& api(gRtAudioApis[index]);
  834. switch (api)
  835. {
  836. case RtAudio::UNSPECIFIED:
  837. return "Unspecified";
  838. case RtAudio::LINUX_ALSA:
  839. return "ALSA";
  840. case RtAudio::LINUX_PULSE:
  841. return "PulseAudio";
  842. case RtAudio::LINUX_OSS:
  843. return "OSS";
  844. case RtAudio::UNIX_JACK:
  845. #if defined(CARLA_OS_WIN)
  846. return "JACK with WinMM";
  847. #elif defined(CARLA_OS_MAC)
  848. return "JACK with CoreMidi";
  849. #elif defined(CARLA_OS_LINUX)
  850. return "JACK with ALSA-MIDI";
  851. #else
  852. return "JACK (RtAudio)";
  853. #endif
  854. case RtAudio::MACOSX_CORE:
  855. return "CoreAudio";
  856. case RtAudio::WINDOWS_ASIO:
  857. return "ASIO";
  858. case RtAudio::WINDOWS_DS:
  859. return "DirectSound";
  860. case RtAudio::RTAUDIO_DUMMY:
  861. return "Dummy";
  862. }
  863. }
  864. return nullptr;
  865. }
  866. const char** CarlaEngine::getRtAudioApiDeviceNames(const unsigned int index)
  867. {
  868. initRtApis();
  869. if (index < gRtAudioApis.size())
  870. {
  871. const RtAudio::Api& api(gRtAudioApis[index]);
  872. RtAudio rtAudio(api);
  873. if (gRetNames != nullptr)
  874. {
  875. int i=0;
  876. while (gRetNames[i] != nullptr)
  877. delete[] gRetNames[i++];
  878. delete[] gRetNames;
  879. gRetNames = nullptr;
  880. }
  881. const unsigned int devCount(rtAudio.getDeviceCount());
  882. if (devCount > 0)
  883. {
  884. NonRtList<const char*> devNames;
  885. for (unsigned int i=0; i < devCount; ++i)
  886. {
  887. RtAudio::DeviceInfo devInfo(rtAudio.getDeviceInfo(i));
  888. if (devInfo.probed && devInfo.outputChannels > 0 /*&& (devInfo.nativeFormats & RTAUDIO_FLOAT32) != 0*/)
  889. devNames.append(carla_strdup(devInfo.name.c_str()));
  890. }
  891. const unsigned int realDevCount(devNames.count());
  892. gRetNames = new const char*[realDevCount+1];
  893. for (unsigned int i=0; i < realDevCount; ++i)
  894. gRetNames[i] = devNames.getAt(i);
  895. gRetNames[realDevCount] = nullptr;
  896. devNames.clear();
  897. return gRetNames;
  898. }
  899. }
  900. return nullptr;
  901. }
  902. // -----------------------------------------
  903. CARLA_BACKEND_END_NAMESPACE
  904. #endif // CARLA_ENGINE_RTAUDIO