diff --git a/plugin/ic1.cpp b/plugin/ic1.cpp index 9d0d811..35b8f46 100644 --- a/plugin/ic1.cpp +++ b/plugin/ic1.cpp @@ -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); } diff --git a/plugin/ic1.hpp b/plugin/ic1.hpp index 6bfe633..f5dffa3 100644 --- a/plugin/ic1.hpp +++ b/plugin/ic1.hpp @@ -33,3 +33,5 @@ class Assigner { uint8_t keymap[NUM_VOICES]; uint8_t voicemap[NUM_VOICES]; }; + +extern Assigner ic1; \ No newline at end of file diff --git a/plugin/ic29.cpp b/plugin/ic29.cpp index ebdc626..26eb674 100644 --- a/plugin/ic29.cpp +++ b/plugin/ic29.cpp @@ -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; \ No newline at end of file +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; + } +} diff --git a/plugin/ic29.hpp b/plugin/ic29.hpp index 3f0b94a..9b17dac 100644 --- a/plugin/ic29.hpp +++ b/plugin/ic29.hpp @@ -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; diff --git a/plugin/peacock.cpp b/plugin/peacock.cpp index a8b556d..ca77b35 100644 --- a/plugin/peacock.cpp +++ b/plugin/peacock.cpp @@ -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(); } diff --git a/plugin/peacock.hpp b/plugin/peacock.hpp index 961eeaf..4892eaf 100644 --- a/plugin/peacock.hpp +++ b/plugin/peacock.hpp @@ -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 {