@@ -1,5 +1,6 @@ | |||
/Rack | |||
/Rack.exe | |||
/libRack.a | |||
/autosave.json | |||
/settings.json | |||
/plugins | |||
@@ -10,3 +10,9 @@ | |||
[submodule "ext/oui-blendish"] | |||
path = ext/oui-blendish | |||
url = https://github.com/AndrewBelt/oui-blendish.git | |||
[submodule "dep/glfw"] | |||
path = dep/glfw | |||
url = https://github.com/glfw/glfw.git | |||
[submodule "dep/rtaudio"] | |||
path = dep/rtaudio | |||
url = https://github.com/thestk/rtaudio.git |
@@ -180,17 +180,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
# portaudio | |||
# RtAudio | |||
PortAudio Portable Real-Time Audio Library | |||
Copyright (c) 1999-2011 Ross Bencina and Phil Burk | |||
RtAudio: a set of realtime audio i/o C++ classes | |||
Copyright (c) 2001-2017 Gary P. Scavone | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
Permission is hereby granted, free of charge, to any person | |||
obtaining a copy of this software and associated documentation files | |||
(the "Software"), to deal in the Software without restriction, | |||
including without limitation the rights to use, copy, modify, merge, | |||
publish, distribute, sublicense, and/or sell copies of the Software, | |||
and to permit persons to whom the Software is furnished to do so, | |||
subject to the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |||
Any person wishing to distribute modifications to the Software is | |||
asked to send the modifications to the original developer so that | |||
they can be incorporated into the canonical version. This is, | |||
however, not a binding provision of this license. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
# RtMidi | |||
@@ -69,8 +69,14 @@ ifeq ($(ARCH), win) | |||
env PATH=dep/bin:/mingw64/bin gdb -ex run ./Rack | |||
endif | |||
perf: $(TARGET) | |||
ifeq ($(ARCH), lin) | |||
LD_LIBRARY_PATH=dep/lib perf record --call-graph dwarf ./Rack | |||
endif | |||
clean: | |||
rm -fv libRack.a | |||
rm -rfv $(TARGET) build dist | |||
# For Windows resources | |||
@@ -106,9 +112,8 @@ ifeq ($(ARCH), mac) | |||
cp dep/lib/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/ | |||
cp dep/lib/librtaudio.dylib $(BUNDLE)/Contents/MacOS/ | |||
install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
@@ -116,9 +121,8 @@ ifeq ($(ARCH), mac) | |||
install_name_tool -change $(PWD)/dep/lib/libsamplerate.0.dylib @executable_path/libsamplerate.0.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change $(PWD)/dep/lib/libportaudio.2.dylib @executable_path/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change $(PWD)/dep/lib/librtmidi.4.dylib @executable_path/librtmidi.4.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change $(PWD)/dep/lib/librtaudio.6.dylib @executable_path/librtaudio.6.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
install_name_tool -change librtaudio.dylib @executable_path/librtaudio.dylib $(BUNDLE)/Contents/MacOS/Rack | |||
otool -L $(BUNDLE)/Contents/MacOS/Rack | |||
@@ -51,6 +51,8 @@ You may use make's `-j$(nproc)` flag to parallelize builds across all your CPU c | |||
make dep | |||
You may use `make dep RTAUDIO_ALL_APIS=1` to attempt to build with all audio driver APIs enabled for your operating system. | |||
You should see a message that all dependencies built successfully. | |||
Build Rack. | |||
@@ -86,17 +86,11 @@ $(glew): | |||
$(MAKE) -C glew-2.1.0 GLEW_DEST="$(LOCAL)" LIBDIR="$(LOCAL)/lib" install | |||
$(glfw): | |||
$(WGET) https://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.zip | |||
$(UNZIP) glfw-3.2.1.zip | |||
cd glfw-3.2.1 && $(CMAKE) . \ | |||
cd glfw && $(CMAKE) . \ | |||
-DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DBUILD_SHARED_LIBS=ON \ | |||
-DGLFW_USE_CHDIR=OFF -DGLFW_USE_MENUBAR=ON -DGLFW_USE_RETINA=ON | |||
$(MAKE) -C glfw-3.2.1 | |||
$(MAKE) -C glfw-3.2.1 install | |||
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): | |||
$(WGET) http://www.digip.org/jansson/releases/jansson-2.10.tar.gz | |||
@@ -136,7 +130,6 @@ $(rtmidi): | |||
$(MAKE) -C rtmidi-3.0.0 install | |||
$(rtaudio): | |||
git clone https://github.com/thestk/rtaudio.git | |||
cd rtaudio && mkdir -p cmakebuild | |||
cd rtaudio/cmakebuild && cmake -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX="$(LOCAL)" $(RTAUDIO_FLAGS) .. | |||
$(MAKE) -C rtaudio/cmakebuild | |||
@@ -0,0 +1 @@ | |||
Subproject commit 30489c5aa1392f698e904c6aced1d7a486634c40 |
@@ -0,0 +1 @@ | |||
Subproject commit ce13dfbf30fd1ab4e7f7eff8886a80f144c75e5d |
@@ -51,6 +51,11 @@ struct SlewLimiter { | |||
float rise = 1.0; | |||
float fall = 1.0; | |||
float out = 0.0; | |||
void setRiseFall(float _rise, float _fall) { | |||
rise = _rise; | |||
fall = _fall; | |||
} | |||
float process(float in) { | |||
float delta = clampf(in - out, -fall, rise); | |||
out += delta; | |||
@@ -17,8 +17,14 @@ namespace rack { | |||
extern GLFWwindow *gWindow; | |||
extern NVGcontext *gVg; | |||
extern NVGcontext *gFramebufferVg; | |||
/** The default font to use for GUI elements */ | |||
extern std::shared_ptr<Font> gGuiFont; | |||
/** The scaling ratio */ | |||
extern float gPixelRatio; | |||
/* The ratio between the framebuffer size and the window size reported by the OS. | |||
This is not equal to gPixelRatio in general. | |||
*/ | |||
extern float gWindowRatio; | |||
extern bool gAllowCursorLock; | |||
extern int gGuiFrame; | |||
extern Vec gMousePos; | |||
@@ -295,7 +295,7 @@ struct Label : Widget { | |||
/** Deletes itself from parent when clicked */ | |||
struct MenuOverlay : OpaqueWidget { | |||
void step() override; | |||
void onDragDrop(EventDragDrop &e) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
}; | |||
@@ -60,8 +60,11 @@ void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) { | |||
json_t *ModuleWidget::toJson() { | |||
json_t *rootJ = json_object(); | |||
// manufacturer | |||
// plugin | |||
json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); | |||
// version (of plugin) | |||
if (!model->plugin->version.empty()) | |||
json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); | |||
// model | |||
json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | |||
// pos | |||
@@ -23,7 +23,7 @@ void SVGSlider::step() { | |||
void SVGSlider::onChange(EventChange &e) { | |||
dirty = true; | |||
ParamWidget::onChange(e); | |||
Knob::onChange(e); | |||
} | |||
@@ -151,12 +151,13 @@ Toolbar::Toolbar() { | |||
struct ZoomSlider : Slider { | |||
void onAction(EventAction &e) override { | |||
Slider::onAction(e); | |||
gRackScene->zoomWidget->setZoom(value / 100.0); | |||
gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); | |||
} | |||
}; | |||
zoomSlider = new ZoomSlider(); | |||
zoomSlider->box.pos = Vec(xPos, margin); | |||
zoomSlider->box.size.x = 150; | |||
zoomSlider->precision = 0; | |||
zoomSlider->label = "Zoom"; | |||
zoomSlider->unit = "%"; | |||
zoomSlider->setLimits(25.0, 200.0); | |||
@@ -110,7 +110,7 @@ void MIDITriggerToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||
if (status == 0x8) { // note off | |||
for (int i = 0; i < NUM_OUTPUTS; i++) { | |||
if (data1 == trigger[i].num) { | |||
trigger[i].val = data2; | |||
trigger[i].val = 0; | |||
} | |||
} | |||
return; | |||
@@ -8,8 +8,10 @@ | |||
#include "../ext/osdialog/osdialog.h" | |||
#define NANOVG_GL2_IMPLEMENTATION | |||
// #define NANOVG_GL3_IMPLEMENTATION | |||
#define NANOVG_GL2 1 | |||
// #define NANOVG_GL3 1 | |||
// #define NANOVG_GLES2 1 | |||
#define NANOVG_GL_IMPLEMENTATION 1 | |||
#include "../ext/nanovg/src/nanovg_gl.h" | |||
// Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported) | |||
#define NANOVG_FBO_VALID 1 | |||
@@ -31,7 +33,8 @@ GLFWwindow *gWindow = NULL; | |||
NVGcontext *gVg = NULL; | |||
NVGcontext *gFramebufferVg = NULL; | |||
std::shared_ptr<Font> gGuiFont; | |||
float gPixelRatio = 0.0; | |||
float gPixelRatio = 1.0; | |||
float gWindowRatio = 1.0; | |||
bool gAllowCursorLock = true; | |||
int gGuiFrame; | |||
Vec gMousePos; | |||
@@ -40,7 +43,6 @@ std::string lastWindowTitle; | |||
void windowSizeCallback(GLFWwindow* window, int width, int height) { | |||
gScene->box.size = Vec(width, height); | |||
} | |||
void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
@@ -143,7 +145,7 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m | |||
} | |||
void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
Vec mousePos = Vec(xpos, ypos).round(); | |||
Vec mousePos = Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round(); | |||
Vec mouseRel = mousePos.minus(gMousePos); | |||
#ifdef ARCH_MAC | |||
@@ -353,11 +355,22 @@ void guiInit() { | |||
glfwSetWindowSizeLimits(gWindow, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); | |||
// Set up NanoVG | |||
#if defined NANOVG_GL2 | |||
gVg = nvgCreateGL2(NVG_ANTIALIAS); | |||
// gVg = nvgCreateGL3(NVG_ANTIALIAS); | |||
#elif defined NANOVG_GL3 | |||
gVg = nvgCreateGL3(NVG_ANTIALIAS); | |||
#elif defined NANOVG_GLES2 | |||
gVg = nvgCreateGLES2(NVG_ANTIALIAS); | |||
#endif | |||
assert(gVg); | |||
#if defined NANOVG_GL2 | |||
gFramebufferVg = nvgCreateGL2(NVG_ANTIALIAS); | |||
#elif defined NANOVG_GL3 | |||
gFramebufferVg = nvgCreateGL3(NVG_ANTIALIAS); | |||
#elif defined NANOVG_GLES2 | |||
gFramebufferVg = nvgCreateGLES2(NVG_ANTIALIAS); | |||
#endif | |||
assert(gFramebufferVg); | |||
// Set up Blendish | |||
@@ -375,20 +388,29 @@ void guiInit() { | |||
void guiDestroy() { | |||
gGuiFont.reset(); | |||
#if defined NANOVG_GL2 | |||
nvgDeleteGL2(gVg); | |||
// nvgDeleteGL3(gVg); | |||
#elif defined NANOVG_GL3 | |||
nvgDeleteGL3(gVg); | |||
#elif defined NANOVG_GLES2 | |||
nvgDeleteGLES2(gVg); | |||
#endif | |||
#if defined NANOVG_GL2 | |||
nvgDeleteGL2(gFramebufferVg); | |||
#elif defined NANOVG_GL3 | |||
nvgDeleteGL3(gFramebufferVg); | |||
#elif defined NANOVG_GLES2 | |||
nvgDeleteGLES2(gFramebufferVg); | |||
#endif | |||
glfwDestroyWindow(gWindow); | |||
glfwTerminate(); | |||
} | |||
void guiRun() { | |||
assert(gWindow); | |||
{ | |||
int width, height; | |||
glfwGetWindowSize(gWindow, &width, &height); | |||
windowSizeCallback(gWindow, width, height); | |||
} | |||
gGuiFrame = 0; | |||
while(!glfwWindowShouldClose(gWindow)) { | |||
double startTime = glfwGetTime(); | |||
@@ -417,18 +439,25 @@ void guiRun() { | |||
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) { | |||
EventZoom eZoom; | |||
gScene->onZoom(eZoom); | |||
gPixelRatio = pixelRatio; | |||
} | |||
// Get framebuffer/window ratio | |||
int width, height; | |||
glfwGetFramebufferSize(gWindow, &width, &height); | |||
int windowWidth, windowHeight; | |||
glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); | |||
gWindowRatio = (float)width / windowWidth; | |||
gScene->box.size = Vec(width, height).div(gPixelRatio / gWindowRatio); | |||
// Step scene | |||
gScene->step(); | |||
@@ -144,6 +144,16 @@ static int loadPlugin(std::string path) { | |||
plugin->handle = handle; | |||
initCallback(plugin); | |||
// Reject plugin if slug already exists | |||
for (Plugin *p : gPlugins) { | |||
if (plugin->slug == p->slug) { | |||
warn("Plugin \"%s\" is already loaded, not attempting to load it again"); | |||
// TODO | |||
// Fix memory leak with `plugin` here | |||
return -1; | |||
} | |||
} | |||
// Add plugin to list | |||
gPlugins.push_back(plugin); | |||
info("Loaded plugin %s", libraryFilename.c_str()); | |||
@@ -13,10 +13,12 @@ void MenuOverlay::step() { | |||
} | |||
} | |||
void MenuOverlay::onDragDrop(EventDragDrop &e) { | |||
if (e.origin == this) { | |||
void MenuOverlay::onMouseDown(EventMouseDown &e) { | |||
Widget::onMouseDown(e); | |||
if (!e.consumed) { | |||
// deletes `this` | |||
gScene->setOverlay(NULL); | |||
e.consumed = true; | |||
} | |||
} | |||
@@ -59,16 +59,19 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { | |||
DEBUG_ONLY(printf(" new shape: %d id \"%s\", fillrule %d, from (%f, %f) to (%f, %f)\n", shapeIndex, shape->id, shape->fillRule, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);) | |||
// Visibility | |||
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | |||
continue; | |||
nvgSave(vg); | |||
// Opacity | |||
if (shape->opacity < 1.0) | |||
nvgGlobalAlpha(vg, shape->opacity); | |||
// Build path | |||
nvgBeginPath(vg); | |||
// Iterate path linked list | |||
for (NSVGpath *path = shape->paths; path; path = path->next) { | |||
DEBUG_ONLY(printf(" new path: %d points, %s, from (%f, %f) to (%f, %f)\n", path->npts, path->closed ? "closed" : "open", path->bounds[0], path->bounds[1], path->bounds[2], path->bounds[3]);) | |||
@@ -81,6 +84,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
DEBUG_ONLY(printf(" bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);) | |||
} | |||
// Close path | |||
if (path->closed) | |||
nvgClosePath(vg); | |||
@@ -161,11 +165,12 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
} | |||
// Stroke shape | |||
nvgStrokeWidth(vg, shape->strokeWidth); | |||
// strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||
// strokeLineJoin, strokeLineCap not yet supported | |||
if (shape->stroke.type) { | |||
nvgStrokeWidth(vg, shape->strokeWidth); | |||
// strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||
nvgLineCap(vg, (NVGlineCap) shape->strokeLineCap); | |||
nvgLineJoin(vg, (int) shape->strokeLineJoin); | |||
switch (shape->stroke.type) { | |||
case NSVG_PAINT_COLOR: { | |||
NVGcolor color = getNVGColor(shape->stroke.color); | |||