From 49654d0c822edf9f72ac9a161d57da4feaa5c1e7 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 20 Sep 2019 06:11:04 -0400 Subject: [PATCH 01/29] Fix sustain pedal bug in polyphonic mode. --- src/core/MIDI_CV.cpp | 50 +++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/core/MIDI_CV.cpp b/src/core/MIDI_CV.cpp index 270829f9..e001432c 100644 --- a/src/core/MIDI_CV.cpp +++ b/src/core/MIDI_CV.cpp @@ -158,9 +158,9 @@ struct MIDI_CV : Module { // note on case 0x9: { if (msg.getValue() > 0) { - int c = (polyMode == MPE_MODE) ? msg.getChannel() : assignChannel(msg.getNote()); + int c = msg.getChannel(); + pressNote(msg.getNote(), &c); velocities[c] = msg.getValue(); - pressNote(msg.getNote(), c); } else { // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. @@ -297,17 +297,24 @@ struct MIDI_CV : Module { } } - void pressNote(uint8_t note, int channel) { + void pressNote(uint8_t note, int* channel) { // Remove existing similar note auto it = std::find(heldNotes.begin(), heldNotes.end(), note); if (it != heldNotes.end()) heldNotes.erase(it); // Push note heldNotes.push_back(note); + // Determine actual channel + if (polyMode == MPE_MODE) { + // Channel is already decided for us + } + else { + *channel = assignChannel(note); + } // Set note - notes[channel] = note; - gates[channel] = true; - retriggerPulses[channel].trigger(1e-3); + notes[*channel] = note; + gates[*channel] = true; + retriggerPulses[*channel].trigger(1e-3); } void releaseNote(uint8_t note) { @@ -336,24 +343,15 @@ struct MIDI_CV : Module { } void pressPedal() { + if (pedal) + return; pedal = true; } void releasePedal() { + if (!pedal) + return; pedal = false; - // Clear all gates - for (int c = 0; c < 16; c++) { - gates[c] = false; - } - // Add back only the gates from heldNotes - for (uint8_t note : heldNotes) { - // Find note's channels - for (int c = 0; c < channels; c++) { - if (notes[c] == note) { - gates[c] = true; - } - } - } // Set last note if monophonic if (channels == 1) { if (!heldNotes.empty()) { @@ -361,6 +359,20 @@ struct MIDI_CV : Module { notes[0] = lastNote; } } + // Clear notes that are not held if polyphonic + else { + for (int c = 0; c < channels; c++) { + if (!gates[c]) + continue; + gates[c] = false; + for (uint8_t note : heldNotes) { + if (notes[c] == note) { + gates[c] = true; + break; + } + } + } + } } void setChannels(int channels) { From 4b78d2020161641b0ff4503adc89683cb4ba9887 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 20 Sep 2019 06:11:58 -0400 Subject: [PATCH 02/29] Add changelogUrl to helper script. --- helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/helper.py b/helper.py index c4e388bc..51fa42fa 100755 --- a/helper.py +++ b/helper.py @@ -169,6 +169,7 @@ def create_manifest(slug, plugin_dir="."): manifest['manualUrl'] = input_default("Manual website URL (optional)", manifest.get('manualUrl', "")) manifest['sourceUrl'] = input_default("Source code URL (optional)", manifest.get('sourceUrl', "")) manifest['donateUrl'] = input_default("Donate URL (optional)", manifest.get('donateUrl', "")) + manifest['changelogUrl'] = manifest.get('changelogUrl', "") if 'modules' not in manifest: manifest['modules'] = [] From cd4dfaf51689b518c6ef5cd638449a1b3c6cf555 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 21 Sep 2019 00:36:58 -0400 Subject: [PATCH 03/29] Update curl and OpenSSL --- dep/Makefile | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/dep/Makefile b/dep/Makefile index 501d1d0e..35fbcd32 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -106,32 +106,32 @@ $(jansson): jansson-2.12 $(MAKE) -C jansson-2.12 $(MAKE) -C jansson-2.12 install -openssl-1.1.1b: - $(WGET) "https://www.openssl.org/source/openssl-1.1.1b.tar.gz" - $(SHA256) openssl-1.1.1b.tar.gz 5c557b023230413dfb0756f3137a13e6d726838ccd1430888ad15bfb2b43ea4b - $(UNTAR) openssl-1.1.1b.tar.gz - rm openssl-1.1.1b.tar.gz +openssl-1.1.1d: + $(WGET) "https://www.openssl.org/source/openssl-1.1.1d.tar.gz" + $(SHA256) openssl-1.1.1d.tar.gz 1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2 + $(UNTAR) openssl-1.1.1d.tar.gz + rm openssl-1.1.1d.tar.gz -$(openssl): openssl-1.1.1b +$(openssl): openssl-1.1.1d @# ./config ignores CFLAGS, so hack it in with CC - cd openssl-1.1.1b && CC="$(CC) $(CFLAGS)" ./config --prefix="$(DEP_PATH)" - $(MAKE) -C openssl-1.1.1b - $(MAKE) -C openssl-1.1.1b install_sw + cd openssl-1.1.1d && CC="$(CC) $(CFLAGS)" ./config --prefix="$(DEP_PATH)" + $(MAKE) -C openssl-1.1.1d + $(MAKE) -C openssl-1.1.1d install_sw -curl-7.64.1: - $(WGET) "https://curl.haxx.se/download/curl-7.64.1.tar.gz" - $(SHA256) curl-7.64.1.tar.gz 432d3f466644b9416bc5b649d344116a753aeaa520c8beaf024a90cba9d3d35d - $(UNTAR) curl-7.64.1.tar.gz - rm curl-7.64.1.tar.gz +curl-7.66.0: + $(WGET) "https://curl.haxx.se/download/curl-7.66.0.tar.gz" + $(SHA256) curl-7.66.0.tar.gz d0393da38ac74ffac67313072d7fe75b1fa1010eb5987f63f349b024a36b7ffb + $(UNTAR) curl-7.66.0.tar.gz + rm curl-7.66.0.tar.gz CURL_FLAGS += --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-manual --disable-shared --disable-symbol-hiding CURL_FLAGS += --without-zlib --without-libpsl --without-libmetalink --without-libssh2 --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-brotli CURL_FLAGS += --with-ssl="$(DEP_PATH)" -$(libcurl): $(openssl) curl-7.64.1 - cd curl-7.64.1 && PKG_CONFIG_PATH= $(CONFIGURE) $(CURL_FLAGS) - $(MAKE) -C curl-7.64.1 - $(MAKE) -C curl-7.64.1 install +$(libcurl): $(openssl) curl-7.66.0 + cd curl-7.66.0 && PKG_CONFIG_PATH= $(CONFIGURE) $(CURL_FLAGS) + $(MAKE) -C curl-7.66.0 + $(MAKE) -C curl-7.66.0 install libzip-1.5.2: $(WGET) "https://libzip.org/download/libzip-1.5.2.tar.gz" @@ -246,7 +246,7 @@ $(pffft): jpommier-pffft-29e4f76ac53b # Helpers -src: glew-2.1.0 glfw jansson-2.12 speexdsp-SpeexDSP-1.2rc3 openssl-1.1.1b curl-7.64.1 libzip-1.5.2 zlib-1.2.11 rtmidi-4.0.0 rtaudio nanovg nanosvg oui-blendish osdialog jpommier-pffft-29e4f76ac53b +src: glew-2.1.0 glfw jansson-2.12 speexdsp-SpeexDSP-1.2rc3 openssl-1.1.1d curl-7.66.0 libzip-1.5.2 zlib-1.2.11 rtmidi-4.0.0 rtaudio nanovg nanosvg oui-blendish osdialog jpommier-pffft-29e4f76ac53b clean: git clean -fdx From 1388e9ebb1c78fe946913125bbc6fd3b972d7612 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 21 Sep 2019 00:37:40 -0400 Subject: [PATCH 04/29] Add network::init(). Add asset::bundlePath. --- include/asset.hpp | 2 ++ include/network.hpp | 1 + src/asset.cpp | 13 ++++++++++++- src/main.cpp | 3 +++ src/network.cpp | 7 +++++++ src/updater.cpp | 15 +++++++++++++-- 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/include/asset.hpp b/include/asset.hpp index b1ba8760..9ccdb2bd 100644 --- a/include/asset.hpp +++ b/include/asset.hpp @@ -31,6 +31,8 @@ extern std::string pluginsPath; extern std::string settingsPath; extern std::string autosavePath; extern std::string templatePath; +// Only defined on Mac +extern std::string bundlePath; } // namespace asset diff --git a/include/network.hpp b/include/network.hpp index 1843bb8d..3d815e2b 100644 --- a/include/network.hpp +++ b/include/network.hpp @@ -18,6 +18,7 @@ enum Method { METHOD_DELETE, }; +void init(); /** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc) Caller must json_decref(). */ diff --git a/src/asset.cpp b/src/asset.cpp index 522623cc..4b1d2915 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -37,9 +37,19 @@ void init() { #if defined ARCH_MAC CFBundleRef bundle = CFBundleGetMainBundle(); assert(bundle); + + CFURLRef bundleUrl = CFBundleCopyBundleURL(bundle); + char bundleBuf[PATH_MAX]; + Boolean success = CFURLGetFileSystemRepresentation(bundleUrl, TRUE, (UInt8*) bundleBuf, sizeof(bundleBuf)); + assert(success); + bundlePath = bundleBuf; + // If the bundle path doesn't end with ".app", assume it's a fake app bundle run from the command line. + if (string::filenameExtension(string::filename(bundlePath)) != "app") + bundlePath = ""; + CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(bundle); char resourcesBuf[PATH_MAX]; - Boolean success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf)); + success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf)); assert(success); CFRelease(resourcesUrl); systemDir = resourcesBuf; @@ -145,6 +155,7 @@ std::string pluginsPath; std::string settingsPath; std::string autosavePath; std::string templatePath; +std::string bundlePath; } // namespace asset diff --git a/src/main.cpp b/src/main.cpp index 6ee1bb2a..47acb830 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,7 @@ int main(int argc, char* argv[]) { patchPath = argv[optind]; } + // Initialize environment asset::init(); logger::init(); @@ -145,6 +147,7 @@ int main(int argc, char* argv[]) { INFO("Initializing environment"); random::init(); + network::init(); midi::init(); rtmidiInit(); bridgeInit(); diff --git a/src/network.cpp b/src/network.cpp index 2fe28477..694a4952 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -30,6 +30,13 @@ static size_t writeStringCallback(char* ptr, size_t size, size_t nmemb, void* us } +void init() { + // curl_easy_init() calls this automatically, but it's good to make sure this is done on the main thread before other threads are spawned. + // https://curl.haxx.se/libcurl/c/curl_easy_init.html + curl_global_init(CURL_GLOBAL_ALL); +} + + json_t* requestJson(Method method, std::string url, json_t* dataJ) { CURL* curl = createCurl(); char* reqStr = NULL; diff --git a/src/updater.cpp b/src/updater.cpp index 15a93fcc..42e6a10e 100644 --- a/src/updater.cpp +++ b/src/updater.cpp @@ -74,8 +74,19 @@ void update() { INFO("Launching update %s", path.c_str()); system::runProcessDetached(path); #elif defined ARCH_MAC - // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly. - std::string cmd = "open \"" + path + "\""; + std::string cmd; + // std::string appPath = asset::userDir + "/Rack.app"; + // cmd = "rm -rf '" + appPath + "'"; + // std::system(cmd.c_str()); + // // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly. + // cmd = "unzip -q '" + path + "' -d '" + asset::userDir + "'"; + // std::system(cmd.c_str()); + // // Open app in Finder + // cmd = "open -R '" + appPath + "'"; + // std::system(cmd.c_str()); + + // Open Archive Utility + cmd = "open '" + path + "'"; std::system(cmd.c_str()); #else system::openBrowser(downloadUrl); From b6fad51506db993cafd42355a9b5d3b9b0cf40ca Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 21 Sep 2019 17:03:10 -0400 Subject: [PATCH 05/29] Don't use GLU header --- include/window.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/window.hpp b/include/window.hpp index c9fa36d6..dbf4edcc 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -5,6 +5,7 @@ #include #include #define GLEW_STATIC +#define GLEW_NO_GLU #include #include #include From deba1974f1d6b876dc51b7dd3244afec200a63b0 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 21 Sep 2019 18:07:39 -0400 Subject: [PATCH 06/29] Don't use GLU in dep.cpp either. --- src/dep.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dep.cpp b/src/dep.cpp index 3a677cdd..9b4961ad 100644 --- a/src/dep.cpp +++ b/src/dep.cpp @@ -1,6 +1,7 @@ // This source file compiles those annoying implementation-in-header libraries #define GLEW_STATIC +#define GLEW_NO_GLU #include #include From 618454e7c07b83c392216be288030eb6266c9c5c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 22 Sep 2019 17:17:13 -0400 Subject: [PATCH 07/29] Stop engine wprker threads when engine is paused. --- CHANGELOG.md | 2 ++ src/engine/Engine.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eddb5de4..9b9f3416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. - Swap order of tags and brands in Module Browser. - Disable smoothing for MIDI CC buttons in MIDI-Map. - Automatically unzip update on Mac. +- Core + - Fix sustain pedal release bug when using polyphonic mode in MIDI-CV. - API - Add libsamplerate library. diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 82c22915..ee0e64b8 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -429,12 +429,12 @@ static void Engine_run(Engine* that) { aheadTime = 0.0; } - // Launch workers - if (internal->threadCount != settings::threadCount || internal->realTime != settings::realTime) { - Engine_relaunchWorkers(that, settings::threadCount, settings::realTime); - } - if (!internal->paused) { + // Launch workers + if (internal->threadCount != settings::threadCount || internal->realTime != settings::realTime) { + Engine_relaunchWorkers(that, settings::threadCount, settings::realTime); + } + std::lock_guard lock(internal->mutex); // Update expander pointers @@ -448,6 +448,12 @@ static void Engine_run(Engine* that) { Engine_step(that); } } + else { + // Stop workers while closed + if (internal->threadCount != 1) { + Engine_relaunchWorkers(that, 1, settings::realTime); + } + } double stepTime = mutexSteps * internal->sampleTime; aheadTime += stepTime; From 63a939d43203b1fa3215e63feaf24104fceba059 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 22 Sep 2019 19:32:59 -0400 Subject: [PATCH 08/29] Hide menu and scrollbars when fullscreen. --- CHANGELOG.md | 2 ++ src/app/RackScrollWidget.cpp | 15 ++++++++++++++- src/app/Scene.cpp | 5 ++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9f3416..d56335db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. - Swap order of tags and brands in Module Browser. - Disable smoothing for MIDI CC buttons in MIDI-Map. - Automatically unzip update on Mac. +- Stop worker threads when engine is paused to save CPU. +- Hide menu and scrollbars when fullscreen. - Core - Fix sustain pedal release bug when using polyphonic mode in MIDI-CV. - API diff --git a/src/app/RackScrollWidget.cpp b/src/app/RackScrollWidget.cpp index 0bd00cf0..da6e132a 100644 --- a/src/app/RackScrollWidget.cpp +++ b/src/app/RackScrollWidget.cpp @@ -77,8 +77,21 @@ void RackScrollWidget::step() { void RackScrollWidget::draw(const DrawArgs& args) { - // DEBUG("%f %f %f %f", RECT_ARGS(args.clipBox)); + // Hide scrollbars if full screen + bool fullscreen = APP->window->isFullScreen(); + bool horizontalVisible; + bool verticalVisible; + if (fullscreen) { + horizontalVisible = horizontalScrollBar->visible; + verticalVisible = verticalScrollBar->visible; + horizontalScrollBar->visible = false; + verticalScrollBar->visible = false; + } ScrollWidget::draw(args); + if (fullscreen) { + horizontalScrollBar->visible = horizontalVisible; + verticalScrollBar->visible = verticalVisible; + } } void RackScrollWidget::onHoverKey(const event::HoverKey& e) { diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 72c25f3e..a6021074 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -23,7 +23,6 @@ Scene::Scene() { menuBar = createMenuBar(); addChild(menuBar); - rackScroll->box.pos.y = menuBar->box.size.y; moduleBrowser = moduleBrowserCreate(); moduleBrowser->hide(); @@ -34,6 +33,10 @@ Scene::~Scene() { } void Scene::step() { + bool fullscreen = APP->window->isFullScreen(); + menuBar->visible = !fullscreen; + rackScroll->box.pos.y = menuBar->visible ? menuBar->box.size.y : 0; + // Resize owned descendants menuBar->box.size.x = box.size.x; rackScroll->box.size = box.size.minus(rackScroll->box.pos); From 3d21a2a7b25a110fcff58c3b3d38ff0d8cbb61d6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 24 Sep 2019 22:19:49 -0400 Subject: [PATCH 09/29] Add key command for engine CPU timer. --- CHANGELOG.md | 1 + src/app/MenuBar.cpp | 3 ++- src/app/Scene.cpp | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d56335db..e4b12e62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. - Automatically unzip update on Mac. - Stop worker threads when engine is paused to save CPU. - Hide menu and scrollbars when fullscreen. +- Add key command (F3) for engine CPU meter. - Core - Fix sustain pedal release bug when using polyphonic mode in MIDI-CV. - API diff --git a/src/app/MenuBar.cpp b/src/app/MenuBar.cpp index a2bb9500..5b7292cb 100644 --- a/src/app/MenuBar.cpp +++ b/src/app/MenuBar.cpp @@ -477,7 +477,8 @@ struct EngineButton : MenuButton { CpuMeterItem* cpuMeterItem = new CpuMeterItem; cpuMeterItem->text = "CPU meter"; - cpuMeterItem->rightText = CHECKMARK(settings::cpuMeter); + cpuMeterItem->rightText = "F3 "; + cpuMeterItem->rightText += CHECKMARK(settings::cpuMeter); menu->addChild(cpuMeterItem); SampleRateItem* sampleRateItem = new SampleRateItem; diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index a6021074..9cb699b4 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -148,6 +148,12 @@ void Scene::onHoverKey(const event::HoverKey& e) { e.consume(this); } } break; + case GLFW_KEY_F3: { + if ((e.mods & RACK_MOD_MASK) == 0) { + settings::cpuMeter ^= true; + e.consume(this); + } + } break; case GLFW_KEY_F11: { if ((e.mods & RACK_MOD_MASK) == 0) { APP->window->setFullScreen(!APP->window->isFullScreen()); From 69681d2dbd456c28aa8c32cdcac418fcfd7ae060 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 24 Sep 2019 22:42:15 -0400 Subject: [PATCH 10/29] Add numpad key commands. Clean up Scene::onHoverKey(). --- CHANGELOG.md | 1 + src/app/Scene.cpp | 166 +++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 96 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b12e62..4ca14cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. - Stop worker threads when engine is paused to save CPU. - Hide menu and scrollbars when fullscreen. - Add key command (F3) for engine CPU meter. +- Add numpad key commands. - Core - Fix sustain pedal release bug when using polyphonic mode in MIDI-CV. - API diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 9cb699b4..add47c34 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -64,102 +64,76 @@ void Scene::onHoverKey(const event::HoverKey& e) { return; if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { - switch (e.key) { - case GLFW_KEY_N: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - APP->patch->resetDialog(); - e.consume(this); - } - } break; - case GLFW_KEY_Q: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - APP->window->close(); - e.consume(this); - } - } break; - case GLFW_KEY_O: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - APP->patch->loadDialog(); - e.consume(this); - } - if ((e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { - APP->patch->revertDialog(); - e.consume(this); - } - } break; - case GLFW_KEY_S: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - APP->patch->saveDialog(); - e.consume(this); - } - if ((e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { - APP->patch->saveAsDialog(); - e.consume(this); - } - } break; - case GLFW_KEY_Z: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - APP->history->undo(); - e.consume(this); - } - if ((e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { - APP->history->redo(); - e.consume(this); - } - } break; - case GLFW_KEY_MINUS: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - float zoom = settings::zoom; - zoom *= 2; - zoom = std::ceil(zoom - 0.01f) - 1; - zoom /= 2; - settings::zoom = zoom; - e.consume(this); - } - } break; - case GLFW_KEY_EQUAL: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - float zoom = settings::zoom; - zoom *= 2; - zoom = std::floor(zoom + 0.01f) + 1; - zoom /= 2; - settings::zoom = zoom; - e.consume(this); - } - } break; - case GLFW_KEY_0: { - if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - settings::zoom = 0.f; - e.consume(this); - } - } break; - case GLFW_KEY_ENTER: { - if ((e.mods & RACK_MOD_MASK) == 0) { - moduleBrowser->show(); - } - e.consume(this); - } break; - case GLFW_KEY_F1: { - if ((e.mods & RACK_MOD_MASK) == 0) { - std::thread t([] { - system::openBrowser("https://vcvrack.com/manual/"); - }); - t.detach(); - e.consume(this); - } - } break; - case GLFW_KEY_F3: { - if ((e.mods & RACK_MOD_MASK) == 0) { - settings::cpuMeter ^= true; - e.consume(this); - } - } break; - case GLFW_KEY_F11: { - if ((e.mods & RACK_MOD_MASK) == 0) { - APP->window->setFullScreen(!APP->window->isFullScreen()); - e.consume(this); - } - } break; + if (e.key == GLFW_KEY_N && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->patch->resetDialog(); + e.consume(this); + } + else if (e.key == GLFW_KEY_Q && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->window->close(); + e.consume(this); + } + else if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->patch->loadDialog(); + e.consume(this); + } + else if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + APP->patch->revertDialog(); + e.consume(this); + } + else if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->patch->saveDialog(); + e.consume(this); + } + else if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + APP->patch->saveAsDialog(); + e.consume(this); + } + else if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->history->undo(); + e.consume(this); + } + else if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + APP->history->redo(); + e.consume(this); + } + else if ((e.key == GLFW_KEY_MINUS || e.key == GLFW_KEY_KP_SUBTRACT) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + float zoom = settings::zoom; + zoom *= 2; + zoom = std::ceil(zoom - 0.01f) - 1; + zoom /= 2; + settings::zoom = zoom; + e.consume(this); + } + else if ((e.key == GLFW_KEY_EQUAL || e.key == GLFW_KEY_KP_ADD) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + float zoom = settings::zoom; + zoom *= 2; + zoom = std::floor(zoom + 0.01f) + 1; + zoom /= 2; + settings::zoom = zoom; + e.consume(this); + } + else if ((e.key == GLFW_KEY_0 || e.key == GLFW_KEY_KP_0) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + settings::zoom = 0.f; + e.consume(this); + } + else if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) { + moduleBrowser->show(); + e.consume(this); + } + else if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) { + std::thread t([] { + system::openBrowser("https://vcvrack.com/manual/"); + }); + t.detach(); + e.consume(this); + } + else if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) { + settings::cpuMeter ^= true; + e.consume(this); + } + else if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) { + APP->window->setFullScreen(!APP->window->isFullScreen()); + e.consume(this); } } } From e59e8aca40f16c34f3d03b21db62dada30c04026 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 24 Sep 2019 23:17:30 -0400 Subject: [PATCH 11/29] Add sort-of-automatic updater on Linux. --- src/updater.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/updater.cpp b/src/updater.cpp index 42e6a10e..ed2fbf90 100644 --- a/src/updater.cpp +++ b/src/updater.cpp @@ -61,13 +61,11 @@ void update() { if (downloadUrl == "") return; -#if defined ARCH_WIN || defined ARCH_MAC // Download update std::string filename = string::filename(network::urlPath(downloadUrl)); std::string path = asset::user(filename); - INFO("Download update %s to %s", downloadUrl.c_str(), path.c_str()); + INFO("Downloading update %s to %s", downloadUrl.c_str(), path.c_str()); network::requestDownload(downloadUrl, path, &progress); -#endif #if defined ARCH_WIN // Launch the installer @@ -88,8 +86,8 @@ void update() { // Open Archive Utility cmd = "open '" + path + "'"; std::system(cmd.c_str()); -#else - system::openBrowser(downloadUrl); +#elif defined ARCH_LIN + system::openFolder(asset::user("")); #endif APP->window->close(); From ac5e544d62ce665b7425946718b3154a7e1bbebd Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Sep 2019 04:40:33 -0400 Subject: [PATCH 12/29] Change screenshot flag to -t [zoom]. Re-enable on Mac. --- src/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 47acb830..0954ae6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,14 +77,12 @@ int main(int argc, char* argv[]) { case 'h': { settings::headless = true; } break; -#if !defined ARCH_MAC // Due to Mac app translocation and Apple adding a -psn... flag when launched, disable screenshots on Mac for now. - case 'p': { + case 't': { screenshot = true; // If parsing number failed, use default value - sscanf(optarg, "%f", &screenshotZoom); + std::sscanf(optarg, "%f", &screenshotZoom); } break; -#endif case 's': { asset::systemDir = optarg; } break; From e631131aeafb4a8fe807400b725a86309b796ff4 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Sep 2019 05:38:11 -0400 Subject: [PATCH 13/29] Replace "frameRateLimit" and "frameRateSync" in settings with "frameSwapInterval". --- include/settings.hpp | 3 +-- src/app/Scene.cpp | 5 +++++ src/settings.cpp | 17 +++++------------ src/window.cpp | 34 ++++++++++++++++------------------ 4 files changed, 27 insertions(+), 32 deletions(-) diff --git a/include/settings.hpp b/include/settings.hpp index 767e0cd8..a2fbe86f 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -34,8 +34,7 @@ extern int threadCount; extern bool paramTooltip; extern bool cpuMeter; extern bool lockModules; -extern float frameRateLimit; -extern bool frameRateSync; +extern int frameSwapInterval; extern float autosavePeriod; extern bool skipLoadOnLaunch; extern std::string patchPath; diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index add47c34..25fc7d2e 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -56,6 +56,11 @@ void Scene::step() { void Scene::draw(const DrawArgs& args) { Widget::draw(args); + + // nvgBeginPath(args.vg); + // nvgRect(args.vg, 0, 0, box.size.x, box.size.y); + // nvgFillColor(args.vg, color::mult(color::WHITE, APP->window->frame % 2)); + // nvgFill(args.vg); } void Scene::onHoverKey(const event::HoverKey& e) { diff --git a/src/settings.cpp b/src/settings.cpp index 954a0952..eeb1ffc4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -29,8 +29,7 @@ int threadCount = 1; bool paramTooltip = false; bool cpuMeter = false; bool lockModules = false; -float frameRateLimit = 70.0; -bool frameRateSync = true; +int frameSwapInterval = 1; float autosavePeriod = 15.0; bool skipLoadOnLaunch = false; std::string patchPath; @@ -75,9 +74,7 @@ json_t* toJson() { json_object_set_new(rootJ, "lockModules", json_boolean(lockModules)); - json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit)); - - json_object_set_new(rootJ, "frameRateSync", json_boolean(frameRateSync)); + json_object_set_new(rootJ, "frameSwapInterval", json_integer(frameSwapInterval)); json_object_set_new(rootJ, "autosavePeriod", json_real(autosavePeriod)); @@ -160,13 +157,9 @@ void fromJson(json_t* rootJ) { if (lockModulesJ) lockModules = json_boolean_value(lockModulesJ); - json_t* frameRateLimitJ = json_object_get(rootJ, "frameRateLimit"); - if (frameRateLimitJ) - frameRateLimit = json_number_value(frameRateLimitJ); - - json_t* frameRateSyncJ = json_object_get(rootJ, "frameRateSync"); - if (frameRateSyncJ) - frameRateSync = json_boolean_value(frameRateSyncJ); + json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval"); + if (frameSwapIntervalJ) + frameSwapInterval = json_integer_value(frameSwapIntervalJ); json_t* autosavePeriodJ = json_object_get(rootJ, "autosavePeriod"); if (autosavePeriodJ) diff --git a/src/window.cpp b/src/window.cpp index 3cab776f..c40fa126 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -95,6 +95,7 @@ struct Window::Internal { int lastWindowHeight = 0; bool ignoreNextMouseDelta = false; + int frameSwapInterval = -1; }; @@ -248,8 +249,7 @@ Window::Window() { glfwSetInputMode(win, GLFW_LOCK_KEY_MODS, 1); glfwMakeContextCurrent(win); - // Enable v-sync - glfwSwapInterval(settings::frameRateSync ? 1 : 0); + glfwSwapInterval(1); // Set window callbacks glfwSetWindowSizeCallback(win, windowSizeCallback); @@ -333,8 +333,14 @@ void Window::run() { // Poll events glfwPollEvents(); - // In case glfwPollEvents() set another OpenGL context + + // In case glfwPollEvents() sets another OpenGL context glfwMakeContextCurrent(win); + if (settings::frameSwapInterval != internal->frameSwapInterval) { + internal->frameSwapInterval = settings::frameSwapInterval; + glfwSwapInterval(settings::frameSwapInterval); + } + // Call cursorPosCallback every frame, not just when the mouse moves { double xpos, ypos; @@ -396,23 +402,13 @@ void Window::run() { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); nvgEndFrame(vg); - - glfwSwapBuffers(win); } - // Limit frame rate - double frameTimeEnd = glfwGetTime(); - if (settings::frameRateLimit > 0.0) { - double frameDuration = frameTimeEnd - frameTimeStart; - double waitDuration = 1.0 / settings::frameRateLimit - frameDuration; - if (waitDuration > 0.0) { - std::this_thread::sleep_for(std::chrono::duration(waitDuration)); - } - } + glfwSwapBuffers(win); // Compute actual frame rate - frameTimeEnd = glfwGetTime(); - // DEBUG("%g fps", 1 / (endTime - startTime)); + double frameTimeEnd = glfwGetTime(); + DEBUG("%g fps", 1 / (frameTimeEnd - frameTimeStart)); frame++; } } @@ -523,10 +519,12 @@ bool Window::isFullScreen() { } bool Window::isFrameOverdue() { - if (settings::frameRateLimit == 0.0) + if (settings::frameSwapInterval == 0) return false; double frameDuration = glfwGetTime() - frameTimeStart; - return frameDuration > 1.0 / settings::frameRateLimit; + // This is fudged a bit because it assumes the monitor refresh rate is 60 Hz. + double frameDeadline = settings::frameSwapInterval / 60.0; + return frameDuration > frameDeadline; } std::shared_ptr Window::loadFont(const std::string& filename) { From 7b0ce9740609cfc492f4de69f2986d7254c5d06c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Sep 2019 06:01:23 -0400 Subject: [PATCH 14/29] Add Engine > Frame rate menu bar item. --- CHANGELOG.md | 7 ++++--- src/app/MenuBar.cpp | 28 ++++++++++++++++++++++++++++ src/app/Scene.cpp | 5 ----- src/window.cpp | 8 ++++---- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca14cfe..787ab181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,14 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. ### 1.1.5 (in development) - Swap order of tags and brands in Module Browser. -- Disable smoothing for MIDI CC buttons in MIDI-Map. -- Automatically unzip update on Mac. -- Stop worker threads when engine is paused to save CPU. +- Add Engine > Frame rate menu bar item. - Hide menu and scrollbars when fullscreen. - Add key command (F3) for engine CPU meter. - Add numpad key commands. +- Automatically unzip update on Mac. +- Stop worker threads when engine is paused to save CPU. - Core + - Disable smoothing for MIDI CC buttons in MIDI-Map. - Fix sustain pedal release bug when using polyphonic mode in MIDI-CV. - API - Add libsamplerate library. diff --git a/src/app/MenuBar.cpp b/src/app/MenuBar.cpp index 5b7292cb..3ac41eef 100644 --- a/src/app/MenuBar.cpp +++ b/src/app/MenuBar.cpp @@ -327,6 +327,30 @@ struct CursorLockItem : ui::MenuItem { } }; +struct FrameRateValueItem : ui::MenuItem { + int frameSwapInterval; + void onAction(const event::Action& e) override { + settings::frameSwapInterval = frameSwapInterval; + } +}; + +struct FrameRateItem : ui::MenuItem { + ui::Menu* createChildMenu() override { + ui::Menu* menu = new ui::Menu; + + for (int i = 1; i <= 6; i++) { + float frameRate = 60.f / i; + + FrameRateValueItem* item = new FrameRateValueItem; + item->frameSwapInterval = i; + item->text = string::f("%.0f Hz", frameRate); + item->rightText += CHECKMARK(settings::frameSwapInterval == i); + menu->addChild(item); + } + return menu; + } +}; + struct FullscreenItem : ui::MenuItem { void onAction(const event::Action& e) override { APP->window->setFullScreen(!APP->window->isFullScreen()); @@ -366,6 +390,10 @@ struct ViewButton : MenuButton { cableTensionSlider->box.size.x = 200.0; menu->addChild(cableTensionSlider); + FrameRateItem* frameRateItem = new FrameRateItem; + frameRateItem->text = "Frame rate"; + menu->addChild(frameRateItem); + FullscreenItem* fullscreenItem = new FullscreenItem; fullscreenItem->text = "Fullscreen"; fullscreenItem->rightText = "F11"; diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 25fc7d2e..add47c34 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -56,11 +56,6 @@ void Scene::step() { void Scene::draw(const DrawArgs& args) { Widget::draw(args); - - // nvgBeginPath(args.vg); - // nvgRect(args.vg, 0, 0, box.size.x, box.size.y); - // nvgFillColor(args.vg, color::mult(color::WHITE, APP->window->frame % 2)); - // nvgFill(args.vg); } void Scene::onHoverKey(const event::HoverKey& e) { diff --git a/src/window.cpp b/src/window.cpp index c40fa126..d58b198d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -325,7 +325,10 @@ Window::~Window() { void Window::run() { frame = 0; while (!glfwWindowShouldClose(win)) { - frameTimeStart = glfwGetTime(); + double frameTime = glfwGetTime(); + // double frameRate = 1.0 / (frameTime - frameTimeStart); + // DEBUG("%g fps", frameRate); + frameTimeStart = frameTime; // Make event handlers and step() have a clean nanovg context nvgReset(vg); @@ -406,9 +409,6 @@ void Window::run() { glfwSwapBuffers(win); - // Compute actual frame rate - double frameTimeEnd = glfwGetTime(); - DEBUG("%g fps", 1 / (frameTimeEnd - frameTimeStart)); frame++; } } From ee876efa88b261220956cb01cb1ff57676fc7fb6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Sep 2019 06:13:49 -0400 Subject: [PATCH 15/29] Use actual monitor refresh rate instead of 60 Hz as frame rate reference. --- include/window.hpp | 1 + src/app/MenuBar.cpp | 4 ++-- src/window.cpp | 10 ++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/window.hpp b/include/window.hpp index dbf4edcc..ad639b24 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -90,6 +90,7 @@ struct Window { void setFullScreen(bool fullScreen); bool isFullScreen(); bool isFrameOverdue(); + double getMonitorRefreshRate(); std::shared_ptr loadFont(const std::string& filename); std::shared_ptr loadImage(const std::string& filename); diff --git a/src/app/MenuBar.cpp b/src/app/MenuBar.cpp index 3ac41eef..f351c989 100644 --- a/src/app/MenuBar.cpp +++ b/src/app/MenuBar.cpp @@ -339,11 +339,11 @@ struct FrameRateItem : ui::MenuItem { ui::Menu* menu = new ui::Menu; for (int i = 1; i <= 6; i++) { - float frameRate = 60.f / i; + double frameRate = APP->window->getMonitorRefreshRate() / i; FrameRateValueItem* item = new FrameRateValueItem; item->frameSwapInterval = i; - item->text = string::f("%.0f Hz", frameRate); + item->text = string::f("%.0lf Hz", frameRate); item->rightText += CHECKMARK(settings::frameSwapInterval == i); menu->addChild(item); } diff --git a/src/window.cpp b/src/window.cpp index d58b198d..41b75ac9 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -96,6 +96,7 @@ struct Window::Internal { bool ignoreNextMouseDelta = false; int frameSwapInterval = -1; + double monitorRefreshRate = -1; }; @@ -250,6 +251,8 @@ Window::Window() { glfwMakeContextCurrent(win); glfwSwapInterval(1); + const GLFWvidmode* monitorMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + internal->monitorRefreshRate = monitorMode->refreshRate; // Set window callbacks glfwSetWindowSizeCallback(win, windowSizeCallback); @@ -522,11 +525,14 @@ bool Window::isFrameOverdue() { if (settings::frameSwapInterval == 0) return false; double frameDuration = glfwGetTime() - frameTimeStart; - // This is fudged a bit because it assumes the monitor refresh rate is 60 Hz. - double frameDeadline = settings::frameSwapInterval / 60.0; + double frameDeadline = settings::frameSwapInterval / internal->monitorRefreshRate; return frameDuration > frameDeadline; } +double Window::getMonitorRefreshRate() { + return internal->monitorRefreshRate; +} + std::shared_ptr Window::loadFont(const std::string& filename) { auto sp = fontCache[filename].lock(); if (!sp) { From d0c92852f91d9b9b7f73cf87bb58897f3e1ee4d4 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Sep 2019 06:33:09 -0400 Subject: [PATCH 16/29] Bump version. --- Core.json | 2 +- Makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core.json b/Core.json index b09cd51f..0b97ed89 100644 --- a/Core.json +++ b/Core.json @@ -1,7 +1,7 @@ { "slug": "Core", "name": "Core", - "version": "1.1.4", + "version": "1.1.5", "license": "GPL-3.0-only", "author": "VCV", "brand": "VCV", diff --git a/Makefile b/Makefile index bef63c93..01d8f4f2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ RACK_DIR ?= . -VERSION := 1.dev.$(shell git rev-parse --short HEAD) -# VERSION := 1.1.4 +# VERSION := 1.dev.$(shell git rev-parse --short HEAD) +VERSION := 1.1.5 FLAGS += -DVERSION=$(VERSION) FLAGS += -Iinclude -Idep/include From 847682845202de90f4068ef67e0425496a3b1ae9 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 26 Sep 2019 09:19:49 -0400 Subject: [PATCH 17/29] Add LEDLightSlider and other component library classes. --- include/componentlibrary.hpp | 333 ++++++++++++------ include/helpers.hpp | 15 + res/ComponentLibrary/LEDSliderHandle.svg | 66 ++++ res/ComponentLibrary/LEDSliderHorizontal.svg | 71 ++++ .../LEDSliderHorizontalHandle.svg | 66 ++++ 5 files changed, 448 insertions(+), 103 deletions(-) create mode 100644 res/ComponentLibrary/LEDSliderHandle.svg create mode 100644 res/ComponentLibrary/LEDSliderHorizontal.svg create mode 100644 res/ComponentLibrary/LEDSliderHorizontalHandle.svg diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index 6cb66dd3..c5071dae 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -39,6 +39,174 @@ static const NVGcolor SCHEME_PURPLE = nvgRGB(0xd5, 0x2b, 0xed); static const NVGcolor SCHEME_LIGHT_GRAY = nvgRGB(0xe6, 0xe6, 0xe6); static const NVGcolor SCHEME_DARK_GRAY = nvgRGB(0x17, 0x17, 0x17); + +//////////////////// +// Lights +//////////////////// + +/* +Many of these classes use CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). + +To use a red light with its default base class for example, use `RedLight` or `TRedLight<>`. (They are synonymous.) + +Use the `TBase` template argument if you want a different base class. +E.g. `RectangleLight` + +Although this paradigm might seem confusing at first, it ends up being extremely simple in your plugin code and perfect for "decorating" your classes with appearance traits and behavioral properties. +For example, need a slider with a green LED? Just use + + createLightParamCentered>(...) +*/ + +template +struct TGrayModuleLightWidget : TBase { + TGrayModuleLightWidget() { + this->bgColor = nvgRGB(0x5a, 0x5a, 0x5a); + this->borderColor = nvgRGBA(0, 0, 0, 0x60); + } +}; +typedef TGrayModuleLightWidget<> GrayModuleLightWidget; + +template +struct TRedLight : TBase { + TRedLight() { + this->addBaseColor(SCHEME_RED); + } +}; +typedef TRedLight<> RedLight; + +template +struct TGreenLight : TBase { + TGreenLight() { + this->addBaseColor(SCHEME_GREEN); + } +}; +typedef TGreenLight<> GreenLight; + +template +struct TYellowLight : TBase { + TYellowLight() { + this->addBaseColor(SCHEME_YELLOW); + } +}; +typedef TYellowLight<> YellowLight; + +template +struct TBlueLight : TBase { + TBlueLight() { + this->addBaseColor(SCHEME_BLUE); + } +}; +typedef TBlueLight<> BlueLight; + +template +struct TWhiteLight : TBase { + TWhiteLight() { + this->addBaseColor(SCHEME_WHITE); + } +}; +typedef TWhiteLight<> WhiteLight; + +/** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ +template +struct TGreenRedLight : TBase { + TGreenRedLight() { + this->addBaseColor(SCHEME_GREEN); + this->addBaseColor(SCHEME_RED); + } +}; +typedef TGreenRedLight<> GreenRedLight; + +template +struct TRedGreenBlueLight : TBase { + TRedGreenBlueLight() { + this->addBaseColor(SCHEME_RED); + this->addBaseColor(SCHEME_GREEN); + this->addBaseColor(SCHEME_BLUE); + } +}; +typedef TRedGreenBlueLight<> RedGreenBlueLight; + +/** Based on the size of 5mm LEDs */ +template +struct LargeLight : TBase { + LargeLight() { + this->box.size = app::mm2px(math::Vec(5.179, 5.179)); + } +}; + +/** Based on the size of 3mm LEDs */ +template +struct MediumLight : TBase { + MediumLight() { + this->box.size = app::mm2px(math::Vec(3.176, 3.176)); + } +}; + +/** Based on the size of 2mm LEDs */ +template +struct SmallLight : TBase { + SmallLight() { + this->box.size = app::mm2px(math::Vec(2.176, 2.176)); + } +}; + +/** Based on the size of 1mm LEDs */ +template +struct TinyLight : TBase { + TinyLight() { + this->box.size = app::mm2px(math::Vec(1.088, 1.088)); + } +}; + +template +struct RectangleLight : TBase { + void drawLight(const widget::Widget::DrawArgs& args) override { + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, this->box.size.x, this->box.size.y); + + // Background + if (this->bgColor.a > 0.0) { + nvgFillColor(args.vg, this->bgColor); + nvgFill(args.vg); + } + + // Foreground + if (this->color.a > 0.0) { + nvgFillColor(args.vg, this->color); + nvgFill(args.vg); + } + + // Border + if (this->borderColor.a > 0.0) { + nvgStrokeWidth(args.vg, 0.5); + nvgStrokeColor(args.vg, this->borderColor); + nvgStroke(args.vg); + } + } +}; + +/** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ +template +struct LEDBezelLight : TBase { + LEDBezelLight() { + this->bgColor = color::BLACK_TRANSPARENT; + this->box.size = app::mm2px(math::Vec(6.0, 6.0)); + } +}; + +/** A light to displayed over PB61303. Must add a color by subclassing or templating. +Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by app::mm2px(math::Vec(0.5, 0.5)). +*/ +template +struct PB61303Light : TBase { + PB61303Light() { + this->bgColor = color::BLACK_TRANSPARENT; + this->box.size = app::mm2px(math::Vec(9.0, 9.0)); + } +}; + + //////////////////// // Knobs //////////////////// @@ -390,134 +558,75 @@ struct LEDSliderWhite : LEDSlider { } }; -//////////////////// -// Ports -//////////////////// - -struct PJ301MPort : app::SvgPort { - PJ301MPort() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ301M.svg"))); +struct LEDSliderHorizontal : app::SvgSlider { + LEDSliderHorizontal() { + horizontal = true; + maxHandlePos = app::mm2px(math::Vec(22.078, 0.738).plus(math::Vec(0, 2))); + minHandlePos = app::mm2px(math::Vec(0.738, 0.738).plus(math::Vec(0, 2))); + setBackgroundSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHorizontal.svg"))); } }; -struct PJ3410Port : app::SvgPort { - PJ3410Port() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ3410.svg"))); - } -}; +template +struct LightSlider : TBase { + app::ModuleLightWidget* light; -struct CL1362Port : app::SvgPort { - CL1362Port() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/CL1362.svg"))); + LightSlider() { + light = new RectangleLight; + this->addChild(light); } -}; - -//////////////////// -// Lights -//////////////////// -struct GrayModuleLightWidget : app::ModuleLightWidget { - GrayModuleLightWidget() { - bgColor = nvgRGB(0x5a, 0x5a, 0x5a); - borderColor = nvgRGBA(0, 0, 0, 0x60); + void setFirstLightId(int firstLightId) { + if (this->paramQuantity) + light->module = this->paramQuantity->module; + light->firstLightId = firstLightId; } -}; - -struct RedLight : GrayModuleLightWidget { - RedLight() { - addBaseColor(SCHEME_RED); - } -}; - -struct GreenLight : GrayModuleLightWidget { - GreenLight() { - addBaseColor(SCHEME_GREEN); - } -}; - -struct YellowLight : GrayModuleLightWidget { - YellowLight() { - addBaseColor(SCHEME_YELLOW); - } -}; -struct BlueLight : GrayModuleLightWidget { - BlueLight() { - addBaseColor(SCHEME_BLUE); + void step() override { + TBase::step(); + // Move center of light to center of handle + light->box.pos = this->handle->box.pos + .plus(this->handle->box.size.div(2)) + .minus(light->box.size.div(2)); } }; -struct WhiteLight : GrayModuleLightWidget { - WhiteLight() { - addBaseColor(SCHEME_WHITE); +template +struct LEDLightSlider : LightSlider { + LEDLightSlider() { + this->setHandleSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHandle.svg"))); + this->light->box.size = app::mm2px(math::Vec(1.524, 3.276)); } }; -/** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ -struct GreenRedLight : GrayModuleLightWidget { - GreenRedLight() { - addBaseColor(SCHEME_GREEN); - addBaseColor(SCHEME_RED); - } -}; - -struct RedGreenBlueLight : GrayModuleLightWidget { - RedGreenBlueLight() { - addBaseColor(SCHEME_RED); - addBaseColor(SCHEME_GREEN); - addBaseColor(SCHEME_BLUE); - } -}; - -/** Based on the size of 5mm LEDs */ -template -struct LargeLight : BASE { - LargeLight() { - this->box.size = app::mm2px(math::Vec(5.179, 5.179)); +template +struct LEDLightSliderHorizontal : LightSlider { + LEDLightSliderHorizontal() { + this->setHandleSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHorizontalHandle.svg"))); + this->light->box.size = app::mm2px(math::Vec(3.276, 1.524)); } }; -/** Based on the size of 3mm LEDs */ -template -struct MediumLight : BASE { - MediumLight() { - this->box.size = app::mm2px(math::Vec(3.176, 3.176)); - } -}; -/** Based on the size of 2mm LEDs */ -template -struct SmallLight : BASE { - SmallLight() { - this->box.size = app::mm2px(math::Vec(2.176, 2.176)); - } -}; +//////////////////// +// Ports +//////////////////// -/** Based on the size of 1mm LEDs */ -template -struct TinyLight : BASE { - TinyLight() { - this->box.size = app::mm2px(math::Vec(1.088, 1.088)); +struct PJ301MPort : app::SvgPort { + PJ301MPort() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ301M.svg"))); } }; -/** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ -template -struct LEDBezelLight : BASE { - LEDBezelLight() { - this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = app::mm2px(math::Vec(6.0, 6.0)); +struct PJ3410Port : app::SvgPort { + PJ3410Port() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ3410.svg"))); } }; -/** A light to displayed over PB61303. Must add a color by subclassing or templating. -Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by app::mm2px(math::Vec(0.5, 0.5)). -*/ -template -struct PB61303Light : BASE { - PB61303Light() { - this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = app::mm2px(math::Vec(9.0, 9.0)); +struct CL1362Port : app::SvgPort { + CL1362Port() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/CL1362.svg"))); } }; @@ -595,6 +704,24 @@ struct LEDBezel : app::SvgSwitch { } }; +template +struct LEDLightBezel : LEDBezel { + app::ModuleLightWidget* light; + + LEDLightBezel() { + light = new LEDBezelLight; + // Move center of light to center of box + light->box.pos = box.size.div(2).minus(light->box.size.div(2)); + addChild(light); + } + + void setFirstLightId(int firstLightId) { + if (paramQuantity) + light->module = paramQuantity->module; + light->firstLightId = firstLightId; + } +}; + struct PB61303 : app::SvgSwitch { PB61303() { momentary = true; diff --git a/include/helpers.hpp b/include/helpers.hpp index fb26c1c3..ebde5c3a 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -124,6 +124,21 @@ TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, i return o; } +/** Creates a param with a light and calls setFirstLightId() on it. */ +template +TParamWidget* createLightParam(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { + TParamWidget* o = createParam(pos, module, paramId); + o->setFirstLightId(firstLightId); + return o; +} + +template +TParamWidget* createLightParamCentered(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { + TParamWidget* o = createParamCentered(pos, module, paramId); + o->setFirstLightId(firstLightId); + return o; +} + template TMenuLabel * createMenuLabel(std::string text) { TMenuLabel* o = new TMenuLabel; diff --git a/res/ComponentLibrary/LEDSliderHandle.svg b/res/ComponentLibrary/LEDSliderHandle.svg new file mode 100644 index 00000000..737514c0 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHandle.svg @@ -0,0 +1,66 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderHorizontal.svg b/res/ComponentLibrary/LEDSliderHorizontal.svg new file mode 100644 index 00000000..fac8fc37 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHorizontal.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderHorizontalHandle.svg b/res/ComponentLibrary/LEDSliderHorizontalHandle.svg new file mode 100644 index 00000000..3673923f --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHorizontalHandle.svg @@ -0,0 +1,66 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + From f68a981d11ef6be2a491c60ef6e45d0b21371640 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 26 Sep 2019 10:25:30 -0400 Subject: [PATCH 18/29] Add SegmentDisplay to component library. Fix huge light halo issue with non-square lights. --- include/componentlibrary.hpp | 54 ++++++++++++++++++++++++++++++++++++ src/app/LightWidget.cpp | 4 +-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index c5071dae..888fa3fd 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -635,6 +635,20 @@ struct CL1362Port : app::SvgPort { // Switches //////////////////// +template +struct LatchingSwitch : TSwitch { + LatchingSwitch() { + this->momentary = false; + } +}; + +template +struct MomentarySwitch : TSwitch { + MomentarySwitch() { + this->momentary = true; + } +}; + struct NKK : app::SvgSwitch { NKK() { addFrame(APP->window->loadSvg(asset::system("res/ComponentLibrary/NKK_0.svg"))); @@ -745,6 +759,46 @@ struct ScrewBlack : app::SvgScrew { } }; +struct SegmentDisplay : widget::Widget { + int lightsLen = 0; + bool vertical = false; + float margin = app::mm2px(0.5); + + void draw(const DrawArgs& args) override { + // Background + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, box.size.x, box.size.y); + nvgFillColor(args.vg, color::BLACK); + nvgFill(args.vg); + Widget::draw(args); + } + + template + void setLights(engine::Module* module, int firstLightId, int lightsLen) { + clearChildren(); + this->lightsLen = lightsLen; + float r = (vertical ? box.size.y : box.size.x) - margin; + for (int i = 0; i < lightsLen; i++) { + float p = float(i) / lightsLen; + app::ModuleLightWidget* light = new RectangleLight; + if (vertical) { + light->box.pos.y = p * r + margin; + light->box.size.y = r / lightsLen - margin; + light->box.size.x = box.size.x; + } + else { + light->box.pos.x = p * r + margin; + light->box.size.x = r / lightsLen - margin; + light->box.size.y = box.size.y; + } + light->module = module; + light->firstLightId = firstLightId; + firstLightId += light->baseColors.size(); + addChild(light); + } + } +}; + } // namespace componentlibrary } // namespace rack diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 2cf46933..3df6dce8 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -12,7 +12,7 @@ void LightWidget::draw(const DrawArgs& args) { } void LightWidget::drawLight(const DrawArgs& args) { - float radius = box.size.x / 2.0; + float radius = std::min(box.size.x, box.size.y) / 2.0; nvgBeginPath(args.vg); nvgCircle(args.vg, radius, radius, radius); @@ -38,7 +38,7 @@ void LightWidget::drawLight(const DrawArgs& args) { } void LightWidget::drawHalo(const DrawArgs& args) { - float radius = box.size.x / 2.0; + float radius = std::min(box.size.x, box.size.y) / 2.0; float oradius = 4.0 * radius; nvgBeginPath(args.vg); From 7390c4a8402779c2812cd247dad86d4904776f78 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 29 Sep 2019 12:05:37 -0400 Subject: [PATCH 19/29] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 787ab181..edb6d839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. -### 1.1.5 (in development) +### 1.1.5 (2019-09-29) - Swap order of tags and brands in Module Browser. - Add Engine > Frame rate menu bar item. - Hide menu and scrollbars when fullscreen. From bf2d98ab8b89dbfe82d881bacdcee56d18f5162f Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 29 Sep 2019 14:10:20 -0400 Subject: [PATCH 20/29] Fix "Frame rate" typo in changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb6d839..baffbaa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. ### 1.1.5 (2019-09-29) - Swap order of tags and brands in Module Browser. -- Add Engine > Frame rate menu bar item. +- Add View > Frame rate menu bar item. - Hide menu and scrollbars when fullscreen. - Add key command (F3) for engine CPU meter. - Add numpad key commands. From 96e623bb70f7325f05f39bfc3c31afc1f12f19df Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 30 Sep 2019 09:29:13 -0400 Subject: [PATCH 21/29] Fix screenshot flag -t --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0954ae6b..6ca694a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) { // Parse command line arguments int c; opterr = 0; - while ((c = getopt(argc, argv, "dhp:s:u:")) != -1) { + while ((c = getopt(argc, argv, "dht:s:u:")) != -1) { switch (c) { case 'd': { settings::devMode = true; @@ -77,7 +77,6 @@ int main(int argc, char* argv[]) { case 'h': { settings::headless = true; } break; - // Due to Mac app translocation and Apple adding a -psn... flag when launched, disable screenshots on Mac for now. case 't': { screenshot = true; // If parsing number failed, use default value From 3a05c02384fe219f129a8bdc1f2eeb3d00c421cb Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 30 Sep 2019 09:31:49 -0400 Subject: [PATCH 22/29] Switch to dev version. --- CHANGELOG.md | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baffbaa5..b4303b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. +### ??? (in development) + + ### 1.1.5 (2019-09-29) - Swap order of tags and brands in Module Browser. - Add View > Frame rate menu bar item. diff --git a/Makefile b/Makefile index 01d8f4f2..262f366a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ RACK_DIR ?= . -# VERSION := 1.dev.$(shell git rev-parse --short HEAD) -VERSION := 1.1.5 +VERSION := 1.dev.$(shell git rev-parse --short HEAD) +# VERSION := 1.1.5 FLAGS += -DVERSION=$(VERSION) FLAGS += -Iinclude -Idep/include From 82098f21c44cc2bc7478ffeb82c11acdf5ee37e6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 30 Sep 2019 12:45:39 -0400 Subject: [PATCH 23/29] Core MIDI-Map: Only set param if CC is initialized. --- src/core/MIDI_Map.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/MIDI_Map.cpp b/src/core/MIDI_Map.cpp index 111d964e..5d703c94 100644 --- a/src/core/MIDI_Map.cpp +++ b/src/core/MIDI_Map.cpp @@ -92,7 +92,7 @@ struct MIDI_Map : Module { Module* module = paramHandles[id].module; if (!module) continue; - // Get ParamQuantity + // Get ParamQuantity from ParamHandle int paramId = paramHandles[id].paramId; ParamQuantity* paramQuantity = module->paramQuantities[paramId]; if (!paramQuantity) @@ -105,7 +105,9 @@ struct MIDI_Map : Module { filterInitialized[id] = true; continue; } - // Set param if value has been initialized + // Check if CC has been set by the MIDI device + if (values[cc] < 0) + continue; float value = values[cc] / 127.f; // Detect behavior from MIDI buttons. if (std::fabs(valueFilters[id].out - value) >= 1.f) { From dbd117ea62a1529bc43fd74c4484d1369f0e4691 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 30 Sep 2019 16:06:34 -0400 Subject: [PATCH 24/29] Include rack:: namespace in APP and logger macros (DEBUG, INFO, WARN, FATAL). --- include/app.hpp | 2 +- include/logger.hpp | 8 ++++---- include/rack.hpp | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index 3304b3db..84fd5760 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -49,7 +49,7 @@ void appDestroy(); App* appGet(); /** Accesses the global App pointer */ -#define APP appGet() +#define APP rack::appGet() } // namespace rack diff --git a/include/logger.hpp b/include/logger.hpp index 9e94bfea..cf356019 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -9,10 +9,10 @@ will print something like [0.123 debug myfile.cpp:45] error: 67 */ -#define DEBUG(format, ...) logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) -#define INFO(format, ...) logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) -#define WARN(format, ...) logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) -#define FATAL(format, ...) logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define DEBUG(format, ...) rack::logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define INFO(format, ...) rack::logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define WARN(format, ...) rack::logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define FATAL(format, ...) rack::logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) namespace rack { diff --git a/include/rack.hpp b/include/rack.hpp index 59f03910..eaec8ab3 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -112,6 +112,9 @@ namespace rack { using plugin::Model; using namespace engine; using namespace componentlibrary; + + // Import namespace recursively to solve the problem of calling `rack::DEBUG(...)` which expands to `rack::rack::logger(...)`. + namespace rack = rack; #endif From e248cc88135c36e5a27e71111f5b5e235c376517 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 1 Oct 2019 21:40:04 -0400 Subject: [PATCH 25/29] Disable crash warning dialog in dev mode. --- src/patch.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/patch.cpp b/src/patch.cpp index e67251b4..7f793332 100644 --- a/src/patch.cpp +++ b/src/patch.cpp @@ -33,14 +33,16 @@ void PatchManager::init(std::string path) { return; } - // 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 oldSkipLoadOnLaunch = settings::skipLoadOnLaunch; - settings::skipLoadOnLaunch = true; - settings::save(asset::settingsPath); - settings::skipLoadOnLaunch = false; - if (oldSkipLoadOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) { - this->path = ""; - return; + if (!settings::devMode) { + // 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 oldSkipLoadOnLaunch = settings::skipLoadOnLaunch; + settings::skipLoadOnLaunch = true; + settings::save(asset::settingsPath); + settings::skipLoadOnLaunch = false; + if (oldSkipLoadOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) { + this->path = ""; + return; + } } // Load autosave From 3f9b22e5b4e763fbbce17b35207fa29134b28772 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 2 Oct 2019 08:56:28 -0400 Subject: [PATCH 26/29] Explicitly specify RTLD_LOCAL for plugin loading. --- src/plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 91c0d0fd..c0090db7 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -71,7 +71,7 @@ static InitCallback loadLibrary(Plugin* plugin) { throw UserException(string::f("Failed to load library %s: code %d", libraryFilename.c_str(), error)); } #else - void* handle = dlopen(libraryFilename.c_str(), RTLD_NOW); + void* handle = dlopen(libraryFilename.c_str(), RTLD_NOW | RTLD_LOCAL); if (!handle) { throw UserException(string::f("Failed to load library %s: %s", libraryFilename.c_str(), dlerror())); } From 08f97d24f14a075c7b5388f830cc4459330e2d6d Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 2 Oct 2019 11:02:59 -0400 Subject: [PATCH 27/29] Nudge ParamTooltip inside parent. --- src/app/ParamWidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index 1127f192..50a6691f 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -72,6 +72,9 @@ struct ParamTooltip : ui::Tooltip { Tooltip::step(); // Position at bottom-right of parameter box.pos = paramWidget->getAbsoluteOffset(paramWidget->box.size).round(); + // Fit inside parent (copied from Tooltip.cpp) + assert(parent); + box = box.nudge(parent->box.zeroPos()); } }; From 966cf1c5b4a3ee0768a923437d132d09df6efcc8 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 3 Oct 2019 15:48:33 -0400 Subject: [PATCH 28/29] Remove RACK_FLATTEN_NAMESPACES since it is not known to be used. --- include/rack.hpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/include/rack.hpp b/include/rack.hpp index eaec8ab3..9771e064 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -100,22 +100,19 @@ namespace rack { -/** Define this macro before including this header to prevent common namespaces from being included in the main `rack::` namespace. */ -#ifndef RACK_FLATTEN_NAMESPACES - // Import some namespaces for convenience - using namespace logger; - using namespace math; - using namespace widget; - using namespace ui; - using namespace app; - using plugin::Plugin; - using plugin::Model; - using namespace engine; - using namespace componentlibrary; +// Import some namespaces for convenience +using namespace logger; +using namespace math; +using namespace widget; +using namespace ui; +using namespace app; +using plugin::Plugin; +using plugin::Model; +using namespace engine; +using namespace componentlibrary; - // Import namespace recursively to solve the problem of calling `rack::DEBUG(...)` which expands to `rack::rack::logger(...)`. - namespace rack = rack; -#endif +// Import namespace recursively to solve the problem of calling `rack::DEBUG(...)` which expands to `rack::rack::logger(...)`. +namespace rack = rack; } // namespace rack From f8ef68c4f1ab22abfc80d7f7f784a7029081f510 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 7 Oct 2019 16:18:59 -0400 Subject: [PATCH 29/29] Delete ModelBox tooltips in a deferred way to avoid a use-after-free. --- src/app/ModuleBrowser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/ModuleBrowser.cpp b/src/app/ModuleBrowser.cpp index 17432edf..e274eb59 100644 --- a/src/app/ModuleBrowser.cpp +++ b/src/app/ModuleBrowser.cpp @@ -219,8 +219,7 @@ struct ModelBox : widget::OpaqueWidget { void setTooltip(ui::Tooltip* tooltip) { if (this->tooltip) { - this->tooltip->parent->removeChild(this->tooltip); - delete this->tooltip; + this->tooltip->requestDelete(); this->tooltip = NULL; }