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.

1039 lines
31KB

  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() override
  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) override
  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() override
  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 override
  221. {
  222. return fAudio.isStreamRunning();
  223. }
  224. bool isOffline() const override
  225. {
  226. return false;
  227. }
  228. EngineType type() const override
  229. {
  230. return kEngineTypeRtAudio;
  231. }
  232. // -------------------------------------------------------------------
  233. // Patchbay
  234. bool patchbayConnect(int portA, int portB) override
  235. {
  236. CARLA_ASSERT(fAudioIsReady);
  237. CARLA_ASSERT(portA > PATCHBAY_PORT_MAX);
  238. CARLA_ASSERT(portB > PATCHBAY_PORT_MAX);
  239. if (! fAudioIsReady)
  240. {
  241. setLastError("Engine not ready");
  242. return false;
  243. }
  244. if (portA < PATCHBAY_PORT_MAX)
  245. {
  246. setLastError("Invalid output port");
  247. return false;
  248. }
  249. if (portB < PATCHBAY_PORT_MAX)
  250. {
  251. setLastError("Invalid input port");
  252. return false;
  253. }
  254. // only allow connections between Carla and other ports
  255. if (portA < 0 && portB < 0)
  256. {
  257. setLastError("Invalid connection");
  258. return false;
  259. }
  260. if (portA >= 0 && portB >= 0)
  261. {
  262. setLastError("Invalid connection");
  263. return false;
  264. }
  265. const int carlaPort = (portA < 0) ? portA : portB;
  266. const int targetPort = (carlaPort == portA) ? portB : portA;
  267. bool makeConnection = false;
  268. switch (carlaPort)
  269. {
  270. case PATCHBAY_PORT_AUDIO_IN1:
  271. case PATCHBAY_PORT_AUDIO_IN2:
  272. case PATCHBAY_PORT_AUDIO_OUT1:
  273. case PATCHBAY_PORT_AUDIO_OUT2:
  274. break;
  275. case PATCHBAY_PORT_MIDI_IN:
  276. CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_IN*1000);
  277. CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_IN*1000 + 999);
  278. disconnectMidiPort(true, true);
  279. for (unsigned int i=0, count=fMidiIn.getPortCount(); i < count; ++i)
  280. {
  281. const char* const portName(fMidiIn.getPortName(i).c_str());
  282. if (getPatchbayPortId(portName) == targetPort)
  283. {
  284. fUsedMidiPortIn = portName;
  285. fMidiIn.openPort(i, "midi-in");
  286. makeConnection = true;
  287. break;
  288. }
  289. }
  290. break;
  291. case PATCHBAY_PORT_MIDI_OUT:
  292. CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000);
  293. CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_OUT*1000 + 999);
  294. disconnectMidiPort(false, true);
  295. for (unsigned int i=0, count=fMidiOut.getPortCount(); i < count; ++i)
  296. {
  297. const char* const portName(fMidiOut.getPortName(i).c_str());
  298. if (getPatchbayPortId(portName) == targetPort)
  299. {
  300. fUsedMidiPortOut = portName;
  301. fMidiOut.openPort(i, "midi-out");
  302. makeConnection = true;
  303. break;
  304. }
  305. }
  306. break;
  307. }
  308. if (makeConnection)
  309. {
  310. setLastError("Invalid conenction");
  311. return false;
  312. }
  313. ConnectionToId connectionToId;
  314. connectionToId.id = fLastConnectionId;
  315. connectionToId.portOut = portA;
  316. connectionToId.portIn = portB;
  317. fUsedConnections.append(connectionToId);
  318. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, portA, portB, nullptr);
  319. fLastConnectionId++;
  320. return true;
  321. }
  322. bool patchbayDisconnect(int connectionId) override
  323. {
  324. CARLA_ASSERT(fAudioIsReady);
  325. if (! fAudioIsReady)
  326. {
  327. setLastError("Engine not ready");
  328. return false;
  329. }
  330. for (int i=0, count=fUsedConnections.count(); i < count; ++i)
  331. {
  332. const ConnectionToId& connection(fUsedConnections.at(i));
  333. if (connection.id == connectionId)
  334. {
  335. const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
  336. if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
  337. {
  338. fMidiOut.closePort();
  339. fUsedMidiPortOut.clear();
  340. }
  341. else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
  342. {
  343. fMidiIn.closePort();
  344. fUsedMidiPortIn.clear();
  345. }
  346. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  347. fUsedConnections.takeAt(i);
  348. }
  349. }
  350. return true;
  351. }
  352. void patchbayRefresh() override
  353. {
  354. CARLA_ASSERT(fAudioIsReady);
  355. if (! fAudioIsReady)
  356. return;
  357. fLastConnectionId = 0;
  358. fUsedPortNames.clear();
  359. fUsedConnections.clear();
  360. // Main
  361. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_CARLA, 0, 0.0f, "Carla");
  362. {
  363. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in1");
  364. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in2");
  365. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out1");
  366. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out2");
  367. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_IN, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, "midi-in");
  368. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_OUT, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, "midi-out");
  369. }
  370. // Audio In
  371. {
  372. // TODO
  373. }
  374. // Audio Out
  375. {
  376. // TODO
  377. }
  378. // MIDI In
  379. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, 0, 0.0f, "Readable MIDI ports");
  380. {
  381. const unsigned int portCount = fMidiIn.getPortCount();
  382. for (unsigned int i=0; i < portCount; ++i)
  383. {
  384. PortNameToId portNameToId;
  385. portNameToId.portId = PATCHBAY_GROUP_MIDI_IN*1000 + i;
  386. portNameToId.name = fMidiIn.getPortName(i).c_str();
  387. fUsedPortNames.append(portNameToId);
  388. const char* const portName(portNameToId.name.toUtf8().constData());
  389. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, portName);
  390. }
  391. }
  392. // MIDI Out
  393. callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, 0, 0.0f, "Writable MIDI ports");
  394. {
  395. const unsigned int portCount = fMidiOut.getPortCount();
  396. for (unsigned int i=0; i < portCount; ++i)
  397. {
  398. PortNameToId portNameToId;
  399. portNameToId.portId = PATCHBAY_GROUP_MIDI_OUT*1000 + i;
  400. portNameToId.name = fMidiOut.getPortName(i).c_str();
  401. fUsedPortNames.append(portNameToId);
  402. const char* const portName(portNameToId.name.toUtf8().constData());
  403. callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, portName);
  404. }
  405. }
  406. // Connections
  407. if (! fUsedMidiPortIn.isEmpty())
  408. {
  409. ConnectionToId connectionToId;
  410. connectionToId.id = fLastConnectionId;
  411. connectionToId.portOut = getPatchbayPortId(fUsedMidiPortIn);
  412. connectionToId.portIn = PATCHBAY_PORT_MIDI_IN;
  413. fUsedConnections.append(connectionToId);
  414. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
  415. fLastConnectionId++;
  416. }
  417. if (! fUsedMidiPortOut.isEmpty())
  418. {
  419. ConnectionToId connectionToId;
  420. connectionToId.id = fLastConnectionId;
  421. connectionToId.portOut = PATCHBAY_PORT_MIDI_OUT;
  422. connectionToId.portIn = getPatchbayPortId(fUsedMidiPortOut);
  423. fUsedConnections.append(connectionToId);
  424. callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
  425. fLastConnectionId++;
  426. }
  427. }
  428. // -------------------------------------------------------------------
  429. protected:
  430. void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status)
  431. {
  432. // get buffers from RtAudio
  433. float* insPtr = (float*)inputBuffer;
  434. float* outsPtr = (float*)outputBuffer;
  435. // assert buffers
  436. CARLA_ASSERT(insPtr != nullptr);
  437. CARLA_ASSERT(outsPtr != nullptr);
  438. if (! fAudioIsReady)
  439. {
  440. carla_zeroFloat(outsPtr, nframes*2);
  441. return proccessPendingEvents();
  442. }
  443. if (kData->curPluginCount == 0)
  444. {
  445. // pass-through
  446. if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
  447. carla_copyFloat(outsPtr, insPtr, nframes*2);
  448. return proccessPendingEvents();
  449. }
  450. // initialize audio input
  451. if (fAudioIsInterleaved)
  452. {
  453. for (unsigned int i=0; i < nframes*2; ++i)
  454. {
  455. if (i % 2 == 0)
  456. fAudioInBuf1[i/2] = insPtr[i];
  457. else
  458. fAudioInBuf2[i/2] = insPtr[i];
  459. }
  460. }
  461. else
  462. {
  463. carla_copyFloat(fAudioInBuf1, insPtr, nframes);
  464. carla_copyFloat(fAudioInBuf2, insPtr+nframes, nframes);
  465. }
  466. // initialize audio output
  467. carla_zeroFloat(fAudioOutBuf1, fBufferSize);
  468. carla_zeroFloat(fAudioOutBuf2, fBufferSize);
  469. // initialize input events
  470. carla_zeroMem(kData->rack.in, sizeof(EngineEvent)*RACK_EVENT_COUNT);
  471. if (fMidiInEvents.mutex.tryLock())
  472. {
  473. uint32_t engineEventIndex = 0;
  474. fMidiInEvents.splice();
  475. while (! fMidiInEvents.data.isEmpty())
  476. {
  477. const RtMidiEvent& midiEvent = fMidiInEvents.data.getFirst(true);
  478. EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
  479. engineEvent->clear();
  480. const uint8_t midiStatus = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
  481. const uint8_t midiChannel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);
  482. engineEvent->channel = midiChannel;
  483. if (midiEvent.time < fTimeInfo.frame)
  484. engineEvent->time = 0;
  485. else if (midiEvent.time >= fTimeInfo.frame + nframes)
  486. {
  487. engineEvent->time = fTimeInfo.frame + nframes-1;
  488. carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent->time, fTimeInfo.frame);
  489. }
  490. else
  491. engineEvent->time = midiEvent.time - fTimeInfo.frame;
  492. //carla_stdout("Got midi, time %f vs %i", midiEvent.time, engineEvent->time);
  493. if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus))
  494. {
  495. const uint8_t midiControl = midiEvent.data[1];
  496. engineEvent->type = kEngineEventTypeControl;
  497. if (MIDI_IS_CONTROL_BANK_SELECT(midiControl))
  498. {
  499. const uint8_t midiBank = midiEvent.data[2];
  500. engineEvent->ctrl.type = kEngineControlEventTypeMidiBank;
  501. engineEvent->ctrl.param = midiBank;
  502. engineEvent->ctrl.value = 0.0;
  503. }
  504. else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF)
  505. {
  506. engineEvent->ctrl.type = kEngineControlEventTypeAllSoundOff;
  507. engineEvent->ctrl.param = 0;
  508. engineEvent->ctrl.value = 0.0;
  509. }
  510. else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF)
  511. {
  512. engineEvent->ctrl.type = kEngineControlEventTypeAllNotesOff;
  513. engineEvent->ctrl.param = 0;
  514. engineEvent->ctrl.value = 0.0;
  515. }
  516. else
  517. {
  518. const uint8_t midiValue = midiEvent.data[2];
  519. engineEvent->ctrl.type = kEngineControlEventTypeParameter;
  520. engineEvent->ctrl.param = midiControl;
  521. engineEvent->ctrl.value = double(midiValue)/127.0;
  522. }
  523. }
  524. else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus))
  525. {
  526. const uint8_t midiProgram = midiEvent.data[1];
  527. engineEvent->type = kEngineEventTypeControl;
  528. engineEvent->ctrl.type = kEngineControlEventTypeMidiProgram;
  529. engineEvent->ctrl.param = midiProgram;
  530. engineEvent->ctrl.value = 0.0;
  531. }
  532. else
  533. {
  534. engineEvent->type = kEngineEventTypeMidi;
  535. engineEvent->midi.data[0] = midiStatus;
  536. engineEvent->midi.data[1] = midiEvent.data[1];
  537. engineEvent->midi.data[2] = midiEvent.data[2];
  538. engineEvent->midi.size = midiEvent.size;
  539. }
  540. if (engineEventIndex >= RACK_EVENT_COUNT)
  541. break;
  542. }
  543. fMidiInEvents.mutex.unlock();
  544. }
  545. // create audio buffers
  546. float* inBuf[2] = { fAudioInBuf1, fAudioInBuf2 };
  547. float* outBuf[2] = { fAudioOutBuf1, fAudioOutBuf2 };
  548. processRack(inBuf, outBuf, nframes);
  549. // output audio
  550. if (fAudioIsInterleaved)
  551. {
  552. for (unsigned int i=0; i < nframes*2; ++i)
  553. {
  554. if (i % 2 == 0)
  555. outsPtr[i] = fAudioOutBuf1[i/2];
  556. else
  557. outsPtr[i] = fAudioOutBuf2[i/2];
  558. }
  559. }
  560. else
  561. {
  562. carla_copyFloat(outsPtr, fAudioOutBuf1, nframes);
  563. carla_copyFloat(outsPtr+nframes, fAudioOutBuf2, nframes);
  564. }
  565. // output events
  566. {
  567. // TODO
  568. //fMidiOut.sendMessage();
  569. }
  570. proccessPendingEvents();
  571. return;
  572. // unused
  573. (void)streamTime;
  574. (void)status;
  575. }
  576. void handleMidiCallback(double timeStamp, std::vector<unsigned char>* const message)
  577. {
  578. const size_t messageSize = message->size();
  579. static uint32_t lastTime = 0;
  580. if (messageSize == 0 || messageSize > 4)
  581. return;
  582. timeStamp /= 2;
  583. if (timeStamp > 0.95)
  584. timeStamp = 0.95;
  585. RtMidiEvent midiEvent;
  586. midiEvent.time = fTimeInfo.frame + (timeStamp*(double)fBufferSize);
  587. carla_stdout("Put midi, frame:%09i/%09i, stamp:%g", fTimeInfo.frame, midiEvent.time, timeStamp);
  588. if (midiEvent.time < lastTime)
  589. midiEvent.time = lastTime;
  590. else
  591. lastTime = midiEvent.time;
  592. if (messageSize == 1)
  593. {
  594. midiEvent.data[0] = message->at(0);
  595. midiEvent.data[1] = 0;
  596. midiEvent.data[2] = 0;
  597. midiEvent.data[3] = 0;
  598. midiEvent.size = 1;
  599. }
  600. else if (messageSize == 2)
  601. {
  602. midiEvent.data[0] = message->at(0);
  603. midiEvent.data[1] = message->at(1);
  604. midiEvent.data[2] = 0;
  605. midiEvent.data[3] = 0;
  606. midiEvent.size = 2;
  607. }
  608. else if (messageSize == 3)
  609. {
  610. midiEvent.data[0] = message->at(0);
  611. midiEvent.data[1] = message->at(1);
  612. midiEvent.data[2] = message->at(2);
  613. midiEvent.data[3] = 0;
  614. midiEvent.size = 3;
  615. }
  616. else
  617. {
  618. midiEvent.data[0] = message->at(0);
  619. midiEvent.data[1] = message->at(1);
  620. midiEvent.data[2] = message->at(2);
  621. midiEvent.data[3] = message->at(3);
  622. midiEvent.size = 4;
  623. }
  624. fMidiInEvents.append(midiEvent);
  625. }
  626. // -------------------------------------
  627. void disconnectMidiPort(const bool isInput, const bool doPatchbay)
  628. {
  629. carla_debug("CarlaEngineRtAudio::disconnectMidiPort(%s, %s)", bool2str(isInput), bool2str(doPatchbay));
  630. if (isInput)
  631. {
  632. if (! fUsedMidiPortIn.isEmpty())
  633. {
  634. fUsedMidiPortIn.clear();
  635. fMidiIn.closePort();
  636. }
  637. }
  638. else
  639. {
  640. if (! fUsedMidiPortOut.isEmpty())
  641. {
  642. fUsedMidiPortOut.clear();
  643. fMidiIn.closePort();
  644. }
  645. }
  646. if (! doPatchbay)
  647. return;
  648. for (int i=0, count=fUsedConnections.count(); i < count; ++i)
  649. {
  650. const ConnectionToId& connection(fUsedConnections.at(i));
  651. const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
  652. if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
  653. {
  654. if (isInput)
  655. continue;
  656. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  657. fUsedConnections.takeAt(i);
  658. break;
  659. }
  660. else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
  661. {
  662. if (! isInput)
  663. continue;
  664. callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
  665. fUsedConnections.takeAt(i);
  666. break;
  667. }
  668. }
  669. }
  670. int getPatchbayPortId(const QString& name)
  671. {
  672. carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\")", name.toUtf8().constData());
  673. for (int i=0, count=fUsedPortNames.count(); i < count; ++i)
  674. {
  675. carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\") VS \"%s\"", name.toUtf8().constData(), fUsedPortNames[i].name.toUtf8().constData());
  676. if (fUsedPortNames[i].name == name)
  677. return fUsedPortNames[i].portId;
  678. }
  679. return PATCHBAY_PORT_MAX;
  680. }
  681. // -------------------------------------
  682. private:
  683. RtAudio fAudio;
  684. bool fAudioIsInterleaved;
  685. bool fAudioIsReady;
  686. float* fAudioInBuf1;
  687. float* fAudioInBuf2;
  688. float* fAudioOutBuf1;
  689. float* fAudioOutBuf2;
  690. RtMidiIn fMidiIn;
  691. RtMidiOut fMidiOut;
  692. enum PatchbayGroupIds {
  693. PATCHBAY_GROUP_CARLA = -1,
  694. PATCHBAY_GROUP_AUDIO_IN = 0,
  695. PATCHBAY_GROUP_AUDIO_OUT = 1,
  696. PATCHBAY_GROUP_MIDI_IN = 2,
  697. PATCHBAY_GROUP_MIDI_OUT = 3,
  698. PATCHBAY_GROUP_MAX = 4
  699. };
  700. enum PatchbayPortIds {
  701. PATCHBAY_PORT_AUDIO_IN1 = -1,
  702. PATCHBAY_PORT_AUDIO_IN2 = -2,
  703. PATCHBAY_PORT_AUDIO_OUT1 = -3,
  704. PATCHBAY_PORT_AUDIO_OUT2 = -4,
  705. PATCHBAY_PORT_MIDI_IN = -5,
  706. PATCHBAY_PORT_MIDI_OUT = -6,
  707. PATCHBAY_PORT_MAX = -7
  708. };
  709. struct PortNameToId {
  710. int portId;
  711. QString name;
  712. };
  713. struct ConnectionToId {
  714. int id;
  715. int portOut;
  716. int portIn;
  717. };
  718. int fLastConnectionId;
  719. QList<PortNameToId> fUsedPortNames;
  720. QList<ConnectionToId> fUsedConnections;
  721. QString fUsedMidiPortIn;
  722. QString fUsedMidiPortOut;
  723. struct RtMidiEvent {
  724. uint32_t time;
  725. unsigned char data[4];
  726. unsigned char size;
  727. };
  728. struct RtMidiEvents {
  729. CarlaMutex mutex;
  730. RtList<RtMidiEvent>::Pool dataPool;
  731. RtList<RtMidiEvent> data;
  732. RtList<RtMidiEvent> dataPending;
  733. RtMidiEvents()
  734. : dataPool(512, 512),
  735. data(&dataPool),
  736. dataPending(&dataPool) {}
  737. ~RtMidiEvents()
  738. {
  739. clear();
  740. }
  741. void append(const RtMidiEvent& event)
  742. {
  743. mutex.lock();
  744. dataPending.append(event);
  745. mutex.unlock();
  746. }
  747. void clear()
  748. {
  749. mutex.lock();
  750. data.clear();
  751. dataPending.clear();
  752. mutex.unlock();
  753. }
  754. void splice()
  755. {
  756. dataPending.spliceAppend(data, true);
  757. }
  758. };
  759. RtMidiEvents fMidiInEvents;
  760. RtMidiEvents fMidiOutEvents;
  761. #define handlePtr ((CarlaEngineRtAudio*)userData)
  762. static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData)
  763. {
  764. handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
  765. return 0;
  766. }
  767. static void carla_rtmidi_callback(double timeStamp, std::vector<unsigned char>* message, void* userData)
  768. {
  769. handlePtr->handleMidiCallback(timeStamp, message);
  770. }
  771. #undef handlePtr
  772. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
  773. };
  774. // -----------------------------------------
  775. static std::vector<RtAudio::Api> sRtAudioApis;
  776. static void initRtApis()
  777. {
  778. static bool initiated = false;
  779. if (! initiated)
  780. {
  781. initiated = true;
  782. RtAudio::getCompiledApi(sRtAudioApis);
  783. }
  784. }
  785. CarlaEngine* CarlaEngine::newRtAudio(RtAudioApi api)
  786. {
  787. RtAudio::Api rtApi = RtAudio::UNSPECIFIED;
  788. switch (api)
  789. {
  790. case RTAUDIO_DUMMY:
  791. rtApi = RtAudio::RTAUDIO_DUMMY;
  792. break;
  793. case RTAUDIO_LINUX_ALSA:
  794. rtApi = RtAudio::LINUX_ALSA;
  795. break;
  796. case RTAUDIO_LINUX_PULSE:
  797. rtApi = RtAudio::LINUX_PULSE;
  798. break;
  799. case RTAUDIO_LINUX_OSS:
  800. rtApi = RtAudio::LINUX_OSS;
  801. break;
  802. case RTAUDIO_UNIX_JACK:
  803. rtApi = RtAudio::UNIX_JACK;
  804. break;
  805. case RTAUDIO_MACOSX_CORE:
  806. rtApi = RtAudio::MACOSX_CORE;
  807. break;
  808. case RTAUDIO_WINDOWS_ASIO:
  809. rtApi = RtAudio::WINDOWS_ASIO;
  810. break;
  811. case RTAUDIO_WINDOWS_DS:
  812. rtApi = RtAudio::WINDOWS_DS;
  813. break;
  814. }
  815. return new CarlaEngineRtAudio(rtApi);
  816. }
  817. size_t CarlaEngine::getRtAudioApiCount()
  818. {
  819. initRtApis();
  820. return sRtAudioApis.size();
  821. }
  822. const char* CarlaEngine::getRtAudioApiName(const unsigned int index)
  823. {
  824. initRtApis();
  825. if (index < sRtAudioApis.size())
  826. {
  827. const RtAudio::Api& api(sRtAudioApis[index]);
  828. switch (api)
  829. {
  830. case RtAudio::UNSPECIFIED:
  831. return "Unspecified";
  832. case RtAudio::LINUX_ALSA:
  833. return "ALSA";
  834. case RtAudio::LINUX_PULSE:
  835. return "PulseAudio";
  836. case RtAudio::LINUX_OSS:
  837. return "OSS";
  838. case RtAudio::UNIX_JACK:
  839. #if defined(CARLA_OS_WIN)
  840. return "JACK with WinMM";
  841. #elif defined(CARLA_OS_MAC)
  842. return "JACK with CoreMidi";
  843. #elif defined(CARLA_OS_LINUX)
  844. return "JACK with ALSA-MIDI";
  845. #else
  846. return "JACK (RtAudio)";
  847. #endif
  848. case RtAudio::MACOSX_CORE:
  849. return "CoreAudio";
  850. case RtAudio::WINDOWS_ASIO:
  851. return "ASIO";
  852. case RtAudio::WINDOWS_DS:
  853. return "DirectSound";
  854. case RtAudio::RTAUDIO_DUMMY:
  855. return "Dummy";
  856. }
  857. }
  858. return nullptr;
  859. }
  860. // -----------------------------------------
  861. CARLA_BACKEND_END_NAMESPACE
  862. #endif // CARLA_ENGINE_RTAUDIO