|  |  | @@ -185,7 +185,13 @@ struct Engine::Internal { | 
		
	
		
			
			|  |  |  | std::vector<Module*> modules; | 
		
	
		
			
			|  |  |  | std::vector<Cable*> cables; | 
		
	
		
			
			|  |  |  | std::set<ParamHandle*> paramHandles; | 
		
	
		
			
			|  |  |  | std::map<std::tuple<int, int>, ParamHandle*> paramHandleCache; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // moduleId | 
		
	
		
			
			|  |  |  | std::map<int, Module*> modulesCache; | 
		
	
		
			
			|  |  |  | // cableId | 
		
	
		
			
			|  |  |  | std::map<int, Cable*> cablesCache; | 
		
	
		
			
			|  |  |  | // (moduleId, paramId) | 
		
	
		
			
			|  |  |  | std::map<std::tuple<int, int>, ParamHandle*> paramHandlesCache; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float sampleRate = 0.f; | 
		
	
		
			
			|  |  |  | float sampleTime = 0.f; | 
		
	
	
		
			
				|  |  | @@ -195,6 +201,7 @@ struct Engine::Internal { | 
		
	
		
			
			|  |  |  | int stepFrames = 0; | 
		
	
		
			
			|  |  |  | Module* primaryModule = NULL; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // For generating new IDs for added objects | 
		
	
		
			
			|  |  |  | int nextModuleId = 0; | 
		
	
		
			
			|  |  |  | int nextCableId = 0; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -214,6 +221,59 @@ struct Engine::Internal { | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_updateExpander(Engine* that, Module::Expander* expander) { | 
		
	
		
			
			|  |  |  | if (expander->moduleId >= 0) { | 
		
	
		
			
			|  |  |  | if (!expander->module || expander->module->id != expander->moduleId) { | 
		
	
		
			
			|  |  |  | expander->module = that->getModule(expander->moduleId); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | else { | 
		
	
		
			
			|  |  |  | if (expander->module) { | 
		
	
		
			
			|  |  |  | expander->module = NULL; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_relaunchWorkers(Engine* that, int threadCount) { | 
		
	
		
			
			|  |  |  | Engine::Internal* internal = that->internal; | 
		
	
		
			
			|  |  |  | if (threadCount == internal->threadCount) | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (internal->threadCount > 0) { | 
		
	
		
			
			|  |  |  | // Stop engine workers | 
		
	
		
			
			|  |  |  | for (EngineWorker& worker : internal->workers) { | 
		
	
		
			
			|  |  |  | worker.requestStop(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | internal->engineBarrier.wait(); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Join and destroy engine workers | 
		
	
		
			
			|  |  |  | for (EngineWorker& worker : internal->workers) { | 
		
	
		
			
			|  |  |  | worker.join(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | internal->workers.resize(0); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Configure engine | 
		
	
		
			
			|  |  |  | internal->threadCount = threadCount; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Set barrier counts | 
		
	
		
			
			|  |  |  | internal->engineBarrier.total = threadCount; | 
		
	
		
			
			|  |  |  | internal->workerBarrier.total = threadCount; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (threadCount > 0) { | 
		
	
		
			
			|  |  |  | // Create and start engine workers | 
		
	
		
			
			|  |  |  | internal->workers.resize(threadCount - 1); | 
		
	
		
			
			|  |  |  | for (int id = 1; id < threadCount; id++) { | 
		
	
		
			
			|  |  |  | EngineWorker& worker = internal->workers[id - 1]; | 
		
	
		
			
			|  |  |  | worker.id = id; | 
		
	
		
			
			|  |  |  | worker.engine = that; | 
		
	
		
			
			|  |  |  | worker.start(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Port_step(Port* that, float deltaTime) { | 
		
	
		
			
			|  |  |  | // Set plug lights | 
		
	
		
			
			|  |  |  | if (that->channels == 0) { | 
		
	
	
		
			
				|  |  | @@ -371,52 +431,60 @@ static void Engine_stepModules(Engine* that) { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_updateExpander(Engine* that, Module::Expander* expander) { | 
		
	
		
			
			|  |  |  | if (expander->moduleId >= 0) { | 
		
	
		
			
			|  |  |  | if (!expander->module || expander->module->id != expander->moduleId) { | 
		
	
		
			
			|  |  |  | expander->module = that->getModule(expander->moduleId); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | else { | 
		
	
		
			
			|  |  |  | if (expander->module) { | 
		
	
		
			
			|  |  |  | expander->module = NULL; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | static void Port_setDisconnected(Port* that) { | 
		
	
		
			
			|  |  |  | that->channels = 0; | 
		
	
		
			
			|  |  |  | for (int c = 0; c < PORT_MAX_CHANNELS; c++) { | 
		
	
		
			
			|  |  |  | that->voltages[c] = 0.f; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_relaunchWorkers(Engine* that, int threadCount) { | 
		
	
		
			
			|  |  |  | Engine::Internal* internal = that->internal; | 
		
	
		
			
			|  |  |  | static void Port_setConnected(Port* that) { | 
		
	
		
			
			|  |  |  | if (that->channels > 0) | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | that->channels = 1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (internal->threadCount > 0) { | 
		
	
		
			
			|  |  |  | // Stop engine workers | 
		
	
		
			
			|  |  |  | for (EngineWorker& worker : internal->workers) { | 
		
	
		
			
			|  |  |  | worker.requestStop(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | internal->engineBarrier.wait(); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Join and destroy engine workers | 
		
	
		
			
			|  |  |  | for (EngineWorker& worker : internal->workers) { | 
		
	
		
			
			|  |  |  | worker.join(); | 
		
	
		
			
			|  |  |  | static void Engine_updateConnected(Engine* that) { | 
		
	
		
			
			|  |  |  | // Find disconnected ports | 
		
	
		
			
			|  |  |  | std::set<Port*> disconnectedPorts; | 
		
	
		
			
			|  |  |  | for (Module* module : that->internal->modules) { | 
		
	
		
			
			|  |  |  | for (Input& input : module->inputs) { | 
		
	
		
			
			|  |  |  | disconnectedPorts.insert(&input); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | for (Output& output : module->outputs) { | 
		
	
		
			
			|  |  |  | disconnectedPorts.insert(&output); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | internal->workers.resize(0); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | for (Cable* cable : that->internal->cables) { | 
		
	
		
			
			|  |  |  | // Connect input | 
		
	
		
			
			|  |  |  | Input& input = cable->inputModule->inputs[cable->inputId]; | 
		
	
		
			
			|  |  |  | auto inputIt = disconnectedPorts.find(&input); | 
		
	
		
			
			|  |  |  | if (inputIt != disconnectedPorts.end()) | 
		
	
		
			
			|  |  |  | disconnectedPorts.erase(inputIt); | 
		
	
		
			
			|  |  |  | Port_setConnected(&input); | 
		
	
		
			
			|  |  |  | // Connect output | 
		
	
		
			
			|  |  |  | Output& output = cable->outputModule->outputs[cable->outputId]; | 
		
	
		
			
			|  |  |  | auto outputIt = disconnectedPorts.find(&output); | 
		
	
		
			
			|  |  |  | if (outputIt != disconnectedPorts.end()) | 
		
	
		
			
			|  |  |  | disconnectedPorts.erase(outputIt); | 
		
	
		
			
			|  |  |  | Port_setConnected(&output); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | // Disconnect ports that have no cable | 
		
	
		
			
			|  |  |  | for (Port* port : disconnectedPorts) { | 
		
	
		
			
			|  |  |  | Port_setDisconnected(port); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Configure engine | 
		
	
		
			
			|  |  |  | internal->threadCount = threadCount; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Set barrier counts | 
		
	
		
			
			|  |  |  | internal->engineBarrier.total = threadCount; | 
		
	
		
			
			|  |  |  | internal->workerBarrier.total = threadCount; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (threadCount > 0) { | 
		
	
		
			
			|  |  |  | // Create and start engine workers | 
		
	
		
			
			|  |  |  | internal->workers.resize(threadCount - 1); | 
		
	
		
			
			|  |  |  | for (int id = 1; id < threadCount; id++) { | 
		
	
		
			
			|  |  |  | EngineWorker& worker = internal->workers[id - 1]; | 
		
	
		
			
			|  |  |  | worker.id = id; | 
		
	
		
			
			|  |  |  | worker.engine = that; | 
		
	
		
			
			|  |  |  | worker.start(); | 
		
	
		
			
			|  |  |  | static void Engine_refreshParamHandleCache(Engine* that) { | 
		
	
		
			
			|  |  |  | // Clear cache | 
		
	
		
			
			|  |  |  | that->internal->paramHandlesCache.clear(); | 
		
	
		
			
			|  |  |  | // Add active ParamHandles to cache | 
		
	
		
			
			|  |  |  | for (ParamHandle* paramHandle : that->internal->paramHandles) { | 
		
	
		
			
			|  |  |  | if (paramHandle->moduleId >= 0) { | 
		
	
		
			
			|  |  |  | that->internal->paramHandlesCache[std::make_tuple(paramHandle->moduleId, paramHandle->paramId)] = paramHandle; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -440,7 +508,10 @@ Engine::~Engine() { | 
		
	
		
			
			|  |  |  | assert(internal->cables.empty()); | 
		
	
		
			
			|  |  |  | assert(internal->modules.empty()); | 
		
	
		
			
			|  |  |  | assert(internal->paramHandles.empty()); | 
		
	
		
			
			|  |  |  | assert(internal->paramHandleCache.empty()); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | assert(internal->modulesCache.empty()); | 
		
	
		
			
			|  |  |  | assert(internal->cablesCache.empty()); | 
		
	
		
			
			|  |  |  | assert(internal->paramHandlesCache.empty()); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | delete internal; | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -484,7 +555,7 @@ void Engine::step(int frames) { | 
		
	
		
			
			|  |  |  | // Set sample rate | 
		
	
		
			
			|  |  |  | if (internal->sampleRate != settings::sampleRate) { | 
		
	
		
			
			|  |  |  | internal->sampleRate = settings::sampleRate; | 
		
	
		
			
			|  |  |  | internal->sampleTime = 1 / internal->sampleRate; | 
		
	
		
			
			|  |  |  | internal->sampleTime = 1.f / internal->sampleRate; | 
		
	
		
			
			|  |  |  | Module::SampleRateChangeEvent e; | 
		
	
		
			
			|  |  |  | e.sampleRate = internal->sampleRate; | 
		
	
		
			
			|  |  |  | e.sampleTime = internal->sampleTime; | 
		
	
	
		
			
				|  |  | @@ -493,17 +564,15 @@ void Engine::step(int frames) { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Launch workers | 
		
	
		
			
			|  |  |  | if (internal->threadCount != settings::threadCount) { | 
		
	
		
			
			|  |  |  | Engine_relaunchWorkers(this, settings::threadCount); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Update expander pointers | 
		
	
		
			
			|  |  |  | for (Module* module : internal->modules) { | 
		
	
		
			
			|  |  |  | Engine_updateExpander(this, &module->leftExpander); | 
		
	
		
			
			|  |  |  | Engine_updateExpander(this, &module->rightExpander); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Launch workers | 
		
	
		
			
			|  |  |  | Engine_relaunchWorkers(this, settings::threadCount); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Step modules | 
		
	
		
			
			|  |  |  | for (int i = 0; i < frames; i++) { | 
		
	
		
			
			|  |  |  | Engine_stepModules(this); | 
		
	
	
		
			
				|  |  | @@ -608,15 +677,16 @@ void Engine::addModule(Module* module) { | 
		
	
		
			
			|  |  |  | else { | 
		
	
		
			
			|  |  |  | // Manual ID | 
		
	
		
			
			|  |  |  | // Check that the ID is not already taken | 
		
	
		
			
			|  |  |  | for (Module* m : internal->modules) { | 
		
	
		
			
			|  |  |  | assert(module->id != m->id); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | auto it = internal->modulesCache.find(module->id); | 
		
	
		
			
			|  |  |  | assert(it == internal->modulesCache.end()); | 
		
	
		
			
			|  |  |  | // If ID is higher than engine's next assigned ID, enlarge it. | 
		
	
		
			
			|  |  |  | if (module->id >= internal->nextModuleId) { | 
		
	
		
			
			|  |  |  | internal->nextModuleId = module->id + 1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | // Add module | 
		
	
		
			
			|  |  |  | internal->modules.push_back(module); | 
		
	
		
			
			|  |  |  | internal->modulesCache[module->id] = module; | 
		
	
		
			
			|  |  |  | // Trigger Add event | 
		
	
		
			
			|  |  |  | Module::AddEvent eAdd; | 
		
	
		
			
			|  |  |  | module->onAdd(eAdd); | 
		
	
	
		
			
				|  |  | @@ -666,23 +736,22 @@ void Engine::removeModule(Module* module) { | 
		
	
		
			
			|  |  |  | if (internal->primaryModule == module) | 
		
	
		
			
			|  |  |  | internal->primaryModule = NULL; | 
		
	
		
			
			|  |  |  | // Remove module | 
		
	
		
			
			|  |  |  | internal->modulesCache.erase(module->id); | 
		
	
		
			
			|  |  |  | internal->modules.erase(it); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Module* Engine::getModule(int moduleId) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | // Find module by id | 
		
	
		
			
			|  |  |  | for (Module* module : internal->modules) { | 
		
	
		
			
			|  |  |  | if (module->id == moduleId) | 
		
	
		
			
			|  |  |  | return module; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | return NULL; | 
		
	
		
			
			|  |  |  | auto it = internal->modulesCache.find(moduleId); | 
		
	
		
			
			|  |  |  | if (it == internal->modulesCache.end()) | 
		
	
		
			
			|  |  |  | return NULL; | 
		
	
		
			
			|  |  |  | return it->second; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::resetModule(Module* module) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | ExclusiveSharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | assert(module); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Module::ResetEvent eReset; | 
		
	
	
		
			
				|  |  | @@ -691,7 +760,7 @@ void Engine::resetModule(Module* module) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::randomizeModule(Module* module) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | ExclusiveSharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | assert(module); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Module::RandomizeEvent eRandomize; | 
		
	
	
		
			
				|  |  | @@ -700,8 +769,9 @@ void Engine::randomizeModule(Module* module) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::bypassModule(Module* module, bool bypassed) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | ExclusiveSharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | assert(module); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (module->bypassed() == bypassed) | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | // Clear outputs and set to 1 channel | 
		
	
	
		
			
				|  |  | @@ -709,6 +779,7 @@ void Engine::bypassModule(Module* module, bool bypassed) { | 
		
	
		
			
			|  |  |  | // This zeros all voltages, but the channel is set to 1 if connected | 
		
	
		
			
			|  |  |  | output.setChannels(0); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | // Set bypassed | 
		
	
		
			
			|  |  |  | module->bypassed() = bypassed; | 
		
	
		
			
			|  |  |  | // Trigger event | 
		
	
		
			
			|  |  |  | if (bypassed) { | 
		
	
	
		
			
				|  |  | @@ -734,53 +805,6 @@ void Engine::moduleFromJson(Module* module, json_t* rootJ) { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Port_setDisconnected(Port* that) { | 
		
	
		
			
			|  |  |  | that->channels = 0; | 
		
	
		
			
			|  |  |  | for (int c = 0; c < PORT_MAX_CHANNELS; c++) { | 
		
	
		
			
			|  |  |  | that->voltages[c] = 0.f; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Port_setConnected(Port* that) { | 
		
	
		
			
			|  |  |  | if (that->channels > 0) | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | that->channels = 1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_updateConnected(Engine* that) { | 
		
	
		
			
			|  |  |  | // Find disconnected ports | 
		
	
		
			
			|  |  |  | std::set<Port*> disconnectedPorts; | 
		
	
		
			
			|  |  |  | for (Module* module : that->internal->modules) { | 
		
	
		
			
			|  |  |  | for (Input& input : module->inputs) { | 
		
	
		
			
			|  |  |  | disconnectedPorts.insert(&input); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | for (Output& output : module->outputs) { | 
		
	
		
			
			|  |  |  | disconnectedPorts.insert(&output); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | for (Cable* cable : that->internal->cables) { | 
		
	
		
			
			|  |  |  | // Connect input | 
		
	
		
			
			|  |  |  | Input& input = cable->inputModule->inputs[cable->inputId]; | 
		
	
		
			
			|  |  |  | auto inputIt = disconnectedPorts.find(&input); | 
		
	
		
			
			|  |  |  | if (inputIt != disconnectedPorts.end()) | 
		
	
		
			
			|  |  |  | disconnectedPorts.erase(inputIt); | 
		
	
		
			
			|  |  |  | Port_setConnected(&input); | 
		
	
		
			
			|  |  |  | // Connect output | 
		
	
		
			
			|  |  |  | Output& output = cable->outputModule->outputs[cable->outputId]; | 
		
	
		
			
			|  |  |  | auto outputIt = disconnectedPorts.find(&output); | 
		
	
		
			
			|  |  |  | if (outputIt != disconnectedPorts.end()) | 
		
	
		
			
			|  |  |  | disconnectedPorts.erase(outputIt); | 
		
	
		
			
			|  |  |  | Port_setConnected(&output); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | // Disconnect ports that have no cable | 
		
	
		
			
			|  |  |  | for (Port* port : disconnectedPorts) { | 
		
	
		
			
			|  |  |  | Port_setDisconnected(port); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::addCable(Cable* cable) { | 
		
	
		
			
			|  |  |  | ExclusiveSharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | assert(cable); | 
		
	
	
		
			
				|  |  | @@ -815,6 +839,7 @@ void Engine::addCable(Cable* cable) { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | // Add the cable | 
		
	
		
			
			|  |  |  | internal->cables.push_back(cable); | 
		
	
		
			
			|  |  |  | internal->cablesCache[cable->id] = cable; | 
		
	
		
			
			|  |  |  | Engine_updateConnected(this); | 
		
	
		
			
			|  |  |  | // Trigger input port event | 
		
	
		
			
			|  |  |  | { | 
		
	
	
		
			
				|  |  | @@ -842,6 +867,7 @@ void Engine::removeCable(Cable* cable) { | 
		
	
		
			
			|  |  |  | auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); | 
		
	
		
			
			|  |  |  | assert(it != internal->cables.end()); | 
		
	
		
			
			|  |  |  | // Remove the cable | 
		
	
		
			
			|  |  |  | internal->cablesCache.erase(cable->id); | 
		
	
		
			
			|  |  |  | internal->cables.erase(it); | 
		
	
		
			
			|  |  |  | Engine_updateConnected(this); | 
		
	
		
			
			|  |  |  | bool outputIsConnected = false; | 
		
	
	
		
			
				|  |  | @@ -872,17 +898,14 @@ void Engine::removeCable(Cable* cable) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Cable* Engine::getCable(int cableId) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | // Find cable by id | 
		
	
		
			
			|  |  |  | for (Cable* cable : internal->cables) { | 
		
	
		
			
			|  |  |  | if (cable->id == cableId) | 
		
	
		
			
			|  |  |  | return cable; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | return NULL; | 
		
	
		
			
			|  |  |  | auto it = internal->cablesCache.find(cableId); | 
		
	
		
			
			|  |  |  | if (it == internal->cablesCache.end()) | 
		
	
		
			
			|  |  |  | return NULL; | 
		
	
		
			
			|  |  |  | return it->second; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::setParam(Module* module, int paramId, float value) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | // If param is being smoothed, cancel smoothing. | 
		
	
		
			
			|  |  |  | if (internal->smoothModule == module && internal->smoothParamId == paramId) { | 
		
	
		
			
			|  |  |  | internal->smoothModule = NULL; | 
		
	
	
		
			
				|  |  | @@ -893,13 +916,11 @@ void Engine::setParam(Module* module, int paramId, float value) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float Engine::getParam(Module* module, int paramId) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | return module->params[paramId].value; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::setSmoothParam(Module* module, int paramId, float value) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | // If another param is being smoothed, jump value | 
		
	
		
			
			|  |  |  | if (internal->smoothModule && !(internal->smoothModule == module && internal->smoothParamId == paramId)) { | 
		
	
		
			
			|  |  |  | internal->smoothModule->params[internal->smoothParamId].value = internal->smoothValue; | 
		
	
	
		
			
				|  |  | @@ -912,25 +933,12 @@ void Engine::setSmoothParam(Module* module, int paramId, float value) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float Engine::getSmoothParam(Module* module, int paramId) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | if (internal->smoothModule == module && internal->smoothParamId == paramId) | 
		
	
		
			
			|  |  |  | return internal->smoothValue; | 
		
	
		
			
			|  |  |  | return module->params[paramId].value; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static void Engine_refreshParamHandleCache(Engine* that) { | 
		
	
		
			
			|  |  |  | // Clear cache | 
		
	
		
			
			|  |  |  | that->internal->paramHandleCache.clear(); | 
		
	
		
			
			|  |  |  | // Add active ParamHandles to cache | 
		
	
		
			
			|  |  |  | for (ParamHandle* paramHandle : that->internal->paramHandles) { | 
		
	
		
			
			|  |  |  | if (paramHandle->moduleId >= 0) { | 
		
	
		
			
			|  |  |  | that->internal->paramHandleCache[std::make_tuple(paramHandle->moduleId, paramHandle->paramId)] = paramHandle; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void Engine::addParamHandle(ParamHandle* paramHandle) { | 
		
	
		
			
			|  |  |  | ExclusiveSharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | // New ParamHandles must be blank. | 
		
	
	
		
			
				|  |  | @@ -943,6 +951,7 @@ void Engine::addParamHandle(ParamHandle* paramHandle) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Add it | 
		
	
		
			
			|  |  |  | internal->paramHandles.insert(paramHandle); | 
		
	
		
			
			|  |  |  | // No need to refresh the cache because the moduleId is not set. | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -961,8 +970,8 @@ void Engine::removeParamHandle(ParamHandle* paramHandle) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | ParamHandle* Engine::getParamHandle(int moduleId, int paramId) { | 
		
	
		
			
			|  |  |  | SharedLock lock(internal->mutex); | 
		
	
		
			
			|  |  |  | auto it = internal->paramHandleCache.find(std::make_tuple(moduleId, paramId)); | 
		
	
		
			
			|  |  |  | if (it == internal->paramHandleCache.end()) | 
		
	
		
			
			|  |  |  | auto it = internal->paramHandlesCache.find(std::make_tuple(moduleId, paramId)); | 
		
	
		
			
			|  |  |  | if (it == internal->paramHandlesCache.end()) | 
		
	
		
			
			|  |  |  | return NULL; | 
		
	
		
			
			|  |  |  | return it->second; | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | 
 |