Cleanup
[carla.git] / source / backend / engine / CarlaEngineRtAudio.cpp
bloba393e09cf4dcc62a917452ab7e67bd51d984dc0c
1 // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
2 // SPDX-License-Identifier: GPL-2.0-or-later
4 #include "CarlaEngineGraph.hpp"
5 #include "CarlaEngineInit.hpp"
6 #include "CarlaEngineInternal.hpp"
7 #include "CarlaBackendUtils.hpp"
8 #include "CarlaMathUtils.hpp"
9 #include "CarlaStringList.hpp"
11 #include "RtLinkedList.hpp"
13 #include "jackbridge/JackBridge.hpp"
15 #if defined(__clang__)
16 # pragma clang diagnostic push
17 # pragma clang diagnostic ignored "-Wconversion"
18 # pragma clang diagnostic ignored "-Wdeprecated-copy"
19 # pragma clang diagnostic ignored "-Weffc++"
20 # pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
21 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
22 # pragma GCC diagnostic push
23 # pragma GCC diagnostic ignored "-Wconversion"
24 # pragma GCC diagnostic ignored "-Weffc++"
25 # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
26 #endif
28 #include "rtaudio/RtAudio.h"
29 #include "rtmidi/RtMidi.h"
31 #if defined(__clang__)
32 # pragma clang diagnostic pop
33 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
34 # pragma GCC diagnostic pop
35 #endif
37 CARLA_BACKEND_START_NAMESPACE
39 // -------------------------------------------------------------------------------------------------------------------
40 // Global static data
42 static CharStringListPtr gDeviceNames;
43 static std::vector<RtAudio::Api> gRtAudioApis;
45 // -------------------------------------------------------------------------------------------------------------------
47 static void initRtAudioAPIsIfNeeded()
49 static bool needsInit = true;
51 if (! needsInit)
52 return;
54 needsInit = false;
56 // get APIs in a local var, and pass wanted ones into gRtAudioApis
58 std::vector<RtAudio::Api> apis;
59 RtAudio::getCompiledApi(apis);
61 for (std::vector<RtAudio::Api>::const_iterator it = apis.begin(), end=apis.end(); it != end; ++it)
63 const RtAudio::Api& api(*it);
65 if (api == RtAudio::UNIX_JACK)
67 #if defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
68 if ( ! jackbridge_is_ok())
69 continue;
70 #else
71 /* NOTE
72 * RtMidi doesn't have a native MIDI backend for these OSes,
73 * Using RtAudio JACK funcitonality is only useful when we need to access the native MIDI APIs.
74 * (JACK audio + ALSA MIDI, or JACK audio + CoreMidi, or JACK audio + Windows MIDI)
75 * Because RtMidi has no native MIDI support outside of win/mac/linux, we skip these RtAudio APIs.
76 * Those OSes can use Carla's JACK support directly, which is much better than RtAudio classes.
78 continue;
79 #endif
82 gRtAudioApis.push_back(api);
86 static const char* getRtAudioApiName(const RtAudio::Api api) noexcept
88 switch (api)
90 case RtAudio::UNSPECIFIED:
91 return "Unspecified";
92 case RtAudio::LINUX_ALSA:
93 return "ALSA";
94 case RtAudio::LINUX_OSS:
95 return "OSS";
96 case RtAudio::UNIX_PULSE:
97 return "PulseAudio";
98 case RtAudio::UNIX_JACK:
99 #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
100 return "JACK with ALSA-MIDI";
101 #elif defined(CARLA_OS_MAC)
102 return "JACK with CoreMidi";
103 #elif defined(CARLA_OS_WIN)
104 return "JACK with WinMM";
105 #else
106 return "JACK (RtAudio)";
107 #endif
108 case RtAudio::MACOSX_CORE:
109 return "CoreAudio";
110 case RtAudio::WINDOWS_ASIO:
111 return "ASIO";
112 case RtAudio::WINDOWS_DS:
113 return "DirectSound";
114 case RtAudio::WINDOWS_WASAPI:
115 return "WASAPI";
116 case RtAudio::RTAUDIO_DUMMY:
117 return "Dummy";
120 carla_stderr("CarlaBackend::getRtAudioApiName(%i) - invalid API", api);
121 return nullptr;
124 static RtMidi::Api getMatchedAudioMidiAPI(const RtAudio::Api rtApi) noexcept
126 switch (rtApi)
128 case RtAudio::UNSPECIFIED:
129 return RtMidi::UNSPECIFIED;
131 case RtAudio::LINUX_ALSA:
132 case RtAudio::LINUX_OSS:
133 return RtMidi::LINUX_ALSA;
135 case RtAudio::UNIX_PULSE:
136 case RtAudio::UNIX_JACK:
137 #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
138 return RtMidi::LINUX_ALSA;
139 #elif defined(CARLA_OS_MAC)
140 return RtMidi::MACOSX_CORE;
141 #elif defined(CARLA_OS_WIN)
142 return RtMidi::WINDOWS_MM;
143 #else
144 return RtMidi::RTMIDI_DUMMY;
145 #endif
147 case RtAudio::MACOSX_CORE:
148 return RtMidi::MACOSX_CORE;
150 case RtAudio::WINDOWS_ASIO:
151 case RtAudio::WINDOWS_DS:
152 case RtAudio::WINDOWS_WASAPI:
153 return RtMidi::WINDOWS_MM;
155 case RtAudio::RTAUDIO_DUMMY:
156 return RtMidi::RTMIDI_DUMMY;
159 return RtMidi::UNSPECIFIED;
162 // -------------------------------------------------------------------------------------------------------------------
163 // RtAudio Engine
165 class CarlaEngineRtAudio : public CarlaEngine
167 public:
168 CarlaEngineRtAudio(const RtAudio::Api api)
169 : CarlaEngine(),
170 fAudio(api),
171 fAudioInterleaved(false),
172 fAudioInCount(0),
173 fAudioOutCount(0),
174 fLastEventTime(0),
175 fDeviceName(),
176 fAudioIntBufIn(nullptr),
177 fAudioIntBufOut(nullptr),
178 fMidiIns(),
179 fMidiInEvents(),
180 fMidiOuts(),
181 fMidiOutMutex(),
182 fMidiOutVector(EngineMidiEvent::kDataSize)
184 carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
186 // just to make sure
187 pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
190 ~CarlaEngineRtAudio() override
192 CARLA_SAFE_ASSERT(fAudioInCount == 0);
193 CARLA_SAFE_ASSERT(fAudioOutCount == 0);
194 CARLA_SAFE_ASSERT(fLastEventTime == 0);
195 carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
198 // -------------------------------------
200 bool init(const char* const clientName) override
202 CARLA_SAFE_ASSERT_RETURN(fAudioInCount == 0, false);
203 CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
204 CARLA_SAFE_ASSERT_RETURN(fLastEventTime == 0, false);
205 CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
206 carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
208 if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
210 setLastError("Invalid process mode");
211 return false;
214 const bool isDummy(fAudio.getCurrentApi() == RtAudio::RtAudio::RTAUDIO_DUMMY);
215 bool deviceSet = false;
216 RtAudio::StreamParameters iParams, oParams;
218 if (isDummy)
220 if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
222 setLastError("Cannot use dummy driver in Rack mode");
223 return false;
226 fDeviceName = "Dummy";
228 else
230 const uint devCount(fAudio.getDeviceCount());
232 if (devCount == 0)
234 setLastError("No audio devices available for this driver");
235 return false;
238 if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
240 for (uint i=0; i < devCount; ++i)
242 RtAudio::DeviceInfo devInfo(fAudio.getDeviceInfo(i));
244 if (devInfo.probed && devInfo.outputChannels > 0 && devInfo.name == pData->options.audioDevice)
246 deviceSet = true;
247 fDeviceName = devInfo.name.c_str();
248 iParams.deviceId = i;
249 oParams.deviceId = i;
250 iParams.nChannels = devInfo.inputChannels;
251 oParams.nChannels = devInfo.outputChannels;
252 break;
257 if (! deviceSet)
259 iParams.deviceId = fAudio.getDefaultInputDevice();
260 oParams.deviceId = fAudio.getDefaultOutputDevice();
261 iParams.nChannels = fAudio.getDeviceInfo(iParams.deviceId).inputChannels;
262 oParams.nChannels = fAudio.getDeviceInfo(oParams.deviceId).outputChannels;
264 carla_stdout("No device set, using %i inputs and %i outputs", iParams.nChannels, oParams.nChannels);
267 if (oParams.nChannels == 0 && pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
269 setLastError("Current audio setup has no outputs, cannot continue");
270 return false;
273 iParams.nChannels = carla_fixedValue(0U, 128U, iParams.nChannels);
274 oParams.nChannels = carla_fixedValue(0U, 128U, oParams.nChannels);
275 fAudioInterleaved = fAudio.getCurrentApi() == RtAudio::UNIX_PULSE;
278 RtAudio::StreamOptions rtOptions;
279 rtOptions.flags = RTAUDIO_SCHEDULE_REALTIME;
280 #ifndef CARLA_OS_MAC
281 rtOptions.flags |= RTAUDIO_MINIMIZE_LATENCY;
282 #endif
283 rtOptions.numberOfBuffers = pData->options.audioTripleBuffer ? 3 : 2;
284 rtOptions.streamName = clientName;
285 rtOptions.priority = 85;
287 if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA && ! deviceSet)
288 rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
289 if (! fAudioInterleaved)
290 rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
292 uint bufferFrames = pData->options.audioBufferSize;
294 try {
295 fAudio.openStream(oParams.nChannels > 0 ? &oParams : nullptr,
296 iParams.nChannels > 0 ? &iParams : nullptr,
297 RTAUDIO_FLOAT32, pData->options.audioSampleRate, &bufferFrames,
298 carla_rtaudio_process_callback, this, &rtOptions,
299 carla_rtaudio_buffer_size_callback);
301 catch (const RtAudioError& e) {
302 setLastError(e.what());
303 return false;
306 if (! pData->init(clientName))
308 close();
309 setLastError("Failed to init internal data");
310 return false;
313 pData->bufferSize = bufferFrames;
314 pData->sampleRate = isDummy ? 44100.0 : fAudio.getStreamSampleRate();
315 pData->initTime(pData->options.transportExtra);
317 fAudioInCount = iParams.nChannels;
318 fAudioOutCount = oParams.nChannels;
319 fLastEventTime = 0;
321 if (fAudioInCount > 0)
322 fAudioIntBufIn = new float[fAudioInCount*bufferFrames];
324 if (fAudioOutCount > 0)
325 fAudioIntBufOut = new float[fAudioOutCount*bufferFrames];
327 pData->graph.create(fAudioInCount, fAudioOutCount, 0, 0);
329 try {
330 fAudio.startStream();
332 catch (const RtAudioError& e)
334 close();
335 setLastError(e.what());
336 return false;
339 patchbayRefresh(true, false, false);
341 if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
342 refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
344 callback(true, true,
345 ENGINE_CALLBACK_ENGINE_STARTED,
347 pData->options.processMode,
348 pData->options.transportMode,
349 static_cast<int>(pData->bufferSize),
350 static_cast<float>(pData->sampleRate),
351 getCurrentDriverName());
352 return true;
355 bool close() override
357 carla_debug("CarlaEngineRtAudio::close()");
359 bool hasError = false;
361 // stop stream first
362 if (fAudio.isStreamOpen() && fAudio.isStreamRunning())
364 try {
365 fAudio.stopStream();
367 catch (const RtAudioError& e)
369 setLastError(e.what());
370 hasError = true;
374 // clear engine data
375 CarlaEngine::close();
377 pData->graph.destroy();
379 for (LinkedList<MidiInPort>::Itenerator it = fMidiIns.begin2(); it.valid(); it.next())
381 static MidiInPort fallback = { nullptr, { '\0' } };
383 MidiInPort& inPort(it.getValue(fallback));
384 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
386 inPort.port->cancelCallback();
387 inPort.port->closePort();
388 delete inPort.port;
391 fMidiIns.clear();
392 fMidiInEvents.clear();
394 fMidiOutMutex.lock();
396 for (LinkedList<MidiOutPort>::Itenerator it = fMidiOuts.begin2(); it.valid(); it.next())
398 static MidiOutPort fallback = { nullptr, { '\0' } };
400 MidiOutPort& outPort(it.getValue(fallback));
401 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
403 outPort.port->closePort();
404 delete outPort.port;
407 fMidiOuts.clear();
408 fMidiOutMutex.unlock();
410 fAudioInCount = 0;
411 fAudioOutCount = 0;
412 fLastEventTime = 0;
413 fDeviceName.clear();
415 if (fAudioIntBufIn != nullptr)
417 delete[] fAudioIntBufIn;
418 fAudioIntBufIn = nullptr;
421 if (fAudioIntBufOut != nullptr)
423 delete[] fAudioIntBufOut;
424 fAudioIntBufOut = nullptr;
427 // close stream
428 if (fAudio.isStreamOpen())
429 fAudio.closeStream();
431 return !hasError;
434 bool hasIdleOnMainThread() const noexcept override
436 return true;
439 bool isRunning() const noexcept override
441 return fAudio.isStreamOpen();
444 bool isOffline() const noexcept override
446 return false;
449 EngineType getType() const noexcept override
451 return kEngineTypeRtAudio;
454 const char* getCurrentDriverName() const noexcept override
456 return CARLA_BACKEND_NAMESPACE::getRtAudioApiName(fAudio.getCurrentApi());
459 // -------------------------------------------------------------------
460 // Patchbay
462 template<class Graph>
463 bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
465 CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
467 char strBuf[STR_MAX+1U];
468 strBuf[STR_MAX] = '\0';
470 ExternalGraph& extGraph(graph->extGraph);
472 // ---------------------------------------------------------------
473 // clear last ports
475 extGraph.clear();
477 // ---------------------------------------------------------------
478 // fill in new ones
480 // Audio In
481 for (uint i=0; i < fAudioInCount; ++i)
483 std::snprintf(strBuf, STR_MAX, "capture_%i", i+1);
485 PortNameToId portNameToId;
486 portNameToId.setData(kExternalGraphGroupAudioIn, i+1, strBuf, "");
488 extGraph.audioPorts.ins.append(portNameToId);
491 // Audio Out
492 for (uint i=0; i < fAudioOutCount; ++i)
494 std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
496 PortNameToId portNameToId;
497 portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
499 extGraph.audioPorts.outs.append(portNameToId);
502 // MIDI In
505 RtMidiIn midiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-in");
507 for (uint i=0, count = midiIn.getPortCount(); i < count; ++i)
509 PortNameToId portNameToId;
510 portNameToId.setData(kExternalGraphGroupMidiIn, i+1, midiIn.getPortName(i).c_str(), "");
512 extGraph.midiPorts.ins.append(portNameToId);
514 } CARLA_SAFE_EXCEPTION("RtMidiIn discovery");
516 // MIDI Out
519 RtMidiOut midiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-out");
521 for (uint i=0, count = midiOut.getPortCount(); i < count; ++i)
523 PortNameToId portNameToId;
524 portNameToId.setData(kExternalGraphGroupMidiOut, i+1, midiOut.getPortName(i).c_str(), "");
526 extGraph.midiPorts.outs.append(portNameToId);
528 } CARLA_SAFE_EXCEPTION("RtMidiOut discovery");
530 // ---------------------------------------------------------------
531 // now refresh
533 if (sendHost || sendOSC)
534 graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
536 // ---------------------------------------------------------------
537 // add midi connections
539 for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
541 static const MidiInPort fallback = { nullptr, { '\0' } };
543 const MidiInPort& inPort(it.getValue(fallback));
544 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
546 const uint portId = extGraph.midiPorts.getPortIdFromName(true, inPort.name);
547 CARLA_SAFE_ASSERT_UINT_CONTINUE(portId < extGraph.midiPorts.ins.count(), portId);
549 ConnectionToId connectionToId;
550 connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupMidiIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiIn);
552 std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
554 extGraph.connections.list.append(connectionToId);
556 callback(sendHost, sendOSC,
557 ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
558 connectionToId.id,
559 0, 0, 0, 0.0f,
560 strBuf);
563 fMidiOutMutex.lock();
565 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
567 static const MidiOutPort fallback = { nullptr, { '\0' } };
569 const MidiOutPort& outPort(it.getValue(fallback));
570 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
572 const uint portId = extGraph.midiPorts.getPortIdFromName(false, outPort.name);
573 CARLA_SAFE_ASSERT_UINT_CONTINUE(portId < extGraph.midiPorts.outs.count(), portId);
575 ConnectionToId connectionToId;
576 connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiOut, kExternalGraphGroupMidiOut, portId);
578 std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
580 extGraph.connections.list.append(connectionToId);
582 callback(sendHost, sendOSC,
583 ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
584 connectionToId.id,
585 0, 0, 0, 0.0f,
586 strBuf);
589 fMidiOutMutex.unlock();
591 return true;
594 bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
596 CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
598 if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
599 return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
601 if (sendHost)
602 pData->graph.setUsingExternalHost(external);
603 if (sendOSC)
604 pData->graph.setUsingExternalOSC(external);
606 if (external)
607 return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
609 return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
612 // -------------------------------------------------------------------
614 protected:
615 void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer,
616 uint nframes, double streamTime, RtAudioStreamStatus status)
618 const PendingRtEventsRunner prt(this, nframes, true);
620 if (status & RTAUDIO_INPUT_OVERFLOW)
621 ++pData->xruns;
622 if (status & RTAUDIO_OUTPUT_UNDERFLOW)
623 ++pData->xruns;
625 // get buffers from RtAudio
626 const float* const insPtr = (const float*)inputBuffer;
627 /* */ float* const outsPtr = (float*)outputBuffer;
629 // assert rtaudio buffers
630 CARLA_SAFE_ASSERT_RETURN(outputBuffer != nullptr,);
632 // set rtaudio buffers as non-interleaved
633 const float* inBuf[fAudioInCount];
634 /* */ float* outBuf[fAudioOutCount];
636 if (fAudioInterleaved)
638 // FIXME - this looks completely wrong!
639 float* inBuf2[fAudioInCount];
641 for (uint i=0, count=fAudioInCount; i<count; ++i)
643 inBuf [i] = fAudioIntBufIn + (nframes*i);
644 inBuf2[i] = fAudioIntBufIn + (nframes*i);
646 for (uint i=0, count=fAudioOutCount; i<count; ++i)
647 outBuf[i] = fAudioIntBufOut + (nframes*i);
649 // init input
650 for (uint i=0; i<nframes; ++i)
651 for (uint j=0; j<fAudioInCount; ++j)
652 inBuf2[j][i] = insPtr[i*fAudioInCount+j];
654 // clear output
655 carla_zeroFloats(fAudioIntBufOut, fAudioOutCount*nframes);
657 else
659 for (uint i=0; i < fAudioInCount; ++i)
660 inBuf[i] = insPtr+(nframes*i);
661 for (uint i=0; i < fAudioOutCount; ++i)
662 outBuf[i] = outsPtr+(nframes*i);
664 // clear output
665 carla_zeroFloats(outsPtr, nframes*fAudioOutCount);
668 // initialize events
669 carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
670 carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
672 if (fMidiInEvents.mutex.tryLock())
674 uint32_t engineEventIndex = 0;
675 fMidiInEvents.splice();
677 for (LinkedList<RtMidiEvent>::Itenerator it = fMidiInEvents.data.begin2(); it.valid(); it.next())
679 static const RtMidiEvent fallback = { 0, 0, { 0 } };
681 const RtMidiEvent& midiEvent(it.getValue(fallback));
682 CARLA_SAFE_ASSERT_CONTINUE(midiEvent.size > 0);
684 EngineEvent& engineEvent(pData->events.in[engineEventIndex++]);
686 if (midiEvent.time < pData->timeInfo.frame)
688 engineEvent.time = 0;
690 else if (midiEvent.time >= pData->timeInfo.frame + nframes)
692 carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame);
693 engineEvent.time = static_cast<uint32_t>(pData->timeInfo.frame) + nframes - 1;
695 else
696 engineEvent.time = static_cast<uint32_t>(midiEvent.time - pData->timeInfo.frame);
698 engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data, 0);
700 if (engineEventIndex >= kMaxEngineEventInternalCount)
701 break;
704 fMidiInEvents.data.clear();
705 fMidiInEvents.mutex.unlock();
708 pData->graph.process(pData, inBuf, outBuf, nframes);
710 fMidiOutMutex.lock();
712 if (fMidiOuts.count() > 0)
714 uint8_t size = 0;
715 uint8_t mdata[3] = { 0, 0, 0 };
716 uint8_t mdataTmp[EngineMidiEvent::kDataSize];
717 const uint8_t* mdataPtr;
719 for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
721 const EngineEvent& engineEvent(pData->events.out[i]);
723 /**/ if (engineEvent.type == kEngineEventTypeNull)
725 break;
727 else if (engineEvent.type == kEngineEventTypeControl)
729 const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
731 size = ctrlEvent.convertToMidiData(engineEvent.channel, mdata);
732 mdataPtr = mdata;
734 else if (engineEvent.type == kEngineEventTypeMidi)
736 const EngineMidiEvent& midiEvent(engineEvent.midi);
738 size = midiEvent.size;
739 CARLA_SAFE_ASSERT_CONTINUE(size > 0);
741 if (size > EngineMidiEvent::kDataSize)
743 CARLA_SAFE_ASSERT_CONTINUE(midiEvent.dataExt != nullptr);
744 mdataPtr = midiEvent.dataExt;
746 else
748 // set first byte
749 mdataTmp[0] = static_cast<uint8_t>(midiEvent.data[0] | (engineEvent.channel & MIDI_CHANNEL_BIT));
751 // copy rest
752 carla_copy<uint8_t>(mdataTmp+1, midiEvent.data+1, size-1U);
754 // done
755 mdataPtr = mdataTmp;
758 else
760 continue;
763 if (size > 0)
765 fMidiOutVector.assign(mdataPtr, mdataPtr + size);
767 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
769 static MidiOutPort fallback = { nullptr, { '\0' } };
771 MidiOutPort& outPort(it.getValue(fallback));
772 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
774 outPort.port->sendMessage(&fMidiOutVector);
780 fMidiOutMutex.unlock();
782 if (fAudioInterleaved)
784 for (uint i=0; i < nframes; ++i)
785 for (uint j=0; j<fAudioOutCount; ++j)
786 outsPtr[i*fAudioOutCount+j] = outBuf[j][i];
789 return; // unused
790 (void)streamTime;
793 void handleBufferSizeCallback(const uint newBufferSize)
795 carla_stdout("bufferSize callback %u %u", pData->bufferSize, newBufferSize);
796 if (pData->bufferSize == newBufferSize)
797 return;
799 if (fAudioInCount > 0)
801 delete[] fAudioIntBufIn;
802 fAudioIntBufIn = new float[fAudioInCount*newBufferSize];
805 if (fAudioOutCount > 0)
807 delete[] fAudioIntBufOut;
808 fAudioIntBufOut = new float[fAudioOutCount*newBufferSize];
811 pData->bufferSize = newBufferSize;
812 bufferSizeChanged(newBufferSize);
815 void handleMidiCallback(double timeStamp, std::vector<uchar>* const message)
817 const size_t messageSize(message->size());
819 if (messageSize == 0 || messageSize > EngineMidiEvent::kDataSize)
820 return;
822 timeStamp /= 2;
824 if (timeStamp > 0.95)
825 timeStamp = 0.95;
826 else if (timeStamp < 0.0)
827 timeStamp = 0.0;
829 RtMidiEvent midiEvent;
830 midiEvent.time = pData->timeInfo.frame + uint64_t(timeStamp * (double)pData->bufferSize);
832 if (midiEvent.time < fLastEventTime)
833 midiEvent.time = fLastEventTime;
834 else
835 fLastEventTime = midiEvent.time;
837 midiEvent.size = static_cast<uint8_t>(messageSize);
839 size_t i=0;
840 for (; i < messageSize; ++i)
841 midiEvent.data[i] = message->at(i);
842 for (; i < EngineMidiEvent::kDataSize; ++i)
843 midiEvent.data[i] = 0;
845 fMidiInEvents.append(midiEvent);
848 // -------------------------------------------------------------------
850 bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
852 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
853 carla_debug("CarlaEngineRtAudio::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
855 switch (connectionType)
857 case kExternalGraphConnectionAudioIn1:
858 case kExternalGraphConnectionAudioIn2:
859 case kExternalGraphConnectionAudioOut1:
860 case kExternalGraphConnectionAudioOut2:
861 return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
863 case kExternalGraphConnectionMidiInput: {
864 CarlaString newRtMidiPortName;
865 newRtMidiPortName += getName();
866 newRtMidiPortName += ":";
867 newRtMidiPortName += portName;
869 RtMidiIn* rtMidiIn;
871 try {
872 rtMidiIn = new RtMidiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer(), 512);
873 } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiIn", false);
875 rtMidiIn->ignoreTypes();
876 rtMidiIn->setCallback(carla_rtmidi_callback, this);
878 bool found = false;
879 uint rtMidiPortIndex;
881 for (uint i=0, count=rtMidiIn->getPortCount(); i < count; ++i)
883 if (rtMidiIn->getPortName(i) == portName)
885 found = true;
886 rtMidiPortIndex = i;
887 break;
891 if (! found)
893 delete rtMidiIn;
894 return false;
897 try {
898 rtMidiIn->openPort(rtMidiPortIndex, portName);
900 catch(...) {
901 delete rtMidiIn;
902 return false;
905 MidiInPort midiPort;
906 midiPort.port = rtMidiIn;
908 std::strncpy(midiPort.name, portName, STR_MAX);
909 midiPort.name[STR_MAX] = '\0';
911 fMidiIns.append(midiPort);
912 return true;
913 } break;
915 case kExternalGraphConnectionMidiOutput: {
916 CarlaString newRtMidiPortName;
917 newRtMidiPortName += getName();
918 newRtMidiPortName += ":";
919 newRtMidiPortName += portName;
921 RtMidiOut* rtMidiOut;
923 try {
924 rtMidiOut = new RtMidiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer());
925 } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiOut", false);
927 bool found = false;
928 uint rtMidiPortIndex;
930 for (uint i=0, count=rtMidiOut->getPortCount(); i < count; ++i)
932 if (rtMidiOut->getPortName(i) == portName)
934 found = true;
935 rtMidiPortIndex = i;
936 break;
940 if (! found)
942 delete rtMidiOut;
943 return false;
946 try {
947 rtMidiOut->openPort(rtMidiPortIndex, portName);
949 catch(...) {
950 delete rtMidiOut;
951 return false;
954 MidiOutPort midiPort;
955 midiPort.port = rtMidiOut;
957 std::strncpy(midiPort.name, portName, STR_MAX);
958 midiPort.name[STR_MAX] = '\0';
960 const CarlaMutexLocker cml(fMidiOutMutex);
962 fMidiOuts.append(midiPort);
963 return true;
964 } break;
967 return false;
970 bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
972 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
973 carla_debug("CarlaEngineRtAudio::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
975 switch (connectionType)
977 case kExternalGraphConnectionAudioIn1:
978 case kExternalGraphConnectionAudioIn2:
979 case kExternalGraphConnectionAudioOut1:
980 case kExternalGraphConnectionAudioOut2:
981 return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
983 case kExternalGraphConnectionMidiInput:
984 for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
986 static MidiInPort fallback = { nullptr, { '\0' } };
988 MidiInPort& inPort(it.getValue(fallback));
989 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
991 if (std::strncmp(inPort.name, portName, STR_MAX) != 0)
992 continue;
994 inPort.port->cancelCallback();
995 inPort.port->closePort();
996 delete inPort.port;
998 fMidiIns.remove(it);
999 return true;
1001 break;
1003 case kExternalGraphConnectionMidiOutput: {
1004 const CarlaMutexLocker cml(fMidiOutMutex);
1006 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
1008 static MidiOutPort fallback = { nullptr, { '\0' } };
1010 MidiOutPort& outPort(it.getValue(fallback));
1011 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
1013 if (std::strncmp(outPort.name, portName, STR_MAX) != 0)
1014 continue;
1016 outPort.port->closePort();
1017 delete outPort.port;
1019 fMidiOuts.remove(it);
1020 return true;
1022 } break;
1025 return false;
1028 // -------------------------------------------------------------------
1030 private:
1031 RtAudio fAudio;
1033 // useful info
1034 bool fAudioInterleaved;
1035 uint fAudioInCount;
1036 uint fAudioOutCount;
1037 uint64_t fLastEventTime;
1039 // current device name
1040 CarlaString fDeviceName;
1042 // temp buffer for interleaved audio
1043 float* fAudioIntBufIn;
1044 float* fAudioIntBufOut;
1046 struct MidiInPort {
1047 RtMidiIn* port;
1048 char name[STR_MAX+1];
1051 struct MidiOutPort {
1052 RtMidiOut* port;
1053 char name[STR_MAX+1];
1056 struct RtMidiEvent {
1057 uint64_t time; // needs to compare to internal time
1058 uint8_t size;
1059 uint8_t data[EngineMidiEvent::kDataSize];
1062 struct RtMidiEvents {
1063 CarlaMutex mutex;
1064 RtLinkedList<RtMidiEvent>::Pool dataPool;
1065 RtLinkedList<RtMidiEvent> data;
1066 RtLinkedList<RtMidiEvent> dataPending;
1068 RtMidiEvents()
1069 : mutex(),
1070 dataPool("RtMidiEvents", 512, 512),
1071 data(dataPool),
1072 dataPending(dataPool) {}
1074 ~RtMidiEvents()
1076 clear();
1079 void append(const RtMidiEvent& event)
1081 mutex.lock();
1082 dataPending.append(event);
1083 mutex.unlock();
1086 void clear()
1088 mutex.lock();
1089 data.clear();
1090 dataPending.clear();
1091 mutex.unlock();
1094 void splice()
1096 if (dataPending.count() > 0)
1097 dataPending.moveTo(data, true /* append */);
1101 LinkedList<MidiInPort> fMidiIns;
1102 RtMidiEvents fMidiInEvents;
1104 LinkedList<MidiOutPort> fMidiOuts;
1105 CarlaMutex fMidiOutMutex;
1106 std::vector<uint8_t> fMidiOutVector;
1108 #define handlePtr ((CarlaEngineRtAudio*)userData)
1110 static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, uint nframes, double streamTime, RtAudioStreamStatus status, void* userData)
1112 handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
1113 return 0;
1116 static bool carla_rtaudio_buffer_size_callback(unsigned int bufferSize, void* userData)
1118 handlePtr->handleBufferSizeCallback(bufferSize);
1119 return true;
1122 static void carla_rtmidi_callback(double timeStamp, std::vector<uchar>* message, void* userData)
1124 handlePtr->handleMidiCallback(timeStamp, message);
1127 #undef handlePtr
1129 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
1132 // -----------------------------------------
1134 namespace EngineInit {
1136 CarlaEngine* newRtAudio(const AudioApi api)
1138 initRtAudioAPIsIfNeeded();
1140 RtAudio::Api rtApi = RtAudio::UNSPECIFIED;
1142 switch (api)
1144 case AUDIO_API_NULL:
1145 rtApi = RtAudio::RTAUDIO_DUMMY;
1146 break;
1147 case AUDIO_API_JACK:
1148 rtApi = RtAudio::UNIX_JACK;
1149 break;
1150 case AUDIO_API_OSS:
1151 rtApi = RtAudio::LINUX_OSS;
1152 break;
1153 case AUDIO_API_ALSA:
1154 rtApi = RtAudio::LINUX_ALSA;
1155 break;
1156 case AUDIO_API_PULSEAUDIO:
1157 rtApi = RtAudio::UNIX_PULSE;
1158 break;
1159 case AUDIO_API_COREAUDIO:
1160 rtApi = RtAudio::MACOSX_CORE;
1161 break;
1162 case AUDIO_API_ASIO:
1163 rtApi = RtAudio::WINDOWS_ASIO;
1164 break;
1165 case AUDIO_API_DIRECTSOUND:
1166 rtApi = RtAudio::WINDOWS_DS;
1167 break;
1168 case AUDIO_API_WASAPI:
1169 rtApi = RtAudio::WINDOWS_WASAPI;
1170 break;
1173 return new CarlaEngineRtAudio(rtApi);
1176 uint getRtAudioApiCount()
1178 initRtAudioAPIsIfNeeded();
1180 return static_cast<uint>(gRtAudioApis.size());
1183 const char* getRtAudioApiName(const uint index)
1185 initRtAudioAPIsIfNeeded();
1187 CARLA_SAFE_ASSERT_RETURN(index < gRtAudioApis.size(), nullptr);
1189 return CARLA_BACKEND_NAMESPACE::getRtAudioApiName(gRtAudioApis[index]);
1192 const char* const* getRtAudioApiDeviceNames(const uint index)
1194 initRtAudioAPIsIfNeeded();
1196 if (index >= gRtAudioApis.size())
1197 return nullptr;
1199 const RtAudio::Api& api(gRtAudioApis[index]);
1200 CarlaStringList devNames;
1202 try {
1203 RtAudio rtAudio(api);
1205 const uint devCount(rtAudio.getDeviceCount());
1207 if (devCount == 0)
1208 return nullptr;
1210 for (uint i=0; i < devCount; ++i)
1212 RtAudio::DeviceInfo devInfo(rtAudio.getDeviceInfo(i));
1214 if (devInfo.probed && devInfo.outputChannels > 0 /*&& (devInfo.nativeFormats & RTAUDIO_FLOAT32) != 0*/)
1215 devNames.append(devInfo.name.c_str());
1218 } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device names", nullptr);
1220 gDeviceNames = devNames.toCharStringListPtr();
1222 return gDeviceNames;
1225 const EngineDriverDeviceInfo* getRtAudioDeviceInfo(const uint index, const char* const deviceName)
1227 initRtAudioAPIsIfNeeded();
1229 if (index >= gRtAudioApis.size())
1230 return nullptr;
1232 static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
1233 static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
1234 static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };
1236 // reset
1237 devInfo.hints = 0x0;
1239 // cleanup
1240 if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
1242 delete[] devInfo.bufferSizes;
1243 devInfo.bufferSizes = nullptr;
1245 if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
1247 delete[] devInfo.sampleRates;
1248 devInfo.sampleRates = nullptr;
1251 const RtAudio::Api& api(gRtAudioApis[index]);
1253 if (api == RtAudio::UNIX_JACK)
1255 devInfo.bufferSizes = nullptr;
1256 devInfo.sampleRates = nullptr;
1257 return &devInfo;
1260 RtAudio::DeviceInfo rtAudioDevInfo;
1262 try {
1263 RtAudio rtAudio(api);
1265 const uint devCount(rtAudio.getDeviceCount());
1267 if (devCount == 0)
1268 return nullptr;
1270 uint i;
1271 for (i=0; i < devCount; ++i)
1273 rtAudioDevInfo = rtAudio.getDeviceInfo(i);
1275 if (rtAudioDevInfo.name == deviceName)
1276 break;
1279 if (i == devCount)
1280 rtAudioDevInfo = rtAudio.getDeviceInfo(rtAudio.getDefaultOutputDevice());
1282 } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device discovery", nullptr);
1284 // a few APIs can do triple buffer
1285 switch (api)
1287 case RtAudio::LINUX_ALSA:
1288 case RtAudio::LINUX_OSS:
1289 case RtAudio::WINDOWS_DS:
1290 devInfo.hints |= ENGINE_DRIVER_DEVICE_CAN_TRIPLE_BUFFER;
1291 break;
1292 default:
1293 break;
1296 // always use default buffer sizes
1297 devInfo.bufferSizes = dummyBufferSizes;
1299 // valid sample rates
1300 if (const size_t sampleRatesCount = rtAudioDevInfo.sampleRates.size())
1302 double* const sampleRates(new double[sampleRatesCount+1]);
1304 for (size_t i=0; i < sampleRatesCount; ++i)
1305 sampleRates[i] = rtAudioDevInfo.sampleRates[i];
1306 sampleRates[sampleRatesCount] = 0.0;
1308 devInfo.sampleRates = sampleRates;
1310 else
1312 devInfo.sampleRates = dummySampleRates;
1315 return &devInfo;
1320 // -----------------------------------------
1322 CARLA_BACKEND_END_NAMESPACE