/* * Carla Plugin Host * Copyright (C) 2011-2014 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "CarlaEngineInternal.hpp" #include "CarlaPlugin.hpp" CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- // Engine Internal helper macro, sets lastError and returns false/NULL #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(cond, err) if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return false; } #define CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERRN(cond, err) if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); lastError = err; return nullptr; } // ----------------------------------------------------------------------- // InternalEvents EngineInternalEvents::EngineInternalEvents() noexcept : in(nullptr), out(nullptr) {} EngineInternalEvents::~EngineInternalEvents() noexcept { CARLA_SAFE_ASSERT(in == nullptr); CARLA_SAFE_ASSERT(out == nullptr); } void EngineInternalEvents::clear() noexcept { if (in != nullptr) { delete[] in; in = nullptr; } if (out != nullptr) { delete[] out; out = nullptr; } } // ----------------------------------------------------------------------- // InternalTime EngineInternalTime::EngineInternalTime() noexcept : playing(false), frame(0) {} // ----------------------------------------------------------------------- // NextAction EngineNextAction::EngineNextAction() noexcept : opcode(kEnginePostActionNull), pluginId(0), value(0), mutex() {} EngineNextAction::~EngineNextAction() noexcept { CARLA_SAFE_ASSERT(opcode == kEnginePostActionNull); } void EngineNextAction::ready() const noexcept { mutex.lock(); mutex.unlock(); } void EngineNextAction::clearAndReset() noexcept { mutex.lock(); opcode = kEnginePostActionNull; pluginId = 0; value = 0; mutex.unlock(); } // ----------------------------------------------------------------------- // CarlaEngine::ProtectedData CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept : thread(engine), #ifdef HAVE_LIBLO osc(engine), oscData(nullptr), #endif callback(nullptr), callbackPtr(nullptr), fileCallback(nullptr), fileCallbackPtr(nullptr), hints(0x0), bufferSize(0), sampleRate(0.0), aboutToClose(false), isIdling(0), curPluginCount(0), maxPluginNumber(0), nextPluginId(0), envMutex(), lastError(), name(), options(), timeInfo(), #ifndef BUILD_BRIDGE plugins(nullptr), #endif events(), #ifndef BUILD_BRIDGE graph(engine), #endif time(), nextAction() { #ifdef BUILD_BRIDGE carla_zeroStructs(plugins, 1); #endif } CarlaEngine::ProtectedData::~ProtectedData() noexcept { CARLA_SAFE_ASSERT(curPluginCount == 0); CARLA_SAFE_ASSERT(maxPluginNumber == 0); CARLA_SAFE_ASSERT(nextPluginId == 0); CARLA_SAFE_ASSERT(isIdling == 0); #ifndef BUILD_BRIDGE CARLA_SAFE_ASSERT(plugins == nullptr); #endif } // ----------------------------------------------------------------------- bool CarlaEngine::ProtectedData::init(const char* const clientName) { CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(name.isEmpty(), "Invalid engine internal data (err #1)"); #ifdef HAVE_LIBLO CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(oscData == nullptr, "Invalid engine internal data (err #2)"); #endif CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.in == nullptr, "Invalid engine internal data (err #4)"); CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.out == nullptr, "Invalid engine internal data (err #5)"); CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(clientName != nullptr && clientName[0] != '\0', "Invalid client name"); #ifndef BUILD_BRIDGE CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(plugins == nullptr, "Invalid engine internal data (err #3)"); #endif aboutToClose = false; curPluginCount = 0; nextPluginId = 0; switch (options.processMode) { case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: maxPluginNumber = MAX_RACK_PLUGINS; options.forceStereo = true; // just in case break; case ENGINE_PROCESS_MODE_PATCHBAY: maxPluginNumber = MAX_PATCHBAY_PLUGINS; break; case ENGINE_PROCESS_MODE_BRIDGE: maxPluginNumber = 1; break; default: maxPluginNumber = MAX_DEFAULT_PLUGINS; break; } switch (options.processMode) { case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: case ENGINE_PROCESS_MODE_PATCHBAY: case ENGINE_PROCESS_MODE_BRIDGE: events.in = new EngineEvent[kMaxEngineEventInternalCount]; events.out = new EngineEvent[kMaxEngineEventInternalCount]; break; default: break; } nextPluginId = maxPluginNumber; name = clientName; name.toBasic(); timeInfo.clear(); #ifdef HAVE_LIBLO osc.init(clientName); # ifndef BUILD_BRIDGE oscData = osc.getControlData(); # endif #endif #ifndef BUILD_BRIDGE plugins = new EnginePluginData[maxPluginNumber]; carla_zeroStructs(plugins, maxPluginNumber); #endif nextAction.ready(); thread.startThread(); return true; } void CarlaEngine::ProtectedData::close() { CARLA_SAFE_ASSERT(name.isNotEmpty()); CARLA_SAFE_ASSERT(plugins != nullptr); CARLA_SAFE_ASSERT(nextPluginId == maxPluginNumber); CARLA_SAFE_ASSERT(nextAction.opcode == kEnginePostActionNull); aboutToClose = true; thread.stopThread(500); nextAction.ready(); #ifdef HAVE_LIBLO osc.close(); oscData = nullptr; #endif aboutToClose = false; curPluginCount = 0; maxPluginNumber = 0; nextPluginId = 0; #ifndef BUILD_BRIDGE if (plugins != nullptr) { delete[] plugins; plugins = nullptr; } #endif events.clear(); name.clear(); } // ----------------------------------------------------------------------- #ifndef BUILD_BRIDGE void CarlaEngine::ProtectedData::doPluginRemove() noexcept { CARLA_SAFE_ASSERT_RETURN(curPluginCount > 0,); CARLA_SAFE_ASSERT_RETURN(nextAction.pluginId < curPluginCount,); --curPluginCount; // move all plugins 1 spot backwards for (uint i=nextAction.pluginId; i < curPluginCount; ++i) { CarlaPlugin* const plugin(plugins[i+1].plugin); CARLA_SAFE_ASSERT_BREAK(plugin != nullptr); plugin->setId(i); plugins[i].plugin = plugin; plugins[i].insPeak[0] = 0.0f; plugins[i].insPeak[1] = 0.0f; plugins[i].outsPeak[0] = 0.0f; plugins[i].outsPeak[1] = 0.0f; } const uint id(curPluginCount); // reset last plugin (now removed) plugins[id].plugin = nullptr; plugins[id].insPeak[0] = 0.0f; plugins[id].insPeak[1] = 0.0f; plugins[id].outsPeak[0] = 0.0f; plugins[id].outsPeak[1] = 0.0f; } void CarlaEngine::ProtectedData::doPluginsSwitch() noexcept { CARLA_SAFE_ASSERT_RETURN(curPluginCount >= 2,); const uint idA(nextAction.pluginId); const uint idB(nextAction.value); CARLA_SAFE_ASSERT_RETURN(idA < curPluginCount,); CARLA_SAFE_ASSERT_RETURN(idB < curPluginCount,); CARLA_SAFE_ASSERT_RETURN(plugins[idA].plugin != nullptr,); CARLA_SAFE_ASSERT_RETURN(plugins[idB].plugin != nullptr,); #if 0 std::swap(plugins[idA].plugin, plugins[idB].plugin); #else CarlaPlugin* const tmp(plugins[idA].plugin); plugins[idA].plugin = plugins[idB].plugin; plugins[idB].plugin = tmp; #endif } #endif void CarlaEngine::ProtectedData::doNextPluginAction(const bool unlock) noexcept { switch (nextAction.opcode) { case kEnginePostActionNull: break; case kEnginePostActionZeroCount: curPluginCount = 0; break; #ifndef BUILD_BRIDGE case kEnginePostActionRemovePlugin: doPluginRemove(); break; case kEnginePostActionSwitchPlugins: doPluginsSwitch(); break; #endif } nextAction.opcode = kEnginePostActionNull; nextAction.pluginId = 0; nextAction.value = 0; if (unlock) { nextAction.mutex.tryLock(); nextAction.mutex.unlock(); } } // ----------------------------------------------------------------------- // PendingRtEventsRunner PendingRtEventsRunner::PendingRtEventsRunner(CarlaEngine* const engine) noexcept : pData(engine->pData) {} PendingRtEventsRunner::~PendingRtEventsRunner() noexcept { pData->doNextPluginAction(true); if (pData->time.playing) pData->time.frame += pData->bufferSize; if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) { pData->timeInfo.playing = pData->time.playing; pData->timeInfo.frame = pData->time.frame; } } // ----------------------------------------------------------------------- // ScopedActionLock ScopedActionLock::ScopedActionLock(CarlaEngine* const engine, const EnginePostAction action, const uint pluginId, const uint value, const bool lockWait) noexcept : pData(engine->pData) { CARLA_SAFE_ASSERT_RETURN(action != kEnginePostActionNull,); pData->nextAction.mutex.lock(); CARLA_SAFE_ASSERT_RETURN(pData->nextAction.opcode == kEnginePostActionNull,); pData->nextAction.opcode = action; pData->nextAction.pluginId = pluginId; pData->nextAction.value = value; if (lockWait) { // block wait for unlock on processing side carla_stdout("ScopedPluginAction(%i) - blocking START", pluginId); pData->nextAction.mutex.lock(); carla_stdout("ScopedPluginAction(%i) - blocking DONE", pluginId); } else { pData->doNextPluginAction(false); } } ScopedActionLock::~ScopedActionLock() noexcept { CARLA_SAFE_ASSERT(pData->nextAction.opcode == kEnginePostActionNull); pData->nextAction.mutex.tryLock(); pData->nextAction.mutex.unlock(); } // ----------------------------------------------------------------------- // ScopedThreadStopper ScopedThreadStopper::ScopedThreadStopper(CarlaEngine* const e) noexcept : engine(e), pData(e->pData) { pData->thread.stopThread(500); } ScopedThreadStopper::~ScopedThreadStopper() noexcept { if (engine->isRunning() && ! pData->aboutToClose) pData->thread.startThread(); } // ----------------------------------------------------------------------- // ScopedEngineEnvironmentLocker ScopedEngineEnvironmentLocker::ScopedEngineEnvironmentLocker(CarlaEngine* const engine) noexcept : pData(engine->pData) { pData->envMutex.lock(); } ScopedEngineEnvironmentLocker::~ScopedEngineEnvironmentLocker() noexcept { pData->envMutex.unlock(); } // ----------------------------------------------------------------------- CARLA_BACKEND_END_NAMESPACE