Browse Source

Implement UI handler for switches, knobs and leds.

faust
Stephane Letz Andrew Belt 4 years ago
parent
commit
ac43a8ce58
3 changed files with 269 additions and 7 deletions
  1. +1
    -0
      Makefile
  2. +63
    -0
      faust_libraries/rack.lib
  3. +205
    -7
      src/FaustEngine.cpp

+ 1
- 0
Makefile View File

@@ -258,6 +258,7 @@ FLAGS += -I/use/local/include
LDFLAGS += -L/usr/local/lib -lfaust
DEPS += $(faust)
OBJECTS += $(faust)
DISTRIBUTABLES += faust_libraries
FAUST_MAKE_FLAGS += prefix="$(DEP_PATH)"
endif



+ 63
- 0
faust_libraries/rack.lib View File

@@ -0,0 +1,63 @@
/* ----------------------------------------------------------------------------

ProtoFaust
==========
DSP prototyping in Faust for VCV Rack

Copyright (c) 2019-2020 Martin Zuther (http://www.mzuther.de/) and
contributors

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
(at your option) 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.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Thank you for using free software!

---------------------------------------------------------------------------- */


// Converts 1 V/oct to frequency in Hertz.
//
// The conversion formula is: 440 * 2 ^ (volts - 0.75)
// The factor 0.75 shifts 0 V to C-4 (261.6256 Hz)
cv_pitch2freq(cv_pitch) = 440 * 2 ^ (cv_pitch - 0.75);


// Converts frequency in Hertz to 1 V/oct.
//
// The conversion formula is: log2(hertz / 440) + 0.75
// The factor 0.75 shifts 0 V to C-4 (261.6256 Hz)
freq2cv_pitch(freq) = ma.log2(freq / 440) + 0.75;


// Converts 200 mV/oct to frequency in Hertz.
i_cv_pitch2freq(i_cv_pitch) = i_cv_pitch : internal2cv_pitch : cv_pitch2freq;


// Converts frequency in Hertz to 200 mV/oct.
freq2i_cv_pitch(freq) = freq : freq2cv_pitch : cv_pitch2internal;


// Converts Eurorack's 1 V/oct to internal 200 mv/oct.
cv_pitch2internal(cv_pitch) = cv_pitch / 5;


// Converts internal 200 mv/oct to Eurorack's 1 V/oct.
internal2cv_pitch(i_cv_pitch) = i_cv_pitch * 5;


// Converts Eurorack's CV (range of 10V) to internal CV (range of 1V)
cv2internal(cv) = cv / 10;


// Converts internal CV (range of 1V) to Eurorack's CV (range of 10V)
internal2cv(i_cv) = i_cv * 10;

+ 205
- 7
src/FaustEngine.cpp View File

@@ -1,17 +1,175 @@
/************************************************************************
FAUST Architecture File
Copyright (C) 2020 GRAME, Centre National de Creation Musicale
---------------------------------------------------------------------
This Architecture section 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 (at your option) 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.
You should have received a copy of the GNU General Public License
along with this program; If not, see <http://www.gnu.org/licenses/>.
EXCEPTION : As a special exception, you may create a larger work
that contains this FAUST architecture section and distribute
that work under terms of your choice, so long as this FAUST
architecture section is not modified.
************************************************************************/

#include "ScriptEngine.hpp"

#include <faust/dsp/llvm-dsp.h>
#include <faust/dsp/libfaust.h>
#include <faust/gui/DecoratorUI.h>
#include <faust/gui/ValueConverter.h>

#include <iostream>

#define kBufferSize 64

extern rack::Plugin* pluginInstance;

