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.

1014 lines
30KB

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