diff --git a/plugin/DistrhoPluginInfo.h b/plugin/DistrhoPluginInfo.h index 83b6c0c..025d159 100644 --- a/plugin/DistrhoPluginInfo.h +++ b/plugin/DistrhoPluginInfo.h @@ -32,9 +32,5 @@ #define DISTRHO_PLUGIN_IS_SYNTH 1 -enum Parameters { - bass, - kParameterCount -}; - +#define NUM_VOICES 8 #endif diff --git a/plugin/Makefile b/plugin/Makefile index 3bab868..f12ecfc 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -9,7 +9,7 @@ NAME = sonnenlicht -FILES_DSP = sonnenlicht.cpp +FILES_DSP = assigner.cpp sonnenlicht.cpp include ../dpf/Makefile.plugins.mk TARGETS += vst2 vst3 jack lv2_dsp diff --git a/plugin/assigner.cpp b/plugin/assigner.cpp new file mode 100644 index 0000000..0a8b63f --- /dev/null +++ b/plugin/assigner.cpp @@ -0,0 +1,139 @@ +/* + sonnenlicht poly ensemble + + Copyright 2025 Gordon JC Pearce + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "assigner.hpp" + +#define DEBUG + +void Assigner::dumpTables() { +#ifdef DEBUG + printf("table state is:\n"); + for (uint8_t i = 0; i < NUM_VOICES; i++) { + uint8_t e = voiceTbl[i]; + uint8_t a = noteTbl[e]; + printf("%2d=%3d%c ", e, a & 0x7f, (a & 0x80) ? '^' : 'v'); + } + printf("\n"); +#endif +} + +Assigner::Assigner() { + d_debug("assigner constructor"); + + // set up the assigner tables + for (uint8_t i = 0; i < NUM_VOICES; i++) { + voiceTbl[i] = i; + noteTbl[i] = 0x80; + } +} + +void Assigner::handleMidi(MidiEvent *ev) { + uint8_t status = ev->data[0]; + switch (status & 0xf0) { + case 0x80: + noteOff(ev->data[1]); + dumpTables(); + break; + case 0x90: + // dumpTables(); + noteOn(ev->data[1]); + dumpTables(); + break; + case 0xb0: + switch (ev->data[1]) { + // handle the following + // CC 1 - modwheel + // CC 64 - sustain + // possibly JU-06 CC values + default: + break; + } + break; // nothing to do here except in special cases where we don't expect the host to pass on controls + case 0xc0: // program change + break; + case 0xe0: // pitch bend; + break; + case 0xf0: // sysex + break; + + default: + d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]); + break; + } +} +void Assigner::noteOff(uint8_t note) { + d_debug("note off %3d", note); + + uint8_t i, v; + // scan for note in table + for (i = 0; i < NUM_VOICES; i++) { + if ((noteTbl[voiceTbl[i]] & 0x7f) == note) break; + } + if (i == NUM_VOICES) return; // got note off for note that isn't in the table + + // get the voice at this slot + // then move the rest down and place this at the end + // and mark the note for this voice as "off" by setting bit 7 + v = voiceTbl[i]; + memmove(voiceTbl + i, voiceTbl + i + 1, NUM_VOICES - i - 1); + voiceTbl[NUM_VOICES - 1] = v; + noteTbl[v] |= 0x80; +} + +void Assigner::noteOn(uint8_t note) { + d_debug("note on %3d", note); + + int8_t i, a, v; + + // don't even attempt to steal voices + // by overwriting the last voice in the table + // we could actually do voice stealing + if ((noteTbl[voiceTbl[NUM_VOICES - 1]] & 0x80) == 0x00) { + d_debug("note table full"); + return; + } + + // loop around the voices + for (i = NUM_VOICES - 1; i >= 0; i--) { + v = voiceTbl[i]; + a = noteTbl[v]; + + // if this note is playing we've gone too far + // we need to back up a slot and consider it for use + if ((a & 0x80) == 0) { + i++; + + // shunt them all up to the end + v = voiceTbl[i]; + memmove(voiceTbl + 1, voiceTbl, i); + voiceTbl[0] = v; + break; // note is already playing in table + } + + if ((a & 0x7f) == note) { + memmove(voiceTbl + 1, voiceTbl, i); + voiceTbl[0] = v; + a &= 0x7f; + break; + } + } + // printf("at end, l=%d e=%d\n", l,e); + noteTbl[v] = note; + + d_debug("send voice on %3d to voice %d", note, v); +} diff --git a/plugin/assigner.hpp b/plugin/assigner.hpp new file mode 100644 index 0000000..6e921d9 --- /dev/null +++ b/plugin/assigner.hpp @@ -0,0 +1,38 @@ +/* + sonnenlicht poly ensemble + + Copyright 2024 Gordon JC Pearce + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _ASSIGNER_HPP +#define _ASSIGNER_HPP + +#include "DistrhoPlugin.hpp" + +class Assigner { + public: + Assigner(); + void handleMidi(MidiEvent *ev); + + private: + void noteOn(uint8_t note); // incoming note on (or off, if velocity = 0) + void noteOff(uint8_t note); // incoming note off + uint8_t voiceTbl[NUM_VOICES]; // voices in order of use + uint8_t noteTbl[NUM_VOICES]; // note played by voice + + void dumpTables(); +}; + +#endif \ No newline at end of file diff --git a/plugin/sonnenlicht.cpp b/plugin/sonnenlicht.cpp index 7a9c477..2e67ac9 100644 --- a/plugin/sonnenlicht.cpp +++ b/plugin/sonnenlicht.cpp @@ -18,34 +18,48 @@ #include "sonnenlicht.hpp" +#include "assigner.hpp" + START_NAMESPACE_DISTRHO Sonnenlicht::Sonnenlicht() : Plugin(kParameterCount, 0, 0), fSampleRate(getSampleRate()) { printf("initialiser called\n"); w = 440.0 / fSampleRate; phase = 0; + assigner = new Assigner; +} + +Sonnenlicht::~Sonnenlicht() { + delete assigner; +} + +void Sonnenlicht::setParameterValue(uint32_t index, float value) { + printf("got parameter %d, %f\n", index, value); +} + +void Sonnenlicht::initAudioPort(bool input, uint32_t index, AudioPort& port) { + port.groupId = kPortGroupStereo; + Plugin::initAudioPort(input, index, port); + + if (!input && index == 0) port.symbol = "Left"; + if (!input && index == 1) port.symbol = "Right"; +} + +void Sonnenlicht::activate() { +} + +void Sonnenlicht::deactivate() { +} + +void Sonnenlicht::initParameter(uint32_t index, Parameter& parameter) { + return; } void Sonnenlicht::run(const float**, float** outputs, uint32_t frames, - const MidiEvent* midiEvents, uint32_t midiEventCount) { - - if (midiEventCount > 0) { - for (uint32_t i=0; i < midiEventCount; i++ ) { - const uint8_t *data = midiEvents[i].data; - const uint8_t status = data[0] & 0xf0; - - - if (status == 0x90) { - w = (261.6 * (powf(2, (data[1]-60)/12.0f)))/fSampleRate; - printf("set w to %f\n", w); - } - } - } - for (uint32_t i = 0; i < frames; i++) { - phase += w; - if (phase > 1) phase -= 1; - outputs[0][i] = phase - 0.5; - outputs[1][i] = phase - 0.5; + const MidiEvent* ev, uint32_t evCount) { + for (uint32_t i = 0; i < evCount; i++) { + //printf("%3d: %02x %02x\n", i, midiEvents[i].data[0], midiEvents[i].data[1]); + assigner->handleMidi((MidiEvent *)&ev[i]); } } diff --git a/plugin/sonnenlicht.hpp b/plugin/sonnenlicht.hpp index d232a66..c32e61e 100644 --- a/plugin/sonnenlicht.hpp +++ b/plugin/sonnenlicht.hpp @@ -1,5 +1,5 @@ /* - Sonnenlicht reverb plugin + sonnenlicht poly ensemble Copyright 2024 Gordon JC Pearce @@ -20,17 +20,18 @@ #define SONNENLICHT_HPP #include "DistrhoPlugin.hpp" +#include "assigner.hpp" START_NAMESPACE_DISTRHO class Sonnenlicht : public Plugin { public: enum Parameters { - bass, kParameterCount }; Sonnenlicht(); + ~Sonnenlicht(); protected: const char *getLabel() const override { return "sonnenlicht"; } @@ -42,17 +43,21 @@ class Sonnenlicht : public Plugin { uint32_t getVersion() const override { return d_version(1, 0, 0); } int64_t getUniqueId() const override { return d_cconst('S', 'L', 'C', 'T'); } + void initParameter(uint32_t index, Parameter ¶meter) override; + void setParameterValue(uint32_t index, float value) override; + // Initialisation - // void initAudioPort(bool input, uint32_t index, AudioPort &port) override; + void initAudioPort(bool input, uint32_t index, AudioPort &port) override; // Processing - // void activate() override; - // void deactivate() override; - + void activate() override; + void deactivate() override; + void run(const float **, float **outputs, uint32_t frames, - const MidiEvent *midiEvents, uint32_t midiEventCount) override; + const MidiEvent *midiEvents, uint32_t midiEventCount) override; private: + Assigner *assigner; double fSampleRate; float phase; float w;