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"
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
37 CARLA_BACKEND_START_NAMESPACE
39 // -------------------------------------------------------------------------------------------------------------------
42 static CharStringListPtr gDeviceNames
;
43 static std::vector
<RtAudio::Api
> gRtAudioApis
;
45 // -------------------------------------------------------------------------------------------------------------------
47 static void initRtAudioAPIsIfNeeded()
49 static bool needsInit
= true;
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())
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.
82 gRtAudioApis
.push_back(api
);
86 static const char* getRtAudioApiName(const RtAudio::Api api
) noexcept
90 case RtAudio::UNSPECIFIED
:
92 case RtAudio::LINUX_ALSA
:
94 case RtAudio::LINUX_OSS
:
96 case RtAudio::UNIX_PULSE
:
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";
106 return "JACK (RtAudio)";
108 case RtAudio::MACOSX_CORE
:
110 case RtAudio::WINDOWS_ASIO
:
112 case RtAudio::WINDOWS_DS
:
113 return "DirectSound";
114 case RtAudio::WINDOWS_WASAPI
:
116 case RtAudio::RTAUDIO_DUMMY
:
120 carla_stderr("CarlaBackend::getRtAudioApiName(%i) - invalid API", api
);
124 static RtMidi::Api
getMatchedAudioMidiAPI(const RtAudio::Api rtApi
) noexcept
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
;
144 return RtMidi::RTMIDI_DUMMY
;
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 // -------------------------------------------------------------------------------------------------------------------
165 class CarlaEngineRtAudio
: public CarlaEngine
168 CarlaEngineRtAudio(const RtAudio::Api api
)
171 fAudioInterleaved(false),
176 fAudioIntBufIn(nullptr),
177 fAudioIntBufOut(nullptr),
182 fMidiOutVector(EngineMidiEvent::kDataSize
)
184 carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api
);
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");
214 const bool isDummy(fAudio
.getCurrentApi() == RtAudio::RtAudio::RTAUDIO_DUMMY
);
215 bool deviceSet
= false;
216 RtAudio::StreamParameters iParams
, oParams
;
220 if (pData
->options
.processMode
== ENGINE_PROCESS_MODE_CONTINUOUS_RACK
)
222 setLastError("Cannot use dummy driver in Rack mode");
226 fDeviceName
= "Dummy";
230 const uint
devCount(fAudio
.getDeviceCount());
234 setLastError("No audio devices available for this driver");
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
)
247 fDeviceName
= devInfo
.name
.c_str();
248 iParams
.deviceId
= i
;
249 oParams
.deviceId
= i
;
250 iParams
.nChannels
= devInfo
.inputChannels
;
251 oParams
.nChannels
= devInfo
.outputChannels
;
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");
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
;
281 rtOptions
.flags
|= RTAUDIO_MINIMIZE_LATENCY
;
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
;
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());
306 if (! pData
->init(clientName
))
309 setLastError("Failed to init internal data");
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
;
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);
330 fAudio
.startStream();
332 catch (const RtAudioError
& e
)
335 setLastError(e
.what());
339 patchbayRefresh(true, false, false);
341 if (pData
->options
.processMode
== ENGINE_PROCESS_MODE_PATCHBAY
)
342 refreshExternalGraphPorts
<PatchbayGraph
>(pData
->graph
.getPatchbayGraph(), false, false);
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());
355 bool close() override
357 carla_debug("CarlaEngineRtAudio::close()");
359 bool hasError
= false;
362 if (fAudio
.isStreamOpen() && fAudio
.isStreamRunning())
367 catch (const RtAudioError
& e
)
369 setLastError(e
.what());
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();
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();
408 fMidiOutMutex
.unlock();
415 if (fAudioIntBufIn
!= nullptr)
417 delete[] fAudioIntBufIn
;
418 fAudioIntBufIn
= nullptr;
421 if (fAudioIntBufOut
!= nullptr)
423 delete[] fAudioIntBufOut
;
424 fAudioIntBufOut
= nullptr;
428 if (fAudio
.isStreamOpen())
429 fAudio
.closeStream();
434 bool hasIdleOnMainThread() const noexcept override
439 bool isRunning() const noexcept override
441 return fAudio
.isStreamOpen();
444 bool isOffline() const noexcept override
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 // -------------------------------------------------------------------
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 // ---------------------------------------------------------------
477 // ---------------------------------------------------------------
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
);
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
);
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");
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 // ---------------------------------------------------------------
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
,
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
,
589 fMidiOutMutex
.unlock();
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
);
602 pData
->graph
.setUsingExternalHost(external
);
604 pData
->graph
.setUsingExternalOSC(external
);
607 return refreshExternalGraphPorts
<PatchbayGraph
>(pData
->graph
.getPatchbayGraph(), sendHost
, sendOSC
);
609 return CarlaEngine::patchbayRefresh(sendHost
, sendOSC
, false);
612 // -------------------------------------------------------------------
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
)
622 if (status
& RTAUDIO_OUTPUT_UNDERFLOW
)
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
);
650 for (uint i
=0; i
<nframes
; ++i
)
651 for (uint j
=0; j
<fAudioInCount
; ++j
)
652 inBuf2
[j
][i
] = insPtr
[i
*fAudioInCount
+j
];
655 carla_zeroFloats(fAudioIntBufOut
, fAudioOutCount
*nframes
);
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
);
665 carla_zeroFloats(outsPtr
, nframes
*fAudioOutCount
);
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;
696 engineEvent
.time
= static_cast<uint32_t>(midiEvent
.time
- pData
->timeInfo
.frame
);
698 engineEvent
.fillFromMidiData(midiEvent
.size
, midiEvent
.data
, 0);
700 if (engineEventIndex
>= kMaxEngineEventInternalCount
)
704 fMidiInEvents
.data
.clear();
705 fMidiInEvents
.mutex
.unlock();
708 pData
->graph
.process(pData
, inBuf
, outBuf
, nframes
);
710 fMidiOutMutex
.lock();
712 if (fMidiOuts
.count() > 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
)
727 else if (engineEvent
.type
== kEngineEventTypeControl
)
729 const EngineControlEvent
& ctrlEvent(engineEvent
.ctrl
);
731 size
= ctrlEvent
.convertToMidiData(engineEvent
.channel
, 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
;
749 mdataTmp
[0] = static_cast<uint8_t>(midiEvent
.data
[0] | (engineEvent
.channel
& MIDI_CHANNEL_BIT
));
752 carla_copy
<uint8_t>(mdataTmp
+1, midiEvent
.data
+1, size
-1U);
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
];
793 void handleBufferSizeCallback(const uint newBufferSize
)
795 carla_stdout("bufferSize callback %u %u", pData
->bufferSize
, newBufferSize
);
796 if (pData
->bufferSize
== newBufferSize
)
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
)
824 if (timeStamp
> 0.95)
826 else if (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
;
835 fLastEventTime
= midiEvent
.time
;
837 midiEvent
.size
= static_cast<uint8_t>(messageSize
);
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
;
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);
879 uint rtMidiPortIndex
;
881 for (uint i
=0, count
=rtMidiIn
->getPortCount(); i
< count
; ++i
)
883 if (rtMidiIn
->getPortName(i
) == portName
)
898 rtMidiIn
->openPort(rtMidiPortIndex
, portName
);
906 midiPort
.port
= rtMidiIn
;
908 std::strncpy(midiPort
.name
, portName
, STR_MAX
);
909 midiPort
.name
[STR_MAX
] = '\0';
911 fMidiIns
.append(midiPort
);
915 case kExternalGraphConnectionMidiOutput
: {
916 CarlaString newRtMidiPortName
;
917 newRtMidiPortName
+= getName();
918 newRtMidiPortName
+= ":";
919 newRtMidiPortName
+= portName
;
921 RtMidiOut
* rtMidiOut
;
924 rtMidiOut
= new RtMidiOut(getMatchedAudioMidiAPI(fAudio
.getCurrentApi()), newRtMidiPortName
.buffer());
925 } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiOut", false);
928 uint rtMidiPortIndex
;
930 for (uint i
=0, count
=rtMidiOut
->getPortCount(); i
< count
; ++i
)
932 if (rtMidiOut
->getPortName(i
) == portName
)
947 rtMidiOut
->openPort(rtMidiPortIndex
, portName
);
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
);
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)
994 inPort
.port
->cancelCallback();
995 inPort
.port
->closePort();
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)
1016 outPort
.port
->closePort();
1017 delete outPort
.port
;
1019 fMidiOuts
.remove(it
);
1028 // -------------------------------------------------------------------
1034 bool fAudioInterleaved
;
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
;
1048 char name
[STR_MAX
+1];
1051 struct MidiOutPort
{
1053 char name
[STR_MAX
+1];
1056 struct RtMidiEvent
{
1057 uint64_t time
; // needs to compare to internal time
1059 uint8_t data
[EngineMidiEvent::kDataSize
];
1062 struct RtMidiEvents
{
1064 RtLinkedList
<RtMidiEvent
>::Pool dataPool
;
1065 RtLinkedList
<RtMidiEvent
> data
;
1066 RtLinkedList
<RtMidiEvent
> dataPending
;
1070 dataPool("RtMidiEvents", 512, 512),
1072 dataPending(dataPool
) {}
1079 void append(const RtMidiEvent
& event
)
1082 dataPending
.append(event
);
1090 dataPending
.clear();
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
);
1116 static bool carla_rtaudio_buffer_size_callback(unsigned int bufferSize
, void* userData
)
1118 handlePtr
->handleBufferSizeCallback(bufferSize
);
1122 static void carla_rtmidi_callback(double timeStamp
, std::vector
<uchar
>* message
, void* userData
)
1124 handlePtr
->handleMidiCallback(timeStamp
, message
);
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
;
1144 case AUDIO_API_NULL
:
1145 rtApi
= RtAudio::RTAUDIO_DUMMY
;
1147 case AUDIO_API_JACK
:
1148 rtApi
= RtAudio::UNIX_JACK
;
1151 rtApi
= RtAudio::LINUX_OSS
;
1153 case AUDIO_API_ALSA
:
1154 rtApi
= RtAudio::LINUX_ALSA
;
1156 case AUDIO_API_PULSEAUDIO
:
1157 rtApi
= RtAudio::UNIX_PULSE
;
1159 case AUDIO_API_COREAUDIO
:
1160 rtApi
= RtAudio::MACOSX_CORE
;
1162 case AUDIO_API_ASIO
:
1163 rtApi
= RtAudio::WINDOWS_ASIO
;
1165 case AUDIO_API_DIRECTSOUND
:
1166 rtApi
= RtAudio::WINDOWS_DS
;
1168 case AUDIO_API_WASAPI
:
1169 rtApi
= RtAudio::WINDOWS_WASAPI
;
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())
1199 const RtAudio::Api
& api(gRtAudioApis
[index
]);
1200 CarlaStringList devNames
;
1203 RtAudio
rtAudio(api
);
1205 const uint
devCount(rtAudio
.getDeviceCount());
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())
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 };
1237 devInfo
.hints
= 0x0;
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;
1260 RtAudio::DeviceInfo rtAudioDevInfo
;
1263 RtAudio
rtAudio(api
);
1265 const uint
devCount(rtAudio
.getDeviceCount());
1271 for (i
=0; i
< devCount
; ++i
)
1273 rtAudioDevInfo
= rtAudio
.getDeviceInfo(i
);
1275 if (rtAudioDevInfo
.name
== deviceName
)
1280 rtAudioDevInfo
= rtAudio
.getDeviceInfo(rtAudio
.getDefaultOutputDevice());
1282 } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device discovery", nullptr);
1284 // a few APIs can do triple buffer
1287 case RtAudio::LINUX_ALSA
:
1288 case RtAudio::LINUX_OSS
:
1289 case RtAudio::WINDOWS_DS
:
1290 devInfo
.hints
|= ENGINE_DRIVER_DEVICE_CAN_TRIPLE_BUFFER
;
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
;
1312 devInfo
.sampleRates
= dummySampleRates
;
1320 // -----------------------------------------
1322 CARLA_BACKEND_END_NAMESPACE