diff --git a/lv2export/Makefile b/lv2export/Makefile index 7b1bffc..993f484 100644 --- a/lv2export/Makefile +++ b/lv2export/Makefile @@ -105,7 +105,8 @@ endif # -------------------------------------------------------------- # Build targets -BUILD_FILES = test.cpp +BUILD_FILES = lv2plugin.cpp +BUILD_FILES += test.cpp BUILD_FILES += dep.cpp BUILD_FILES += dep2.cpp BUILD_FILES += ../src/Rack/src/logger.cpp @@ -122,9 +123,9 @@ BUILD_FILES += ../src/Rack/dep/pffft/pffft.c BUILD_FILES += ../src/Rack/dep/pffft/fftpack.c endif -TARGET = test +TARGETS = test$(APP_EXT) lv2plugin$(LIB_EXT) -all: $(TARGET) +all: $(TARGETS) EXTRA_LIBS = ../plugins/plugins.a @@ -143,12 +144,19 @@ endif # -------------------------------------------------------------- # Build commands -OBJS = $(BUILD_FILES:%=$(BUILD_DIR)/%.o) +# OBJS = $(BUILD_FILES:%=$(BUILD_DIR)/%.o) -$(TARGET): $(OBJS) +FILES1 = $(filter-out lv2plugin.cpp,$(BUILD_FILES)) +FILES2 = $(filter-out test.cpp,$(BUILD_FILES)) + +test$(APP_EXT): $(FILES1:%=$(BUILD_DIR)/%.o) -@mkdir -p $(shell dirname $@) $(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) -o $@ +lv2plugin$(LIB_EXT): $(FILES2:%=$(BUILD_DIR)/%.o) + -@mkdir -p $(shell dirname $@) + $(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(SHARED) -o $@ + $(BUILD_DIR)/%.c.o: %.c -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" diff --git a/lv2export/lv2plugin.cpp b/lv2export/lv2plugin.cpp new file mode 100644 index 0000000..1db214f --- /dev/null +++ b/lv2export/lv2plugin.cpp @@ -0,0 +1,221 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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. + */ + +#include "src/lv2/buf-size.h" +#include "src/lv2/options.h" +#include +#include + +#include "DistrhoUtils.hpp" + +using namespace rack; + +extern Model* modelSpringReverb; +Plugin* pluginInstance__Befaco; + +namespace rack { +namespace settings { +bool cpuMeter = false; +} +Context::~Context() { +} +static thread_local Context* threadContext = nullptr; +Context* contextGet() { + DISTRHO_SAFE_ASSERT(threadContext != nullptr); + return threadContext; +} +// Apple's clang incorrectly compiles this function when -O2 or higher is enabled. +#ifdef ARCH_MAC +__attribute__((optnone)) +#endif +void contextSet(Context* const context) { + // DISTRHO_SAFE_ASSERT(threadContext == nullptr); + threadContext = context; +} +Exception::Exception(const char* format, ...) +{ + va_list args; + va_start(args, format); + msg = string::fV(format, args); + va_end(args); +} +namespace asset { +std::string plugin(plugin::Plugin* plugin, std::string filename) { return {}; } +std::string system(std::string filename) { return {}; } +} +namespace engine { +float Engine::getParamValue(Module* module, int paramId) { return 0.0f; } +float Engine::getParamSmoothValue(Module* module, int paramId) { return 0.0f; } +void Engine::setParamValue(Module* module, int paramId, float value) {} +void Engine::setParamSmoothValue(Module* module, int paramId, float value) {} +} +namespace plugin { +void Plugin::addModel(Model* model) +{ + // Check that the model is not added to a plugin already + DISTRHO_SAFE_ASSERT_RETURN(model != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(model->plugin == nullptr,); + model->plugin = this; + models.push_back(model); +} +Model* modelFromJson(json_t* moduleJ) { + return nullptr; +} +std::vector plugins; +} // namespace plugin +} // namespace rack + +struct PluginLv2 { + Plugin* plugin; + engine::Module* module; + float sampleRate; + int frameCount = 0; + + void* ports[11]; + + PluginLv2(double sr) + { + sampleRate = sr; + plugin = new Plugin; + pluginInstance__Befaco = plugin; + plugin->addModel(modelSpringReverb); + module = modelSpringReverb->createModule(); + + // FIXME we need to detect if something is connected + // module->inputs[0].channels = 1; + // module->inputs[1].channels = 1; + module->inputs[2].channels = 1; + module->inputs[3].channels = 1; + module->inputs[4].channels = 1; + module->outputs[0].channels = 1; + module->outputs[1].channels = 1; + } + + void lv2_connect_port(const uint32_t port, void* const dataLocation) + { + ports[port] = dataLocation; + } + + void lv2_run(const uint32_t sampleCount) + { + if (sampleCount == 0) + return; + + Module::ProcessArgs args = { + sampleRate, + 1.0f / sampleRate, + frameCount + }; + + const float* CV1_INPUT = (float*)ports[0]; + const float* CV2_INPUT = (float*)ports[1]; + const float* IN1_INPUT = (float*)ports[2]; + const float* IN2_INPUT = (float*)ports[3]; + const float* MIX_CV_INPUT = (float*)ports[4]; + float* MIX_OUTPUT = (float*)ports[5]; + float* WET_OUTPUT = (float*)ports[6]; + + const float drywet = *(float*)ports[7] * 0.01f; + const float lvl1 = *(float*)ports[8] * 0.01f; + const float lvl2 = *(float*)ports[9] * 0.01f; + const float hpf = *(float*)ports[10]; + + module->params[0].setValue(drywet); + module->params[1].setValue(lvl1); + module->params[2].setValue(lvl2); + module->params[3].setValue(hpf); + + for (uint32_t i=0; iinputs[0].setVoltage(CV1_INPUT[i]); + module->inputs[1].setVoltage(CV2_INPUT[i]); + module->inputs[2].setVoltage(IN1_INPUT[i] * 10); + module->inputs[3].setVoltage(IN2_INPUT[i] * 10); + module->inputs[4].setVoltage(MIX_CV_INPUT[i]); + module->doProcess(args); + MIX_OUTPUT[i] = module->outputs[0].getVoltage() * 0.1f; + WET_OUTPUT[i] = module->outputs[1].getVoltage() * 0.1f; + ++args.frame; + } + + frameCount += sampleCount; + } + +}; + +static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) +{ + return new PluginLv2(sampleRate); +} + +// ----------------------------------------------------------------------- + +#define instancePtr ((PluginLv2*)instance) + +static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) +{ + instancePtr->lv2_connect_port(port, dataLocation); +} + +static void lv2_activate(LV2_Handle instance) +{ +} + +static void lv2_run(LV2_Handle instance, uint32_t sampleCount) +{ + instancePtr->lv2_run(sampleCount); +} + +static void lv2_deactivate(LV2_Handle instance) +{ +} + +static void lv2_cleanup(LV2_Handle instance) +{ + delete instancePtr; +} + +// ----------------------------------------------------------------------- + +static const void* lv2_extension_data(const char* uri) +{ + return nullptr; +} + +#undef instancePtr + +// ----------------------------------------------------------------------- + +static const LV2_Descriptor sLv2Descriptor = { + "urn:Cardinal:Befaco", + lv2_instantiate, + lv2_connect_port, + lv2_activate, + lv2_run, + lv2_deactivate, + lv2_cleanup, + lv2_extension_data +}; + +DISTRHO_PLUGIN_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &sLv2Descriptor : nullptr; +} + +// ----------------------------------------------------------------------- diff --git a/lv2export/lv2plugin.ttl b/lv2export/lv2plugin.ttl new file mode 100644 index 0000000..80f2e3e --- /dev/null +++ b/lv2export/lv2plugin.ttl @@ -0,0 +1,101 @@ +@prefix atom: . +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix opts: . +@prefix pg: . +@prefix patch: . +@prefix rdf: . +@prefix rdfs: . +@prefix rsz: . +@prefix spdx: . +@prefix ui: . +@prefix unit: . + + + a lv2:Plugin, doap:Project ; + + lv2:port [ + a lv2:InputPort, lv2:CVPort, mod:CVPort ; + lv2:index 0 ; + lv2:symbol "lv2_audio_in_1" ; + lv2:name "CV1_INPUT" ; + lv2:default 10; + lv2:minimum 0; + lv2:maximum 10; + ] , [ + a lv2:InputPort, lv2:CVPort, mod:CVPort ; + lv2:index 1 ; + lv2:symbol "lv2_audio_in_2" ; + lv2:name "CV2_INPUT" ; + lv2:default 10; + lv2:minimum 0; + lv2:maximum 10; + ] , [ + a lv2:InputPort, lv2:AudioPort ; + lv2:index 2 ; + lv2:symbol "lv2_audio_in_3" ; + lv2:name "IN1_INPUT" ; + ] , [ + a lv2:InputPort, lv2:AudioPort ; + lv2:index 3 ; + lv2:symbol "lv2_audio_in_4" ; + lv2:name "IN2_INPUT" ; + ] , [ + a lv2:InputPort, lv2:CVPort, mod:CVPort ; + lv2:index 4 ; + lv2:symbol "lv2_audio_in_5" ; + lv2:name "MIX_CV_INPUT" ; + lv2:default 0; + lv2:minimum 0; + lv2:maximum 10; + ] , [ + a lv2:OutputPort, lv2:AudioPort ; + lv2:index 5 ; + lv2:symbol "lv2_audio_out_1" ; + lv2:name "Mix Output" ; + ] , [ + a lv2:OutputPort, lv2:AudioPort ; + lv2:index 6 ; + lv2:symbol "lv2_audio_out_2" ; + lv2:name "Wet Output" ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 7 ; + lv2:name "Dry/wet" ; + lv2:symbol "param_1" ; + lv2:default 50 ; + lv2:minimum 0 ; + lv2:maximum 100 ; + unit:unit unit:pc; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 8 ; + lv2:name "In 1 level" ; + lv2:symbol "param_2" ; + lv2:default 25 ; + lv2:minimum 0 ; + lv2:maximum 100 ; + unit:unit unit:pc; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 9 ; + lv2:name "In 2 level" ; + lv2:symbol "param_3" ; + lv2:default 25 ; + lv2:minimum 0 ; + lv2:maximum 100 ; + unit:unit unit:pc; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 10 ; + lv2:name "High pass filter cutoff" ; + lv2:symbol "param_4" ; + lv2:default 0.5 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + ] ; + + doap:name "Befaco" . diff --git a/lv2export/manifest.ttl b/lv2export/manifest.ttl new file mode 100644 index 0000000..ddef990 --- /dev/null +++ b/lv2export/manifest.ttl @@ -0,0 +1,7 @@ +@prefix lv2: . +@prefix rdfs: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso .