3 * Copyright (C) 2011-2020 Filipe Coelho <falktx@falktx.com>
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.
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.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaEngineInternal.hpp"
19 #include "CarlaPlugin.hpp"
20 #include "CarlaSemUtils.hpp"
22 #include "jackbridge/JackBridge.hpp"
27 # include <sys/timeb.h>
28 # include <sys/types.h>
30 # include <sys/time.h>
33 CARLA_BACKEND_START_NAMESPACE
35 // -----------------------------------------------------------------------
36 // Engine Internal helper macro, sets lastError and returns false/NULL
38 #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(cond, err) if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return false; }
39 #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERRN(cond, err) if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return nullptr; }
41 // -----------------------------------------------------------------------
44 EngineInternalEvents::EngineInternalEvents() noexcept
48 EngineInternalEvents::~EngineInternalEvents() noexcept
50 CARLA_SAFE_ASSERT(in
== nullptr);
51 CARLA_SAFE_ASSERT(out
== nullptr);
54 void EngineInternalEvents::clear() noexcept
69 // -----------------------------------------------------------------------
72 static const double kTicksPerBeat
= 1920.0;
74 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
75 static uint32_t calculate_link_latency(const double bufferSize
, const double sampleRate
) noexcept
77 CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate
), 0);
79 const long long int latency
= llround(1.0e6
* bufferSize
/ sampleRate
);
80 CARLA_SAFE_ASSERT_RETURN(latency
>= 0 && latency
< UINT32_MAX
, 0);
82 return static_cast<uint32_t>(latency
);
86 EngineInternalTime::EngineInternalTime(EngineTimeInfo
& ti
, const EngineTransportMode
& tm
) noexcept
88 beatsPerMinute(120.0),
99 void EngineInternalTime::init(const uint32_t bsize
, const double srate
)
104 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
105 if (hylia
.instance
!= nullptr)
107 hylia_set_beats_per_bar(hylia
.instance
, beatsPerBar
);
108 hylia_set_beats_per_minute(hylia
.instance
, beatsPerMinute
);
109 hylia_set_output_latency(hylia
.instance
, calculate_link_latency(bsize
, srate
));
112 hylia_enable(hylia
.instance
, true);
119 void EngineInternalTime::updateAudioValues(const uint32_t bsize
, const double srate
)
124 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
125 if (hylia
.instance
!= nullptr)
126 hylia_set_output_latency(hylia
.instance
, calculate_link_latency(bsize
, srate
));
132 void EngineInternalTime::enableLink(const bool enable
)
134 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
135 if (hylia
.enabled
== enable
)
138 if (hylia
.instance
!= nullptr)
140 hylia
.enabled
= enable
;
141 hylia_enable(hylia
.instance
, enable
);
151 void EngineInternalTime::setBPM(const double bpm
)
153 beatsPerMinute
= bpm
;
155 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
156 if (hylia
.instance
!= nullptr)
157 hylia_set_beats_per_minute(hylia
.instance
, bpm
);
161 void EngineInternalTime::setNeedsReset() noexcept
166 void EngineInternalTime::pause() noexcept
168 timeInfo
.playing
= false;
169 nextFrame
= timeInfo
.frame
;
173 void EngineInternalTime::relocate(const uint64_t frame
) noexcept
175 timeInfo
.frame
= frame
;
180 void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames
) noexcept
182 CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate
),);
183 CARLA_SAFE_ASSERT_RETURN(newFrames
> 0,);
187 if (transportMode
== ENGINE_TRANSPORT_MODE_INTERNAL
)
190 timeInfo
.frame
= nextFrame
;
195 timeInfo
.bbt
.valid
= true;
196 timeInfo
.bbt
.beatType
= 4.0f
;
197 timeInfo
.bbt
.ticksPerBeat
= kTicksPerBeat
;
199 double abs_beat
, abs_tick
;
201 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
204 if (hylia
.timeInfo
.beat
>= 0.0)
206 abs_beat
= hylia
.timeInfo
.beat
;
207 abs_tick
= abs_beat
* kTicksPerBeat
;
213 timeInfo
.playing
= false;
219 const double min
= static_cast<double>(timeInfo
.frame
) / (sampleRate
* 60.0);
220 abs_beat
= min
* beatsPerMinute
;
221 abs_tick
= abs_beat
* kTicksPerBeat
;
225 const double bar
= std::floor(abs_beat
/ beatsPerBar
);
226 const double beat
= std::floor(std::fmod(abs_beat
, beatsPerBar
));
228 timeInfo
.bbt
.bar
= static_cast<int32_t>(bar
) + 1;
229 timeInfo
.bbt
.beat
= static_cast<int32_t>(beat
) + 1;
230 timeInfo
.bbt
.barStartTick
= ((bar
* beatsPerBar
) + beat
) * kTicksPerBeat
;
232 ticktmp
= abs_tick
- timeInfo
.bbt
.barStartTick
;
234 else if (timeInfo
.playing
)
236 ticktmp
= timeInfo
.bbt
.tick
+ (newFrames
* kTicksPerBeat
* beatsPerMinute
/ (sampleRate
* 60));
238 while (ticktmp
>= kTicksPerBeat
)
240 ticktmp
-= kTicksPerBeat
;
242 if (++timeInfo
.bbt
.beat
> beatsPerBar
)
245 timeInfo
.bbt
.beat
= 1;
246 timeInfo
.bbt
.barStartTick
+= beatsPerBar
* kTicksPerBeat
;
252 ticktmp
= timeInfo
.bbt
.tick
;
255 timeInfo
.bbt
.beatsPerBar
= static_cast<float>(beatsPerBar
);
256 timeInfo
.bbt
.beatsPerMinute
= beatsPerMinute
;
257 timeInfo
.bbt
.tick
= ticktmp
;
259 if (transportMode
== ENGINE_TRANSPORT_MODE_INTERNAL
&& timeInfo
.playing
)
260 nextFrame
+= newFrames
;
263 void EngineInternalTime::fillJackTimeInfo(jack_position_t
* const pos
, const uint32_t newFrames
) noexcept
265 CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate
),);
266 CARLA_SAFE_ASSERT_RETURN(newFrames
> 0,);
267 CARLA_SAFE_ASSERT(transportMode
== ENGINE_TRANSPORT_MODE_JACK
);
269 fillEngineTimeInfo(newFrames
);
271 pos
->bar
= timeInfo
.bbt
.bar
;
272 pos
->beat
= timeInfo
.bbt
.beat
;
273 pos
->tick
= static_cast<int32_t>(timeInfo
.bbt
.tick
+ 0.5);
274 pos
->bar_start_tick
= timeInfo
.bbt
.barStartTick
;
275 pos
->beats_per_bar
= timeInfo
.bbt
.beatsPerBar
;
276 pos
->beat_type
= timeInfo
.bbt
.beatType
;
277 pos
->ticks_per_beat
= kTicksPerBeat
;
278 pos
->beats_per_minute
= beatsPerMinute
;
279 #ifdef JACK_TICK_DOUBLE
280 pos
->tick_double
= timeInfo
.bbt
.tick
;
281 pos
->valid
= static_cast<jack_position_bits_t
>(JackPositionBBT
|JackTickDouble
);
283 pos
->valid
= JackPositionBBT
;
287 void EngineInternalTime::preProcess(const uint32_t numFrames
)
289 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
292 hylia_process(hylia
.instance
, numFrames
, &hylia
.timeInfo
);
294 const double new_bpb
= hylia
.timeInfo
.beatsPerBar
;
295 const double new_bpm
= hylia
.timeInfo
.beatsPerMinute
;
297 if (new_bpb
>= 1.0 && carla_isNotEqual(beatsPerBar
, new_bpb
))
299 beatsPerBar
= new_bpb
;
302 if (new_bpm
> 0.0 && carla_isNotEqual(beatsPerMinute
, new_bpm
))
304 beatsPerMinute
= new_bpm
;
310 if (transportMode
== ENGINE_TRANSPORT_MODE_INTERNAL
)
311 fillEngineTimeInfo(numFrames
);
314 // -----------------------------------------------------------------------
315 // EngineInternalTime::Hylia
318 EngineInternalTime::Hylia::Hylia()
323 carla_zeroStruct(timeInfo
);
326 instance
= hylia_create();
330 EngineInternalTime::Hylia::~Hylia()
333 hylia_cleanup(instance
);
338 // -----------------------------------------------------------------------
341 EngineNextAction::EngineNextAction() noexcept
342 : opcode(kEnginePostActionNull
),
348 sem(carla_sem_create(false)) {}
350 EngineNextAction::~EngineNextAction() noexcept
352 CARLA_SAFE_ASSERT(opcode
== kEnginePostActionNull
);
356 carla_sem_destroy(sem
);
361 void EngineNextAction::clearAndReset() noexcept
364 CARLA_SAFE_ASSERT(opcode
== kEnginePostActionNull
);
366 opcode
= kEnginePostActionNull
;
374 // -----------------------------------------------------------------------
377 EngineEvent
* CarlaEngine::getInternalEventBuffer(const bool isInput
) const noexcept
379 return isInput
? pData
->events
.in
: pData
->events
.out
;
382 // -----------------------------------------------------------------------
383 // CarlaEngine::ProtectedData
385 CarlaEngine::ProtectedData::ProtectedData(CarlaEngine
* const engine
)
387 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
391 callbackPtr(nullptr),
392 fileCallback(nullptr),
393 fileCallbackPtr(nullptr),
394 actionCanceled(false),
395 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
396 loadingProject(false),
397 ignoreClientPrefix(false),
398 currentProjectFilename(),
399 currentProjectFolder(),
413 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
418 pluginsToDeleteMutex(),
421 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
424 time(timeInfo
, options
.transportMode
),
427 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
428 plugins
[0].plugin
= nullptr;
429 carla_zeroStructs(plugins
[0].peaks
, 1);
433 CarlaEngine::ProtectedData::~ProtectedData()
435 CARLA_SAFE_ASSERT(curPluginCount
== 0);
436 CARLA_SAFE_ASSERT(maxPluginNumber
== 0);
437 CARLA_SAFE_ASSERT(nextPluginId
== 0);
438 CARLA_SAFE_ASSERT(isIdling
== 0);
439 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
440 CARLA_SAFE_ASSERT(plugins
== nullptr);
443 const CarlaMutexLocker
cml(pluginsToDeleteMutex
);
445 if (pluginsToDelete
.size() != 0)
447 for (std::vector
<CarlaPluginPtr
>::iterator it
= pluginsToDelete
.begin(); it
!= pluginsToDelete
.end(); ++it
)
449 carla_stderr2("Plugin not yet deleted, name: '%s', usage count: '%u'",
450 (*it
)->getName(), it
->use_count());
454 pluginsToDelete
.clear();
457 // -----------------------------------------------------------------------
459 bool CarlaEngine::ProtectedData::init(const char* const clientName
)
461 CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(name
.isEmpty(), "Invalid engine internal data (err #1)");
462 CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events
.in
== nullptr, "Invalid engine internal data (err #4)");
463 CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events
.out
== nullptr, "Invalid engine internal data (err #5)");
464 CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(clientName
!= nullptr && clientName
[0] != '\0', "Invalid client name");
465 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
466 CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(plugins
== nullptr, "Invalid engine internal data (err #3)");
469 aboutToClose
= false;
473 switch (options
.processMode
)
475 case ENGINE_PROCESS_MODE_CONTINUOUS_RACK
:
476 maxPluginNumber
= MAX_RACK_PLUGINS
;
477 options
.forceStereo
= true;
479 case ENGINE_PROCESS_MODE_PATCHBAY
:
480 maxPluginNumber
= MAX_PATCHBAY_PLUGINS
;
482 case ENGINE_PROCESS_MODE_BRIDGE
:
486 maxPluginNumber
= MAX_DEFAULT_PLUGINS
;
490 switch (options
.processMode
)
492 case ENGINE_PROCESS_MODE_CONTINUOUS_RACK
:
493 case ENGINE_PROCESS_MODE_PATCHBAY
:
494 case ENGINE_PROCESS_MODE_BRIDGE
:
495 events
.in
= new EngineEvent
[kMaxEngineEventInternalCount
];
496 events
.out
= new EngineEvent
[kMaxEngineEventInternalCount
];
497 carla_zeroStructs(events
.in
, kMaxEngineEventInternalCount
);
498 carla_zeroStructs(events
.out
, kMaxEngineEventInternalCount
);
504 nextPluginId
= maxPluginNumber
;
511 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
512 if (options
.oscEnabled
)
513 osc
.init(clientName
, options
.oscPortTCP
, options
.oscPortUDP
);
516 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
517 plugins
= new EnginePluginData
[maxPluginNumber
];
522 nextAction
.clearAndReset();
528 void CarlaEngine::ProtectedData::close()
530 CARLA_SAFE_ASSERT(name
.isNotEmpty());
531 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
532 CARLA_SAFE_ASSERT(plugins
!= nullptr);
533 CARLA_SAFE_ASSERT(nextPluginId
== maxPluginNumber
);
539 nextAction
.clearAndReset();
541 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
545 aboutToClose
= false;
550 deletePluginsAsNeeded();
552 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
553 if (plugins
!= nullptr)
564 void CarlaEngine::ProtectedData::initTime(const char* const features
)
566 time
.init(bufferSize
, sampleRate
);
568 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
569 const bool linkEnabled
= features
!= nullptr && std::strstr(features
, ":link:") != nullptr;
570 time
.enableLink(linkEnabled
);
579 // -----------------------------------------------------------------------
581 void CarlaEngine::ProtectedData::deletePluginsAsNeeded()
583 std::vector
<CarlaPluginPtr
> safePluginListToDelete
;
585 if (const size_t size
= pluginsToDelete
.size())
586 safePluginListToDelete
.reserve(size
);
589 const CarlaMutexLocker
cml(pluginsToDeleteMutex
);
591 for (std::vector
<CarlaPluginPtr
>::iterator it
= pluginsToDelete
.begin(); it
!= pluginsToDelete
.end();)
593 if (it
->use_count() == 1)
595 const CarlaPluginPtr plugin
= *it
;
596 safePluginListToDelete
.push_back(plugin
);
597 pluginsToDelete
.erase(it
);
607 // -----------------------------------------------------------------------
609 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
610 void CarlaEngine::ProtectedData::doPluginRemove(const uint pluginId
) noexcept
612 CARLA_SAFE_ASSERT_RETURN(curPluginCount
> 0,);
613 CARLA_SAFE_ASSERT_RETURN(pluginId
< curPluginCount
,);
616 // move all plugins 1 spot backwards
617 for (uint i
=pluginId
; i
< curPluginCount
; ++i
)
619 const CarlaPluginPtr plugin
= plugins
[i
+1].plugin
;
620 CARLA_SAFE_ASSERT_BREAK(plugin
.get() != nullptr);
624 plugins
[i
].plugin
= plugin
;
625 carla_zeroStruct(plugins
[i
].peaks
);
628 const uint id
= curPluginCount
;
630 // reset last plugin (now removed)
631 plugins
[id
].plugin
.reset();
632 carla_zeroFloats(plugins
[id
].peaks
, 4);
635 void CarlaEngine::ProtectedData::doPluginsSwitch(const uint idA
, const uint idB
) noexcept
637 CARLA_SAFE_ASSERT_RETURN(curPluginCount
>= 2,);
639 CARLA_SAFE_ASSERT_RETURN(idA
< curPluginCount
,);
640 CARLA_SAFE_ASSERT_RETURN(idB
< curPluginCount
,);
642 const CarlaPluginPtr pluginA
= plugins
[idA
].plugin
;
643 CARLA_SAFE_ASSERT_RETURN(pluginA
.get() != nullptr,);
645 const CarlaPluginPtr pluginB
= plugins
[idB
].plugin
;
646 CARLA_SAFE_ASSERT_RETURN(pluginB
.get() != nullptr,);
649 plugins
[idA
].plugin
= pluginB
;
652 plugins
[idB
].plugin
= pluginA
;
656 void CarlaEngine::ProtectedData::doNextPluginAction() noexcept
658 if (! nextAction
.mutex
.tryLock())
661 const EnginePostAction opcode
= nextAction
.opcode
;
662 const bool needsPost
= nextAction
.needsPost
;
663 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
664 const uint pluginId
= nextAction
.pluginId
;
665 const uint value
= nextAction
.value
;
668 nextAction
.opcode
= kEnginePostActionNull
;
669 nextAction
.pluginId
= 0;
670 nextAction
.value
= 0;
671 nextAction
.needsPost
= false;
673 nextAction
.mutex
.unlock();
677 case kEnginePostActionNull
:
679 case kEnginePostActionZeroCount
:
682 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
683 case kEnginePostActionRemovePlugin
:
684 doPluginRemove(pluginId
);
686 case kEnginePostActionSwitchPlugins
:
687 doPluginsSwitch(pluginId
, value
);
694 if (nextAction
.sem
!= nullptr)
695 carla_sem_post(*nextAction
.sem
);
696 nextAction
.postDone
= true;
700 // -----------------------------------------------------------------------
701 // PendingRtEventsRunner
703 static int64_t getTimeInMicroseconds() noexcept
705 #if defined(_MSC_VER)
708 return ((int64_t) tb
.time
) * 1000 + tb
.millitm
;
709 #elif defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
711 gettimeofday(&tv
, nullptr);
712 return (tv
.tv_sec
* 1000000) + tv
.tv_usec
;
715 #ifdef CLOCK_MONOTONIC_RAW
716 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts
);
718 clock_gettime(CLOCK_MONOTONIC
, &ts
);
720 return (ts
.tv_sec
* 1000000) + (ts
.tv_nsec
/ 1000);
724 PendingRtEventsRunner::PendingRtEventsRunner(CarlaEngine
* const engine
,
725 const uint32_t frames
,
726 const bool calcDSPLoad
) noexcept
727 : pData(engine
->pData
),
728 prevTime(calcDSPLoad
? getTimeInMicroseconds() : 0)
730 pData
->time
.preProcess(frames
);
733 PendingRtEventsRunner::~PendingRtEventsRunner() noexcept
735 pData
->doNextPluginAction();
737 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
740 const int64_t newTime
= getTimeInMicroseconds();
742 if (newTime
< prevTime
)
745 const double timeDiff
= static_cast<double>(newTime
- prevTime
) / 1000000.0;
746 const double maxTime
= pData
->bufferSize
/ pData
->sampleRate
;
747 const float dspLoad
= static_cast<float>(timeDiff
/ maxTime
) * 100.0f
;
749 if (dspLoad
> pData
->dspLoad
)
750 pData
->dspLoad
= std::min(100.0f
, dspLoad
);
752 pData
->dspLoad
*= static_cast<float>(1.0 - maxTime
) + 1e-12f
;
757 // -----------------------------------------------------------------------
760 ScopedActionLock::ScopedActionLock(CarlaEngine
* const engine
,
761 const EnginePostAction action
,
763 const uint value
) noexcept
764 : pData(engine
->pData
)
766 CARLA_SAFE_ASSERT_RETURN(action
!= kEnginePostActionNull
,);
769 const CarlaMutexLocker
cml(pData
->nextAction
.mutex
);
771 CARLA_SAFE_ASSERT_RETURN(pData
->nextAction
.opcode
== kEnginePostActionNull
,);
773 pData
->nextAction
.opcode
= action
;
774 pData
->nextAction
.pluginId
= pluginId
;
775 pData
->nextAction
.value
= value
;
776 pData
->nextAction
.needsPost
= engine
->isRunning();
777 pData
->nextAction
.postDone
= false;
781 #define ACTION_MSG_PREFIX "Bridge: "
783 #define ACTION_MSG_PREFIX ""
786 if (pData
->nextAction
.needsPost
)
788 bool engineStoppedWhileWaiting
= false;
790 #ifndef CARLA_OS_WASM
791 #if defined(DEBUG) || defined(BUILD_BRIDGE)
792 // block wait for unlock on processing side
793 carla_stdout(ACTION_MSG_PREFIX
"ScopedPluginAction(%i|%i:%s) - blocking START",
794 pluginId
, action
, EnginePostAction2Str(action
));
797 if (! pData
->nextAction
.postDone
)
799 for (int i
= 10; --i
>= 0;)
801 if (pData
->nextAction
.sem
!= nullptr)
803 if (carla_sem_timedwait(*pData
->nextAction
.sem
, 200))
811 if (! engine
->isRunning())
813 engineStoppedWhileWaiting
= true;
819 #if defined(DEBUG) || defined(BUILD_BRIDGE)
820 carla_stdout(ACTION_MSG_PREFIX
"ScopedPluginAction(%i|%i:%s) - blocking DONE",
821 pluginId
, action
, EnginePostAction2Str(action
));
825 // check if anything went wrong...
826 if (! pData
->nextAction
.postDone
)
828 bool needsCorrection
= false;
831 const CarlaMutexLocker
cml(pData
->nextAction
.mutex
);
833 if (pData
->nextAction
.opcode
!= kEnginePostActionNull
)
835 needsCorrection
= true;
836 pData
->nextAction
.needsPost
= false;
842 pData
->doNextPluginAction();
844 if (! engineStoppedWhileWaiting
)
845 carla_stderr2(ACTION_MSG_PREFIX
"Failed to wait for engine, is audio not running?");
851 pData
->doNextPluginAction();
855 ScopedActionLock::~ScopedActionLock() noexcept
857 CARLA_SAFE_ASSERT(pData
->nextAction
.opcode
== kEnginePostActionNull
);
860 // -----------------------------------------------------------------------
861 // ScopedRunnerStopper
863 ScopedRunnerStopper::ScopedRunnerStopper(CarlaEngine
* const e
) noexcept
867 pData
->runner
.stop();
870 ScopedRunnerStopper::~ScopedRunnerStopper() noexcept
872 if (engine
->isRunning() && ! pData
->aboutToClose
)
873 pData
->runner
.start();
876 // -----------------------------------------------------------------------
877 // ScopedEngineEnvironmentLocker
879 ScopedEngineEnvironmentLocker::ScopedEngineEnvironmentLocker(CarlaEngine
* const engine
) noexcept
880 : pData(engine
->pData
)
882 pData
->envMutex
.lock();
885 ScopedEngineEnvironmentLocker::~ScopedEngineEnvironmentLocker() noexcept
887 pData
->envMutex
.unlock();
890 // -----------------------------------------------------------------------
892 CARLA_BACKEND_END_NAMESPACE