Browse Source

Merge branch 'master' of https://github.com/VCVRack/Rack

tags/v0.5.0
ben 7 years ago
parent
commit
61ada9bc4b
18 changed files with 186 additions and 105 deletions
  1. +1
    -1
      Makefile
  2. +1
    -1
      README.md
  3. +0
    -1
      compile.mk
  4. +3
    -3
      include/asset.hpp
  5. +21
    -15
      include/plugin.hpp
  6. +3
    -6
      include/rack.hpp
  7. +1
    -0
      include/widgets.hpp
  8. +2
    -2
      plugin.mk
  9. +3
    -3
      src/app/ModuleWidget.cpp
  10. +3
    -2
      src/app/RackScene.cpp
  11. +46
    -32
      src/app/RackWidget.cpp
  12. +1
    -1
      src/core/MidiClockToCV.cpp
  13. +44
    -0
      src/core/Notes.cpp
  14. +10
    -11
      src/core/core.cpp
  15. +7
    -0
      src/core/core.hpp
  16. +17
    -27
      src/plugin.cpp
  17. +15
    -0
      src/widgets/FramebufferWidget.cpp
  18. +8
    -0
      src/widgets/TextField.cpp

+ 1
- 1
Makefile View File

@@ -75,7 +75,7 @@ include compile.mk

dist: all
ifndef VERSION
$(error VERSION must be defined when calling make)
$(error VERSION must be defined when making distributables)
endif
rm -rf dist
$(MAKE) -C plugins/Fundamental dist


+ 1
- 1
README.md View File

@@ -9,7 +9,7 @@ This README includes instructions for building Rack from source. For information
## The [Issue Tracker](https://github.com/VCVRack/Rack/issues) *is* the official developer's forum

Bug reports, feature requests, and even *questions/discussions* are welcome on the GitHub Issue Tracker for all VCVRack repos.
However, please search before posting to avoid duplicates.
However, please search before posting to avoid duplicates, and limit to one issue per post.

You may vote on feature requests by using the Thumbs Up/Down reaction on the first post.



+ 0
- 1
compile.mk View File

@@ -42,7 +42,6 @@ DEPS = $(patsubst %, build/%.d, $(SOURCES))
$(TARGET): $(OBJECTS)
$(CXX) -o $@ $^ $(LDFLAGS)

# Object targets

-include $(DEPS)



+ 3
- 3
include/asset.hpp View File

