147 lines
4.2 KiB
C++
147 lines
4.2 KiB
C++
/*
|
|
tonewheel organ plugin
|
|
|
|
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(Voice *v) {
|
|
d_debug("assigner constructor");
|
|
|
|
// set up the assigner tables
|
|
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
|
voiceTbl[i] = i;
|
|
noteTbl[i] = 0x80;
|
|
}
|
|
|
|
// populate the pointer to the generator voice table
|
|
voices = v;
|
|
}
|
|
|
|
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]) {
|
|
// maybe handle the following
|
|
// CC 1 - modwheel
|
|
// CC 64 - sustain
|
|
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;
|
|
voices[v].stopNote();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
noteTbl[v] = note;
|
|
|
|
// limit lowest note to C2 and highest note to C7
|
|
while(note>96) note -= 12;
|
|
while(note<36) note += 12;
|
|
|
|
voices[v].startNote(note);
|
|
d_debug("send voice on note %3d to voice %d", note, v);
|
|
}
|