various envelope and control range fixes, also an audio PWM bug
This commit is contained in:
parent
66d2cec2c9
commit
3cfe118acc
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "peacock.hpp"
|
#include "peacock.hpp"
|
||||||
|
#include "ic29.hpp"
|
||||||
|
|
||||||
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
@ -311,3 +312,107 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
|||||||
}
|
}
|
||||||
// chorus, porta, bend range, key mode still to do
|
// chorus, porta, bend range, key mode still to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Peacock::setParameterValue(uint32_t index, float value) {
|
||||||
|
// should be trapped by host, but let's be safe
|
||||||
|
if (value < 0.0f) value = 0.0f;
|
||||||
|
if (value > 127.0f) value = 127.0f;
|
||||||
|
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case p_lfoRate:
|
||||||
|
ic29.patchRam.lfoRate = value;
|
||||||
|
break;
|
||||||
|
case p_lfoDelay:
|
||||||
|
ic29.patchRam.lfoDelay = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcoLfoMod:
|
||||||
|
ic29.patchRam.vcoLfoMod = value;
|
||||||
|
break;
|
||||||
|
case p_pwmLfoMod:
|
||||||
|
ic29.patchRam.pwmLfoMod = value / 1.27;
|
||||||
|
break;
|
||||||
|
case p_subLevel:
|
||||||
|
ic29.patchRam.subLevel = value;
|
||||||
|
break;
|
||||||
|
case p_noiseLevel:
|
||||||
|
ic29.patchRam.noiseLevel = value;
|
||||||
|
break;
|
||||||
|
case p_vcfCutoff:
|
||||||
|
ic29.patchRam.vcfCutoff = value;
|
||||||
|
break;
|
||||||
|
case p_vcfReso:
|
||||||
|
ic29.patchRam.vcfReso = value;
|
||||||
|
break;
|
||||||
|
case p_vcfEnvMod:
|
||||||
|
ic29.patchRam.vcfEnvMod = value;
|
||||||
|
break;
|
||||||
|
case p_vcfLFoMod:
|
||||||
|
ic29.patchRam.vcfLfoMod = value;
|
||||||
|
break;
|
||||||
|
case p_vcfKeyTrk:
|
||||||
|
ic29.patchRam.vcfKeyTrk = value;
|
||||||
|
break;
|
||||||
|
case p_vcaLevel:
|
||||||
|
ic29.patchRam.vcaLevel = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_attack:
|
||||||
|
ic29.patchRam.attack = value;
|
||||||
|
break;
|
||||||
|
case p_decay:
|
||||||
|
ic29.patchRam.decay = value;
|
||||||
|
break;
|
||||||
|
case p_sustain:
|
||||||
|
ic29.patchRam.sustain = value;
|
||||||
|
break;
|
||||||
|
case p_release:
|
||||||
|
ic29.patchRam.release = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// switch 1 params
|
||||||
|
case p_vcoRange: // bits 0-2 of switch 1
|
||||||
|
// doesn't look great in Carla because of odd behaviour with small integer knobs
|
||||||
|
ic29.patchRam.switch1 &= 0xf8;
|
||||||
|
ic29.patchRam.switch1 |= (1 << (int)(value - 1));
|
||||||
|
break;
|
||||||
|
case p_sqrOn: // bit 3 of switch 1
|
||||||
|
ic29.patchRam.switch1 &= 0xf7;
|
||||||
|
ic29.patchRam.switch1 |= (value >= 0.5) << 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_sawOn: // bit 4 of switch 1
|
||||||
|
ic29.patchRam.switch1 &= 0xef;
|
||||||
|
ic29.patchRam.switch1 |= (value >= 0.5) << 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// missing chorus switch
|
||||||
|
|
||||||
|
// switch 2 params
|
||||||
|
case p_pwmMode: // bit 0 of switch 2
|
||||||
|
ic29.patchRam.switch2 &= 0xfe;
|
||||||
|
ic29.patchRam.switch2 |= (value >= 0.5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcfEnvPol: // bit 1 of switch 2
|
||||||
|
ic29.patchRam.switch2 &= 0xfd;
|
||||||
|
ic29.patchRam.switch2 |= (value >= 0.5) << 1;
|
||||||
|
break;
|
||||||
|
case p_vcaEnvGate:
|
||||||
|
ic29.patchRam.switch2 &= 0xfb;
|
||||||
|
ic29.patchRam.switch2 |= (value >= 0.5) << 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_hpfMode: // bits 3-4 of switch 2
|
||||||
|
// doesn't look great in Carla because of odd behaviour with small integer knobs
|
||||||
|
if (value > 3) value = 3;
|
||||||
|
ic29.patchRam.switch2 &= 0xf3;
|
||||||
|
ic29.patchRam.switch2 |= (int)value << 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_modWheel:
|
||||||
|
//s.ff64 = (int)value << 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -49,6 +49,8 @@ void Assigner::handleMidi(const MidiEvent *ev) {
|
|||||||
case 0x90:
|
case 0x90:
|
||||||
noteOn(ev->data[1]);
|
noteOn(ev->data[1]);
|
||||||
break;
|
break;
|
||||||
|
case 0xb0:
|
||||||
|
break; // nothing to do here except in special cases where we don't expect the host to pass on controls
|
||||||
default:
|
default:
|
||||||
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
|
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
|
||||||
break;
|
break;
|
||||||
|
@ -25,7 +25,6 @@ Synth ic29;
|
|||||||
Synth::Synth() {
|
Synth::Synth() {
|
||||||
d_debug("initialising synth\n");
|
d_debug("initialising synth\n");
|
||||||
portaCoeff = 0x0;
|
portaCoeff = 0x0;
|
||||||
lfo.speed = 0x06;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::buildTables(double sampleRate) {
|
void Synth::buildTables(double sampleRate) {
|
||||||
@ -54,7 +53,7 @@ void Synth::run() {
|
|||||||
uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
|
uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
|
||||||
if (ic29.patchRam.switch2 & 0x01) pwmVal = 0x3fff;
|
if (ic29.patchRam.switch2 & 0x01) pwmVal = 0x3fff;
|
||||||
|
|
||||||
ic29.pwm = 0.5 - pwmVal / 40960.0f * (ic29.patchRam.pwmLfoMod/128.0f);
|
ic29.pwm = 0.5 - pwmVal / 32768.0f * (ic29.patchRam.pwmLfoMod / 106.0f);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||||
ic29.voices[i].update();
|
ic29.voices[i].update();
|
||||||
@ -88,7 +87,7 @@ void LFO::run() {
|
|||||||
// slightly different from the real synth code which does not use signed
|
// slightly different from the real synth code which does not use signed
|
||||||
// variables, since the CPU doesn't support them
|
// variables, since the CPU doesn't support them
|
||||||
|
|
||||||
lfoOut += phase ? lfoRateTable[speed] : -lfoRateTable[speed];
|
lfoOut += phase ? lfoRateTable[rate] : -lfoRateTable[rate];
|
||||||
if (lfoOut > 0x1fff) {
|
if (lfoOut > 0x1fff) {
|
||||||
lfoOut = 0x1fff;
|
lfoOut = 0x1fff;
|
||||||
phase = 0;
|
phase = 0;
|
||||||
@ -103,7 +102,7 @@ void LFO::run() {
|
|||||||
|
|
||||||
Envelope::Envelope() {
|
Envelope::Envelope() {
|
||||||
level = 0;
|
level = 0;
|
||||||
phase = ENV_IDLE;
|
phase = ENV_RLS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Envelope::run() {
|
void Envelope::run() {
|
||||||
@ -180,19 +179,26 @@ void Voice::update() {
|
|||||||
// calculate the once-per-block values
|
// calculate the once-per-block values
|
||||||
env.run();
|
env.run();
|
||||||
calcPitch();
|
calcPitch();
|
||||||
if (ic29.patchRam.switch1 & 0x08) pw = ic29.pwm;
|
|
||||||
else pw = 0;
|
|
||||||
|
|
||||||
saw = (float)((ic29.patchRam.switch1 & 0x10) == true);
|
pw = (ic29.patchRam.switch1 & 0x08) ? ic29.pwm : 0.0f;
|
||||||
|
saw = (ic29.patchRam.switch1 & 0x10) ? 1.0f : 0.0f;
|
||||||
sub = ic29.patchRam.subLevel / 128.0f;
|
sub = ic29.patchRam.subLevel / 128.0f;
|
||||||
|
|
||||||
|
ic29.lfo.rate = ic29.patchRam.lfoRate;
|
||||||
|
|
||||||
// do filter values
|
// do filter values
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::on(uint8_t key) {
|
void Voice::on(uint8_t key) {
|
||||||
voiceState = V_ON;
|
voiceState = V_ON;
|
||||||
|
if (note != key) {
|
||||||
|
phase = 0;
|
||||||
|
}
|
||||||
note = key;
|
note = key;
|
||||||
|
if (env.phase == env.ENV_RLS) {
|
||||||
env.on(); // FIXME move to synth update code
|
env.on(); // FIXME move to synth update code
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::off() {
|
void Voice::off() {
|
||||||
|
@ -25,7 +25,7 @@ class LFO {
|
|||||||
LFO();
|
LFO();
|
||||||
void run();
|
void run();
|
||||||
int16_t lfoOut;
|
int16_t lfoOut;
|
||||||
uint8_t speed;
|
uint8_t rate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t
|
uint8_t
|
||||||
@ -56,13 +56,14 @@ class Envelope {
|
|||||||
|
|
||||||
uint16_t level;
|
uint16_t level;
|
||||||
|
|
||||||
// private:
|
|
||||||
enum {
|
enum {
|
||||||
ENV_ATK,
|
ENV_ATK,
|
||||||
ENV_DCY,
|
ENV_DCY,
|
||||||
ENV_RLS,
|
ENV_RLS,
|
||||||
ENV_IDLE
|
ENV_IDLE
|
||||||
} phase;
|
} phase;
|
||||||
|
|
||||||
|
private:
|
||||||
static const uint16_t atkTable[128];
|
static const uint16_t atkTable[128];
|
||||||
static const uint16_t dcyTable[128];
|
static const uint16_t dcyTable[128];
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,7 @@ void Voice::run(float *buffer, uint32_t samples) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth
|
delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth
|
||||||
delay += (0.63 - (pw * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level
|
delay += (0.63 - (pwrc * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level
|
||||||
// the DC correction is important because the hardware synth is AC-coupled effectively high-passing
|
// the DC correction is important because the hardware synth is AC-coupled effectively high-passing
|
||||||
// the signal at about 10Hz or so, preventing any PWM rumble from leaking through!
|
// the signal at about 10Hz or so, preventing any PWM rumble from leaking through!
|
||||||
delay += subosc;
|
delay += subosc;
|
||||||
|
@ -74,7 +74,7 @@ class Peacock : public Plugin {
|
|||||||
// void initAudioPort(bool input, uint32_t index, AudioPort &port) override;
|
// void initAudioPort(bool input, uint32_t index, AudioPort &port) override;
|
||||||
void initParameter(uint32_t index, Parameter ¶meter) override;
|
void initParameter(uint32_t index, Parameter ¶meter) override;
|
||||||
|
|
||||||
// void setParameterValue(uint32_t index, float value) override;
|
void setParameterValue(uint32_t index, float value) override;
|
||||||
// float getParameterValue(uint32_t index) const override;
|
// float getParameterValue(uint32_t index) const override;
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
Loading…
Reference in New Issue
Block a user