1 /*
2 * Carla Plugin Host
3 * Copyright (C) 2011-2020 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 doc/GPL.txt file.
16 */
17
18 #include "CarlaEngineGraph.hpp"
19 #include "CarlaEngineInit.hpp"
20 #include "CarlaEngineInternal.hpp"
21 #include "CarlaBackendUtils.hpp"
22 #include "CarlaMathUtils.hpp"
23 #include "CarlaStringList.hpp"
24
25 #include "RtLinkedList.hpp"
26
27 #include "jackbridge/JackBridge.hpp"
28
29 #if defined(__clang__)
30 # pragma clang diagnostic push
31 # pragma clang diagnostic ignored "-Wconversion"
32 # pragma clang diagnostic ignored "-Wdeprecated-copy"
33 # pragma clang diagnostic ignored "-Weffc++"
34 # pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
35 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
36 # pragma GCC diagnostic push
37 # pragma GCC diagnostic ignored "-Wconversion"
38 # pragma GCC diagnostic ignored "-Weffc++"
39 # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
40 #endif
41
42 #include "rtaudio/RtAudio.h"
43 #include "rtmidi/RtMidi.h"
44
45 #if defined(__clang__)
46 # pragma clang diagnostic pop
47 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
48 # pragma GCC diagnostic pop
49 #endif
50
51 CARLA_BACKEND_START_NAMESPACE
52
53 // -------------------------------------------------------------------------------------------------------------------
54 // Global static data
55
56 static CharStringListPtr gDeviceNames;
57 static std::vector<RtAudio::Api> gRtAudioApis;
58
59 // -------------------------------------------------------------------------------------------------------------------
60
initRtAudioAPIsIfNeeded()61 static void initRtAudioAPIsIfNeeded()
62 {
63 static bool needsInit = true;
64
65 if (! needsInit)
66 return;
67
68 needsInit = false;
69
70 // get APIs in a local var, and pass wanted ones into gRtAudioApis
71
72 std::vector<RtAudio::Api> apis;
73 RtAudio::getCompiledApi(apis);
74
75 for (std::vector<RtAudio::Api>::const_iterator it = apis.begin(), end=apis.end(); it != end; ++it)
76 {
77 const RtAudio::Api& api(*it);
78
79 if (api == RtAudio::UNIX_JACK)
80 {
81 #if defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
82 if ( ! jackbridge_is_ok())
83 continue;
84 #else
85 /* NOTE
86 * RtMidi doesn't have a native MIDI backend for these OSes,
87 * Using RtAudio JACK funcitonality is only useful when we need to access the native MIDI APIs.
88 * (JACK audio + ALSA MIDI, or JACK audio + CoreMidi, or JACK audio + Windows MIDI)
89 * Because RtMidi has no native MIDI support outside of win/mac/linux, we skip these RtAudio APIs.
90 * Those OSes can use Carla's JACK support directly, which is much better than RtAudio classes.
91 */
92 continue;
93 #endif
94 }
95
96 gRtAudioApis.push_back(api);
97 }
98 }
99
getRtAudioApiName(const RtAudio::Api api)100 static const char* getRtAudioApiName(const RtAudio::Api api) noexcept
101 {
102 switch (api)
103 {
104 case RtAudio::UNSPECIFIED:
105 return "Unspecified";
106 case RtAudio::LINUX_ALSA:
107 return "ALSA";
108 case RtAudio::LINUX_OSS:
109 return "OSS";
110 case RtAudio::UNIX_PULSE:
111 return "PulseAudio";
112 case RtAudio::UNIX_JACK:
113 #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
114 return "JACK with ALSA-MIDI";
115 #elif defined(CARLA_OS_MAC)
116 return "JACK with CoreMidi";
117 #elif defined(CARLA_OS_WIN)
118 return "JACK with WinMM";
119 #else
120 return "JACK (RtAudio)";
121 #endif
122 case RtAudio::MACOSX_CORE:
123 return "CoreAudio";
124 case RtAudio::WINDOWS_ASIO:
125 return "ASIO";
126 case RtAudio::WINDOWS_DS:
127 return "DirectSound";
128 case RtAudio::WINDOWS_WASAPI:
129 return "WASAPI";
130 case RtAudio::RTAUDIO_DUMMY:
131 return "Dummy";
132 }
133
134 carla_stderr("CarlaBackend::getRtAudioApiName(%i) - invalid API", api);
135 return nullptr;
136 }
137
getMatchedAudioMidiAPI(const RtAudio::Api rtApi)138 static RtMidi::Api getMatchedAudioMidiAPI(const RtAudio::Api rtApi) noexcept
139 {
140 switch (rtApi)
141 {
142 case RtAudio::UNSPECIFIED:
143 return RtMidi::UNSPECIFIED;
144
145 case RtAudio::LINUX_ALSA:
146 case RtAudio::LINUX_OSS:
147 return RtMidi::LINUX_ALSA;
148
149 case RtAudio::UNIX_PULSE:
150 case RtAudio::UNIX_JACK:
151 #if defined(CARLA_OS_LINUX) && defined(__LINUX_ALSA__)
152 return RtMidi::LINUX_ALSA;
153 #elif defined(CARLA_OS_MAC)
154 return RtMidi::MACOSX_CORE;
155 #elif defined(CARLA_OS_WIN)
156 return RtMidi::WINDOWS_MM;
157 #else
158 return RtMidi::RTMIDI_DUMMY;
159 #endif
160
161 case RtAudio::MACOSX_CORE:
162 return RtMidi::MACOSX_CORE;
163
164 case RtAudio::WINDOWS_ASIO:
165 case RtAudio::WINDOWS_DS:
166 case RtAudio::WINDOWS_WASAPI:
167 return RtMidi::WINDOWS_MM;
168
169 case RtAudio::RTAUDIO_DUMMY:
170 return RtMidi::RTMIDI_DUMMY;
171 }
172
173 return RtMidi::UNSPECIFIED;
174 }
175
176 // -------------------------------------------------------------------------------------------------------------------
177 // RtAudio Engine
178
179 class CarlaEngineRtAudio : public CarlaEngine
180 {
181 public:
CarlaEngineRtAudio(const RtAudio::Api api)182 CarlaEngineRtAudio(const RtAudio::Api api)
183 : CarlaEngine(),
184 fAudio(api),
185 fAudioInterleaved(false),
186 fAudioInCount(0),
187 fAudioOutCount(0),
188 fLastEventTime(0),
189 fDeviceName(),
190 fAudioIntBufIn(nullptr),
191 fAudioIntBufOut(nullptr),
192 fMidiIns(),
193 fMidiInEvents(),
194 fMidiOuts(),
195 fMidiOutMutex(),
196 fMidiOutVector(EngineMidiEvent::kDataSize)
197 {
198 carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
199
200 // just to make sure
201 pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
202 }
203
~CarlaEngineRtAudio()204 ~CarlaEngineRtAudio() override
205 {
206 CARLA_SAFE_ASSERT(fAudioInCount == 0);
207 CARLA_SAFE_ASSERT(fAudioOutCount == 0);
208 CARLA_SAFE_ASSERT(fLastEventTime == 0);
209 carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
210 }
211
212 // -------------------------------------
213
init(const char * const clientName)214 bool init(const char* const clientName) override
215 {
216 CARLA_SAFE_ASSERT_RETURN(fAudioInCount == 0, false);
217 CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
218 CARLA_SAFE_ASSERT_RETURN(fLastEventTime == 0, false);
219 CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
220 carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
221
222 if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
223 {
224 setLastError("Invalid process mode");
225 return false;
226 }
227
228 const bool isDummy(fAudio.getCurrentApi() == RtAudio::RtAudio::RTAUDIO_DUMMY);
229 bool deviceSet = false;
230 RtAudio::StreamParameters iParams, oParams;
231
232 if (isDummy)
233 {
234 if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
235 {
236 setLastError("Cannot use dummy driver in Rack mode");
237 return false;
238 }
239
240 fDeviceName = "Dummy";
241 }
242 else
243 {
244 const uint devCount(fAudio.getDeviceCount());
245
246 if (devCount == 0)
247 {
248 setLastError("No audio devices available for this driver");
249 return false;
250 }
251
252 if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
253 {
254 for (uint i=0; i < devCount; ++i)
255 {
256 RtAudio::DeviceInfo devInfo(fAudio.getDeviceInfo(i));
257
258 if (devInfo.probed && devInfo.outputChannels > 0 && devInfo.name == pData->options.audioDevice)
259 {
260 deviceSet = true;
261 fDeviceName = devInfo.name.c_str();
262 iParams.deviceId = i;
263 oParams.deviceId = i;
264 iParams.nChannels = devInfo.inputChannels;
265 oParams.nChannels = devInfo.outputChannels;
266 break;
267 }
268 }
269 }
270
271 if (! deviceSet)
272 {
273 iParams.deviceId = fAudio.getDefaultInputDevice();
274 oParams.deviceId = fAudio.getDefaultOutputDevice();
275 iParams.nChannels = fAudio.getDeviceInfo(iParams.deviceId).inputChannels;
276 oParams.nChannels = fAudio.getDeviceInfo(oParams.deviceId).outputChannels;
277
278 carla_stdout("No device set, using %i inputs and %i outputs", iParams.nChannels, oParams.nChannels);
279 }
280
281 if (oParams.nChannels == 0 && pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
282 {
283 setLastError("Current audio setup has no outputs, cannot continue");
284 return false;
285 }
286
287 iParams.nChannels = carla_fixedValue(0U, 128U, iParams.nChannels);
288 oParams.nChannels = carla_fixedValue(0U, 128U, oParams.nChannels);
289 fAudioInterleaved = fAudio.getCurrentApi() == RtAudio::UNIX_PULSE;
290 }
291
292 RtAudio::StreamOptions rtOptions;
293 rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_SCHEDULE_REALTIME;
294 rtOptions.numberOfBuffers = pData->options.audioTripleBuffer ? 3 : 2;
295 rtOptions.streamName = clientName;
296 rtOptions.priority = 85;
297
298 if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA && ! deviceSet)
299 rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
300 if (! fAudioInterleaved)
301 rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
302
303 uint bufferFrames = pData->options.audioBufferSize;
304
305 try {
306 fAudio.openStream(oParams.nChannels > 0 ? &oParams : nullptr,
307 iParams.nChannels > 0 ? &iParams : nullptr,
308 RTAUDIO_FLOAT32, pData->options.audioSampleRate, &bufferFrames,
309 carla_rtaudio_process_callback, this, &rtOptions,
310 carla_rtaudio_buffer_size_callback);
311 }
312 catch (const RtAudioError& e) {
313 setLastError(e.what());
314 return false;
315 }
316
317 if (! pData->init(clientName))
318 {
319 close();
320 setLastError("Failed to init internal data");
321 return false;
322 }
323
324 pData->bufferSize = bufferFrames;
325 pData->sampleRate = isDummy ? 44100.0 : fAudio.getStreamSampleRate();
326 pData->initTime(pData->options.transportExtra);
327
328 fAudioInCount = iParams.nChannels;
329 fAudioOutCount = oParams.nChannels;
330 fLastEventTime = 0;
331
332 if (fAudioInCount > 0)
333 fAudioIntBufIn = new float[fAudioInCount*bufferFrames];
334
335 if (fAudioOutCount > 0)
336 fAudioIntBufOut = new float[fAudioOutCount*bufferFrames];
337
338 pData->graph.create(fAudioInCount, fAudioOutCount, 0, 0);
339
340 try {
341 fAudio.startStream();
342 }
343 catch (const RtAudioError& e)
344 {
345 close();
346 setLastError(e.what());
347 return false;
348 }
349
350 patchbayRefresh(true, false, false);
351
352 if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
353 refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
354
355 callback(true, true,
356 ENGINE_CALLBACK_ENGINE_STARTED,
357 0,
358 pData->options.processMode,
359 pData->options.transportMode,
360 static_cast<int>(pData->bufferSize),
361 static_cast<float>(pData->sampleRate),
362 getCurrentDriverName());
363 return true;
364 }
365
close()366 bool close() override
367 {
368 carla_debug("CarlaEngineRtAudio::close()");
369
370 bool hasError = false;
371
372 // stop stream first
373 if (fAudio.isStreamOpen() && fAudio.isStreamRunning())
374 {
375 try {
376 fAudio.stopStream();
377 }
378 catch (const RtAudioError& e)
379 {
380 setLastError(e.what());
381 hasError = true;
382 }
383 }
384
385 // clear engine data
386 CarlaEngine::close();
387
388 pData->graph.destroy();
389
390 for (LinkedList<MidiInPort>::Itenerator it = fMidiIns.begin2(); it.valid(); it.next())
391 {
392 static MidiInPort fallback = { nullptr, { '\0' } };
393
394 MidiInPort& inPort(it.getValue(fallback));
395 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
396
397 inPort.port->cancelCallback();
398 inPort.port->closePort();
399 delete inPort.port;
400 }
401
402 fMidiIns.clear();
403 fMidiInEvents.clear();
404
405 fMidiOutMutex.lock();
406
407 for (LinkedList<MidiOutPort>::Itenerator it = fMidiOuts.begin2(); it.valid(); it.next())
408 {
409 static MidiOutPort fallback = { nullptr, { '\0' } };
410
411 MidiOutPort& outPort(it.getValue(fallback));
412 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
413
414 outPort.port->closePort();
415 delete outPort.port;
416 }
417
418 fMidiOuts.clear();
419 fMidiOutMutex.unlock();
420
421 fAudioInCount = 0;
422 fAudioOutCount = 0;
423 fLastEventTime = 0;
424 fDeviceName.clear();
425
426 if (fAudioIntBufIn != nullptr)
427 {
428 delete[] fAudioIntBufIn;
429 fAudioIntBufIn = nullptr;
430 }
431
432 if (fAudioIntBufOut != nullptr)
433 {
434 delete[] fAudioIntBufOut;
435 fAudioIntBufOut = nullptr;
436 }
437
438 // close stream
439 if (fAudio.isStreamOpen())
440 fAudio.closeStream();
441
442 return !hasError;
443 }
444
isRunning() const445 bool isRunning() const noexcept override
446 {
447 return fAudio.isStreamOpen();
448 }
449
isOffline() const450 bool isOffline() const noexcept override
451 {
452 return false;
453 }
454
getType() const455 EngineType getType() const noexcept override
456 {
457 return kEngineTypeRtAudio;
458 }
459
getCurrentDriverName() const460 const char* getCurrentDriverName() const noexcept override
461 {
462 return CarlaBackend::getRtAudioApiName(fAudio.getCurrentApi());
463 }
464
465 // -------------------------------------------------------------------
466 // Patchbay
467
468 template<class Graph>
refreshExternalGraphPorts(Graph * const graph,const bool sendHost,const bool sendOSC)469 bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
470 {
471 CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
472
473 char strBuf[STR_MAX+1U];
474 strBuf[STR_MAX] = '\0';
475
476 ExternalGraph& extGraph(graph->extGraph);
477
478 // ---------------------------------------------------------------
479 // clear last ports
480
481 extGraph.clear();
482
483 // ---------------------------------------------------------------
484 // fill in new ones
485
486 // Audio In
487 for (uint i=0; i < fAudioInCount; ++i)
488 {
489 std::snprintf(strBuf, STR_MAX, "capture_%i", i+1);
490
491 PortNameToId portNameToId;
492 portNameToId.setData(kExternalGraphGroupAudioIn, i+1, strBuf, "");
493
494 extGraph.audioPorts.ins.append(portNameToId);
495 }
496
497 // Audio Out
498 for (uint i=0; i < fAudioOutCount; ++i)
499 {
500 std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
501
502 PortNameToId portNameToId;
503 portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
504
505 extGraph.audioPorts.outs.append(portNameToId);
506 }
507
508 // MIDI In
509 try
510 {
511 RtMidiIn midiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-in");
512
513 for (uint i=0, count = midiIn.getPortCount(); i < count; ++i)
514 {
515 PortNameToId portNameToId;
516 portNameToId.setData(kExternalGraphGroupMidiIn, i+1, midiIn.getPortName(i).c_str(), "");
517
518 extGraph.midiPorts.ins.append(portNameToId);
519 }
520 } CARLA_SAFE_EXCEPTION("RtMidiIn discovery");
521
522 // MIDI Out
523 try
524 {
525 RtMidiOut midiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), "carla-discovery-out");
526
527 for (uint i=0, count = midiOut.getPortCount(); i < count; ++i)
528 {
529 PortNameToId portNameToId;
530 portNameToId.setData(kExternalGraphGroupMidiOut, i+1, midiOut.getPortName(i).c_str(), "");
531
532 extGraph.midiPorts.outs.append(portNameToId);
533 }
534 } CARLA_SAFE_EXCEPTION("RtMidiOut discovery");
535
536 // ---------------------------------------------------------------
537 // now refresh
538
539 if (sendHost || sendOSC)
540 graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
541
542 // ---------------------------------------------------------------
543 // add midi connections
544
545 for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
546 {
547 static const MidiInPort fallback = { nullptr, { '\0' } };
548
549 const MidiInPort& inPort(it.getValue(fallback));
550 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
551
552 const uint portId(extGraph.midiPorts.getPortId(true, inPort.name));
553 CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.ins.count());
554
555 ConnectionToId connectionToId;
556 connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupMidiIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiIn);
557
558 std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
559
560 extGraph.connections.list.append(connectionToId);
561
562 callback(sendHost, sendOSC,
563 ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
564 connectionToId.id,
565 0, 0, 0, 0.0f,
566 strBuf);
567 }
568
569 fMidiOutMutex.lock();
570
571 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
572 {
573 static const MidiOutPort fallback = { nullptr, { '\0' } };
574
575 const MidiOutPort& outPort(it.getValue(fallback));
576 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
577
578 const uint portId(extGraph.midiPorts.getPortId(false, outPort.name));
579 CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.outs.count());
580
581 ConnectionToId connectionToId;
582 connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiOut, kExternalGraphGroupMidiOut, portId);
583
584 std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
585
586 extGraph.connections.list.append(connectionToId);
587
588 callback(sendHost, sendOSC,
589 ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED,
590 connectionToId.id,
591 0, 0, 0, 0.0f,
592 strBuf);
593 }
594
595 fMidiOutMutex.unlock();
596
597 return true;
598 }
599
patchbayRefresh(const bool sendHost,const bool sendOSC,const bool external)600 bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
601 {
602 CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
603
604 if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
605 return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
606
607 if (sendHost)
608 pData->graph.setUsingExternalHost(external);
609 if (sendOSC)
610 pData->graph.setUsingExternalOSC(external);
611
612 if (external)
613 return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
614
615 return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
616 }
617
618 // -------------------------------------------------------------------
619
620 protected:
handleAudioProcessCallback(void * outputBuffer,void * inputBuffer,uint nframes,double streamTime,RtAudioStreamStatus status)621 void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer,
622 uint nframes, double streamTime, RtAudioStreamStatus status)
623 {
624 const PendingRtEventsRunner prt(this, nframes, true);
625
626 if (status & RTAUDIO_INPUT_OVERFLOW)
627 ++pData->xruns;
628 if (status & RTAUDIO_OUTPUT_UNDERFLOW)
629 ++pData->xruns;
630
631 // get buffers from RtAudio
632 const float* const insPtr = (const float*)inputBuffer;
633 /* */ float* const outsPtr = (float*)outputBuffer;
634
635 // assert rtaudio buffers
636 CARLA_SAFE_ASSERT_RETURN(outputBuffer != nullptr,);
637
638 // set rtaudio buffers as non-interleaved
639 const float* inBuf[fAudioInCount];
640 /* */ float* outBuf[fAudioOutCount];
641
642 if (fAudioInterleaved)
643 {
644 // FIXME - this looks completely wrong!
645 float* inBuf2[fAudioInCount];
646
647 for (uint i=0, count=fAudioInCount; i<count; ++i)
648 {
649 inBuf [i] = fAudioIntBufIn + (nframes*i);
650 inBuf2[i] = fAudioIntBufIn + (nframes*i);
651 }
652 for (uint i=0, count=fAudioOutCount; i<count; ++i)
653 outBuf[i] = fAudioIntBufOut + (nframes*i);
654
655 // init input
656 for (uint i=0; i<nframes; ++i)
657 for (uint j=0; j<fAudioInCount; ++j)
658 inBuf2[j][i] = insPtr[i*fAudioInCount+j];
659
660 // clear output
661 carla_zeroFloats(fAudioIntBufOut, fAudioOutCount*nframes);
662 }
663 else
664 {
665 for (uint i=0; i < fAudioInCount; ++i)
666 inBuf[i] = insPtr+(nframes*i);
667 for (uint i=0; i < fAudioOutCount; ++i)
668 outBuf[i] = outsPtr+(nframes*i);
669
670 // clear output
671 carla_zeroFloats(outsPtr, nframes*fAudioOutCount);
672 }
673
674 // initialize events
675 carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
676 carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
677
678 if (fMidiInEvents.mutex.tryLock())
679 {
680 uint32_t engineEventIndex = 0;
681 fMidiInEvents.splice();
682
683 for (LinkedList<RtMidiEvent>::Itenerator it = fMidiInEvents.data.begin2(); it.valid(); it.next())
684 {
685 static const RtMidiEvent fallback = { 0, 0, { 0 } };
686
687 const RtMidiEvent& midiEvent(it.getValue(fallback));
688 CARLA_SAFE_ASSERT_CONTINUE(midiEvent.size > 0);
689
690 EngineEvent& engineEvent(pData->events.in[engineEventIndex++]);
691
692 if (midiEvent.time < pData->timeInfo.frame)
693 {
694 engineEvent.time = 0;
695 }
696 else if (midiEvent.time >= pData->timeInfo.frame + nframes)
697 {
698 carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame);
699 engineEvent.time = static_cast<uint32_t>(pData->timeInfo.frame) + nframes - 1;
700 }
701 else
702 engineEvent.time = static_cast<uint32_t>(midiEvent.time - pData->timeInfo.frame);
703
704 engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data, 0);
705
706 if (engineEventIndex >= kMaxEngineEventInternalCount)
707 break;
708 }
709
710 fMidiInEvents.data.clear();
711 fMidiInEvents.mutex.unlock();
712 }
713
714 pData->graph.process(pData, inBuf, outBuf, nframes);
715
716 fMidiOutMutex.lock();
717
718 if (fMidiOuts.count() > 0)
719 {
720 uint8_t size = 0;
721 uint8_t mdata[3] = { 0, 0, 0 };
722 uint8_t mdataTmp[EngineMidiEvent::kDataSize];
723 const uint8_t* mdataPtr;
724
725 for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
726 {
727 const EngineEvent& engineEvent(pData->events.out[i]);
728
729 /**/ if (engineEvent.type == kEngineEventTypeNull)
730 {
731 break;
732 }
733 else if (engineEvent.type == kEngineEventTypeControl)
734 {
735 const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
736
737 size = ctrlEvent.convertToMidiData(engineEvent.channel, mdata);
738 mdataPtr = mdata;
739 }
740 else if (engineEvent.type == kEngineEventTypeMidi)
741 {
742 const EngineMidiEvent& midiEvent(engineEvent.midi);
743
744 size = midiEvent.size;
745 CARLA_SAFE_ASSERT_CONTINUE(size > 0);
746
747 if (size > EngineMidiEvent::kDataSize)
748 {
749 CARLA_SAFE_ASSERT_CONTINUE(midiEvent.dataExt != nullptr);
750 mdataPtr = midiEvent.dataExt;
751 }
752 else
753 {
754 // set first byte
755 mdataTmp[0] = static_cast<uint8_t>(midiEvent.data[0] | (engineEvent.channel & MIDI_CHANNEL_BIT));
756
757 // copy rest
758 carla_copy<uint8_t>(mdataTmp+1, midiEvent.data+1, size-1U);
759
760 // done
761 mdataPtr = mdataTmp;
762 }
763 }
764 else
765 {
766 continue;
767 }
768
769 if (size > 0)
770 {
771 fMidiOutVector.assign(mdataPtr, mdataPtr + size);
772
773 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
774 {
775 static MidiOutPort fallback = { nullptr, { '\0' } };
776
777 MidiOutPort& outPort(it.getValue(fallback));
778 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
779
780 outPort.port->sendMessage(&fMidiOutVector);
781 }
782 }
783 }
784 }
785
786 fMidiOutMutex.unlock();
787
788 if (fAudioInterleaved)
789 {
790 for (uint i=0; i < nframes; ++i)
791 for (uint j=0; j<fAudioOutCount; ++j)
792 outsPtr[i*fAudioOutCount+j] = outBuf[j][i];
793 }
794
795 return; // unused
796 (void)streamTime;
797 }
798
handleBufferSizeCallback(const uint newBufferSize)799 void handleBufferSizeCallback(const uint newBufferSize)
800 {
801 carla_stdout("bufferSize callback %u %u", pData->bufferSize, newBufferSize);
802 if (pData->bufferSize == newBufferSize)
803 return;
804
805 if (fAudioInCount > 0)
806 {
807 delete[] fAudioIntBufIn;
808 fAudioIntBufIn = new float[fAudioInCount*newBufferSize];
809 }
810
811 if (fAudioOutCount > 0)
812 {
813 delete[] fAudioIntBufOut;
814 fAudioIntBufOut = new float[fAudioOutCount*newBufferSize];
815 }
816
817 pData->bufferSize = newBufferSize;
818 bufferSizeChanged(newBufferSize);
819 }
820
handleMidiCallback(double timeStamp,std::vector<uchar> * const message)821 void handleMidiCallback(double timeStamp, std::vector<uchar>* const message)
822 {
823 const size_t messageSize(message->size());
824
825 if (messageSize == 0 || messageSize > EngineMidiEvent::kDataSize)
826 return;
827
828 timeStamp /= 2;
829
830 if (timeStamp > 0.95)
831 timeStamp = 0.95;
832 else if (timeStamp < 0.0)
833 timeStamp = 0.0;
834
835 RtMidiEvent midiEvent;
836 midiEvent.time = pData->timeInfo.frame + uint64_t(timeStamp * (double)pData->bufferSize);
837
838 if (midiEvent.time < fLastEventTime)
839 midiEvent.time = fLastEventTime;
840 else
841 fLastEventTime = midiEvent.time;
842
843 midiEvent.size = static_cast<uint8_t>(messageSize);
844
845 size_t i=0;
846 for (; i < messageSize; ++i)
847 midiEvent.data[i] = message->at(i);
848 for (; i < EngineMidiEvent::kDataSize; ++i)
849 midiEvent.data[i] = 0;
850
851 fMidiInEvents.append(midiEvent);
852 }
853
854 // -------------------------------------------------------------------
855
connectExternalGraphPort(const uint connectionType,const uint portId,const char * const portName)856 bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
857 {
858 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
859 carla_debug("CarlaEngineRtAudio::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
860
861 switch (connectionType)
862 {
863 case kExternalGraphConnectionAudioIn1:
864 case kExternalGraphConnectionAudioIn2:
865 case kExternalGraphConnectionAudioOut1:
866 case kExternalGraphConnectionAudioOut2:
867 return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
868
869 case kExternalGraphConnectionMidiInput: {
870 CarlaString newRtMidiPortName;
871 newRtMidiPortName += getName();
872 newRtMidiPortName += ":";
873 newRtMidiPortName += portName;
874
875 RtMidiIn* rtMidiIn;
876
877 try {
878 rtMidiIn = new RtMidiIn(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer(), 512);
879 } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiIn", false);
880
881 rtMidiIn->ignoreTypes();
882 rtMidiIn->setCallback(carla_rtmidi_callback, this);
883
884 bool found = false;
885 uint rtMidiPortIndex;
886
887 for (uint i=0, count=rtMidiIn->getPortCount(); i < count; ++i)
888 {
889 if (rtMidiIn->getPortName(i) == portName)
890 {
891 found = true;
892 rtMidiPortIndex = i;
893 break;
894 }
895 }
896
897 if (! found)
898 {
899 delete rtMidiIn;
900 return false;
901 }
902
903 try {
904 rtMidiIn->openPort(rtMidiPortIndex, portName);
905 }
906 catch(...) {
907 delete rtMidiIn;
908 return false;
909 };
910
911 MidiInPort midiPort;
912 midiPort.port = rtMidiIn;
913
914 std::strncpy(midiPort.name, portName, STR_MAX);
915 midiPort.name[STR_MAX] = '\0';
916
917 fMidiIns.append(midiPort);
918 return true;
919 } break;
920
921 case kExternalGraphConnectionMidiOutput: {
922 CarlaString newRtMidiPortName;
923 newRtMidiPortName += getName();
924 newRtMidiPortName += ":";
925 newRtMidiPortName += portName;
926
927 RtMidiOut* rtMidiOut;
928
929 try {
930 rtMidiOut = new RtMidiOut(getMatchedAudioMidiAPI(fAudio.getCurrentApi()), newRtMidiPortName.buffer());
931 } CARLA_SAFE_EXCEPTION_RETURN("new RtMidiOut", false);
932
933 bool found = false;
934 uint rtMidiPortIndex;
935
936 for (uint i=0, count=rtMidiOut->getPortCount(); i < count; ++i)
937 {
938 if (rtMidiOut->getPortName(i) == portName)
939 {
940 found = true;
941 rtMidiPortIndex = i;
942 break;
943 }
944 }
945
946 if (! found)
947 {
948 delete rtMidiOut;
949 return false;
950 }
951
952 try {
953 rtMidiOut->openPort(rtMidiPortIndex, portName);
954 }
955 catch(...) {
956 delete rtMidiOut;
957 return false;
958 };
959
960 MidiOutPort midiPort;
961 midiPort.port = rtMidiOut;
962
963 std::strncpy(midiPort.name, portName, STR_MAX);
964 midiPort.name[STR_MAX] = '\0';
965
966 const CarlaMutexLocker cml(fMidiOutMutex);
967
968 fMidiOuts.append(midiPort);
969 return true;
970 } break;
971 }
972
973 return false;
974 }
975
disconnectExternalGraphPort(const uint connectionType,const uint portId,const char * const portName)976 bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
977 {
978 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
979 carla_debug("CarlaEngineRtAudio::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
980
981 switch (connectionType)
982 {
983 case kExternalGraphConnectionAudioIn1:
984 case kExternalGraphConnectionAudioIn2:
985 case kExternalGraphConnectionAudioOut1:
986 case kExternalGraphConnectionAudioOut2:
987 return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
988
989 case kExternalGraphConnectionMidiInput:
990 for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
991 {
992 static MidiInPort fallback = { nullptr, { '\0' } };
993
994 MidiInPort& inPort(it.getValue(fallback));
995 CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);
996
997 if (std::strncmp(inPort.name, portName, STR_MAX) != 0)
998 continue;
999
1000 inPort.port->cancelCallback();
1001 inPort.port->closePort();
1002 delete inPort.port;
1003
1004 fMidiIns.remove(it);
1005 return true;
1006 }
1007 break;
1008
1009 case kExternalGraphConnectionMidiOutput: {
1010 const CarlaMutexLocker cml(fMidiOutMutex);
1011
1012 for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
1013 {
1014 static MidiOutPort fallback = { nullptr, { '\0' } };
1015
1016 MidiOutPort& outPort(it.getValue(fallback));
1017 CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);
1018
1019 if (std::strncmp(outPort.name, portName, STR_MAX) != 0)
1020 continue;
1021
1022 outPort.port->closePort();
1023 delete outPort.port;
1024
1025 fMidiOuts.remove(it);
1026 return true;
1027 }
1028 } break;
1029 }
1030
1031 return false;
1032 }
1033
1034 // -------------------------------------------------------------------
1035
1036 private:
1037 RtAudio fAudio;
1038
1039 // useful info
1040 bool fAudioInterleaved;
1041 uint fAudioInCount;
1042 uint fAudioOutCount;
1043 uint64_t fLastEventTime;
1044
1045 // current device name
1046 CarlaString fDeviceName;
1047
1048 // temp buffer for interleaved audio
1049 float* fAudioIntBufIn;
1050 float* fAudioIntBufOut;
1051
1052 struct MidiInPort {
1053 RtMidiIn* port;
1054 char name[STR_MAX+1];
1055 };
1056
1057 struct MidiOutPort {
1058 RtMidiOut* port;
1059 char name[STR_MAX+1];
1060 };
1061
1062 struct RtMidiEvent {
1063 uint64_t time; // needs to compare to internal time
1064 uint8_t size;
1065 uint8_t data[EngineMidiEvent::kDataSize];
1066 };
1067
1068 struct RtMidiEvents {
1069 CarlaMutex mutex;
1070 RtLinkedList<RtMidiEvent>::Pool dataPool;
1071 RtLinkedList<RtMidiEvent> data;
1072 RtLinkedList<RtMidiEvent> dataPending;
1073
RtMidiEventsCarlaEngineRtAudio::RtMidiEvents1074 RtMidiEvents()
1075 : mutex(),
1076 dataPool("RtMidiEvents", 512, 512),
1077 data(dataPool),
1078 dataPending(dataPool) {}
1079
~RtMidiEventsCarlaEngineRtAudio::RtMidiEvents1080 ~RtMidiEvents()
1081 {
1082 clear();
1083 }
1084
appendCarlaEngineRtAudio::RtMidiEvents1085 void append(const RtMidiEvent& event)
1086 {
1087 mutex.lock();
1088 dataPending.append(event);
1089 mutex.unlock();
1090 }
1091
clearCarlaEngineRtAudio::RtMidiEvents1092 void clear()
1093 {
1094 mutex.lock();
1095 data.clear();
1096 dataPending.clear();
1097 mutex.unlock();
1098 }
1099
spliceCarlaEngineRtAudio::RtMidiEvents1100 void splice()
1101 {
1102 if (dataPending.count() > 0)
1103 dataPending.moveTo(data, true /* append */);
1104 }
1105 };
1106
1107 LinkedList<MidiInPort> fMidiIns;
1108 RtMidiEvents fMidiInEvents;
1109
1110 LinkedList<MidiOutPort> fMidiOuts;
1111 CarlaMutex fMidiOutMutex;
1112 std::vector<uint8_t> fMidiOutVector;
1113
1114 #define handlePtr ((CarlaEngineRtAudio*)userData)
1115
carla_rtaudio_process_callback(void * outputBuffer,void * inputBuffer,uint nframes,double streamTime,RtAudioStreamStatus status,void * userData)1116 static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, uint nframes, double streamTime, RtAudioStreamStatus status, void* userData)
1117 {
1118 handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
1119 return 0;
1120 }
1121
carla_rtaudio_buffer_size_callback(unsigned int bufferSize,void * userData)1122 static bool carla_rtaudio_buffer_size_callback(unsigned int bufferSize, void* userData)
1123 {
1124 handlePtr->handleBufferSizeCallback(bufferSize);
1125 return true;
1126 }
1127
carla_rtmidi_callback(double timeStamp,std::vector<uchar> * message,void * userData)1128 static void carla_rtmidi_callback(double timeStamp, std::vector<uchar>* message, void* userData)
1129 {
1130 handlePtr->handleMidiCallback(timeStamp, message);
1131 }
1132
1133 #undef handlePtr
1134
1135 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
1136 };
1137
1138 // -----------------------------------------
1139
1140 namespace EngineInit {
1141
newRtAudio(const AudioApi api)1142 CarlaEngine* newRtAudio(const AudioApi api)
1143 {
1144 initRtAudioAPIsIfNeeded();
1145
1146 RtAudio::Api rtApi = RtAudio::UNSPECIFIED;
1147
1148 switch (api)
1149 {
1150 case AUDIO_API_NULL:
1151 rtApi = RtAudio::RTAUDIO_DUMMY;
1152 break;
1153 case AUDIO_API_JACK:
1154 rtApi = RtAudio::UNIX_JACK;
1155 break;
1156 case AUDIO_API_OSS:
1157 rtApi = RtAudio::LINUX_OSS;
1158 break;
1159 case AUDIO_API_ALSA:
1160 rtApi = RtAudio::LINUX_ALSA;
1161 break;
1162 case AUDIO_API_PULSEAUDIO:
1163 rtApi = RtAudio::UNIX_PULSE;
1164 break;
1165 case AUDIO_API_COREAUDIO:
1166 rtApi = RtAudio::MACOSX_CORE;
1167 break;
1168 case AUDIO_API_ASIO:
1169 rtApi = RtAudio::WINDOWS_ASIO;
1170 break;
1171 case AUDIO_API_DIRECTSOUND:
1172 rtApi = RtAudio::WINDOWS_DS;
1173 break;
1174 case AUDIO_API_WASAPI:
1175 rtApi = RtAudio::WINDOWS_WASAPI;
1176 break;
1177 }
1178
1179 return new CarlaEngineRtAudio(rtApi);
1180 }
1181
getRtAudioApiCount()1182 uint getRtAudioApiCount()
1183 {
1184 initRtAudioAPIsIfNeeded();
1185
1186 return static_cast<uint>(gRtAudioApis.size());
1187 }
1188
getRtAudioApiName(const uint index)1189 const char* getRtAudioApiName(const uint index)
1190 {
1191 initRtAudioAPIsIfNeeded();
1192
1193 CARLA_SAFE_ASSERT_RETURN(index < gRtAudioApis.size(), nullptr);
1194
1195 return CarlaBackend::getRtAudioApiName(gRtAudioApis[index]);
1196 }
1197
getRtAudioApiDeviceNames(const uint index)1198 const char* const* getRtAudioApiDeviceNames(const uint index)
1199 {
1200 initRtAudioAPIsIfNeeded();
1201
1202 if (index >= gRtAudioApis.size())
1203 return nullptr;
1204
1205 const RtAudio::Api& api(gRtAudioApis[index]);
1206 CarlaStringList devNames;
1207
1208 try {
1209 RtAudio rtAudio(api);
1210
1211 const uint devCount(rtAudio.getDeviceCount());
1212
1213 if (devCount == 0)
1214 return nullptr;
1215
1216 for (uint i=0; i < devCount; ++i)
1217 {
1218 RtAudio::DeviceInfo devInfo(rtAudio.getDeviceInfo(i));
1219
1220 if (devInfo.probed && devInfo.outputChannels > 0 /*&& (devInfo.nativeFormats & RTAUDIO_FLOAT32) != 0*/)
1221 devNames.append(devInfo.name.c_str());
1222 }
1223
1224 } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device names", nullptr);
1225
1226 gDeviceNames = devNames.toCharStringListPtr();
1227
1228 return gDeviceNames;
1229 }
1230
getRtAudioDeviceInfo(const uint index,const char * const deviceName)1231 const EngineDriverDeviceInfo* getRtAudioDeviceInfo(const uint index, const char* const deviceName)
1232 {
1233 initRtAudioAPIsIfNeeded();
1234
1235 if (index >= gRtAudioApis.size())
1236 return nullptr;
1237
1238 static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
1239 static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
1240 static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };
1241
1242 // reset
1243 devInfo.hints = 0x0;
1244
1245 // cleanup
1246 if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
1247 {
1248 delete[] devInfo.bufferSizes;
1249 devInfo.bufferSizes = nullptr;
1250 }
1251 if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
1252 {
1253 delete[] devInfo.sampleRates;
1254 devInfo.sampleRates = nullptr;
1255 }
1256
1257 const RtAudio::Api& api(gRtAudioApis[index]);
1258
1259 if (api == RtAudio::UNIX_JACK)
1260 {
1261 devInfo.bufferSizes = nullptr;
1262 devInfo.sampleRates = nullptr;
1263 return &devInfo;
1264 }
1265
1266 RtAudio::DeviceInfo rtAudioDevInfo;
1267
1268 try {
1269 RtAudio rtAudio(api);
1270
1271 const uint devCount(rtAudio.getDeviceCount());
1272
1273 if (devCount == 0)
1274 return nullptr;
1275
1276 uint i;
1277 for (i=0; i < devCount; ++i)
1278 {
1279 rtAudioDevInfo = rtAudio.getDeviceInfo(i);
1280
1281 if (rtAudioDevInfo.name == deviceName)
1282 break;
1283 }
1284
1285 if (i == devCount)
1286 rtAudioDevInfo = rtAudio.getDeviceInfo(rtAudio.getDefaultOutputDevice());
1287
1288 } CARLA_SAFE_EXCEPTION_RETURN("RtAudio device discovery", nullptr);
1289
1290 // a few APIs can do triple buffer
1291 switch (api)
1292 {
1293 case RtAudio::LINUX_ALSA:
1294 case RtAudio::LINUX_OSS:
1295 case RtAudio::WINDOWS_DS:
1296 devInfo.hints |= ENGINE_DRIVER_DEVICE_CAN_TRIPLE_BUFFER;
1297 break;
1298 default:
1299 break;
1300 }
1301
1302 // always use default buffer sizes
1303 devInfo.bufferSizes = dummyBufferSizes;
1304
1305 // valid sample rates
1306 if (const size_t sampleRatesCount = rtAudioDevInfo.sampleRates.size())
1307 {
1308 double* const sampleRates(new double[sampleRatesCount+1]);
1309
1310 for (size_t i=0; i < sampleRatesCount; ++i)
1311 sampleRates[i] = rtAudioDevInfo.sampleRates[i];
1312 sampleRates[sampleRatesCount] = 0.0;
1313
1314 devInfo.sampleRates = sampleRates;
1315 }
1316 else
1317 {
1318 devInfo.sampleRates = dummySampleRates;
1319 }
1320
1321 return &devInfo;
1322 }
1323
1324 }
1325
1326 // -----------------------------------------
1327
1328 CARLA_BACKEND_END_NAMESPACE
1329