lfo, partial sub

This commit is contained in:
Gordon JC Pearce 2025-01-07 14:02:31 +00:00
parent 81904ead80
commit a06266892e
3 changed files with 116 additions and 12 deletions

View File

@ -38,7 +38,6 @@ void AlphaOsc::initAudioPort(bool input, uint32_t index, AudioPort &port) {
void AlphaOsc::activate() { void AlphaOsc::activate() {
// calculate filter coefficients and stuff // calculate filter coefficients and stuff
printf("called activate()\n"); printf("called activate()\n");
omega = (1 << 31) / sampleRate * 130;
lfoOmega = (1 << 31) / sampleRate * 3.51; lfoOmega = (1 << 31) / sampleRate * 3.51;
omega = (130 / sampleRate) * (1 << 23); omega = (130 / sampleRate) * (1 << 23);
} }
@ -57,16 +56,23 @@ void AlphaOsc::run(const float **, float **outputs, uint32_t frames, const MidiE
uint16_t i; uint16_t i;
uint32_t osc; uint32_t osc;
uint8_t lfo; uint8_t lfo;
float saw, sqr, sub, wave; float saw, sqr, sub;
float oct1, oct3, pwg; float oct1, oct3, pwg;
float sub1, sub2;
float out, in; float out, in;
// steeply "logarithmic" curve similar to the Juno 106 LFO rate curve
// goes from about 0.1Hz to about 60Hz because this "feels about right"
// totally unscientific
lfoOmega = (0.1 + (pwmrate / (1 + (1 - pwmrate) * 4.75)*60)) / sampleRate * (1<<23);
omega = (130 / sampleRate) * (1 << 23);
// calculate an entire block of samples // calculate an entire block of samples
for (i = 0; i < frames; i++) { for (i = 0; i < frames; i++) {
// increment phase of saw counter // increment phase of saw counter
// phase is a 32-bit counter because we're on a PC and we can afford to be profligate with silicon // phase is a 24-bit counter because we're on a PC and we can afford to be profligate with silicon
// the actual Alpha Juno oscillators might well have been 8-bit for reasons loosely explained in the README // the actual Alpha Juno oscillators might well have been 8-bit for reasons loosely explained in the README
phase += omega; phase += omega;
@ -78,10 +84,10 @@ void AlphaOsc::run(const float **, float **outputs, uint32_t frames, const MidiE
osc = phase >> 14; osc = phase >> 14;
// LFO is 7-bit triangle // LFO is 7-bit triangle
lfo = (lfoPhase >> 24) & 0x7f; lfo = (lfoPhase >> 16) & 0x7f;
lfo = (lfoPhase & 0x80000000) ? lfo : 127 - lfo; lfo = (lfoPhase & 0x00800000) ? lfo : 127 - lfo;
pw = 0; pw = lfo * pwmdepth;
// the oscillator outputs in the chip are probably digital signals // the oscillator outputs in the chip are probably digital signals
// with the saw being the 8-bit outputs of the counter // with the saw being the 8-bit outputs of the counter
@ -95,13 +101,40 @@ void AlphaOsc::run(const float **, float **outputs, uint32_t frames, const MidiE
sqr = (float)(osc & 0x80) != 0; sqr = (float)(osc & 0x80) != 0;
oct1 = (float)(osc & 0x40) != 0; oct1 = (float)(osc & 0x40) != 0;
oct3 = (float)(osc & 0x10) != 0; oct3 = (float)(osc & 0x10) != 0;
sub1 = (float)(osc & 0x40) != 0;
sub2 = (float)(osc & 0x10) != 0;
// pulse width gate // pulse width gate
// lower seven bits of the saw osc, compared with PW setting // lower seven bits of the saw osc, compared with PW setting
pw = lfo*0.5;
pwg = (float)((osc & 0x7f) >= pw) != 0; pwg = (float)((osc & 0x7f) >= pw) != 0;
// calculate the oscillator output // calculate the oscillator output
switch (submode) {
case 0:
sub = sub1;
break; // one octave down
case 1:
sub = sub1 * sqr;
break; // saw is fine, do nothing
case 2:
saw *= oct1; // pulsed
break;
case 3:
saw *= pwg; // pwm
break;
case 4:
saw *= oct3; // oct3 pulse
break;
case 5:
saw *= oct1 * oct3; // both pulse
break;
}
switch (sqrmode) { switch (sqrmode) {
case 0: case 0:
case 4: case 4:
@ -137,6 +170,9 @@ void AlphaOsc::run(const float **, float **outputs, uint32_t frames, const MidiE
break; break;
} }
// mix the signals, probably done with some resistors in the chip // mix the signals, probably done with some resistors in the chip
in = (saw * 0.8) + (sqr * 0.63); // scaled similarly to Juno 106 in = (saw * 0.8) + (sqr * 0.63); // scaled similarly to Juno 106

View File

@ -27,10 +27,11 @@ class AlphaOsc : public Plugin {
enum Parameters { enum Parameters {
pSqrMode, pSqrMode,
pSawMode, pSawMode,
//pSubMode, pSubMode,
pSubLevel,
//pLFORate, pPWMRate,
//pLFODepth, pPWMDepth,
parameterCount parameterCount
}; };
@ -59,7 +60,6 @@ class AlphaOsc : public Plugin {
void deactivate() override; void deactivate() override;
void run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) override; void run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) override;
private: private:
double sampleRate; double sampleRate;
uint32_t omega, lfoOmega; uint32_t omega, lfoOmega;
@ -67,9 +67,10 @@ class AlphaOsc : public Plugin {
uint8_t pw; uint8_t pw;
float pwmrate, pwmdepth, sublevel;
uint8_t sqrmode, sawmode, submode; uint8_t sqrmode, sawmode, submode;
float hpfx, hpfy; float hpfx = 0, hpfy = 0;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AlphaOsc); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AlphaOsc);
}; };

View File

@ -74,6 +74,61 @@ void AlphaOsc::initParameter(uint32_t index, Parameter& parameter) {
parameter.enumValues.values = enumValues; parameter.enumValues.values = enumValues;
} }
break; break;
case pSubMode:
// set up Sub slider
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Sub";
parameter.symbol = "ao_sub";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 5.0f;
parameter.ranges.def = 1.0f;
parameter.enumValues.count = 6;
parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[6];
enumValues[0].value = 0;
enumValues[0].label = "-1 oct";
enumValues[1].value = 1;
enumValues[1].label = "-1 oct 25%";
enumValues[2].value = 2;
enumValues[2].label = "-1 oct + 2 oct";
enumValues[3].value = 3;
enumValues[3].label = "-1 oct + 3 oct";
enumValues[3].value = 4;
enumValues[3].label = "-2 oct";
enumValues[3].value = 5;
enumValues[3].label = "-2 oct 25%";
parameter.enumValues.values = enumValues;
}
break;
case pSubLevel:
// set up Sub slider
parameter.hints = kParameterIsAutomatable;
parameter.name = "Sub Level";
parameter.symbol = "ao_sublev";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.0f;
break;
case pPWMRate:
// set up PWM Rate slider
parameter.hints = kParameterIsAutomatable;
parameter.name = "PWM Rate";
parameter.symbol = "ao_pwmrate";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.5f;
break;
case pPWMDepth:
// set up PWM Depth slider
parameter.hints = kParameterIsAutomatable;
parameter.name = "PWM Depth";
parameter.symbol = "ao_pwmdepth";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.5f;
break;
} }
} }
@ -85,6 +140,18 @@ void AlphaOsc::setParameterValue(uint32_t index, float value) {
case pSawMode: case pSawMode:
sawmode = value; sawmode = value;
break; break;
case pSubMode:
submode = value;
break;
case pSubLevel:
sublevel = value;
break;
case pPWMDepth:
pwmdepth = value;
break;
case pPWMRate:
pwmrate = value;
break;
} }
// we don't need to handle a default condition // we don't need to handle a default condition
// if there's a parameter left unhandled, it's probably // if there's a parameter left unhandled, it's probably