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 "CarlaEngineInternal.hpp"
19 #include "CarlaPlugin.hpp"
20 #include "CarlaSemUtils.hpp"
21 
22 #include "jackbridge/JackBridge.hpp"
23 
24 #include <ctime>
25 #include <sys/time.h>
26 
27 CARLA_BACKEND_START_NAMESPACE
28 
29 // -----------------------------------------------------------------------
30 // Engine Internal helper macro, sets lastError and returns false/NULL
31 
32 #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(cond, err)  if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return false;   }
33 #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERRN(cond, err) if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return nullptr; }
34 
35 // -----------------------------------------------------------------------
36 // InternalEvents
37 
EngineInternalEvents()38 EngineInternalEvents::EngineInternalEvents() noexcept
39     : in(nullptr),
40       out(nullptr) {}
41 
~EngineInternalEvents()42 EngineInternalEvents::~EngineInternalEvents() noexcept
43 {
44     CARLA_SAFE_ASSERT(in == nullptr);
45     CARLA_SAFE_ASSERT(out == nullptr);
46 }
47 
clear()48 void EngineInternalEvents::clear() noexcept
49 {
50     if (in != nullptr)
51     {
52         delete[] in;
53         in = nullptr;
54     }
55 
56     if (out != nullptr)
57     {
58         delete[] out;
59         out = nullptr;
60     }
61 }
62 
63 // -----------------------------------------------------------------------
64 // InternalTime
65 
66 static const double kTicksPerBeat = 1920.0;
67 
68 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
calculate_link_latency(const double bufferSize,const double sampleRate)69 static uint32_t calculate_link_latency(const double bufferSize, const double sampleRate) noexcept
70 {
71     CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate), 0);
72 
73     const long long int latency = llround(1.0e6 * bufferSize / sampleRate);
74     CARLA_SAFE_ASSERT_RETURN(latency >= 0 && latency < UINT32_MAX, 0);
75 
76     return static_cast<uint32_t>(latency);
77 }
78 #endif
79 
EngineInternalTime(EngineTimeInfo & ti,const EngineTransportMode & tm)80 EngineInternalTime::EngineInternalTime(EngineTimeInfo& ti, const EngineTransportMode& tm) noexcept
81     : beatsPerBar(4.0),
82       beatsPerMinute(120.0),
83       bufferSize(0.0),
84       sampleRate(0.0),
85       needsReset(false),
86       nextFrame(0),
87 #ifndef BUILD_BRIDGE
88       hylia(),
89 #endif
90       timeInfo(ti),
91       transportMode(tm) {}
92 
init(const uint32_t bsize,const double srate)93 void EngineInternalTime::init(const uint32_t bsize, const double srate)
94 {
95     bufferSize = bsize;
96     sampleRate = srate;
97 
98 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
99     if (hylia.instance != nullptr)
100     {
101         hylia_set_beats_per_bar(hylia.instance, beatsPerBar);
102         hylia_set_beats_per_minute(hylia.instance, beatsPerMinute);
103         hylia_set_output_latency(hylia.instance, calculate_link_latency(bsize, srate));
104 
105         if (hylia.enabled)
106             hylia_enable(hylia.instance, true);
107     }
108 #endif
109 
110     needsReset = true;
111 }
112 
updateAudioValues(const uint32_t bsize,const double srate)113 void EngineInternalTime::updateAudioValues(const uint32_t bsize, const double srate)
114 {
115     bufferSize = bsize;
116     sampleRate = srate;
117 
118 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
119     if (hylia.instance != nullptr)
120         hylia_set_output_latency(hylia.instance, calculate_link_latency(bsize, srate));
121 #endif
122 
123     needsReset = true;
124 }
125 
enableLink(const bool enable)126 void EngineInternalTime::enableLink(const bool enable)
127 {
128 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
129     if (hylia.enabled == enable)
130         return;
131 
132     if (hylia.instance != nullptr)
133     {
134         hylia.enabled = enable;
135         hylia_enable(hylia.instance, enable);
136     }
137 #else
138     // unused
139     (void)enable;
140 #endif
141 
142     needsReset = true;
143 }
144 
setBPM(const double bpm)145 void EngineInternalTime::setBPM(const double bpm)
146 {
147     beatsPerMinute = bpm;
148 
149 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
150     if (hylia.instance != nullptr)
151         hylia_set_beats_per_minute(hylia.instance, bpm);
152 #endif
153 }
154 
setNeedsReset()155 void EngineInternalTime::setNeedsReset() noexcept
156 {
157     needsReset = true;
158 }
159 
pause()160 void EngineInternalTime::pause() noexcept
161 {
162     timeInfo.playing = false;
163     nextFrame = timeInfo.frame;
164     needsReset = true;
165 }
166 
relocate(const uint64_t frame)167 void EngineInternalTime::relocate(const uint64_t frame) noexcept
168 {
169     timeInfo.frame = frame;
170     nextFrame = frame;
171     needsReset = true;
172 }
173 
fillEngineTimeInfo(const uint32_t newFrames)174 void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept
175 {
176     CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate),);
177     CARLA_SAFE_ASSERT_RETURN(newFrames > 0,);
178 
179     double ticktmp;
180 
181     if (transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
182     {
183         timeInfo.usecs = 0;
184         timeInfo.frame = nextFrame;
185     }
186 
187     if (needsReset)
188     {
189         timeInfo.bbt.valid = true;
190         timeInfo.bbt.beatType = 4.0f;
191         timeInfo.bbt.ticksPerBeat = kTicksPerBeat;
192 
193         double abs_beat, abs_tick;
194 
195 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
196         if (hylia.enabled)
197         {
198             if (hylia.timeInfo.beat >= 0.0)
199             {
200                 abs_beat = hylia.timeInfo.beat;
201                 abs_tick = abs_beat * kTicksPerBeat;
202             }
203             else
204             {
205                 abs_beat = 0.0;
206                 abs_tick = 0.0;
207                 timeInfo.playing = false;
208             }
209         }
210         else
211 #endif
212         {
213             const double min = static_cast<double>(timeInfo.frame) / (sampleRate * 60.0);
214             abs_beat = min * beatsPerMinute;
215             abs_tick = abs_beat * kTicksPerBeat;
216             needsReset = false;
217         }
218 
219         const double bar  = std::floor(abs_beat / beatsPerBar);
220         const double beat = std::floor(std::fmod(abs_beat, beatsPerBar));
221 
222         timeInfo.bbt.bar  = static_cast<int32_t>(bar) + 1;
223         timeInfo.bbt.beat = static_cast<int32_t>(beat) + 1;
224         timeInfo.bbt.barStartTick = ((bar * beatsPerBar) + beat) * kTicksPerBeat;
225 
226         ticktmp = abs_tick - timeInfo.bbt.barStartTick;
227     }
228     else if (timeInfo.playing)
229     {
230         ticktmp = timeInfo.bbt.tick + (newFrames * kTicksPerBeat * beatsPerMinute / (sampleRate * 60));
231 
232         while (ticktmp >= kTicksPerBeat)
233         {
234             ticktmp -= kTicksPerBeat;
235 
236             if (++timeInfo.bbt.beat > beatsPerBar)
237             {
238                 ++timeInfo.bbt.bar;
239                 timeInfo.bbt.beat = 1;
240                 timeInfo.bbt.barStartTick += beatsPerBar * kTicksPerBeat;
241             }
242         }
243     }
244     else
245     {
246         ticktmp = timeInfo.bbt.tick;
247     }
248 
249     timeInfo.bbt.beatsPerBar = static_cast<float>(beatsPerBar);
250     timeInfo.bbt.beatsPerMinute = beatsPerMinute;
251     timeInfo.bbt.tick = ticktmp;
252 
253     if (transportMode == ENGINE_TRANSPORT_MODE_INTERNAL && timeInfo.playing)
254         nextFrame += newFrames;
255 }
256 
fillJackTimeInfo(jack_position_t * const pos,const uint32_t newFrames)257 void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint32_t newFrames) noexcept
258 {
259     CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate),);
260     CARLA_SAFE_ASSERT_RETURN(newFrames > 0,);
261     CARLA_SAFE_ASSERT(transportMode == ENGINE_TRANSPORT_MODE_JACK);
262 
263     fillEngineTimeInfo(newFrames);
264 
265     pos->bar              = timeInfo.bbt.bar;
266     pos->beat             = timeInfo.bbt.beat;
267     pos->tick             = static_cast<int32_t>(timeInfo.bbt.tick + 0.5);
268     pos->bar_start_tick   = timeInfo.bbt.barStartTick;
269     pos->beats_per_bar    = timeInfo.bbt.beatsPerBar;
270     pos->beat_type        = timeInfo.bbt.beatType;
271     pos->ticks_per_beat   = kTicksPerBeat;
272     pos->beats_per_minute = beatsPerMinute;
273 #ifdef JACK_TICK_DOUBLE
274     pos->tick_double      = timeInfo.bbt.tick;
275     pos->valid            = static_cast<jack_position_bits_t>(JackPositionBBT|JackTickDouble);
276 #else
277     pos->valid            = JackPositionBBT;
278 #endif
279 }
280 
preProcess(const uint32_t numFrames)281 void EngineInternalTime::preProcess(const uint32_t numFrames)
282 {
283 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
284     if (hylia.enabled)
285     {
286         hylia_process(hylia.instance, numFrames, &hylia.timeInfo);
287 
288         const double new_bpb = hylia.timeInfo.beatsPerBar;
289         const double new_bpm = hylia.timeInfo.beatsPerMinute;
290 
291         if (new_bpb >= 1.0 && carla_isNotEqual(beatsPerBar, new_bpb))
292         {
293             beatsPerBar = new_bpb;
294             needsReset = true;
295         }
296         if (new_bpm > 0.0 && carla_isNotEqual(beatsPerMinute, new_bpm))
297         {
298             beatsPerMinute = new_bpm;
299             needsReset = true;
300         }
301     }
302 #endif
303 
304     if (transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
305         fillEngineTimeInfo(numFrames);
306 }
307 
308 // -----------------------------------------------------------------------
309 // EngineInternalTime::Hylia
310 
311 #ifndef BUILD_BRIDGE
Hylia()312 EngineInternalTime::Hylia::Hylia()
313     : enabled(false),
314       instance(nullptr),
315       timeInfo()
316 {
317     carla_zeroStruct(timeInfo);
318 
319 # ifdef HAVE_HYLIA
320     instance = hylia_create();
321 # endif
322 }
323 
~Hylia()324 EngineInternalTime::Hylia::~Hylia()
325 {
326 # ifdef HAVE_HYLIA
327     hylia_cleanup(instance);
328 # endif
329 }
330 #endif
331 
332 // -----------------------------------------------------------------------
333 // NextAction
334 
EngineNextAction()335 EngineNextAction::EngineNextAction() noexcept
336     : opcode(kEnginePostActionNull),
337       pluginId(0),
338       value(0),
339       mutex(),
340       needsPost(false),
341       postDone(false),
342       sem(carla_sem_create(false)) {}
343 
~EngineNextAction()344 EngineNextAction::~EngineNextAction() noexcept
345 {
346     CARLA_SAFE_ASSERT(opcode == kEnginePostActionNull);
347 
348     if (sem != nullptr)
349     {
350         carla_sem_destroy(sem);
351         sem = nullptr;
352     }
353 }
354 
clearAndReset()355 void EngineNextAction::clearAndReset() noexcept
356 {
357     mutex.lock();
358     CARLA_SAFE_ASSERT(opcode == kEnginePostActionNull);
359 
360     opcode    = kEnginePostActionNull;
361     pluginId  = 0;
362     value     = 0;
363     needsPost = false;
364     postDone  = false;
365     mutex.unlock();
366 }
367 
368 // -----------------------------------------------------------------------
369 // Helper functions
370 
getInternalEventBuffer(const bool isInput) const371 EngineEvent* CarlaEngine::getInternalEventBuffer(const bool isInput) const noexcept
372 {
373     return isInput ? pData->events.in : pData->events.out;
374 }
375 
376 // -----------------------------------------------------------------------
377 // CarlaEngine::ProtectedData
378 
ProtectedData(CarlaEngine * const engine)379 CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine)
380     : thread(engine),
381 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
382       osc(engine),
383 #endif
384       callback(nullptr),
385       callbackPtr(nullptr),
386       fileCallback(nullptr),
387       fileCallbackPtr(nullptr),
388       actionCanceled(false),
389 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
390       loadingProject(false),
391       ignoreClientPrefix(false),
392       currentProjectFilename(),
393       currentProjectFolder(),
394 #endif
395       bufferSize(0),
396       sampleRate(0.0),
397       aboutToClose(false),
398       isIdling(0),
399       curPluginCount(0),
400       maxPluginNumber(0),
401       nextPluginId(0),
402       envMutex(),
403       lastError(),
404       name(),
405       options(),
406       timeInfo(),
407 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
408       plugins(nullptr),
409       xruns(0),
410       dspLoad(0.0f),
411 #endif
412       pluginsToDelete(),
413       events(),
414 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
415       graph(engine),
416 #endif
417       time(timeInfo, options.transportMode),
418       nextAction()
419 {
420 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
421     plugins[0].plugin = nullptr;
422     carla_zeroStructs(plugins[0].peaks, 1);
423 #endif
424 }
425 
~ProtectedData()426 CarlaEngine::ProtectedData::~ProtectedData()
427 {
428     CARLA_SAFE_ASSERT(curPluginCount == 0);
429     CARLA_SAFE_ASSERT(maxPluginNumber == 0);
430     CARLA_SAFE_ASSERT(nextPluginId == 0);
431     CARLA_SAFE_ASSERT(isIdling == 0);
432 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
433     CARLA_SAFE_ASSERT(plugins == nullptr);
434 #endif
435 
436     if (pluginsToDelete.size() != 0)
437     {
438         for (std::vector<CarlaPluginPtr>::iterator it = pluginsToDelete.begin(); it != pluginsToDelete.end(); ++it)
439         {
440             carla_stderr2("Plugin not yet deleted, name: '%s', usage count: '%u'",
441                           (*it)->getName(), it->use_count());
442         }
443     }
444 
445     pluginsToDelete.clear();
446 }
447 
448 // -----------------------------------------------------------------------
449 
init(const char * const clientName)450 bool CarlaEngine::ProtectedData::init(const char* const clientName)
451 {
452     CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(name.isEmpty(), "Invalid engine internal data (err #1)");
453     CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.in  == nullptr, "Invalid engine internal data (err #4)");
454     CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.out == nullptr, "Invalid engine internal data (err #5)");
455     CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(clientName != nullptr && clientName[0] != '\0', "Invalid client name");
456 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
457     CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(plugins == nullptr, "Invalid engine internal data (err #3)");
458 #endif
459 
460     aboutToClose   = false;
461     curPluginCount = 0;
462     nextPluginId   = 0;
463 
464     switch (options.processMode)
465     {
466     case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
467         maxPluginNumber = MAX_RACK_PLUGINS;
468         options.forceStereo = true;
469         break;
470     case ENGINE_PROCESS_MODE_PATCHBAY:
471         maxPluginNumber = MAX_PATCHBAY_PLUGINS;
472         break;
473     case ENGINE_PROCESS_MODE_BRIDGE:
474         maxPluginNumber = 1;
475         break;
476     default:
477         maxPluginNumber = MAX_DEFAULT_PLUGINS;
478         break;
479     }
480 
481     switch (options.processMode)
482     {
483     case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
484     case ENGINE_PROCESS_MODE_PATCHBAY:
485     case ENGINE_PROCESS_MODE_BRIDGE:
486         events.in  = new EngineEvent[kMaxEngineEventInternalCount];
487         events.out = new EngineEvent[kMaxEngineEventInternalCount];
488         carla_zeroStructs(events.in,  kMaxEngineEventInternalCount);
489         carla_zeroStructs(events.out, kMaxEngineEventInternalCount);
490         break;
491     default:
492         break;
493     }
494 
495     nextPluginId = maxPluginNumber;
496 
497     name = clientName;
498     name.toBasic();
499 
500     timeInfo.clear();
501 
502 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
503     if (options.oscEnabled)
504         osc.init(clientName, options.oscPortTCP, options.oscPortUDP);
505 #endif
506 
507 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
508     plugins = new EnginePluginData[maxPluginNumber];
509     xruns = 0;
510     dspLoad = 0.0f;
511 #endif
512 
513     nextAction.clearAndReset();
514     thread.startThread();
515 
516     return true;
517 }
518 
close()519 void CarlaEngine::ProtectedData::close()
520 {
521     CARLA_SAFE_ASSERT(name.isNotEmpty());
522 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
523     CARLA_SAFE_ASSERT(plugins != nullptr);
524     CARLA_SAFE_ASSERT(nextPluginId == maxPluginNumber);
525 #endif
526 
527     aboutToClose = true;
528 
529     thread.stopThread(500);
530     nextAction.clearAndReset();
531 
532 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
533     osc.close();
534 #endif
535 
536     aboutToClose    = false;
537     curPluginCount  = 0;
538     maxPluginNumber = 0;
539     nextPluginId    = 0;
540 
541     deletePluginsAsNeeded();
542 
543 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
544     if (plugins != nullptr)
545     {
546         delete[] plugins;
547         plugins = nullptr;
548     }
549 #endif
550 
551     events.clear();
552     name.clear();
553 }
554 
initTime(const char * const features)555 void CarlaEngine::ProtectedData::initTime(const char* const features)
556 {
557     time.init(bufferSize, sampleRate);
558 
559 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
560     const bool linkEnabled = features != nullptr && std::strstr(features, ":link:") != nullptr;
561     time.enableLink(linkEnabled);
562 #else
563     return;
564 
565     // unused
566     (void)features;
567 #endif
568 }
569 
570 // -----------------------------------------------------------------------
571 
deletePluginsAsNeeded()572 void CarlaEngine::ProtectedData::deletePluginsAsNeeded()
573 {
574     for (bool stop;;)
575     {
576         stop = true;
577 
578         for (std::vector<CarlaPluginPtr>::iterator it = pluginsToDelete.begin(); it != pluginsToDelete.end(); ++it)
579         {
580             if (it->use_count() == 1)
581             {
582                 stop = false;
583                 pluginsToDelete.erase(it);
584                 break;
585             }
586         }
587 
588         if (stop)
589             break;
590     }
591 }
592 
593 // -----------------------------------------------------------------------
594 
595 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
doPluginRemove(const uint pluginId)596 void CarlaEngine::ProtectedData::doPluginRemove(const uint pluginId) noexcept
597 {
598     CARLA_SAFE_ASSERT_RETURN(curPluginCount > 0,);
599     CARLA_SAFE_ASSERT_RETURN(pluginId < curPluginCount,);
600     --curPluginCount;
601 
602     // move all plugins 1 spot backwards
603     for (uint i=pluginId; i < curPluginCount; ++i)
604     {
605         const CarlaPluginPtr plugin = plugins[i+1].plugin;
606         CARLA_SAFE_ASSERT_BREAK(plugin.get() != nullptr);
607 
608         plugin->setId(i);
609 
610         plugins[i].plugin = plugin;
611         carla_zeroStruct(plugins[i].peaks);
612     }
613 
614     const uint id = curPluginCount;
615 
616     // reset last plugin (now removed)
617     plugins[id].plugin.reset();
618     carla_zeroFloats(plugins[id].peaks, 4);
619 }
620 
doPluginsSwitch(const uint idA,const uint idB)621 void CarlaEngine::ProtectedData::doPluginsSwitch(const uint idA, const uint idB) noexcept
622 {
623     CARLA_SAFE_ASSERT_RETURN(curPluginCount >= 2,);
624 
625     CARLA_SAFE_ASSERT_RETURN(idA < curPluginCount,);
626     CARLA_SAFE_ASSERT_RETURN(idB < curPluginCount,);
627 
628     const CarlaPluginPtr pluginA = plugins[idA].plugin;
629     CARLA_SAFE_ASSERT_RETURN(pluginA.get() != nullptr,);
630 
631     const CarlaPluginPtr pluginB = plugins[idB].plugin;
632     CARLA_SAFE_ASSERT_RETURN(pluginB.get() != nullptr,);
633 
634     pluginA->setId(idB);
635     plugins[idA].plugin = pluginB;
636 
637     pluginB->setId(idA);
638     plugins[idB].plugin = pluginA;
639 }
640 #endif
641 
doNextPluginAction()642 void CarlaEngine::ProtectedData::doNextPluginAction() noexcept
643 {
644     if (! nextAction.mutex.tryLock())
645         return;
646 
647     const EnginePostAction opcode    = nextAction.opcode;
648     const bool             needsPost = nextAction.needsPost;
649 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
650     const uint             pluginId  = nextAction.pluginId;
651     const uint             value     = nextAction.value;
652 #endif
653 
654     nextAction.opcode    = kEnginePostActionNull;
655     nextAction.pluginId  = 0;
656     nextAction.value     = 0;
657     nextAction.needsPost = false;
658 
659     nextAction.mutex.unlock();
660 
661     switch (opcode)
662     {
663     case kEnginePostActionNull:
664         break;
665     case kEnginePostActionZeroCount:
666         curPluginCount = 0;
667         break;
668 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
669     case kEnginePostActionRemovePlugin:
670         doPluginRemove(pluginId);
671         break;
672     case kEnginePostActionSwitchPlugins:
673         doPluginsSwitch(pluginId, value);
674         break;
675 #endif
676     }
677 
678     if (needsPost)
679     {
680         if (nextAction.sem != nullptr)
681             carla_sem_post(*nextAction.sem);
682         nextAction.postDone = true;
683     }
684 }
685 
686 // -----------------------------------------------------------------------
687 // PendingRtEventsRunner
688 
getTimeInMicroseconds()689 static int64_t getTimeInMicroseconds() noexcept
690 {
691 #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
692     struct timeval tv;
693     gettimeofday(&tv, nullptr);
694 
695     return (tv.tv_sec * 1000000) + tv.tv_usec;
696 #else
697     struct timespec ts;
698 # ifdef CLOCK_MONOTONIC_RAW
699     clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
700 # else
701     clock_gettime(CLOCK_MONOTONIC, &ts);
702 # endif
703 
704     return (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
705 #endif
706 }
707 
PendingRtEventsRunner(CarlaEngine * const engine,const uint32_t frames,const bool calcDSPLoad)708 PendingRtEventsRunner::PendingRtEventsRunner(CarlaEngine* const engine,
709                                              const uint32_t frames,
710                                              const bool calcDSPLoad) noexcept
711     : pData(engine->pData),
712       prevTime(calcDSPLoad ? getTimeInMicroseconds() : 0)
713 {
714     pData->time.preProcess(frames);
715 }
716 
~PendingRtEventsRunner()717 PendingRtEventsRunner::~PendingRtEventsRunner() noexcept
718 {
719     pData->doNextPluginAction();
720 
721 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
722     if (prevTime > 0)
723     {
724         const int64_t newTime = getTimeInMicroseconds();
725 
726         if (newTime < prevTime)
727             return;
728 
729         const double timeDiff = static_cast<double>(newTime - prevTime) / 1000000.0;
730         const double maxTime = pData->bufferSize / pData->sampleRate;
731         const float dspLoad = static_cast<float>(timeDiff / maxTime) * 100.0f;
732 
733         if (dspLoad > pData->dspLoad)
734             pData->dspLoad = std::min(100.0f, dspLoad);
735         else
736             pData->dspLoad *= static_cast<float>(1.0 - maxTime) + 1e-12f;
737     }
738 #endif
739 }
740 
741 // -----------------------------------------------------------------------
742 // ScopedActionLock
743 
ScopedActionLock(CarlaEngine * const engine,const EnginePostAction action,const uint pluginId,const uint value)744 ScopedActionLock::ScopedActionLock(CarlaEngine* const engine,
745                                    const EnginePostAction action,
746                                    const uint pluginId,
747                                    const uint value) noexcept
748     : pData(engine->pData)
749 {
750     CARLA_SAFE_ASSERT_RETURN(action != kEnginePostActionNull,);
751 
752     {
753         const CarlaMutexLocker cml(pData->nextAction.mutex);
754 
755         CARLA_SAFE_ASSERT_RETURN(pData->nextAction.opcode == kEnginePostActionNull,);
756 
757         pData->nextAction.opcode    = action;
758         pData->nextAction.pluginId  = pluginId;
759         pData->nextAction.value     = value;
760         pData->nextAction.needsPost = engine->isRunning();
761         pData->nextAction.postDone  = false;
762     }
763 
764    #ifdef BUILD_BRIDGE
765     #define ACTION_MSG_PREFIX "Bridge: "
766    #else
767     #define ACTION_MSG_PREFIX ""
768    #endif
769 
770     if (pData->nextAction.needsPost)
771     {
772        #if defined(DEBUG) || defined(BUILD_BRIDGE)
773         // block wait for unlock on processing side
774         carla_stdout(ACTION_MSG_PREFIX "ScopedPluginAction(%i) - blocking START", pluginId);
775        #endif
776 
777         bool engineStoppedWhileWaiting = false;
778 
779         if (! pData->nextAction.postDone)
780         {
781             for (int i = 10; --i >= 0;)
782             {
783                 if (pData->nextAction.sem != nullptr)
784                 {
785                     if (carla_sem_timedwait(*pData->nextAction.sem, 200))
786                         break;
787                 }
788                 else
789                 {
790                     carla_msleep(200);
791                 }
792 
793                 if (! engine->isRunning())
794                 {
795                     engineStoppedWhileWaiting = true;
796                     break;
797                 }
798             }
799         }
800 
801        #if defined(DEBUG) || defined(BUILD_BRIDGE)
802         carla_stdout(ACTION_MSG_PREFIX "ScopedPluginAction(%i) - blocking DONE", pluginId);
803        #endif
804 
805         // check if anything went wrong...
806         if (! pData->nextAction.postDone)
807         {
808             bool needsCorrection = false;
809 
810             {
811                 const CarlaMutexLocker cml(pData->nextAction.mutex);
812 
813                 if (pData->nextAction.opcode != kEnginePostActionNull)
814                 {
815                     needsCorrection = true;
816                     pData->nextAction.needsPost = false;
817                 }
818             }
819 
820             if (needsCorrection)
821             {
822                 pData->doNextPluginAction();
823 
824                 if (! engineStoppedWhileWaiting)
825                     carla_stderr2(ACTION_MSG_PREFIX "Failed to wait for engine, is audio not running?");
826             }
827         }
828     }
829     else
830     {
831         pData->doNextPluginAction();
832     }
833 }
834 
~ScopedActionLock()835 ScopedActionLock::~ScopedActionLock() noexcept
836 {
837     CARLA_SAFE_ASSERT(pData->nextAction.opcode == kEnginePostActionNull);
838 }
839 
840 // -----------------------------------------------------------------------
841 // ScopedThreadStopper
842 
ScopedThreadStopper(CarlaEngine * const e)843 ScopedThreadStopper::ScopedThreadStopper(CarlaEngine* const e) noexcept
844     : engine(e),
845       pData(e->pData)
846 {
847     pData->thread.stopThread(500);
848 }
849 
~ScopedThreadStopper()850 ScopedThreadStopper::~ScopedThreadStopper() noexcept
851 {
852     if (engine->isRunning() && ! pData->aboutToClose)
853         pData->thread.startThread();
854 }
855 
856 // -----------------------------------------------------------------------
857 // ScopedEngineEnvironmentLocker
858 
ScopedEngineEnvironmentLocker(CarlaEngine * const engine)859 ScopedEngineEnvironmentLocker::ScopedEngineEnvironmentLocker(CarlaEngine* const engine) noexcept
860     : pData(engine->pData)
861 {
862     pData->envMutex.lock();
863 }
864 
~ScopedEngineEnvironmentLocker()865 ScopedEngineEnvironmentLocker::~ScopedEngineEnvironmentLocker() noexcept
866 {
867     pData->envMutex.unlock();
868 }
869 
870 // -----------------------------------------------------------------------
871 
872 CARLA_BACKEND_END_NAMESPACE
873