Browse Source

Initial attempt at plugin state/restore

tags/22.02
falkTX 4 years ago
parent
commit
b5785743e8
6 changed files with 366 additions and 77 deletions
  1. +122
    -34
      src/CardinalPlugin.cpp
  2. +109
    -40
      src/CardinalUI.cpp
  3. +2
    -0
      src/DistrhoPluginInfo.h
  4. +5
    -1
      src/PluginContext.hpp
  5. +68
    -2
      src/Window.cpp
  6. +60
    -0
      src/WindowParameters.hpp

+ 122
- 34
src/CardinalPlugin.cpp View File

@@ -32,7 +32,8 @@
#include <osdialog.h> #include <osdialog.h>
#include "PluginContext.hpp" #include "PluginContext.hpp"
#include "extra/Mutex.hpp"
#include "WindowParameters.hpp"
#include "extra/Base64.hpp"
namespace rack { namespace rack {
namespace plugin { namespace plugin {
@@ -128,8 +129,13 @@ class CardinalPlugin : public CardinalBasePlugin
rack::audio::Device* fCurrentDevice; rack::audio::Device* fCurrentDevice;
Mutex fDeviceMutex; Mutex fDeviceMutex;
float fParameters[kWindowParameterCount];
struct ScopedContext { struct ScopedContext {
ScopedContext(CardinalPlugin* const plugin)
const MutexLocker cml;
ScopedContext(const CardinalPlugin* const plugin)
: cml(plugin->contextMutex)
{ {
rack::contextSet(plugin->fContext); rack::contextSet(plugin->fContext);
} }
@@ -142,13 +148,18 @@ class CardinalPlugin : public CardinalBasePlugin
public: public:
CardinalPlugin() CardinalPlugin()
: CardinalBasePlugin(0, 0, 0),
: CardinalBasePlugin(kWindowParameterCount, 0, 1),
fContext(new CardinalPluginContext(this)), fContext(new CardinalPluginContext(this)),
fAudioBufferIn(nullptr), fAudioBufferIn(nullptr),
fAudioBufferOut(nullptr), fAudioBufferOut(nullptr),
fIsActive(false), fIsActive(false),
fCurrentDevice(nullptr) fCurrentDevice(nullptr)
{ {
fParameters[kWindowParameterCableOpacity] = 50.0f;
fParameters[kWindowParameterCableTension] = 50.0f;
fParameters[kWindowParameterRackBrightness] = 100.0f;
fParameters[kWindowParameterHaloBrightness] = 25.0f;
// create unique temporary path for this instance // create unique temporary path for this instance
try { try {
char uidBuf[24]; char uidBuf[24];
@@ -232,60 +243,38 @@ protected:
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Information */ * Information */
/**
Get the plugin label.
A plugin label follows the same rules as Parameter::symbol, with the exception that it can start with numbers.
*/
const char* getLabel() const override const char* getLabel() const override
{ {
return "Cardinal"; return "Cardinal";
} }
/**
Get an extensive comment/description about the plugin.
*/
const char* getDescription() const override const char* getDescription() const override
{ {
return "...";
return ""
"Cardinal is an open-source self-contained special plugin version of VCVRack, using DPF.\n"
"It is NOT an official VCV project, and it is not affiliated with it in any way.\n";
} }
/**
Get the plugin author/maker.
*/
const char* getMaker() const override const char* getMaker() const override
{ {
return "DISTRHO"; return "DISTRHO";
} }
/**
Get the plugin homepage.
*/
const char* getHomePage() const override const char* getHomePage() const override
{ {
return "https://github.com/DISTRHO/Cardinal"; return "https://github.com/DISTRHO/Cardinal";
} }
/**
Get the plugin license name (a single line of text).
For commercial plugins this should return some short copyright information.
*/
const char* getLicense() const override const char* getLicense() const override
{ {
return "ISC";
return "GPLv3+";
} }
/**
Get the plugin version, in hexadecimal.
*/
uint32_t getVersion() const override uint32_t getVersion() const override
{ {
return d_version(1, 0, 0);
return d_version(2, 0, 0);
} }
/**
Get the plugin unique Id.
This value is used by LADSPA, DSSI and VST plugin formats.
*/
int64_t getUniqueId() const override int64_t getUniqueId() const override
{ {
return d_cconst('d', 'C', 'd', 'n'); return d_cconst('d', 'C', 'd', 'n');
@@ -294,9 +283,110 @@ protected:
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Init */ * Init */
void initParameter(const uint32_t index, Parameter& parameter) override
{
switch (index)
{
case kWindowParameterCableOpacity:
parameter.name = "Cable Opacity";
parameter.symbol = "cableOpacity";
parameter.unit = "%";
parameter.hints = kParameterIsAutomable;
parameter.ranges.def = 50.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f;
break;
case kWindowParameterCableTension:
parameter.name = "Cable Tension";
parameter.symbol = "cableTension";
parameter.unit = "%";
parameter.hints = kParameterIsAutomable;
parameter.ranges.def = 50.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f;
break;
case kWindowParameterRackBrightness:
parameter.name = "Rack Brightness";
parameter.symbol = "rackBrightness";
parameter.unit = "%";
parameter.hints = kParameterIsAutomable;
parameter.ranges.def = 100.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f;
break;
case kWindowParameterHaloBrightness:
parameter.name = "Halo Brightness";
parameter.symbol = "haloBrightness";
parameter.unit = "%";
parameter.hints = kParameterIsAutomable;
parameter.ranges.def = 25.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f;
break;
}
}
void initState(const uint32_t index, String& stateKey, String& defaultStateValue) override
{
DISTRHO_SAFE_ASSERT_RETURN(index == 0,);
stateKey = "patch";
defaultStateValue = "";
}
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Internal data */ * Internal data */
float getParameterValue(const uint32_t index) const override
{
return fParameters[index];
}
void setParameterValue(const uint32_t index, float value) override
{
fParameters[index] = value;
}
String getState(const char* const key) const override
{
if (std::strcmp(key, "patch") != 0)
return String();
if (fAutosavePath.empty())
return String();
std::vector<uint8_t> data;
{
const ScopedContext sc(this);
fContext->engine->prepareSave();
fContext->patch->saveAutosave();
fContext->patch->cleanAutosave();
data = rack::system::archiveDirectory(fAutosavePath, 1);
}
return String::asBase64(data.data(), data.size());
}
void setState(const char* const key, const char* const value) override
{
if (std::strcmp(key, "patch") != 0)
return;
if (fAutosavePath.empty())
return;
const std::vector<uint8_t> data(d_getChunkFromBase64String(value));
const ScopedContext sc(this);
rack::system::removeRecursively(fAutosavePath);
rack::system::createDirectories(fAutosavePath);
rack::system::unarchiveToDirectory(data, fAutosavePath);
fContext->patch->loadAutosave();
}
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Process */ * Process */
@@ -329,9 +419,6 @@ protected:
fAudioBufferIn = fAudioBufferOut = nullptr; fAudioBufferIn = fAudioBufferOut = nullptr;
} }
/**
Run/process function for plugins without MIDI input.
*/
void run(const float** const inputs, float** const outputs, const uint32_t frames) override void run(const float** const inputs, float** const outputs, const uint32_t frames) override
{ {
/* /*
@@ -340,8 +427,9 @@ protected:
*/ */
const MutexLocker cml(fDeviceMutex); const MutexLocker cml(fDeviceMutex);
// const MutexTryLocker cmtl(fPatchMutex);
if (fCurrentDevice == nullptr)
if (fCurrentDevice == nullptr /*|| cmtl.wasNotLocked()*/)
{ {
std::memset(outputs[0], 0, sizeof(float)*frames); std::memset(outputs[0], 0, sizeof(float)*frames);
std::memset(outputs[1], 0, sizeof(float)*frames); std::memset(outputs[1], 0, sizeof(float)*frames);


+ 109
- 40
src/CardinalUI.cpp View File

@@ -22,9 +22,13 @@
#include <ui/MenuItem.hpp> #include <ui/MenuItem.hpp>
#include <window/Window.hpp> #include <window/Window.hpp>


#include "PluginContext.hpp"
#ifdef NDEBUG
# undef DEBUG
#endif


#include "DistrhoUI.hpp" #include "DistrhoUI.hpp"
#include "PluginContext.hpp"
#include "WindowParameters.hpp"
#include "ResizeHandle.hpp" #include "ResizeHandle.hpp"


GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; } GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; }
@@ -45,20 +49,29 @@ START_NAMESPACE_DISTRHO


CardinalPluginContext* getRackContextFromPlugin(void* ptr); CardinalPluginContext* getRackContextFromPlugin(void* ptr);


class CardinalUI : public UI
class CardinalUI : public UI,
public WindowParametersCallback
{ {
CardinalPluginContext* const fContext; CardinalPluginContext* const fContext;
rack::math::Vec fLastMousePos; rack::math::Vec fLastMousePos;
ResizeHandle fResizeHandle; ResizeHandle fResizeHandle;
WindowParameters fWindowParameters;


struct ScopedContext { struct ScopedContext {
CardinalPluginContext* const context;
const MutexLocker cml;

ScopedContext(CardinalUI* const ui) ScopedContext(CardinalUI* const ui)
: context(ui->fContext),
cml(context->plugin->contextMutex)
{ {
rack::contextSet(ui->fContext);
rack::contextSet(context);
WindowParametersRestore(context->window);
} }


~ScopedContext() ~ScopedContext()
{ {
WindowParametersSave(context->window);
rack::contextSet(nullptr); rack::contextSet(nullptr);
} }
}; };
@@ -72,58 +85,63 @@ public:
if (isResizable()) if (isResizable())
fResizeHandle.hide(); fResizeHandle.hide();


const ScopedContext sc(this);
fContext->window = new rack::window::Window;


fContext->event = new rack::widget::EventState;
fContext->scene = new rack::app::Scene;
fContext->event->rootWidget = fContext->scene;
{
const ScopedContext sc(this);


fContext->window = new rack::window::Window;
rack::window::WindowInit(fContext->window, this);
fContext->event = new rack::widget::EventState;
fContext->scene = new rack::app::Scene;
fContext->event->rootWidget = fContext->scene;


// Hide non-wanted menu entries
typedef rack::ui::Button rButton;
// typedef rack::ui::MenuItem rMenuItem;
typedef rack::widget::Widget rWidget;
typedef std::list<rWidget*>::iterator rWidgetIterator;
rack::window::WindowInit(fContext->window, this);


rWidget* const layout = fContext->scene->menuBar->children.front();
// Hide non-wanted menu entries
typedef rack::ui::Button rButton;
// typedef rack::ui::MenuItem rMenuItem;
typedef rack::widget::Widget rWidget;
typedef std::list<rWidget*>::iterator rWidgetIterator;


for (rWidgetIterator it = layout->children.begin(); it != layout->children.end(); ++it)
{
if (rButton* const button = reinterpret_cast<rButton*>(*it))
rWidget* const layout = fContext->scene->menuBar->children.front();
for (rWidgetIterator it = layout->children.begin(); it != layout->children.end(); ++it)
{ {
/* FIXME this doesnt work
if (button->text == "Engine")
if (rButton* const button = reinterpret_cast<rButton*>(*it))
{ {
for (rWidgetIterator it2 = button->children.begin(); it2 != button->children.end(); ++it2)
/* FIXME this doesnt work
if (button->text == "Engine")
{ {
if (rMenuItem* const item = reinterpret_cast<rMenuItem*>(*it2))
for (rWidgetIterator it2 = button->children.begin(); it2 != button->children.end(); ++it2)
{ {
if (item->text == "Sample rate")
if (rMenuItem* const item = reinterpret_cast<rMenuItem*>(*it2))
{ {
button->children.erase(it2);
delete button;
break;
if (item->text == "Sample rate")
{
button->children.erase(it2);
delete button;
break;
}
} }
} }
} }
}
*/
if (button->text == "Library")
{
layout->children.erase(it);
delete button;
break;
*/
if (button->text == "Library")
{
layout->children.erase(it);
delete button;
break;
}
} }
} }

// we need to reload current patch for things to show on screen :(
// FIXME always save
if (! fContext->patch->hasAutosave())
fContext->patch->saveAutosave();
fContext->patch->loadAutosave();
} }


// we need to reload current patch for things to show on screen :(
// FIXME always save
if (! fContext->patch->hasAutosave())
fContext->patch->saveAutosave();
fContext->patch->loadAutosave();
WindowParametersSetCallback(fContext->window, this);
} }


~CardinalUI() override ~CardinalUI() override
@@ -143,7 +161,6 @@ public:
void onNanoDisplay() override void onNanoDisplay() override
{ {
const ScopedContext sc(this); const ScopedContext sc(this);

fContext->window->step(); fContext->window->step();
} }


@@ -152,6 +169,35 @@ public:
repaint(); repaint();
} }


void WindowParametersChanged(const WindowParameterList param, const float value) override
{
float mult;

switch (param)
{
case kWindowParameterCableOpacity:
mult = 100.0f;
fWindowParameters.cableOpacity = value;
break;
case kWindowParameterCableTension:
mult = 100.0f;
fWindowParameters.cableTension = value;
break;
case kWindowParameterRackBrightness:
mult = 100.0f;
fWindowParameters.rackBrightness = value;
break;
case kWindowParameterHaloBrightness:
mult = 100.0f;
fWindowParameters.haloBrightness = value;
break;
default:
return;
}

setParameterValue((uint)param, value * mult);
}

protected: protected:
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* DSP/Plugin Callbacks */ * DSP/Plugin Callbacks */
@@ -160,7 +206,30 @@ protected:
A parameter has changed on the plugin side. A parameter has changed on the plugin side.
This is called by the host to inform the UI about parameter changes. This is called by the host to inform the UI about parameter changes.
*/ */
void parameterChanged(uint32_t index, float value) override
void parameterChanged(const uint32_t index, const float value) override
{
switch (index)
{
case kWindowParameterCableOpacity:
fWindowParameters.cableOpacity = value / 100.0f;
break;
case kWindowParameterCableTension:
fWindowParameters.cableTension = value / 100.0f;
break;
case kWindowParameterRackBrightness:
fWindowParameters.rackBrightness = value / 100.0f;
break;
case kWindowParameterHaloBrightness:
fWindowParameters.haloBrightness = value / 100.0f;
break;
default:
return;
}

WindowParametersSetValues(fContext->window, fWindowParameters);
}

void stateChanged(const char* key, const char* value) override
{ {
} }




+ 2
- 0
src/DistrhoPluginInfo.h View File

@@ -25,6 +25,8 @@
#define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_HAS_UI 1
#define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_INPUTS 2
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2
#define DISTRHO_PLUGIN_WANT_FULL_STATE 1
#define DISTRHO_PLUGIN_WANT_STATE 1
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 #define DISTRHO_PLUGIN_WANT_TIMEPOS 1
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1
// #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:AnalyserPlugin" // #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:AnalyserPlugin"


+ 5
- 1
src/PluginContext.hpp View File

@@ -25,6 +25,7 @@
#endif #endif


#include "DistrhoPlugin.hpp" #include "DistrhoPlugin.hpp"
#include "extra/Mutex.hpp"


START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO


@@ -39,6 +40,9 @@ public:
virtual bool canAssignDevice() const noexcept = 0; virtual bool canAssignDevice() const noexcept = 0;
virtual void assignDevice(rack::audio::Device* dev) noexcept = 0; virtual void assignDevice(rack::audio::Device* dev) noexcept = 0;
virtual bool clearDevice(rack::audio::Device* dev) noexcept = 0; virtual bool clearDevice(rack::audio::Device* dev) noexcept = 0;

// ensure context validity through UI and setState
Mutex contextMutex;
}; };


// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
@@ -110,7 +114,7 @@ struct CardinalAudioDriver : rack::audio::Driver {


std::vector<int> getDeviceIds() override std::vector<int> getDeviceIds() override
{ {
return std::vector<int>({ 0 });
return std::vector<int>({ 1 });
} }


std::string getDeviceName(int) override std::string getDeviceName(int) override


+ 68
- 2
src/Window.cpp View File

@@ -19,6 +19,7 @@
#endif #endif


#include "DistrhoUI.hpp" #include "DistrhoUI.hpp"
#include "WindowParameters.hpp"


namespace rack { namespace rack {
namespace window { namespace window {
@@ -67,13 +68,19 @@ std::shared_ptr<Image> Image::load(const std::string& filename) {
} }




struct WindowParams {
float rackBrightness = 1.0f;
};

struct Window::Internal { struct Window::Internal {
int mods = 0;
DISTRHO_NAMESPACE::UI* ui = nullptr; DISTRHO_NAMESPACE::UI* ui = nullptr;
math::Vec size = minWindowSize;
DISTRHO_NAMESPACE::WindowParameters params;
DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr;


math::Vec size = minWindowSize;
std::string lastWindowTitle; std::string lastWindowTitle;


int mods = 0;
int frame = 0; int frame = 0;
int frameSwapInterval = 1; int frameSwapInterval = 1;
double monitorRefreshRate = 60.0; // FIXME double monitorRefreshRate = 60.0; // FIXME
@@ -107,6 +114,9 @@ void WindowInit(Window* const window, DISTRHO_NAMESPACE::UI* const ui)
window->uiFont = window->loadFont(asset::system("res/fonts/DejaVuSans.ttf")); window->uiFont = window->loadFont(asset::system("res/fonts/DejaVuSans.ttf"));
bndSetFont(window->uiFont->handle); bndSetFont(window->uiFont->handle);


// Init settings
WindowParametersRestore(window);

if (APP->scene) { if (APP->scene) {
widget::Widget::ContextCreateEvent e; widget::Widget::ContextCreateEvent e;
APP->scene->onContextCreate(e); APP->scene->onContextCreate(e);
@@ -321,3 +331,59 @@ bool& Window::fbDirtyOnSubpixelChange() {


} // namespace window } // namespace window
} // namespace rack } // namespace rack


START_NAMESPACE_DISTRHO

void WindowParametersSave(rack::window::Window* const window)
{
if (d_isNotEqual(window->internal->params.cableOpacity, rack::settings::cableOpacity))
{
window->internal->params.cableOpacity = rack::settings::cableOpacity;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterCableOpacity,
rack::settings::cableOpacity);
}
if (d_isNotEqual(window->internal->params.cableTension, rack::settings::cableTension))
{
window->internal->params.cableTension = rack::settings::cableTension;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterCableTension,
rack::settings::cableTension);
}
if (d_isNotEqual(window->internal->params.rackBrightness, rack::settings::rackBrightness))
{
window->internal->params.rackBrightness = rack::settings::rackBrightness;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterRackBrightness,
rack::settings::rackBrightness);
}
if (d_isNotEqual(window->internal->params.haloBrightness, rack::settings::haloBrightness))
{
window->internal->params.haloBrightness = rack::settings::haloBrightness;
if (window->internal->callback != nullptr)
window->internal->callback->WindowParametersChanged(kWindowParameterHaloBrightness,
rack::settings::haloBrightness);
}
}

void WindowParametersRestore(rack::window::Window* const window)
{
rack::settings::cableOpacity = window->internal->params.cableOpacity;
rack::settings::cableTension = window->internal->params.cableTension;
rack::settings::rackBrightness = window->internal->params.rackBrightness;
rack::settings::haloBrightness = window->internal->params.haloBrightness;
}

void WindowParametersSetCallback(rack::window::Window* const window, WindowParametersCallback* const callback)
{
window->internal->callback = callback;
}

void WindowParametersSetValues(rack::window::Window* const window, const WindowParameters& params)
{
window->internal->params = params;
}

END_NAMESPACE_DISTRHO


+ 60
- 0
src/WindowParameters.hpp View File

@@ -0,0 +1,60 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#pragma once

#include "DistrhoUtils.hpp"

namespace rack {
namespace window {
struct Window;
}
}

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------------------------------------------

enum WindowParameterList {
kWindowParameterCableOpacity,
kWindowParameterCableTension,
kWindowParameterRackBrightness,
kWindowParameterHaloBrightness,
kWindowParameterCount,
};

struct WindowParameters {
float cableOpacity = 0.5f;
float cableTension = 0.5f;
float rackBrightness = 1.0f;
float haloBrightness = 0.25f;
// KnobMode knobMode = KNOB_MODE_LINEAR;
};

struct WindowParametersCallback {
virtual ~WindowParametersCallback() {}
virtual void WindowParametersChanged(WindowParameterList param, float value) = 0;
};

void WindowParametersSave(rack::window::Window* window);
void WindowParametersRestore(rack::window::Window* window);
void WindowParametersSetCallback(rack::window::Window* window, WindowParametersCallback* callback);
void WindowParametersSetValues(rack::window::Window* window, const WindowParameters& params);

// -----------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

Loading…
Cancel
Save