// UI handler for switches, knobs and leds
struct RackUI : public GenericUI
{
FAUSTFLOAT* fSwitches[NUM_ROWS];
ConverterZoneControl* fKnobs[NUM_ROWS];
FAUSTFLOAT* fLedRed[NUM_ROWS];
FAUSTFLOAT* fLedGreen[NUM_ROWS];
FAUSTFLOAT* fLedBlue[NUM_ROWS];
std::string fKey, fValue, fScale;
void addItem(FAUSTFLOAT* table[NUM_ROWS], FAUSTFLOAT* zone, const std::string& value)
{
try {
int index = std::stoi(value);
if (index >= 0 && index <= NUM_ROWS) {
table[index-1] = zone;
} else {
std::cerr << "ERROR : incorrect '" << index << "' value !\n";
}
} catch (std::invalid_argument& e) {
std::cerr << "ERROR : " << e.what() << std::endl;
}
fValue = fKey = fScale = "";
}
void addItemConverter(ConverterZoneControl* table[NUM_ROWS], FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, const std::string& value)
{
try {
int index = std::stoi(value);
if (index >= 0 && index <= NUM_ROWS) {
// Select appropriate converter according to scale mode
if (fScale == "log") {
table[index-1] = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max));
} else if (fScale == "exp") {
table[index-1] = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max));
} else {
table[index-1] = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max));
}
} else {
std::cerr << "ERROR : incorrect '" << index << "' value !\n";
}
} catch (std::invalid_argument& e) {
std::cerr << "ERROR : " << e.what() << std::endl;
}
fValue = fKey = fScale = "";
}
RackUI()
{
fScale = "lin";
for (int i = 0; i < NUM_ROWS; i++) {
fSwitches[i] = nullptr;
fKnobs[i] = nullptr;
fLedRed[i] = nullptr;
fLedGreen[i] = nullptr;
fLedBlue[i] = nullptr;
}
}
virtual ~RackUI()
{
for (int i = 0; i < NUM_ROWS; i++) {
delete fKnobs[i];
}
}
void addButton(const char* label, FAUSTFLOAT* zone)
{
if (fKey == "switch") {
addItem(fSwitches, zone, fValue);
}
}
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addNumEntry(label, zone, init, min, max, step);
}
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addNumEntry(label, zone, init, min, max, step);
}
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
if (fKey == "knob") {
addItemConverter(fKnobs, zone, min, max, fValue);
}
}
void addBarGraph(FAUSTFLOAT* zone)
{
if (fKey == "led_red") {
addItem(fLedRed, zone, fValue);
} else if (fKey == "led_green") {
addItem(fLedGreen, zone, fValue);
} else if (fKey == "led_blue") {
addItem(fLedBlue, zone, fValue);
}
}
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addBarGraph(zone);
}
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addBarGraph(zone);
}
void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{
if ((std::string(key) == "switch")
|| (std::string(key) == "knob")
|| (std::string(key) == "led_red")
|| (std::string(key) == "led_green")
|| (std::string(key) == "led_blue")) {
fKey = key;
fValue = val;
} else if (std::string(key) == "scale") {
fScale = val;
}
}
};

// Faust engine using libfaust/LLVM
class FaustEngine : public ScriptEngine {
public:
FaustEngine():fDSPFactory(nullptr), fDSP(nullptr), fInputs(nullptr), fOutputs(nullptr)
FaustEngine():
fDSPFactory(nullptr),
fDSP(nullptr),
fInputs(nullptr),
fOutputs(nullptr),
fDSPLibraries(rack::asset::plugin(pluginInstance, "faust_libraries"))
{}
~FaustEngine()
@@ -41,9 +199,16 @@ class FaustEngine : public ScriptEngine {
if (!fDSPFactory) {
// Otherwise recompile the DSP
fDSPFactory = createDSPFactoryFromString("FaustDSP", script, 0, NULL, "", error_msg, -1);
int argc = 0;
const char* argv[8];
argv[argc++] = "-I";
argv[argc++] = fDSPLibraries.c_str();
argv[argc] = nullptr; // NULL terminated argv
fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1);
if (!fDSPFactory) {
display("ERROR: cannot create factory !");
display("ERROR : cannot create factory !");
std::cerr << error_msg;
return -1;
} else {
// And save the cache
@@ -62,16 +227,19 @@ class FaustEngine : public ScriptEngine {
}
// Prepare inputs/outputs
if (fDSP->getNumInputs() > 6) {
if (fDSP->getNumInputs() > NUM_ROWS) {
display("ERROR: DSP has " + std::to_string(fDSP->getNumInputs()) + " inputs !");
return -1;
}
if (fDSP->getNumOutputs() > 6) {
if (fDSP->getNumOutputs() > NUM_ROWS) {
display("ERROR: DSP has " + std::to_string(fDSP->getNumInputs()) + " outputs !");
return -1;
}
// Setup UI
fDSP->buildUserInterface(&fRackUI);
setFrameDivider(1);
setBufferSize(kBufferSize);
@@ -95,11 +263,39 @@ class FaustEngine : public ScriptEngine {
int process() override
{
ProcessBlock* block = getProcessBlock();
// Possibly update SR
if (getProcessBlock()->sampleRate != fDSP->getSampleRate()) {
fDSP->init(getProcessBlock()->sampleRate);
if (block->sampleRate != fDSP->getSampleRate()) {
fDSP->init(block->sampleRate);
}
// Update inputs controllers
for (int i = 0; i < NUM_ROWS; i++) {
if (fRackUI.fSwitches[i]) {
*fRackUI.fSwitches[i] = block->switches[i];
}
if (fRackUI.fKnobs[i]) {
fRackUI.fKnobs[i]->update(block->knobs[i]);
}
}
// Compute samples
fDSP->compute(kBufferSize, fInputs, fOutputs);
// Update output controllers
for (int i = 0; i < NUM_ROWS; i++) {
if (fRackUI.fLedRed[i]) {
block->lights[i][0] = *fRackUI.fLedRed[i];
}
if (fRackUI.fLedGreen[i]) {
block->lights[i][1] = *fRackUI.fLedGreen[i];
}
if (fRackUI.fLedBlue[i]) {
block->lights[i][2] = *fRackUI.fLedBlue[i];
}
}
return 0;
}
@@ -108,6 +304,8 @@ class FaustEngine : public ScriptEngine {
llvm_dsp* fDSP;
FAUSTFLOAT** fInputs;
FAUSTFLOAT** fOutputs;
RackUI fRackUI;
std::string fDSPLibraries;
};

__attribute__((constructor(1000)))


Loading…
Cancel
Save