| @@ -1,5 +1,6 @@ | |||||
| /Rack | /Rack | ||||
| /Rack.exe | /Rack.exe | ||||
| /libRack.a | |||||
| /autosave.json | /autosave.json | ||||
| /settings.json | /settings.json | ||||
| /plugins | /plugins | ||||
| @@ -10,3 +10,9 @@ | |||||
| [submodule "ext/oui-blendish"] | [submodule "ext/oui-blendish"] | ||||
| path = ext/oui-blendish | path = ext/oui-blendish | ||||
| url = https://github.com/AndrewBelt/oui-blendish.git | 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 | |||||
| @@ -1,6 +1,15 @@ | |||||
| Tip: Use `git checkout v0.4.0` for example to check out any previous version mentioned here. | 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) | ### v0.5.0 (2017-11-21) | ||||
| - Added zoom scaling from 25% to 200% | - 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) | ### v0.4.0 (2017-10-13) | ||||
| - Cables can now stack on output ports | - Cables can now stack on output ports | ||||
| - Added sub-menus for each plugin, includes optional plugin metadata like URLs | - Added sub-menus for each plugin, includes optional plugin metadata like URLs | ||||
| - Added MIDI CC-to-CV Interface, updated MIDI-to-CV Interface | - 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) | ### v0.3.2 (2017-09-25) | ||||
| - Added key commands | - Added key commands | ||||
| - Fixed "invisible knobs/ports" rendering bug for ~2010 Macs | - Fixed "invisible knobs/ports" rendering bug for ~2010 Macs | ||||
| - Added "allowCursorLock" to settings.json (set to "false" for touch screen support) | - 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) | ### v0.3.0 (2017-09-10) | ||||
| - Knobcon public Beta release | - Knobcon public Beta release | ||||
| @@ -14,7 +14,7 @@ ifeq ($(ARCH), lin) | |||||
| LDFLAGS += -rdynamic \ | LDFLAGS += -rdynamic \ | ||||
| -lpthread -lGL -ldl \ | -lpthread -lGL -ldl \ | ||||
| $(shell pkg-config --libs gtk+-2.0) \ | $(shell pkg-config --libs gtk+-2.0) \ | ||||
| -Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lrtaudio -lrtmidi -lcrypto -lssl | |||||
| -Ldep/lib -lGLEW -lglfw -ljansson -lspeexdsp -lcurl -lzip -lrtaudio -lrtmidi -lcrypto -lssl -lossia | |||||
| TARGET = Rack | TARGET = Rack | ||||
| endif | endif | ||||
| @@ -23,7 +23,7 @@ ifeq ($(ARCH), mac) | |||||
| CXXFLAGS += -DAPPLE -stdlib=libc++ | CXXFLAGS += -DAPPLE -stdlib=libc++ | ||||
| LDFLAGS += -stdlib=libc++ -lpthread -ldl \ | LDFLAGS += -stdlib=libc++ -lpthread -ldl \ | ||||
| -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo \ | -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo \ | ||||
| -Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lrtaudio -lrtmidi -lcrypto | |||||
| -Ldep/lib -lGLEW -lglfw -ljansson -lspeexdsp -lcurl -lzip -lrtaudio -lrtmidi -lcrypto -lssl -lossia | |||||
| TARGET = Rack | TARGET = Rack | ||||
| BUNDLE = dist/$(TARGET).app | BUNDLE = dist/$(TARGET).app | ||||
| endif | endif | ||||
| @@ -33,8 +33,8 @@ ifeq ($(ARCH), win) | |||||
| LDFLAGS += -static-libgcc -static-libstdc++ -lpthread \ | LDFLAGS += -static-libgcc -static-libstdc++ -lpthread \ | ||||
| -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | ||||
| -lgdi32 -lopengl32 -lcomdlg32 -lole32 \ | -lgdi32 -lopengl32 -lcomdlg32 -lole32 \ | ||||
| -Ldep/lib -lglew32 -lglfw3dll -lcurl -lzip -lrtaudio -lrtmidi \ | |||||
| -Wl,-Bstatic -ljansson -lsamplerate -lcrypto | |||||
| -Ldep/lib -lglew32 -lglfw3dll -lcurl -lzip -lrtaudio -lrtmidi -lcrypto -lssl \ | |||||
| -Wl,-Bstatic -ljansson -lspeexdsp -lossia | |||||
| TARGET = Rack.exe | TARGET = Rack.exe | ||||
| OBJECTS = Rack.res | OBJECTS = Rack.res | ||||
| endif | endif | ||||
| @@ -69,8 +69,14 @@ ifeq ($(ARCH), win) | |||||
| env PATH=dep/bin:/mingw64/bin gdb -ex run ./Rack | env PATH=dep/bin:/mingw64/bin gdb -ex run ./Rack | ||||
| endif | endif | ||||
| perf: $(TARGET) | |||||
| ifeq ($(ARCH), lin) | |||||
| LD_LIBRARY_PATH=dep/lib perf record --call-graph dwarf ./Rack | |||||
| endif | |||||
| clean: | clean: | ||||
| rm -fv libRack.a | |||||
| rm -rfv $(TARGET) build dist | rm -rfv $(TARGET) build dist | ||||
| # For Windows resources | # For Windows resources | ||||
| @@ -103,7 +109,7 @@ ifeq ($(ARCH), mac) | |||||
| cp dep/lib/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/ | ||||
| cp dep/lib/libglfw.3.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/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/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ | ||||
| cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ | ||||
| cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ | ||||
| @@ -114,7 +120,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 /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 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/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/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/libzip.5.dylib @executable_path/libzip.5.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 | ||||
| @@ -144,7 +150,7 @@ ifeq ($(ARCH), win) | |||||
| cp dep/bin/libcurl-4.dll dist/Rack/ | cp dep/bin/libcurl-4.dll dist/Rack/ | ||||
| cp dep/bin/libjansson-4.dll dist/Rack/ | cp dep/bin/libjansson-4.dll dist/Rack/ | ||||
| cp dep/bin/librtmidi-4.dll dist/Rack/ | cp dep/bin/librtmidi-4.dll dist/Rack/ | ||||
| cp dep/bin/libsamplerate-0.dll dist/Rack/ | |||||
| cp dep/bin/libspeexdsp-1.dll dist/Rack/ | |||||
| cp dep/bin/libzip-5.dll dist/Rack/ | cp dep/bin/libzip-5.dll dist/Rack/ | ||||
| cp dep/bin/librtaudio.dll dist/Rack/ | cp dep/bin/librtaudio.dll dist/Rack/ | ||||
| cp dep/bin/libcrypto-1_1-x64.dll dist/Rack/ | cp dep/bin/libcrypto-1_1-x64.dll dist/Rack/ | ||||
| @@ -161,7 +167,7 @@ ifeq ($(ARCH), lin) | |||||
| mkdir -p dist/Rack | mkdir -p dist/Rack | ||||
| cp -R LICENSE* res dist/Rack/ | cp -R LICENSE* res dist/Rack/ | ||||
| cp Rack Rack.sh 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/libjansson.so.4 dist/Rack/ | ||||
| cp dep/lib/libGLEW.so.2.1 dist/Rack/ | cp dep/lib/libGLEW.so.2.1 dist/Rack/ | ||||
| cp dep/lib/libglfw.so.3 dist/Rack/ | cp dep/lib/libglfw.so.3 dist/Rack/ | ||||
| @@ -51,6 +51,8 @@ You may use make's `-j$(nproc)` flag to parallelize builds across all your CPU c | |||||
| make dep | 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. | You should see a message that all dependencies built successfully. | ||||
| Build Rack. | Build Rack. | ||||
| @@ -1,5 +1,5 @@ | |||||
| ifdef VERSION | ifdef VERSION | ||||
| FLAGS += -DVERSION=$(VERSION) | |||||
| FLAGS += -DVERSION=$(VERSION) | |||||
| endif | endif | ||||
| # Generate dependency files alongside the object files | # Generate dependency files alongside the object files | ||||
| @@ -9,9 +9,9 @@ FLAGS += -g | |||||
| FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only | FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only | ||||
| FLAGS += -Wall -Wextra -Wno-unused-parameter | FLAGS += -Wall -Wextra -Wno-unused-parameter | ||||
| ifneq ($(ARCH), mac) | ifneq ($(ARCH), mac) | ||||
| CXXFLAGS += -Wsuggest-override | |||||
| CXXFLAGS += -Wsuggest-override | |||||
| endif | endif | ||||
| CXXFLAGS += -std=c++11 | |||||
| CXXFLAGS += -std=c++14 | |||||
| ifeq ($(ARCH), lin) | ifeq ($(ARCH), lin) | ||||
| @@ -32,6 +32,9 @@ ifeq ($(ARCH), win) | |||||
| FLAGS += -D_USE_MATH_DEFINES | FLAGS += -D_USE_MATH_DEFINES | ||||
| endif | endif | ||||
| CFLAGS += $(FLAGS) | |||||
| CXXFLAGS += $(FLAGS) | |||||
| OBJECTS += $(patsubst %, build/%.o, $(SOURCES)) | OBJECTS += $(patsubst %, build/%.o, $(SOURCES)) | ||||
| DEPS = $(patsubst %, build/%.d, $(SOURCES)) | DEPS = $(patsubst %, build/%.d, $(SOURCES)) | ||||
| @@ -47,16 +50,16 @@ $(TARGET): $(OBJECTS) | |||||
| build/%.c.o: %.c | build/%.c.o: %.c | ||||
| @mkdir -p $(@D) | @mkdir -p $(@D) | ||||
| $(CC) $(FLAGS) $(CFLAGS) -c -o $@ $< | |||||
| $(CC) $(CFLAGS) -c -o $@ $< | |||||
| build/%.cpp.o: %.cpp | build/%.cpp.o: %.cpp | ||||
| @mkdir -p $(@D) | @mkdir -p $(@D) | ||||
| $(CXX) $(FLAGS) $(CXXFLAGS) -c -o $@ $< | |||||
| $(CXX) $(CXXFLAGS) -c -o $@ $< | |||||
| build/%.cc.o: %.cc | build/%.cc.o: %.cc | ||||
| @mkdir -p $(@D) | @mkdir -p $(@D) | ||||
| $(CXX) $(FLAGS) $(CXXFLAGS) -c -o $@ $< | |||||
| $(CXX) $(CXXFLAGS) -c -o $@ $< | |||||
| build/%.m.o: %.m | build/%.m.o: %.m | ||||
| @mkdir -p $(@D) | @mkdir -p $(@D) | ||||
| $(CC) $(FLAGS) $(CFLAGS) -c -o $@ $< | |||||
| $(CC) $(CFLAGS) -c -o $@ $< | |||||
| @@ -26,36 +26,39 @@ ifeq ($(ARCH),lin) | |||||
| glew = lib/libGLEW.so | glew = lib/libGLEW.so | ||||
| glfw = lib/libglfw.so | glfw = lib/libglfw.so | ||||
| jansson = lib/libjansson.so | jansson = lib/libjansson.so | ||||
| libsamplerate = lib/libsamplerate.so | |||||
| libspeexdsp = lib/libspeexdsp.so | |||||
| libcurl = lib/libcurl.so | libcurl = lib/libcurl.so | ||||
| libzip = lib/libzip.so | libzip = lib/libzip.so | ||||
| rtmidi = lib/librtmidi.so | rtmidi = lib/librtmidi.so | ||||
| rtaudio = lib/librtaudio.so | rtaudio = lib/librtaudio.so | ||||
| openssl = lib/libssl.so | openssl = lib/libssl.so | ||||
| ossia = lib/libossia.so | |||||
| endif | endif | ||||
| ifeq ($(ARCH),mac) | ifeq ($(ARCH),mac) | ||||
| glew = lib/libGLEW.dylib | glew = lib/libGLEW.dylib | ||||
| glfw = lib/libglfw.dylib | glfw = lib/libglfw.dylib | ||||
| jansson = lib/libjansson.dylib | jansson = lib/libjansson.dylib | ||||
| libsamplerate = lib/libsamplerate.dylib | |||||
| libspeexdsp = lib/libspeexdsp.dylib | |||||
| libcurl = lib/libcurl.dylib | libcurl = lib/libcurl.dylib | ||||
| libzip = lib/libzip.dylib | libzip = lib/libzip.dylib | ||||
| rtmidi = lib/librtmidi.dylib | rtmidi = lib/librtmidi.dylib | ||||
| rtaudio = lib/librtaudio.dylib | rtaudio = lib/librtaudio.dylib | ||||
| openssl = lib/libssl.dylib | openssl = lib/libssl.dylib | ||||
| ossia = lib/libossia.so | |||||
| endif | endif | ||||
| ifeq ($(ARCH),win) | ifeq ($(ARCH),win) | ||||
| glew = bin/glew32.dll | glew = bin/glew32.dll | ||||
| glfw = bin/glfw3.dll | glfw = bin/glfw3.dll | ||||
| jansson = bin/libjansson-4.dll | jansson = bin/libjansson-4.dll | ||||
| libsamplerate = bin/libsamplerate-0.dll | |||||
| libspeexdsp = bin/libspeexdsp.dll | |||||
| libcurl = bin/libcurl-4.dll | libcurl = bin/libcurl-4.dll | ||||
| libzip = bin/libzip-5.dll | libzip = bin/libzip-5.dll | ||||
| rtmidi = bin/librtmidi-4.dll | rtmidi = bin/librtmidi-4.dll | ||||
| rtaudio = bin/librtaudio.dll | rtaudio = bin/librtaudio.dll | ||||
| openssl = bin/libssl-1_1-x64.dll | openssl = bin/libssl-1_1-x64.dll | ||||
| ossia = bin/ossia.dll | |||||
| endif | endif | ||||
| # Library configuration | # Library configuration | ||||
| @@ -74,7 +77,7 @@ endif | |||||
| .NOTPARALLEL: | .NOTPARALLEL: | ||||
| all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) | |||||
| all: $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) $(ossia) | |||||
| @echo "" | @echo "" | ||||
| @echo "#######################################" | @echo "#######################################" | ||||
| @echo "# Built all dependencies successfully #" | @echo "# Built all dependencies successfully #" | ||||
| @@ -89,17 +92,11 @@ $(glew): | |||||
| $(MAKE) -C glew-2.1.0 GLEW_DEST="$(LOCAL)" LIBDIR="$(LOCAL)/lib" install | $(MAKE) -C glew-2.1.0 GLEW_DEST="$(LOCAL)" LIBDIR="$(LOCAL)/lib" install | ||||
| $(glfw): | $(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) . \ | |||||
| cd glfw && $(CMAKE) . \ | |||||
| -DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DBUILD_SHARED_LIBS=ON \ | -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 | |||||
| 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 | |||||
| -DGLFW_COCOA_CHDIR_RESOURCES=OFF -DGLFW_COCOA_MENUBAR=ON -DGLFW_COCOA_RETINA_FRAMEBUFFER=ON | |||||
| $(MAKE) -C glfw | |||||
| $(MAKE) -C glfw install | |||||
| $(jansson): | $(jansson): | ||||
| $(WGET) http://www.digip.org/jansson/releases/jansson-2.10.tar.gz | $(WGET) http://www.digip.org/jansson/releases/jansson-2.10.tar.gz | ||||
| @@ -108,12 +105,12 @@ $(jansson): | |||||
| $(MAKE) -C jansson-2.10 | $(MAKE) -C jansson-2.10 | ||||
| $(MAKE) -C jansson-2.10 install | $(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 | |||||
| $(openssl): | $(openssl): | ||||
| $(WGET) https://www.openssl.org/source/openssl-1.1.0g.tar.gz | $(WGET) https://www.openssl.org/source/openssl-1.1.0g.tar.gz | ||||
| @@ -151,18 +148,25 @@ $(libzip): | |||||
| $(MAKE) -C libzip-1.2.0 install | $(MAKE) -C libzip-1.2.0 install | ||||
| $(rtmidi): | $(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): | $(rtaudio): | ||||
| git clone https://github.com/thestk/rtaudio.git | |||||
| cd rtaudio && mkdir -p cmakebuild | cd rtaudio && mkdir -p cmakebuild | ||||
| cd rtaudio/cmakebuild && cmake -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX="$(LOCAL)" $(RTAUDIO_FLAGS) .. | cd rtaudio/cmakebuild && cmake -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX="$(LOCAL)" $(RTAUDIO_FLAGS) .. | ||||
| $(MAKE) -C rtaudio/cmakebuild | $(MAKE) -C rtaudio/cmakebuild | ||||
| $(MAKE) -C rtaudio/cmakebuild install | $(MAKE) -C rtaudio/cmakebuild install | ||||
| $(ossia): | |||||
| git clone https://github.com/OSSIA/libossia --depth=1 | |||||
| mkdir -p build-libossia && cd build-libossia && $(CMAKE) \ | |||||
| -DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DOSSIA_PD=OFF \ | |||||
| -DOSSIA_COTIRE=OFF -DOSSIA_PROTOCOL_MIDI=OFF \ | |||||
| -DOSSIA_EDITOR=OFF -DBUILD_TYPE=Release ../libossia && \ | |||||
| $(MAKE) -j4 && \ | |||||
| $(MAKE) install | |||||
| clean: | clean: | ||||
| git clean -ffdxi | |||||
| git clean -ffdx | |||||
| @@ -0,0 +1 @@ | |||||
| Subproject commit 682f1cf203707f21c2eed4fa3f89c23c52accc49 | |||||
| @@ -0,0 +1 @@ | |||||
| Subproject commit ce13dfbf30fd1ab4e7f7eff8886a80f144c75e5d | |||||
| @@ -20,10 +20,9 @@ struct SVGPanel; | |||||
| // module | // 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_WIDTH 15 | ||||
| #define RACK_GRID_HEIGHT 380 | #define RACK_GRID_HEIGHT 380 | ||||
| static const Vec RACK_GRID_SIZE = Vec(15, 380); | static const Vec RACK_GRID_SIZE = Vec(15, 380); | ||||
| @@ -48,6 +47,8 @@ struct ModuleWidget : OpaqueWidget { | |||||
| virtual json_t *toJson(); | virtual json_t *toJson(); | ||||
| virtual void fromJson(json_t *rootJ); | virtual void fromJson(json_t *rootJ); | ||||
| virtual void create(); | |||||
| virtual void _delete(); | |||||
| /** Disconnects cables from all ports | /** Disconnects cables from all ports | ||||
| Called when the user clicks Disconnect Cables in the context menu. | Called when the user clicks Disconnect Cables in the context menu. | ||||
| */ | */ | ||||
| @@ -75,14 +76,11 @@ struct ModuleWidget : OpaqueWidget { | |||||
| void onDragMove(EventDragMove &e) override; | void onDragMove(EventDragMove &e) override; | ||||
| }; | }; | ||||
| struct ValueLight; | |||||
| struct WireWidget : OpaqueWidget { | struct WireWidget : OpaqueWidget { | ||||
| Port *outputPort = NULL; | Port *outputPort = NULL; | ||||
| Port *inputPort = NULL; | Port *inputPort = NULL; | ||||
| Port *hoveredOutputPort = NULL; | Port *hoveredOutputPort = NULL; | ||||
| Port *hoveredInputPort = NULL; | Port *hoveredInputPort = NULL; | ||||
| ValueLight *inputLight; | |||||
| ValueLight *outputLight; | |||||
| Wire *wire = NULL; | Wire *wire = NULL; | ||||
| NVGcolor color; | NVGcolor color; | ||||
| @@ -199,6 +197,8 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { | |||||
| struct Knob : ParamWidget { | struct Knob : ParamWidget { | ||||
| /** Snap to nearest integer while dragging */ | /** Snap to nearest integer while dragging */ | ||||
| bool snap = false; | bool snap = false; | ||||
| /** Multiplier for mouse movement to adjust knob value */ | |||||
| float speed = 1.0; | |||||
| float dragValue; | float dragValue; | ||||
| void onDragStart(EventDragStart &e) override; | void onDragStart(EventDragStart &e) override; | ||||
| void onDragMove(EventDragMove &e) override; | void onDragMove(EventDragMove &e) override; | ||||
| @@ -226,14 +226,14 @@ struct SVGKnob : virtual Knob, FramebufferWidget { | |||||
| void onChange(EventChange &e) override; | void onChange(EventChange &e) override; | ||||
| }; | }; | ||||
| struct SVGSlider : Knob, FramebufferWidget { | |||||
| struct SVGFader : Knob, FramebufferWidget { | |||||
| /** Intermediate positions will be interpolated between these positions */ | /** Intermediate positions will be interpolated between these positions */ | ||||
| Vec minHandlePos, maxHandlePos; | Vec minHandlePos, maxHandlePos; | ||||
| /** Not owned */ | /** Not owned */ | ||||
| SVGWidget *background; | SVGWidget *background; | ||||
| SVGWidget *handle; | SVGWidget *handle; | ||||
| SVGSlider(); | |||||
| SVGFader(); | |||||
| void step() override; | void step() override; | ||||
| void onChange(EventChange &e) override; | void onChange(EventChange &e) override; | ||||
| }; | }; | ||||
| @@ -249,7 +249,6 @@ struct SVGSwitch : virtual Switch, FramebufferWidget { | |||||
| SVGSwitch(); | SVGSwitch(); | ||||
| /** Adds an SVG file to represent the next switch position */ | /** Adds an SVG file to represent the next switch position */ | ||||
| void addFrame(std::shared_ptr<SVG> svg); | void addFrame(std::shared_ptr<SVG> svg); | ||||
| void step() override; | |||||
| void onChange(EventChange &e) override; | void onChange(EventChange &e) override; | ||||
| }; | }; | ||||
| @@ -271,6 +270,8 @@ struct MomentarySwitch : virtual Switch { | |||||
| void randomize() override {} | void randomize() override {} | ||||
| void onDragStart(EventDragStart &e) override { | void onDragStart(EventDragStart &e) override { | ||||
| setValue(maxValue); | setValue(maxValue); | ||||
| EventAction eAction; | |||||
| onAction(eAction); | |||||
| } | } | ||||
| void onDragEnd(EventDragEnd &e) override { | void onDragEnd(EventDragEnd &e) override { | ||||
| setValue(minValue); | setValue(minValue); | ||||
| @@ -285,6 +286,8 @@ struct LightWidget : TransparentWidget { | |||||
| NVGcolor bgColor = nvgRGBf(0, 0, 0); | NVGcolor bgColor = nvgRGBf(0, 0, 0); | ||||
| NVGcolor color = nvgRGBf(1, 1, 1); | NVGcolor color = nvgRGBf(1, 1, 1); | ||||
| void draw(NVGcontext *vg) override; | 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 */ | /** Mixes a list of colors based on a list of brightness values */ | ||||
| @@ -322,7 +322,7 @@ struct BefacoTinyKnob : SVGKnob { | |||||
| } | } | ||||
| }; | }; | ||||
| struct BefacoSlidePot : SVGSlider { | |||||
| struct BefacoSlidePot : SVGFader { | |||||
| BefacoSlidePot() { | BefacoSlidePot() { | ||||
| Vec margin = Vec(3.5, 3.5); | Vec margin = Vec(3.5, 3.5); | ||||
| maxHandlePos = Vec(-1, -2).plus(margin); | maxHandlePos = Vec(-1, -2).plus(margin); | ||||
| @@ -51,6 +51,11 @@ struct SlewLimiter { | |||||
| float rise = 1.0; | float rise = 1.0; | ||||
| float fall = 1.0; | float fall = 1.0; | ||||
| float out = 0.0; | float out = 0.0; | ||||
| void setRiseFall(float _rise, float _fall) { | |||||
| rise = _rise; | |||||
| fall = _fall; | |||||
| } | |||||
| float process(float in) { | float process(float in) { | ||||
| float delta = clampf(in - out, -fall, rise); | float delta = clampf(in - out, -fall, rise); | ||||
| out += delta; | out += delta; | ||||
| @@ -1,7 +1,8 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <samplerate.h> | |||||
| #include <string.h> | |||||
| #include <speex/speex_resampler.h> | |||||
| #include "frame.hpp" | #include "frame.hpp" | ||||
| @@ -9,41 +10,46 @@ namespace rack { | |||||
| template<int CHANNELS> | template<int CHANNELS> | ||||
| struct SampleRateConverter { | struct SampleRateConverter { | ||||
| SRC_STATE *state; | |||||
| SRC_DATA data; | |||||
| SpeexResamplerState *state = NULL; | |||||
| bool bypass = false; | |||||
| SampleRateConverter() { | SampleRateConverter() { | ||||
| int error; | 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() { | ~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 setQuality(int quality) { | |||||
| speex_resampler_set_quality(state, quality); | |||||
| } | } | ||||
| void setRatioSmooth(float r) { | |||||
| data.src_ratio = r; | |||||
| 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 */ | /** `in` and `out` are interlaced with the number of channels */ | ||||
| void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) { | void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) { | ||||
| // 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; | |||||
| if (bypass) { | |||||
| int len = std::min(*inFrames, *outFrames); | |||||
| memcpy(out, in, len * sizeof(Frame<CHANNELS>)); | |||||
| *inFrames = len; | |||||
| *outFrames = len; | |||||
| return; | |||||
| } | |||||
| speex_resampler_process_interleaved_float(state, (const float*)in, (unsigned int*)inFrames, (float*)out, (unsigned int*)outFrames); | |||||
| } | } | ||||
| void reset() { | void reset() { | ||||
| src_reset(state); | |||||
| int error = speex_resampler_reset_mem(state); | |||||
| assert(error == RESAMPLER_ERR_SUCCESS); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -3,12 +3,17 @@ | |||||
| #include "util.hpp" | #include "util.hpp" | ||||
| #include <jansson.h> | #include <jansson.h> | ||||
| #include <ossia/network/network.hpp> | |||||
| #include <ossia/network/oscquery/oscquery_server.hpp> | |||||
| namespace rack { | namespace rack { | ||||
| extern ossia::net::generic_device& root_dev(); | |||||
| struct Param { | struct Param { | ||||
| float value = 0.0; | float value = 0.0; | ||||
| std::string name = "param.1"; | |||||
| ossia::net::parameter_base* ossia_param; | |||||
| }; | }; | ||||
| struct Light { | struct Light { | ||||
| @@ -16,7 +21,7 @@ struct Light { | |||||
| float value = 0.0; | float value = 0.0; | ||||
| float getBrightness(); | float getBrightness(); | ||||
| void setBrightness(float brightness) { | void setBrightness(float brightness) { | ||||
| value = brightness * brightness; | |||||
| value = (brightness > 0.f) ? brightness * brightness : 0.f; | |||||
| } | } | ||||
| void setBrightnessSmooth(float brightness); | void setBrightnessSmooth(float brightness); | ||||
| }; | }; | ||||
| @@ -41,7 +46,6 @@ struct Output { | |||||
| Light plugLights[2]; | Light plugLights[2]; | ||||
| }; | }; | ||||
| struct Module { | struct Module { | ||||
| std::vector<Param> params; | std::vector<Param> params; | ||||
| std::vector<Input> inputs; | std::vector<Input> inputs; | ||||
| @@ -49,6 +53,8 @@ struct Module { | |||||
| std::vector<Light> lights; | std::vector<Light> lights; | ||||
| /** For CPU usage meter */ | /** For CPU usage meter */ | ||||
| float cpuTime = 0.0; | float cpuTime = 0.0; | ||||
| ossia::net::node_base* node{}; | |||||
| /** Deprecated, use constructor below this one */ | /** Deprecated, use constructor below this one */ | ||||
| Module() DEPRECATED {} | Module() DEPRECATED {} | ||||
| @@ -58,6 +64,8 @@ struct Module { | |||||
| inputs.resize(numInputs); | inputs.resize(numInputs); | ||||
| outputs.resize(numOutputs); | outputs.resize(numOutputs); | ||||
| lights.resize(numLights); | lights.resize(numLights); | ||||
| node = &ossia::net::create_node(rack::root_dev(),"module"); | |||||
| } | } | ||||
| virtual ~Module() {} | virtual ~Module() {} | ||||
| @@ -65,11 +73,14 @@ struct Module { | |||||
| virtual void step() {} | virtual void step() {} | ||||
| virtual void onSampleRateChange() {} | 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 */ | /** Override these to store extra internal data in the "data" property */ | ||||
| virtual json_t *toJson() { return NULL; } | virtual json_t *toJson() { return NULL; } | ||||
| @@ -17,8 +17,14 @@ namespace rack { | |||||
| extern GLFWwindow *gWindow; | extern GLFWwindow *gWindow; | ||||
| extern NVGcontext *gVg; | extern NVGcontext *gVg; | ||||
| extern NVGcontext *gFramebufferVg; | extern NVGcontext *gFramebufferVg; | ||||
| /** The default font to use for GUI elements */ | |||||
| extern std::shared_ptr<Font> gGuiFont; | extern std::shared_ptr<Font> gGuiFont; | ||||
| /** The scaling ratio */ | |||||
| extern float gPixelRatio; | 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 bool gAllowCursorLock; | ||||
| extern int gGuiFrame; | extern int gGuiFrame; | ||||
| extern Vec gMousePos; | extern Vec gMousePos; | ||||
| @@ -120,15 +120,6 @@ inline float sincf(float x) { | |||||
| return sinf(x) / 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` | /** Linearly interpolate an array `p` with index `x` | ||||
| Assumes that the array at `p` is of length at least floor(x)+1. | 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 | /** 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) | i.e. cmultf(&ar, &ai, ar, ai, br, bi) | ||||
| */ | */ | ||||
| inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) { | inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) { | ||||
| @@ -1,59 +1,13 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <string> | #include <string> | ||||
| #include <list> | #include <list> | ||||
| #include "tags.hpp" | |||||
| #include <ossia/network/network.hpp> | |||||
| namespace rack { | 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 ModuleWidget; | ||||
| struct Model; | struct Model; | ||||
| @@ -71,11 +25,12 @@ struct Plugin { | |||||
| */ | */ | ||||
| std::string slug; | 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 | 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. | Do not include the "v" in "v1.0" for example. | ||||
| */ | */ | ||||
| std::string version; | std::string version; | ||||
| /** URL for plugin homepage (optional) */ | /** URL for plugin homepage (optional) */ | ||||
| std::string website; | std::string website; | ||||
| /** URL for plugin manual (optional) */ | /** URL for plugin manual (optional) */ | ||||
| @@ -101,6 +56,8 @@ struct Model { | |||||
| virtual ~Model() {} | virtual ~Model() {} | ||||
| virtual ModuleWidget *createModuleWidget() { return NULL; } | virtual ModuleWidget *createModuleWidget() { return NULL; } | ||||
| ossia::net::node_base* node; | |||||
| }; | }; | ||||
| void pluginInit(); | void pluginInit(); | ||||
| @@ -119,7 +76,6 @@ std::string pluginGetLoginStatus(); | |||||
| extern std::list<Plugin*> gPlugins; | extern std::list<Plugin*> gPlugins; | ||||
| extern std::string gToken; | extern std::string gToken; | ||||
| extern std::string gTagNames[NUM_TAGS]; | |||||
| } // namespace rack | } // namespace rack | ||||
| @@ -8,7 +8,8 @@ | |||||
| #include "gui.hpp" | #include "gui.hpp" | ||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "components.hpp" | #include "components.hpp" | ||||
| #include <iostream> | |||||
| #include <sstream> | |||||
| namespace rack { | namespace rack { | ||||
| @@ -24,6 +25,8 @@ Model *createModel(std::string manufacturer, std::string slug, std::string name, | |||||
| ModuleWidget *createModuleWidget() override { | ModuleWidget *createModuleWidget() override { | ||||
| ModuleWidget *moduleWidget = new TModuleWidget(); | ModuleWidget *moduleWidget = new TModuleWidget(); | ||||
| moduleWidget->model = this; | moduleWidget->model = this; | ||||
| moduleWidget->module->node->set_name(name); | |||||
| return moduleWidget; | return moduleWidget; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -43,13 +46,38 @@ Widget *createScrew(Vec pos) { | |||||
| } | } | ||||
| template <class TParamWidget> | template <class TParamWidget> | ||||
| ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||||
| ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue, std::string name = std::string("")) { | |||||
| ParamWidget *param = new TParamWidget(); | ParamWidget *param = new TParamWidget(); | ||||
| param->box.pos = pos; | param->box.pos = pos; | ||||
| param->module = module; | param->module = module; | ||||
| param->paramId = paramId; | param->paramId = paramId; | ||||
| param->setLimits(minValue, maxValue); | |||||
| auto& p = module->params[paramId]; | |||||
| if (name == "") | |||||
| { | |||||
| std::stringstream ss; | |||||
| ss << "param." << paramId; | |||||
| name = ss.str(); | |||||
| } | |||||
| auto& p_node = ossia::net::create_node(*module->node, name); | |||||
| p.ossia_param = p_node.create_parameter(ossia::val_type::FLOAT); | |||||
| p.ossia_param->set_domain(ossia::make_domain(minValue,maxValue)); | |||||
| p.ossia_param->set_bounding(ossia::bounding_mode::CLIP); | |||||
| p.ossia_param->push_value(defaultValue); | |||||
| p.ossia_param->set_default_value(defaultValue); | |||||
| p.ossia_param->add_callback([param] (const ossia::value& v) { | |||||
| auto& p = param->module->params[param->paramId]; | |||||
| param->value = v.get<float>(); | |||||
| p.value = param->value; | |||||
| if ( auto fbw = dynamic_cast<FramebufferWidget*>(param)) | |||||
| fbw->dirty = true; | |||||
| }); | |||||
| param->setLimits(minValue, maxValue); | |||||
| param->setDefaultValue(defaultValue); | param->setDefaultValue(defaultValue); | ||||
| return param; | return param; | ||||
| } | } | ||||
| @@ -0,0 +1,72 @@ | |||||
| #pragma once | |||||
| #include <string> | |||||
| 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 | |||||
| @@ -178,6 +178,7 @@ struct ZoomWidget : Widget { | |||||
| void onMouseMove(EventMouseMove &e) override; | void onMouseMove(EventMouseMove &e) override; | ||||
| void onHoverKey(EventHoverKey &e) override; | void onHoverKey(EventHoverKey &e) override; | ||||
| void onScroll(EventScroll &e) override; | void onScroll(EventScroll &e) override; | ||||
| void onPathDrop(EventPathDrop &e) override; | |||||
| }; | }; | ||||
| //////////////////// | //////////////////// | ||||
| @@ -295,7 +296,7 @@ struct Label : Widget { | |||||
| /** Deletes itself from parent when clicked */ | /** Deletes itself from parent when clicked */ | ||||
| struct MenuOverlay : OpaqueWidget { | struct MenuOverlay : OpaqueWidget { | ||||
| void step() override; | void step() override; | ||||
| void onDragDrop(EventDragDrop &e) override; | |||||
| void onMouseDown(EventMouseDown &e) override; | |||||
| void onHoverKey(EventHoverKey &e) override; | void onHoverKey(EventHoverKey &e) override; | ||||
| }; | }; | ||||
| @@ -21,6 +21,8 @@ ifeq ($(ARCH), win) | |||||
| TARGET = plugin.dll | TARGET = plugin.dll | ||||
| endif | endif | ||||
| DISTRIBUTABLES += $(TARGET) | |||||
| all: $(TARGET) | all: $(TARGET) | ||||
| @@ -1,5 +1,7 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include <ossia/network/network.hpp> | |||||
| #include <ossia/network/oscquery/oscquery_server.hpp> | |||||
| namespace rack { | namespace rack { | ||||
| @@ -28,5 +30,12 @@ void sceneDestroy() { | |||||
| gScene = NULL; | gScene = NULL; | ||||
| } | } | ||||
| ossia::net::generic_device& root_dev(){ | |||||
| static ossia::net::generic_device dev{ | |||||
| std::make_unique<ossia::oscquery::oscquery_server_protocol>(1234, 5678), | |||||
| "VCV-Rack"}; | |||||
| return dev; | |||||
| } | |||||
| } // namespace rack | } // namespace rack | ||||
| @@ -18,7 +18,11 @@ void Knob::onDragStart(EventDragStart &e) { | |||||
| void Knob::onDragMove(EventDragMove &e) { | void Knob::onDragMove(EventDragMove &e) { | ||||
| // Drag slower if Mod | // Drag slower if Mod | ||||
| float delta = KNOB_SENSITIVITY * (maxValue - minValue) * -e.mouseRel.y; | |||||
| float range = maxValue - minValue; | |||||
| float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed; | |||||
| if (std::isfinite(range)) | |||||
| delta *= range; | |||||
| if (guiIsModPressed()) | if (guiIsModPressed()) | ||||
| delta /= 16.0; | delta /= 16.0; | ||||
| dragValue += delta; | dragValue += delta; | ||||
| @@ -5,35 +5,46 @@ namespace rack { | |||||
| void LightWidget::draw(NVGcontext *vg) { | 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.r = clampf(color.r, 0.0, 1.0); | ||||
| color.g = clampf(color.g, 0.0, 1.0); | color.g = clampf(color.g, 0.0, 1.0); | ||||
| color.b = clampf(color.b, 0.0, 1.0); | color.b = clampf(color.b, 0.0, 1.0); | ||||
| color.a = clampf(color.a, 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); | nvgBeginPath(vg); | ||||
| nvgCircle(vg, radius, radius, radius); | nvgCircle(vg, radius, radius, radius); | ||||
| // Background | |||||
| nvgFillColor(vg, bgColor); | nvgFillColor(vg, bgColor); | ||||
| nvgFill(vg); | 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 | // Inner glow | ||||
| nvgGlobalCompositeOperation(vg, NVG_LIGHTER); | |||||
| nvgFillColor(vg, color); | nvgFillColor(vg, color); | ||||
| nvgFill(vg); | nvgFill(vg); | ||||
| } | |||||
| void LightWidget::drawHalo(NVGcontext *vg) { | |||||
| float radius = box.size.x / 2.0; | |||||
| float oradius = radius + 15.0; | |||||
| // Outer glow | |||||
| nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
| nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); | nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); | ||||
| NVGpaint paint; | NVGpaint paint; | ||||
| NVGcolor icol = color; | NVGcolor icol = color; | ||||
| icol.a *= 0.10; | icol.a *= 0.10; | ||||
| @@ -41,8 +52,11 @@ void LightWidget::draw(NVGcontext *vg) { | |||||
| ocol.a = 0.0; | ocol.a = 0.0; | ||||
| paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); | paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); | ||||
| nvgFillPaint(vg, paint); | nvgFillPaint(vg, paint); | ||||
| nvgGlobalCompositeOperation(vg, NVG_LIGHTER); | |||||
| nvgFill(vg); | nvgFill(vg); | ||||
| } | } | ||||
| } // namespace rack | } // namespace rack | ||||
| @@ -60,8 +60,11 @@ void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) { | |||||
| json_t *ModuleWidget::toJson() { | json_t *ModuleWidget::toJson() { | ||||
| json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
| // manufacturer | |||||
| // plugin | |||||
| json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); | 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 | // model | ||||
| json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | ||||
| // pos | // pos | ||||
| @@ -118,12 +121,24 @@ void ModuleWidget::disconnect() { | |||||
| } | } | ||||
| } | } | ||||
| void ModuleWidget::create() { | |||||
| if (module) { | |||||
| module->onCreate(); | |||||
| } | |||||
| } | |||||
| void ModuleWidget::_delete() { | |||||
| if (module) { | |||||
| module->onDelete(); | |||||
| } | |||||
| } | |||||
| void ModuleWidget::reset() { | void ModuleWidget::reset() { | ||||
| for (ParamWidget *param : params) { | for (ParamWidget *param : params) { | ||||
| param->setValue(param->defaultValue); | param->setValue(param->defaultValue); | ||||
| } | } | ||||
| if (module) { | if (module) { | ||||
| module->reset(); | |||||
| module->onReset(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -132,7 +147,7 @@ void ModuleWidget::randomize() { | |||||
| param->randomize(); | param->randomize(); | ||||
| } | } | ||||
| if (module) { | if (module) { | ||||
| module->randomize(); | |||||
| module->onRandomize(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -188,7 +203,7 @@ void ModuleWidget::onMouseMove(EventMouseMove &e) { | |||||
| gRackWidget->deleteModule(this); | gRackWidget->deleteModule(this); | ||||
| this->finalizeEvents(); | this->finalizeEvents(); | ||||
| delete this; | delete this; | ||||
| // Kinda sketchy because events will be passed further down the tree | |||||
| e.consumed = true; | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| @@ -279,36 +294,36 @@ Menu *ModuleWidget::createContextMenu() { | |||||
| MenuLabel *menuLabel = new MenuLabel(); | MenuLabel *menuLabel = new MenuLabel(); | ||||
| menuLabel->text = model->manufacturer + " " + model->name; | menuLabel->text = model->manufacturer + " " + model->name; | ||||
| menu->pushChild(menuLabel); | |||||
| menu->addChild(menuLabel); | |||||
| ResetMenuItem *resetItem = new ResetMenuItem(); | ResetMenuItem *resetItem = new ResetMenuItem(); | ||||
| resetItem->text = "Initialize"; | resetItem->text = "Initialize"; | ||||
| resetItem->rightText = GUI_MOD_KEY_NAME "+I"; | resetItem->rightText = GUI_MOD_KEY_NAME "+I"; | ||||
| resetItem->moduleWidget = this; | resetItem->moduleWidget = this; | ||||
| menu->pushChild(resetItem); | |||||
| menu->addChild(resetItem); | |||||
| RandomizeMenuItem *randomizeItem = new RandomizeMenuItem(); | RandomizeMenuItem *randomizeItem = new RandomizeMenuItem(); | ||||
| randomizeItem->text = "Randomize"; | randomizeItem->text = "Randomize"; | ||||
| randomizeItem->rightText = GUI_MOD_KEY_NAME "+R"; | randomizeItem->rightText = GUI_MOD_KEY_NAME "+R"; | ||||
| randomizeItem->moduleWidget = this; | randomizeItem->moduleWidget = this; | ||||
| menu->pushChild(randomizeItem); | |||||
| menu->addChild(randomizeItem); | |||||
| DisconnectMenuItem *disconnectItem = new DisconnectMenuItem(); | DisconnectMenuItem *disconnectItem = new DisconnectMenuItem(); | ||||
| disconnectItem->text = "Disconnect cables"; | disconnectItem->text = "Disconnect cables"; | ||||
| disconnectItem->moduleWidget = this; | disconnectItem->moduleWidget = this; | ||||
| menu->pushChild(disconnectItem); | |||||
| menu->addChild(disconnectItem); | |||||
| CloneMenuItem *cloneItem = new CloneMenuItem(); | CloneMenuItem *cloneItem = new CloneMenuItem(); | ||||
| cloneItem->text = "Duplicate"; | cloneItem->text = "Duplicate"; | ||||
| cloneItem->rightText = GUI_MOD_KEY_NAME "+D"; | cloneItem->rightText = GUI_MOD_KEY_NAME "+D"; | ||||
| cloneItem->moduleWidget = this; | cloneItem->moduleWidget = this; | ||||
| menu->pushChild(cloneItem); | |||||
| menu->addChild(cloneItem); | |||||
| DeleteMenuItem *deleteItem = new DeleteMenuItem(); | DeleteMenuItem *deleteItem = new DeleteMenuItem(); | ||||
| deleteItem->text = "Delete"; | deleteItem->text = "Delete"; | ||||
| deleteItem->rightText = "Backspace/Delete"; | deleteItem->rightText = "Backspace/Delete"; | ||||
| deleteItem->moduleWidget = this; | deleteItem->moduleWidget = this; | ||||
| menu->pushChild(deleteItem); | |||||
| menu->addChild(deleteItem); | |||||
| return menu; | return menu; | ||||
| } | } | ||||
| @@ -46,6 +46,15 @@ void RackRail::draw(NVGcontext *vg) { | |||||
| nvgLineTo(vg, box.size.x, railY + RACK_GRID_HEIGHT - 0.5); | nvgLineTo(vg, box.size.x, railY + RACK_GRID_HEIGHT - 0.5); | ||||
| nvgStroke(vg); | 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); | |||||
| } | |||||
| } | } | ||||
| @@ -38,6 +38,25 @@ void RackWidget::clear() { | |||||
| wireContainer->clearChildren(); | wireContainer->clearChildren(); | ||||
| moduleContainer->clearChildren(); | moduleContainer->clearChildren(); | ||||
| lastPath = ""; | 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() { | void RackWidget::reset() { | ||||
| @@ -83,20 +102,19 @@ void RackWidget::saveAsDialog() { | |||||
| } | } | ||||
| } | } | ||||
| void RackWidget::savePatch(std::string path) { | void RackWidget::savePatch(std::string path) { | ||||
| info("Saving patch %s", path.c_str()); | info("Saving patch %s", path.c_str()); | ||||
| FILE *file = fopen(path.c_str(), "w"); | |||||
| if (!file) | |||||
| json_t *rootJ = toJson(); | |||||
| if (!rootJ) | |||||
| return; | return; | ||||
| json_t *rootJ = toJson(); | |||||
| if (rootJ) { | |||||
| FILE *file = fopen(path.c_str(), "w"); | |||||
| if (file) { | |||||
| json_dumpf(rootJ, file, JSON_INDENT(2)); | json_dumpf(rootJ, file, JSON_INDENT(2)); | ||||
| json_decref(rootJ); | |||||
| fclose(file); | |||||
| } | } | ||||
| fclose(file); | |||||
| json_decref(rootJ); | |||||
| } | } | ||||
| void RackWidget::loadPatch(std::string path) { | void RackWidget::loadPatch(std::string path) { | ||||
| @@ -127,8 +145,10 @@ json_t *RackWidget::toJson() { | |||||
| json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
| // version | // 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 | // modules | ||||
| json_t *modulesJ = json_array(); | json_t *modulesJ = json_array(); | ||||
| @@ -285,9 +305,11 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| void RackWidget::addModule(ModuleWidget *m) { | void RackWidget::addModule(ModuleWidget *m) { | ||||
| moduleContainer->addChild(m); | moduleContainer->addChild(m); | ||||
| m->create(); | |||||
| } | } | ||||
| void RackWidget::deleteModule(ModuleWidget *m) { | void RackWidget::deleteModule(ModuleWidget *m) { | ||||
| m->_delete(); | |||||
| moduleContainer->removeChild(m); | moduleContainer->removeChild(m); | ||||
| } | } | ||||
| @@ -323,8 +345,8 @@ bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) { | |||||
| int x0 = roundf(box.pos.x / RACK_GRID_WIDTH); | int x0 = roundf(box.pos.x / RACK_GRID_WIDTH); | ||||
| int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT); | int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT); | ||||
| std::vector<Vec> positions; | std::vector<Vec> 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)); | positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT)); | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,7 +4,7 @@ | |||||
| namespace rack { | namespace rack { | ||||
| SVGSlider::SVGSlider() { | |||||
| SVGFader::SVGFader() { | |||||
| background = new SVGWidget(); | background = new SVGWidget(); | ||||
| addChild(background); | addChild(background); | ||||
| @@ -12,7 +12,7 @@ SVGSlider::SVGSlider() { | |||||
| addChild(handle); | addChild(handle); | ||||
| } | } | ||||
| void SVGSlider::step() { | |||||
| void SVGFader::step() { | |||||
| if (dirty) { | if (dirty) { | ||||
| // Update handle position | // Update handle position | ||||
| Vec handlePos = Vec(rescalef(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescalef(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | Vec handlePos = Vec(rescalef(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescalef(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | ||||
| @@ -21,9 +21,9 @@ void SVGSlider::step() { | |||||
| FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
| } | } | ||||
| void SVGSlider::onChange(EventChange &e) { | |||||
| void SVGFader::onChange(EventChange &e) { | |||||
| dirty = true; | dirty = true; | ||||
| ParamWidget::onChange(e); | |||||
| Knob::onChange(e); | |||||
| } | } | ||||
| @@ -23,7 +23,9 @@ void SVGKnob::step() { | |||||
| // Re-transform TransformWidget if dirty | // Re-transform TransformWidget if dirty | ||||
| if (dirty) { | if (dirty) { | ||||
| tw->box.size = box.size; | 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(); | tw->identity(); | ||||
| // Scale SVG to box | // Scale SVG to box | ||||
| tw->scale(box.size.div(sw->box.size)); | tw->scale(box.size.div(sw->box.size)); | ||||
| @@ -18,10 +18,6 @@ void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | |||||
| } | } | ||||
| } | } | ||||
| void SVGSwitch::step() { | |||||
| FramebufferWidget::step(); | |||||
| } | |||||
| void SVGSwitch::onChange(EventChange &e) { | void SVGSwitch::onChange(EventChange &e) { | ||||
| assert(frames.size() > 0); | assert(frames.size() > 0); | ||||
| float valueScaled = rescalef(value, minValue, maxValue, 0, frames.size() - 1); | float valueScaled = rescalef(value, minValue, maxValue, 0, frames.size() - 1); | ||||
| @@ -43,11 +43,11 @@ struct FileChoice : ChoiceButton { | |||||
| menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
| { | { | ||||
| menu->pushChild(construct<NewItem>(&MenuItem::text, "New", &MenuItem::rightText, GUI_MOD_KEY_NAME "+N")); | |||||
| menu->pushChild(construct<OpenItem>(&MenuItem::text, "Open", &MenuItem::rightText, GUI_MOD_KEY_NAME "+O")); | |||||
| menu->pushChild(construct<SaveItem>(&MenuItem::text, "Save", &MenuItem::rightText, GUI_MOD_KEY_NAME "+S")); | |||||
| menu->pushChild(construct<SaveAsItem>(&MenuItem::text, "Save as", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Shift+S")); | |||||
| menu->pushChild(construct<QuitItem>(&MenuItem::text, "Quit", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Q")); | |||||
| menu->addChild(construct<NewItem>(&MenuItem::text, "New", &MenuItem::rightText, GUI_MOD_KEY_NAME "+N")); | |||||
| menu->addChild(construct<OpenItem>(&MenuItem::text, "Open", &MenuItem::rightText, GUI_MOD_KEY_NAME "+O")); | |||||
| menu->addChild(construct<SaveItem>(&MenuItem::text, "Save", &MenuItem::rightText, GUI_MOD_KEY_NAME "+S")); | |||||
| menu->addChild(construct<SaveAsItem>(&MenuItem::text, "Save as", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Shift+S")); | |||||
| menu->addChild(construct<QuitItem>(&MenuItem::text, "Quit", &MenuItem::rightText, GUI_MOD_KEY_NAME "+Q")); | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -75,7 +75,7 @@ struct SampleRateChoice : ChoiceButton { | |||||
| PauseItem *pauseItem = new PauseItem(); | PauseItem *pauseItem = new PauseItem(); | ||||
| pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; | pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; | ||||
| menu->pushChild(pauseItem); | |||||
| menu->addChild(pauseItem); | |||||
| float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; | float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; | ||||
| int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); | int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); | ||||
| @@ -83,7 +83,7 @@ struct SampleRateChoice : ChoiceButton { | |||||
| SampleRateItem *item = new SampleRateItem(); | SampleRateItem *item = new SampleRateItem(); | ||||
| item->text = stringf("%.0f Hz", sampleRates[i]); | item->text = stringf("%.0f Hz", sampleRates[i]); | ||||
| item->sampleRate = sampleRates[i]; | item->sampleRate = sampleRates[i]; | ||||
| menu->pushChild(item); | |||||
| menu->addChild(item); | |||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -151,12 +151,13 @@ Toolbar::Toolbar() { | |||||
| struct ZoomSlider : Slider { | struct ZoomSlider : Slider { | ||||
| void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
| Slider::onAction(e); | Slider::onAction(e); | ||||
| gRackScene->zoomWidget->setZoom(value / 100.0); | |||||
| gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); | |||||
| } | } | ||||
| }; | }; | ||||
| zoomSlider = new ZoomSlider(); | zoomSlider = new ZoomSlider(); | ||||
| zoomSlider->box.pos = Vec(xPos, margin); | zoomSlider->box.pos = Vec(xPos, margin); | ||||
| zoomSlider->box.size.x = 150; | zoomSlider->box.size.x = 150; | ||||
| zoomSlider->precision = 0; | |||||
| zoomSlider->label = "Zoom"; | zoomSlider->label = "Zoom"; | ||||
| zoomSlider->unit = "%"; | zoomSlider->unit = "%"; | ||||
| zoomSlider->setLimits(25.0, 200.0); | zoomSlider->setLimits(25.0, 200.0); | ||||
| @@ -86,11 +86,6 @@ static int lastWireColorId = -1; | |||||
| WireWidget::WireWidget() { | WireWidget::WireWidget() { | ||||
| lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors); | lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors); | ||||
| color = wireColors[lastWireColorId]; | color = wireColors[lastWireColorId]; | ||||
| // inputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| // outputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| // addChild(inputLight); | |||||
| // addChild(outputLight); | |||||
| } | } | ||||
| WireWidget::~WireWidget() { | WireWidget::~WireWidget() { | ||||
| @@ -128,7 +128,7 @@ struct AudioInterface : Module { | |||||
| openStream(); | openStream(); | ||||
| } | } | ||||
| void reset() override { | |||||
| void onReset() override { | |||||
| closeStream(); | closeStream(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -169,7 +169,7 @@ void AudioInterface::step() { | |||||
| // Once full, sample rate convert the input | // Once full, sample rate convert the input | ||||
| // inputBuffer -> SRC -> inputSrcBuffer | // inputBuffer -> SRC -> inputSrcBuffer | ||||
| if (inputBuffer.full()) { | if (inputBuffer.full()) { | ||||
| inputSrc.setRatio(sampleRate / engineGetSampleRate()); | |||||
| inputSrc.setRates(engineGetSampleRate(), sampleRate); | |||||
| int inLen = inputBuffer.size(); | int inLen = inputBuffer.size(); | ||||
| int outLen = inputSrcBuffer.capacity(); | int outLen = inputSrcBuffer.capacity(); | ||||
| inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); | 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 | // Pass output through sample rate converter | ||||
| outputSrc.setRatio(engineGetSampleRate() / sampleRate); | |||||
| outputSrc.setRates(sampleRate, engineGetSampleRate()); | |||||
| int inLen = numFrames; | int inLen = numFrames; | ||||
| int outLen = outputBuffer.capacity(); | int outLen = outputBuffer.capacity(); | ||||
| outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); | outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); | ||||
| @@ -414,7 +414,7 @@ struct AudioDriverChoice : ChoiceButton { | |||||
| audioItem->audioInterface = audioInterface; | audioItem->audioInterface = audioInterface; | ||||
| audioItem->driver = driver; | audioItem->driver = driver; | ||||
| audioItem->text = audioInterface->getDriverName(driver); | audioItem->text = audioInterface->getDriverName(driver); | ||||
| menu->pushChild(audioItem); | |||||
| menu->addChild(audioItem); | |||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -446,14 +446,14 @@ struct AudioDeviceChoice : ChoiceButton { | |||||
| audioItem->audioInterface = audioInterface; | audioItem->audioInterface = audioInterface; | ||||
| audioItem->device = -1; | audioItem->device = -1; | ||||
| audioItem->text = "No device"; | audioItem->text = "No device"; | ||||
| menu->pushChild(audioItem); | |||||
| menu->addChild(audioItem); | |||||
| } | } | ||||
| for (int device = 0; device < deviceCount; device++) { | for (int device = 0; device < deviceCount; device++) { | ||||
| AudioDeviceItem *audioItem = new AudioDeviceItem(); | AudioDeviceItem *audioItem = new AudioDeviceItem(); | ||||
| audioItem->audioInterface = audioInterface; | audioItem->audioInterface = audioInterface; | ||||
| audioItem->device = device; | audioItem->device = device; | ||||
| audioItem->text = audioInterface->getDeviceName(device); | audioItem->text = audioInterface->getDeviceName(device); | ||||
| menu->pushChild(audioItem); | |||||
| menu->addChild(audioItem); | |||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -487,7 +487,7 @@ struct SampleRateChoice : ChoiceButton { | |||||
| item->audioInterface = audioInterface; | item->audioInterface = audioInterface; | ||||
| item->sampleRate = sampleRate; | item->sampleRate = sampleRate; | ||||
| item->text = stringf("%.0f Hz", sampleRate); | item->text = stringf("%.0f Hz", sampleRate); | ||||
| menu->pushChild(item); | |||||
| menu->addChild(item); | |||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -519,7 +519,7 @@ struct BlockSizeChoice : ChoiceButton { | |||||
| item->audioInterface = audioInterface; | item->audioInterface = audioInterface; | ||||
| item->blockSize = blockSizes[i]; | item->blockSize = blockSizes[i]; | ||||
| item->text = stringf("%d", blockSizes[i]); | item->text = stringf("%d", blockSizes[i]); | ||||
| menu->pushChild(item); | |||||
| menu->addChild(item); | |||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -1,9 +1,9 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| struct CCValue { | struct CCValue { | ||||
| int val = 0; // Controller value | int val = 0; // Controller value | ||||
| TransitionSmoother tSmooth; | TransitionSmoother tSmooth; | ||||
| @@ -81,7 +81,7 @@ struct MIDICCToCVInterface : MidiIO, Module { | |||||
| } | } | ||||
| } | } | ||||
| void reset() override { | |||||
| void onReset() override { | |||||
| resetMidi(); | resetMidi(); | ||||
| } | } | ||||
| @@ -91,11 +91,11 @@ void MIDICCToCVInterface::step() { | |||||
| if (isPortOpen()) { | if (isPortOpen()) { | ||||
| std::vector<unsigned char> message; | std::vector<unsigned char> message; | ||||
| // midiIn->getMessage returns empty vector if there are no messages in the queue | // midiIn->getMessage returns empty vector if there are no messages in the queue | ||||
| getMessage(&message); | getMessage(&message); | ||||
| while (message.size() > 0) { | |||||
| if (message.size() > 0) { | |||||
| processMidi(message); | processMidi(message); | ||||
| getMessage(&message); | |||||
| } | } | ||||
| } | } | ||||
| @@ -117,7 +117,7 @@ void MIDICCToCVInterface::resetMidi() { | |||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | for (int i = 0; i < NUM_OUTPUTS; i++) { | ||||
| cc[i].val = 0; | cc[i].val = 0; | ||||
| cc[i].resetSync(); | cc[i].resetSync(); | ||||
| cc[i].tSmooth.set(0,0); | |||||
| cc[i].tSmooth.set(0, 0); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -147,7 +147,8 @@ void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| cc[i].syncFirst = false; | cc[i].syncFirst = false; | ||||
| if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { | if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { | ||||
| cc[i].sync = 0; | cc[i].sync = 0; | ||||
| }else { | |||||
| } | |||||
| else { | |||||
| cc[i].sync = absi(data2 - cc[i].val); | cc[i].sync = absi(data2 - cc[i].val); | ||||
| } | } | ||||
| return; | return; | ||||
| @@ -156,7 +157,8 @@ void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| if (cc[i].sync == 0) { | if (cc[i].sync == 0) { | ||||
| cc[i].val = data2; | cc[i].val = data2; | ||||
| cc[i].changed = true; | cc[i].changed = true; | ||||
| } else { | |||||
| } | |||||
| else { | |||||
| cc[i].sync = absi(data2 - cc[i].val); | cc[i].sync = absi(data2 - cc[i].val); | ||||
| } | } | ||||
| } | } | ||||
| @@ -225,12 +227,14 @@ void CCTextField::onTextChange() { | |||||
| text = ""; | text = ""; | ||||
| begin = end = 0; | begin = end = 0; | ||||
| module->cc[outNum].num = -1; | module->cc[outNum].num = -1; | ||||
| } else { | |||||
| } | |||||
| else { | |||||
| module->cc[outNum].num = num; | module->cc[outNum].num = num; | ||||
| module->cc[outNum].resetSync(); | module->cc[outNum].resetSync(); | ||||
| } | } | ||||
| } catch (...) { | |||||
| } | |||||
| catch (...) { | |||||
| text = ""; | text = ""; | ||||
| begin = end = 0; | begin = end = 0; | ||||
| module->cc[outNum].num = -1; | module->cc[outNum].num = -1; | ||||
| @@ -309,7 +313,8 @@ MIDICCToCVWidget::MIDICCToCVWidget() { | |||||
| if ((i + 1) % 4 == 0) { | if ((i + 1) % 4 == 0) { | ||||
| yPos += 47 + margin; | yPos += 47 + margin; | ||||
| } else { | |||||
| } | |||||
| else { | |||||
| yPos -= labelHeight + margin; | yPos -= labelHeight + margin; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| #include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
| using namespace rack; | using namespace rack; | ||||
| struct MIDIClockToCVInterface : MidiIO, Module { | struct MIDIClockToCVInterface : MidiIO, Module { | ||||
| @@ -71,7 +71,7 @@ struct MIDIClockToCVInterface : MidiIO, Module { | |||||
| void resetMidi() override; | void resetMidi() override; | ||||
| json_t *toJson() override{ | |||||
| json_t *toJson() override { | |||||
| json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
| addBaseJson(rootJ); | addBaseJson(rootJ); | ||||
| json_object_set_new(rootJ, "clock1ratio", json_integer(clock1ratio)); | json_object_set_new(rootJ, "clock1ratio", json_integer(clock1ratio)); | ||||
| @@ -79,7 +79,7 @@ struct MIDIClockToCVInterface : MidiIO, Module { | |||||
| return rootJ; | return rootJ; | ||||
| } | } | ||||
| void fromJson(json_t *rootJ) override{ | |||||
| void fromJson(json_t *rootJ) override { | |||||
| baseFromJson(rootJ); | baseFromJson(rootJ); | ||||
| json_t *c1rJ = json_object_get(rootJ, "clock1ratio"); | json_t *c1rJ = json_object_get(rootJ, "clock1ratio"); | ||||
| if (c1rJ) { | if (c1rJ) { | ||||
| @@ -101,9 +101,8 @@ void MIDIClockToCVInterface::step() { | |||||
| // midiIn->getMessage returns empty vector if there are no messages in the queue | // midiIn->getMessage returns empty vector if there are no messages in the queue | ||||
| getMessage(&message); | getMessage(&message); | ||||
| while (message.size() > 0) { | |||||
| if (message.size() > 0) { | |||||
| processMidi(message); | processMidi(message); | ||||
| getMessage(&message); | |||||
| } | } | ||||
| } | } | ||||
| @@ -185,18 +184,18 @@ void MIDIClockToCVInterface::resetMidi() { | |||||
| void MIDIClockToCVInterface::processMidi(std::vector<unsigned char> msg) { | void MIDIClockToCVInterface::processMidi(std::vector<unsigned char> msg) { | ||||
| switch (msg[0]) { | 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 +217,15 @@ struct ClockRatioItem : MenuItem { | |||||
| struct ClockRatioChoice : ChoiceButton { | struct ClockRatioChoice : ChoiceButton { | ||||
| int *clockRatio; | int *clockRatio; | ||||
| const std::vector<std::string> ratioNames = {"Sixteenth note (1:4 ratio)", "Eighth note triplet (1:3 ratio)", | const std::vector<std::string> 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<std::string> ratioNames_short = {"1:4 ratio", "1:3 ratio", "1:2 ratio", "2:3 ratio", "1:1 ratio", | const std::vector<std::string> 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 { | void onAction(EventAction &e) override { | ||||
| Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
| @@ -236,7 +237,7 @@ struct ClockRatioChoice : ChoiceButton { | |||||
| clockRatioItem->ratio = ratio; | clockRatioItem->ratio = ratio; | ||||
| clockRatioItem->clockRatio = clockRatio; | clockRatioItem->clockRatio = clockRatio; | ||||
| clockRatioItem->text = ratioNames[ratio]; | clockRatioItem->text = ratioNames[ratio]; | ||||
| menu->pushChild(clockRatioItem); | |||||
| menu->addChild(clockRatioItem); | |||||
| } | } | ||||
| } | } | ||||
| @@ -270,7 +271,7 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { | |||||
| label->box.pos = Vec(box.size.x - margin - 7 * 15, margin); | label->box.pos = Vec(box.size.x - margin - 7 * 15, margin); | ||||
| label->text = "MIDI Clk-CV"; | label->text = "MIDI Clk-CV"; | ||||
| addChild(label); | addChild(label); | ||||
| yPos = labelHeight*2; | |||||
| yPos = labelHeight * 2; | |||||
| } | } | ||||
| { | { | ||||
| @@ -320,8 +321,8 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { | |||||
| addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK1_RATIO)); | addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK1_RATIO)); | ||||
| ClockRatioChoice *ratioChoice = new ClockRatioChoice(); | ClockRatioChoice *ratioChoice = new ClockRatioChoice(); | ||||
| ratioChoice->clockRatio = &module->clock1ratio; | 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); | addChild(ratioChoice); | ||||
| yPos += ratioChoice->box.size.y + margin * 3; | yPos += ratioChoice->box.size.y + margin * 3; | ||||
| @@ -344,8 +345,8 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { | |||||
| addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK2_RATIO)); | addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK2_RATIO)); | ||||
| ClockRatioChoice *ratioChoice = new ClockRatioChoice(); | ClockRatioChoice *ratioChoice = new ClockRatioChoice(); | ||||
| ratioChoice->clockRatio = &module->clock2ratio; | 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); | addChild(ratioChoice); | ||||
| yPos += ratioChoice->box.size.y + margin * 3; | yPos += ratioChoice->box.size.y + margin * 3; | ||||
| @@ -1,9 +1,9 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| using namespace rack; | using namespace rack; | ||||
| @@ -59,7 +59,8 @@ std::vector<std::string> MidiIO::getDevices() { | |||||
| RtMidiIn *m; | RtMidiIn *m; | ||||
| try { | try { | ||||
| m = new RtMidiIn(); | m = new RtMidiIn(); | ||||
| } catch (RtMidiError &error) { | |||||
| } | |||||
| catch (RtMidiError &error) { | |||||
| warn("Failed to create RtMidiIn: %s", error.getMessage().c_str()); | warn("Failed to create RtMidiIn: %s", error.getMessage().c_str()); | ||||
| return names; | return names; | ||||
| } | } | ||||
| @@ -209,7 +210,7 @@ void MidiChoice::onAction(EventAction &e) { | |||||
| MidiItem *midiItem = new MidiItem(); | MidiItem *midiItem = new MidiItem(); | ||||
| midiItem->midiModule = midiModule; | midiItem->midiModule = midiModule; | ||||
| midiItem->text = ""; | midiItem->text = ""; | ||||
| menu->pushChild(midiItem); | |||||
| menu->addChild(midiItem); | |||||
| } | } | ||||
| std::vector<std::string> deviceNames = midiModule->getDevices(); | std::vector<std::string> deviceNames = midiModule->getDevices(); | ||||
| @@ -217,7 +218,7 @@ void MidiChoice::onAction(EventAction &e) { | |||||
| MidiItem *midiItem = new MidiItem(); | MidiItem *midiItem = new MidiItem(); | ||||
| midiItem->midiModule = midiModule; | midiItem->midiModule = midiModule; | ||||
| midiItem->text = deviceNames[i]; | midiItem->text = deviceNames[i]; | ||||
| menu->pushChild(midiItem); | |||||
| menu->addChild(midiItem); | |||||
| } | } | ||||
| } | } | ||||
| @@ -245,14 +246,14 @@ void ChannelChoice::onAction(EventAction &e) { | |||||
| channelItem->midiModule = midiModule; | channelItem->midiModule = midiModule; | ||||
| channelItem->channel = -1; | channelItem->channel = -1; | ||||
| channelItem->text = "All"; | channelItem->text = "All"; | ||||
| menu->pushChild(channelItem); | |||||
| menu->addChild(channelItem); | |||||
| } | } | ||||
| for (int channel = 0; channel < 16; channel++) { | for (int channel = 0; channel < 16; channel++) { | ||||
| ChannelItem *channelItem = new ChannelItem(); | ChannelItem *channelItem = new ChannelItem(); | ||||
| channelItem->midiModule = midiModule; | channelItem->midiModule = midiModule; | ||||
| channelItem->channel = channel; | channelItem->channel = channel; | ||||
| channelItem->text = stringf("%d", channel + 1); | channelItem->text = stringf("%d", channel + 1); | ||||
| menu->pushChild(channelItem); | |||||
| menu->addChild(channelItem); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,15 @@ | |||||
| #include <unordered_map> | #include <unordered_map> | ||||
| #include "rack.hpp" | #include "rack.hpp" | ||||
| #pragma GCC diagnostic push | |||||
| #pragma GCC diagnostic ignored "-Wsuggest-override" | |||||
| #include "rtmidi/RtMidi.h" | #include "rtmidi/RtMidi.h" | ||||
| #pragma GCC diagnostic pop | |||||
| using namespace rack; | using namespace rack; | ||||
| struct IgnoreFlags { | struct IgnoreFlags { | ||||
| bool midiSysex = true; | bool midiSysex = true; | ||||
| bool midiTime = true; | bool midiTime = true; | ||||
| @@ -16,7 +21,6 @@ struct MidiMessage { | |||||
| double timeStamp; | double timeStamp; | ||||
| MidiMessage() : bytes(0), timeStamp(0.0) {}; | MidiMessage() : bytes(0), timeStamp(0.0) {}; | ||||
| }; | }; | ||||
| /** | /** | ||||
| @@ -131,10 +135,10 @@ struct TransitionSmoother { | |||||
| switch (m) { | switch (m) { | ||||
| case DELTA: | case DELTA: | ||||
| /* If the change is smaller, the transition phase is longer */ | /* 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; | break; | ||||
| case CONST: | case CONST: | ||||
| this->step = 1.0/l; | |||||
| this->step = 1.0 / l; | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -149,13 +153,13 @@ struct TransitionSmoother { | |||||
| switch (t) { | switch (t) { | ||||
| case SMOOTHSTEP: | case SMOOTHSTEP: | ||||
| next += delta*x*x*(3-2*x); | |||||
| next += delta * x * x * (3 - 2 * x); | |||||
| break; | break; | ||||
| case EXP: | case EXP: | ||||
| next += delta*x*x; | |||||
| next += delta * x * x; | |||||
| break; | break; | ||||
| case LIN: | case LIN: | ||||
| next += delta*x; | |||||
| next += delta * x; | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| #include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
| /* | /* | ||||
| * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod wheel to | * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod wheel to | ||||
| * CV | * CV | ||||
| @@ -74,7 +74,7 @@ struct MIDIToCVInterface : MidiIO, Module { | |||||
| baseFromJson(rootJ); | baseFromJson(rootJ); | ||||
| } | } | ||||
| void reset() override { | |||||
| void onReset() override { | |||||
| resetMidi(); | resetMidi(); | ||||
| } | } | ||||
| @@ -100,9 +100,8 @@ void MIDIToCVInterface::step() { | |||||
| // midiIn->getMessage returns empty vector if there are no messages in the queue | // midiIn->getMessage returns empty vector if there are no messages in the queue | ||||
| getMessage(&message); | getMessage(&message); | ||||
| while (message.size() > 0) { | |||||
| if (message.size() > 0) { | |||||
| processMidi(message); | processMidi(message); | ||||
| getMessage(&message); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| #include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
| using namespace rack; | using namespace rack; | ||||
| struct TriggerValue { | struct TriggerValue { | ||||
| @@ -63,7 +63,7 @@ struct MIDITriggerToCVInterface : MidiIO, Module { | |||||
| } | } | ||||
| } | } | ||||
| void reset() override { | |||||
| void onReset() override { | |||||
| resetMidi(); | resetMidi(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -75,9 +75,8 @@ void MIDITriggerToCVInterface::step() { | |||||
| // midiIn->getMessage returns empty vector if there are no messages in the queue | // midiIn->getMessage returns empty vector if there are no messages in the queue | ||||
| getMessage(&message); | getMessage(&message); | ||||
| while (message.size() > 0) { | |||||
| if (message.size() > 0) { | |||||
| processMidi(message); | processMidi(message); | ||||
| getMessage(&message); | |||||
| } | } | ||||
| } | } | ||||
| @@ -169,10 +168,12 @@ void TriggerTextField::onTextChange() { | |||||
| text = ""; | text = ""; | ||||
| begin = end = 0; | begin = end = 0; | ||||
| module->trigger[outNum].num = -1; | module->trigger[outNum].num = -1; | ||||
| }else { | |||||
| } | |||||
| else { | |||||
| module->trigger[outNum].num = num; | module->trigger[outNum].num = num; | ||||
| } | } | ||||
| } catch (...) { | |||||
| } | |||||
| catch (...) { | |||||
| text = ""; | text = ""; | ||||
| begin = end = 0; | begin = end = 0; | ||||
| module->trigger[outNum].num = -1; | module->trigger[outNum].num = -1; | ||||
| @@ -271,7 +272,8 @@ MIDITriggerToCVWidget::MIDITriggerToCVWidget() { | |||||
| if ((i + 1) % 4 == 0) { | if ((i + 1) % 4 == 0) { | ||||
| yPos += 47 + margin; | yPos += 47 + margin; | ||||
| } else { | |||||
| } | |||||
| else { | |||||
| yPos -= labelHeight + margin; | yPos -= labelHeight + margin; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,15 +1,16 @@ | |||||
| #include <list> | #include <list> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "rtmidi/RtMidi.h" | |||||
| #include "core.hpp" | #include "core.hpp" | ||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| #include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
| struct MidiKey { | struct MidiKey { | ||||
| int pitch = 60; | int pitch = 60; | ||||
| int at = 0; // aftertouch | int at = 0; // aftertouch | ||||
| int vel = 0; // velocity | int vel = 0; // velocity | ||||
| bool gate = false; | bool gate = false; | ||||
| bool pedal_gate_released = false; | |||||
| }; | }; | ||||
| struct QuadMIDIToCVInterface : MidiIO, Module { | struct QuadMIDIToCVInterface : MidiIO, Module { | ||||
| @@ -72,7 +73,7 @@ struct QuadMIDIToCVInterface : MidiIO, Module { | |||||
| baseFromJson(rootJ); | baseFromJson(rootJ); | ||||
| } | } | ||||
| void reset() override { | |||||
| void onReset() override { | |||||
| resetMidi(); | resetMidi(); | ||||
| } | } | ||||
| @@ -98,16 +99,11 @@ void QuadMIDIToCVInterface::resetMidi() { | |||||
| void QuadMIDIToCVInterface::step() { | void QuadMIDIToCVInterface::step() { | ||||
| if (isPortOpen()) { | if (isPortOpen()) { | ||||
| std::vector<unsigned char> message; | std::vector<unsigned char> message; | ||||
| int msgsProcessed = 0; | |||||
| // midiIn->getMessage returns empty vector if there are no messages in the queue | // 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); | getMessage(&message); | ||||
| while (msgsProcessed < 4 && message.size() > 0) { | |||||
| if (message.size() > 0) { | |||||
| processMidi(message); | processMidi(message); | ||||
| getMessage(&message); | |||||
| msgsProcessed++; | |||||
| } | } | ||||
| } | } | ||||
| @@ -140,49 +136,60 @@ void QuadMIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| return; | return; | ||||
| switch (status) { | 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; | 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++) { | |||||
| } | |||||
| return; | |||||
| case 0xb: // cc | |||||
| if (data1 == 0x40) { // pedal | |||||
| pedal = (data2 >= 64); | |||||
| if (!pedal) { | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (activeKeys[i].pedal_gate_released) { | |||||
| activeKeys[i].gate = false; | activeKeys[i].gate = false; | ||||
| open.push_back(i); | |||||
| activeKeys[i].pedal_gate_released = false; | |||||
| if (std::find(open.begin(), open.end(), i) != open.end()) { | |||||
| open.remove(i); | |||||
| } | |||||
| open.push_front(i); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return; | |||||
| default: | |||||
| return; | |||||
| } | |||||
| return; | |||||
| default: | |||||
| return; | |||||
| } | } | ||||
| if (pedal && !gate) { | if (pedal && !gate) { | ||||
| for (int i = 0; i < 4; i++) { | |||||
| if (activeKeys[i].pitch == data1 && activeKeys[i].gate) { | |||||
| activeKeys[i].pedal_gate_released = true; | |||||
| } | |||||
| } | |||||
| return; | return; | ||||
| } | } | ||||
| if (!gate) { | if (!gate) { | ||||
| for (int i = 0; i < 4; i++) { | 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].gate = false; | ||||
| activeKeys[i].vel = data2; | activeKeys[i].vel = data2; | ||||
| if (std::find(open.begin(), open.end(), i) != open.end()) { | if (std::find(open.begin(), open.end(), i) != open.end()) { | ||||
| @@ -201,25 +208,26 @@ void QuadMIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| } | } | ||||
| if (!activeKeys[0].gate && !activeKeys[1].gate && | if (!activeKeys[0].gate && !activeKeys[1].gate && | ||||
| !activeKeys[2].gate && !activeKeys[3].gate) { | |||||
| !activeKeys[2].gate && !activeKeys[3].gate) { | |||||
| open.sort(); | open.sort(); | ||||
| } | } | ||||
| switch (mode) { | 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) { | |||||
| open.clear(); | |||||
| 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(); | int next = open.front(); | ||||
| @@ -233,10 +241,12 @@ void QuadMIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| open.push_front(i); | open.push_front(i); | ||||
| activeKeys[i].gate = false; | activeKeys[i].gate = false; | ||||
| activeKeys[i].pedal_gate_released = false; | |||||
| } | } | ||||
| } | } | ||||
| activeKeys[next].gate = true; | activeKeys[next].gate = true; | ||||
| activeKeys[next].pedal_gate_released = false; | |||||
| activeKeys[next].pitch = data1; | activeKeys[next].pitch = data1; | ||||
| activeKeys[next].vel = data2; | activeKeys[next].vel = data2; | ||||
| } | } | ||||
| @@ -254,7 +264,7 @@ struct ModeItem : MenuItem { | |||||
| int mode; | int mode; | ||||
| QuadMIDIToCVInterface *module; | QuadMIDIToCVInterface *module; | ||||
| void onAction(EventAction &e) { | |||||
| void onAction(EventAction &e) override { | |||||
| module->setMode(mode); | module->setMode(mode); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -264,7 +274,7 @@ struct ModeChoice : ChoiceButton { | |||||
| const std::vector<std::string> modeNames = {"ROTATE", "RESET", "REASSIGN"}; | const std::vector<std::string> modeNames = {"ROTATE", "RESET", "REASSIGN"}; | ||||
| void onAction(EventAction &e) { | |||||
| void onAction(EventAction &e) override { | |||||
| Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | ||||
| menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
| @@ -274,11 +284,11 @@ struct ModeChoice : ChoiceButton { | |||||
| modeItem->mode = i; | modeItem->mode = i; | ||||
| modeItem->module = module; | modeItem->module = module; | ||||
| modeItem->text = modeNames[i]; | modeItem->text = modeNames[i]; | ||||
| menu->pushChild(modeItem); | |||||
| menu->addChild(modeItem); | |||||
| } | } | ||||
| } | } | ||||
| void step() { | |||||
| void step() override { | |||||
| text = modeNames[module->getMode()]; | text = modeNames[module->getMode()]; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -313,7 +323,7 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { | |||||
| } | } | ||||
| addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, | addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, | ||||
| 0.0)); | |||||
| 0.0)); | |||||
| addChild(createLight<SmallLight<RedLight>>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT)); | addChild(createLight<SmallLight<RedLight>>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT)); | ||||
| { | { | ||||
| Label *label = new Label(); | Label *label = new Label(); | ||||
| @@ -11,7 +11,6 @@ | |||||
| #include "engine.hpp" | #include "engine.hpp" | ||||
| #include "util.hpp" | #include "util.hpp" | ||||
| namespace rack { | namespace rack { | ||||
| float sampleRate; | float sampleRate; | ||||
| @@ -33,7 +32,6 @@ static Module *smoothModule = NULL; | |||||
| static int smoothParamId; | static int smoothParamId; | ||||
| static float smoothValue; | static float smoothValue; | ||||
| float Light::getBrightness() { | float Light::getBrightness() { | ||||
| return sqrtf(fmaxf(0.0, value)); | return sqrtf(fmaxf(0.0, value)); | ||||
| } | } | ||||
| @@ -76,11 +74,13 @@ static void engineStep() { | |||||
| float delta = smoothValue - value; | float delta = smoothValue - value; | ||||
| if (fabsf(delta) < snap) { | if (fabsf(delta) < snap) { | ||||
| smoothModule->params[smoothParamId].value = smoothValue; | smoothModule->params[smoothParamId].value = smoothValue; | ||||
| smoothModule->params[smoothParamId].ossia_param->push_value(smoothValue); | |||||
| smoothModule = NULL; | smoothModule = NULL; | ||||
| } | } | ||||
| else { | else { | ||||
| value += delta * lambda * sampleTime; | value += delta * lambda * sampleTime; | ||||
| smoothModule->params[smoothParamId].value = value; | smoothModule->params[smoothParamId].value = value; | ||||
| smoothModule->params[smoothParamId].ossia_param->push_value(value); | |||||
| } | } | ||||
| } | } | ||||
| @@ -240,6 +240,7 @@ void engineRemoveWire(Wire *wire) { | |||||
| void engineSetParam(Module *module, int paramId, float value) { | void engineSetParam(Module *module, int paramId, float value) { | ||||
| module->params[paramId].value = value; | module->params[paramId].value = value; | ||||
| module->params[paramId].ossia_param->push_value(value); | |||||
| } | } | ||||
| void engineSetParamSmooth(Module *module, int paramId, float value) { | void engineSetParamSmooth(Module *module, int paramId, float value) { | ||||
| @@ -248,10 +249,12 @@ void engineSetParamSmooth(Module *module, int paramId, float value) { | |||||
| // Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state | // Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state | ||||
| if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) { | if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) { | ||||
| smoothModule->params[smoothParamId].value = smoothValue; | smoothModule->params[smoothParamId].value = smoothValue; | ||||
| smoothModule->params[smoothParamId].ossia_param->push_value(smoothValue); | |||||
| } | } | ||||
| smoothModule = module; | smoothModule = module; | ||||
| smoothParamId = paramId; | smoothParamId = paramId; | ||||
| smoothValue = value; | smoothValue = value; | ||||
| module->params[paramId].ossia_param->push_value(smoothValue); | |||||
| } | } | ||||
| void engineSetSampleRate(float newSampleRate) { | void engineSetSampleRate(float newSampleRate) { | ||||
| @@ -8,8 +8,10 @@ | |||||
| #include "../ext/osdialog/osdialog.h" | #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" | #include "../ext/nanovg/src/nanovg_gl.h" | ||||
| // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported) | // 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 | ||||
| @@ -31,7 +33,8 @@ GLFWwindow *gWindow = NULL; | |||||
| NVGcontext *gVg = NULL; | NVGcontext *gVg = NULL; | ||||
| NVGcontext *gFramebufferVg = NULL; | NVGcontext *gFramebufferVg = NULL; | ||||
| std::shared_ptr<Font> gGuiFont; | std::shared_ptr<Font> gGuiFont; | ||||
| float gPixelRatio = 0.0; | |||||
| float gPixelRatio = 1.0; | |||||
| float gWindowRatio = 1.0; | |||||
| bool gAllowCursorLock = true; | bool gAllowCursorLock = true; | ||||
| int gGuiFrame; | int gGuiFrame; | ||||
| Vec gMousePos; | Vec gMousePos; | ||||
| @@ -40,7 +43,6 @@ std::string lastWindowTitle; | |||||
| void windowSizeCallback(GLFWwindow* window, int width, int height) { | void windowSizeCallback(GLFWwindow* window, int width, int height) { | ||||
| gScene->box.size = Vec(width, height); | |||||
| } | } | ||||
| void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | ||||
| @@ -143,14 +145,16 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m | |||||
| } | } | ||||
| void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | 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); | Vec mouseRel = mousePos.minus(gMousePos); | ||||
| int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR); | |||||
| (void) cursorMode; | |||||
| #ifdef ARCH_MAC | #ifdef ARCH_MAC | ||||
| // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own. | // 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. | // 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); | // CGSetLocalEventsSuppressionInterval(0.0); | ||||
| glfwSetCursorPos(gWindow, gMousePos.x, gMousePos.y); | glfwSetCursorPos(gWindow, gMousePos.x, gMousePos.y); | ||||
| CGAssociateMouseAndMouseCursorPosition(true); | CGAssociateMouseAndMouseCursorPosition(true); | ||||
| @@ -311,12 +315,15 @@ void guiInit() { | |||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| #if defined NANOVG_GL2 | |||||
| glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | ||||
| glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); | 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_MAXIMIZED, GLFW_TRUE); | ||||
| glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); | glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); | ||||
| lastWindowTitle = ""; | lastWindowTitle = ""; | ||||
| @@ -332,6 +339,7 @@ void guiInit() { | |||||
| glfwSetWindowSizeCallback(gWindow, windowSizeCallback); | glfwSetWindowSizeCallback(gWindow, windowSizeCallback); | ||||
| glfwSetMouseButtonCallback(gWindow, mouseButtonStickyCallback); | glfwSetMouseButtonCallback(gWindow, mouseButtonStickyCallback); | ||||
| // Call this ourselves, but on every frame instead of only when the mouse moves | |||||
| // glfwSetCursorPosCallback(gWindow, cursorPosCallback); | // glfwSetCursorPosCallback(gWindow, cursorPosCallback); | ||||
| glfwSetCursorEnterCallback(gWindow, cursorEnterCallback); | glfwSetCursorEnterCallback(gWindow, cursorEnterCallback); | ||||
| glfwSetScrollCallback(gWindow, scrollCallback); | glfwSetScrollCallback(gWindow, scrollCallback); | ||||
| @@ -353,11 +361,22 @@ void guiInit() { | |||||
| glfwSetWindowSizeLimits(gWindow, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); | glfwSetWindowSizeLimits(gWindow, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); | ||||
| // Set up NanoVG | // Set up NanoVG | ||||
| #if defined NANOVG_GL2 | |||||
| gVg = nvgCreateGL2(NVG_ANTIALIAS); | 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); | assert(gVg); | ||||
| #if defined NANOVG_GL2 | |||||
| gFramebufferVg = nvgCreateGL2(NVG_ANTIALIAS); | gFramebufferVg = nvgCreateGL2(NVG_ANTIALIAS); | ||||
| #elif defined NANOVG_GL3 | |||||
| gFramebufferVg = nvgCreateGL3(NVG_ANTIALIAS); | |||||
| #elif defined NANOVG_GLES2 | |||||
| gFramebufferVg = nvgCreateGLES2(NVG_ANTIALIAS); | |||||
| #endif | |||||
| assert(gFramebufferVg); | assert(gFramebufferVg); | ||||
| // Set up Blendish | // Set up Blendish | ||||
| @@ -375,20 +394,29 @@ void guiInit() { | |||||
| void guiDestroy() { | void guiDestroy() { | ||||
| gGuiFont.reset(); | gGuiFont.reset(); | ||||
| #if defined NANOVG_GL2 | |||||
| nvgDeleteGL2(gVg); | nvgDeleteGL2(gVg); | ||||
| // nvgDeleteGL3(gVg); | |||||
| #elif defined NANOVG_GL3 | |||||
| nvgDeleteGL3(gVg); | |||||
| #elif defined NANOVG_GLES2 | |||||
| nvgDeleteGLES2(gVg); | |||||
| #endif | |||||
| #if defined NANOVG_GL2 | |||||
| nvgDeleteGL2(gFramebufferVg); | nvgDeleteGL2(gFramebufferVg); | ||||
| #elif defined NANOVG_GL3 | |||||
| nvgDeleteGL3(gFramebufferVg); | |||||
| #elif defined NANOVG_GLES2 | |||||
| nvgDeleteGLES2(gFramebufferVg); | |||||
| #endif | |||||
| glfwDestroyWindow(gWindow); | glfwDestroyWindow(gWindow); | ||||
| glfwTerminate(); | glfwTerminate(); | ||||
| } | } | ||||
| void guiRun() { | void guiRun() { | ||||
| assert(gWindow); | assert(gWindow); | ||||
| { | |||||
| int width, height; | |||||
| glfwGetWindowSize(gWindow, &width, &height); | |||||
| windowSizeCallback(gWindow, width, height); | |||||
| } | |||||
| gGuiFrame = 0; | gGuiFrame = 0; | ||||
| while(!glfwWindowShouldClose(gWindow)) { | while(!glfwWindowShouldClose(gWindow)) { | ||||
| double startTime = glfwGetTime(); | double startTime = glfwGetTime(); | ||||
| @@ -417,18 +445,25 @@ void guiRun() { | |||||
| lastWindowTitle = windowTitle; | lastWindowTitle = windowTitle; | ||||
| } | } | ||||
| // Get framebuffer size | |||||
| int width, height; | |||||
| glfwGetFramebufferSize(gWindow, &width, &height); | |||||
| int windowWidth, windowHeight; | |||||
| glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); | |||||
| float pixelRatio = (float)width / windowWidth; | |||||
| // Get desired scaling | |||||
| float pixelRatio; | |||||
| glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); | |||||
| pixelRatio = roundf(pixelRatio); | |||||
| if (pixelRatio != gPixelRatio) { | if (pixelRatio != gPixelRatio) { | ||||
| EventZoom eZoom; | EventZoom eZoom; | ||||
| gScene->onZoom(eZoom); | gScene->onZoom(eZoom); | ||||
| gPixelRatio = pixelRatio; | 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 | // Step scene | ||||
| gScene->step(); | gScene->step(); | ||||
| @@ -32,52 +32,6 @@ namespace rack { | |||||
| std::list<Plugin*> gPlugins; | std::list<Plugin*> gPlugins; | ||||
| std::string gToken; | 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 bool isDownloading = false; | ||||
| static float downloadProgress = 0.0; | static float downloadProgress = 0.0; | ||||
| @@ -145,6 +99,16 @@ static int loadPlugin(std::string path) { | |||||
| plugin->handle = handle; | plugin->handle = handle; | ||||
| initCallback(plugin); | 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 | // Add plugin to list | ||||
| gPlugins.push_back(plugin); | gPlugins.push_back(plugin); | ||||
| info("Loaded plugin %s", libraryFilename.c_str()); | info("Loaded plugin %s", libraryFilename.c_str()); | ||||
| @@ -410,6 +374,7 @@ bool pluginSync(bool dryRun) { | |||||
| //////////////////// | //////////////////// | ||||
| void pluginInit() { | void pluginInit() { | ||||
| tagsInit(); | |||||
| // Load core | // Load core | ||||
| // This function is defined in core.cpp | // This function is defined in core.cpp | ||||
| Plugin *coreManufacturer = new Plugin(); | Plugin *coreManufacturer = new Plugin(); | ||||
| @@ -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 | |||||
| @@ -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` | // deletes `this` | ||||
| gScene->setOverlay(NULL); | gScene->setOverlay(NULL); | ||||
| e.consumed = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -59,16 +59,19 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
| for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { | 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]);) | 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)) | if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | ||||
| continue; | continue; | ||||
| nvgSave(vg); | nvgSave(vg); | ||||
| // Opacity | |||||
| if (shape->opacity < 1.0) | if (shape->opacity < 1.0) | ||||
| nvgGlobalAlpha(vg, shape->opacity); | nvgGlobalAlpha(vg, shape->opacity); | ||||
| // Build path | // Build path | ||||
| nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
| // Iterate path linked list | // Iterate path linked list | ||||
| for (NSVGpath *path = shape->paths; path; path = path->next) { | 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]);) | 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]);) | DEBUG_ONLY(printf(" bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);) | ||||
| } | } | ||||
| // Close path | |||||
| if (path->closed) | if (path->closed) | ||||
| nvgClosePath(vg); | nvgClosePath(vg); | ||||
| @@ -161,11 +165,12 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
| } | } | ||||
| // Stroke shape | // Stroke shape | ||||
| nvgStrokeWidth(vg, shape->strokeWidth); | |||||
| // strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||||
| // strokeLineJoin, strokeLineCap not yet supported | |||||
| if (shape->stroke.type) { | 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) { | switch (shape->stroke.type) { | ||||
| case NSVG_PAINT_COLOR: { | case NSVG_PAINT_COLOR: { | ||||
| NVGcolor color = getNVGColor(shape->stroke.color); | NVGcolor color = getNVGColor(shape->stroke.color); | ||||
| @@ -65,5 +65,12 @@ void ZoomWidget::onScroll(EventScroll &e) { | |||||
| e.pos = pos; | 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 | } // namespace rack | ||||