From d6eb97e6bfbf8582d75f47b6875503783fd2fac0 Mon Sep 17 00:00:00 2001 From: Jeremy Wentworth Date: Thu, 23 Nov 2017 12:51:01 -0500 Subject: [PATCH 01/66] fixed note off velocity --- src/core/MidiTriggerToCV.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index 4bb60020..ec0f11db 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -110,7 +110,7 @@ void MIDITriggerToCVInterface::processMidi(std::vector msg) { if (status == 0x8) { // note off for (int i = 0; i < NUM_OUTPUTS; i++) { if (data1 == trigger[i].num) { - trigger[i].val = data2; + trigger[i].val = 0; } } return; From c336e67577451c521bfc03329b3cbe0606e079ff Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 25 Nov 2017 08:39:07 -0500 Subject: [PATCH 02/66] Delete MenuOverlay on mouse down --- include/widgets.hpp | 2 +- src/widgets/MenuOverlay.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/widgets.hpp b/include/widgets.hpp index b26245af..454c9c43 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -295,7 +295,7 @@ struct Label : Widget { /** Deletes itself from parent when clicked */ struct MenuOverlay : OpaqueWidget { void step() override; - void onDragDrop(EventDragDrop &e) override; + void onMouseDown(EventMouseDown &e) override; void onHoverKey(EventHoverKey &e) override; }; diff --git a/src/widgets/MenuOverlay.cpp b/src/widgets/MenuOverlay.cpp index efecef8c..85665b66 100644 --- a/src/widgets/MenuOverlay.cpp +++ b/src/widgets/MenuOverlay.cpp @@ -13,10 +13,12 @@ void MenuOverlay::step() { } } -void MenuOverlay::onDragDrop(EventDragDrop &e) { - if (e.origin == this) { +void MenuOverlay::onMouseDown(EventMouseDown &e) { + Widget::onMouseDown(e); + if (!e.consumed) { // deletes `this` gScene->setOverlay(NULL); + e.consumed = true; } } From 264c040c6a2f959a6d573238b2f4a3b623cdf624 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 25 Nov 2017 12:14:34 -0500 Subject: [PATCH 03/66] Round to nearest zoom percent --- src/app/Toolbar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index cde1221b..4414b2ee 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -151,12 +151,13 @@ Toolbar::Toolbar() { struct ZoomSlider : Slider { void onAction(EventAction &e) override { Slider::onAction(e); - gRackScene->zoomWidget->setZoom(value / 100.0); + gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); } }; zoomSlider = new ZoomSlider(); zoomSlider->box.pos = Vec(xPos, margin); zoomSlider->box.size.x = 150; + zoomSlider->precision = 0; zoomSlider->label = "Zoom"; zoomSlider->unit = "%"; zoomSlider->setLimits(25.0, 200.0); From 79ac79f0480959cd7f84dcc3cae534a6c2a178e6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 28 Nov 2017 04:48:21 -0500 Subject: [PATCH 04/66] Document RTAUDIO_ALL_APIS --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 40204931..6f9d0e72 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ You may use make's `-j$(nproc)` flag to parallelize builds across all your CPU c make dep +You may use `make dep RTAUDIO_ALL_APIS=1` to attempt to build with all audio driver APIs enabled for your operating system. + You should see a message that all dependencies built successfully. Build Rack. From feff8b7571bd26481d75ffa94edb9b1c9d31423f Mon Sep 17 00:00:00 2001 From: Christoph Scholtes Date: Tue, 28 Nov 2017 19:14:37 -0700 Subject: [PATCH 05/66] Clean up libRack.a file. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 4ebc2723..61b9bfd1 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,7 @@ endif clean: + rm -fv libRack.a rm -rfv $(TARGET) build dist # For Windows resources From 892d4767bf9248977421e87a502428a4c063f7c2 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 29 Nov 2017 12:05:58 -0500 Subject: [PATCH 06/66] Add plugin "version" to module in .vcv file --- src/app/ModuleWidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 975cf835..8c04a89b 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -60,8 +60,11 @@ void ModuleWidget::setPanel(std::shared_ptr svg) { json_t *ModuleWidget::toJson() { json_t *rootJ = json_object(); - // manufacturer + // plugin json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); + // version (of plugin) + if (!model->plugin->version.empty()) + json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); // model json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); // pos From 4310e1c80ec84ab569114217cc2f874c7dd4e1c4 Mon Sep 17 00:00:00 2001 From: Christoph Scholtes Date: Wed, 29 Nov 2017 21:01:56 -0700 Subject: [PATCH 07/66] Ignore libRack.a file. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8e77f61a..d22540ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /Rack /Rack.exe +/libRack.a /autosave.json /settings.json /plugins From 17f3421769aaba11f250c1322edf8b6fcce341a1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 2 Dec 2017 01:17:49 -0500 Subject: [PATCH 08/66] Don't load plugin if slug already exists --- src/plugin.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index ca873ed0..d63b962f 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -144,6 +144,16 @@ static int loadPlugin(std::string path) { plugin->handle = handle; initCallback(plugin); + // Reject plugin if slug already exists + for (Plugin *p : gPlugins) { + if (plugin->slug == p->slug) { + warn("Plugin \"%s\" is already loaded, not attempting to load it again"); + // TODO + // Fix memory leak with `plugin` here + return -1; + } + } + // Add plugin to list gPlugins.push_back(plugin); info("Loaded plugin %s", libraryFilename.c_str()); From 88fb95d590a16dd7153671ac715a22d8ad5c40e5 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 2 Dec 2017 12:35:36 -0500 Subject: [PATCH 09/66] Add line cap and line join to SVGWidget renderer --- src/widgets/SVGWidget.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/widgets/SVGWidget.cpp b/src/widgets/SVGWidget.cpp index 52b503e5..10138fc2 100644 --- a/src/widgets/SVGWidget.cpp +++ b/src/widgets/SVGWidget.cpp @@ -59,16 +59,19 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { DEBUG_ONLY(printf(" new shape: %d id \"%s\", fillrule %d, from (%f, %f) to (%f, %f)\n", shapeIndex, shape->id, shape->fillRule, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);) + // Visibility if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue; nvgSave(vg); + // Opacity if (shape->opacity < 1.0) nvgGlobalAlpha(vg, shape->opacity); // Build path nvgBeginPath(vg); + // Iterate path linked list for (NSVGpath *path = shape->paths; path; path = path->next) { DEBUG_ONLY(printf(" new path: %d points, %s, from (%f, %f) to (%f, %f)\n", path->npts, path->closed ? "closed" : "open", path->bounds[0], path->bounds[1], path->bounds[2], path->bounds[3]);) @@ -81,6 +84,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { DEBUG_ONLY(printf(" bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);) } + // Close path if (path->closed) nvgClosePath(vg); @@ -161,11 +165,12 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { } // Stroke shape - nvgStrokeWidth(vg, shape->strokeWidth); - // strokeDashOffset, strokeDashArray, strokeDashCount not yet supported - // strokeLineJoin, strokeLineCap not yet supported - if (shape->stroke.type) { + nvgStrokeWidth(vg, shape->strokeWidth); + // strokeDashOffset, strokeDashArray, strokeDashCount not yet supported + nvgLineCap(vg, (NVGlineCap) shape->strokeLineCap); + nvgLineJoin(vg, (int) shape->strokeLineJoin); + switch (shape->stroke.type) { case NSVG_PAINT_COLOR: { NVGcolor color = getNVGColor(shape->stroke.color); From d1ed8faa21c0dd610e6b3b16f91ed582afa36067 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 3 Dec 2017 15:38:56 -0500 Subject: [PATCH 10/66] Smooth interpolate SVGSlider --- src/app/SVGSlider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SVGSlider.cpp b/src/app/SVGSlider.cpp index 5d0beb60..b65bcd30 100644 --- a/src/app/SVGSlider.cpp +++ b/src/app/SVGSlider.cpp @@ -23,7 +23,7 @@ void SVGSlider::step() { void SVGSlider::onChange(EventChange &e) { dirty = true; - ParamWidget::onChange(e); + Knob::onChange(e); } From c6cd497e1724e434877d9a52d8c3e20749cba84f Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 4 Dec 2017 12:49:14 -0500 Subject: [PATCH 11/66] Add SlewLimiter::setRiseFall() --- include/dsp/filter.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dsp/filter.hpp b/include/dsp/filter.hpp index 35098ecf..a29785bd 100644 --- a/include/dsp/filter.hpp +++ b/include/dsp/filter.hpp @@ -51,6 +51,11 @@ struct SlewLimiter { float rise = 1.0; float fall = 1.0; float out = 0.0; + + void setRiseFall(float _rise, float _fall) { + rise = _rise; + fall = _fall; + } float process(float in) { float delta = clampf(in - out, -fall, rise); out += delta; From 30807e5ed715144faee69b05df7b2a8f755e093d Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 10:43:07 -0500 Subject: [PATCH 12/66] Remove portaudio from license, add RtAudio --- LICENSE-dist.txt | 31 ++++++++++++++++++++++++------- Makefile | 7 +++++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/LICENSE-dist.txt b/LICENSE-dist.txt index 62b28280..7260a68e 100644 --- a/LICENSE-dist.txt +++ b/LICENSE-dist.txt @@ -180,17 +180,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# portaudio +# RtAudio -PortAudio Portable Real-Time Audio Library -Copyright (c) 1999-2011 Ross Bencina and Phil Burk +RtAudio: a set of realtime audio i/o C++ classes +Copyright (c) 2001-2017 Gary P. Scavone -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +Any person wishing to distribute modifications to the Software is +asked to send the modifications to the original developer so that +they can be incorporated into the canonical version. This is, +however, not a binding provision of this license. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # RtMidi diff --git a/Makefile b/Makefile index 61b9bfd1..790615bb 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,11 @@ ifeq ($(ARCH), win) env PATH=dep/bin:/mingw64/bin gdb -ex run ./Rack endif +perf: $(TARGET) +ifeq ($(ARCH), lin) + LD_LIBRARY_PATH=dep/lib perf record --call-graph dwarf ./Rack +endif + clean: rm -fv libRack.a @@ -107,7 +112,6 @@ ifeq ($(ARCH), mac) cp dep/lib/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ - cp dep/lib/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/ @@ -117,7 +121,6 @@ ifeq ($(ARCH), mac) install_name_tool -change $(PWD)/dep/lib/libsamplerate.0.dylib @executable_path/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack - install_name_tool -change $(PWD)/dep/lib/libportaudio.2.dylib @executable_path/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/librtaudio.6.dylib @executable_path/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/Rack From 62fd356b973d44c1df7a39e93a6c87f6895a06b1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 22:11:15 -0500 Subject: [PATCH 13/66] Switch to glfw master, use glfwGetWindowContentScale() to get DPI of window --- dep/Makefile | 11 +++++------ src/gui.cpp | 8 +++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dep/Makefile b/dep/Makefile index 6f43c97b..9038f68e 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -86,13 +86,12 @@ $(glew): $(MAKE) -C glew-2.1.0 GLEW_DEST="$(LOCAL)" LIBDIR="$(LOCAL)/lib" install $(glfw): - $(WGET) https://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.zip - $(UNZIP) glfw-3.2.1.zip - cd glfw-3.2.1 && $(CMAKE) . \ + git clone https://github.com/glfw/glfw.git + cd glfw && $(CMAKE) . \ -DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DBUILD_SHARED_LIBS=ON \ - -DGLFW_USE_CHDIR=OFF -DGLFW_USE_MENUBAR=ON -DGLFW_USE_RETINA=ON - $(MAKE) -C glfw-3.2.1 - $(MAKE) -C glfw-3.2.1 install + -DGLFW_COCOA_CHDIR_RESOURCES=OFF -DGLFW_COCOA_MENUBAR=ON -DGLFW_COCOA_RETINA_FRAMEBUFFER=ON + $(MAKE) -C glfw + $(MAKE) -C glfw install ifeq ($(ARCH),win) # Not sure why the GLFW build system puts a .dll in the lib directory mv "$(LOCAL)/lib/glfw3.dll" "$(LOCAL)/bin/" diff --git a/src/gui.cpp b/src/gui.cpp index af9632fd..bfb1165b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -418,11 +418,9 @@ void guiRun() { } // Get framebuffer size - int width, height; - glfwGetFramebufferSize(gWindow, &width, &height); - int windowWidth, windowHeight; - glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); - float pixelRatio = (float)width / windowWidth; + float pixelRatio; + glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); + pixelRatio = roundf(pixelRatio); if (pixelRatio != gPixelRatio) { EventZoom eZoom; gScene->onZoom(eZoom); From 572af6e2c0151ab2b1a1231d81bdf19fae631683 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 22:23:19 -0500 Subject: [PATCH 14/66] Fix Mac rtaudio path in `make dist` --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4ebc2723..c504f990 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,7 @@ ifeq ($(ARCH), mac) cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ - cp dep/lib/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/ + cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack @@ -118,7 +118,7 @@ ifeq ($(ARCH), mac) install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libportaudio.2.dylib @executable_path/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack - install_name_tool -change $(PWD)/dep/lib/librtaudio.6.dylib @executable_path/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/Rack + install_name_tool -change librtaudio.dylib @executable_path/librtaudio.dylib $(BUNDLE)/Contents/MacOS/Rack otool -L $(BUNDLE)/Contents/MacOS/Rack From ee2be22c80d9b400cd69017ca293c3847a9c3413 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 22:23:38 -0500 Subject: [PATCH 15/66] Toggle GL2, GL3, and GLES2 with macros --- src/gui.cpp | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/gui.cpp b/src/gui.cpp index af9632fd..4aec9b93 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -8,11 +8,13 @@ #include "../ext/osdialog/osdialog.h" -#define NANOVG_GL2_IMPLEMENTATION -// #define NANOVG_GL3_IMPLEMENTATION +#define NANOVG_GL2 1 +// #define NANOVG_GL3 1 +// #define NANOVG_GLES2 1 +#define NANOVG_GL_IMPLEMENTATION 1 #include "../ext/nanovg/src/nanovg_gl.h" // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported) -#define NANOVG_FBO_VALID 1 +// #define NANOVG_FBO_VALID 1 #include "../ext/nanovg/src/nanovg_gl_utils.h" #define BLENDISH_IMPLEMENTATION #include "../ext/oui-blendish/blendish.h" @@ -353,11 +355,22 @@ void guiInit() { glfwSetWindowSizeLimits(gWindow, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); // Set up NanoVG +#if defined NANOVG_GL2 gVg = nvgCreateGL2(NVG_ANTIALIAS); - // gVg = nvgCreateGL3(NVG_ANTIALIAS); +#elif defined NANOVG_GL3 + gVg = nvgCreateGL3(NVG_ANTIALIAS); +#elif defined NANOVG_GLES2 + gVg = nvgCreateGLES2(NVG_ANTIALIAS); +#endif assert(gVg); +#if defined NANOVG_GL2 gFramebufferVg = nvgCreateGL2(NVG_ANTIALIAS); +#elif defined NANOVG_GL3 + gFramebufferVg = nvgCreateGL3(NVG_ANTIALIAS); +#elif defined NANOVG_GLES2 + gFramebufferVg = nvgCreateGLES2(NVG_ANTIALIAS); +#endif assert(gFramebufferVg); // Set up Blendish @@ -375,9 +388,23 @@ void guiInit() { void guiDestroy() { gGuiFont.reset(); + +#if defined NANOVG_GL2 nvgDeleteGL2(gVg); - // nvgDeleteGL3(gVg); +#elif defined NANOVG_GL3 + nvgDeleteGL3(gVg); +#elif defined NANOVG_GLES2 + nvgDeleteGLES2(gVg); +#endif + +#if defined NANOVG_GL2 nvgDeleteGL2(gFramebufferVg); +#elif defined NANOVG_GL3 + nvgDeleteGL3(gFramebufferVg); +#elif defined NANOVG_GLES2 + nvgDeleteGLES2(gFramebufferVg); +#endif + glfwDestroyWindow(gWindow); glfwTerminate(); } From 3ecf22533994573d718d3388c0059dbd8e80250b Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 22:33:13 -0500 Subject: [PATCH 16/66] Remove patch for glfw dll --- dep/Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dep/Makefile b/dep/Makefile index 9038f68e..9582e69d 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -92,10 +92,6 @@ $(glfw): -DGLFW_COCOA_CHDIR_RESOURCES=OFF -DGLFW_COCOA_MENUBAR=ON -DGLFW_COCOA_RETINA_FRAMEBUFFER=ON $(MAKE) -C glfw $(MAKE) -C glfw install -ifeq ($(ARCH),win) - # Not sure why the GLFW build system puts a .dll in the lib directory - mv "$(LOCAL)/lib/glfw3.dll" "$(LOCAL)/bin/" -endif $(jansson): $(WGET) http://www.digip.org/jansson/releases/jansson-2.10.tar.gz From 7f1035730ae71163859256a2dad51327d708581c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 23:04:54 -0500 Subject: [PATCH 17/66] Add gWindowRatio and set window size on each step --- include/gui.hpp | 6 ++++++ src/gui.cpp | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/gui.hpp b/include/gui.hpp index 9df01144..fd6c0e77 100644 --- a/include/gui.hpp +++ b/include/gui.hpp @@ -17,8 +17,14 @@ namespace rack { extern GLFWwindow *gWindow; extern NVGcontext *gVg; extern NVGcontext *gFramebufferVg; +/** The default font to use for GUI elements */ extern std::shared_ptr gGuiFont; +/** The scaling ratio */ extern float gPixelRatio; +/* The ratio between the framebuffer size and the window size reported by the OS. +This is not equal to gPixelRatio in general. +*/ +extern float gWindowRatio; extern bool gAllowCursorLock; extern int gGuiFrame; extern Vec gMousePos; diff --git a/src/gui.cpp b/src/gui.cpp index b74a6988..fdb58ad3 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -33,7 +33,8 @@ GLFWwindow *gWindow = NULL; NVGcontext *gVg = NULL; NVGcontext *gFramebufferVg = NULL; std::shared_ptr gGuiFont; -float gPixelRatio = 0.0; +float gPixelRatio = 1.0; +float gWindowRatio = 1.0; bool gAllowCursorLock = true; int gGuiFrame; Vec gMousePos; @@ -42,7 +43,6 @@ std::string lastWindowTitle; void windowSizeCallback(GLFWwindow* window, int width, int height) { - gScene->box.size = Vec(width, height); } void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { @@ -145,7 +145,7 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m } void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { - Vec mousePos = Vec(xpos, ypos).round(); + Vec mousePos = Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round(); Vec mouseRel = mousePos.minus(gMousePos); #ifdef ARCH_MAC @@ -411,11 +411,6 @@ void guiDestroy() { void guiRun() { assert(gWindow); - { - int width, height; - glfwGetWindowSize(gWindow, &width, &height); - windowSizeCallback(gWindow, width, height); - } gGuiFrame = 0; while(!glfwWindowShouldClose(gWindow)) { double startTime = glfwGetTime(); @@ -444,7 +439,7 @@ void guiRun() { lastWindowTitle = windowTitle; } - // Get framebuffer size + // Get desired scaling float pixelRatio; glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); pixelRatio = roundf(pixelRatio); @@ -454,6 +449,15 @@ void guiRun() { gPixelRatio = pixelRatio; } + // Get framebuffer/window ratio + int width, height; + glfwGetFramebufferSize(gWindow, &width, &height); + int windowWidth, windowHeight; + glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); + gWindowRatio = (float)width / windowWidth; + + gScene->box.size = Vec(width, height).div(gPixelRatio / gWindowRatio); + // Step scene gScene->step(); From 2019178cba5869db5e7bb857e641f6d7ab56898c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 23:14:58 -0500 Subject: [PATCH 18/66] Reenable NANOVG_FBO_VALID --- src/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui.cpp b/src/gui.cpp index fdb58ad3..31b961d5 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -14,7 +14,7 @@ #define NANOVG_GL_IMPLEMENTATION 1 #include "../ext/nanovg/src/nanovg_gl.h" // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported) -// #define NANOVG_FBO_VALID 1 +#define NANOVG_FBO_VALID 1 #include "../ext/nanovg/src/nanovg_gl_utils.h" #define BLENDISH_IMPLEMENTATION #include "../ext/oui-blendish/blendish.h" From f7ea40f677a73671463728ef47d5410888b72b7f Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 5 Dec 2017 23:43:29 -0500 Subject: [PATCH 19/66] Add glfw and rtaudio git submodules --- .gitmodules | 6 ++++++ dep/Makefile | 2 -- dep/glfw | 1 + dep/rtaudio | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) create mode 160000 dep/glfw create mode 160000 dep/rtaudio diff --git a/.gitmodules b/.gitmodules index 0d52f7cb..1dc85fb4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,9 @@ [submodule "ext/oui-blendish"] path = ext/oui-blendish url = https://github.com/AndrewBelt/oui-blendish.git +[submodule "dep/glfw"] + path = dep/glfw + url = https://github.com/glfw/glfw.git +[submodule "dep/rtaudio"] + path = dep/rtaudio + url = https://github.com/thestk/rtaudio.git diff --git a/dep/Makefile b/dep/Makefile index 9582e69d..9218d608 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -86,7 +86,6 @@ $(glew): $(MAKE) -C glew-2.1.0 GLEW_DEST="$(LOCAL)" LIBDIR="$(LOCAL)/lib" install $(glfw): - git clone https://github.com/glfw/glfw.git cd glfw && $(CMAKE) . \ -DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DBUILD_SHARED_LIBS=ON \ -DGLFW_COCOA_CHDIR_RESOURCES=OFF -DGLFW_COCOA_MENUBAR=ON -DGLFW_COCOA_RETINA_FRAMEBUFFER=ON @@ -131,7 +130,6 @@ $(rtmidi): $(MAKE) -C rtmidi-3.0.0 install $(rtaudio): - git clone https://github.com/thestk/rtaudio.git cd rtaudio && mkdir -p cmakebuild cd rtaudio/cmakebuild && cmake -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX="$(LOCAL)" $(RTAUDIO_FLAGS) .. $(MAKE) -C rtaudio/cmakebuild diff --git a/dep/glfw b/dep/glfw new file mode 160000 index 00000000..30489c5a --- /dev/null +++ b/dep/glfw @@ -0,0 +1 @@ +Subproject commit 30489c5aa1392f698e904c6aced1d7a486634c40 diff --git a/dep/rtaudio b/dep/rtaudio new file mode 160000 index 00000000..ce13dfbf --- /dev/null +++ b/dep/rtaudio @@ -0,0 +1 @@ +Subproject commit ce13dfbf30fd1ab4e7f7eff8886a80f144c75e5d From bd91692158830b049a3d916d2fbc2a72b6f3bed1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 6 Dec 2017 21:16:34 -0500 Subject: [PATCH 20/66] Refactor gui.cpp --- src/gui.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/gui.cpp b/src/gui.cpp index 31b961d5..8b418c40 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -148,11 +148,13 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { Vec mousePos = Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round(); Vec mouseRel = mousePos.minus(gMousePos); + int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR); + (void) cursorMode; + #ifdef ARCH_MAC // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own. // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped. - int mouseMode = glfwGetInputMode(gWindow, GLFW_CURSOR); - if (mouseMode == GLFW_CURSOR_HIDDEN) { + if (cursorMode == GLFW_CURSOR_HIDDEN) { // CGSetLocalEventsSuppressionInterval(0.0); glfwSetCursorPos(gWindow, gMousePos.x, gMousePos.y); CGAssociateMouseAndMouseCursorPosition(true); @@ -313,12 +315,15 @@ void guiInit() { exit(1); } +#if defined NANOVG_GL2 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#elif defined NANOVG_GL3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#endif glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); lastWindowTitle = ""; @@ -334,6 +339,7 @@ void guiInit() { glfwSetWindowSizeCallback(gWindow, windowSizeCallback); glfwSetMouseButtonCallback(gWindow, mouseButtonStickyCallback); + // Call this ourselves, but on every frame instead of only when the mouse moves // glfwSetCursorPosCallback(gWindow, cursorPosCallback); glfwSetCursorEnterCallback(gWindow, cursorEnterCallback); glfwSetScrollCallback(gWindow, scrollCallback); From 734d367a8a07c26c31048ef82286019199bd1835 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 06:04:27 -0500 Subject: [PATCH 21/66] A few random changes from the last week --- include/app.hpp | 3 ++- include/dsp/samplerate.hpp | 10 ++++++++++ include/engine.hpp | 2 +- src/app/Knob.cpp | 2 +- src/app/RackRail.cpp | 9 +++++++++ src/app/RackWidget.cpp | 29 +++++++++++++++++++++++++---- src/app/SVGSwitch.cpp | 4 ---- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index 5acb6bef..4b795dd3 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -199,6 +199,8 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { struct Knob : ParamWidget { /** Snap to nearest integer while dragging */ bool snap = false; + /** Multiplier for mouse movement to adjust knob value */ + float speed = 1.0; float dragValue; void onDragStart(EventDragStart &e) override; void onDragMove(EventDragMove &e) override; @@ -249,7 +251,6 @@ struct SVGSwitch : virtual Switch, FramebufferWidget { SVGSwitch(); /** Adds an SVG file to represent the next switch position */ void addFrame(std::shared_ptr svg); - void step() override; void onChange(EventChange &e) override; }; diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index 7cc988cf..3a87d640 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "frame.hpp" @@ -33,6 +34,15 @@ struct SampleRateConverter { } /** `in` and `out` are interlaced with the number of channels */ void process(const Frame *in, int *inFrames, Frame *out, int *outFrames) { + /* + if (nearf(data.src_ratio, 1.0)) { + int len = mini(*inFrames, *outFrames); + memcpy(out, in, len * sizeof(Frame)); + *inFrames = len; + *outFrames = len; + return; + } + */ // Old versions of libsamplerate use float* here instead of const float* data.data_in = (float*) in; data.input_frames = *inFrames; diff --git a/include/engine.hpp b/include/engine.hpp index ad3cf9e5..289b47ad 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -16,7 +16,7 @@ struct Light { float value = 0.0; float getBrightness(); void setBrightness(float brightness) { - value = brightness * brightness; + value = (brightness > 0.f) ? brightness * brightness : 0.f; } void setBrightnessSmooth(float brightness); }; diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index 4495b98e..fbb5ee37 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -18,7 +18,7 @@ void Knob::onDragStart(EventDragStart &e) { void Knob::onDragMove(EventDragMove &e) { // Drag slower if Mod - float delta = KNOB_SENSITIVITY * (maxValue - minValue) * -e.mouseRel.y; + float delta = KNOB_SENSITIVITY * (maxValue - minValue) * -e.mouseRel.y * speed; if (guiIsModPressed()) delta /= 16.0; dragValue += delta; diff --git a/src/app/RackRail.cpp b/src/app/RackRail.cpp index e126f38a..8dc97e27 100644 --- a/src/app/RackRail.cpp +++ b/src/app/RackRail.cpp @@ -46,6 +46,15 @@ void RackRail::draw(NVGcontext *vg) { nvgLineTo(vg, box.size.x, railY + RACK_GRID_HEIGHT - 0.5); nvgStroke(vg); } + + + // Useful for screenshots + if (0) { + nvgBeginPath(vg); + nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); + nvgFillColor(vg, nvgRGBf(1.0, 1.0, 1.0)); + nvgFill(vg); + } } diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index a7bf58be..2cbb7a52 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -38,6 +38,25 @@ void RackWidget::clear() { wireContainer->clearChildren(); moduleContainer->clearChildren(); lastPath = ""; + +/* + // Add all modules to rack + Vec pos; + for (Plugin *plugin : gPlugins) { + for (Model *model : plugin->models) { + ModuleWidget *moduleWidget = model->createModuleWidget(); + moduleContainer->addChild(moduleWidget); + // Move module nearest to the mouse position + Rect box; + box.size = moduleWidget->box.size; + box.pos = pos; + requestModuleBoxNearest(moduleWidget, box); + pos.x += box.size.x; + } + pos.y += RACK_GRID_HEIGHT; + pos.x = 0; + } +*/ } void RackWidget::reset() { @@ -127,8 +146,10 @@ json_t *RackWidget::toJson() { json_t *rootJ = json_object(); // version - json_t *versionJ = json_string(gApplicationVersion.c_str()); - json_object_set_new(rootJ, "version", versionJ); + if (!gApplicationVersion.empty()) { + json_t *versionJ = json_string(gApplicationVersion.c_str()); + json_object_set_new(rootJ, "version", versionJ); + } // modules json_t *modulesJ = json_array(); @@ -323,8 +344,8 @@ bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) { int x0 = roundf(box.pos.x / RACK_GRID_WIDTH); int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT); std::vector positions; - for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) { - for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) { + for (int y = maxi(0, y0 - 8); y < y0 + 8; y++) { + for (int x = maxi(0, x0 - 400); x < x0 + 400; x++) { positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT)); } } diff --git a/src/app/SVGSwitch.cpp b/src/app/SVGSwitch.cpp index eda234cc..eecaeb1e 100644 --- a/src/app/SVGSwitch.cpp +++ b/src/app/SVGSwitch.cpp @@ -18,10 +18,6 @@ void SVGSwitch::addFrame(std::shared_ptr svg) { } } -void SVGSwitch::step() { - FramebufferWidget::step(); -} - void SVGSwitch::onChange(EventChange &e) { assert(frames.size() > 0); float valueScaled = rescalef(value, minValue, maxValue, 0, frames.size() - 1); From 66f11d9e82539ffebafa7a7457e10662ae29c0a3 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 06:47:05 -0500 Subject: [PATCH 22/66] Split tags into tags.{hpp,cpp} --- dep/glfw | 2 +- include/app.hpp | 1 - include/plugin.hpp | 50 +--------------------------------------------- src/plugin.cpp | 47 +------------------------------------------ 4 files changed, 3 insertions(+), 97 deletions(-) diff --git a/dep/glfw b/dep/glfw index 30489c5a..682f1cf2 160000 --- a/dep/glfw +++ b/dep/glfw @@ -1 +1 @@ -Subproject commit 30489c5aa1392f698e904c6aced1d7a486634c40 +Subproject commit 682f1cf203707f21c2eed4fa3f89c23c52accc49 diff --git a/include/app.hpp b/include/app.hpp index 4b795dd3..88d74b86 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -23,7 +23,6 @@ struct SVGPanel; // A 1U module should be 15x380. Thus the width of a module should be a factor of 15. #define RACK_GRID_WIDTH 15 #define RACK_GRID_HEIGHT 380 - static const Vec RACK_GRID_SIZE = Vec(15, 380); diff --git a/include/plugin.hpp b/include/plugin.hpp index bd243473..8e9f612b 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -1,59 +1,12 @@ #pragma once #include #include +#include "tags.hpp" namespace rack { -enum ModelTag { - AMPLIFIER_TAG, - ATTENUATOR_TAG, - BLANK_TAG, - CLOCK_TAG, - CONTROLLER_TAG, - DELAY_TAG, - DIGITAL_TAG, - DISTORTION_TAG, - DRUM_TAG, - DUAL_TAG, - DYNAMICS_TAG, - EFFECT_TAG, - ENVELOPE_FOLLOWER_TAG, - ENVELOPE_GENERATOR_TAG, - EQUALIZER_TAG, - EXTERNAL_TAG, - FILTER_TAG, - FUNCTION_GENERATOR_TAG, - GRANULAR_TAG, - LFO_TAG, - LOGIC_TAG, - LOW_PASS_GATE_TAG, - MIDI_TAG, - MIXER_TAG, - MULTIPLE_TAG, - NOISE_TAG, - OSCILLATOR_TAG, - PANNING_TAG, - QUAD_TAG, - QUANTIZER_TAG, - RANDOM_TAG, - REVERB_TAG, - RING_MODULATOR_TAG, - SAMPLE_AND_HOLD_TAG, - SAMPLER_TAG, - SEQUENCER_TAG, - SLEW_LIMITER_TAG, - SWITCH_TAG, - SYNTH_VOICE_TAG, - TUNER_TAG, - UTILITY_TAG, - VISUAL_TAG, - WAVESHAPER_TAG, - NUM_TAGS -}; - - struct ModuleWidget; struct Model; @@ -118,7 +71,6 @@ std::string pluginGetLoginStatus(); extern std::list gPlugins; extern std::string gToken; -extern std::string gTagNames[NUM_TAGS]; } // namespace rack diff --git a/src/plugin.cpp b/src/plugin.cpp index d63b962f..3b0b1b51 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -31,52 +31,6 @@ namespace rack { std::list gPlugins; std::string gToken; -std::string gTagNames[NUM_TAGS] = { - "Amplifier/VCA", - "Attenuator", - "Blank", - "Clock", - "Controller", - "Delay", - "Digital", - "Distortion", - "Drum", - "Dual/Stereo", - "Dynamics", - "Effect", - "Envelope Follower", - "Envelope Generator", - "Equalizer", - "External", - "Filter/VCF", - "Function Generator", - "Granular", - "LFO", - "Logic", - "Low Pass Gate", - "MIDI", - "Mixer", - "Multiple", - "Noise", - "Oscillator/VCO", - "Panning", - "Quad", - "Quantizer", - "Random", - "Reverb", - "Ring Modulator", - "Sample and Hold", - "Sampler", - "Sequencer", - "Slew Limiter", - "Switch", - "Synth Voice", - "Tuner", - "Utility", - "Visual", - "Waveshaper", -}; - static bool isDownloading = false; static float downloadProgress = 0.0; @@ -293,6 +247,7 @@ static void refreshPurchase(json_t *pluginJ) { //////////////////// void pluginInit() { + tagsInit(); // Load core // This function is defined in core.cpp Plugin *coreManufacturer = new Plugin(); From 382babbb280c2b04a5f661a4a94b2b68d8313969 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 06:47:05 -0500 Subject: [PATCH 23/66] Split tags into tags.{hpp,cpp} --- dep/glfw | 2 +- include/app.hpp | 1 - include/plugin.hpp | 50 +------------------------------- include/tags.hpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/plugin.cpp | 47 +----------------------------- src/tags.cpp | 64 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 139 insertions(+), 97 deletions(-) create mode 100644 include/tags.hpp create mode 100644 src/tags.cpp diff --git a/dep/glfw b/dep/glfw index 30489c5a..682f1cf2 160000 --- a/dep/glfw +++ b/dep/glfw @@ -1 +1 @@ -Subproject commit 30489c5aa1392f698e904c6aced1d7a486634c40 +Subproject commit 682f1cf203707f21c2eed4fa3f89c23c52accc49 diff --git a/include/app.hpp b/include/app.hpp index 4b795dd3..88d74b86 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -23,7 +23,6 @@ struct SVGPanel; // A 1U module should be 15x380. Thus the width of a module should be a factor of 15. #define RACK_GRID_WIDTH 15 #define RACK_GRID_HEIGHT 380 - static const Vec RACK_GRID_SIZE = Vec(15, 380); diff --git a/include/plugin.hpp b/include/plugin.hpp index bd243473..8e9f612b 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -1,59 +1,12 @@ #pragma once #include #include +#include "tags.hpp" namespace rack { -enum ModelTag { - AMPLIFIER_TAG, - ATTENUATOR_TAG, - BLANK_TAG, - CLOCK_TAG, - CONTROLLER_TAG, - DELAY_TAG, - DIGITAL_TAG, - DISTORTION_TAG, - DRUM_TAG, - DUAL_TAG, - DYNAMICS_TAG, - EFFECT_TAG, - ENVELOPE_FOLLOWER_TAG, - ENVELOPE_GENERATOR_TAG, - EQUALIZER_TAG, - EXTERNAL_TAG, - FILTER_TAG, - FUNCTION_GENERATOR_TAG, - GRANULAR_TAG, - LFO_TAG, - LOGIC_TAG, - LOW_PASS_GATE_TAG, - MIDI_TAG, - MIXER_TAG, - MULTIPLE_TAG, - NOISE_TAG, - OSCILLATOR_TAG, - PANNING_TAG, - QUAD_TAG, - QUANTIZER_TAG, - RANDOM_TAG, - REVERB_TAG, - RING_MODULATOR_TAG, - SAMPLE_AND_HOLD_TAG, - SAMPLER_TAG, - SEQUENCER_TAG, - SLEW_LIMITER_TAG, - SWITCH_TAG, - SYNTH_VOICE_TAG, - TUNER_TAG, - UTILITY_TAG, - VISUAL_TAG, - WAVESHAPER_TAG, - NUM_TAGS -}; - - struct ModuleWidget; struct Model; @@ -118,7 +71,6 @@ std::string pluginGetLoginStatus(); extern std::list gPlugins; extern std::string gToken; -extern std::string gTagNames[NUM_TAGS]; } // namespace rack diff --git a/include/tags.hpp b/include/tags.hpp new file mode 100644 index 00000000..cbf9a2d0 --- /dev/null +++ b/include/tags.hpp @@ -0,0 +1,72 @@ +#pragma once +#include + + +namespace rack { + + +/** Describes the type(s) of each module +To see comments, turn word wrap on. I'm using inline comments so I can automatically sort the list when more tags are added. +*/ +enum ModelTag { + AMPLIFIER_TAG, + ATTENUATOR_TAG, + BLANK_TAG, + CHORUS_TAG, + CLOCK_TAG, + COMPRESSOR_TAG, + CONTROLLER_TAG, // Use only if the artist "performs" with this module. Knobs are not sufficient. Examples: on-screen keyboard, XY pad. + DELAY_TAG, + DIGITAL_TAG, + DISTORTION_TAG, + DRUM_TAG, + DUAL_TAG, // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module. + DYNAMICS_TAG, + EFFECT_TAG, + ENVELOPE_FOLLOWER_TAG, + ENVELOPE_GENERATOR_TAG, + EQUALIZER_TAG, + EXTERNAL_TAG, + FILTER_TAG, + FLANGER_TAG, + FUNCTION_GENERATOR_TAG, + GRANULAR_TAG, + LFO_TAG, + LIMITER_TAG, + LOGIC_TAG, + LOW_PASS_GATE_TAG, + MIDI_TAG, + MIXER_TAG, + MULTIPLE_TAG, + NOISE_TAG, + OSCILLATOR_TAG, + PANNING_TAG, + PHASER_TAG, + PHYSICAL_MODELING_TAG, + QUAD_TAG, // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module. + QUANTIZER_TAG, + RANDOM_TAG, + RECORDING_TAG, + REVERB_TAG, + RING_MODULATOR_TAG, + SAMPLE_AND_HOLD_TAG, + SAMPLER_TAG, + SEQUENCER_TAG, + SLEW_LIMITER_TAG, + SWITCH_TAG, + SYNTH_VOICE_TAG, // A synth voice must have an envelope built-in. + TUNER_TAG, + UTILITY_TAG, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc. + VISUAL_TAG, + VOCODER_TAG, + WAVESHAPER_TAG, + NUM_TAGS +}; + + +void tagsInit(); + +extern std::string gTagNames[NUM_TAGS]; + + +} // namespace rack diff --git a/src/plugin.cpp b/src/plugin.cpp index d63b962f..3b0b1b51 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -31,52 +31,6 @@ namespace rack { std::list gPlugins; std::string gToken; -std::string gTagNames[NUM_TAGS] = { - "Amplifier/VCA", - "Attenuator", - "Blank", - "Clock", - "Controller", - "Delay", - "Digital", - "Distortion", - "Drum", - "Dual/Stereo", - "Dynamics", - "Effect", - "Envelope Follower", - "Envelope Generator", - "Equalizer", - "External", - "Filter/VCF", - "Function Generator", - "Granular", - "LFO", - "Logic", - "Low Pass Gate", - "MIDI", - "Mixer", - "Multiple", - "Noise", - "Oscillator/VCO", - "Panning", - "Quad", - "Quantizer", - "Random", - "Reverb", - "Ring Modulator", - "Sample and Hold", - "Sampler", - "Sequencer", - "Slew Limiter", - "Switch", - "Synth Voice", - "Tuner", - "Utility", - "Visual", - "Waveshaper", -}; - static bool isDownloading = false; static float downloadProgress = 0.0; @@ -293,6 +247,7 @@ static void refreshPurchase(json_t *pluginJ) { //////////////////// void pluginInit() { + tagsInit(); // Load core // This function is defined in core.cpp Plugin *coreManufacturer = new Plugin(); diff --git a/src/tags.cpp b/src/tags.cpp new file mode 100644 index 00000000..49302164 --- /dev/null +++ b/src/tags.cpp @@ -0,0 +1,64 @@ +#include "tags.hpp" + + +namespace rack { + + +std::string gTagNames[NUM_TAGS]; + + +void tagsInit() { + gTagNames[AMPLIFIER_TAG] = "Amplifier/VCA"; + gTagNames[ATTENUATOR_TAG] = "Attenuator"; + gTagNames[BLANK_TAG] = "Blank"; + gTagNames[CHORUS_TAG] = "Chorus"; + gTagNames[CLOCK_TAG] = "Clock"; + gTagNames[COMPRESSOR_TAG] = "Compressor"; + gTagNames[CONTROLLER_TAG] = "Controller"; + gTagNames[DELAY_TAG] = "Delay"; + gTagNames[DIGITAL_TAG] = "Digital"; + gTagNames[DISTORTION_TAG] = "Distortion"; + gTagNames[DRUM_TAG] = "Drum"; + gTagNames[DUAL_TAG] = "Dual/Stereo"; + gTagNames[DYNAMICS_TAG] = "Dynamics"; + gTagNames[EFFECT_TAG] = "Effect"; + gTagNames[ENVELOPE_FOLLOWER_TAG] = "Envelope Follower"; + gTagNames[ENVELOPE_GENERATOR_TAG] = "Envelope Generator"; + gTagNames[EQUALIZER_TAG] = "Equalizer"; + gTagNames[EXTERNAL_TAG] = "External"; + gTagNames[FILTER_TAG] = "Filter/VCF"; + gTagNames[FLANGER_TAG] = "Flanger"; + gTagNames[FUNCTION_GENERATOR_TAG] = "Function Generator"; + gTagNames[GRANULAR_TAG] = "Granular"; + gTagNames[LFO_TAG] = "LFO"; + gTagNames[LIMITER_TAG] = "Limiter"; + gTagNames[LOGIC_TAG] = "Logic"; + gTagNames[LOW_PASS_GATE_TAG] = "Low Pass Gate"; + gTagNames[MIDI_TAG] = "MIDI"; + gTagNames[MIXER_TAG] = "Mixer"; + gTagNames[MULTIPLE_TAG] = "Multiple"; + gTagNames[NOISE_TAG] = "Noise"; + gTagNames[OSCILLATOR_TAG] = "Oscillator/VCO"; + gTagNames[PANNING_TAG] = "Panning"; + gTagNames[PHASER_TAG] = "Phaser"; + gTagNames[QUAD_TAG] = "Quad"; + gTagNames[QUANTIZER_TAG] = "Quantizer"; + gTagNames[RANDOM_TAG] = "Random"; + gTagNames[RECORDING_TAG] = "Recording"; + gTagNames[REVERB_TAG] = "Reverb"; + gTagNames[RING_MODULATOR_TAG] = "Ring Modulator"; + gTagNames[SAMPLE_AND_HOLD_TAG] = "Sample and Hold"; + gTagNames[SAMPLER_TAG] = "Sampler"; + gTagNames[SEQUENCER_TAG] = "Sequencer"; + gTagNames[SLEW_LIMITER_TAG] = "Slew Limiter"; + gTagNames[SWITCH_TAG] = "Switch"; + gTagNames[SYNTH_VOICE_TAG] = "Synth Voice"; + gTagNames[TUNER_TAG] = "Tuner"; + gTagNames[UTILITY_TAG] = "Utility"; + gTagNames[VISUAL_TAG] = "Visual"; + gTagNames[VOCODER_TAG] = "Vocoder"; + gTagNames[WAVESHAPER_TAG] = "Waveshaper"; +} + + +} // namespace rack From 199f99b0bf0d40ac7c398405d1f61b991c931eb3 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 07:29:23 -0500 Subject: [PATCH 24/66] Rename SVGSlider to SVGFader, refactor LightWidget --- include/app.hpp | 11 +++---- include/components.hpp | 2 +- src/app/LightWidget.cpp | 38 +++++++++++++++++-------- src/app/{SVGSlider.cpp => SVGFader.cpp} | 6 ++-- src/app/WireWidget.cpp | 5 ---- 5 files changed, 36 insertions(+), 26 deletions(-) rename src/app/{SVGSlider.cpp => SVGFader.cpp} (83%) diff --git a/include/app.hpp b/include/app.hpp index 88d74b86..9cea300e 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -74,14 +74,11 @@ struct ModuleWidget : OpaqueWidget { void onDragMove(EventDragMove &e) override; }; -struct ValueLight; struct WireWidget : OpaqueWidget { Port *outputPort = NULL; Port *inputPort = NULL; Port *hoveredOutputPort = NULL; Port *hoveredInputPort = NULL; - ValueLight *inputLight; - ValueLight *outputLight; Wire *wire = NULL; NVGcolor color; @@ -227,14 +224,14 @@ struct SVGKnob : virtual Knob, FramebufferWidget { void onChange(EventChange &e) override; }; -struct SVGSlider : Knob, FramebufferWidget { +struct SVGFader : Knob, FramebufferWidget { /** Intermediate positions will be interpolated between these positions */ Vec minHandlePos, maxHandlePos; /** Not owned */ SVGWidget *background; SVGWidget *handle; - SVGSlider(); + SVGFader(); void step() override; void onChange(EventChange &e) override; }; @@ -271,6 +268,8 @@ struct MomentarySwitch : virtual Switch { void randomize() override {} void onDragStart(EventDragStart &e) override { setValue(maxValue); + EventAction eAction; + onAction(eAction); } void onDragEnd(EventDragEnd &e) override { setValue(minValue); @@ -285,6 +284,8 @@ struct LightWidget : TransparentWidget { NVGcolor bgColor = nvgRGBf(0, 0, 0); NVGcolor color = nvgRGBf(1, 1, 1); void draw(NVGcontext *vg) override; + virtual void drawLight(NVGcontext *vg); + virtual void drawHalo(NVGcontext *vg); }; /** Mixes a list of colors based on a list of brightness values */ diff --git a/include/components.hpp b/include/components.hpp index c300e44b..03854062 100644 --- a/include/components.hpp +++ b/include/components.hpp @@ -322,7 +322,7 @@ struct BefacoTinyKnob : SVGKnob { } }; -struct BefacoSlidePot : SVGSlider { +struct BefacoSlidePot : SVGFader { BefacoSlidePot() { Vec margin = Vec(3.5, 3.5); maxHandlePos = Vec(-1, -2).plus(margin); diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index a75bf23c..70f3a00d 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -5,35 +5,46 @@ namespace rack { void LightWidget::draw(NVGcontext *vg) { - float radius = box.size.x / 2.0; - float oradius = radius + 15.0; - color.r = clampf(color.r, 0.0, 1.0); color.g = clampf(color.g, 0.0, 1.0); color.b = clampf(color.b, 0.0, 1.0); color.a = clampf(color.a, 0.0, 1.0); - // Solid + drawLight(vg); + drawHalo(vg); +} + + +void LightWidget::drawLight(NVGcontext *vg) { + float radius = box.size.x / 2.0; + nvgBeginPath(vg); nvgCircle(vg, radius, radius, radius); + + // Background nvgFillColor(vg, bgColor); nvgFill(vg); - // Border - nvgStrokeWidth(vg, 1.0); - NVGcolor borderColor = bgColor; - borderColor.a *= 0.5; - nvgStrokeColor(vg, borderColor); - nvgStroke(vg); + // // Border + // nvgStrokeWidth(vg, 1.0); + // NVGcolor borderColor = bgColor; + // borderColor.a *= 0.5; + // nvgStrokeColor(vg, borderColor); + // nvgStroke(vg); // Inner glow - nvgGlobalCompositeOperation(vg, NVG_LIGHTER); nvgFillColor(vg, color); nvgFill(vg); +} + + +void LightWidget::drawHalo(NVGcontext *vg) { + float radius = box.size.x / 2.0; + float oradius = radius + 15.0; - // Outer glow nvgBeginPath(vg); nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); + NVGpaint paint; NVGcolor icol = color; icol.a *= 0.10; @@ -41,8 +52,11 @@ void LightWidget::draw(NVGcontext *vg) { ocol.a = 0.0; paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); nvgFillPaint(vg, paint); + nvgGlobalCompositeOperation(vg, NVG_LIGHTER); nvgFill(vg); } + + } // namespace rack diff --git a/src/app/SVGSlider.cpp b/src/app/SVGFader.cpp similarity index 83% rename from src/app/SVGSlider.cpp rename to src/app/SVGFader.cpp index b65bcd30..ee170ea1 100644 --- a/src/app/SVGSlider.cpp +++ b/src/app/SVGFader.cpp @@ -4,7 +4,7 @@ namespace rack { -SVGSlider::SVGSlider() { +SVGFader::SVGFader() { background = new SVGWidget(); addChild(background); @@ -12,7 +12,7 @@ SVGSlider::SVGSlider() { addChild(handle); } -void SVGSlider::step() { +void SVGFader::step() { if (dirty) { // Update handle position Vec handlePos = Vec(rescalef(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescalef(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); @@ -21,7 +21,7 @@ void SVGSlider::step() { FramebufferWidget::step(); } -void SVGSlider::onChange(EventChange &e) { +void SVGFader::onChange(EventChange &e) { dirty = true; Knob::onChange(e); } diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 07d16226..75b73516 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -86,11 +86,6 @@ static int lastWireColorId = -1; WireWidget::WireWidget() { lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors); color = wireColors[lastWireColorId]; - - // inputLight = construct(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); - // outputLight = construct(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); - // addChild(inputLight); - // addChild(outputLight); } WireWidget::~WireWidget() { From 9a5fd5899d04d2010d313719ebc4fe553e709435 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 07:52:15 -0500 Subject: [PATCH 25/66] Added ZoomWidget::onPathDrop --- include/math.hpp | 11 +---------- include/widgets.hpp | 1 + src/widgets/ZoomWidget.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/math.hpp b/include/math.hpp index 0b94c9c1..815f3746 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -120,15 +120,6 @@ inline float sincf(float x) { return sinf(x) / x; } -inline float getf(const float *p, float v = 0.0) { - return p ? *p : v; -} - -inline void setf(float *p, float v) { - if (p) - *p = v; -} - /** Linearly interpolate an array `p` with index `x` Assumes that the array at `p` is of length at least floor(x)+1. */ @@ -139,7 +130,7 @@ inline float interpf(const float *p, float x) { } /** Complex multiply c = a * b -It is of course acceptable to reuse arguments +Arguments may be the same pointers i.e. cmultf(&ar, &ai, ar, ai, br, bi) */ inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) { diff --git a/include/widgets.hpp b/include/widgets.hpp index 454c9c43..6c8fa9f3 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -178,6 +178,7 @@ struct ZoomWidget : Widget { void onMouseMove(EventMouseMove &e) override; void onHoverKey(EventHoverKey &e) override; void onScroll(EventScroll &e) override; + void onPathDrop(EventPathDrop &e) override; }; //////////////////// diff --git a/src/widgets/ZoomWidget.cpp b/src/widgets/ZoomWidget.cpp index c6d6cb5d..5ef4d335 100644 --- a/src/widgets/ZoomWidget.cpp +++ b/src/widgets/ZoomWidget.cpp @@ -65,5 +65,12 @@ void ZoomWidget::onScroll(EventScroll &e) { e.pos = pos; } +void ZoomWidget::onPathDrop(EventPathDrop &e) { + Vec pos = e.pos; + e.pos = e.pos.div(zoom); + Widget::onPathDrop(e); + e.pos = pos; +} + } // namespace rack From 56219530cb6963f9ca78c4cce59fe6ee97ce9493 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 08:00:03 -0500 Subject: [PATCH 26/66] Clean up warnings --- src/app/ModuleWidget.cpp | 12 ++++++------ src/app/Toolbar.cpp | 14 +++++++------- src/core/AudioInterface.cpp | 10 +++++----- src/core/MidiCCToCV.cpp | 2 +- src/core/MidiClockToCV.cpp | 4 ++-- src/core/MidiIO.cpp | 10 +++++----- src/core/MidiIO.hpp | 4 ++++ src/core/MidiToCV.cpp | 2 +- src/core/MidiTriggerToCV.cpp | 2 +- src/core/QuadMidiToCV.cpp | 10 +++++----- 10 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 8c04a89b..7de226f6 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -282,36 +282,36 @@ Menu *ModuleWidget::createContextMenu() { MenuLabel *menuLabel = new MenuLabel(); menuLabel->text = model->manufacturer + " " + model->name; - menu->pushChild(menuLabel); + menu->addChild(menuLabel); ResetMenuItem *resetItem = new ResetMenuItem(); resetItem->text = "Initialize"; resetItem->rightText = GUI_MOD_KEY_NAME "+I"; resetItem->moduleWidget = this; - menu->pushChild(resetItem); + menu->addChild(resetItem); RandomizeMenuItem *randomizeItem = new RandomizeMenuItem(); randomizeItem->text = "Randomize"; randomizeItem->rightText = GUI_MOD_KEY_NAME "+R"; randomizeItem->moduleWidget = this; - menu->pushChild(randomizeItem); + menu->addChild(randomizeItem); DisconnectMenuItem *disconnectItem = new DisconnectMenuItem(); disconnectItem->text = "Disconnect cables"; disconnectItem->moduleWidget = this; - menu->pushChild(disconnectItem); + menu->addChild(disconnectItem); CloneMenuItem *cloneItem = new CloneMenuItem(); cloneItem->text = "Duplicate"; cloneItem->rightText = GUI_MOD_KEY_NAME "+D"; cloneItem->moduleWidget = this; - menu->pushChild(cloneItem); + menu->addChild(cloneItem); DeleteMenuItem *deleteItem = new DeleteMenuItem(); deleteItem->text = "Delete"; deleteItem->rightText = "Backspace/Delete"; deleteItem->moduleWidget = this; - menu->pushChild(deleteItem); + menu->addChild(deleteItem); return menu; } diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 4414b2ee..6fe03649 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -43,11 +43,11 @@ struct FileChoice : ChoiceButton { menu->box.size.x = box.size.x; { - menu->pushChild(construct(&MenuItem::text, "New", &MenuItem::rightText, GUI_MOD_KEY_NAME "+N")); - menu->pushChild(construct(&MenuItem::text, "Open", &MenuItem::rightText, GUI_MOD_KEY_NAME "+O")); - menu->pushChild(construct(&MenuItem::text, "Save", &MenuItem::rightText, GUI_MOD_KEY_NAME "+S")); - menu->pushChild(construct(&MenuItem::text, "Save as", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Shift+S")); - menu->pushChild(construct(&MenuItem::text, "Quit", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Q")); + menu->addChild(construct(&MenuItem::text, "New", &MenuItem::rightText, GUI_MOD_KEY_NAME "+N")); + menu->addChild(construct(&MenuItem::text, "Open", &MenuItem::rightText, GUI_MOD_KEY_NAME "+O")); + menu->addChild(construct(&MenuItem::text, "Save", &MenuItem::rightText, GUI_MOD_KEY_NAME "+S")); + menu->addChild(construct(&MenuItem::text, "Save as", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Shift+S")); + menu->addChild(construct(&MenuItem::text, "Quit", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Q")); } } }; @@ -75,7 +75,7 @@ struct SampleRateChoice : ChoiceButton { PauseItem *pauseItem = new PauseItem(); pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; - menu->pushChild(pauseItem); + menu->addChild(pauseItem); float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); @@ -83,7 +83,7 @@ struct SampleRateChoice : ChoiceButton { SampleRateItem *item = new SampleRateItem(); item->text = stringf("%.0f Hz", sampleRates[i]); item->sampleRate = sampleRates[i]; - menu->pushChild(item); + menu->addChild(item); } } void step() override { diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 4f6b5159..937ace0d 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -414,7 +414,7 @@ struct AudioDriverChoice : ChoiceButton { audioItem->audioInterface = audioInterface; audioItem->driver = driver; audioItem->text = audioInterface->getDriverName(driver); - menu->pushChild(audioItem); + menu->addChild(audioItem); } } void step() override { @@ -446,14 +446,14 @@ struct AudioDeviceChoice : ChoiceButton { audioItem->audioInterface = audioInterface; audioItem->device = -1; audioItem->text = "No device"; - menu->pushChild(audioItem); + menu->addChild(audioItem); } for (int device = 0; device < deviceCount; device++) { AudioDeviceItem *audioItem = new AudioDeviceItem(); audioItem->audioInterface = audioInterface; audioItem->device = device; audioItem->text = audioInterface->getDeviceName(device); - menu->pushChild(audioItem); + menu->addChild(audioItem); } } void step() override { @@ -487,7 +487,7 @@ struct SampleRateChoice : ChoiceButton { item->audioInterface = audioInterface; item->sampleRate = sampleRate; item->text = stringf("%.0f Hz", sampleRate); - menu->pushChild(item); + menu->addChild(item); } } void step() override { @@ -519,7 +519,7 @@ struct BlockSizeChoice : ChoiceButton { item->audioInterface = audioInterface; item->blockSize = blockSizes[i]; item->text = stringf("%d", blockSizes[i]); - menu->pushChild(item); + menu->addChild(item); } } void step() override { diff --git a/src/core/MidiCCToCV.cpp b/src/core/MidiCCToCV.cpp index 706b84b8..a5f42fa1 100644 --- a/src/core/MidiCCToCV.cpp +++ b/src/core/MidiCCToCV.cpp @@ -1,9 +1,9 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" + struct CCValue { int val = 0; // Controller value TransitionSmoother tSmooth; diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index 8e3f5516..543ab370 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -1,10 +1,10 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" #include "dsp/digital.hpp" + using namespace rack; struct MIDIClockToCVInterface : MidiIO, Module { @@ -236,7 +236,7 @@ struct ClockRatioChoice : ChoiceButton { clockRatioItem->ratio = ratio; clockRatioItem->clockRatio = clockRatio; clockRatioItem->text = ratioNames[ratio]; - menu->pushChild(clockRatioItem); + menu->addChild(clockRatioItem); } } diff --git a/src/core/MidiIO.cpp b/src/core/MidiIO.cpp index 2ca166ee..efd4fcd7 100644 --- a/src/core/MidiIO.cpp +++ b/src/core/MidiIO.cpp @@ -1,9 +1,9 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" + using namespace rack; @@ -209,7 +209,7 @@ void MidiChoice::onAction(EventAction &e) { MidiItem *midiItem = new MidiItem(); midiItem->midiModule = midiModule; midiItem->text = ""; - menu->pushChild(midiItem); + menu->addChild(midiItem); } std::vector deviceNames = midiModule->getDevices(); @@ -217,7 +217,7 @@ void MidiChoice::onAction(EventAction &e) { MidiItem *midiItem = new MidiItem(); midiItem->midiModule = midiModule; midiItem->text = deviceNames[i]; - menu->pushChild(midiItem); + menu->addChild(midiItem); } } @@ -245,14 +245,14 @@ void ChannelChoice::onAction(EventAction &e) { channelItem->midiModule = midiModule; channelItem->channel = -1; channelItem->text = "All"; - menu->pushChild(channelItem); + menu->addChild(channelItem); } for (int channel = 0; channel < 16; channel++) { ChannelItem *channelItem = new ChannelItem(); channelItem->midiModule = midiModule; channelItem->channel = channel; channelItem->text = stringf("%d", channel + 1); - menu->pushChild(channelItem); + menu->addChild(channelItem); } } diff --git a/src/core/MidiIO.hpp b/src/core/MidiIO.hpp index 5148c5fa..0c1d8899 100644 --- a/src/core/MidiIO.hpp +++ b/src/core/MidiIO.hpp @@ -1,6 +1,10 @@ #include #include "rack.hpp" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" #include "rtmidi/RtMidi.h" +#pragma GCC diagnostic pop using namespace rack; diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index 69be33be..4a417ba6 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -1,10 +1,10 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" #include "dsp/digital.hpp" + /* * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod wheel to * CV diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index ec0f11db..2503745a 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -1,10 +1,10 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" #include "dsp/digital.hpp" + using namespace rack; struct TriggerValue { diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 39982913..7df68601 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -1,10 +1,10 @@ #include #include -#include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" #include "dsp/digital.hpp" + struct MidiKey { int pitch = 60; int at = 0; // aftertouch @@ -254,7 +254,7 @@ struct ModeItem : MenuItem { int mode; QuadMIDIToCVInterface *module; - void onAction(EventAction &e) { + void onAction(EventAction &e) override { module->setMode(mode); } }; @@ -264,7 +264,7 @@ struct ModeChoice : ChoiceButton { const std::vector modeNames = {"ROTATE", "RESET", "REASSIGN"}; - void onAction(EventAction &e) { + void onAction(EventAction &e) override { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; @@ -274,11 +274,11 @@ struct ModeChoice : ChoiceButton { modeItem->mode = i; modeItem->module = module; modeItem->text = modeNames[i]; - menu->pushChild(modeItem); + menu->addChild(modeItem); } } - void step() { + void step() override { text = modeNames[module->getMode()]; } }; From e60117701c2d45c5d0b9a8d929b02f6349e9fd80 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 8 Dec 2017 08:06:08 -0500 Subject: [PATCH 27/66] Run autoformatter on Core MIDI modules --- src/core/MidiCCToCV.cpp | 17 ++++--- src/core/MidiClockToCV.cpp | 50 +++++++++++---------- src/core/MidiIO.cpp | 3 +- src/core/MidiIO.hpp | 12 ++--- src/core/MidiTriggerToCV.cpp | 9 ++-- src/core/QuadMidiToCV.cpp | 87 ++++++++++++++++++------------------ 6 files changed, 95 insertions(+), 83 deletions(-) diff --git a/src/core/MidiCCToCV.cpp b/src/core/MidiCCToCV.cpp index a5f42fa1..dcafc589 100644 --- a/src/core/MidiCCToCV.cpp +++ b/src/core/MidiCCToCV.cpp @@ -117,7 +117,7 @@ void MIDICCToCVInterface::resetMidi() { for (int i = 0; i < NUM_OUTPUTS; i++) { cc[i].val = 0; cc[i].resetSync(); - cc[i].tSmooth.set(0,0); + cc[i].tSmooth.set(0, 0); } }; @@ -147,7 +147,8 @@ void MIDICCToCVInterface::processMidi(std::vector msg) { cc[i].syncFirst = false; if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { cc[i].sync = 0; - }else { + } + else { cc[i].sync = absi(data2 - cc[i].val); } return; @@ -156,7 +157,8 @@ void MIDICCToCVInterface::processMidi(std::vector msg) { if (cc[i].sync == 0) { cc[i].val = data2; cc[i].changed = true; - } else { + } + else { cc[i].sync = absi(data2 - cc[i].val); } } @@ -225,12 +227,14 @@ void CCTextField::onTextChange() { text = ""; begin = end = 0; module->cc[outNum].num = -1; - } else { + } + else { module->cc[outNum].num = num; module->cc[outNum].resetSync(); } - } catch (...) { + } + catch (...) { text = ""; begin = end = 0; module->cc[outNum].num = -1; @@ -309,7 +313,8 @@ MIDICCToCVWidget::MIDICCToCVWidget() { if ((i + 1) % 4 == 0) { yPos += 47 + margin; - } else { + } + else { yPos -= labelHeight + margin; } } diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index 543ab370..0fa146af 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -71,7 +71,7 @@ struct MIDIClockToCVInterface : MidiIO, Module { void resetMidi() override; - json_t *toJson() override{ + json_t *toJson() override { json_t *rootJ = json_object(); addBaseJson(rootJ); json_object_set_new(rootJ, "clock1ratio", json_integer(clock1ratio)); @@ -79,7 +79,7 @@ struct MIDIClockToCVInterface : MidiIO, Module { return rootJ; } - void fromJson(json_t *rootJ) override{ + void fromJson(json_t *rootJ) override { baseFromJson(rootJ); json_t *c1rJ = json_object_get(rootJ, "clock1ratio"); if (c1rJ) { @@ -185,18 +185,18 @@ void MIDIClockToCVInterface::resetMidi() { void MIDIClockToCVInterface::processMidi(std::vector msg) { switch (msg[0]) { - case 0xfa: - start = true; - break; - case 0xfb: - cont = true; - break; - case 0xfc: - stop = true; - break; - case 0xf8: - tick = true; - break; + case 0xfa: + start = true; + break; + case 0xfb: + cont = true; + break; + case 0xfc: + stop = true; + break; + case 0xf8: + tick = true; + break; } @@ -218,13 +218,15 @@ struct ClockRatioItem : MenuItem { struct ClockRatioChoice : ChoiceButton { int *clockRatio; const std::vector ratioNames = {"Sixteenth note (1:4 ratio)", "Eighth note triplet (1:3 ratio)", - "Eighth note (1:2 ratio)", "Quarter note triplet (2:3 ratio)", - "Quarter note (tap speed)", "Half note triplet (4:3 ratio)", - "Half note (2:1 ratio)", "Whole note (4:1 ratio)", - "Two whole notes (8:1 ratio)"}; + "Eighth note (1:2 ratio)", "Quarter note triplet (2:3 ratio)", + "Quarter note (tap speed)", "Half note triplet (4:3 ratio)", + "Half note (2:1 ratio)", "Whole note (4:1 ratio)", + "Two whole notes (8:1 ratio)" + }; const std::vector ratioNames_short = {"1:4 ratio", "1:3 ratio", "1:2 ratio", "2:3 ratio", "1:1 ratio", - "4:3", "2:1 ratio", "4:1 ratio", "8:1 ratio"}; + "4:3", "2:1 ratio", "4:1 ratio", "8:1 ratio" + }; void onAction(EventAction &e) override { Menu *menu = gScene->createMenu(); @@ -270,7 +272,7 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { label->box.pos = Vec(box.size.x - margin - 7 * 15, margin); label->text = "MIDI Clk-CV"; addChild(label); - yPos = labelHeight*2; + yPos = labelHeight * 2; } { @@ -320,8 +322,8 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { addInput(createInput(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK1_RATIO)); ClockRatioChoice *ratioChoice = new ClockRatioChoice(); ratioChoice->clockRatio = &module->clock1ratio; - ratioChoice->box.pos = Vec(int(box.size.x/3), yPos); - ratioChoice->box.size.x = int(box.size.x/1.5 - margin); + ratioChoice->box.pos = Vec(int(box.size.x / 3), yPos); + ratioChoice->box.size.x = int(box.size.x / 1.5 - margin); addChild(ratioChoice); yPos += ratioChoice->box.size.y + margin * 3; @@ -344,8 +346,8 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { addInput(createInput(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK2_RATIO)); ClockRatioChoice *ratioChoice = new ClockRatioChoice(); ratioChoice->clockRatio = &module->clock2ratio; - ratioChoice->box.pos = Vec(int(box.size.x/3), yPos); - ratioChoice->box.size.x = int(box.size.x/1.5 - margin); + ratioChoice->box.pos = Vec(int(box.size.x / 3), yPos); + ratioChoice->box.size.x = int(box.size.x / 1.5 - margin); addChild(ratioChoice); yPos += ratioChoice->box.size.y + margin * 3; diff --git a/src/core/MidiIO.cpp b/src/core/MidiIO.cpp index efd4fcd7..74f71bfb 100644 --- a/src/core/MidiIO.cpp +++ b/src/core/MidiIO.cpp @@ -59,7 +59,8 @@ std::vector MidiIO::getDevices() { RtMidiIn *m; try { m = new RtMidiIn(); - } catch (RtMidiError &error) { + } + catch (RtMidiError &error) { warn("Failed to create RtMidiIn: %s", error.getMessage().c_str()); return names; } diff --git a/src/core/MidiIO.hpp b/src/core/MidiIO.hpp index 0c1d8899..756822d6 100644 --- a/src/core/MidiIO.hpp +++ b/src/core/MidiIO.hpp @@ -9,6 +9,7 @@ using namespace rack; + struct IgnoreFlags { bool midiSysex = true; bool midiTime = true; @@ -20,7 +21,6 @@ struct MidiMessage { double timeStamp; MidiMessage() : bytes(0), timeStamp(0.0) {}; - }; /** @@ -135,10 +135,10 @@ struct TransitionSmoother { switch (m) { case DELTA: /* If the change is smaller, the transition phase is longer */ - this->step = delta > 0 ? delta/l : -delta/l; + this->step = delta > 0 ? delta / l : -delta / l; break; case CONST: - this->step = 1.0/l; + this->step = 1.0 / l; break; } @@ -153,13 +153,13 @@ struct TransitionSmoother { switch (t) { case SMOOTHSTEP: - next += delta*x*x*(3-2*x); + next += delta * x * x * (3 - 2 * x); break; case EXP: - next += delta*x*x; + next += delta * x * x; break; case LIN: - next += delta*x; + next += delta * x; break; } diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index 2503745a..67a4687b 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -169,10 +169,12 @@ void TriggerTextField::onTextChange() { text = ""; begin = end = 0; module->trigger[outNum].num = -1; - }else { + } + else { module->trigger[outNum].num = num; } - } catch (...) { + } + catch (...) { text = ""; begin = end = 0; module->trigger[outNum].num = -1; @@ -271,7 +273,8 @@ MIDITriggerToCVWidget::MIDITriggerToCVWidget() { if ((i + 1) % 4 == 0) { yPos += 47 + margin; - } else { + } + else { yPos -= labelHeight + margin; } } diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 7df68601..ce18b955 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -140,40 +140,41 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { return; switch (status) { - // note off - case 0x8: { + // note off + case 0x8: { + gate = false; + } + break; + case 0x9: // note on + if (data2 > 0) { + gate = true; + } + else { + // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. gate = false; } - break; - case 0x9: // note on - if (data2 > 0) { - gate = true; - } else { - // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. - gate = false; - } - break; - case 0xa: // channel aftertouch - for (int i = 0; i < 4; i++) { - if (activeKeys[i].pitch == data1) { - activeKeys[i].at = data2; - } + break; + case 0xa: // channel aftertouch + for (int i = 0; i < 4; i++) { + if (activeKeys[i].pitch == data1) { + activeKeys[i].at = data2; } - return; - case 0xb: // cc - if (data1 == 0x40) { // pedal - pedal = (data2 >= 64); - if (!pedal) { - open.clear(); - for (int i = 0; i < 4; i++) { - activeKeys[i].gate = false; - open.push_back(i); - } + } + return; + case 0xb: // cc + if (data1 == 0x40) { // pedal + pedal = (data2 >= 64); + if (!pedal) { + open.clear(); + for (int i = 0; i < 4; i++) { + activeKeys[i].gate = false; + open.push_back(i); } } - return; - default: - return; + } + return; + default: + return; } if (pedal && !gate) { @@ -201,25 +202,25 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { } if (!activeKeys[0].gate && !activeKeys[1].gate && - !activeKeys[2].gate && !activeKeys[3].gate) { + !activeKeys[2].gate && !activeKeys[3].gate) { open.sort(); } switch (mode) { - case RESET: - if (open.size() >= 4) { - for (int i = 0; i < 4; i++) { - activeKeys[i].gate = false; - open.push_back(i); - } + case RESET: + if (open.size() >= 4) { + for (int i = 0; i < 4; i++) { + activeKeys[i].gate = false; + open.push_back(i); } - break; - case REASSIGN: - open.push_back(open.front()); - break; - case ROTATE: - break; + } + break; + case REASSIGN: + open.push_back(open.front()); + break; + case ROTATE: + break; } int next = open.front(); @@ -313,7 +314,7 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { } addParam(createParam(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, - 0.0)); + 0.0)); addChild(createLight>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT)); { Label *label = new Label(); From 6787867d27f46ec183a217764bcd1bd89b36da05 Mon Sep 17 00:00:00 2001 From: Jeremy Wentworth Date: Sat, 9 Dec 2017 12:36:48 -0500 Subject: [PATCH 28/66] clarified comment about sizes (#510) * Update app.hpp * Update app.hpp --- include/app.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/app.hpp b/include/app.hpp index 9cea300e..dc199964 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -20,7 +20,7 @@ struct SVGPanel; // module //////////////////// -// A 1U module should be 15x380. Thus the width of a module should be a factor of 15. +// A 1HPx3U module should be 15x380. Thus the width of a module should be a factor of 15. #define RACK_GRID_WIDTH 15 #define RACK_GRID_HEIGHT 380 static const Vec RACK_GRID_SIZE = Vec(15, 380); From 36cd050ba428a7687bea3a60b115c6c03c56bbdb Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 10 Dec 2017 17:46:15 -0500 Subject: [PATCH 29/66] Remove Module::reset(), Module::initialize(), and Module::randomize(), add onRandomize(), onReset(), onCreate(), and onDelete() --- include/app.hpp | 2 ++ include/engine.hpp | 13 ++++++++----- src/app/ModuleWidget.cpp | 18 +++++++++++++++--- src/app/RackWidget.cpp | 2 ++ src/core/AudioInterface.cpp | 2 +- src/core/MidiCCToCV.cpp | 2 +- src/core/MidiToCV.cpp | 2 +- src/core/MidiTriggerToCV.cpp | 2 +- src/core/QuadMidiToCV.cpp | 2 +- 9 files changed, 32 insertions(+), 13 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index 9cea300e..ea9817d4 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -47,6 +47,8 @@ struct ModuleWidget : OpaqueWidget { virtual json_t *toJson(); virtual void fromJson(json_t *rootJ); + virtual void create(); + virtual void _delete(); /** Disconnects cables from all ports Called when the user clicks Disconnect Cables in the context menu. */ diff --git a/include/engine.hpp b/include/engine.hpp index 289b47ad..29eedfdf 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -65,11 +65,14 @@ struct Module { virtual void step() {} virtual void onSampleRateChange() {} - /** Override these to implement spacial behavior when user clicks Initialize and Randomize */ - virtual void reset() {} - virtual void randomize() {} - /** Deprecated */ - virtual void initialize() final {} + /** Called when module is created by the Add Module popup, cloning, or when loading a patch or autosave */ + virtual void onCreate() {} + /** 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() {} + /** Called when user clicks Randomize in the module context menu */ + virtual void onRandomize() {} /** Override these to store extra internal data in the "data" property */ virtual json_t *toJson() { return NULL; } diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 7de226f6..bdd5e0d8 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -121,12 +121,24 @@ void ModuleWidget::disconnect() { } } +void ModuleWidget::create() { + if (module) { + module->onCreate(); + } +} + +void ModuleWidget::_delete() { + if (module) { + module->onDelete(); + } +} + void ModuleWidget::reset() { for (ParamWidget *param : params) { param->setValue(param->defaultValue); } if (module) { - module->reset(); + module->onReset(); } } @@ -135,7 +147,7 @@ void ModuleWidget::randomize() { param->randomize(); } if (module) { - module->randomize(); + module->onRandomize(); } } @@ -191,7 +203,7 @@ void ModuleWidget::onMouseMove(EventMouseMove &e) { gRackWidget->deleteModule(this); this->finalizeEvents(); delete this; - // Kinda sketchy because events will be passed further down the tree + e.consumed = true; return; } } diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 2cbb7a52..a8003e93 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -306,9 +306,11 @@ void RackWidget::fromJson(json_t *rootJ) { void RackWidget::addModule(ModuleWidget *m) { moduleContainer->addChild(m); + m->create(); } void RackWidget::deleteModule(ModuleWidget *m) { + m->_delete(); moduleContainer->removeChild(m); } diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 937ace0d..e120362c 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -128,7 +128,7 @@ struct AudioInterface : Module { openStream(); } - void reset() override { + void onReset() override { closeStream(); } }; diff --git a/src/core/MidiCCToCV.cpp b/src/core/MidiCCToCV.cpp index dcafc589..de651a1b 100644 --- a/src/core/MidiCCToCV.cpp +++ b/src/core/MidiCCToCV.cpp @@ -81,7 +81,7 @@ struct MIDICCToCVInterface : MidiIO, Module { } } - void reset() override { + void onReset() override { resetMidi(); } diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index 4a417ba6..783a78ce 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -74,7 +74,7 @@ struct MIDIToCVInterface : MidiIO, Module { baseFromJson(rootJ); } - void reset() override { + void onReset() override { resetMidi(); } diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index 67a4687b..376dccf3 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -63,7 +63,7 @@ struct MIDITriggerToCVInterface : MidiIO, Module { } } - void reset() override { + void onReset() override { resetMidi(); } }; diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index ce18b955..99009699 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -72,7 +72,7 @@ struct QuadMIDIToCVInterface : MidiIO, Module { baseFromJson(rootJ); } - void reset() override { + void onReset() override { resetMidi(); } From d9efe431122936b500b001f3a9f99f87928a8993 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 12 Dec 2017 10:12:20 -0500 Subject: [PATCH 30/66] Don't write empty file if toJson is unsuccessful --- src/app/RackWidget.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index a8003e93..d01cf012 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -102,20 +102,19 @@ void RackWidget::saveAsDialog() { } } - void RackWidget::savePatch(std::string path) { info("Saving patch %s", path.c_str()); - FILE *file = fopen(path.c_str(), "w"); - if (!file) + json_t *rootJ = toJson(); + if (!rootJ) return; - json_t *rootJ = toJson(); - if (rootJ) { + FILE *file = fopen(path.c_str(), "w"); + if (file) { json_dumpf(rootJ, file, JSON_INDENT(2)); - json_decref(rootJ); + fclose(file); } - fclose(file); + json_decref(rootJ); } void RackWidget::loadPatch(std::string path) { From 45e28ebbb1d1454e6e020ba295087b09da966017 Mon Sep 17 00:00:00 2001 From: Matt Demanett Date: Tue, 19 Dec 2017 22:33:55 -0500 Subject: [PATCH 31/66] Replace libsamplerate with libspeexdsp for sample rate conversion. --- Makefile | 14 +++++----- dep/Makefile | 20 +++++++------- include/dsp/samplerate.hpp | 52 ++++++++++++++++--------------------- src/core/AudioInterface.cpp | 4 +-- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 62e32b68..6438ad1b 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ ifeq ($(ARCH), lin) LDFLAGS += -rdynamic \ -lpthread -lGL -ldl \ $(shell pkg-config --libs gtk+-2.0) \ - -Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lrtaudio -lrtmidi + -Ldep/lib -lGLEW -lglfw -ljansson -lspeexdsp -lcurl -lzip -lrtaudio -lrtmidi TARGET = Rack endif @@ -23,7 +23,7 @@ ifeq ($(ARCH), mac) CXXFLAGS += -DAPPLE -stdlib=libc++ LDFLAGS += -stdlib=libc++ -lpthread -ldl \ -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo \ - -Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lrtaudio -lrtmidi + -Ldep/lib -lGLEW -lglfw -ljansson -lspeexdsp -lcurl -lzip -lrtaudio -lrtmidi TARGET = Rack BUNDLE = dist/$(TARGET).app endif @@ -34,7 +34,7 @@ ifeq ($(ARCH), win) -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ -lgdi32 -lopengl32 -lcomdlg32 -lole32 \ -Ldep/lib -lglew32 -lglfw3dll -lcurl -lzip -lrtaudio -lrtmidi \ - -Wl,-Bstatic -ljansson -lsamplerate + -Wl,-Bstatic -ljansson -lspeexdsp TARGET = Rack.exe OBJECTS = Rack.res endif @@ -109,7 +109,7 @@ ifeq ($(ARCH), mac) cp dep/lib/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libjansson.4.dylib $(BUNDLE)/Contents/MacOS/ - cp dep/lib/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/ + cp dep/lib/libspeexdsp.1.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ @@ -118,7 +118,7 @@ ifeq ($(ARCH), mac) install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libjansson.4.dylib @executable_path/libjansson.4.dylib $(BUNDLE)/Contents/MacOS/Rack - install_name_tool -change $(PWD)/dep/lib/libsamplerate.0.dylib @executable_path/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/Rack + install_name_tool -change $(PWD)/dep/lib/libspeexdsp.1.dylib @executable_path/libspeexdsp.1.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack @@ -146,7 +146,7 @@ ifeq ($(ARCH), win) cp dep/bin/libcurl-4.dll dist/Rack/ cp dep/bin/libjansson-4.dll dist/Rack/ cp dep/bin/librtmidi-4.dll dist/Rack/ - cp dep/bin/libsamplerate-0.dll dist/Rack/ + cp dep/bin/libspeexdsp.dll dist/Rack/ cp dep/bin/libzip-5.dll dist/Rack/ cp dep/bin/librtaudio.dll dist/Rack/ mkdir -p dist/Rack/plugins @@ -161,7 +161,7 @@ ifeq ($(ARCH), lin) mkdir -p dist/Rack cp -R LICENSE* res dist/Rack/ cp Rack Rack.sh dist/Rack/ - cp dep/lib/libsamplerate.so.0 dist/Rack/ + cp dep/lib/libspeexdsp.so dist/Rack/ cp dep/lib/libjansson.so.4 dist/Rack/ cp dep/lib/libGLEW.so.2.1 dist/Rack/ cp dep/lib/libglfw.so.3 dist/Rack/ diff --git a/dep/Makefile b/dep/Makefile index 9218d608..eff4442e 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -26,7 +26,7 @@ ifeq ($(ARCH),lin) glew = lib/libGLEW.so glfw = lib/libglfw.so jansson = lib/libjansson.so - libsamplerate = lib/libsamplerate.so + libspeexdsp = lib/libspeexdsp.so libcurl = lib/libcurl.so libzip = lib/libzip.so rtmidi = lib/librtmidi.so @@ -37,7 +37,7 @@ ifeq ($(ARCH),mac) glew = lib/libGLEW.dylib glfw = lib/libglfw.dylib jansson = lib/libjansson.dylib - libsamplerate = lib/libsamplerate.dylib + libspeexdsp = lib/libspeexdsp.dylib libcurl = lib/libcurl.dylib libzip = lib/libzip.dylib rtmidi = lib/librtmidi.dylib @@ -48,7 +48,7 @@ ifeq ($(ARCH),win) glew = bin/glew32.dll glfw = bin/glfw3.dll jansson = bin/libjansson-4.dll - libsamplerate = bin/libsamplerate-0.dll + libspeexdsp = bin/libspeexdsp.dll libcurl = bin/libcurl-4.dll libzip = bin/libzip-5.dll rtmidi = bin/librtmidi-4.dll @@ -71,7 +71,7 @@ endif .NOTPARALLEL: -all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) +all: $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) @echo "" @echo "#######################################" @echo "# Built all dependencies successfully #" @@ -99,12 +99,12 @@ $(jansson): $(MAKE) -C jansson-2.10 $(MAKE) -C jansson-2.10 install -$(libsamplerate): - $(WGET) http://www.mega-nerd.com/SRC/libsamplerate-0.1.9.tar.gz - $(UNTAR) libsamplerate-0.1.9.tar.gz - cd libsamplerate-0.1.9 && ./configure --prefix="$(LOCAL)" --disable-fftw --disable-sndfile - $(MAKE) -C libsamplerate-0.1.9/src - $(MAKE) -C libsamplerate-0.1.9/src install +$(libspeexdsp): + $(WGET) https://github.com/xiph/speexdsp/archive/SpeexDSP-1.2rc3.tar.gz + $(UNTAR) SpeexDSP-1.2rc3.tar.gz + cd speexdsp-SpeexDSP-1.2rc3 && ./autogen.sh && ./configure --prefix="$(LOCAL)" + $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 + $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 install $(libcurl): $(WGET) https://github.com/curl/curl/releases/download/curl-7_56_0/curl-7.56.0.tar.gz diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index 3a87d640..623ba650 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -1,8 +1,7 @@ #pragma once #include -#include -#include +#include #include "frame.hpp" @@ -10,50 +9,45 @@ namespace rack { template struct SampleRateConverter { - SRC_STATE *state; - SRC_DATA data; + SpeexResamplerState *state = NULL; + bool noConversion = true; SampleRateConverter() { int error; - state = src_new(SRC_SINC_FASTEST, CHANNELS, &error); - assert(!error); - - data.src_ratio = 1.0; - data.end_of_input = false; + state = speex_resampler_init(CHANNELS, 44100, 44100, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error); + assert(error == RESAMPLER_ERR_SUCCESS); } ~SampleRateConverter() { - src_delete(state); + speex_resampler_destroy(state); } - /** output_sample_rate / input_sample_rate */ - void setRatio(float r) { - src_set_ratio(state, r); - data.src_ratio = r; + + void setRates(float inRate, float outRate) { + int error = speex_resampler_set_rate(state, inRate, outRate); + assert(error == RESAMPLER_ERR_SUCCESS); + noConversion = inRate == outRate; } - void setRatioSmooth(float r) { - data.src_ratio = r; + + void setRatioSmooth(float ratio) DEPRECATED { + // FIXME: this doesn't do a smooth change -- speex doesn't appear to support that. + const int base = 1000; + setRates(base, ratio * base); } + /** `in` and `out` are interlaced with the number of channels */ void process(const Frame *in, int *inFrames, Frame *out, int *outFrames) { - /* - if (nearf(data.src_ratio, 1.0)) { - int len = mini(*inFrames, *outFrames); + if (noConversion) { + int len = std::min(*inFrames, *outFrames); memcpy(out, in, len * sizeof(Frame)); *inFrames = len; *outFrames = len; return; } - */ - // Old versions of libsamplerate use float* here instead of const float* - data.data_in = (float*) in; - data.input_frames = *inFrames; - data.data_out = (float*) out; - data.output_frames = *outFrames; - src_process(state, &data); - *inFrames = data.input_frames_used; - *outFrames = data.output_frames_gen; + speex_resampler_process_interleaved_float(state, (const float*)in, (unsigned int*)inFrames, (float*)out, (unsigned int*)outFrames); } + void reset() { - src_reset(state); + int error = speex_resampler_reset_mem(state); + assert(error == RESAMPLER_ERR_SUCCESS); } }; diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index e120362c..b9bdfdca 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -169,7 +169,7 @@ void AudioInterface::step() { // Once full, sample rate convert the input // inputBuffer -> SRC -> inputSrcBuffer if (inputBuffer.full()) { - inputSrc.setRatio(sampleRate / engineGetSampleRate()); + inputSrc.setRates(engineGetSampleRate(), sampleRate); int inLen = inputBuffer.size(); int outLen = inputSrcBuffer.capacity(); inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); @@ -211,7 +211,7 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames } // Pass output through sample rate converter - outputSrc.setRatio(engineGetSampleRate() / sampleRate); + outputSrc.setRates(sampleRate, engineGetSampleRate()); int inLen = numFrames; int outLen = outputBuffer.capacity(); outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); From cf34b2cf55c69f47ab3a4b3ff6606202a112e925 Mon Sep 17 00:00:00 2001 From: Matt Demanett Date: Wed, 20 Dec 2017 00:47:52 -0500 Subject: [PATCH 32/66] libspeexdsp fix for windows. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6438ad1b..42f169fa 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,7 @@ ifeq ($(ARCH), win) cp dep/bin/libcurl-4.dll dist/Rack/ cp dep/bin/libjansson-4.dll dist/Rack/ cp dep/bin/librtmidi-4.dll dist/Rack/ - cp dep/bin/libspeexdsp.dll dist/Rack/ + cp dep/bin/libspeexdsp-1.dll dist/Rack/ cp dep/bin/libzip-5.dll dist/Rack/ cp dep/bin/librtaudio.dll dist/Rack/ mkdir -p dist/Rack/plugins From b8f300d1306c44c59cf212cdc9f6861066e8ec1c Mon Sep 17 00:00:00 2001 From: Matt Demanett Date: Wed, 20 Dec 2017 01:06:13 -0500 Subject: [PATCH 33/66] Header fix for linux. --- include/dsp/samplerate.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index 623ba650..73478582 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "frame.hpp" From 170561fb9bf78e6a3afea62ed2a61ca688aa6054 Mon Sep 17 00:00:00 2001 From: Matt Demanett Date: Wed, 20 Dec 2017 19:40:13 -0500 Subject: [PATCH 34/66] Only set speex conversion rates if they actually change. --- include/dsp/samplerate.hpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index 73478582..b7087b34 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -12,20 +12,26 @@ template struct SampleRateConverter { SpeexResamplerState *state = NULL; bool noConversion = true; + int inRate = 44100; + int outRate = 44100; SampleRateConverter() { int error; - state = speex_resampler_init(CHANNELS, 44100, 44100, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error); + state = speex_resampler_init(CHANNELS, inRate, outRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error); assert(error == RESAMPLER_ERR_SUCCESS); } ~SampleRateConverter() { speex_resampler_destroy(state); } - void setRates(float inRate, float outRate) { - int error = speex_resampler_set_rate(state, inRate, outRate); - assert(error == RESAMPLER_ERR_SUCCESS); - noConversion = inRate == outRate; + void setRates(int in, int out) { + if (in != inRate || out != outRate) { // speex doesn't optimize setting the rates to the existing values. + int error = speex_resampler_set_rate(state, in, out); + assert(error == RESAMPLER_ERR_SUCCESS); + inRate = in; + outRate = out; + noConversion = in == out; + } } void setRatioSmooth(float ratio) DEPRECATED { From 5584def2e8b616f44269a536036042a304ac0a0e Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 23 Dec 2017 16:40:05 +0100 Subject: [PATCH 35/66] Implement single message per step for all midi modules and Fix quad midi reset bug --- src/core/MidiCCToCV.cpp | 4 ++-- src/core/MidiClockToCV.cpp | 3 +-- src/core/MidiToCV.cpp | 3 +-- src/core/MidiTriggerToCV.cpp | 3 +-- src/core/QuadMidiToCV.cpp | 10 +++------- 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/core/MidiCCToCV.cpp b/src/core/MidiCCToCV.cpp index de651a1b..6e60e5f3 100644 --- a/src/core/MidiCCToCV.cpp +++ b/src/core/MidiCCToCV.cpp @@ -91,11 +91,11 @@ void MIDICCToCVInterface::step() { if (isPortOpen()) { std::vector message; + // midiIn->getMessage returns empty vector if there are no messages in the queue getMessage(&message); - while (message.size() > 0) { + if (message.size() > 0) { processMidi(message); - getMessage(&message); } } diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index 0fa146af..1b5186c7 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -101,9 +101,8 @@ void MIDIClockToCVInterface::step() { // midiIn->getMessage returns empty vector if there are no messages in the queue getMessage(&message); - while (message.size() > 0) { + if (message.size() > 0) { processMidi(message); - getMessage(&message); } } diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index 783a78ce..df8accd9 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -100,9 +100,8 @@ void MIDIToCVInterface::step() { // midiIn->getMessage returns empty vector if there are no messages in the queue getMessage(&message); - while (message.size() > 0) { + if (message.size() > 0) { processMidi(message); - getMessage(&message); } } diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index 376dccf3..3fb0f1c1 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -75,9 +75,8 @@ void MIDITriggerToCVInterface::step() { // midiIn->getMessage returns empty vector if there are no messages in the queue getMessage(&message); - while (message.size() > 0) { + if (message.size() > 0) { processMidi(message); - getMessage(&message); } } diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 99009699..d6b865c7 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -98,16 +98,11 @@ void QuadMIDIToCVInterface::resetMidi() { void QuadMIDIToCVInterface::step() { if (isPortOpen()) { std::vector message; - int msgsProcessed = 0; // midiIn->getMessage returns empty vector if there are no messages in the queue - // NOTE: For the quadmidi we will process max 4 midi messages per step to avoid - // problems with parallel input. getMessage(&message); - while (msgsProcessed < 4 && message.size() > 0) { + if (message.size() > 0) { processMidi(message); - getMessage(&message); - msgsProcessed++; } } @@ -183,7 +178,7 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { if (!gate) { for (int i = 0; i < 4; i++) { - if (activeKeys[i].pitch == data1) { + if (activeKeys[i].pitch == data1 && activeKeys[i].gate) { activeKeys[i].gate = false; activeKeys[i].vel = data2; if (std::find(open.begin(), open.end(), i) != open.end()) { @@ -210,6 +205,7 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { switch (mode) { case RESET: if (open.size() >= 4) { + open.clear(); for (int i = 0; i < 4; i++) { activeKeys[i].gate = false; open.push_back(i); From 4d3c3e3502abf11de3300ec596756c2a0fc05ef8 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 23 Dec 2017 16:43:23 +0100 Subject: [PATCH 36/66] Fix rtaudio link (pull from git) --- dep/Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dep/Makefile b/dep/Makefile index 9218d608..d3c0525e 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -123,11 +123,10 @@ $(libzip): $(MAKE) -C libzip-1.2.0 install $(rtmidi): - $(WGET) http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-3.0.0.tar.gz - $(UNTAR) rtmidi-3.0.0.tar.gz - cd rtmidi-3.0.0 && ./configure --prefix="$(LOCAL)" - $(MAKE) -C rtmidi-3.0.0 - $(MAKE) -C rtmidi-3.0.0 install + git clone https://github.com/thestk/rtmidi.git + cd rtmidi && ./autogen.sh --no-configure && ./configure --prefix="$(LOCAL)" + $(MAKE) -C rtmidi + $(MAKE) -C rtmidi install $(rtaudio): cd rtaudio && mkdir -p cmakebuild From f06bca3a89ec18126390adf706d0f88dfd9f6ce1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 23 Dec 2017 15:35:19 -0500 Subject: [PATCH 37/66] Update CHANGELOG for v0.5.1 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d39a2f16..edb6a485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Tip: Use `git checkout v0.4.0` for example to check out any previous version mentioned here. +### v0.5.1 (2017-12-19) + +- Added Plugin Manager support +- Fixed metadata panel in the Add Module window + +- Fundamental + - Added Sequential Switch 1 & 2 + + ### v0.5.0 (2017-11-21) - Added zoom scaling from 25% to 200% @@ -27,6 +36,7 @@ Tip: Use `git checkout v0.4.0` for example to check out any previous version men ### v0.4.0 (2017-10-13) + - Cables can now stack on output ports - Added sub-menus for each plugin, includes optional plugin metadata like URLs - Added MIDI CC-to-CV Interface, updated MIDI-to-CV Interface @@ -49,6 +59,7 @@ Tip: Use `git checkout v0.4.0` for example to check out any previous version men ### v0.3.2 (2017-09-25) + - Added key commands - Fixed "invisible knobs/ports" rendering bug for ~2010 Macs - Added "allowCursorLock" to settings.json (set to "false" for touch screen support) @@ -73,4 +84,5 @@ Tip: Use `git checkout v0.4.0` for example to check out any previous version men ### v0.3.0 (2017-09-10) + - Knobcon public Beta release From 23da7e61ef3a259a5a4f38cd025c6fcb5189de92 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 23 Dec 2017 15:35:33 -0500 Subject: [PATCH 38/66] Update samplerate.hpp API --- include/dsp/samplerate.hpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index b7087b34..a4a99880 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -11,38 +11,33 @@ namespace rack { template struct SampleRateConverter { SpeexResamplerState *state = NULL; - bool noConversion = true; - int inRate = 44100; - int outRate = 44100; + bool bypass = false; SampleRateConverter() { int error; - state = speex_resampler_init(CHANNELS, inRate, outRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error); + state = speex_resampler_init(CHANNELS, 44100, 44100, SPEEX_RESAMPLER_QUALITY_DEFAULT, &error); assert(error == RESAMPLER_ERR_SUCCESS); } ~SampleRateConverter() { speex_resampler_destroy(state); } - void setRates(int in, int out) { - if (in != inRate || out != outRate) { // speex doesn't optimize setting the rates to the existing values. - int error = speex_resampler_set_rate(state, in, out); - assert(error == RESAMPLER_ERR_SUCCESS); - inRate = in; - outRate = out; - noConversion = in == out; - } + void setQuality(int quality) { + speex_resampler_set_quality(state, quality); } - void setRatioSmooth(float ratio) DEPRECATED { - // FIXME: this doesn't do a smooth change -- speex doesn't appear to support that. - const int base = 1000; - setRates(base, ratio * base); + void setRates(int inRate, int outRate) { + spx_uint32_t oldInRate, oldOutRate; + speex_resampler_get_rate(state, &oldInRate, &oldOutRate); + if (inRate == (int) oldInRate && outRate == (int) oldOutRate) + return; + int error = speex_resampler_set_rate(state, inRate, outRate); + assert(error == RESAMPLER_ERR_SUCCESS); } /** `in` and `out` are interlaced with the number of channels */ void process(const Frame *in, int *inFrames, Frame *out, int *outFrames) { - if (noConversion) { + if (bypass) { int len = std::min(*inFrames, *outFrames); memcpy(out, in, len * sizeof(Frame)); *inFrames = len; From d2aa5af287d5599fca78537d8de4ba9ea5e4f1b9 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 24 Dec 2017 11:27:17 +0100 Subject: [PATCH 39/66] Fix quad midi pedal handling --- src/core/QuadMidiToCV.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index d6b865c7..e3c7670a 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -10,6 +10,7 @@ struct MidiKey { int at = 0; // aftertouch int vel = 0; // velocity bool gate = false; + bool pedal_gate_released = false; }; struct QuadMIDIToCVInterface : MidiIO, Module { @@ -162,6 +163,10 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { if (!pedal) { open.clear(); for (int i = 0; i < 4; i++) { + if (activeKeys[i].pedal_gate_released) { + activeKeys[i].pedal_gate_released = false; + continue; + } activeKeys[i].gate = false; open.push_back(i); } @@ -173,7 +178,12 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { } if (pedal && !gate) { - return; + for (int i = 0; i < 4; i++) { + if (activeKeys[i].pitch == data1) { + activeKeys[i].pedal_gate_released = true; + return; + } + } } if (!gate) { @@ -197,7 +207,7 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { } if (!activeKeys[0].gate && !activeKeys[1].gate && - !activeKeys[2].gate && !activeKeys[3].gate) { + !activeKeys[2].gate && !activeKeys[3].gate) { open.sort(); } @@ -310,7 +320,7 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { } addParam(createParam(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, - 0.0)); + 0.0)); addChild(createLight>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT)); { Label *label = new Label(); From 6a9bf0146d8bea400f5d42c30eba45e5604cccf6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 24 Dec 2017 10:11:11 -0500 Subject: [PATCH 40/66] Fix merge conflict --- Makefile | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Makefile b/Makefile index db4fe127..3a43250c 100644 --- a/Makefile +++ b/Makefile @@ -112,14 +112,9 @@ ifeq ($(ARCH), mac) cp dep/lib/libspeexdsp.1.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ -<<<<<<< HEAD - cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ - cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ -======= cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ cp dep/lib/libcrypto.1.1.dylib $(BUNDLE)/Contents/MacOS/ ->>>>>>> v0.5 install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack @@ -127,14 +122,9 @@ ifeq ($(ARCH), mac) install_name_tool -change $(PWD)/dep/lib/libspeexdsp.1.dylib @executable_path/libspeexdsp.1.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack -<<<<<<< HEAD - install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack - install_name_tool -change librtaudio.dylib @executable_path/librtaudio.dylib $(BUNDLE)/Contents/MacOS/Rack -======= install_name_tool -change librtaudio.dylib @executable_path/librtaudio.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack install_name_tool -change $(PWD)/dep/lib/libcrypto.1.1.dylib @executable_path/libcrypto.1.1.dylib $(BUNDLE)/Contents/MacOS/Rack ->>>>>>> v0.5 otool -L $(BUNDLE)/Contents/MacOS/Rack From c4fed6d8785ca0e2a74a2521428fac2363d84860 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 24 Dec 2017 20:29:22 -0500 Subject: [PATCH 41/66] Add `make dist` to plugins.mk --- plugin.mk | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugin.mk b/plugin.mk index 1977a1d0..b1d9e4cd 100644 --- a/plugin.mk +++ b/plugin.mk @@ -21,6 +21,8 @@ ifeq ($(ARCH), win) TARGET = plugin.dll endif +DISTRIBUTABLES += $(TARGET) + all: $(TARGET) @@ -28,3 +30,11 @@ include ../../compile.mk clean: rm -rfv build $(TARGET) dist + +dist: all + rm -rf dist + mkdir -p dist/$(SLUG) + cp -R $(DISTRIBUTABLES) dist/$(SLUG)/ + cd dist && zip -5 -r $(SLUG)-$(VERSION)-$(ARCH).zip $(SLUG) + +.PHONY: clean dist \ No newline at end of file From 57da20904dfac3d6af53053bfd48715a9a78f04d Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 26 Dec 2017 10:23:21 -0500 Subject: [PATCH 42/66] Remove interactive mode from dep `make clean` --- dep/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dep/Makefile b/dep/Makefile index 2066846d..6aaa162a 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -147,4 +147,4 @@ $(rtaudio): $(MAKE) -C rtaudio/cmakebuild install clean: - git clean -ffdxi + git clean -ffdx From f4834d816389e0fbb8c238c69add3afa383b962a Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 26 Dec 2017 10:48:45 -0500 Subject: [PATCH 43/66] Build system fixes --- compile.mk | 15 +++++++++------ dep/Makefile | 16 +++++++++++----- include/plugin.hpp | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/compile.mk b/compile.mk index 0ebc1501..307c6fe7 100644 --- a/compile.mk +++ b/compile.mk @@ -1,5 +1,5 @@ ifdef VERSION -FLAGS += -DVERSION=$(VERSION) + FLAGS += -DVERSION=$(VERSION) endif # Generate dependency files alongside the object files @@ -9,7 +9,7 @@ FLAGS += -g FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only FLAGS += -Wall -Wextra -Wno-unused-parameter ifneq ($(ARCH), mac) -CXXFLAGS += -Wsuggest-override + CXXFLAGS += -Wsuggest-override endif CXXFLAGS += -std=c++11 @@ -32,6 +32,9 @@ ifeq ($(ARCH), win) FLAGS += -D_USE_MATH_DEFINES endif +CFLAGS += $(FLAGS) +CXXFLAGS += $(FLAGS) + OBJECTS += $(patsubst %, build/%.o, $(SOURCES)) DEPS = $(patsubst %, build/%.d, $(SOURCES)) @@ -47,16 +50,16 @@ $(TARGET): $(OBJECTS) build/%.c.o: %.c @mkdir -p $(@D) - $(CC) $(FLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) -c -o $@ $< build/%.cpp.o: %.cpp @mkdir -p $(@D) - $(CXX) $(FLAGS) $(CXXFLAGS) -c -o $@ $< + $(CXX) $(CXXFLAGS) -c -o $@ $< build/%.cc.o: %.cc @mkdir -p $(@D) - $(CXX) $(FLAGS) $(CXXFLAGS) -c -o $@ $< + $(CXX) $(CXXFLAGS) -c -o $@ $< build/%.m.o: %.m @mkdir -p $(@D) - $(CC) $(FLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/dep/Makefile b/dep/Makefile index 6aaa162a..0d79b552 100755 --- a/dep/Makefile +++ b/dep/Makefile @@ -3,14 +3,20 @@ LOCAL = $(shell pwd) # Arch-specifics include ../arch.mk +FLAGS += -g -O3 -march=nocona + ifeq ($(ARCH),mac) - MACOS_FLAGS = -mmacosx-version-min=10.7 -stdlib=libc++ - export CFLAGS = $(MACOS_FLAGS) - export CXXFLAGS = $(MACOS_FLAGS) - export CPPFLAGS = $(MACOS_FLAGS) - export LDFLAGS = $(MACOS_FLAGS) + FLAGS += -mmacosx-version-min=10.7 -stdlib=libc++ + LDFLAGS += -mmacosx-version-min=10.7 -stdlib=libc++ endif +CFLAGS += $(FLAGS) +CXXFLAGS += $(FLAGS) +export CFLAGS +export CXXFLAGS +export LDFLAGS + + # Commands WGET = wget -nc UNTAR = tar xf diff --git a/include/plugin.hpp b/include/plugin.hpp index 9f258352..85bb2561 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -24,11 +24,12 @@ struct Plugin { */ std::string slug; - /** The version of your plugin (optional) + /** The version of your plugin Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266 Do not include the "v" in "v1.0" for example. */ std::string version; + /** URL for plugin homepage (optional) */ std::string website; /** URL for plugin manual (optional) */ From 3e5890b79d192e329d9ef48606aea77e3665070c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 26 Dec 2017 17:33:00 -0500 Subject: [PATCH 44/66] Added support for continuous encoders, aka Knob widgets with infinite min/max values --- src/app/Knob.cpp | 6 +++++- src/app/SVGKnob.cpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index fbb5ee37..f47f3349 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -18,7 +18,11 @@ void Knob::onDragStart(EventDragStart &e) { void Knob::onDragMove(EventDragMove &e) { // Drag slower if Mod - float delta = KNOB_SENSITIVITY * (maxValue - minValue) * -e.mouseRel.y * speed; + float range = maxValue - minValue; + float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed; + if (std::isfinite(range)) + delta *= range; + if (guiIsModPressed()) delta /= 16.0; dragValue += delta; diff --git a/src/app/SVGKnob.cpp b/src/app/SVGKnob.cpp index ae25ddb3..fc96b131 100644 --- a/src/app/SVGKnob.cpp +++ b/src/app/SVGKnob.cpp @@ -23,7 +23,9 @@ void SVGKnob::step() { // Re-transform TransformWidget if dirty if (dirty) { tw->box.size = box.size; - float angle = rescalef(value, minValue, maxValue, minAngle, maxAngle); + float angle = 0.0; + if (std::isfinite(minValue) && std::isfinite(maxValue)) + angle = rescalef(value, minValue, maxValue, minAngle, maxAngle); tw->identity(); // Scale SVG to box tw->scale(box.size.div(sw->box.size)); From a99bca7daa192b1bb90eac621a70eb0e0962dcdd Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 27 Dec 2017 19:37:05 +0100 Subject: [PATCH 45/66] fix pedal gate release --- src/core/QuadMidiToCV.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index e3c7670a..84c7335b 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -161,14 +161,15 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { if (data1 == 0x40) { // pedal pedal = (data2 >= 64); if (!pedal) { - open.clear(); for (int i = 0; i < 4; i++) { if (activeKeys[i].pedal_gate_released) { - activeKeys[i].pedal_gate_released = false; - continue; + activeKeys[i].gate = false; + activeKeys[i].pedal_gate_released = false; + if (std::find(open.begin(), open.end(), i) != open.end()) { + open.remove(i); + } + open.push_front(i); } - activeKeys[i].gate = false; - open.push_back(i); } } } @@ -179,11 +180,11 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { if (pedal && !gate) { for (int i = 0; i < 4; i++) { - if (activeKeys[i].pitch == data1) { + if (activeKeys[i].pitch == data1 && activeKeys[i].gate) { activeKeys[i].pedal_gate_released = true; - return; } } + return; } if (!gate) { @@ -240,10 +241,12 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { open.push_front(i); activeKeys[i].gate = false; + activeKeys[i].pedal_gate_released = false; } } activeKeys[next].gate = true; + activeKeys[next].pedal_gate_released = false; activeKeys[next].pitch = data1; activeKeys[next].vel = data2; } From f129f026f677e470397083f17c4e1923f4234999 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Jan 2018 17:40:36 -0500 Subject: [PATCH 46/66] Add AudioWidget --- include/app.hpp | 15 + include/audio.hpp | 42 ++ .../{components.hpp => componentlibrary.hpp} | 16 + include/rack.hpp | 2 +- include/widgets.hpp | 3 + res/ComponentLibrary/ADAT.svg | 366 +++++++++++++ res/ComponentLibrary/MIDI_DIN.svg | 397 ++++++++++++++ res/ComponentLibrary/USB-B.svg | 376 ++++++++++++++ src/app/AudioWidget.cpp | 115 +++++ src/app/Port.cpp | 2 +- src/app/Toolbar.cpp | 21 +- src/app/WireWidget.cpp | 2 +- src/audio.cpp | 212 ++++++++ src/core/AudioInterface.cpp | 486 ++---------------- 14 files changed, 1605 insertions(+), 450 deletions(-) create mode 100644 include/audio.hpp rename include/{components.hpp => componentlibrary.hpp} (97%) create mode 100644 res/ComponentLibrary/ADAT.svg create mode 100644 res/ComponentLibrary/MIDI_DIN.svg create mode 100644 res/ComponentLibrary/USB-B.svg create mode 100644 src/app/AudioWidget.cpp create mode 100644 src/audio.cpp diff --git a/include/app.hpp b/include/app.hpp index bad646d0..1e1550c0 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -278,6 +278,21 @@ struct MomentarySwitch : virtual Switch { } }; +//////////////////// +// IO widgets +//////////////////// + +struct AudioIO; + +struct AudioWidget : OpaqueWidget { + /** Not owned */ + AudioIO *audioIO = NULL; + void onMouseDown(EventMouseDown &e) override; +}; + +struct MIDIWidget : OpaqueWidget { +}; + //////////////////// // lights //////////////////// diff --git a/include/audio.hpp b/include/audio.hpp new file mode 100644 index 00000000..6570be5d --- /dev/null +++ b/include/audio.hpp @@ -0,0 +1,42 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#include +#pragma GCC diagnostic pop + + +namespace rack { + + +struct AudioIO { + int maxOutputs = 8; + int maxInputs = 8; + + RtAudio *stream = NULL; + // Stream properties + int device = -1; + int sampleRate = 44100; + int blockSize = 256; + int numOutputs = 0; + int numInputs = 0; + + AudioIO(); + ~AudioIO(); + + std::vector listDrivers(); + std::string getDriverName(int driver); + int getDriver(); + void setDriver(int driver); + + int getDeviceCount(); + std::string getDeviceName(int device); + std::string getDeviceDetail(int device); + void openStream(); + void closeStream(); + + std::vector listSampleRates(); +}; + + +} // namespace rack diff --git a/include/components.hpp b/include/componentlibrary.hpp similarity index 97% rename from include/components.hpp rename to include/componentlibrary.hpp index cac1cf91..5ae6c671 100644 --- a/include/components.hpp +++ b/include/componentlibrary.hpp @@ -336,6 +336,22 @@ struct BefacoSlidePot : SVGFader { } }; +//////////////////// +// IO widgets +//////////////////// + +struct USB_B_AudioWidget : AudioWidget, SVGWidget { + USB_B_AudioWidget() { + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/USB-B.svg"))); + } +}; + +struct DIN_MIDIWidget : MIDIWidget, SVGWidget { + DIN_MIDIWidget() { + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/MIDI_DIN.svg"))); + } +}; + //////////////////// // Jacks //////////////////// diff --git a/include/rack.hpp b/include/rack.hpp index b4371009..6177612e 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -7,7 +7,7 @@ #include "engine.hpp" #include "gui.hpp" #include "app.hpp" -#include "components.hpp" +#include "componentlibrary.hpp" namespace rack { diff --git a/include/widgets.hpp b/include/widgets.hpp index 6c8fa9f3..c400d80a 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -25,6 +25,9 @@ inline Vec mm2px(Vec millimeters) { return millimeters.mult(SVG_DPI / 25.4); } +#define CHECKMARK_STRING "✔" +#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") + //////////////////// // resources diff --git a/res/ComponentLibrary/ADAT.svg b/res/ComponentLibrary/ADAT.svg new file mode 100644 index 00000000..5be20bcc --- /dev/null +++ b/res/ComponentLibrary/ADAT.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/res/ComponentLibrary/MIDI_DIN.svg b/res/ComponentLibrary/MIDI_DIN.svg new file mode 100644 index 00000000..58ca88f3 --- /dev/null +++ b/res/ComponentLibrary/MIDI_DIN.svg @@ -0,0 +1,397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/res/ComponentLibrary/USB-B.svg b/res/ComponentLibrary/USB-B.svg new file mode 100644 index 00000000..bed8222e --- /dev/null +++ b/res/ComponentLibrary/USB-B.svg @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/app/AudioWidget.cpp b/src/app/AudioWidget.cpp new file mode 100644 index 00000000..67ca64d9 --- /dev/null +++ b/src/app/AudioWidget.cpp @@ -0,0 +1,115 @@ +#include "app.hpp" +#include "audio.hpp" + + +namespace rack { + + +struct AudioDriverItem : MenuItem { + AudioIO *audioIO; + int driver; + void onAction(EventAction &e) override { + audioIO->setDriver(driver); + } +}; + + +struct AudioDeviceItem : MenuItem { + AudioIO *audioIO; + int device; + void onAction(EventAction &e) override { + audioIO->device = device; + audioIO->openStream(); + } +}; + + +struct AudioSampleRateItem : MenuItem { + AudioIO *audioIO; + int sampleRate; + void onAction(EventAction &e) override { + audioIO->sampleRate = sampleRate; + audioIO->openStream(); + } +}; + + +struct AudioBlockSizeItem : MenuItem { + AudioIO *audioIO; + int blockSize; + void onAction(EventAction &e) override { + audioIO->blockSize = blockSize; + audioIO->openStream(); + } +}; + + +void AudioWidget::onMouseDown(EventMouseDown &e) { + OpaqueWidget::onMouseDown(e); + + if (!audioIO) + return; + + Menu *menu = gScene->createMenu(); + + // Audio driver + menu->addChild(construct(&MenuLabel::text, "Audio driver")); + for (int driver : audioIO->listDrivers()) { + AudioDriverItem *item = new AudioDriverItem(); + item->audioIO = audioIO; + item->driver = driver; + item->text = audioIO->getDriverName(driver); + item->rightText = CHECKMARK(item->driver == audioIO->getDriver()); + menu->addChild(item); + } + menu->addChild(construct()); + + // Audio device + menu->addChild(construct(&MenuLabel::text, "Audio device")); + int deviceCount = audioIO->getDeviceCount(); + { + AudioDeviceItem *item = new AudioDeviceItem(); + item->audioIO = audioIO; + item->device = -1; + item->text = "No device"; + item->rightText = CHECKMARK(item->device == audioIO->device); + menu->addChild(item); + } + for (int device = 0; device < deviceCount; device++) { + AudioDeviceItem *item = new AudioDeviceItem(); + item->audioIO = audioIO; + item->device = device; + item->text = audioIO->getDeviceDetail(device); + item->rightText = CHECKMARK(item->device == audioIO->device); + menu->addChild(item); + } + menu->addChild(construct()); + + // Sample rate + menu->addChild(construct(&MenuLabel::text, "Sample rate")); + for (int sampleRate : audioIO->listSampleRates()) { + AudioSampleRateItem *item = new AudioSampleRateItem(); + item->audioIO = audioIO; + item->sampleRate = sampleRate; + item->text = stringf("%d Hz", sampleRate); + item->rightText = CHECKMARK(item->sampleRate == audioIO->sampleRate); + menu->addChild(item); + } + menu->addChild(construct()); + + // Block size + menu->addChild(construct(&MenuLabel::text, "Block size")); + std::vector blockSizes = {64, 128, 256, 512, 1024, 2048, 4096}; + for (int blockSize : blockSizes) { + AudioBlockSizeItem *item = new AudioBlockSizeItem(); + item->audioIO = audioIO; + item->blockSize = blockSize; + float latency = (float) blockSize / audioIO->sampleRate * 1000.0; + item->text = stringf("%d (%.1f ms)", blockSize, latency); + item->rightText = CHECKMARK(item->blockSize == audioIO->blockSize); + menu->addChild(item); + } +} + + +} // namespace rack diff --git a/src/app/Port.cpp b/src/app/Port.cpp index 8d7b3cbb..17424fc1 100644 --- a/src/app/Port.cpp +++ b/src/app/Port.cpp @@ -1,7 +1,7 @@ #include "app.hpp" #include "gui.hpp" -#include "components.hpp" #include "engine.hpp" +#include "componentlibrary.hpp" namespace rack { diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 6fe03649..00ccbdd9 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -53,13 +53,13 @@ struct FileChoice : ChoiceButton { }; -struct PauseItem : MenuItem { +struct EnginePauseItem : MenuItem { void onAction(EventAction &e) override { gPaused = !gPaused; } }; -struct SampleRateItem : MenuItem { +struct EngineSampleRateItem : MenuItem { float sampleRate; void onAction(EventAction &e) override { engineSetSampleRate(sampleRate); @@ -67,22 +67,21 @@ struct SampleRateItem : MenuItem { } }; -struct SampleRateChoice : ChoiceButton { +struct EngineSampleRateChoice : ChoiceButton { void onAction(EventAction &e) override { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; - PauseItem *pauseItem = new PauseItem(); + EnginePauseItem *pauseItem = new EnginePauseItem(); pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; menu->addChild(pauseItem); - float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; - int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); - for (int i = 0; i < sampleRatesLen; i++) { - SampleRateItem *item = new SampleRateItem(); - item->text = stringf("%.0f Hz", sampleRates[i]); - item->sampleRate = sampleRates[i]; + std::vector sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; + for (float sampleRate : sampleRates) { + EngineSampleRateItem *item = new EngineSampleRateItem(); + item->text = stringf("%.0f Hz", sampleRate); + item->sampleRate = sampleRate; menu->addChild(item); } } @@ -112,7 +111,7 @@ Toolbar::Toolbar() { xPos += margin; { - SampleRateChoice *srChoice = new SampleRateChoice(); + EngineSampleRateChoice *srChoice = new EngineSampleRateChoice(); srChoice->box.pos = Vec(xPos, margin); srChoice->box.size.x = 100; addChild(srChoice); diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 75b73516..f75574de 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -1,6 +1,6 @@ #include "app.hpp" #include "engine.hpp" -#include "components.hpp" +#include "componentlibrary.hpp" #include "gui.hpp" diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 00000000..3484160f --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1,212 @@ +#include "util.hpp" +#include "math.hpp" +#include "audio.hpp" + + +namespace rack { + + +AudioIO::AudioIO() { + setDriver(RtAudio::UNSPECIFIED); +} + +AudioIO::~AudioIO() { + closeStream(); +} + +std::vector AudioIO::listDrivers() { + std::vector apis; + RtAudio::getCompiledApi(apis); + std::vector drivers; + for (RtAudio::Api api : apis) + drivers.push_back((int) api); + return drivers; +} + +std::string AudioIO::getDriverName(int driver) { + switch (driver) { + case RtAudio::UNSPECIFIED: return "Unspecified"; + case RtAudio::LINUX_ALSA: return "ALSA"; + case RtAudio::LINUX_PULSE: return "PulseAudio"; + case RtAudio::LINUX_OSS: return "OSS"; + case RtAudio::UNIX_JACK: return "JACK"; + case RtAudio::MACOSX_CORE: return "Core Audio"; + case RtAudio::WINDOWS_WASAPI: return "WASAPI"; + case RtAudio::WINDOWS_ASIO: return "ASIO"; + case RtAudio::WINDOWS_DS: return "DirectSound"; + case RtAudio::RTAUDIO_DUMMY: return "Dummy"; + default: return "Unknown"; + } +} + +int AudioIO::getDriver() { + if (!stream) + return RtAudio::UNSPECIFIED; + return stream->getCurrentApi(); +} + +void AudioIO::setDriver(int driver) { + // closeStream(); + if (stream) + delete stream; + stream = new RtAudio((RtAudio::Api) driver); +} + +int AudioIO::getDeviceCount() { + if (!stream) + return 0; + return stream->getDeviceCount(); +} + +std::string AudioIO::getDeviceName(int device) { + if (!stream || device < 0) + return ""; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + return deviceInfo.name; + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return ""; + } +} + +std::string AudioIO::getDeviceDetail(int device) { + if (!stream || device < 0) + return ""; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return ""; + } +} + +static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { + AudioIO *audioIO = (AudioIO*) userData; + assert(audioIO); + // audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); + return 0; +} + +void AudioIO::openStream() { + // Close device but remember the current device number + int device = this->device; + closeStream(); + if (!stream) + return; + + // Open new device + if (device >= 0) { + RtAudio::DeviceInfo deviceInfo; + try { + deviceInfo = stream->getDeviceInfo(device); + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return; + } + + numOutputs = mini(deviceInfo.outputChannels, maxOutputs); + numInputs = mini(deviceInfo.inputChannels, maxInputs); + + if (numOutputs == 0 && numInputs == 0) { + warn("Audio device %d has 0 inputs and 0 outputs"); + return; + } + + RtAudio::StreamParameters outParameters; + outParameters.deviceId = device; + outParameters.nChannels = numOutputs; + + RtAudio::StreamParameters inParameters; + inParameters.deviceId = device; + inParameters.nChannels = numInputs; + + RtAudio::StreamOptions options; + // options.flags |= RTAUDIO_SCHEDULE_REALTIME; + + // Find closest sample rate + unsigned int closestSampleRate = 0; + for (unsigned int sr : deviceInfo.sampleRates) { + if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { + closestSampleRate = sr; + } + } + + try { + debug("Opening audio stream %d", device); + stream->openStream( + numOutputs == 0 ? NULL : &outParameters, + numInputs == 0 ? NULL : &inParameters, + RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); + } + catch (RtAudioError &e) { + warn("Failed to open audio stream: %s", e.what()); + return; + } + + try { + debug("Starting audio stream %d", device); + stream->startStream(); + } + catch (RtAudioError &e) { + warn("Failed to start audio stream: %s", e.what()); + return; + } + + // Update sample rate because this may have changed + this->sampleRate = stream->getStreamSampleRate(); + this->device = device; + } +} + +void AudioIO::closeStream() { + if (stream) { + if (stream->isStreamRunning()) { + debug("Aborting audio stream %d", device); + try { + stream->abortStream(); + } + catch (RtAudioError &e) { + warn("Failed to abort stream %s", e.what()); + } + } + if (stream->isStreamOpen()) { + debug("Closing audio stream %d", device); + try { + stream->closeStream(); + } + catch (RtAudioError &e) { + warn("Failed to close stream %s", e.what()); + } + } + } + + // Reset stream settings + device = -1; + numOutputs = 0; + numInputs = 0; +} + +std::vector AudioIO::listSampleRates() { + if (!stream || device < 0) + return {}; + + try { + RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); + std::vector sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); + return sampleRates; + } + catch (RtAudioError &e) { + warn("Failed to query audio device: %s", e.what()); + return {}; + } +} + + +} // namespace rack diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index b9bdfdca..19475460 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include "core.hpp" +#include "audio.hpp" #include "dsp/samplerate.hpp" #include "dsp/ringbuffer.hpp" @@ -29,13 +29,7 @@ struct AudioInterface : Module { NUM_OUTPUTS = AUDIO1_OUTPUT + 8 }; - RtAudio *stream = NULL; - // Stream properties - int device = -1; - float sampleRate = 44100.0; - int blockSize = 256; - int numOutputs = 0; - int numInputs = 0; + AudioIO audioIO; SampleRateConverter<8> inputSrc; SampleRateConverter<8> outputSrc; @@ -47,89 +41,43 @@ struct AudioInterface : Module { DoubleRingBuffer, (1<<15)> inputSrcBuffer; AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { - setDriver(RtAudio::UNSPECIFIED); - } - ~AudioInterface() { - closeStream(); } void step() override; void stepStream(const float *input, float *output, int numFrames); - int getDeviceCount(); - std::string getDeviceName(int device); - - void openStream(); - void closeStream(); - - void setDriver(int driver) { - closeStream(); - if (stream) - delete stream; - stream = new RtAudio((RtAudio::Api) driver); - } - int getDriver() { - if (!stream) - return RtAudio::UNSPECIFIED; - return stream->getCurrentApi(); - } - std::vector getAvailableDrivers() { - std::vector apis; - RtAudio::getCompiledApi(apis); - std::vector drivers; - for (RtAudio::Api api : apis) - drivers.push_back(api); - return drivers; - } - std::string getDriverName(int driver) { - switch (driver) { - case RtAudio::UNSPECIFIED: return "Unspecified"; - case RtAudio::LINUX_ALSA: return "ALSA"; - case RtAudio::LINUX_PULSE: return "PulseAudio"; - case RtAudio::LINUX_OSS: return "OSS"; - case RtAudio::UNIX_JACK: return "JACK"; - case RtAudio::MACOSX_CORE: return "Core Audio"; - case RtAudio::WINDOWS_WASAPI: return "WASAPI"; - case RtAudio::WINDOWS_ASIO: return "ASIO"; - case RtAudio::WINDOWS_DS: return "DirectSound"; - case RtAudio::RTAUDIO_DUMMY: return "Dummy"; - default: return "Unknown"; - } - } - - std::vector getSampleRates(); json_t *toJson() override { json_t *rootJ = json_object(); - json_object_set_new(rootJ, "driver", json_integer(getDriver())); - json_object_set_new(rootJ, "device", json_integer(device)); - json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); - json_object_set_new(rootJ, "blockSize", json_integer(blockSize)); + // json_object_set_new(rootJ, "driver", json_integer(getDriver())); + // json_object_set_new(rootJ, "device", json_integer(device)); + // json_object_set_new(rootJ, "audioIO.sampleRate", json_real(audioIO.sampleRate)); + // json_object_set_new(rootJ, "audioIO.blockSize", json_integer(audioIO.blockSize)); return rootJ; } void fromJson(json_t *rootJ) override { - json_t *driverJ = json_object_get(rootJ, "driver"); - if (driverJ) - setDriver(json_number_value(driverJ)); + // json_t *driverJ = json_object_get(rootJ, "driver"); + // if (driverJ) + // setDriver(json_number_value(driverJ)); - json_t *deviceJ = json_object_get(rootJ, "device"); - if (deviceJ) - device = json_number_value(deviceJ); + // json_t *deviceJ = json_object_get(rootJ, "device"); + // if (deviceJ) + // device = json_number_value(deviceJ); - json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); - if (sampleRateJ) - sampleRate = json_number_value(sampleRateJ); + // json_t *sampleRateJ = json_object_get(rootJ, "audioIO.sampleRate"); + // if (sampleRateJ) + // audioIO.sampleRate = json_number_value(sampleRateJ); - json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); - if (blockSizeJ) - blockSize = json_integer_value(blockSizeJ); + // json_t *blockSizeJ = json_object_get(rootJ, "audioIO.blockSize"); + // if (blockSizeJ) + // audioIO.blockSize = json_integer_value(blockSizeJ); - openStream(); + // openStream(); } void onReset() override { - closeStream(); + audioIO.closeStream(); } }; @@ -149,15 +97,15 @@ struct AudioInterface : Module { void AudioInterface::step() { // debug("inputBuffer %d inputSrcBuffer %d outputBuffer %d", inputBuffer.size(), inputSrcBuffer.size(), outputBuffer.size()); // Read/write stream if we have enough input, OR the output buffer is empty if we have no input - if (numOutputs > 0) { - TIMED_SLEEP_LOCK(inputSrcBuffer.size() < blockSize, 100e-6, 0.2); + if (audioIO.numOutputs > 0) { + TIMED_SLEEP_LOCK(inputSrcBuffer.size() < audioIO.blockSize, 100e-6, 0.2); } - else if (numInputs > 0) { + else if (audioIO.numInputs > 0) { TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); } // Get input and pass it through the sample rate converter - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { if (!inputBuffer.full()) { Frame<8> f; for (int i = 0; i < 8; i++) { @@ -169,7 +117,7 @@ void AudioInterface::step() { // Once full, sample rate convert the input // inputBuffer -> SRC -> inputSrcBuffer if (inputBuffer.full()) { - inputSrc.setRates(engineGetSampleRate(), sampleRate); + inputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); int inLen = inputBuffer.size(); int outLen = inputSrcBuffer.capacity(); inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); @@ -189,29 +137,29 @@ void AudioInterface::step() { void AudioInterface::stepStream(const float *input, float *output, int numFrames) { if (gPaused) { - memset(output, 0, sizeof(float) * numOutputs * numFrames); + memset(output, 0, sizeof(float) * audioIO.numOutputs * numFrames); return; } - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { // Wait for enough input before proceeding TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); } - else if (numInputs > 0) { + else if (audioIO.numInputs > 0) { TIMED_SLEEP_LOCK(outputBuffer.empty(), 100e-6, 0.2); } // input stream -> output buffer - if (numInputs > 0) { + if (audioIO.numInputs > 0) { Frame<8> inputFrames[numFrames]; for (int i = 0; i < numFrames; i++) { for (int c = 0; c < 8; c++) { - inputFrames[i].samples[c] = (c < numInputs) ? input[i*numInputs + c] : 0.0; + inputFrames[i].samples[c] = (c < audioIO.numInputs) ? input[i*audioIO.numInputs + c] : 0.0; } } // Pass output through sample rate converter - outputSrc.setRates(sampleRate, engineGetSampleRate()); + outputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); int inLen = numFrames; int outLen = outputBuffer.capacity(); outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); @@ -219,7 +167,7 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames } // input buffer -> output stream - if (numOutputs > 0) { + if (audioIO.numOutputs > 0) { for (int i = 0; i < numFrames; i++) { Frame<8> f; if (inputSrcBuffer.empty()) { @@ -228,304 +176,23 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames else { f = inputSrcBuffer.shift(); } - for (int c = 0; c < numOutputs; c++) { - output[i*numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); + for (int c = 0; c < audioIO.numOutputs; c++) { + output[i*audioIO.numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); } } } } -int AudioInterface::getDeviceCount() { - if (!stream) - return 0; - return stream->getDeviceCount(); -} - -std::string AudioInterface::getDeviceName(int device) { - if (!stream || device < 0) - return ""; - - try { - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return ""; - } -} - -static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { - AudioInterface *audioInterface = (AudioInterface *) userData; - assert(audioInterface); - audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); - return 0; -} - -void AudioInterface::openStream() { - int device = this->device; - closeStream(); - if (!stream) - return; - - // Open new device - if (device >= 0) { - RtAudio::DeviceInfo deviceInfo; - try { - deviceInfo = stream->getDeviceInfo(device); - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return; - } - - numOutputs = mini(deviceInfo.outputChannels, 8); - numInputs = mini(deviceInfo.inputChannels, 8); - - if (numOutputs == 0 && numInputs == 0) { - warn("Audio device %d has 0 inputs and 0 outputs"); - return; - } - - RtAudio::StreamParameters outParameters; - outParameters.deviceId = device; - outParameters.nChannels = numOutputs; - - RtAudio::StreamParameters inParameters; - inParameters.deviceId = device; - inParameters.nChannels = numInputs; - - RtAudio::StreamOptions options; - // options.flags |= RTAUDIO_SCHEDULE_REALTIME; - - // Find closest sample rate - unsigned int closestSampleRate = 0; - for (unsigned int sr : deviceInfo.sampleRates) { - if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { - closestSampleRate = sr; - } - } - - try { - // Don't use stream parameters if 0 input or output channels - debug("Opening audio stream %d", device); - stream->openStream( - numOutputs == 0 ? NULL : &outParameters, - numInputs == 0 ? NULL : &inParameters, - RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); - } - catch (RtAudioError &e) { - warn("Failed to open audio stream: %s", e.what()); - return; - } - - try { - debug("Starting audio stream %d", device); - stream->startStream(); - } - catch (RtAudioError &e) { - warn("Failed to start audio stream: %s", e.what()); - return; - } - - // Update sample rate because this may have changed - this->sampleRate = stream->getStreamSampleRate(); - this->device = device; - } -} - -void AudioInterface::closeStream() { - if (stream) { - if (stream->isStreamRunning()) { - debug("Aborting audio stream %d", device); - try { - stream->abortStream(); - } - catch (RtAudioError &e) { - warn("Failed to abort stream %s", e.what()); - } - } - if (stream->isStreamOpen()) { - debug("Closing audio stream %d", device); - try { - stream->closeStream(); - } - catch (RtAudioError &e) { - warn("Failed to close stream %s", e.what()); - } - } - } - - // Reset stream settings - device = -1; - numOutputs = 0; - numInputs = 0; - - // Clear buffers - inputBuffer.clear(); - outputBuffer.clear(); - inputSrcBuffer.clear(); - inputSrc.reset(); - outputSrc.reset(); -} - -std::vector AudioInterface::getSampleRates() { - std::vector allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; - if (!stream || device < 0) - return allowedSampleRates; - - try { - std::vector sampleRates; - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - for (int sr : deviceInfo.sampleRates) { - float sampleRate = sr; - auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate); - if (allowedIt != allowedSampleRates.end()) { - sampleRates.push_back(sampleRate); - } - } - return sampleRates; - } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return {}; - } -} - - - -struct AudioDriverItem : MenuItem { - AudioInterface *audioInterface; - int driver; - void onAction(EventAction &e) override { - audioInterface->setDriver(driver); - } -}; - -struct AudioDriverChoice : ChoiceButton { - AudioInterface *audioInterface; - void onAction(EventAction &e) override { - Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); - menu->box.size.x = box.size.x; - - for (int driver : audioInterface->getAvailableDrivers()) { - AudioDriverItem *audioItem = new AudioDriverItem(); - audioItem->audioInterface = audioInterface; - audioItem->driver = driver; - audioItem->text = audioInterface->getDriverName(driver); - menu->addChild(audioItem); - } - } - void step() override { - text = audioInterface->getDriverName(audioInterface->getDriver()); - } -}; - - -struct AudioDeviceItem : MenuItem { - AudioInterface *audioInterface; - int device; - void onAction(EventAction &e) override { - audioInterface->device = device; - audioInterface->openStream(); - } -}; - -struct AudioDeviceChoice : ChoiceButton { - int lastDeviceId = -1; - AudioInterface *audioInterface; - void onAction(EventAction &e) override { - Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); - menu->box.size.x = box.size.x; - - int deviceCount = audioInterface->getDeviceCount(); - { - AudioDeviceItem *audioItem = new AudioDeviceItem(); - audioItem->audioInterface = audioInterface; - audioItem->device = -1; - audioItem->text = "No device"; - menu->addChild(audioItem); - } - for (int device = 0; device < deviceCount; device++) { - AudioDeviceItem *audioItem = new AudioDeviceItem(); - audioItem->audioInterface = audioInterface; - audioItem->device = device; - audioItem->text = audioInterface->getDeviceName(device); - menu->addChild(audioItem); - } - } - void step() override { - if (lastDeviceId != audioInterface->device) { - std::string name = audioInterface->getDeviceName(audioInterface->device); - text = ellipsize(name, 24); - lastDeviceId = audioInterface->device; - } - } -}; - - -struct SampleRateItem : MenuItem { - AudioInterface *audioInterface; - float sampleRate; - void onAction(EventAction &e) override { - audioInterface->sampleRate = sampleRate; - audioInterface->openStream(); - } -}; - -struct SampleRateChoice : ChoiceButton { - AudioInterface *audioInterface; - void onAction(EventAction &e) override { - Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); - menu->box.size.x = box.size.x; - - for (float sampleRate : audioInterface->getSampleRates()) { - SampleRateItem *item = new SampleRateItem(); - item->audioInterface = audioInterface; - item->sampleRate = sampleRate; - item->text = stringf("%.0f Hz", sampleRate); - menu->addChild(item); - } - } - void step() override { - this->text = stringf("%.0f Hz", audioInterface->sampleRate); - } -}; - -struct BlockSizeItem : MenuItem { - AudioInterface *audioInterface; - int blockSize; - void onAction(EventAction &e) override { - audioInterface->blockSize = blockSize; - audioInterface->openStream(); - } -}; +// void AudioInterface::closeStream() { +// // Clear buffers +// inputBuffer.clear(); +// outputBuffer.clear(); +// inputSrcBuffer.clear(); +// inputSrc.reset(); +// outputSrc.reset(); +// } -struct BlockSizeChoice : ChoiceButton { - AudioInterface *audioInterface; - void onAction(EventAction &e) override { - Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); - menu->box.size.x = box.size.x; - - const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; - int blockSizesLen = sizeof(blockSizes) / sizeof(blockSizes[0]); - for (int i = 0; i < blockSizesLen; i++) { - BlockSizeItem *item = new BlockSizeItem(); - item->audioInterface = audioInterface; - item->blockSize = blockSizes[i]; - item->text = stringf("%d", blockSizes[i]); - menu->addChild(item); - } - } - void step() override { - this->text = stringf("%d", audioInterface->blockSize); - } -}; AudioInterfaceWidget::AudioInterfaceWidget() { @@ -545,69 +212,9 @@ AudioInterfaceWidget::AudioInterfaceWidget() { Vec margin = Vec(5, 2); float labelHeight = 15; - float yPos = margin.y; + float yPos = margin.y + 100; float xPos; - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Audio driver"; - addChild(label); - yPos += labelHeight + margin.y; - - AudioDriverChoice *choice = new AudioDriverChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Audio device"; - addChild(label); - yPos += labelHeight + margin.y; - - AudioDeviceChoice *choice = new AudioDeviceChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Sample rate"; - addChild(label); - yPos += labelHeight + margin.y; - - SampleRateChoice *choice = new SampleRateChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin.x, yPos); - label->text = "Block size"; - addChild(label); - yPos += labelHeight + margin.y; - - BlockSizeChoice *choice = new BlockSizeChoice(); - choice->audioInterface = module; - choice->box.pos = Vec(margin.x, yPos); - choice->box.size.x = box.size.x - 2*margin.x; - addChild(choice); - yPos += choice->box.size.y + margin.y; - } - { Label *label = new Label(); label->box.pos = Vec(margin.x, yPos); @@ -675,4 +282,11 @@ AudioInterfaceWidget::AudioInterfaceWidget() { xPos += 37 + margin.x; } yPos += 35 + margin.y; + + AudioWidget *audioWidget = construct(); + audioWidget->audioIO = &module->audioIO; + addChild(audioWidget); + // Widget *w = construct(); + // w->box.pos = Vec(100, 0); + // addChild(w); } From 34de07192ae1ab6fa008344a80b63d9cdc0f0c5d Mon Sep 17 00:00:00 2001 From: Joakim Lindbom Date: Thu, 4 Jan 2018 16:08:31 +0100 Subject: [PATCH 47/66] Added make parameter to control native/common compilation --- compile.mk | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compile.mk b/compile.mk index 0ebc1501..9e6ecf73 100644 --- a/compile.mk +++ b/compile.mk @@ -6,7 +6,14 @@ endif FLAGS += -MMD FLAGS += -g # Optimization -FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only +FLAGS += -O3 +# Native compile flag will be used unless COMMON=true is passed as a parameter to make +ifeq ($(COMMON), true) + FLAGS += -march=nocona +else + FLAGS += -march=native +endif +FLAGS += -ffast-math -fno-finite-math-only FLAGS += -Wall -Wextra -Wno-unused-parameter ifneq ($(ARCH), mac) CXXFLAGS += -Wsuggest-override From 092fb60b5753f6dbf3b4b8c386a59c5ba8f203d0 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 5 Jan 2018 16:23:06 -0500 Subject: [PATCH 48/66] Remove request URL from log.txt --- src/util/request.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/request.cpp b/src/util/request.cpp index 72702c04..e874b441 100644 --- a/src/util/request.cpp +++ b/src/util/request.cpp @@ -85,7 +85,7 @@ json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText); // Perform request - info("Requesting %s", url.c_str()); + // info("Requesting %s", url.c_str()); // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); CURLcode res = curl_easy_perform(curl); From ce1906a28827ca2888c6f65196c962c0d68a6716 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 6 Jan 2018 04:23:44 -0500 Subject: [PATCH 49/66] Use AudioIO in AudioInterface --- include/audio.hpp | 6 +- include/dsp/samplerate.hpp | 15 +- src/audio.cpp | 6 +- src/core/AudioInterface.cpp | 264 +++++++++++++++++++----------------- 4 files changed, 160 insertions(+), 131 deletions(-) diff --git a/include/audio.hpp b/include/audio.hpp index 6570be5d..fff6ce3c 100644 --- a/include/audio.hpp +++ b/include/audio.hpp @@ -22,7 +22,7 @@ struct AudioIO { int numInputs = 0; AudioIO(); - ~AudioIO(); + virtual ~AudioIO(); std::vector listDrivers(); std::string getDriverName(int driver); @@ -36,6 +36,10 @@ struct AudioIO { void closeStream(); std::vector listSampleRates(); + + virtual void processStream(const float *input, float *output, int length) {} + virtual void onCloseStream() {} + virtual void onOpenStream() {} }; diff --git a/include/dsp/samplerate.hpp b/include/dsp/samplerate.hpp index a4a99880..d74d99a6 100644 --- a/include/dsp/samplerate.hpp +++ b/include/dsp/samplerate.hpp @@ -10,7 +10,7 @@ namespace rack { template struct SampleRateConverter { - SpeexResamplerState *state = NULL; + SpeexResamplerState *state; bool bypass = false; SampleRateConverter() { @@ -27,14 +27,21 @@ struct SampleRateConverter { } void setRates(int inRate, int outRate) { - spx_uint32_t oldInRate, oldOutRate; - speex_resampler_get_rate(state, &oldInRate, &oldOutRate); - if (inRate == (int) oldInRate && outRate == (int) oldOutRate) + int oldInRate, oldOutRate; + getRates(&oldInRate, &oldOutRate); + if (inRate == oldInRate && outRate == oldOutRate) return; int error = speex_resampler_set_rate(state, inRate, outRate); assert(error == RESAMPLER_ERR_SUCCESS); } + void getRates(int *inRate, int *outRate) { + spx_uint32_t inRate32, outRate32; + speex_resampler_get_rate(state, &inRate32, &outRate32); + if (inRate) *inRate = inRate32; + if (outRate) *outRate = outRate32; + } + /** `in` and `out` are interlaced with the number of channels */ void process(const Frame *in, int *inFrames, Frame *out, int *outFrames) { if (bypass) { diff --git a/src/audio.cpp b/src/audio.cpp index 3484160f..b4c31d77 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -46,7 +46,7 @@ int AudioIO::getDriver() { } void AudioIO::setDriver(int driver) { - // closeStream(); + closeStream(); if (stream) delete stream; stream = new RtAudio((RtAudio::Api) driver); @@ -89,7 +89,7 @@ std::string AudioIO::getDeviceDetail(int device) { static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { AudioIO *audioIO = (AudioIO*) userData; assert(audioIO); - // audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); + audioIO->processStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); return 0; } @@ -162,6 +162,7 @@ void AudioIO::openStream() { // Update sample rate because this may have changed this->sampleRate = stream->getStreamSampleRate(); this->device = device; + onOpenStream(); } } @@ -191,6 +192,7 @@ void AudioIO::closeStream() { device = -1; numOutputs = 0; numInputs = 0; + onCloseStream(); } std::vector AudioIO::listSampleRates() { diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 19475460..2cdd81fe 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include "core.hpp" #include "audio.hpp" #include "dsp/samplerate.hpp" @@ -12,33 +15,93 @@ #pragma GCC diagnostic pop +#define MAX_OUTPUTS 8 +#define MAX_INPUTS 8 + +static auto audioTimeout = std::chrono::milliseconds(100); + using namespace rack; +struct AudioInterfaceIO : AudioIO { + std::mutex engineMutex; + std::condition_variable engineCv; + std::mutex audioMutex; + std::condition_variable audioCv; + // Audio thread produces, engine thread consumes + DoubleRingBuffer, (1<<15)> inputBuffer; + // Audio thread consumes, engine thread produces + DoubleRingBuffer, (1<<15)> outputBuffer; + + AudioInterfaceIO() { + maxOutputs = MAX_OUTPUTS; + maxInputs = MAX_INPUTS; + } + + void processStream(const float *input, float *output, int length) override { + if (numInputs > 0) { + // TODO Do we need to wait on the input to be consumed here? + for (int i = 0; i < length; i++) { + if (inputBuffer.full()) + break; + Frame f; + memset(&f, 0, sizeof(f)); + memcpy(&f, &input[numInputs * i], numInputs * sizeof(float)); + inputBuffer.push(f); + } + } + + if (numOutputs > 0) { + std::unique_lock lock(audioMutex); + auto cond = [&] { + return outputBuffer.size() >= length; + }; + if (audioCv.wait_for(lock, audioTimeout, cond)) { + // Consume audio block + for (int i = 0; i < length; i++) { + Frame f = outputBuffer.shift(); + memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float)); + } + } + else { + // Timed out, fill output with zeros + memset(output, 0, length * numOutputs * sizeof(float)); + } + } + + // Notify engine when finished processing + engineCv.notify_all(); + } + + void onCloseStream() override { + inputBuffer.clear(); + outputBuffer.clear(); + } +}; + + struct AudioInterface : Module { enum ParamIds { NUM_PARAMS }; enum InputIds { - AUDIO1_INPUT, - NUM_INPUTS = AUDIO1_INPUT + 8 + ENUMS(AUDIO_INPUT, MAX_INPUTS), + NUM_INPUTS }; enum OutputIds { - AUDIO1_OUTPUT, - NUM_OUTPUTS = AUDIO1_OUTPUT + 8 + ENUMS(AUDIO_OUTPUT, MAX_OUTPUTS), + NUM_OUTPUTS }; - AudioIO audioIO; + AudioInterfaceIO audioIO; SampleRateConverter<8> inputSrc; SampleRateConverter<8> outputSrc; // in rack's sample rate - DoubleRingBuffer, 16> inputBuffer; - DoubleRingBuffer, (1<<15)> outputBuffer; - // in device's sample rate - DoubleRingBuffer, (1<<15)> inputSrcBuffer; + DoubleRingBuffer, 16> inputBuffer; + DoubleRingBuffer, 16> outputBuffer; AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { } @@ -49,31 +112,40 @@ struct AudioInterface : Module { json_t *toJson() override { json_t *rootJ = json_object(); - // json_object_set_new(rootJ, "driver", json_integer(getDriver())); - // json_object_set_new(rootJ, "device", json_integer(device)); - // json_object_set_new(rootJ, "audioIO.sampleRate", json_real(audioIO.sampleRate)); - // json_object_set_new(rootJ, "audioIO.blockSize", json_integer(audioIO.blockSize)); + json_object_set_new(rootJ, "driver", json_integer(audioIO.getDriver())); + std::string deviceName = audioIO.getDeviceName(audioIO.device); + json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); + json_object_set_new(rootJ, "sampleRate", json_integer(audioIO.sampleRate)); + json_object_set_new(rootJ, "blockSize", json_integer(audioIO.blockSize)); return rootJ; } void fromJson(json_t *rootJ) override { - // json_t *driverJ = json_object_get(rootJ, "driver"); - // if (driverJ) - // setDriver(json_number_value(driverJ)); - - // json_t *deviceJ = json_object_get(rootJ, "device"); - // if (deviceJ) - // device = json_number_value(deviceJ); + json_t *driverJ = json_object_get(rootJ, "driver"); + if (driverJ) + audioIO.setDriver(json_number_value(driverJ)); + + json_t *deviceNameJ = json_object_get(rootJ, "deviceName"); + if (deviceNameJ) { + std::string deviceName = json_string_value(deviceNameJ); + // Search for device ID with equal name + for (int device = 0; device < audioIO.getDeviceCount(); device++) { + if (audioIO.getDeviceName(device) == deviceName) { + audioIO.device = device; + break; + } + } + } - // json_t *sampleRateJ = json_object_get(rootJ, "audioIO.sampleRate"); - // if (sampleRateJ) - // audioIO.sampleRate = json_number_value(sampleRateJ); + json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); + if (sampleRateJ) + audioIO.sampleRate = json_integer_value(sampleRateJ); - // json_t *blockSizeJ = json_object_get(rootJ, "audioIO.blockSize"); - // if (blockSizeJ) - // audioIO.blockSize = json_integer_value(blockSizeJ); + json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); + if (blockSizeJ) + audioIO.blockSize = json_integer_value(blockSizeJ); - // openStream(); + audioIO.openStream(); } void onReset() override { @@ -82,117 +154,61 @@ struct AudioInterface : Module { }; -#define TIMED_SLEEP_LOCK(_cond, _spinTime, _totalTime) { \ - auto startTime = std::chrono::high_resolution_clock::now(); \ - while (!(_cond)) { \ - std::this_thread::sleep_for(std::chrono::duration(_spinTime)); \ - auto currTime = std::chrono::high_resolution_clock::now(); \ - float totalTime = std::chrono::duration(currTime - startTime).count(); \ - if (totalTime > (_totalTime)) \ - break; \ - } \ -} - - void AudioInterface::step() { - // debug("inputBuffer %d inputSrcBuffer %d outputBuffer %d", inputBuffer.size(), inputSrcBuffer.size(), outputBuffer.size()); - // Read/write stream if we have enough input, OR the output buffer is empty if we have no input - if (audioIO.numOutputs > 0) { - TIMED_SLEEP_LOCK(inputSrcBuffer.size() < audioIO.blockSize, 100e-6, 0.2); - } - else if (audioIO.numInputs > 0) { - TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); - } + Frame inputFrame; + memset(&inputFrame, 0, sizeof(inputFrame)); - // Get input and pass it through the sample rate converter - if (audioIO.numOutputs > 0) { - if (!inputBuffer.full()) { - Frame<8> f; - for (int i = 0; i < 8; i++) { - f.samples[i] = inputs[AUDIO1_INPUT + i].value / 10.0; - } - inputBuffer.push(f); - } - - // Once full, sample rate convert the input - // inputBuffer -> SRC -> inputSrcBuffer - if (inputBuffer.full()) { - inputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); - int inLen = inputBuffer.size(); - int outLen = inputSrcBuffer.capacity(); - inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); - inputBuffer.startIncr(inLen); - inputSrcBuffer.endIncr(outLen); + if (audioIO.numInputs > 0) { + if (inputBuffer.empty()) { + inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); + int inLen = audioIO.inputBuffer.size(); + int outLen = inputBuffer.capacity(); + inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); + audioIO.inputBuffer.startIncr(inLen); + inputBuffer.endIncr(outLen); } } - // Set output - if (!outputBuffer.empty()) { - Frame<8> f = outputBuffer.shift(); - for (int i = 0; i < 8; i++) { - outputs[AUDIO1_OUTPUT + i].value = 10.0 * f.samples[i]; - } + if (!inputBuffer.empty()) { + inputFrame = inputBuffer.shift(); } -} - -void AudioInterface::stepStream(const float *input, float *output, int numFrames) { - if (gPaused) { - memset(output, 0, sizeof(float) * audioIO.numOutputs * numFrames); - return; + for (int i = 0; i < MAX_INPUTS; i++) { + outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i]; } if (audioIO.numOutputs > 0) { - // Wait for enough input before proceeding - TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); - } - else if (audioIO.numInputs > 0) { - TIMED_SLEEP_LOCK(outputBuffer.empty(), 100e-6, 0.2); - } - - // input stream -> output buffer - if (audioIO.numInputs > 0) { - Frame<8> inputFrames[numFrames]; - for (int i = 0; i < numFrames; i++) { - for (int c = 0; c < 8; c++) { - inputFrames[i].samples[c] = (c < audioIO.numInputs) ? input[i*audioIO.numInputs + c] : 0.0; + // Get and push output SRC frame + if (!outputBuffer.full()) { + Frame f; + for (int i = 0; i < audioIO.numOutputs; i++) { + f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0; } + outputBuffer.push(f); } - // Pass output through sample rate converter - outputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); - int inLen = numFrames; - int outLen = outputBuffer.capacity(); - outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); - outputBuffer.endIncr(outLen); - } - - // input buffer -> output stream - if (audioIO.numOutputs > 0) { - for (int i = 0; i < numFrames; i++) { - Frame<8> f; - if (inputSrcBuffer.empty()) { - memset(&f, 0, sizeof(f)); + if (outputBuffer.full()) { + // Wait until outputs are needed + std::unique_lock lock(audioIO.engineMutex); + auto cond = [&] { + return audioIO.outputBuffer.size() < audioIO.blockSize; + }; + if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) { + // Push converted output + outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); + int inLen = outputBuffer.size(); + int outLen = audioIO.outputBuffer.capacity(); + outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen); + outputBuffer.startIncr(inLen); + audioIO.outputBuffer.endIncr(outLen); } else { - f = inputSrcBuffer.shift(); - } - for (int c = 0; c < audioIO.numOutputs; c++) { - output[i*audioIO.numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); + // Give up on pushing output } } } -} - - -// void AudioInterface::closeStream() { -// // Clear buffers -// inputBuffer.clear(); -// outputBuffer.clear(); -// inputSrcBuffer.clear(); -// inputSrc.reset(); -// outputSrc.reset(); -// } + audioIO.audioCv.notify_all(); +} AudioInterfaceWidget::AudioInterfaceWidget() { @@ -226,7 +242,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { yPos += 5; xPos = 10; for (int i = 0; i < 4; i++) { - addInput(createInput(Vec(xPos, yPos), module, AudioInterface::AUDIO1_INPUT + i)); + addInput(createInput(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i)); Label *label = new Label(); label->box.pos = Vec(xPos + 4, yPos + 28); label->text = stringf("%d", i + 1); @@ -239,7 +255,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { yPos += 5; xPos = 10; for (int i = 4; i < 8; i++) { - addInput(createInput(Vec(xPos, yPos), module, AudioInterface::AUDIO1_INPUT + i)); + addInput(createInput(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i)); Label *label = new Label(); label->box.pos = Vec(xPos + 4, yPos + 28); label->text = stringf("%d", i + 1); @@ -260,7 +276,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { yPos += 5; xPos = 10; for (int i = 0; i < 4; i++) { - addOutput(createOutput(Vec(xPos, yPos), module, AudioInterface::AUDIO1_OUTPUT + i)); + addOutput(createOutput(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i)); Label *label = new Label(); label->box.pos = Vec(xPos + 4, yPos + 28); label->text = stringf("%d", i + 1); @@ -273,7 +289,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { yPos += 5; xPos = 10; for (int i = 4; i < 8; i++) { - addOutput(createOutput(Vec(xPos, yPos), module, AudioInterface::AUDIO1_OUTPUT + i)); + addOutput(createOutput(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i)); Label *label = new Label(); label->box.pos = Vec(xPos + 4, yPos + 28); label->text = stringf("%d", i + 1); From 144de3943c96f617ad7e70d8ba0a9936cd0d1f52 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 6 Jan 2018 04:34:05 -0500 Subject: [PATCH 50/66] Stop audio instead of abort, use requested sample rate instead of closest --- src/audio.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index b4c31d77..10c3c8db 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -130,20 +130,12 @@ void AudioIO::openStream() { RtAudio::StreamOptions options; // options.flags |= RTAUDIO_SCHEDULE_REALTIME; - // Find closest sample rate - unsigned int closestSampleRate = 0; - for (unsigned int sr : deviceInfo.sampleRates) { - if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { - closestSampleRate = sr; - } - } - try { debug("Opening audio stream %d", device); stream->openStream( numOutputs == 0 ? NULL : &outParameters, numInputs == 0 ? NULL : &inParameters, - RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); + RTAUDIO_FLOAT32, sampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); } catch (RtAudioError &e) { warn("Failed to open audio stream: %s", e.what()); @@ -169,12 +161,12 @@ void AudioIO::openStream() { void AudioIO::closeStream() { if (stream) { if (stream->isStreamRunning()) { - debug("Aborting audio stream %d", device); + debug("Stopping audio stream %d", device); try { - stream->abortStream(); + stream->stopStream(); } catch (RtAudioError &e) { - warn("Failed to abort stream %s", e.what()); + warn("Failed to stop stream %s", e.what()); } } if (stream->isStreamOpen()) { From e616e12fa5eff6a07a0a650c6693ba10bf1413f5 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 6 Jan 2018 10:16:29 -0500 Subject: [PATCH 51/66] Return specialized type in rack.hpp helper functions --- include/rack.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/rack.hpp b/include/rack.hpp index 6177612e..d3e78007 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -36,15 +36,15 @@ Model *createModel(std::string manufacturer, std::string slug, std::string name, } template -Widget *createScrew(Vec pos) { - Widget *screw = new TScrew(); +TScrew *createScrew(Vec pos) { + TScrew *screw = new TScrew(); screw->box.pos = pos; return screw; } template -ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { - ParamWidget *param = new TParamWidget(); +TParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { + TParamWidget *param = new TParamWidget(); param->box.pos = pos; param->module = module; param->paramId = paramId; @@ -54,8 +54,8 @@ ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, f } template -Port *createInput(Vec pos, Module *module, int inputId) { - Port *port = new TPort(); +TPort *createInput(Vec pos, Module *module, int inputId) { + TPort *port = new TPort(); port->box.pos = pos; port->module = module; port->type = Port::INPUT; @@ -64,8 +64,8 @@ Port *createInput(Vec pos, Module *module, int inputId) { } template -Port *createOutput(Vec pos, Module *module, int outputId) { - Port *port = new TPort(); +TPort *createOutput(Vec pos, Module *module, int outputId) { + TPort *port = new TPort(); port->box.pos = pos; port->module = module; port->type = Port::OUTPUT; @@ -74,8 +74,8 @@ Port *createOutput(Vec pos, Module *module, int outputId) { } template -ModuleLightWidget *createLight(Vec pos, Module *module, int firstLightId) { - ModuleLightWidget *light = new TModuleLightWidget(); +TModuleLightWidget *createLight(Vec pos, Module *module, int firstLightId) { + TModuleLightWidget *light = new TModuleLightWidget(); light->box.pos = pos; light->module = module; light->firstLightId = firstLightId; From ca5d43ed9dccd10af6709867563f3231ab138e16 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 8 Jan 2018 21:02:47 -0500 Subject: [PATCH 52/66] Use closest sample rate in AudioIO::openStream() --- src/audio.cpp | 9 ++++++++- src/core/AudioInterface.cpp | 6 ++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index 10c3c8db..cc80df47 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -130,12 +130,19 @@ void AudioIO::openStream() { RtAudio::StreamOptions options; // options.flags |= RTAUDIO_SCHEDULE_REALTIME; + int closestSampleRate = deviceInfo.preferredSampleRate; + for (int sr : deviceInfo.sampleRates) { + if (abs(sr - sampleRate) < abs(closestSampleRate - sampleRate)) { + closestSampleRate = sr; + } + } + try { debug("Opening audio stream %d", device); stream->openStream( numOutputs == 0 ? NULL : &outParameters, numInputs == 0 ? NULL : &inParameters, - RTAUDIO_FLOAT32, sampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); + RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); } catch (RtAudioError &e) { warn("Failed to open audio stream: %s", e.what()); diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 2cdd81fe..d990f8bb 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -96,8 +96,8 @@ struct AudioInterface : Module { AudioInterfaceIO audioIO; - SampleRateConverter<8> inputSrc; - SampleRateConverter<8> outputSrc; + SampleRateConverter inputSrc; + SampleRateConverter outputSrc; // in rack's sample rate DoubleRingBuffer, 16> inputBuffer; @@ -107,8 +107,6 @@ struct AudioInterface : Module { } void step() override; - void stepStream(const float *input, float *output, int numFrames); - json_t *toJson() override { json_t *rootJ = json_object(); From d7875d5e30f79fcbaf934764a9bcdc5d9b0d76ce Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 13 Jan 2018 03:51:06 -0500 Subject: [PATCH 53/66] Add midi.cpp with MidiIO class --- include/app.hpp | 6 +++- include/componentlibrary.hpp | 4 +-- include/midi.hpp | 41 ++++++++++++++++++++++ src/app/MidiWidget.cpp | 66 ++++++++++++++++++++++++++++++++++++ src/core/AudioInterface.cpp | 8 +++-- src/core/MidiCCToCV.cpp | 2 ++ src/core/MidiClockToCV.cpp | 2 ++ src/core/MidiIO.cpp | 2 ++ src/core/MidiIO.hpp | 3 ++ src/core/MidiToCV.cpp | 2 ++ src/core/MidiTriggerToCV.cpp | 2 ++ src/core/QuadMidiToCV.cpp | 2 ++ src/core/core.cpp | 10 +++--- src/midi.cpp | 42 +++++++++++++++++++++++ 14 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 include/midi.hpp create mode 100644 src/app/MidiWidget.cpp create mode 100644 src/midi.cpp diff --git a/include/app.hpp b/include/app.hpp index 1e1550c0..81812647 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -283,6 +283,7 @@ struct MomentarySwitch : virtual Switch { //////////////////// struct AudioIO; +struct MidiIO; struct AudioWidget : OpaqueWidget { /** Not owned */ @@ -290,7 +291,10 @@ struct AudioWidget : OpaqueWidget { void onMouseDown(EventMouseDown &e) override; }; -struct MIDIWidget : OpaqueWidget { +struct MidiWidget : OpaqueWidget { + /** Not owned */ + MidiIO *midiIO = NULL; + void onMouseDown(EventMouseDown &e) override; }; //////////////////// diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index 5ae6c671..4bc68fe9 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -346,8 +346,8 @@ struct USB_B_AudioWidget : AudioWidget, SVGWidget { } }; -struct DIN_MIDIWidget : MIDIWidget, SVGWidget { - DIN_MIDIWidget() { +struct MIDI_DIN_MidiWidget : MidiWidget, SVGWidget { + MIDI_DIN_MidiWidget() { setSVG(SVG::load(assetGlobal("res/ComponentLibrary/MIDI_DIN.svg"))); } }; diff --git a/include/midi.hpp b/include/midi.hpp new file mode 100644 index 00000000..a55d4ac5 --- /dev/null +++ b/include/midi.hpp @@ -0,0 +1,41 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#include "rtmidi/RtMidi.h" +#pragma GCC diagnostic pop + + +namespace rack { + + +struct MidiIO { + RtMidi *midi; + int port = -1; + /* For MIDI output, the channel to output messages. + For MIDI input, the channel to filter. + Set to -1 to allow all MIDI channels (for input). + Zero indexed. + */ + int channel = -1; + + virtual ~MidiIO() {} + virtual int getPortCount(); + virtual std::string getPortName(int port); + virtual void openPort(int port); +}; + + +struct MidiInput : MidiIO { + MidiInput(); + ~MidiInput(); +}; + + +struct MidiOutput : MidiIO { + MidiOutput(); + ~MidiOutput(); +}; + + +} // namespace rack diff --git a/src/app/MidiWidget.cpp b/src/app/MidiWidget.cpp new file mode 100644 index 00000000..5f832278 --- /dev/null +++ b/src/app/MidiWidget.cpp @@ -0,0 +1,66 @@ +#include "app.hpp" +#include "midi.hpp" + + +namespace rack { + + +struct MidiPortItem : MenuItem { + MidiIO *midiIO; + int port; + void onAction(EventAction &e) override { + midiIO->openPort(port); + } +}; + + +struct MidiChannelItem : MenuItem { + MidiIO *midiIO; + int channel; + void onAction(EventAction &e) override { + midiIO->channel = channel; + } +}; + + +void MidiWidget::onMouseDown(EventMouseDown &e) { + OpaqueWidget::onMouseDown(e); + + if (!midiIO) + return; + + Menu *menu = gScene->createMenu(); + + menu->addChild(construct(&MenuLabel::text, "MIDI port")); + for (int port = 0; port < midiIO->getPortCount(); port++) { + MidiPortItem *item = new MidiPortItem(); + item->midiIO = midiIO; + item->port = port; + item->text = midiIO->getPortName(port); + item->rightText = CHECKMARK(item->port == midiIO->port); + menu->addChild(item); + } + menu->addChild(construct()); + + menu->addChild(construct(&MenuLabel::text, "MIDI channel")); + MidiInput *midiInput = dynamic_cast(midiIO); + if (midiInput) { + MidiChannelItem *item = new MidiChannelItem(); + item->midiIO = midiIO; + item->channel = -1; + item->text = "All"; + item->rightText = CHECKMARK(item->channel == midiIO->channel); + menu->addChild(item); + } + for (int channel = 0; channel < 16; channel++) { + MidiChannelItem *item = new MidiChannelItem(); + item->midiIO = midiIO; + item->channel = channel; + item->text = stringf("%d", channel + 1); + item->rightText = CHECKMARK(item->channel == midiIO->channel); + menu->addChild(item); + } +} + + +} // namespace rack diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index d990f8bb..fe90229c 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -39,6 +39,11 @@ struct AudioInterfaceIO : AudioIO { maxInputs = MAX_INPUTS; } + ~AudioInterfaceIO() { + // Wait until processStream() is finished + std::lock_guard lock(audioMutex); + } + void processStream(const float *input, float *output, int length) override { if (numInputs > 0) { // TODO Do we need to wait on the input to be consumed here? @@ -300,7 +305,4 @@ AudioInterfaceWidget::AudioInterfaceWidget() { AudioWidget *audioWidget = construct(); audioWidget->audioIO = &module->audioIO; addChild(audioWidget); - // Widget *w = construct(); - // w->box.pos = Vec(100, 0); - // addChild(w); } diff --git a/src/core/MidiCCToCV.cpp b/src/core/MidiCCToCV.cpp index 6e60e5f3..c9aa2be4 100644 --- a/src/core/MidiCCToCV.cpp +++ b/src/core/MidiCCToCV.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -324,3 +325,4 @@ void MIDICCToCVWidget::step() { ModuleWidget::step(); } +#endif \ No newline at end of file diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index 1b5186c7..eda9a413 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -369,3 +370,4 @@ void MIDIClockToCVWidget::step() { ModuleWidget::step(); } +#endif \ No newline at end of file diff --git a/src/core/MidiIO.cpp b/src/core/MidiIO.cpp index 74f71bfb..1a2f726e 100644 --- a/src/core/MidiIO.cpp +++ b/src/core/MidiIO.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -260,3 +261,4 @@ void ChannelChoice::onAction(EventAction &e) { void ChannelChoice::step() { text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All"; } +#endif \ No newline at end of file diff --git a/src/core/MidiIO.hpp b/src/core/MidiIO.hpp index 756822d6..ed4db49e 100644 --- a/src/core/MidiIO.hpp +++ b/src/core/MidiIO.hpp @@ -1,3 +1,4 @@ +#if 0 #include #include "rack.hpp" @@ -201,3 +202,5 @@ struct ChannelChoice : ChoiceButton { void step() override; void onAction(EventAction &e) override; }; + +#endif \ No newline at end of file diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index df8accd9..67d8b865 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -303,3 +304,4 @@ void MidiToCVWidget::step() { ModuleWidget::step(); } +#endif \ No newline at end of file diff --git a/src/core/MidiTriggerToCV.cpp b/src/core/MidiTriggerToCV.cpp index 3fb0f1c1..0936468f 100644 --- a/src/core/MidiTriggerToCV.cpp +++ b/src/core/MidiTriggerToCV.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -282,3 +283,4 @@ MIDITriggerToCVWidget::MIDITriggerToCVWidget() { void MIDITriggerToCVWidget::step() { ModuleWidget::step(); } +#endif \ No newline at end of file diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 84c7335b..70b8f7ae 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -1,3 +1,4 @@ +#if 0 #include #include #include "core.hpp" @@ -416,3 +417,4 @@ void QuadMidiToCVWidget::step() { ModuleWidget::step(); } +#endif \ No newline at end of file diff --git a/src/core/core.cpp b/src/core/core.cpp index 416b3ca6..09a50a01 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -8,12 +8,12 @@ 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)); - p->addModel(createModel("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_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)); + // p->addModel(createModel("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG)); // p->addModel(createModel("Core", "Bridge", "Bridge")); p->addModel(createModel("Core", "Blank", "Blank", BLANK_TAG)); diff --git a/src/midi.cpp b/src/midi.cpp new file mode 100644 index 00000000..e30b3163 --- /dev/null +++ b/src/midi.cpp @@ -0,0 +1,42 @@ +#include "midi.hpp" + + +namespace rack { + + +int MidiIO::getPortCount() { + return midi->getPortCount(); +} + +std::string MidiIO::getPortName(int port) { + return midi->getPortName(port); +} + +void MidiIO::openPort(int port) { + midi->closePort(); + + if (port >= 0) { + midi->openPort(port); + } + this->port = port; +} + +MidiInput::MidiInput() { + midi = new RtMidiIn(); +} + +MidiInput::~MidiInput() { + delete dynamic_cast(midi); +} + + +MidiOutput::MidiOutput() { + midi = new RtMidiOut(); +} + +MidiOutput::~MidiOutput() { + delete dynamic_cast(midi); +} + + +} // namespace rack From 680ef9ed08a785c918ee08354e1a5f394d575d92 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 13 Jan 2018 03:51:23 -0500 Subject: [PATCH 54/66] Refactor plugin sync code --- src/plugin.cpp | 187 ++++++++++++++++++++++++++----------------------- 1 file changed, 98 insertions(+), 89 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 8c5d0b41..a0435f7d 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -8,6 +8,7 @@ #include // for MAXPATHLEN #include #include +#include #include #include @@ -190,14 +191,31 @@ static int extractZip(const char *filename, const char *dir) { return err; } -static void syncPlugin(json_t *pluginJ) { +static bool syncPlugin(json_t *pluginJ, bool dryRun) { json_t *slugJ = json_object_get(pluginJ, "slug"); - if (!slugJ) return; + if (!slugJ) + return false; std::string slug = json_string_value(slugJ); - info("Syncing plugin %s", slug.c_str()); + + // Get community version + std::string version; + json_t *versionJ = json_object_get(pluginJ, "version"); + if (versionJ) { + version = json_string_value(versionJ); + } + + // Check whether we already have a plugin with the same slug and version + for (Plugin *plugin : gPlugins) { + if (plugin->slug == slug) { + // plugin->version might be blank, so adding a version of the manifest will update the plugin + if (plugin->version == version) + return false; + } + } json_t *nameJ = json_object_get(pluginJ, "name"); - if (!nameJ) return; + if (!nameJ) + return false; std::string name = json_string_value(nameJ); std::string download; @@ -237,7 +255,7 @@ static void syncPlugin(json_t *pluginJ) { if (download.empty()) { warn("Could not get download URL for plugin %s", slug.c_str()); - return; + return false; } // If plugin is not loaded, download the zip file to /plugins @@ -249,66 +267,30 @@ static void syncPlugin(json_t *pluginJ) { std::string pluginPath = pluginsDir + "/" + slug; std::string zipPath = pluginPath + ".zip"; bool success = requestDownload(download, zipPath, &downloadProgress); - if (success) { - if (!sha256.empty()) { - // Check SHA256 hash - std::string actualSha256 = requestSHA256File(zipPath); - if (actualSha256 != sha256) { - warn("Plugin %s does not match expected SHA256 checksum", slug.c_str()); - return; - } - } - - // Unzip file - int err = extractZip(zipPath.c_str(), pluginsDir.c_str()); - if (!err) { - // Delete zip - remove(zipPath.c_str()); - // Load plugin - // loadPlugin(pluginPath); - } - } - - downloadName = ""; -} - -static bool trySyncPlugin(json_t *pluginJ, json_t *communityPluginsJ, bool dryRun) { - std::string slug = json_string_value(pluginJ); - - // Find community plugin - size_t communityIndex; - json_t *communityPluginJ = NULL; - json_array_foreach(communityPluginsJ, communityIndex, communityPluginJ) { - json_t *communitySlugJ = json_object_get(communityPluginJ, "slug"); - if (communitySlugJ) { - std::string communitySlug = json_string_value(communitySlugJ); - if (slug == communitySlug) - break; - } - } - if (communityIndex == json_array_size(communityPluginsJ)) { - warn("Plugin sync error: %s not found in community", slug.c_str()); + if (!success) { + warn("Plugin %s download was unsuccessful"); return false; } - // Get community version - std::string version; - json_t *versionJ = json_object_get(communityPluginJ, "version"); - if (versionJ) { - version = json_string_value(versionJ); + if (!sha256.empty()) { + // Check SHA256 hash + std::string actualSha256 = requestSHA256File(zipPath); + if (actualSha256 != sha256) { + warn("Plugin %s does not match expected SHA256 checksum", slug.c_str()); + return false; + } } - // Check whether we already have a plugin with the same slug and version - for (Plugin *plugin : gPlugins) { - if (plugin->slug == slug) { - // plugin->version might be blank, so adding a version of the manifest will update the plugin - if (plugin->version == version) - return false; - } + // Unzip file + int err = extractZip(zipPath.c_str(), pluginsDir.c_str()); + if (!err) { + // Delete zip + remove(zipPath.c_str()); + // Load plugin + // loadPlugin(pluginPath); } - if (!dryRun) - syncPlugin(communityPluginJ); + downloadName = ""; return true; } @@ -318,50 +300,77 @@ bool pluginSync(bool dryRun) { bool available = false; - // Download my plugins - json_t *reqJ = json_object(); - json_object_set(reqJ, "version", json_string(gApplicationVersion.c_str())); - json_object_set(reqJ, "token", json_string(gToken.c_str())); - json_t *resJ = requestJson(METHOD_GET, gApiHost + "/plugins", reqJ); - json_decref(reqJ); - - // Download community plugins - json_t *communityResJ = requestJson(METHOD_GET, gApiHost + "/community/plugins", NULL); - if (!dryRun) { isDownloading = true; downloadProgress = 0.0; - downloadName = ""; + downloadName = "Updating plugins..."; } - if (resJ && communityResJ) { + json_t *resJ = NULL; + json_t *communityResJ = NULL; + + try { + // Download plugin slugs + json_t *reqJ = json_object(); + json_object_set(reqJ, "version", json_string(gApplicationVersion.c_str())); + json_object_set(reqJ, "token", json_string(gToken.c_str())); + resJ = requestJson(METHOD_GET, gApiHost + "/plugins", reqJ); + json_decref(reqJ); + if (!resJ) + throw std::runtime_error("No response from server"); + json_t *errorJ = json_object_get(resJ, "error"); - json_t *communityErrorJ = json_object_get(resJ, "error"); - if (errorJ) { - warn("Plugin sync error: %s", json_string_value(errorJ)); - } - else if (communityErrorJ) { - warn("Plugin sync error: %s", json_string_value(communityErrorJ)); - } - else { - // Check each plugin in list of my plugins - json_t *pluginsJ = json_object_get(resJ, "plugins"); - json_t *communityPluginsJ = json_object_get(communityResJ, "plugins"); - size_t index; - json_t *pluginJ; - json_array_foreach(pluginsJ, index, pluginJ) { - if (trySyncPlugin(pluginJ, communityPluginsJ, dryRun)) - available = true; + if (errorJ) + throw std::runtime_error(json_string_value(errorJ)); + + // Download community plugins + communityResJ = requestJson(METHOD_GET, gApiHost + "/community/plugins", NULL); + if (!communityResJ) + throw std::runtime_error("No response from server"); + + json_t *communityErrorJ = json_object_get(communityResJ, "error"); + if (communityErrorJ) + throw std::runtime_error(json_string_value(communityErrorJ)); + + // Check each plugin in list of plugin slugs + json_t *pluginSlugsJ = json_object_get(resJ, "plugins"); + json_t *communityPluginsJ = json_object_get(communityResJ, "plugins"); + + size_t index; + json_t *pluginSlugJ; + json_array_foreach(pluginSlugsJ, index, pluginSlugJ) { + std::string slug = json_string_value(pluginSlugJ); + // Search for plugin slug in community + size_t communityIndex; + json_t *communityPluginJ = NULL; + json_array_foreach(communityPluginsJ, communityIndex, communityPluginJ) { + json_t *communitySlugJ = json_object_get(communityPluginJ, "slug"); + if (!communitySlugJ) + continue; + std::string communitySlug = json_string_value(communitySlugJ); + if (slug == communitySlug) + break; + } + + // Sync plugin + if (syncPlugin(communityPluginJ, dryRun)) { + available = true; + } + else { + warn("Plugin %s not found in community", slug.c_str()); } } } - - if (resJ) - json_decref(resJ); + catch (std::runtime_error &e) { + warn("Plugin sync error: %s", e.what()); + } if (communityResJ) json_decref(communityResJ); + if (resJ) + json_decref(resJ); + if (!dryRun) { isDownloading = false; } From 355614f86d103f1c0ecc0639cce77803616a1e24 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 13 Jan 2018 14:21:25 -0500 Subject: [PATCH 55/66] Close stream upon destructing AudioInterface --- src/core/AudioInterface.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index fe90229c..dd05a6d6 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -39,11 +39,6 @@ struct AudioInterfaceIO : AudioIO { maxInputs = MAX_INPUTS; } - ~AudioInterfaceIO() { - // Wait until processStream() is finished - std::lock_guard lock(audioMutex); - } - void processStream(const float *input, float *output, int length) override { if (numInputs > 0) { // TODO Do we need to wait on the input to be consumed here? @@ -111,6 +106,10 @@ struct AudioInterface : Module { AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { } + ~AudioInterface() { + audioIO.closeStream(); + } + void step() override; json_t *toJson() override { From 0142ee2d204771260d4cc573c660ef34ba0d85a2 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 13 Jan 2018 14:26:59 -0500 Subject: [PATCH 56/66] Last commit doesn't do anything, reverted --- src/core/AudioInterface.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index dd05a6d6..d8125fa5 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -106,10 +106,6 @@ struct AudioInterface : Module { AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { } - ~AudioInterface() { - audioIO.closeStream(); - } - void step() override; json_t *toJson() override { From ae251d8e575920142c56accb22b9a03cbef158c7 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 13 Jan 2018 15:00:49 -0500 Subject: [PATCH 57/66] Un-reverting last commit --- src/core/AudioInterface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index d8125fa5..68b0ed33 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -39,6 +39,10 @@ struct AudioInterfaceIO : AudioIO { maxInputs = MAX_INPUTS; } + ~AudioInterfaceIO() { + closeStream(); + } + void processStream(const float *input, float *output, int length) override { if (numInputs > 0) { // TODO Do we need to wait on the input to be consumed here? From 575e4df2fdbb3155c0a072adcb11cacf3090920b Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 15 Jan 2018 00:02:42 -0500 Subject: [PATCH 58/66] Use RELEASE=1 to build release version. VERSION is automatically set in the Makefile now. Add detection code for legacy1 mode for patch files. Initialize patch on launch if the last launch crashed after 15 seconds. --- Makefile | 9 ++++++--- include/engine.hpp | 15 +++++++++++++-- include/settings.hpp | 3 +++ include/util.hpp | 5 +++-- src/app.cpp | 7 +------ src/app/AddModuleWindow.cpp | 4 ++-- src/app/RackScene.cpp | 10 +++++----- src/app/RackWidget.cpp | 18 ++++++++++++------ src/app/Toolbar.cpp | 2 +- src/asset.cpp | 12 ++++++------ src/core/MidiToCV.cpp | 17 +++++------------ src/core/core.cpp | 2 +- src/core/core.hpp | 34 ++++++++++++++++------------------ src/gui.cpp | 5 +---- src/main.cpp | 19 ++++++++++++------- src/settings.cpp | 23 +++++++++++++++++------ src/util.cpp | 8 ++++++-- 17 files changed, 110 insertions(+), 83 deletions(-) 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); From 3a8dc0e0e3a0442bfd89461600fa9db8fdc5f4c3 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 15 Jan 2018 08:24:29 -0500 Subject: [PATCH 59/66] Use Module::params/inputs/outputs indices instead of ModuleWidget::params/inputs/outputs indices for patch ids --- src/app/ModuleWidget.cpp | 33 +++++++++++++++++++++--- src/app/ParamWidget.cpp | 10 +++++--- src/app/RackWidget.cpp | 54 +++++++++++++++++++++++++++++----------- 3 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index bdd5e0d8..cab832b7 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -89,6 +89,12 @@ json_t *ModuleWidget::toJson() { } void ModuleWidget::fromJson(json_t *rootJ) { + // legacy + int legacy = 0; + json_t *legacyJ = json_object_get(rootJ, "legacy"); + if (legacyJ) + legacy = json_integer_value(legacyJ); + // pos json_t *posJ = json_object_get(rootJ, "pos"); double x, y; @@ -97,11 +103,30 @@ void ModuleWidget::fromJson(json_t *rootJ) { // params json_t *paramsJ = json_object_get(rootJ, "params"); - size_t paramId; + size_t i; json_t *paramJ; - json_array_foreach(paramsJ, paramId, paramJ) { - if (paramId < params.size()) { - params[paramId]->fromJson(paramJ); + json_array_foreach(paramsJ, i, paramJ) { + if (legacy && legacy <= 1) { + // The index in the array we're iterating is the index of the ParamWidget in the params vector. + if (i < params.size()) { + // Create upgraded version of param JSON object + json_t *newParamJ = json_object(); + json_object_set(newParamJ, "value", paramJ); + params[i]->fromJson(newParamJ); + json_decref(newParamJ); + } + } + else { + // Get paramId + json_t *paramIdJ = json_object_get(paramJ, "paramId"); + if (!paramIdJ) + continue; + int paramId = json_integer_value(paramIdJ); + // Find ParamWidget(s) with paramId + for (ParamWidget *paramWidget : params) { + if (paramWidget->paramId == paramId) + paramWidget->fromJson(paramJ); + } } } diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index dcfde25f..c3ee57d1 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -6,12 +6,16 @@ namespace rack { json_t *ParamWidget::toJson() { - json_t *paramJ = json_real(value); - return paramJ; + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "paramId", json_integer(paramId)); + json_object_set_new(rootJ, "value", json_real(value)); + return rootJ; } void ParamWidget::fromJson(json_t *rootJ) { - setValue(json_number_value(rootJ)); + json_t *valueJ = json_object_get(rootJ, "value"); + if (valueJ) + setValue(json_number_value(valueJ)); } void ParamWidget::randomize() { diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 9a51193a..c252b72c 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -184,13 +184,8 @@ json_t *RackWidget::toJson() { int inputModuleId = moduleIds[inputModuleWidget]; // Get output/input ports - auto outputIt = std::find(outputModuleWidget->outputs.begin(), outputModuleWidget->outputs.end(), wireWidget->outputPort); - assert(outputIt != outputModuleWidget->outputs.end()); - int outputId = outputIt - outputModuleWidget->outputs.begin(); - - auto inputIt = std::find(inputModuleWidget->inputs.begin(), inputModuleWidget->inputs.end(), wireWidget->inputPort); - assert(inputIt != inputModuleWidget->inputs.end()); - int inputId = inputIt - inputModuleWidget->inputs.begin(); + int outputId = wireWidget->outputPort->portId; + int inputId = wireWidget->inputPort->portId; json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId)); json_object_set_new(wire, "outputId", json_integer(outputId)); @@ -218,9 +213,12 @@ void RackWidget::fromJson(json_t *rootJ) { // 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"); + int legacy = 0; + if (startsWith(version, "0.3.") || startsWith(version, "0.4.") || startsWith(version, "0.5.") || version == "" || version == "dev") { + legacy = 1; + } + if (legacy) { + info("Loading patch using legacy mode %d", legacy); } // modules @@ -230,6 +228,10 @@ void RackWidget::fromJson(json_t *rootJ) { size_t moduleId; json_t *moduleJ; json_array_foreach(modulesJ, moduleId, moduleJ) { + // Set legacy property + if (legacy) + json_object_set_new(moduleJ, "legacy", json_integer(legacy)); + json_t *pluginSlugJ = json_object_get(moduleJ, "plugin"); if (!pluginSlugJ) continue; json_t *modelSlugJ = json_object_get(moduleJ, "model"); @@ -285,15 +287,37 @@ void RackWidget::fromJson(json_t *rootJ) { "outputModuleId", &outputModuleId, "outputId", &outputId, "inputModuleId", &inputModuleId, "inputId", &inputId); if (err) continue; - // Get ports + + // Get module widgets ModuleWidget *outputModuleWidget = moduleWidgets[outputModuleId]; if (!outputModuleWidget) continue; - Port *outputPort = outputModuleWidget->outputs[outputId]; - if (!outputPort) continue; ModuleWidget *inputModuleWidget = moduleWidgets[inputModuleId]; if (!inputModuleWidget) continue; - Port *inputPort = inputModuleWidget->inputs[inputId]; - if (!inputPort) continue; + + // Get port widgets + Port *outputPort = NULL; + Port *inputPort = NULL; + if (legacy && legacy <= 1) { + outputPort = outputModuleWidget->outputs[outputId]; + inputPort = inputModuleWidget->inputs[inputId]; + } + else { + for (Port *port : outputModuleWidget->outputs) { + if (port->portId == outputId) { + outputPort = port; + break; + } + } + for (Port *port : inputModuleWidget->inputs) { + if (port->portId == inputId) { + inputPort = port; + break; + } + } + } + if (!outputPort || !inputPort) + continue; + // Create WireWidget WireWidget *wireWidget = new WireWidget(); wireWidget->outputPort = outputPort; From 4a3bf84e42c77b4d331e1a25e905f1d867295224 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 15 Jan 2018 09:03:05 -0500 Subject: [PATCH 60/66] Serialize wire color --- include/app.hpp | 12 +++++++++- include/widgets.hpp | 3 --- src/app.cpp | 19 ++++++++++++++++ src/app/RackWidget.cpp | 51 +++++++++++++++++++++--------------------- src/app/WireWidget.cpp | 12 ++++++++++ src/widgets.cpp | 1 + 6 files changed, 68 insertions(+), 30 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index 81812647..f122f2ff 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -7,6 +7,10 @@ namespace rack { +#define CHECKMARK_STRING "✔" +#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") + + struct Model; struct Module; struct Wire; @@ -90,6 +94,8 @@ struct WireWidget : OpaqueWidget { void updateWire(); Vec getOutputPos(); Vec getInputPos(); + json_t *toJson(); + void fromJson(json_t *rootJ); void draw(NVGcontext *vg) override; void drawPlugs(NVGcontext *vg); }; @@ -132,7 +138,7 @@ struct RackWidget : OpaqueWidget { void fromJson(json_t *rootJ); void addModule(ModuleWidget *m); - /** Transfers ownership to the caller so they must `delete` it if that is the intension */ + /** Removes the module and transfers ownership to the caller */ void deleteModule(ModuleWidget *m); void cloneModule(ModuleWidget *m); /** Sets a module's box if non-colliding. Returns true if set */ @@ -420,4 +426,8 @@ extern Toolbar *gToolbar; void sceneInit(); void sceneDestroy(); +json_t *colorToJson(NVGcolor color); +NVGcolor jsonToColor(json_t *colorJ); + + } // namespace rack diff --git a/include/widgets.hpp b/include/widgets.hpp index c400d80a..6c8fa9f3 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -25,9 +25,6 @@ inline Vec mm2px(Vec millimeters) { return millimeters.mult(SVG_DPI / 25.4); } -#define CHECKMARK_STRING "✔" -#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") - //////////////////// // resources diff --git a/src/app.cpp b/src/app.cpp index 188c4a1b..c8dc1ad8 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -24,4 +24,23 @@ void sceneDestroy() { } +json_t *colorToJson(NVGcolor color) { + json_t *colorJ = json_object(); + json_object_set_new(colorJ, "r", json_real(color.r)); + json_object_set_new(colorJ, "g", json_real(color.g)); + json_object_set_new(colorJ, "b", json_real(color.b)); + json_object_set_new(colorJ, "a", json_real(color.a)); + return colorJ; +} + +NVGcolor jsonToColor(json_t *colorJ) { + NVGcolor color; + color.r = json_number_value(json_object_get(colorJ, "r")); + color.g = json_number_value(json_object_get(colorJ, "g")); + color.b = json_number_value(json_object_get(colorJ, "b")); + color.a = json_number_value(json_object_get(colorJ, "a")); + return color; +} + + } // namespace rack diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index c252b72c..cd1b7f0b 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -172,26 +172,26 @@ json_t *RackWidget::toJson() { if (!(wireWidget->outputPort && wireWidget->inputPort)) continue; // wire - json_t *wire = json_object(); - { - // Get the modules at each end of the wire - ModuleWidget *outputModuleWidget = wireWidget->outputPort->getAncestorOfType(); - assert(outputModuleWidget); - int outputModuleId = moduleIds[outputModuleWidget]; - - ModuleWidget *inputModuleWidget = wireWidget->inputPort->getAncestorOfType(); - assert(inputModuleWidget); - int inputModuleId = moduleIds[inputModuleWidget]; - - // Get output/input ports - int outputId = wireWidget->outputPort->portId; - int inputId = wireWidget->inputPort->portId; - - json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId)); - json_object_set_new(wire, "outputId", json_integer(outputId)); - json_object_set_new(wire, "inputModuleId", json_integer(inputModuleId)); - json_object_set_new(wire, "inputId", json_integer(inputId)); - } + json_t *wire = wireWidget->toJson(); + + // Get the modules at each end of the wire + ModuleWidget *outputModuleWidget = wireWidget->outputPort->getAncestorOfType(); + assert(outputModuleWidget); + int outputModuleId = moduleIds[outputModuleWidget]; + + ModuleWidget *inputModuleWidget = wireWidget->inputPort->getAncestorOfType(); + assert(inputModuleWidget); + int inputModuleId = moduleIds[inputModuleWidget]; + + // Get output/input ports + int outputId = wireWidget->outputPort->portId; + int inputId = wireWidget->inputPort->portId; + + json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId)); + json_object_set_new(wire, "outputId", json_integer(outputId)); + json_object_set_new(wire, "inputModuleId", json_integer(inputModuleId)); + json_object_set_new(wire, "inputId", json_integer(inputId)); + json_array_append_new(wires, wire); } json_object_set_new(rootJ, "wires", wires); @@ -281,12 +281,10 @@ void RackWidget::fromJson(json_t *rootJ) { size_t wireId; json_t *wireJ; json_array_foreach(wiresJ, wireId, wireJ) { - int outputModuleId, outputId; - int inputModuleId, inputId; - int err = json_unpack(wireJ, "{s:i, s:i, s:i, s:i}", - "outputModuleId", &outputModuleId, "outputId", &outputId, - "inputModuleId", &inputModuleId, "inputId", &inputId); - if (err) continue; + int outputModuleId = json_integer_value(json_object_get(wireJ, "outputModuleId")); + int outputId = json_integer_value(json_object_get(wireJ, "outputId")); + int inputModuleId = json_integer_value(json_object_get(wireJ, "inputModuleId")); + int inputId = json_integer_value(json_object_get(wireJ, "inputId")); // Get module widgets ModuleWidget *outputModuleWidget = moduleWidgets[outputModuleId]; @@ -320,6 +318,7 @@ void RackWidget::fromJson(json_t *rootJ) { // Create WireWidget WireWidget *wireWidget = new WireWidget(); + wireWidget->fromJson(wireJ); wireWidget->outputPort = outputPort; wireWidget->inputPort = inputPort; wireWidget->updateWire(); diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index f75574de..e202d247 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -142,6 +142,18 @@ Vec WireWidget::getInputPos() { } } +json_t *WireWidget::toJson() { + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "color", colorToJson(color)); + return rootJ; +} + +void WireWidget::fromJson(json_t *rootJ) { + json_t *colorJ = json_object_get(rootJ, "color"); + if (colorJ) + color = jsonToColor(colorJ); +} + void WireWidget::draw(NVGcontext *vg) { float opacity = gToolbar->wireOpacitySlider->value / 100.0; float tension = gToolbar->wireTensionSlider->value; diff --git a/src/widgets.cpp b/src/widgets.cpp index ff6c5c0b..037fc173 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -9,4 +9,5 @@ Widget *gFocusedWidget = NULL; Scene *gScene = NULL; + } // namespace rack From d55d460c1f10c9f26d04b7f43de7138e0158ffb2 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 15 Jan 2018 09:13:59 -0500 Subject: [PATCH 61/66] ModuleWidget serializes pos in rack grid coordinates instead of pixel coordinates --- include/app.hpp | 14 ++++++++++++-- include/widgets.hpp | 13 ------------- src/app/ModuleWidget.cpp | 11 +++++++++-- src/gui.cpp | 1 + 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index f122f2ff..dbcc6fe2 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -4,13 +4,23 @@ #include "widgets.hpp" -namespace rack { - +#define SVG_DPI 75.0 #define CHECKMARK_STRING "✔" #define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") +namespace rack { + +inline Vec in2px(Vec inches) { + return inches.mult(SVG_DPI); +} + +inline Vec mm2px(Vec millimeters) { + return millimeters.mult(SVG_DPI / 25.4); +} + + struct Model; struct Module; struct Wire; diff --git a/include/widgets.hpp b/include/widgets.hpp index 6c8fa9f3..6ebfaec6 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -11,21 +11,8 @@ #include "events.hpp" -#define SVG_DPI 75.0 - - namespace rack { - -inline Vec in2px(Vec inches) { - return inches.mult(SVG_DPI); -} - -inline Vec mm2px(Vec millimeters) { - return millimeters.mult(SVG_DPI / 25.4); -} - - //////////////////// // resources //////////////////// diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index cab832b7..393aee5b 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -68,7 +68,8 @@ json_t *ModuleWidget::toJson() { // model json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); // pos - json_t *posJ = json_pack("[f, f]", (double) box.pos.x, (double) box.pos.y); + Vec pos = box.pos.div(RACK_GRID_SIZE).round(); + json_t *posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y); json_object_set_new(rootJ, "pos", posJ); // params json_t *paramsJ = json_array(); @@ -99,7 +100,13 @@ void ModuleWidget::fromJson(json_t *rootJ) { json_t *posJ = json_object_get(rootJ, "pos"); double x, y; json_unpack(posJ, "[F, F]", &x, &y); - box.pos = Vec(x, y); + Vec pos = Vec(x, y); + if (legacy && legacy <= 1) { + box.pos = pos; + } + else { + box.pos = pos.mult(RACK_GRID_SIZE); + } // params json_t *paramsJ = json_object_get(rootJ, "params"); diff --git a/src/gui.cpp b/src/gui.cpp index 6a940374..e614a77f 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -27,6 +27,7 @@ #include #endif + namespace rack { GLFWwindow *gWindow = NULL; From b51e380e3684076229725c83d70e93f25bad16c8 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 15 Jan 2018 09:25:41 -0500 Subject: [PATCH 62/66] Draw opaque wire if one of its ports is hovered --- src/app/WireWidget.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index e202d247..6b58c749 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -158,9 +158,17 @@ void WireWidget::draw(NVGcontext *vg) { float opacity = gToolbar->wireOpacitySlider->value / 100.0; float tension = gToolbar->wireTensionSlider->value; - // Draw as opaque if an "incomplete" wire - if (!(inputPort && outputPort)) - opacity = 1.0; + WireWidget *activeWire = gRackWidget->wireContainer->activeWire; + if (activeWire) { + // Draw as opaque if the wire is active + if (activeWire == this) + opacity = 1.0; + } + else { + Port *hoveredPort = dynamic_cast(gHoveredWidget); + if (hoveredPort && (hoveredPort == outputPort || hoveredPort == inputPort)) + opacity = 1.0; + } Vec outputPos = getOutputPos(); Vec inputPos = getInputPos(); From 5e6463f5af0a6b5536c84f9f00d26c5a23c9c80d Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 19 Jan 2018 06:55:58 -0500 Subject: [PATCH 63/66] Reorganize midi.hpp and audio.hpp, add prompt before clearing patch after crash, add Bridge scaffolding to AudioIO --- include/audio.hpp | 8 +- include/midi.hpp | 29 +++++- src/app/AudioWidget.cpp | 2 +- src/app/RackWidget.cpp | 2 +- src/audio.cpp | 189 ++++++++++++++++++++++++------------ src/core/AudioInterface.cpp | 37 +------ src/core/Bridge.cpp | 44 --------- src/core/MidiToCV.cpp | 59 ++++------- src/core/core.cpp | 3 +- src/main.cpp | 7 +- src/midi.cpp | 79 +++++++++++++-- src/settings.cpp | 2 +- 12 files changed, 260 insertions(+), 201 deletions(-) delete mode 100644 src/core/Bridge.cpp diff --git a/include/audio.hpp b/include/audio.hpp index fff6ce3c..b967410f 100644 --- a/include/audio.hpp +++ b/include/audio.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsuggest-override" #include @@ -13,20 +15,20 @@ struct AudioIO { int maxOutputs = 8; int maxInputs = 8; - RtAudio *stream = NULL; // Stream properties + int driver = 0; int device = -1; int sampleRate = 44100; int blockSize = 256; int numOutputs = 0; int numInputs = 0; + RtAudio *rtAudio = NULL; AudioIO(); virtual ~AudioIO(); std::vector listDrivers(); std::string getDriverName(int driver); - int getDriver(); void setDriver(int driver); int getDeviceCount(); @@ -40,6 +42,8 @@ struct AudioIO { virtual void processStream(const float *input, float *output, int length) {} virtual void onCloseStream() {} virtual void onOpenStream() {} + json_t *toJson(); + void fromJson(json_t *rootJ); }; diff --git a/include/midi.hpp b/include/midi.hpp index a55d4ac5..342a5b55 100644 --- a/include/midi.hpp +++ b/include/midi.hpp @@ -1,5 +1,11 @@ #pragma once +#include "util.hpp" +#include +#include +#include + + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsuggest-override" #include "rtmidi/RtMidi.h" @@ -9,8 +15,13 @@ namespace rack { +struct MidiMessage { + double time; + std::vector data; +}; + + struct MidiIO { - RtMidi *midi; int port = -1; /* For MIDI output, the channel to output messages. For MIDI input, the channel to filter. @@ -18,17 +29,27 @@ struct MidiIO { Zero indexed. */ int channel = -1; + RtMidi *rtMidi = NULL; virtual ~MidiIO() {} - virtual int getPortCount(); - virtual std::string getPortName(int port); - virtual void openPort(int port); + int getPortCount(); + std::string getPortName(int port); + void openPort(int port); + json_t *toJson(); + void fromJson(json_t *rootJ); }; struct MidiInput : MidiIO { MidiInput(); ~MidiInput(); + virtual void onMessage(const MidiMessage &message) {} +}; + + +struct MidiInputQueue : MidiInput { + std::queue messageQueue; + void onMessage(const MidiMessage &message) override; }; diff --git a/src/app/AudioWidget.cpp b/src/app/AudioWidget.cpp index 67ca64d9..bd73f662 100644 --- a/src/app/AudioWidget.cpp +++ b/src/app/AudioWidget.cpp @@ -59,7 +59,7 @@ void AudioWidget::onMouseDown(EventMouseDown &e) { item->audioIO = audioIO; item->driver = driver; item->text = audioIO->getDriverName(driver); - item->rightText = CHECKMARK(item->driver == audioIO->getDriver()); + item->rightText = CHECKMARK(item->driver == audioIO->driver); menu->addChild(item); } menu->addChild(construct()); diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index cd1b7f0b..ef37b5b5 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -110,7 +110,7 @@ void RackWidget::savePatch(std::string path) { FILE *file = fopen(path.c_str(), "w"); if (file) { - json_dumpf(rootJ, file, JSON_INDENT(2)); + json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); fclose(file); } diff --git a/src/audio.cpp b/src/audio.cpp index cc80df47..812951eb 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -3,6 +3,9 @@ #include "audio.hpp" +#define DRIVER_BRIDGE -1 + + namespace rack { @@ -20,6 +23,8 @@ std::vector AudioIO::listDrivers() { std::vector drivers; for (RtAudio::Api api : apis) drivers.push_back((int) api); + // Add Bridge fake driver + // drivers.push_back(DRIVER_BRIDGE); return drivers; } @@ -35,55 +40,71 @@ std::string AudioIO::getDriverName(int driver) { case RtAudio::WINDOWS_ASIO: return "ASIO"; case RtAudio::WINDOWS_DS: return "DirectSound"; case RtAudio::RTAUDIO_DUMMY: return "Dummy"; + case DRIVER_BRIDGE: return "VCV Bridge"; default: return "Unknown"; } } -int AudioIO::getDriver() { - if (!stream) - return RtAudio::UNSPECIFIED; - return stream->getCurrentApi(); -} - void AudioIO::setDriver(int driver) { + // Close driver closeStream(); - if (stream) - delete stream; - stream = new RtAudio((RtAudio::Api) driver); + if (rtAudio) { + delete rtAudio; + rtAudio = NULL; + } + this->driver = 0; + + // Open driver + if (driver >= 0) { + rtAudio = new RtAudio((RtAudio::Api) driver); + this->driver = (int) rtAudio->getCurrentApi(); + } + else if (driver == DRIVER_BRIDGE) { + // TODO Connect to Bridge + this->driver = DRIVER_BRIDGE; + } } int AudioIO::getDeviceCount() { - if (!stream) - return 0; - return stream->getDeviceCount(); + if (rtAudio) { + return rtAudio->getDeviceCount(); + } + if (driver == DRIVER_BRIDGE) { + return 16; + } + return 0; } std::string AudioIO::getDeviceName(int device) { - if (!stream || device < 0) - return ""; - - try { - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - return deviceInfo.name; + if (rtAudio) { + try { + RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device); + return deviceInfo.name; + } + catch (RtAudioError &e) { + warn("Failed to query RtAudio device: %s", e.what()); + } } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return ""; + if (driver == DRIVER_BRIDGE) { + return stringf("%d", device + 1); } + return ""; } std::string AudioIO::getDeviceDetail(int device) { - if (!stream || device < 0) - return ""; - - try { - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); + if (rtAudio) { + try { + RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device); + return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); + } + catch (RtAudioError &e) { + warn("Failed to query RtAudio device: %s", e.what()); + } } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return ""; + if (driver == DRIVER_BRIDGE) { + return stringf("Channel %d", device + 1); } + return ""; } static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { @@ -97,17 +118,18 @@ void AudioIO::openStream() { // Close device but remember the current device number int device = this->device; closeStream(); - if (!stream) + + if (device < 0) return; - // Open new device - if (device >= 0) { + if (rtAudio) { + // Open new device RtAudio::DeviceInfo deviceInfo; try { - deviceInfo = stream->getDeviceInfo(device); + deviceInfo = rtAudio->getDeviceInfo(device); } catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); + warn("Failed to query RtAudio device: %s", e.what()); return; } @@ -115,7 +137,7 @@ void AudioIO::openStream() { numInputs = mini(deviceInfo.inputChannels, maxInputs); if (numOutputs == 0 && numInputs == 0) { - warn("Audio device %d has 0 inputs and 0 outputs"); + warn("RtAudio device %d has 0 inputs and 0 outputs"); return; } @@ -138,56 +160,56 @@ void AudioIO::openStream() { } try { - debug("Opening audio stream %d", device); - stream->openStream( + debug("Opening audio RtAudio device %d", device); + rtAudio->openStream( numOutputs == 0 ? NULL : &outParameters, numInputs == 0 ? NULL : &inParameters, RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); } catch (RtAudioError &e) { - warn("Failed to open audio stream: %s", e.what()); + warn("Failed to open RtAudio stream: %s", e.what()); return; } try { - debug("Starting audio stream %d", device); - stream->startStream(); + debug("Starting RtAudio stream %d", device); + rtAudio->startStream(); } catch (RtAudioError &e) { - warn("Failed to start audio stream: %s", e.what()); + warn("Failed to start RtAudio stream: %s", e.what()); return; } // Update sample rate because this may have changed - this->sampleRate = stream->getStreamSampleRate(); + this->sampleRate = rtAudio->getStreamSampleRate(); this->device = device; onOpenStream(); } } void AudioIO::closeStream() { - if (stream) { - if (stream->isStreamRunning()) { - debug("Stopping audio stream %d", device); + if (rtAudio) { + if (rtAudio->isStreamRunning()) { + debug("Stopping RtAudio stream %d", device); try { - stream->stopStream(); + rtAudio->stopStream(); } catch (RtAudioError &e) { - warn("Failed to stop stream %s", e.what()); + warn("Failed to stop RtAudio stream %s", e.what()); } } - if (stream->isStreamOpen()) { - debug("Closing audio stream %d", device); + if (rtAudio->isStreamOpen()) { + debug("Closing RtAudio stream %d", device); try { - stream->closeStream(); + rtAudio->closeStream(); } catch (RtAudioError &e) { - warn("Failed to close stream %s", e.what()); + warn("Failed to close RtAudio stream %s", e.what()); } } } - // Reset stream settings + // Reset rtAudio settings device = -1; numOutputs = 0; numInputs = 0; @@ -195,18 +217,59 @@ void AudioIO::closeStream() { } std::vector AudioIO::listSampleRates() { - if (!stream || device < 0) - return {}; - - try { - RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); - std::vector sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); - return sampleRates; + if (rtAudio) { + try { + RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device); + std::vector sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); + return sampleRates; + } + catch (RtAudioError &e) { + warn("Failed to query RtAudio device: %s", e.what()); + } } - catch (RtAudioError &e) { - warn("Failed to query audio device: %s", e.what()); - return {}; + if (driver == DRIVER_BRIDGE) { + return {44100, 48000, 88200, 96000, 176400, 192000}; } + + return {}; +} + +json_t *AudioIO::toJson() { + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "driver", json_integer(driver)); + std::string deviceName = getDeviceName(device); + json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); + json_object_set_new(rootJ, "sampleRate", json_integer(sampleRate)); + json_object_set_new(rootJ, "blockSize", json_integer(blockSize)); + return rootJ; +} + +void AudioIO::fromJson(json_t *rootJ) { + json_t *driverJ = json_object_get(rootJ, "driver"); + if (driverJ) + setDriver(json_number_value(driverJ)); + + json_t *deviceNameJ = json_object_get(rootJ, "deviceName"); + if (deviceNameJ) { + std::string deviceName = json_string_value(deviceNameJ); + // Search for device ID with equal name + for (int device = 0; device < getDeviceCount(); device++) { + if (getDeviceName(device) == deviceName) { + this->device = device; + break; + } + } + } + + json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); + if (sampleRateJ) + sampleRate = json_integer_value(sampleRateJ); + + json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); + if (blockSizeJ) + blockSize = json_integer_value(blockSizeJ); + + openStream(); } diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 68b0ed33..e4edc563 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -114,40 +114,13 @@ struct AudioInterface : Module { json_t *toJson() override { json_t *rootJ = json_object(); - json_object_set_new(rootJ, "driver", json_integer(audioIO.getDriver())); - std::string deviceName = audioIO.getDeviceName(audioIO.device); - json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); - json_object_set_new(rootJ, "sampleRate", json_integer(audioIO.sampleRate)); - json_object_set_new(rootJ, "blockSize", json_integer(audioIO.blockSize)); + json_object_set_new(rootJ, "audio", audioIO.toJson()); return rootJ; } void fromJson(json_t *rootJ) override { - json_t *driverJ = json_object_get(rootJ, "driver"); - if (driverJ) - audioIO.setDriver(json_number_value(driverJ)); - - json_t *deviceNameJ = json_object_get(rootJ, "deviceName"); - if (deviceNameJ) { - std::string deviceName = json_string_value(deviceNameJ); - // Search for device ID with equal name - for (int device = 0; device < audioIO.getDeviceCount(); device++) { - if (audioIO.getDeviceName(device) == deviceName) { - audioIO.device = device; - break; - } - } - } - - json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); - if (sampleRateJ) - audioIO.sampleRate = json_integer_value(sampleRateJ); - - json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); - if (blockSizeJ) - audioIO.blockSize = json_integer_value(blockSizeJ); - - audioIO.openStream(); + json_t *audioJ = json_object_get(rootJ, "audio"); + audioIO.fromJson(audioJ); } void onReset() override { @@ -236,7 +209,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { { Label *label = new Label(); label->box.pos = Vec(margin.x, yPos); - label->text = "Outputs"; + label->text = "Outputs (DACs)"; addChild(label); yPos += labelHeight + margin.y; } @@ -270,7 +243,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { { Label *label = new Label(); label->box.pos = Vec(margin.x, yPos); - label->text = "Inputs"; + label->text = "Inputs (ADCs)"; addChild(label); yPos += labelHeight + margin.y; } diff --git a/src/core/Bridge.cpp b/src/core/Bridge.cpp deleted file mode 100644 index bb7ade53..00000000 --- a/src/core/Bridge.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "core.hpp" - -using namespace rack; - - -struct Bridge : Module { - enum ParamIds { - NUM_PARAMS - }; - enum InputIds { - NUM_INPUTS - }; - enum OutputIds { - NUM_OUTPUTS - }; - - Bridge() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { - } - ~Bridge() { - } - void step() override; -}; - - -void Bridge::step() { -} - - -BridgeWidget::BridgeWidget() { - Bridge *module = new Bridge(); - setModule(module); - box.size = Vec(15*8, 380); - - { - Panel *panel = new LightPanel(); - panel->box.size = box.size; - addChild(panel); - } - - addChild(createScrew(Vec(15, 0))); - addChild(createScrew(Vec(box.size.x-30, 0))); - addChild(createScrew(Vec(15, 365))); - addChild(createScrew(Vec(box.size.x-30, 365))); -} diff --git a/src/core/MidiToCV.cpp b/src/core/MidiToCV.cpp index 89fd38a9..ba3620c1 100644 --- a/src/core/MidiToCV.cpp +++ b/src/core/MidiToCV.cpp @@ -1,4 +1,3 @@ -#if 0 #include #include #include "core.hpp" @@ -12,7 +11,7 @@ */ struct MidiValue { int val = 0; // Controller value - TransitionSmoother tSmooth; + // TransitionSmoother tSmooth; bool changed = false; // Value has been changed by midi message (only if it is in sync!) }; @@ -38,6 +37,7 @@ struct MIDIToCVInterface : Module { NUM_LIGHTS }; + MidiInputQueue midiInput; std::list notes; bool pedal = false; int note = 60; // C4, most modules should use 261.626 Hz @@ -51,7 +51,7 @@ struct MIDIToCVInterface : Module { MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { pitchWheel.val = 64; - pitchWheel.tSmooth.set(0, 0); + // pitchWheel.tSmooth.set(0, 0); } ~MIDIToCVInterface() { @@ -67,22 +67,22 @@ struct MIDIToCVInterface : Module { json_t *toJson() override { json_t *rootJ = json_object(); - addBaseJson(rootJ); + // addBaseJson(rootJ); return rootJ; } void fromJson(json_t *rootJ) override { - baseFromJson(rootJ); + // baseFromJson(rootJ); } void onReset() override { - resetMidi(); + // resetMidi(); } - void resetMidi() override; - + // void resetMidi() override; }; +/* void MIDIToCVInterface::resetMidi() { mod.val = 0; mod.tSmooth.set(0, 0); @@ -94,8 +94,10 @@ void MIDIToCVInterface::resetMidi() { gate = false; notes.clear(); } +*/ void MIDIToCVInterface::step() { + /* if (isPortOpen()) { std::vector message; @@ -132,11 +134,8 @@ void MIDIToCVInterface::step() { } outputs[PITCHWHEEL_OUTPUT].value = pitchWheel.tSmooth.next(); - - /* NOTE: I'll leave out value smoothing for after touch for now. I currently don't - * have an after touch capable device around and I assume it would require different - * smoothing*/ outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0; + */ } void MIDIToCVInterface::pressNote(int note) { @@ -171,6 +170,7 @@ void MIDIToCVInterface::releaseNote(int note) { } void MIDIToCVInterface::processMidi(std::vector msg) { + /* int channel = msg[0] & 0xf; int status = (msg[0] >> 4) & 0xf; int data1 = msg[1]; @@ -220,6 +220,7 @@ void MIDIToCVInterface::processMidi(std::vector msg) { afterTouch.changed = true; break; } + */ } @@ -254,35 +255,6 @@ 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)); - { - Label *label = new Label(); - label->box.pos = Vec(margin, yPos); - label->text = "MIDI Interface"; - addChild(label); - yPos += labelHeight + margin; - - MidiChoice *midiChoice = new MidiChoice(); - midiChoice->midiModule = dynamic_cast(module); - midiChoice->box.pos = Vec(margin, yPos); - midiChoice->box.size.x = box.size.x - 10; - addChild(midiChoice); - yPos += midiChoice->box.size.y + margin; - } - - { - Label *label = new Label(); - label->box.pos = Vec(margin, yPos); - label->text = "Channel"; - addChild(label); - yPos += labelHeight + margin; - - ChannelChoice *channelChoice = new ChannelChoice(); - channelChoice->midiModule = dynamic_cast(module); - channelChoice->box.pos = Vec(margin, yPos); - channelChoice->box.size.x = box.size.x - 10; - addChild(channelChoice); - yPos += channelChoice->box.size.y + margin + 15; - } std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", "Aftertouch"}; @@ -296,5 +268,8 @@ MidiToCVWidget::MidiToCVWidget() { yPos += yGap + margin; } + + MidiWidget *midiWidget = construct(); + midiWidget->midiIO = &module->midiInput; + addChild(midiWidget); } -#endif \ No newline at end of file diff --git a/src/core/core.cpp b/src/core/core.cpp index a444e273..ad4de7b7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -9,13 +9,12 @@ void init(rack::Plugin *p) { 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)); // p->addModel(createModel("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG)); - // p->addModel(createModel("Core", "Bridge", "Bridge")); p->addModel(createModel("Core", "Blank", "Blank", BLANK_TAG)); p->addModel(createModel("Core", "Notes", "Notes", BLANK_TAG)); } diff --git a/src/main.cpp b/src/main.cpp index 6252506c..8f707823 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "settings.hpp" #include "asset.hpp" #include +#include "../ext/osdialog/osdialog.h" using namespace rack; @@ -40,8 +41,12 @@ int main(int argc, char* argv[]) { skipAutosaveOnLaunch = true; settingsSave(assetLocal("settings.json")); skipAutosaveOnLaunch = false; - if (!oldSkipAutosaveOnLaunch) + if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, likely caused by a faulty module in your patch. Would you like to clear your patch and start over?")) { + // Do nothing. Empty patch is already loaded. + } + else { gRackWidget->loadPatch(assetLocal("autosave.vcv")); + } engineStart(); guiRun(); diff --git a/src/midi.cpp b/src/midi.cpp index e30b3163..bbb2a9a1 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -4,38 +4,101 @@ namespace rack { +//////////////////// +// MidiIO +//////////////////// + int MidiIO::getPortCount() { - return midi->getPortCount(); + return rtMidi->getPortCount(); } std::string MidiIO::getPortName(int port) { - return midi->getPortName(port); + if (port < 0) + return ""; + return rtMidi->getPortName(port); } void MidiIO::openPort(int port) { - midi->closePort(); + rtMidi->closePort(); if (port >= 0) { - midi->openPort(port); + rtMidi->openPort(port); } this->port = port; } +json_t *MidiIO::toJson() { + json_t *rootJ = json_object(); + std::string portName = getPortName(port); + json_object_set_new(rootJ, "port", json_string(portName.c_str())); + json_object_set_new(rootJ, "channel", json_integer(channel)); + return rootJ; +} + +void MidiIO::fromJson(json_t *rootJ) { + json_t *portNameJ = json_object_get(rootJ, "port"); + if (portNameJ) { + std::string portName = json_string_value(portNameJ); + // Search for port with equal name + for (int port = 0; port < getPortCount(); port++) { + if (getPortName(port) == portName) { + openPort(port); + break; + } + } + } + + json_t *channelJ = json_object_get(rootJ, "channel"); + if (channelJ) + channel = json_integer_value(channelJ); +} + +//////////////////// +// MidiInput +//////////////////// + +static void midiInputCallback(double timeStamp, std::vector *message, void *userData) { + if (!message) return; + if (!userData) return; + + MidiInput *midiInput = (MidiInput*) userData; + if (!midiInput) return; + MidiMessage midiMessage; + midiMessage.time = timeStamp; + midiMessage.data = *message; + midiInput->onMessage(midiMessage); +} + MidiInput::MidiInput() { - midi = new RtMidiIn(); + RtMidiIn *rtMidiIn = new RtMidiIn(); + rtMidi = rtMidiIn; + rtMidiIn->setCallback(midiInputCallback, this); } MidiInput::~MidiInput() { - delete dynamic_cast(midi); + delete dynamic_cast(rtMidi); +} + +void MidiInputQueue::onMessage(const MidiMessage &message) { + for (uint8_t d : message.data) { + debug("MIDI message: %02x", d); + } + + const int messageQueueSize = 8192; + if (messageQueue.size() < messageQueueSize) + messageQueue.push(message); } +//////////////////// +// MidiOutput +//////////////////// MidiOutput::MidiOutput() { - midi = new RtMidiOut(); + rtMidi = new RtMidiOut(); } MidiOutput::~MidiOutput() { - delete dynamic_cast(midi); + delete dynamic_cast(rtMidi); } diff --git a/src/settings.cpp b/src/settings.cpp index a6425fb1..e593c92b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -137,7 +137,7 @@ void settingsSave(std::string filename) { if (!file) return; - json_dumpf(rootJ, file, JSON_INDENT(2)); + json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); json_decref(rootJ); fclose(file); } From 0e104ae86944fba0e77d21b2587d0a0ae9a8167c Mon Sep 17 00:00:00 2001 From: Brian Sorahan Date: Fri, 19 Jan 2018 11:12:24 -0600 Subject: [PATCH 64/66] fix error message in RackWidget::fromJson --- src/app/RackWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index ef37b5b5..9a75d62d 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -263,7 +263,7 @@ void RackWidget::fromJson(json_t *rootJ) { } if (!model) { - message += stringf("Could not find module \"%s\" in plugin \"%s\"\n", pluginSlug.c_str(), modelSlug.c_str()); + message += stringf("Could not find module \"%s\" in plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); continue; } From 077c179a1ee87a40d850b10ab73497ccaf73a243 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 20 Jan 2018 12:22:30 -0500 Subject: [PATCH 65/66] Re-fix LightWidget drawing --- src/app/LightWidget.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 70f3a00d..2ec2a3f5 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -14,7 +14,6 @@ void LightWidget::draw(NVGcontext *vg) { drawHalo(vg); } - void LightWidget::drawLight(NVGcontext *vg) { float radius = box.size.x / 2.0; @@ -25,19 +24,19 @@ void LightWidget::drawLight(NVGcontext *vg) { nvgFillColor(vg, bgColor); nvgFill(vg); - // // Border - // nvgStrokeWidth(vg, 1.0); - // NVGcolor borderColor = bgColor; - // borderColor.a *= 0.5; - // nvgStrokeColor(vg, borderColor); - // nvgStroke(vg); + // Border + nvgStrokeWidth(vg, 1.0); + NVGcolor borderColor = bgColor; + borderColor.a *= 0.5; + nvgStrokeColor(vg, borderColor); + nvgStroke(vg); // Inner glow nvgFillColor(vg, color); + nvgGlobalCompositeOperation(vg, NVG_LIGHTER); nvgFill(vg); } - void LightWidget::drawHalo(NVGcontext *vg) { float radius = box.size.x / 2.0; float oradius = radius + 15.0; @@ -57,6 +56,4 @@ void LightWidget::drawHalo(NVGcontext *vg) { } - - } // namespace rack From f31c13270d72d07dfa58173e410c554f0ace7cd4 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 20 Jan 2018 15:11:24 -0500 Subject: [PATCH 66/66] Enable denormals-are-zero (DAZ) mode in engine --- src/engine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index d7097e4b..7dd2fd07 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "engine.hpp" #include "util.hpp" @@ -113,9 +114,10 @@ static void engineStep() { } static void engineRun() { - // Set CPU to denormals-are-zero mode - // http://carlh.net/plugins/denormals.php + // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode + // https://software.intel.com/en-us/node/682949 _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); // Every time the engine waits and locks a mutex, it steps this many frames const int mutexSteps = 64;