# Conflicts: # .gitignorepull/1514/head^2
| @@ -12,5 +12,5 @@ | |||||
| /autosave.vcv | /autosave.vcv | ||||
| /settings.json | /settings.json | ||||
| /screenshots | /screenshots | ||||
| /.vs | |||||
| /_ReSharper.Caches | |||||
| .vs/ | |||||
| _ReSharper.Caches/ | |||||
| @@ -2,6 +2,13 @@ | |||||
| In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. | In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. | ||||
| ### 1.1.5 (in development) | |||||
| - Swap order of tags and brands in Module Browser. | |||||
| - Disable smoothing for MIDI CC buttons in MIDI-Map. | |||||
| - Automatically unzip update on Mac. | |||||
| - API | |||||
| - Add libsamplerate library. | |||||
| ### 1.1.4 (2019-08-22) | ### 1.1.4 (2019-08-22) | ||||
| - Fix parameter smoothing of MIDI-Map. | - Fix parameter smoothing of MIDI-Map. | ||||
| - Sort modules within plugin in the Module Browser according to plugin rather than alphabetically. | - Sort modules within plugin in the Module Browser according to plugin rather than alphabetically. | ||||
| @@ -536,4 +536,33 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 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 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | |||||
| SOFTWARE. | |||||
| # libsamplerate | |||||
| Copyright (c) 2012-2016, Erik de Castro Lopo <erikd@mega-nerd.com> | |||||
| All rights reserved. | |||||
| Redistribution and use in source and binary forms, with or without | |||||
| modification, are permitted provided that the following conditions are | |||||
| met: | |||||
| 1. Redistributions of source code must retain the above copyright | |||||
| notice, this list of conditions and the following disclaimer. | |||||
| 2. Redistributions in binary form must reproduce the above copyright | |||||
| notice, this list of conditions and the following disclaimer in the | |||||
| documentation and/or other materials provided with the distribution. | |||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | |||||
| IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |||||
| TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | |||||
| PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |||||
| TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||||
| PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||||
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||||
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
| @@ -1,6 +1,6 @@ | |||||
| RACK_DIR ?= . | RACK_DIR ?= . | ||||
| # VERSION := 1.dev.$(shell git rev-parse --short HEAD) | |||||
| VERSION := 1.1.4 | |||||
| VERSION := 1.dev.$(shell git rev-parse --short HEAD) | |||||
| # VERSION := 1.1.4 | |||||
| FLAGS += -DVERSION=$(VERSION) | FLAGS += -DVERSION=$(VERSION) | ||||
| FLAGS += -Iinclude -Idep/include | FLAGS += -Iinclude -Idep/include | ||||
| @@ -21,7 +21,7 @@ ifdef ARCH_LIN | |||||
| build/dep/osdialog/osdialog_gtk2.c.o: FLAGS += $(shell pkg-config --cflags gtk+-2.0) | build/dep/osdialog/osdialog_gtk2.c.o: FLAGS += $(shell pkg-config --cflags gtk+-2.0) | ||||
| LDFLAGS += -rdynamic \ | LDFLAGS += -rdynamic \ | ||||
| dep/lib/libglfw3.a dep/lib/libGLEW.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/librtmidi.a dep/lib/librtaudio.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a \ | |||||
| dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a \ | |||||
| -lpthread -lGL -ldl -lX11 -lasound -ljack \ | -lpthread -lGL -ldl -lX11 -lasound -ljack \ | ||||
| $(shell pkg-config --libs gtk+-2.0) | $(shell pkg-config --libs gtk+-2.0) | ||||
| TARGET := Rack | TARGET := Rack | ||||
| @@ -31,14 +31,14 @@ ifdef ARCH_MAC | |||||
| SOURCES += dep/osdialog/osdialog_mac.m | SOURCES += dep/osdialog/osdialog_mac.m | ||||
| LDFLAGS += -lpthread -ldl \ | LDFLAGS += -lpthread -ldl \ | ||||
| -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI \ | -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI \ | ||||
| dep/lib/libglfw3.a dep/lib/libGLEW.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/librtaudio.a dep/lib/librtmidi.a dep/lib/libcrypto.a dep/lib/libssl.a dep/lib/libcurl.a | |||||
| dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a | |||||
| TARGET := Rack | TARGET := Rack | ||||
| endif | endif | ||||
| ifdef ARCH_WIN | ifdef ARCH_WIN | ||||
| SOURCES += dep/osdialog/osdialog_win.c | SOURCES += dep/osdialog/osdialog_win.c | ||||
| LDFLAGS += -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | LDFLAGS += -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | ||||
| dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a \ | |||||
| dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a \ | |||||
| -lpthread -lopengl32 -lgdi32 -lws2_32 -lcomdlg32 -lole32 -ldsound -lwinmm -lksuser -lshlwapi -lmfplat -lmfuuid -lwmcodecdspuuid -ldbghelp | -lpthread -lopengl32 -lgdi32 -lws2_32 -lcomdlg32 -lole32 -ldsound -lwinmm -lksuser -lshlwapi -lmfplat -lmfuuid -lwmcodecdspuuid -ldbghelp | ||||
| TARGET := Rack.exe | TARGET := Rack.exe | ||||
| OBJECTS += Rack.res | OBJECTS += Rack.res | ||||
| @@ -10,39 +10,42 @@ ifdef ARCH_LIN | |||||
| glew = lib/libGLEW.a | glew = lib/libGLEW.a | ||||
| glfw = lib/libglfw3.a | glfw = lib/libglfw3.a | ||||
| jansson = lib/libjansson.a | jansson = lib/libjansson.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| openssl = lib/libssl.a | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| libzip = lib/libzip.a | libzip = lib/libzip.a | ||||
| zlib = lib/libz.a | zlib = lib/libz.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| libsamplerate = lib/libsamplerate.a | |||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| rtaudio = lib/librtaudio.a | rtaudio = lib/librtaudio.a | ||||
| openssl = lib/libssl.a | |||||
| endif | endif | ||||
| ifdef ARCH_MAC | ifdef ARCH_MAC | ||||
| glew = lib/libGLEW.a | glew = lib/libGLEW.a | ||||
| glfw = lib/libglfw3.a | glfw = lib/libglfw3.a | ||||
| jansson = lib/libjansson.a | jansson = lib/libjansson.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| openssl = lib/libssl.a | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| libzip = lib/libzip.a | libzip = lib/libzip.a | ||||
| zlib = lib/libz.a | zlib = lib/libz.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| libsamplerate = lib/libsamplerate.a | |||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| rtaudio = lib/librtaudio.a | rtaudio = lib/librtaudio.a | ||||
| openssl = lib/libssl.a | |||||
| endif | endif | ||||
| ifdef ARCH_WIN | ifdef ARCH_WIN | ||||
| glew = lib/libglew32.a | glew = lib/libglew32.a | ||||
| glfw = lib/libglfw3.a | glfw = lib/libglfw3.a | ||||
| jansson = lib/libjansson.a | jansson = lib/libjansson.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| openssl = lib/libssl.a | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| libzip = lib/libzip.a | libzip = lib/libzip.a | ||||
| zlib = lib/libz.a | zlib = lib/libz.a | ||||
| libspeexdsp = lib/libspeexdsp.a | |||||
| libsamplerate = lib/libsamplerate.a | |||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| rtaudio = lib/librtaudio.a | rtaudio = lib/librtaudio.a | ||||
| openssl = lib/libssl.a | |||||
| endif | endif | ||||
| nanovg = include/nanovg.h | nanovg = include/nanovg.h | ||||
| @@ -51,7 +54,20 @@ oui-blendish = include/blendish.h | |||||
| osdialog = include/osdialog.h | osdialog = include/osdialog.h | ||||
| pffft = include/pffft.h | pffft = include/pffft.h | ||||
| DEPS += $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) $(nanovg) $(nanosvg) $(oui-blendish) $(osdialog) $(pffft) | |||||
| DEPS += $(glew) | |||||
| DEPS += $(glfw) | |||||
| DEPS += $(jansson) | |||||
| DEPS += $(libcurl) | |||||
| DEPS += $(libzip) | |||||
| DEPS += $(libspeexdsp) | |||||
| DEPS += $(libsamplerate) | |||||
| DEPS += $(rtmidi) | |||||
| DEPS += $(rtaudio) | |||||
| DEPS += $(nanovg) | |||||
| DEPS += $(nanosvg) | |||||
| DEPS += $(oui-blendish) | |||||
| DEPS += $(osdialog) | |||||
| DEPS += $(pffft) | |||||
| DEP_LOCAL := . | DEP_LOCAL := . | ||||
| @@ -90,17 +106,6 @@ $(jansson): jansson-2.12 | |||||
| $(MAKE) -C jansson-2.12 | $(MAKE) -C jansson-2.12 | ||||
| $(MAKE) -C jansson-2.12 install | $(MAKE) -C jansson-2.12 install | ||||
| speexdsp-SpeexDSP-1.2rc3: | |||||
| $(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz" | |||||
| $(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd | |||||
| $(UNTAR) speexdsp-SpeexDSP-1.2rc3.tgz | |||||
| rm speexdsp-SpeexDSP-1.2rc3.tgz | |||||
| $(libspeexdsp): speexdsp-SpeexDSP-1.2rc3 | |||||
| cd speexdsp-SpeexDSP-1.2rc3 && $(CONFIGURE) | |||||
| $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 | |||||
| $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 install | |||||
| openssl-1.1.1b: | openssl-1.1.1b: | ||||
| $(WGET) "https://www.openssl.org/source/openssl-1.1.1b.tar.gz" | $(WGET) "https://www.openssl.org/source/openssl-1.1.1b.tar.gz" | ||||
| $(SHA256) openssl-1.1.1b.tar.gz 5c557b023230413dfb0756f3137a13e6d726838ccd1430888ad15bfb2b43ea4b | $(SHA256) openssl-1.1.1b.tar.gz 5c557b023230413dfb0756f3137a13e6d726838ccd1430888ad15bfb2b43ea4b | ||||
| @@ -157,6 +162,28 @@ else | |||||
| $(MAKE) -C zlib-1.2.11 install | $(MAKE) -C zlib-1.2.11 install | ||||
| endif | endif | ||||
| speexdsp-SpeexDSP-1.2rc3: | |||||
| $(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz" | |||||
| $(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd | |||||
| $(UNTAR) speexdsp-SpeexDSP-1.2rc3.tgz | |||||
| rm speexdsp-SpeexDSP-1.2rc3.tgz | |||||
| $(libspeexdsp): speexdsp-SpeexDSP-1.2rc3 | |||||
| cd speexdsp-SpeexDSP-1.2rc3 && $(CONFIGURE) | |||||
| $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 | |||||
| $(MAKE) -C speexdsp-SpeexDSP-1.2rc3 install | |||||
| libsamplerate-0.1.9: | |||||
| $(WGET) "http://www.mega-nerd.com/SRC/libsamplerate-0.1.9.tar.gz" | |||||
| $(SHA256) libsamplerate-0.1.9.tar.gz 0a7eb168e2f21353fb6d84da152e4512126f7dc48ccb0be80578c565413444c1 | |||||
| $(UNTAR) libsamplerate-0.1.9.tar.gz | |||||
| rm libsamplerate-0.1.9.tar.gz | |||||
| $(libsamplerate): libsamplerate-0.1.9 | |||||
| cd libsamplerate-0.1.9 && $(CONFIGURE) --disable-fftw --disable-sndfile | |||||
| $(MAKE) -C libsamplerate-0.1.9 | |||||
| $(MAKE) -C libsamplerate-0.1.9 install | |||||
| rtmidi-4.0.0: | rtmidi-4.0.0: | ||||
| $(WGET) "http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz" | $(WGET) "http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz" | ||||
| $(SHA256) rtmidi-4.0.0.tar.gz 370cfe710f43fbeba8d2b8c8bc310f314338c519c2cf2865e2d2737b251526cd | $(SHA256) rtmidi-4.0.0.tar.gz 370cfe710f43fbeba8d2b8c8bc310f314338c519c2cf2865e2d2737b251526cd | ||||
| @@ -100,10 +100,10 @@ include $(RACK_DIR)/plugin.mk | |||||
| using namespace rack; | using namespace rack; | ||||
| // Declare the Plugin, defined in plugin.cpp | // Declare the Plugin, defined in plugin.cpp | ||||
| extern Plugin *pluginInstance; | |||||
| extern Plugin* pluginInstance; | |||||
| // Declare each Model, defined in each module source file | // Declare each Model, defined in each module source file | ||||
| // extern Model *modelMyModule; | |||||
| // extern Model* modelMyModule; | |||||
| """ | """ | ||||
| with open(os.path.join(plugin_dir, "src/plugin.hpp"), "w") as f: | with open(os.path.join(plugin_dir, "src/plugin.hpp"), "w") as f: | ||||
| f.write(plugin_hpp) | f.write(plugin_hpp) | ||||
| @@ -112,10 +112,10 @@ extern Plugin *pluginInstance; | |||||
| plugin_cpp = """#include "plugin.hpp" | plugin_cpp = """#include "plugin.hpp" | ||||
| Plugin *pluginInstance; | |||||
| Plugin* pluginInstance; | |||||
| void init(Plugin *p) { | |||||
| void init(Plugin* p) { | |||||
| pluginInstance = p; | pluginInstance = p; | ||||
| // Add modules here | // Add modules here | ||||
| @@ -245,7 +245,7 @@ def create_module(slug, panel_filename=None, source_filename=None): | |||||
| # Tell user to add model to plugin.hpp and plugin.cpp | # Tell user to add model to plugin.hpp and plugin.cpp | ||||
| print(f""" | print(f""" | ||||
| To enable the module, add | To enable the module, add | ||||
| extern Model *model{identifier}; | |||||
| extern Model* model{identifier}; | |||||
| to plugin.hpp, and add | to plugin.hpp, and add | ||||
| p->addModel(model{identifier}); | p->addModel(model{identifier}); | ||||
| to the init() function in plugin.cpp.""") | to the init() function in plugin.cpp.""") | ||||
| @@ -396,7 +396,7 @@ struct {identifier} : Module {{""" | |||||
| source += """ | source += """ | ||||
| } | } | ||||
| void process(const ProcessArgs &args) override { | |||||
| void process(const ProcessArgs& args) override { | |||||
| } | } | ||||
| };""" | };""" | ||||
| @@ -404,7 +404,7 @@ struct {identifier} : Module {{""" | |||||
| struct {identifier}Widget : ModuleWidget {{ | struct {identifier}Widget : ModuleWidget {{ | ||||
| {identifier}Widget({identifier} *module) {{ | |||||
| {identifier}Widget({identifier}* module) {{ | |||||
| setModule(module); | setModule(module); | ||||
| setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/{slug}.svg"))); | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/{slug}.svg"))); | ||||
| @@ -475,7 +475,7 @@ struct {identifier}Widget : ModuleWidget {{ | |||||
| }}; | }}; | ||||
| Model *model{identifier} = createModel<{identifier}, {identifier}Widget>("{slug}");""" | |||||
| Model* model{identifier} = createModel<{identifier}, {identifier}Widget>("{slug}");""" | |||||
| return source | return source | ||||
| @@ -147,7 +147,7 @@ inline float crossfade(float a, float b, float p) { | |||||
| } | } | ||||
| /** Linearly interpolates an array `p` with index `x`. | /** Linearly interpolates an array `p` with index `x`. | ||||
| Assumes that the array at `p` is of length at least `floor(x) + 1`. | |||||
| The array at `p` must be at least length `floor(x) + 2`. | |||||
| */ | */ | ||||
| inline float interpolateLinear(const float* p, float x) { | inline float interpolateLinear(const float* p, float x) { | ||||
| int xi = x; | int xi = x; | ||||
| @@ -47,6 +47,11 @@ The launched process will continue running if the current process is closed. | |||||
| */ | */ | ||||
| void runProcessDetached(const std::string& path); | void runProcessDetached(const std::string& path); | ||||
| std::string getOperatingSystemInfo(); | std::string getOperatingSystemInfo(); | ||||
| /** Unzips a ZIP file to a folder. | |||||
| The folder must exist. | |||||
| Returns 0 if successful. | |||||
| */ | |||||
| int unzipToFolder(const std::string& zipPath, const std::string& dir); | |||||
| } // namespace system | } // namespace system | ||||
| @@ -320,12 +320,12 @@ struct ClearButton : ui::Button { | |||||
| struct BrowserSidebar : widget::Widget { | struct BrowserSidebar : widget::Widget { | ||||
| BrowserSearchField* searchField; | BrowserSearchField* searchField; | ||||
| ClearButton* clearButton; | ClearButton* clearButton; | ||||
| ui::Label* brandLabel; | |||||
| ui::List* brandList; | |||||
| ui::ScrollWidget* brandScroll; | |||||
| ui::Label* tagLabel; | ui::Label* tagLabel; | ||||
| ui::List* tagList; | ui::List* tagList; | ||||
| ui::ScrollWidget* tagScroll; | ui::ScrollWidget* tagScroll; | ||||
| ui::Label* brandLabel; | |||||
| ui::List* brandList; | |||||
| ui::ScrollWidget* brandScroll; | |||||
| BrowserSidebar() { | BrowserSidebar() { | ||||
| // Search | // Search | ||||
| @@ -337,7 +337,28 @@ struct BrowserSidebar : widget::Widget { | |||||
| clearButton->text = "Reset filters"; | clearButton->text = "Reset filters"; | ||||
| addChild(clearButton); | addChild(clearButton); | ||||
| // Bbrand label | |||||
| // Tag label | |||||
| tagLabel = new ui::Label; | |||||
| // tagLabel->fontSize = 16; | |||||
| tagLabel->color = nvgRGB(0x80, 0x80, 0x80); | |||||
| tagLabel->text = "Tags"; | |||||
| addChild(tagLabel); | |||||
| // Tag list | |||||
| tagScroll = new ui::ScrollWidget; | |||||
| addChild(tagScroll); | |||||
| tagList = new ui::List; | |||||
| tagScroll->container->addChild(tagList); | |||||
| for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) { | |||||
| TagItem* item = new TagItem; | |||||
| item->text = tag::tagAliases[tagId][0]; | |||||
| item->tagId = tagId; | |||||
| tagList->addChild(item); | |||||
| } | |||||
| // Brand label | |||||
| brandLabel = new ui::Label; | brandLabel = new ui::Label; | ||||
| // brandLabel->fontSize = 16; | // brandLabel->fontSize = 16; | ||||
| brandLabel->color = nvgRGB(0x80, 0x80, 0x80); | brandLabel->color = nvgRGB(0x80, 0x80, 0x80); | ||||
| @@ -362,27 +383,6 @@ struct BrowserSidebar : widget::Widget { | |||||
| item->text = brand; | item->text = brand; | ||||
| brandList->addChild(item); | brandList->addChild(item); | ||||
| } | } | ||||
| // Tag label | |||||
| tagLabel = new ui::Label; | |||||
| // tagLabel->fontSize = 16; | |||||
| tagLabel->color = nvgRGB(0x80, 0x80, 0x80); | |||||
| tagLabel->text = "Tags"; | |||||
| addChild(tagLabel); | |||||
| // Tag list | |||||
| tagScroll = new ui::ScrollWidget; | |||||
| addChild(tagScroll); | |||||
| tagList = new ui::List; | |||||
| tagScroll->container->addChild(tagList); | |||||
| for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) { | |||||
| TagItem* item = new TagItem; | |||||
| item->text = tag::tagAliases[tagId][0]; | |||||
| item->tagId = tagId; | |||||
| tagList->addChild(item); | |||||
| } | |||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -393,20 +393,20 @@ struct BrowserSidebar : widget::Widget { | |||||
| float listHeight = (box.size.y - clearButton->box.getBottom()) / 2; | float listHeight = (box.size.y - clearButton->box.getBottom()) / 2; | ||||
| listHeight = std::floor(listHeight); | listHeight = std::floor(listHeight); | ||||
| brandLabel->box.pos = clearButton->box.getBottomLeft(); | |||||
| brandLabel->box.size.x = box.size.x; | |||||
| brandScroll->box.pos = brandLabel->box.getBottomLeft(); | |||||
| brandScroll->box.size.y = listHeight - brandLabel->box.size.y; | |||||
| brandScroll->box.size.x = box.size.x; | |||||
| brandList->box.size.x = brandScroll->box.size.x; | |||||
| tagLabel->box.pos = brandScroll->box.getBottomLeft(); | |||||
| tagLabel->box.pos = clearButton->box.getBottomLeft(); | |||||
| tagLabel->box.size.x = box.size.x; | tagLabel->box.size.x = box.size.x; | ||||
| tagScroll->box.pos = tagLabel->box.getBottomLeft(); | tagScroll->box.pos = tagLabel->box.getBottomLeft(); | ||||
| tagScroll->box.size.y = listHeight - tagLabel->box.size.y; | tagScroll->box.size.y = listHeight - tagLabel->box.size.y; | ||||
| tagScroll->box.size.x = box.size.x; | tagScroll->box.size.x = box.size.x; | ||||
| tagList->box.size.x = tagScroll->box.size.x; | tagList->box.size.x = tagScroll->box.size.x; | ||||
| brandLabel->box.pos = tagScroll->box.getBottomLeft(); | |||||
| brandLabel->box.size.x = box.size.x; | |||||
| brandScroll->box.pos = brandLabel->box.getBottomLeft(); | |||||
| brandScroll->box.size.y = listHeight - brandLabel->box.size.y; | |||||
| brandScroll->box.size.x = box.size.x; | |||||
| brandList->box.size.x = brandScroll->box.size.x; | |||||
| Widget::step(); | Widget::step(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -615,7 +615,7 @@ inline void TagItem::step() { | |||||
| } | } | ||||
| inline void BrowserSearchField::onSelectKey(const event::SelectKey& e) { | inline void BrowserSearchField::onSelectKey(const event::SelectKey& e) { | ||||
| if (e.action == GLFW_PRESS) { | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||||
| switch (e.key) { | switch (e.key) { | ||||
| case GLFW_KEY_ESCAPE: { | case GLFW_KEY_ESCAPE: { | ||||
| BrowserOverlay* overlay = getAncestorOfType<BrowserOverlay>(); | BrowserOverlay* overlay = getAncestorOfType<BrowserOverlay>(); | ||||
| @@ -246,6 +246,7 @@ ModuleWidget::ModuleWidget() { | |||||
| } | } | ||||
| ModuleWidget::~ModuleWidget() { | ModuleWidget::~ModuleWidget() { | ||||
| clearChildren(); | |||||
| setModule(NULL); | setModule(NULL); | ||||
| } | } | ||||
| @@ -103,13 +103,20 @@ struct MIDI_Map : Module { | |||||
| if (!filterInitialized[id]) { | if (!filterInitialized[id]) { | ||||
| valueFilters[id].out = paramQuantity->getScaledValue(); | valueFilters[id].out = paramQuantity->getScaledValue(); | ||||
| filterInitialized[id] = true; | filterInitialized[id] = true; | ||||
| continue; | |||||
| } | } | ||||
| // Set param if value has been initialized | // Set param if value has been initialized | ||||
| if (values[cc] >= 0) { | |||||
| float v = values[cc] / 127.f; | |||||
| v = valueFilters[id].process(args.sampleTime * divider.getDivision(), v); | |||||
| paramQuantity->setScaledValue(v); | |||||
| float value = values[cc] / 127.f; | |||||
| // Detect behavior from MIDI buttons. | |||||
| if (std::fabs(valueFilters[id].out - value) >= 1.f) { | |||||
| // Jump value | |||||
| valueFilters[id].out = value; | |||||
| } | |||||
| else { | |||||
| // Smooth value with filter | |||||
| valueFilters[id].process(args.sampleTime * divider.getDivision(), value); | |||||
| } | } | ||||
| paramQuantity->setScaledValue(valueFilters[id].out); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -17,8 +17,6 @@ | |||||
| #include <map> | #include <map> | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #define ZIP_STATIC | |||||
| #include <zip.h> | |||||
| #include <jansson.h> | #include <jansson.h> | ||||
| #if defined ARCH_WIN | #if defined ARCH_WIN | ||||
| @@ -174,74 +172,6 @@ static void loadPlugins(std::string path) { | |||||
| } | } | ||||
| } | } | ||||
| /** Returns 0 if successful */ | |||||
| static int extractZipHandle(zip_t* za, std::string dir) { | |||||
| int err; | |||||
| for (int i = 0; i < zip_get_num_entries(za, 0); i++) { | |||||
| zip_stat_t zs; | |||||
| err = zip_stat_index(za, i, 0, &zs); | |||||
| if (err) { | |||||
| WARN("zip_stat_index() failed: error %d", err); | |||||
| return err; | |||||
| } | |||||
| std::string path = dir + "/" + zs.name; | |||||
| if (path[path.size() - 1] == '/') { | |||||
| system::createDirectory(path); | |||||
| // HACK | |||||
| // Create and delete file to update the directory's mtime. | |||||
| std::string tmpPath = path + "/.tmp"; | |||||
| FILE* tmpFile = fopen(tmpPath.c_str(), "w"); | |||||
| fclose(tmpFile); | |||||
| std::remove(tmpPath.c_str()); | |||||
| } | |||||
| else { | |||||
| zip_file_t* zf = zip_fopen_index(za, i, 0); | |||||
| if (!zf) { | |||||
| WARN("zip_fopen_index() failed"); | |||||
| return -1; | |||||
| } | |||||
| FILE* outFile = fopen(path.c_str(), "wb"); | |||||
| if (!outFile) | |||||
| continue; | |||||
| while (1) { | |||||
| char buffer[1 << 15]; | |||||
| int len = zip_fread(zf, buffer, sizeof(buffer)); | |||||
| if (len <= 0) | |||||
| break; | |||||
| fwrite(buffer, 1, len, outFile); | |||||
| } | |||||
| err = zip_fclose(zf); | |||||
| if (err) { | |||||
| WARN("zip_fclose() failed: error %d", err); | |||||
| return err; | |||||
| } | |||||
| fclose(outFile); | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| /** Returns 0 if successful */ | |||||
| static int extractZip(std::string filename, std::string path) { | |||||
| int err; | |||||
| zip_t* za = zip_open(filename.c_str(), 0, &err); | |||||
| if (!za) { | |||||
| WARN("Could not open zip %s: error %d", filename.c_str(), err); | |||||
| return err; | |||||
| } | |||||
| DEFER({ | |||||
| zip_close(za); | |||||
| }); | |||||
| err = extractZipHandle(za, path); | |||||
| return err; | |||||
| } | |||||
| static void extractPackages(std::string path) { | static void extractPackages(std::string path) { | ||||
| std::string message; | std::string message; | ||||
| @@ -250,7 +180,7 @@ static void extractPackages(std::string path) { | |||||
| continue; | continue; | ||||
| INFO("Extracting package %s", packagePath.c_str()); | INFO("Extracting package %s", packagePath.c_str()); | ||||
| // Extract package | // Extract package | ||||
| if (extractZip(packagePath, path)) { | |||||
| if (system::unzipToFolder(packagePath, path)) { | |||||
| WARN("Package %s failed to extract", packagePath.c_str()); | WARN("Package %s failed to extract", packagePath.c_str()); | ||||
| message += string::f("Could not extract package %s\n", packagePath.c_str()); | message += string::f("Could not extract package %s\n", packagePath.c_str()); | ||||
| continue; | continue; | ||||
| @@ -289,7 +219,7 @@ void init() { | |||||
| std::string fundamentalDir = asset::pluginsPath + "/Fundamental"; | std::string fundamentalDir = asset::pluginsPath + "/Fundamental"; | ||||
| if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) { | if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) { | ||||
| INFO("Extracting bundled Fundamental package"); | INFO("Extracting bundled Fundamental package"); | ||||
| extractZip(fundamentalSrc.c_str(), asset::pluginsPath.c_str()); | |||||
| system::unzipToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str()); | |||||
| loadPlugin(fundamentalDir); | loadPlugin(fundamentalDir); | ||||
| } | } | ||||
| @@ -27,6 +27,9 @@ | |||||
| #include <dbghelp.h> | #include <dbghelp.h> | ||||
| #endif | #endif | ||||
| #define ZIP_STATIC | |||||
| #include <zip.h> | |||||
| namespace rack { | namespace rack { | ||||
| namespace system { | namespace system { | ||||
| @@ -324,5 +327,73 @@ std::string getOperatingSystemInfo() { | |||||
| } | } | ||||
| int unzipToFolder(const std::string& zipPath, const std::string& dir) { | |||||
| int err; | |||||
| // Open ZIP file | |||||
| zip_t* za = zip_open(zipPath.c_str(), 0, &err); | |||||
| if (!za) { | |||||
| WARN("Could not open ZIP file %s: error %d", zipPath.c_str(), err); | |||||
| return err; | |||||
| } | |||||
| DEFER({ | |||||
| zip_close(za); | |||||
| }); | |||||
| // Iterate ZIP entries | |||||
| for (int i = 0; i < zip_get_num_entries(za, 0); i++) { | |||||
| zip_stat_t zs; | |||||
| err = zip_stat_index(za, i, 0, &zs); | |||||
| if (err) { | |||||
| WARN("zip_stat_index() failed: error %d", err); | |||||
| return err; | |||||
| } | |||||
| std::string path = dir + "/" + zs.name; | |||||
| if (path[path.size() - 1] == '/') { | |||||
| // Create directory | |||||
| system::createDirectory(path); | |||||
| // HACK | |||||
| // Create and delete file to update the directory's mtime. | |||||
| std::string tmpPath = path + "/.tmp"; | |||||
| FILE* tmpFile = fopen(tmpPath.c_str(), "w"); | |||||
| fclose(tmpFile); | |||||
| std::remove(tmpPath.c_str()); | |||||
| } | |||||
| else { | |||||
| // Open ZIP entry | |||||
| zip_file_t* zf = zip_fopen_index(za, i, 0); | |||||
| if (!zf) { | |||||
| WARN("zip_fopen_index() failed"); | |||||
| return -1; | |||||
| } | |||||
| DEFER({ | |||||
| zip_fclose(zf); | |||||
| }); | |||||
| // Create file | |||||
| FILE* outFile = fopen(path.c_str(), "wb"); | |||||
| if (!outFile) { | |||||
| WARN("Could not create file %s", path.c_str()); | |||||
| return -1; | |||||
| } | |||||
| DEFER({ | |||||
| fclose(outFile); | |||||
| }); | |||||
| // Read buffer and copy to file | |||||
| while (true) { | |||||
| char buffer[1 << 15]; | |||||
| int len = zip_fread(zf, buffer, sizeof(buffer)); | |||||
| if (len <= 0) | |||||
| break; | |||||
| fwrite(buffer, 1, len, outFile); | |||||
| } | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| } // namespace system | } // namespace system | ||||
| } // namespace rack | } // namespace rack | ||||
| @@ -8,30 +8,31 @@ namespace tag { | |||||
| const std::vector<std::vector<std::string>> tagAliases = { | const std::vector<std::vector<std::string>> tagAliases = { | ||||
| {"Arpeggiator"}, | |||||
| {"Attenuator"}, // With a level knob and not much else. | |||||
| {"Blank"}, // No parameters or ports. Serves no purpose except visual. | |||||
| {"Arpeggiator"}, // With a level knob and not much else. | |||||
| {"Attenuator"}, // No parameters or ports. Serves no purpose except visual. | |||||
| {"Blank"}, | |||||
| {"Chorus"}, | {"Chorus"}, | ||||
| {"Clock generator", "Clock"}, | |||||
| {"Clock modulator"}, // Clock dividers, multipliers, etc. | |||||
| {"Compressor"}, // With threshold, ratio, knee, etc parameters. | |||||
| {"Controller"}, // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad. | |||||
| {"Clock generator", "Clock"}, // Clock dividers, multipliers, etc. | |||||
| {"Clock modulator"}, // With threshold, ratio, knee, etc parameters. | |||||
| {"Compressor"}, // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad. | |||||
| {"Controller"}, | |||||
| {"Delay"}, | {"Delay"}, | ||||
| {"Digital"}, | {"Digital"}, | ||||
| {"Distortion"}, | {"Distortion"}, | ||||
| {"Drum", "Drums", "Percussion"}, | |||||
| {"Dual"}, // 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. | |||||
| {"Drum", "Drums", "Percussion"}, // 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. | |||||
| {"Dual"}, | |||||
| {"Dynamics"}, | {"Dynamics"}, | ||||
| {"Effect"}, | {"Effect"}, | ||||
| {"Envelope follower"}, | {"Envelope follower"}, | ||||
| {"Envelope generator"}, | {"Envelope generator"}, | ||||
| {"Equalizer", "EQ"}, | |||||
| {"Expander"}, // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module. | |||||
| {"Equalizer", "EQ"}, // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module. | |||||
| {"Expander"}, | |||||
| {"External"}, | {"External"}, | ||||
| {"Filter", "VCF", "Voltage controlled filter"}, | {"Filter", "VCF", "Voltage controlled filter"}, | ||||
| {"Flanger"}, | {"Flanger"}, | ||||
| {"Function generator"}, | {"Function generator"}, | ||||
| {"Granular"}, | {"Granular"}, | ||||
| {"Hardware clone", "Hardware"}, // Clones the functionality *and* appearance of a real-world hardware module. | |||||
| {"Limiter"}, | {"Limiter"}, | ||||
| {"Logic"}, | {"Logic"}, | ||||
| {"Low-frequency oscillator", "LFO", "Low frequency oscillator"}, | {"Low-frequency oscillator", "LFO", "Low frequency oscillator"}, | ||||
| @@ -44,8 +45,8 @@ const std::vector<std::vector<std::string>> tagAliases = { | |||||
| {"Panning", "Pan"}, | {"Panning", "Pan"}, | ||||
| {"Phaser"}, | {"Phaser"}, | ||||
| {"Physical modeling"}, | {"Physical modeling"}, | ||||
| {"Polyphonic", "Poly"}, | |||||
| {"Quad"}, // 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. | |||||
| {"Polyphonic", "Poly"}, // 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. | |||||
| {"Quad"}, | |||||
| {"Quantizer"}, | {"Quantizer"}, | ||||
| {"Random"}, | {"Random"}, | ||||
| {"Recording"}, | {"Recording"}, | ||||
| @@ -55,10 +56,10 @@ const std::vector<std::vector<std::string>> tagAliases = { | |||||
| {"Sampler"}, | {"Sampler"}, | ||||
| {"Sequencer"}, | {"Sequencer"}, | ||||
| {"Slew limiter"}, | {"Slew limiter"}, | ||||
| {"Switch"}, | |||||
| {"Synth voice"}, // A synth voice must have, at the minimum, a built-in oscillator and envelope. | |||||
| {"Tuner"}, | |||||
| {"Utility"}, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc. | |||||
| {"Switch"}, // A synth voice must have, at the minimum, a built-in oscillator and envelope. | |||||
| {"Synth voice"}, | |||||
| {"Tuner"}, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc. | |||||
| {"Utility"}, | |||||
| {"Visual"}, | {"Visual"}, | ||||
| {"Vocoder"}, | {"Vocoder"}, | ||||
| {"Voltage-controlled amplifier", "Amplifier", "VCA", "Voltage controlled amplifier"}, | {"Voltage-controlled amplifier", "Amplifier", "VCA", "Voltage controlled amplifier"}, | ||||
| @@ -61,16 +61,23 @@ void update() { | |||||
| if (downloadUrl == "") | if (downloadUrl == "") | ||||
| return; | return; | ||||
| #if defined ARCH_WIN | |||||
| // Download and launch the installer on Windows | |||||
| #if defined ARCH_WIN || defined ARCH_MAC | |||||
| // Download update | |||||
| std::string filename = string::filename(network::urlPath(downloadUrl)); | std::string filename = string::filename(network::urlPath(downloadUrl)); | ||||
| std::string path = asset::user(filename); | std::string path = asset::user(filename); | ||||
| INFO("Download update %s to %s", downloadUrl.c_str(), path.c_str()); | INFO("Download update %s to %s", downloadUrl.c_str(), path.c_str()); | ||||
| network::requestDownload(downloadUrl, path, &progress); | network::requestDownload(downloadUrl, path, &progress); | ||||
| #endif | |||||
| #if defined ARCH_WIN | |||||
| // Launch the installer | |||||
| INFO("Launching update %s", path.c_str()); | INFO("Launching update %s", path.c_str()); | ||||
| system::runProcessDetached(path); | system::runProcessDetached(path); | ||||
| #elif defined ARCH_MAC | |||||
| // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly. | |||||
| std::string cmd = "open \"" + path + "\""; | |||||
| std::system(cmd.c_str()); | |||||
| #else | #else | ||||
| // Open the browser on Mac and Linux. The user will know what to do. | |||||
| system::openBrowser(downloadUrl); | system::openBrowser(downloadUrl); | ||||
| #endif | #endif | ||||