@@ -7,13 +7,13 @@
namespace rack {


/** Searches for a global read-only resource and returns its path, or "" if not found
/** Returns the path of a global resource. Read-only
*/
std::string assetGlobal(std::string filename);
/** Searches for a local resource
/** Returns the path of a local resource. Read/write
*/
std::string assetLocal(std::string filename);
/** Searches for a plugin resource, given a Plugin object
/** Returns the path of a resource in the plugin's folder. Read-only
*/
std::string assetPlugin(Plugin *plugin, std::string filename);



+ 21
- 15
include/plugin.hpp View File

@@ -11,32 +11,37 @@ struct Model;

// Subclass this and return a pointer to a new one when init() is called
struct Plugin {
virtual ~Plugin();

/** A unique identifier for your plugin, e.g. "foo" */
std::string slug;
/** Human readable name for your plugin, e.g. "Foo Modular" */
std::string name;
/** A list of the models made available by this plugin */
/** A list of the models available by this plugin, add with addModel() */
std::list<Model*> models;
/** The file path of the plugins directory */
/** The file path of the plugin's directory */
std::string path;
/** OS-dependent library handle */
void *handle = NULL;
/** Optional metadata for the Add Module context menu */
std::string homepageUrl;
std::string manualUrl;

/** Used when syncing plugins with the API */
std::string slug;
/** The version of your plugin
Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266
Do not include the "v" in "v1.0" for example.
*/
std::string version;

virtual ~Plugin();
void addModel(Model *model);
};

struct Model {
virtual ~Model() {}

Plugin *plugin;
/** A unique identifier for the model in this plugin, e.g. "VCO" */
Plugin *plugin = NULL;
/** An identifier for the model, e.g. "VCO". Used for saving patches. The slug, manufacturerSlug pair must be unique. */
std::string slug;
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */
std::string name;
/** An identifier for the manufacturer, e.g. "foo". Used for saving patches. */
std::string manufacturerSlug;
/** Human readable name for the manufacturer, e.g. "Foo Modular" */
std::string manufacturerName;

virtual ~Model() {}
virtual ModuleWidget *createModuleWidget() { return NULL; }
};

@@ -63,6 +68,7 @@ std::string pluginGetLoginStatus();
////////////////////

/** Called once to initialize and return the Plugin instance.
You must implement this in your plugin
*/
extern "C"
void init(rack::Plugin *plugin);

+ 3
- 6
include/rack.hpp View File

@@ -18,7 +18,7 @@ namespace rack {
////////////////////

template <class TModuleWidget>
Model *createModel(Plugin *plugin, std::string slug, std::string name) {
Model *createModel(std::string manufacturerSlug, std::string manufacturerName, std::string slug, std::string name) {
struct TModel : Model {
ModuleWidget *createModuleWidget() override {
ModuleWidget *moduleWidget = new TModuleWidget();
@@ -27,13 +27,10 @@ Model *createModel(Plugin *plugin, std::string slug, std::string name) {
}
};
Model *model = new TModel();
model->plugin = plugin;
model->slug = slug;
model->name = name;
// Create bi-directional association between the Plugin and Model
if (plugin) {
plugin->models.push_back(model);
}
model->manufacturerSlug = manufacturerSlug;
model->manufacturerName = manufacturerName;
return model;
}



+ 1
- 0
include/widgets.hpp View File

@@ -399,6 +399,7 @@ struct ZoomWidget : Widget {
struct TextField : OpaqueWidget {
std::string text;
std::string placeholder;
bool multiline = false;
int begin = 0;
int end = 0;



+ 2
- 2
plugin.mk View File

@@ -24,7 +24,7 @@ endif

all: $(TARGET)

include ../../compile.mk

clean:
rm -rfv build $(TARGET) dist

include ../../compile.mk

+ 3
- 3
src/app/ModuleWidget.cpp View File

@@ -45,8 +45,8 @@ void ModuleWidget::addParam(ParamWidget *param) {
json_t *ModuleWidget::toJson() {
json_t *rootJ = json_object();

// plugin
json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str()));
// manufacturer
json_object_set_new(rootJ, "manufacturer", json_string(model->manufacturerSlug.c_str()));
// model
json_object_set_new(rootJ, "model", json_string(model->slug.c_str()));
// pos
@@ -245,7 +245,7 @@ Menu *ModuleWidget::createContextMenu() {
Menu *menu = gScene->createMenu();

MenuLabel *menuLabel = new MenuLabel();
menuLabel->text = model->plugin->name + ": " + model->name;
menuLabel->text = model->manufacturerName + " " + model->name;
menu->pushChild(menuLabel);

ResetMenuItem *resetItem = new ResetMenuItem();


+ 3
- 2
src/app/RackScene.cpp View File

@@ -2,6 +2,7 @@
#include "gui.hpp"
#include "util/request.hpp"
#include "../ext/osdialog/osdialog.h"
#include <string.h>
#include <thread>


@@ -17,7 +18,7 @@ static void checkVersion() {
json_t *versionJ = json_object_get(resJ, "version");
if (versionJ) {
const char *version = json_string_value(versionJ);
if (version && version != gApplicationVersion) {
if (version && strlen(version) > 0 && version != gApplicationVersion) {
newVersion = version;
}
}
@@ -45,7 +46,7 @@ RackScene::RackScene() {
scrollWidget->box.pos.y = gToolbar->box.size.y;

// Check for new version
if (gApplicationVersion != "dev") {
if (!gApplicationVersion.empty()) {
std::thread versionThread(checkVersion);
versionThread.detach();
}


+ 46
- 32
src/app/RackWidget.cpp View File

@@ -1,12 +1,13 @@
#include <map>
#include <algorithm>
#include <thread>
#include "app.hpp"
#include "engine.hpp"
#include "plugin.hpp"
#include "gui.hpp"
#include "settings.hpp"
#include "asset.hpp"
#include <map>
#include <algorithm>
#include <thread>
#include <set>
#include "../ext/osdialog/osdialog.h"


@@ -200,36 +201,29 @@ void RackWidget::fromJson(json_t *rootJ) {
size_t moduleId;
json_t *moduleJ;
json_array_foreach(modulesJ, moduleId, moduleJ) {
json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
if (!pluginSlugJ) continue;
json_t *manufacturerSlugJ = json_object_get(moduleJ, "manufacturer");
if (!manufacturerSlugJ) {
// Backward compatibility with Rack v0.4 and lower
manufacturerSlugJ = json_object_get(moduleJ, "plugin");
if (!manufacturerSlugJ) continue;
}
json_t *modelSlugJ = json_object_get(moduleJ, "model");
if (!modelSlugJ) continue;
const char *pluginSlug = json_string_value(pluginSlugJ);
const char *modelSlug = json_string_value(modelSlugJ);

// Search for plugin
Plugin *plugin = NULL;
for (Plugin *p : gPlugins) {
if (p->slug == pluginSlug) {
plugin = p;
break;
}
}
if (!plugin) {
message += stringf("Could not find plugin \"%s\" for module \"%s\".\n", pluginSlug, modelSlug);
continue;
}
std::string manufacturerSlug = json_string_value(manufacturerSlugJ);
std::string modelSlug = json_string_value(modelSlugJ);

// Get for model
// Search for model
Model *model = NULL;
for (Model *m : plugin->models) {
if (m->slug == modelSlug) {
model = m;
break;
for (Plugin *plugin : gPlugins) {
for (Model *m : plugin->models) {
if (m->manufacturerSlug == manufacturerSlug && m->slug == modelSlug) {
model = m;
}
}
}

if (!model) {
message += stringf("Could not find module \"%s\" in plugin \"%s\".\n", modelSlug, pluginSlug);
message += stringf("Could not find \"%s %s\" module\n", manufacturerSlug.c_str(), modelSlug.c_str());
continue;
}

@@ -388,13 +382,23 @@ struct UrlItem : MenuItem {
}
};

struct AddPluginMenuItem : MenuItem {
Plugin *plugin;
struct AddManufacturerMenuItem : MenuItem {
std::string manufacturerName;
Vec modulePos;
Menu *createChildMenu() override {
// Collect models which have this manufacturer name
std::set<Model*> models;
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
if (model->manufacturerName == manufacturerName) {
models.insert(model);
}
}
}

// Model items
Menu *menu = new Menu();
for (Model *model : plugin->models) {
for (Model *model : models) {
AddModuleMenuItem *item = new AddModuleMenuItem();
item->text = model->name;
item->model = model;
@@ -403,6 +407,7 @@ struct AddPluginMenuItem : MenuItem {
}

// Metadata items
/*
{
MenuLabel *label = new MenuLabel();
menu->pushChild(label);
@@ -439,6 +444,7 @@ struct AddPluginMenuItem : MenuItem {
item->text = "Version: v" + plugin->version;
menu->pushChild(item);
}
*/

return menu;
}
@@ -452,10 +458,18 @@ void RackWidget::onMouseDownOpaque(int button) {
MenuLabel *menuLabel = new MenuLabel();
menuLabel->text = "Add module";
menu->pushChild(menuLabel);
// Collect manufacturer names
std::set<std::string> manufacturerNames;
for (Plugin *plugin : gPlugins) {
AddPluginMenuItem *item = new AddPluginMenuItem();
item->text = plugin->name;
item->plugin = plugin;
for (Model *model : plugin->models) {
manufacturerNames.insert(model->manufacturerName);
}
}
// Add menu item for each manufacturer name
for (std::string manufacturerName : manufacturerNames) {
AddManufacturerMenuItem *item = new AddManufacturerMenuItem();
item->text = manufacturerName;
item->manufacturerName = manufacturerName;
item->modulePos = modulePos;
menu->pushChild(item);
}


+ 1
- 1
src/core/MidiClockToCV.cpp View File

@@ -241,7 +241,7 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() {
{
Label *label = new Label();
label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
label->text = "MIDI Clock to CV";
label->text = "MIDI Clk-CV";
addChild(label);
yPos = labelHeight * 2;
}


+ 44
- 0
src/core/Notes.cpp View File

@@ -0,0 +1,44 @@
#include "core.hpp"

using namespace rack;



NotesWidget::NotesWidget() {
box.size = Vec(RACK_GRID_WIDTH * 18, RACK_GRID_HEIGHT);

{
Panel *panel = new LightPanel();
panel->box.size = box.size;
addChild(panel);
}

addChild(createScrew<ScrewSilver>(Vec(15, 0)));
addChild(createScrew<ScrewSilver>(Vec(15, 365)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));

textField = new TextField();
textField->box.pos = Vec(15, 15);
textField->box.size = box.size.minus(Vec(30, 30));
textField->multiline = true;
addChild(textField);
}

json_t *NotesWidget::toJson() {
json_t *rootJ = ModuleWidget::toJson();

// text
json_object_set_new(rootJ, "text", json_string(textField->text.c_str()));

return rootJ;
}

void NotesWidget::fromJson(json_t *rootJ) {
ModuleWidget::fromJson(rootJ);

// text
json_t *textJ = json_object_get(rootJ, "text");
if (textJ)
textField->text = json_string_value(textJ);
}

+ 10
- 11
src/core/core.cpp View File

@@ -2,15 +2,14 @@
#include "MidiIO.hpp"


void init(rack::Plugin *plugin) {
plugin->slug = "Core";
plugin->name = "Core";
plugin->homepageUrl = "https://vcvrack.com/";
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface");
createModel<MidiToCVWidget>(plugin, "MIDIToCVInterface", "MIDI-to-CV Interface");
createModel<MIDICCToCVWidget>(plugin, "MIDICCToCVInterface", "MIDI CC-to-CV Interface");
createModel<MIDIClockToCVWidget>(plugin, "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface");
createModel<MIDITriggerToCVWidget>(plugin, "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface");
// createModel<BridgeWidget>(plugin, "Bridge", "Bridge");
createModel<BlankWidget>(plugin, "Blank", "Blank");
void init(rack::Plugin *p) {
p->slug = "Core";
p->addModel(createModel<AudioInterfaceWidget>("Core", "Core", "AudioInterface", "Audio Interface"));
p->addModel(createModel<MidiToCVWidget>("Core", "Core", "MIDIToCVInterface", "MIDI-to-CV Interface"));
p->addModel(createModel<MIDICCToCVWidget>("Core", "Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface"));
p->addModel(createModel<MIDIClockToCVWidget>("Core", "Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface"));
p->addModel(createModel<MIDITriggerToCVWidget>("Core", "Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface"));
// p->addModel(createModel<BridgeWidget>("Core", "Core", "Bridge", "Bridge"));
p->addModel(createModel<BlankWidget>("Core", "Core", "Blank", "Blank"));
p->addModel(createModel<NotesWidget>("Core", "Core", "Notes", "Notes"));
}

+ 7
- 0
src/core/core.hpp View File

@@ -26,3 +26,10 @@ struct BlankWidget : ModuleWidget {
json_t *toJson() override;
void fromJson(json_t *rootJ) override;
};

struct NotesWidget : ModuleWidget {
TextField *textField;
NotesWidget();
json_t *toJson() override;
void fromJson(json_t *rootJ) override;
};

+ 17
- 27
src/plugin.cpp View File

@@ -44,6 +44,13 @@ Plugin::~Plugin() {
}
}

void Plugin::addModel(Model *model) {
assert(!model->plugin);
model->plugin = this;
models.push_back(model);
}


static int loadPlugin(std::string path) {
std::string libraryFilename;
#if ARCH_LIN
@@ -70,7 +77,7 @@ static int loadPlugin(std::string path) {
}
#endif

// Call plugin init() function
// Call plugin's init() function
typedef void (*InitCallback)(Plugin *);
InitCallback initCallback;
#if ARCH_WIN
@@ -89,21 +96,12 @@ static int loadPlugin(std::string path) {
plugin->handle = handle;
initCallback(plugin);

// Check that this is a unique slug
for (Plugin *otherPlugin : gPlugins) {
assert(plugin->slug != otherPlugin->slug);
}

// Add plugin to list
gPlugins.push_back(plugin);
fprintf(stderr, "Loaded plugin %s\n", libraryFilename.c_str());
return 0;
}

static bool comparePlugins(Plugin *a, Plugin *b) {
return a->slug < b->slug;
}

static void loadPlugins(std::string path) {
DIR *dir = opendir(path.c_str());
if (dir) {
@@ -115,9 +113,6 @@ static void loadPlugins(std::string path) {
}
closedir(dir);
}

// Sort plugins
gPlugins.sort(comparePlugins);
}

////////////////////
@@ -200,13 +195,6 @@ static void refreshPurchase(json_t *pluginJ) {
url += "&token=";
url += gToken;

// Find slug in plugins list, and return silently if slug already exists
for (Plugin *p : gPlugins) {
if (p->slug == slug) {
return;
}
}

// If plugin is not loaded, download the zip file to /plugins
downloadName = name;
downloadProgress = 0.0;
@@ -237,9 +225,9 @@ static void refreshPurchase(json_t *pluginJ) {
void pluginInit() {
// Load core
// This function is defined in core.cpp
Plugin *corePlugin = new Plugin();
init(corePlugin);
gPlugins.push_back(corePlugin);
Plugin *coreManufacturer = new Plugin();
init(coreManufacturer);
gPlugins.push_back(coreManufacturer);

// Load plugins from global directory
std::string globalPlugins = assetGlobal("plugins");
@@ -248,10 +236,11 @@ void pluginInit() {

// Load plugins from local directory
std::string localPlugins = assetLocal("plugins");
mkdir(localPlugins.c_str(), 0755);
printf("Loading plugins from %s\n", localPlugins.c_str());
if (globalPlugins != localPlugins)
if (globalPlugins != localPlugins) {
mkdir(localPlugins.c_str(), 0755);
printf("Loading plugins from %s\n", localPlugins.c_str());
loadPlugins(localPlugins);
}
}

void pluginDestroy() {
@@ -265,7 +254,8 @@ void pluginDestroy() {
dlclose(plugin->handle);
#endif

// For some reason this segfaults
// For some reason this segfaults.
// It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
// delete plugin;
}
gPlugins.clear();


+ 15
- 0
src/widgets/FramebufferWidget.cpp View File

@@ -42,6 +42,7 @@ void FramebufferWidget::step() {
internal->box.size = box.size;
internal->box.size = Vec(ceilf(internal->box.size.x), ceilf(internal->box.size.y));
Vec fbSize = internal->box.size.mult(gPixelRatio * oversample);

// assert(fbSize.isFinite());
// Reject zero area size
if (fbSize.x <= 0.0 || fbSize.y <= 0.0)
@@ -71,6 +72,20 @@ void FramebufferWidget::step() {
}

void FramebufferWidget::draw(NVGcontext *vg) {
// {
// float xform[6];
// nvgCurrentTransform(vg, xform);
// printf("%f %f %f %f %f %f\n", xform[0], xform[1], xform[2], xform[3], xform[4], xform[5]);
// nvgSave(vg);
// nvgResetTransform(vg);
// nvgTranslate(vg, xform[5], xform[6]);
// nvgBeginPath(vg);
// nvgRect(vg, 0, 0, 50, 50);
// nvgFillColor(vg, nvgRGBf(1.0, 0.0, 0.0));
// nvgFill(vg);
// nvgRestore(vg);
// }

if (!internal->fb) {
// Bypass framebuffer cache entirely
// Widget::draw(vg);


+ 8
- 0
src/widgets/TextField.cpp View File

@@ -101,6 +101,14 @@ bool TextField::onFocusKey(int key) {
}
}
break;
case GLFW_KEY_ENTER:
if (multiline) {
insertText("\n");
}
else {
onAction();
}
break;
}

begin = mini(maxi(begin, 0), text.size());


Loading…
Cancel
Save