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 "ic29.hpp"
#include "ic29.hpp"
#include "peacock.hpp"
Assigner ic1;
void Assigner::printMap() {
printf("note memory:\n");
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) {
// Poly 1 note off
d_debug("Note Off %02x", note);
for (uint8_t i = 0; i < NUM_VOICES; i++) {
if (keymap[i] == note) {
d_debug("Found note at slot %02x, moving to last", i);
uint8_t voice = voicemap[i]; // save the voice
memmove(voicemap + i, voicemap + i + 1, NUM_VOICES - i - 1);
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
voicemap[NUM_VOICES - 1] = voice;
keymap[NUM_VOICES - 1] = note | 0x80;
s.voiceOff(voice);
ic29.voiceOff(voice);
}
}
printMap();
}
void Assigner::noteOn(uint8_t note) {
// Poly 1 assigner
// scan for the same note
for (uint8_t i = 0; i < NUM_VOICES; i++) {
if ((keymap[i] & 0x7f) == note) {
// 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];
memmove(voicemap + 1, voicemap, i);
memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on
voicemap[0] = voice;
s.voiceOn(voice, note);
ic29.voiceOn(voice, note);
return;
}
}
@ -92,17 +88,13 @@ void Assigner::noteOn(uint8_t note) {
for (uint8_t i = 0; i < NUM_VOICES; i++) {
if (keymap[i] & 0x80) {
// voice is free
d_debug("voice %02x on in slot %02x, setting note %02x", voicemap[i], i, note);
uint8_t voice = voicemap[i];
memmove(voicemap + 1, voicemap, i);
memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on
voicemap[0] = voice;
s.voiceOn(voice, note);
ic29.voiceOn(voice, note);
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 voicemap[NUM_VOICES];
};
extern Assigner ic1;

View File

@ -18,13 +18,20 @@
#include "ic29.hpp"
Synth s;
Synth ic29;
Synth::Synth() {
d_debug("initialising synth\n");
envAtk = 0x007f;
envDcy = envRls = 0xfe90;
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() {
@ -32,23 +39,33 @@ void Synth::run() {
// callled once every 4.3ms block of samples
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) {
// enable synth voice, start it all running
voice &= 0x7f;
s.voices[voice].env.on();
ic29.voices[voice].env.on();
// 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) {
// enable synth voice, start it all running
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() {
@ -59,23 +76,23 @@ Envelope::Envelope() {
void Envelope::run() {
switch (phase) {
case ENV_ATK:
level += s.envAtk;
level += ic29.envAtk;
if (level > 0x3fff) {
level = 0x3fff;
phase = ENV_DCY;
}
break;
case ENV_DCY:
if (level > s.envStn) {
level -= s.envStn;
level = (level * s.envDcy) >> 16;
level += s.envStn;
if (level > ic29.envStn) {
level -= ic29.envStn;
level = (level * ic29.envDcy) >> 16;
level += ic29.envStn;
} else {
level = s.envStn;
level = ic29.envStn;
}
break;
case ENV_RLS:
level = (level * s.envRls) >> 16;
level = (level * ic29.envRls) >> 16;
break;
case ENV_IDLE:
default:
@ -86,4 +103,37 @@ void Envelope::run() {
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:
Voice();
void run();
uint8_t note;
uint8_t note = 0x3c; // middle C
// private:
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,
V_OFF,
V_ON } voiceState;
void calcPitch();
void run(float *buffer, uint32_t samples);
};
class Synth {
friend Envelope;
friend Peacock;
friend Voice;
public:
Synth();
@ -68,15 +71,24 @@ class Synth {
void sustainSwitch(uint8_t val);
void pitchBend(int16_t bend);
void modWheel(uint8_t wheel);
uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value
protected:
uint16_t envAtk, envDcy, envStn, envRls;
void buildTables(double sampleRate);
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];
void runLfo();
void runEnvelope(Voice voice);
void basePitch();
};
// global
extern Synth s;
extern Synth ic29;

View File

@ -23,28 +23,25 @@
START_NAMESPACE_DISTRHO
Assigner ic1;
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) {
// handle MIDI events, starting at lastEvent and continuing until timeLimit
uint32_t i;
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
ic1.handleMidi((const MidiEvent *)&ev[i]);
}
lastEvent = i;
}
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
// 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
@ -63,22 +60,30 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
lastEvent = 0;
runMidi(midiEvents, midiEventCount, blockLeft);
// generate a buffer's worth of samples
memset(outputs[0], 0, sizeof(float) * frames);
while (framePos < frames) {
if (blockLeft == 0) {
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
runMidi(midiEvents, midiEventCount, framePos + blockLeft);
s.run();
ic29.run();
}
sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft;
// 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
framesLeft -= sizeThisTime;
blockLeft -= sizeThisTime;
}
// output processing goes here
memcpy(outputs[1], outputs[0], sizeof(float) * frames);
}
Plugin *createPlugin() { return new Peacock(); }

View File

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