202 lines
6.6 KiB
C++
202 lines
6.6 KiB
C++
/*
|
|
Alpha Juno Oscillator POC
|
|
|
|
Copyright 2024 Gordon JC Pearce <gordonjcp@gjcp.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "alphaosc.hpp"
|
|
|
|
START_NAMESPACE_DISTRHO
|
|
|
|
AlphaOsc::AlphaOsc() : Plugin(parameterCount, 0, 0), sampleRate(getSampleRate()) {
|
|
// initial code here
|
|
}
|
|
|
|
// Initialisation functions
|
|
|
|
void AlphaOsc::initAudioPort(bool input, uint32_t index, AudioPort &port) {
|
|
// port.groupId = kPortGroupStereo;
|
|
Plugin::initAudioPort(input, index, port);
|
|
|
|
if (!input && index == 0) port.name = "Osc Out";
|
|
}
|
|
|
|
// Processing functions
|
|
|
|
void AlphaOsc::activate() {
|
|
// calculate filter coefficients and stuff
|
|
printf("called activate()\n");
|
|
lfoOmega = (1 << 31) / sampleRate * 3.51;
|
|
omega = (130 / sampleRate) * (1 << 23);
|
|
}
|
|
|
|
void AlphaOsc::deactivate() {
|
|
printf("called deactivate()\n");
|
|
}
|
|
|
|
void AlphaOsc::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
|
|
bzero(outputs[0], sizeof(float) * frames);
|
|
|
|
// cast unused parameters to void for now to stop the compiler complaining
|
|
(void)midiEventCount;
|
|
(void)midiEvents;
|
|
|
|
uint16_t i;
|
|
uint32_t osc;
|
|
uint8_t lfo;
|
|
|
|
// oscillator outputs
|
|
float saw, sqr, sub, pwg;
|
|
|
|
// counter bits derived from osc
|
|
float bit4, bit5, bit6, bit7, bit8, bit9;
|
|
|
|
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
|
|
|
|
for (i = 0; i < frames; i++) {
|
|
// increment phase of saw counter
|
|
// 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
|
|
|
|
phase += omega;
|
|
lfoPhase += lfoOmega;
|
|
|
|
// now osc is a ten-bit counter, to give room for the sub osc outputs
|
|
// square output will be bit 7 of osc, 25% will be bit 7 & bit 6
|
|
// PWM square will be bit 7 & comparator, with the PWM being compared against bits 0-6
|
|
osc = phase >> 14;
|
|
|
|
// LFO is 7-bit triangle
|
|
lfo = (lfoPhase >> 16) & 0x7f;
|
|
lfo = (lfoPhase & 0x00800000) ? lfo : 127 - lfo;
|
|
|
|
pw = lfo * pwmdepth;
|
|
|
|
// the oscillator outputs in the chip are probably digital signals
|
|
// with the saw being the 8-bit outputs of the counter
|
|
// the square and sub signals picked off the counter bits
|
|
// and a couple of flipflops to generate the sub osc signals
|
|
|
|
// 8-bit saw scaled
|
|
saw = (osc & 0xff) / 256.0f;
|
|
|
|
// various counter bits scaled from 0-1
|
|
bit4 = (float)(osc & 0x010) != 0; // 3 octaves up
|
|
bit5 = (float)(osc & 0x020) != 0; // 2 octaves up
|
|
bit6 = (float)(osc & 0x040) != 0; // 1 octave up
|
|
bit7 = (float)(osc & 0x080) != 0; // square wave, top bit of saw counter
|
|
bit8 = (float)(osc & 0x100) != 0; // 1 octave down
|
|
bit9 = (float)(osc & 0x200) != 0; // 2 octaves down
|
|
|
|
// pulse width gate
|
|
// lower seven bits of the saw osc, compared with PW setting
|
|
pwg = (float)((osc & 0x7f) >= pw) != 0;
|
|
|
|
// calculate the oscillator output
|
|
// because all the "bits" are scaled to floats from 0 to 1
|
|
// we can just multiply them
|
|
// in the real chip it probably uses AND gates to control outputs
|
|
// including an AND gate driving the DAC latch pin
|
|
switch (submode) {
|
|
case 0:
|
|
default:
|
|
sub = bit8;
|
|
break; // one octave down
|
|
case 1:
|
|
sub = bit8 * bit7; // one octave down, 25% PW
|
|
break;
|
|
case 2:
|
|
sub = bit8 * bit6; // one octave down modulated by one octave up
|
|
break;
|
|
case 3:
|
|
sub = bit8 * bit5; // one octave down modulated by two octaves up
|
|
break;
|
|
case 4:
|
|
sub = bit9; // two octaves down
|
|
break;
|
|
case 5:
|
|
sub = bit9 * bit8; // two octaves down, 25% PW
|
|
break;
|
|
}
|
|
|
|
switch (sqrmode) {
|
|
case 0:
|
|
case 4:
|
|
default:
|
|
sqr = 0; // oscillator is off
|
|
break;
|
|
case 1: // fundamental
|
|
sqr = bit7;
|
|
break;
|
|
case 2:
|
|
sqr = bit7 * bit6; // 25% pulse
|
|
break;
|
|
case 3:
|
|
sqr = bit7 * pwg; // pwm
|
|
break;
|
|
}
|
|
|
|
switch (sawmode) {
|
|
case 0:
|
|
default:
|
|
saw = 0;
|
|
break; // oscillator is off
|
|
case 1:
|
|
break; // saw is fine, do nothing
|
|
case 2:
|
|
saw *= bit6; // pulsed
|
|
break;
|
|
case 3:
|
|
saw *= pwg; // pwm
|
|
break;
|
|
case 4:
|
|
saw *= bit4; // oct3 pulse
|
|
break;
|
|
case 5:
|
|
saw *= bit6 * bit4; // both pulse
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
|
|
// mix the signals, probably done with some resistors in the chip
|
|
in = (sub * sublevel) + (saw * 0.8) + (sqr * 0.63); // scaled similarly to Juno 106
|
|
|
|
// DC removal highpass filter
|
|
// this is very approximately 6Hz at 44.1kHz and 48kHz
|
|
// honestly it doesn't matter all that much if it's wrong at higher sample rates
|
|
out = in - hpfx + .99915 * hpfy;
|
|
hpfx = in;
|
|
hpfy = out;
|
|
|
|
outputs[0][i] = out*0.5;
|
|
}
|
|
// printf("%f %f\n", sqr, saw);
|
|
}
|
|
|
|
// create the plugin
|
|
Plugin *createPlugin() { return new AlphaOsc(); }
|
|
|
|
END_NAMESPACE_DISTRHO
|