This commit is contained in:
Gordon JC Pearce 2025-08-14 23:05:42 +01:00
parent 8661286e79
commit 5bfe06f721
6 changed files with 224 additions and 32 deletions

View File

@ -32,9 +32,5 @@
#define DISTRHO_PLUGIN_IS_SYNTH 1
enum Parameters {
bass,
kParameterCount
};
#define NUM_VOICES 8
#endif

View File

@ -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

139
plugin/assigner.cpp Normal file
View File

@ -0,0 +1,139 @@
/*
sonnenlicht poly ensemble
Copyright 2025 Gordon JC Pearce <gordonjcp@gjcp.net>
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);
}

38
plugin/assigner.hpp Normal file
View File

@ -0,0 +1,38 @@
/*
sonnenlicht poly ensemble
Copyright 2024 Gordon JC Pearce <gordonjcp@gjcp.net>
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

View File

@ -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]);
}
}

View File

@ -1,5 +1,5 @@
/*
Sonnenlicht reverb plugin
sonnenlicht poly ensemble
Copyright 2024 Gordon JC Pearce <gordonjcp@gjcp.net>
@ -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 &parameter) 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;
private:
Assigner *assigner;
double fSampleRate;
float phase;
float w;