first noise, pitches about correct

This commit is contained in:
Gordon JC Pearce 2024-10-13 23:27:54 +01:00
parent 0fe57ff017
commit 5c1c79464e
6 changed files with 107 additions and 46 deletions

View File

@ -17,10 +17,12 @@
*/ */
#include "ic1.hpp" #include "ic1.hpp"
#include "ic29.hpp"
#include "ic29.hpp"
#include "peacock.hpp" #include "peacock.hpp"
Assigner ic1;
void Assigner::printMap() { void Assigner::printMap() {
printf("note memory:\n"); printf("note memory:\n");
for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]); for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]);
@ -54,36 +56,30 @@ void Assigner::handleMidi(const MidiEvent *ev) {
} }
void Assigner::noteOff(uint8_t note) { void Assigner::noteOff(uint8_t note) {
// Poly 1 note off // Poly 1 note off
d_debug("Note Off %02x", note);
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
if (keymap[i] == note) { if (keymap[i] == note) {
d_debug("Found note at slot %02x, moving to last", i);
uint8_t voice = voicemap[i]; // save the voice uint8_t voice = voicemap[i]; // save the voice
memmove(voicemap + i, voicemap + i + 1, NUM_VOICES - i - 1); memmove(voicemap + i, voicemap + i + 1, NUM_VOICES - i - 1);
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1); memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
voicemap[NUM_VOICES - 1] = voice; voicemap[NUM_VOICES - 1] = voice;
keymap[NUM_VOICES - 1] = note | 0x80; keymap[NUM_VOICES - 1] = note | 0x80;
s.voiceOff(voice); ic29.voiceOff(voice);
} }
} }
printMap();
} }
void Assigner::noteOn(uint8_t note) { void Assigner::noteOn(uint8_t note) {
// Poly 1 assigner // Poly 1 assigner
// scan for the same note // scan for the same note
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
if ((keymap[i] & 0x7f) == note) { if ((keymap[i] & 0x7f) == note) {
// note found, move it to the front of the queue // note found, move it to the front of the queue
d_debug("voice %02x on in slot %02x, existing note %02x", voicemap[i], i, keymap[i]);
uint8_t voice = voicemap[i]; uint8_t voice = voicemap[i];
memmove(voicemap + 1, voicemap, i); memmove(voicemap + 1, voicemap, i);
memmove(keymap + 1, keymap, i); memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on keymap[0] = note; // show note as on
voicemap[0] = voice; voicemap[0] = voice;
s.voiceOn(voice, note); ic29.voiceOn(voice, note);
return; return;
} }
} }
@ -92,17 +88,13 @@ void Assigner::noteOn(uint8_t note) {
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
if (keymap[i] & 0x80) { if (keymap[i] & 0x80) {
// voice is free // voice is free
d_debug("voice %02x on in slot %02x, setting note %02x", voicemap[i], i, note);
uint8_t voice = voicemap[i]; uint8_t voice = voicemap[i];
memmove(voicemap + 1, voicemap, i); memmove(voicemap + 1, voicemap, i);
memmove(keymap + 1, keymap, i); memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on keymap[0] = note; // show note as on
voicemap[0] = voice; voicemap[0] = voice;
s.voiceOn(voice, note); ic29.voiceOn(voice, note);
return; return;
} }
} }
d_debug("unable to allocate a voice for note %02x\n", note);
} }

View File

@ -33,3 +33,5 @@ class Assigner {
uint8_t keymap[NUM_VOICES]; uint8_t keymap[NUM_VOICES];
uint8_t voicemap[NUM_VOICES]; uint8_t voicemap[NUM_VOICES];
}; };
extern Assigner ic1;

View File

