diff --git a/Makefile b/Makefile index 3a43250c..5a67ad2c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,13 @@ +VERSION = 0.6.0dev + FLAGS += \ -Iinclude \ -Idep/include -Idep/lib/libzip/include +ifdef RELEASE + FLAGS += -DRELEASE=$(RELEASE) +endif + SOURCES = $(wildcard src/*.cpp src/*/*.cpp) \ ext/nanovg/src/nanovg.c @@ -87,9 +93,6 @@ include compile.mk dist: all -ifndef VERSION - $(error VERSION must be defined when making distributables) -endif rm -rf dist $(MAKE) -C plugins/Fundamental dist diff --git a/include/engine.hpp b/include/engine.hpp index 29eedfdf..df0edf17 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -70,13 +70,24 @@ struct Module { /** Called when user explicitly deletes the module, not when Rack is closed or a new patch is loaded */ virtual void onDelete() {} /** Called when user clicks Initialize in the module context menu */ - virtual void onReset() {} + virtual void onReset() { + // Call deprecated method + reset(); + } /** Called when user clicks Randomize in the module context menu */ - virtual void onRandomize() {} + virtual void onRandomize() { + // Call deprecated method + randomize(); + } /** Override these to store extra internal data in the "data" property */ virtual json_t *toJson() { return NULL; } virtual void fromJson(json_t *root) {} + + /** Deprecated */ + virtual void reset() {} + /** Deprecated */ + virtual void randomize() {} }; struct Wire { diff --git a/include/settings.hpp b/include/settings.hpp index 8714c63a..c0a8858d 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -10,4 +10,7 @@ void settingsSave(std::string filename); void settingsLoad(std::string filename); +extern bool skipAutosaveOnLaunch; + + } // namespace rack diff --git a/include/util.hpp b/include/util.hpp index 3e7b53dc..004458af 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -83,11 +83,12 @@ float randomNormal(); /** Converts a printf format string and optional arguments into a std::string */ std::string stringf(const char *format, ...); -std::string tolower(std::string s); -std::string toupper(std::string s); +std::string lowercase(std::string s); +std::string uppercase(std::string s); /** Truncates and adds "..." to a string, not exceeding `len` characters */ std::string ellipsize(std::string s, size_t len); +bool startsWith(std::string str, std::string prefix); std::string extractDirectory(std::string path); std::string extractFilename(std::string path); diff --git a/src/app.cpp b/src/app.cpp index 84795945..188c4a1b 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -4,12 +4,7 @@ namespace rack { std::string gApplicationName = "VCV Rack"; -std::string gApplicationVersion = -#ifdef VERSION - TOSTRING(VERSION); -#else - ""; -#endif +std::string gApplicationVersion = TOSTRING(VERSION); std::string gApiHost = "https://api.vcvrack.com"; // std::string gApiHost = "http://localhost:8081"; diff --git a/src/app/AddModuleWindow.cpp b/src/app/AddModuleWindow.cpp index ac99389e..8e6fd377 100644 --- a/src/app/AddModuleWindow.cpp +++ b/src/app/AddModuleWindow.cpp @@ -98,8 +98,8 @@ static bool isModelMatch(Model *model, std::string search) { str += " "; str += gTagNames[tag]; } - str = tolower(str); - search = tolower(search); + str = lowercase(str); + search = lowercase(search); return (str.find(search) != std::string::npos); } diff --git a/src/app/RackScene.cpp b/src/app/RackScene.cpp index f9e9db26..417e3b16 100644 --- a/src/app/RackScene.cpp +++ b/src/app/RackScene.cpp @@ -45,10 +45,10 @@ RackScene::RackScene() { scrollWidget->box.pos.y = gToolbar->box.size.y; // Check for new version - if (!gApplicationVersion.empty()) { - std::thread versionThread(checkVersion); - versionThread.detach(); - } +#if defined(RELEASE) + std::thread versionThread(checkVersion); + versionThread.detach(); +#endif } void RackScene::step() { @@ -68,7 +68,7 @@ void RackScene::step() { // Version popup message if (!newVersion.empty()) { - std::string versionMessage = stringf("Rack %s is available.\n\nYou have Rack %s.\n\nWould you like to download the new version on the website?", newVersion.c_str(), gApplicationVersion.c_str()); + std::string versionMessage = stringf("Rack v%s is available.\n\nYou have Rack v%s.\n\nWould you like to download the new version on the website?", newVersion.c_str(), gApplicationVersion.c_str()); if (osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, versionMessage.c_str())) { std::thread t(openBrowser, "https://vcvrack.com/"); t.detach(); diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index d01cf012..9a51193a 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -145,10 +145,8 @@ json_t *RackWidget::toJson() { json_t *rootJ = json_object(); // version - if (!gApplicationVersion.empty()) { - json_t *versionJ = json_string(gApplicationVersion.c_str()); - json_object_set_new(rootJ, "version", versionJ); - } + json_t *versionJ = json_string(gApplicationVersion.c_str()); + json_object_set_new(rootJ, "version", versionJ); // modules json_t *modulesJ = json_array(); @@ -210,11 +208,19 @@ void RackWidget::fromJson(json_t *rootJ) { std::string message; // version + std::string version; json_t *versionJ = json_object_get(rootJ, "version"); if (versionJ) { - std::string version = json_string_value(versionJ); + version = json_string_value(versionJ); if (!version.empty() && gApplicationVersion != version) - message += stringf("This patch was created with Rack %s. Saving it will convert it to a Rack %s patch.\n\n", version.c_str(), gApplicationVersion.empty() ? "dev" : gApplicationVersion.c_str()); + message += stringf("This patch was created with Rack %s. Saving it will convert it to a Rack %s patch.\n\n", version.c_str(), gApplicationVersion.c_str()); + } + + // Detect old patches with ModuleWidget::params/inputs/outputs indices. + // (We now use Module::params/inputs/outputs indices.) + bool legacy1 = (startsWith(version, "0.3.") || startsWith(version, "0.4.") || startsWith(version, "0.5.") || version == "" || version == "dev"); + if (legacy1) { + info("Converting patch using legacy1 loader"); } // modules diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 00ccbdd9..7383406c 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -178,7 +178,7 @@ Toolbar::Toolbar() { xPos += margin; */ -#ifdef VERSION +#if defined(RELEASE) { Widget *pluginManager = new PluginManagerWidget(); pluginManager->box.pos = Vec(xPos, margin); diff --git a/src/asset.cpp b/src/asset.cpp index 7f819fa0..8e95aee2 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -26,7 +26,7 @@ namespace rack { std::string assetGlobal(std::string filename) { std::string dir; -#ifdef VERSION +#if defined(RELEASE) #if ARCH_MAC CFBundleRef bundle = CFBundleGetMainBundle(); assert(bundle); @@ -45,16 +45,16 @@ std::string assetGlobal(std::string filename) { // TODO For now, users should launch Rack from their terminal in the global directory dir = "."; #endif -#else // VERSION +#else // RELEASE dir = "."; -#endif // VERSION +#endif // RELEASE return dir + "/" + filename; } std::string assetLocal(std::string filename) { std::string dir; -#ifdef VERSION +#if defined(RELEASE) #if ARCH_MAC // Get home directory struct passwd *pw = getpwuid(getuid()); @@ -83,9 +83,9 @@ std::string assetLocal(std::string filename) { dir += "/.Rack"; mkdir(dir.c_str(), 0755); #endif -#else // VERSION +#else // RELEASE dir = "."; -#endif // VERSION +#endif // RELEASE return dir + "/" + filename; } diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index 67d8b865..89fd38a9 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -2,7 +2,7 @@ #include #include #include "core.hpp" -#include "MidiIO.hpp" +#include "midi.hpp" #include "dsp/digital.hpp" @@ -16,7 +16,7 @@ struct MidiValue { bool changed = false; // Value has been changed by midi message (only if it is in sync!) }; -struct MIDIToCVInterface : MidiIO, Module { +struct MIDIToCVInterface : Module { enum ParamIds { RESET_PARAM, NUM_PARAMS @@ -49,7 +49,7 @@ struct MIDIToCVInterface : MidiIO, Module { SchmittTrigger resetTrigger; - MIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { + MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { pitchWheel.val = 64; pitchWheel.tSmooth.set(0, 0); } @@ -253,8 +253,7 @@ MidiToCVWidget::MidiToCVWidget() { } addParam(createParam(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0)); - addChild(createLight>(Vec(7 * 15 + 5, labelHeight + 5), module, - MIDIToCVInterface::RESET_LIGHT)); + addChild(createLight>(Vec(7 * 15 + 5, labelHeight + 5), module, MIDIToCVInterface::RESET_LIGHT)); { Label *label = new Label(); label->box.pos = Vec(margin, yPos); @@ -285,8 +284,7 @@ MidiToCVWidget::MidiToCVWidget() { yPos += channelChoice->box.size.y + margin + 15; } - std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", - "Aftertouch"}; + std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", "Aftertouch"}; for (int i = 0; i < MIDIToCVInterface::NUM_OUTPUTS; i++) { Label *label = new Label(); @@ -299,9 +297,4 @@ MidiToCVWidget::MidiToCVWidget() { yPos += yGap + margin; } } - -void MidiToCVWidget::step() { - - ModuleWidget::step(); -} #endif \ No newline at end of file diff --git a/src/core/core.cpp b/src/core/core.cpp index 09a50a01..a444e273 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -8,8 +8,8 @@ void init(rack::Plugin *p) { #endif p->addModel(createModel("Core", "AudioInterface", "Audio Interface", EXTERNAL_TAG)); - // p->addModel(createModel("Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); + // p->addModel(createModel("Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); // p->addModel(createModel("Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); // p->addModel(createModel("Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, CLOCK_TAG)); // p->addModel(createModel("Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); diff --git a/src/core/core.hpp b/src/core/core.hpp index 8ff746f3..17fd465d 100644 --- a/src/core/core.hpp +++ b/src/core/core.hpp @@ -14,29 +14,27 @@ struct AudioInterfaceWidget : ModuleWidget { struct MidiToCVWidget : ModuleWidget { MidiToCVWidget(); - void step() override; }; -struct MIDICCToCVWidget : ModuleWidget { - MIDICCToCVWidget(); - void step() override; -}; - -struct MIDIClockToCVWidget : ModuleWidget { - MIDIClockToCVWidget(); - void step() override; -}; +// struct MIDICCToCVWidget : ModuleWidget { +// MIDICCToCVWidget(); +// void step() override; +// }; -struct MIDITriggerToCVWidget : ModuleWidget { - MIDITriggerToCVWidget(); - void step() override; -}; +// struct MIDIClockToCVWidget : ModuleWidget { +// MIDIClockToCVWidget(); +// void step() override; +// }; -struct QuadMidiToCVWidget : ModuleWidget { - QuadMidiToCVWidget(); - void step() override; -}; +// struct MIDITriggerToCVWidget : ModuleWidget { +// MIDITriggerToCVWidget(); +// void step() override; +// }; +// struct QuadMidiToCVWidget : ModuleWidget { +// QuadMidiToCVWidget(); +// void step() override; +// }; struct BridgeWidget : ModuleWidget { BridgeWidget(); diff --git a/src/gui.cpp b/src/gui.cpp index 8b418c40..6a940374 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -432,10 +432,7 @@ void guiRun() { mouseButtonStickyPop(); // Set window title - std::string windowTitle = gApplicationName; - if (!gApplicationVersion.empty()) { - windowTitle += " v" + gApplicationVersion; - } + std::string windowTitle = gApplicationName + " v" + gApplicationVersion; if (!gRackWidget->lastPath.empty()) { windowTitle += " - "; windowTitle += extractFilename(gRackWidget->lastPath); diff --git a/src/main.cpp b/src/main.cpp index bc32af17..6252506c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,14 +12,12 @@ using namespace rack; int main(int argc, char* argv[]) { randomSeedTime(); -#ifdef VERSION +#ifdef RELEASE std::string logFilename = assetLocal("log.txt"); gLogFile = fopen(logFilename.c_str(), "w"); #endif - if (!gApplicationVersion.empty()) { - info("Rack v%s", gApplicationVersion.c_str()); - } + info("Rack v%s", gApplicationVersion.c_str()); { char *cwd = getcwd(NULL, 0); @@ -35,21 +33,28 @@ int main(int argc, char* argv[]) { engineInit(); guiInit(); sceneInit(); - gRackWidget->loadPatch(assetLocal("autosave.vcv")); settingsLoad(assetLocal("settings.json")); + // To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded. + bool oldSkipAutosaveOnLaunch = skipAutosaveOnLaunch; + skipAutosaveOnLaunch = true; + settingsSave(assetLocal("settings.json")); + skipAutosaveOnLaunch = false; + if (!oldSkipAutosaveOnLaunch) + gRackWidget->loadPatch(assetLocal("autosave.vcv")); + engineStart(); guiRun(); engineStop(); - settingsSave(assetLocal("settings.json")); gRackWidget->savePatch(assetLocal("autosave.vcv")); + settingsSave(assetLocal("settings.json")); sceneDestroy(); guiDestroy(); engineDestroy(); pluginDestroy(); -#ifdef VERSION +#ifdef RELEASE fclose(gLogFile); #endif diff --git a/src/settings.cpp b/src/settings.cpp index 1020f192..a6425fb1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -9,6 +9,9 @@ namespace rack { +bool skipAutosaveOnLaunch = false; + + static json_t *settingsToJson() { // root json_t *rootJ = json_object(); @@ -56,6 +59,11 @@ static json_t *settingsToJson() { json_t *lastPathJ = json_string(gRackWidget->lastPath.c_str()); json_object_set_new(rootJ, "lastPath", lastPathJ); + // skipAutosaveOnLaunch + if (skipAutosaveOnLaunch) { + json_object_set_new(rootJ, "skipAutosaveOnLaunch", json_true()); + } + return rootJ; } @@ -114,22 +122,25 @@ static void settingsFromJson(json_t *rootJ) { json_t *lastPathJ = json_object_get(rootJ, "lastPath"); if (lastPathJ) gRackWidget->lastPath = json_string_value(lastPathJ); + + json_t *skipAutosaveOnLaunchJ = json_object_get(rootJ, "skipAutosaveOnLaunch"); + if (skipAutosaveOnLaunchJ) + skipAutosaveOnLaunch = json_boolean_value(skipAutosaveOnLaunchJ); } void settingsSave(std::string filename) { info("Saving settings %s", filename.c_str()); - FILE *file = fopen(filename.c_str(), "w"); - if (!file) - return; - json_t *rootJ = settingsToJson(); if (rootJ) { + FILE *file = fopen(filename.c_str(), "w"); + if (!file) + return; + json_dumpf(rootJ, file, JSON_INDENT(2)); json_decref(rootJ); + fclose(file); } - - fclose(file); } void settingsLoad(std::string filename) { diff --git a/src/util.cpp b/src/util.cpp index 7417af1f..ae53f039 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -99,12 +99,12 @@ std::string stringf(const char *format, ...) { return s; } -std::string tolower(std::string s) { +std::string lowercase(std::string s) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; } -std::string toupper(std::string s) { +std::string uppercase(std::string s) { std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s; } @@ -116,6 +116,10 @@ std::string ellipsize(std::string s, size_t len) { return s.substr(0, len - 3) + "..."; } +bool startsWith(std::string str, std::string prefix) { + return str.substr(0, prefix.size()) == prefix; +} + std::string extractDirectory(std::string path) { char *pathDup = strdup(path.c_str()); std::string directory = dirname(pathDup);