#include #include #include #include #include #include #include #include namespace rack { namespace engine { // Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors. static const int METER_DIVIDER = 37; static const int METER_BUFFER_LEN = 32; static const float METER_TIME = 1.f; struct Module::Internal { bool bypassed = false; int meterSamples = 0; float meterDurationTotal = 0.f; float meterBuffer[METER_BUFFER_LEN] = {}; int meterIndex = 0; }; Module::Module() { internal = new Internal; } Module::~Module() { for (ParamQuantity* paramQuantity : paramQuantities) { if (paramQuantity) delete paramQuantity; } for (PortInfo* inputInfo : inputInfos) { if (inputInfo) delete inputInfo; } for (PortInfo* outputInfo : outputInfos) { if (outputInfo) delete outputInfo; } for (LightInfo* lightInfo : lightInfos) { if (lightInfo) delete lightInfo; } delete internal; } void Module::config(int numParams, int numInputs, int numOutputs, int numLights) { // This method should only be called once. assert(params.empty() && inputs.empty() && outputs.empty() && lights.empty() && paramQuantities.empty()); params.resize(numParams); inputs.resize(numInputs); outputs.resize(numOutputs); lights.resize(numLights); // Initialize paramQuantities paramQuantities.resize(numParams); for (int i = 0; i < numParams; i++) { configParam(i, 0.f, 1.f, 0.f); } // Initialize PortInfos inputInfos.resize(numInputs); for (int i = 0; i < numInputs; i++) { configInput(i); } outputInfos.resize(numOutputs); for (int i = 0; i < numOutputs; i++) { configOutput(i); } // Initialize LightInfos with null lightInfos.resize(numLights); } std::string Module::createPatchStorageDirectory() { std::string path = getPatchStorageDirectory(); system::createDirectories(path); return path; } std::string Module::getPatchStorageDirectory() { if (id < 0) throw Exception("getPatchStorageDirectory() cannot be called unless Module belongs to Engine and thus has a valid ID"); return system::join(APP->patch->autosavePath, "modules", std::to_string(id)); } void Module::processBypass(const ProcessArgs& args) { for (BypassRoute& bypassRoute : bypassRoutes) { // Route input voltages to output Input& input = inputs[bypassRoute.inputId]; Output& output = outputs[bypassRoute.outputId]; int channels = input.getChannels(); for (int c = 0; c < channels; c++) { float v = input.getVoltage(c); output.setVoltage(v, c); } output.setChannels(channels); } } json_t* Module::toJson() { json_t* rootJ = json_object(); // id json_object_set_new(rootJ, "id", json_integer(id)); // plugin json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); // model json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); // version json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); // params json_t* paramsJ = paramsToJson(); if (paramsJ) json_object_set_new(rootJ, "params", paramsJ); // bypass if (internal->bypassed) json_object_set_new(rootJ, "bypass", json_boolean(true)); // leftModuleId if (leftExpander.moduleId >= 0) json_object_set_new(rootJ, "leftModuleId", json_integer(leftExpander.moduleId)); // rightModuleId if (rightExpander.moduleId >= 0) json_object_set_new(rootJ, "rightModuleId", json_integer(rightExpander.moduleId)); // data json_t* dataJ = dataToJson(); if (dataJ) { json_object_set_new(rootJ, "data", dataJ); } return rootJ; } void Module::fromJson(json_t* rootJ) { plugin::Model* model = plugin::modelFromJson(rootJ); assert(model); if (model != this->model) throw Exception("Model %s %s does not match Module's model %s %s.", model->plugin->slug.c_str(), model->slug.c_str(), this->model->plugin->slug.c_str(), this->model->slug.c_str()); // Check plugin version json_t* versionJ = json_object_get(rootJ, "version"); if (versionJ) { std::string version = json_string_value(versionJ); if (version != this->model->plugin->version) { INFO("Patch created with %s v%s, currently using v%s.", this->model->plugin->slug.c_str(), version.c_str(), this->model->plugin->version.c_str()); } } // id // Only set ID if unset if (id < 0) { json_t* idJ = json_object_get(rootJ, "id"); if (idJ) id = json_integer_value(idJ); } // params json_t* paramsJ = json_object_get(rootJ, "params"); if (paramsJ) paramsFromJson(paramsJ); // bypass json_t* bypassJ = json_object_get(rootJ, "bypass"); // legacy "disabled" in v1 if (!bypassJ) bypassJ = json_object_get(rootJ, "disabled"); if (bypassJ) internal->bypassed = json_boolean_value(bypassJ); // leftModuleId json_t *leftModuleIdJ = json_object_get(rootJ, "leftModuleId"); if (leftModuleIdJ) leftExpander.moduleId = json_integer_value(leftModuleIdJ); // rightModuleId json_t *rightModuleIdJ = json_object_get(rootJ, "rightModuleId"); if (rightModuleIdJ) rightExpander.moduleId = json_integer_value(rightModuleIdJ); // data json_t* dataJ = json_object_get(rootJ, "data"); if (dataJ) dataFromJson(dataJ); } json_t* Module::paramsToJson() { json_t* rootJ = json_array(); for (size_t paramId = 0; paramId < paramQuantities.size(); paramId++) { // Don't serialize unbounded Params if (!paramQuantities[paramId]->isBounded()) continue; json_t* paramJ = paramQuantities[paramId]->toJson(); json_object_set_new(paramJ, "id", json_integer(paramId)); json_array_append(rootJ, paramJ); } return rootJ; } void Module::paramsFromJson(json_t* rootJ) { size_t i; json_t* paramJ; json_array_foreach(rootJ, i, paramJ) { // Get paramId json_t* paramIdJ = json_object_get(paramJ, "id"); // Legacy v0.6 to = paramQuantities.size()) continue; ParamQuantity* pq = paramQuantities[paramId]; // Check that the Param is bounded if (!pq->isBounded()) continue; json_t* valueJ = json_object_get(paramJ, "value"); if (valueJ) pq->setValue(json_number_value(valueJ)); } } void Module::onReset(const ResetEvent& e) { // Reset all parameters for (ParamQuantity* pq : paramQuantities) { if (!pq->resetEnabled) continue; if (!pq->isBounded()) continue; pq->reset(); } // Call deprecated event onReset(); } void Module::onRandomize(const RandomizeEvent& e) { // Randomize all parameters for (ParamQuantity* pq : paramQuantities) { if (!pq->randomizeEnabled) continue; if (!pq->isBounded()) continue; pq->randomize(); } // Call deprecated event onRandomize(); } bool Module::isBypassed() { return internal->bypassed; } void Module::setBypassed(bool bypassed) { internal->bypassed = bypassed; } const float* Module::meterBuffer() { return internal->meterBuffer; } int Module::meterLength() { return METER_BUFFER_LEN; } int Module::meterIndex() { return internal->meterIndex; } static void Port_step(Port* that, float deltaTime) { // Set plug lights if (that->channels == 0) { that->plugLights[0].setBrightness(0.f); that->plugLights[1].setBrightness(0.f); that->plugLights[2].setBrightness(0.f); } else if (that->channels == 1) { float v = that->getVoltage() / 10.f; that->plugLights[0].setSmoothBrightness(-v, deltaTime); that->plugLights[1].setSmoothBrightness(v, deltaTime); that->plugLights[2].setBrightness(0.f); } else { float v = that->getVoltageRMS() / 10.f; that->plugLights[0].setBrightness(0.f); that->plugLights[1].setBrightness(0.f); that->plugLights[2].setSmoothBrightness(v, deltaTime); } } void Module::doProcess(const ProcessArgs& args) { // This global setting can change while the function is running, so use a local variable. bool meterEnabled = settings::cpuMeter && (args.frame % METER_DIVIDER == 0); // Start CPU timer double startTime; if (meterEnabled) { startTime = system::getTime(); } // Step module if (!internal->bypassed) process(args); else processBypass(args); // Stop CPU timer if (meterEnabled) { double endTime = system::getTime(); // Subtract call time of getTime() itself, since we only want to measure process() time. double endTime2 = system::getTime(); float duration = (endTime - startTime) - (endTime2 - endTime); internal->meterSamples++; internal->meterDurationTotal += duration; // Seconds we've been measuring float meterTime = internal->meterSamples * METER_DIVIDER * args.sampleTime; if (meterTime >= METER_TIME) { // Push time to buffer if (internal->meterSamples > 0) { internal->meterIndex++; internal->meterIndex %= METER_BUFFER_LEN; internal->meterBuffer[internal->meterIndex] = internal->meterDurationTotal / internal->meterSamples; } // Reset total internal->meterSamples = 0; internal->meterDurationTotal = 0.f; } } // Iterate ports to step plug lights const int portDivider = 8; if (args.frame % portDivider == 0) { float portTime = args.sampleTime * portDivider; for (Input& input : inputs) { Port_step(&input, portTime); } for (Output& output : outputs) { Port_step(&output, portTime); } } } void Module::jsonStripIds(json_t* rootJ) { json_object_del(rootJ, "id"); json_object_del(rootJ, "leftModuleId"); json_object_del(rootJ, "rightModuleId"); } } // namespace engine } // namespace rack