@ -18,13 +18,20 @@
#include "ic29.hpp" #include "ic29.hpp"
Synth s; Synth ic29;
Synth::Synth() { Synth::Synth() {
d_debug("initialising synth\n"); d_debug("initialising synth\n");
envAtk = 0x007f; envAtk = 0x007f;
envDcy = envRls = 0xfe90; envDcy = envRls = 0xfe90;
envStn = 0x1fff; envStn = 0x1fff;
portaCoeff = 0x0;
}
void Synth::buildTables(double sampleRate) {
for (uint8_t i = 0; i < 104; i++) {
pitchTable[i] = 440.0f * powf(2, (i - 45) / 12.0f) / sampleRate;
}
} }
void Synth::run() { void Synth::run() {
@ -32,23 +39,33 @@ void Synth::run() {
// callled once every 4.3ms block of samples // callled once every 4.3ms block of samples
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
s.voices[i].env.run(); ic29.voices[i].env.run();
ic29.voices[i].calcPitch();
} }
} }
void Synth::voiceOn(uint8_t voice, uint8_t note) { void Synth::voiceOn(uint8_t voice, uint8_t note) {
// enable synth voice, start it all running // enable synth voice, start it all running
voice &= 0x7f; voice &= 0x7f;
s.voices[voice].env.on(); ic29.voices[voice].env.on();
// FIXME determine if we need to reset the counter // FIXME determine if we need to reset the counter
s.voices[voice].note = note; ic29.voices[voice].note = note;
} }
void Synth::voiceOff(uint8_t voice) { void Synth::voiceOff(uint8_t voice) {
// enable synth voice, start it all running // enable synth voice, start it all running
voice &= 0x7f; voice &= 0x7f;
s.voices[voice].env.off(); ic29.voices[voice].env.off();
}
void Synth::basePitch() {
uint16_t pitch = 0x1818;
pitch += lfoPitch;
pitch += bendPitch;
// tuning too but that's zero by default;
masterPitch = pitch;
} }
Envelope::Envelope() { Envelope::Envelope() {
@ -59,23 +76,23 @@ Envelope::Envelope() {
void Envelope::run() { void Envelope::run() {
switch (phase) { switch (phase) {
case ENV_ATK: case ENV_ATK:
level += s.envAtk; level += ic29.envAtk;
if (level > 0x3fff) { if (level > 0x3fff) {
level = 0x3fff; level = 0x3fff;
phase = ENV_DCY; phase = ENV_DCY;
} }
break; break;
case ENV_DCY: case ENV_DCY:
if (level > s.envStn) { if (level > ic29.envStn) {
level -= s.envStn; level -= ic29.envStn;
level = (level * s.envDcy) >> 16; level = (level * ic29.envDcy) >> 16;
level += s.envStn; level += ic29.envStn;
} else { } else {
level = s.envStn; level = ic29.envStn;
} }
break; break;
case ENV_RLS: case ENV_RLS:
level = (level * s.envRls) >> 16; level = (level * ic29.envRls) >> 16;
break; break;
case ENV_IDLE: case ENV_IDLE:
default: default:
@ -86,4 +103,37 @@ void Envelope::run() {
Voice::Voice() { Voice::Voice() {
} }
extern Synth s; void Voice::calcPitch() {
uint16_t target = (note - 24) << 8;
if (ic29.portaCoeff != 0) {
// porta up
if (pitch < target) {
pitch += ic29.portaCoeff;
if (pitch > target) pitch = target;
}
// porta down
if (pitch > target) {
pitch -= ic29.portaCoeff;
if (pitch < target) pitch = target;
}
} else {
pitch = target;
}
double o1 = ic29.pitchTable[pitch >> 8];
double o2 = ic29.pitchTable[(pitch >> 8) + 1];
double frac = (pitch & 0xff) / 255.0;
omega = ((o2 - o1) * frac) + o1;
}
void Voice::run(float *buffer, uint32_t samples) {
float gain = env.level / 16384.0;
gain *= 0.125;
for (uint32_t i = 0; i < samples; i++) {
phase += omega;
if (phase > 1.0f) phase -= 1.0f;
buffer[i] += phase * gain;
}
}

View File

@ -46,19 +46,22 @@ class Voice {
public: public:
Voice(); Voice();
void run(); void run();
uint8_t note; uint8_t note = 0x3c; // middle C
// private: // private:
Envelope env; // calculated envelope value Envelope env; // calculated envelope value
uint16_t pitch; // calculated pitch value with porta and master pitch etc uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
float phase=0, omega=0;
enum { V_DONE, enum { V_DONE,
V_OFF, V_OFF,
V_ON } voiceState; V_ON } voiceState;
void calcPitch();
void run(float *buffer, uint32_t samples);
}; };
class Synth { class Synth {
friend Envelope; friend Envelope;
friend Peacock; friend Voice;
public: public:
Synth(); Synth();
@ -68,15 +71,24 @@ class Synth {
void sustainSwitch(uint8_t val); void sustainSwitch(uint8_t val);
void pitchBend(int16_t bend); void pitchBend(int16_t bend);
void modWheel(uint8_t wheel); void modWheel(uint8_t wheel);
uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value void buildTables(double sampleRate);
protected:
uint16_t envAtk, envDcy, envStn, envRls;
private: double sampleRate;
uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value
// protected:
uint16_t envAtk, envDcy, envStn, envRls;
int16_t portaCoeff;
double pitchTable[104];
//private:
int16_t lfoPitch;
int16_t bendPitch;
Voice voices[NUM_VOICES]; Voice voices[NUM_VOICES];
void runLfo(); void runLfo();
void runEnvelope(Voice voice); void basePitch();
}; };
// global // global
extern Synth s; extern Synth ic29;

View File

@ -23,28 +23,25 @@
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
Assigner ic1;
Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) { Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
s.run(); printf("peacock constructor\n");
ic29.buildTables(getSampleRate());
} }
void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) { void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
// handle MIDI events, starting at lastEvent and continuing until timeLimit // handle MIDI events, starting at lastEvent and continuing until timeLimit
uint32_t i;
if (count == 0) return; // no events to do, at all if (count == 0) return; // no events to do, at all
for (uint32_t i = lastEvent; i < count; i++) { for (i = lastEvent; i < count; i++) {
if (ev[i].frame > timeLimit) break; // exceeded the time limit if (ev[i].frame > timeLimit) break; // exceeded the time limit
ic1.handleMidi((const MidiEvent *)&ev[i]); ic1.handleMidi((const MidiEvent *)&ev[i]);
} }
lastEvent = i;
} }
void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) { void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
// no content yet
// dispose of parameters
(void)outputs;
// calculate an entire jack period's worth of samples // calculate an entire jack period's worth of samples
// harder than it sounds because for short jack periods there may be many // harder than it sounds because for short jack periods there may be many
// such calls between 4.3ms control updates, or there may be many updates // such calls between 4.3ms control updates, or there may be many updates
@ -63,22 +60,30 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
lastEvent = 0; lastEvent = 0;
runMidi(midiEvents, midiEventCount, blockLeft); runMidi(midiEvents, midiEventCount, blockLeft);
// generate a buffer's worth of samples
memset(outputs[0], 0, sizeof(float) * frames);
while (framePos < frames) { while (framePos < frames) {
if (blockLeft == 0) { if (blockLeft == 0) {
blockLeft = (int)(getSampleRate() * 0.0043); // how many samples per 4.3ms block? blockLeft = (int)(getSampleRate() * 0.0043); // how many samples per 4.3ms block?
// handle all MIDI events for this block, up to the end of the block and frame // handle all MIDI events for this block, up to the end of the block and frame
runMidi(midiEvents, midiEventCount, framePos + blockLeft); runMidi(midiEvents, midiEventCount, framePos + blockLeft);
s.run(); ic29.run();
} }
sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft; sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft;
// run every synth voice into the buffer here FIXME // run every synth voice into the buffer here FIXME
for (uint8_t i = 0; i < NUM_VOICES; i++) {
ic29.voices[i].run(outputs[0] + framePos, sizeThisTime);
}
framePos += sizeThisTime; // move along the frame framePos += sizeThisTime; // move along the frame
framesLeft -= sizeThisTime; framesLeft -= sizeThisTime;
blockLeft -= sizeThisTime; blockLeft -= sizeThisTime;
} }
// output processing goes here
memcpy(outputs[1], outputs[0], sizeof(float) * frames);
} }
Plugin *createPlugin() { return new Peacock(); } Plugin *createPlugin() { return new Peacock(); }

View File

@ -32,9 +32,9 @@ class Peacock : public Plugin {
}; };
Peacock(); Peacock();
float sampleRate;
protected: protected:
float sampleRate;
const char *getLabel() const override { return "peacock-8"; } const char *getLabel() const override { return "peacock-8"; }
const char *getDescription() const override { const char *getDescription() const override {