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