Compare commits
No commits in common. "cleanup" and "main" have entirely different histories.
1
Makefile
1
Makefile
@ -9,7 +9,6 @@
|
||||
|
||||
include dpf/Makefile.base.mk
|
||||
|
||||
CFLAGS += -ggdb
|
||||
all: plugins gen
|
||||
|
||||
plugins:
|
||||
|
@ -12,10 +12,9 @@ NAME = peacock
|
||||
FILES_DSP = \
|
||||
peacock.cpp \
|
||||
controls.cpp \
|
||||
assigner.cpp \
|
||||
ic1.cpp \
|
||||
oscillator.cpp \
|
||||
filter.cpp \
|
||||
voiceboard.cpp
|
||||
ic29.cpp
|
||||
|
||||
include ../dpf/Makefile.plugins.mk
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "assigner.hpp"
|
||||
#include "peacock.hpp"
|
||||
|
||||
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
||||
@ -291,127 +290,24 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
||||
parameter.midiCC = 26;
|
||||
break;
|
||||
|
||||
case p_vcoBend:
|
||||
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
|
||||
parameter.name = "VCO Bend";
|
||||
parameter.symbol = "pfau_vcobend";
|
||||
case p_modWheel:
|
||||
parameter.hints = kParameterIsAutomatable | kParameterIsHidden;
|
||||
parameter.name = "Mod wheel";
|
||||
parameter.symbol = "pfau_modwheel";
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 127.0f;
|
||||
parameter.ranges.def = 43.0f;
|
||||
parameter.ranges.def = 0.0f;
|
||||
parameter.midiCC = 1;
|
||||
break;
|
||||
case p_vcfBend:
|
||||
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
|
||||
parameter.name = "VCF Bend";
|
||||
parameter.symbol = "pfau_vcfbend";
|
||||
case p_holdPedal:
|
||||
parameter.hints = kParameterIsAutomatable | kParameterIsHidden;
|
||||
parameter.name = "Hold Pedal";
|
||||
parameter.symbol = "pfau_holdpedal";
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 127.0f;
|
||||
parameter.ranges.def = 43.0f;
|
||||
parameter.ranges.def = 0.0f;
|
||||
parameter.midiCC = 64;
|
||||
break;
|
||||
}
|
||||
// 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;
|
||||
|
||||
#if 0
|
||||
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 << (uint8_t)(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 |= (uint8_t)value << 3;
|
||||
break;
|
||||
case p_vcoBend:
|
||||
ic29.vcoBend = (uint8_t)value;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
Peacock-8 VA polysynth
|
||||
|
||||
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 "voiceboard.hpp"
|
||||
|
||||
void Voice::filter(float *buffer, uint32_t pos, uint32_t frames) {
|
||||
for (uint32_t i = 0; i < frames; i++) {
|
||||
for (uint8_t ovs = 0; ovs < 4; ovs++) {
|
||||
fb = b4;
|
||||
// hard clip
|
||||
if (fb > 1) fb = 1;
|
||||
if (fb < -1) fb = -1;
|
||||
|
||||
fb = buffer[i+pos] - (fb * reso);
|
||||
b1 = ((fb - b1) * cut) + b1;
|
||||
b2 = ((b1 - b2) * cut) + b2;
|
||||
b3 = ((b2 - b3) * cut) + b3;
|
||||
b4 = ((b3 - b4) * cut) + b4;
|
||||
}
|
||||
|
||||
buffer[i+pos] = b4;
|
||||
}
|
||||
}
|
@ -16,13 +16,14 @@
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "assigner.hpp"
|
||||
#include "ic1.hpp"
|
||||
|
||||
#include "voiceboard.hpp"
|
||||
#include "ic29.hpp"
|
||||
#include "peacock.hpp"
|
||||
|
||||
Assigner ic1;
|
||||
|
||||
void Assigner::printMap() {
|
||||
return;
|
||||
printf("note memory:\n");
|
||||
for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]);
|
||||
printf("\n");
|
||||
@ -34,7 +35,7 @@ Assigner::Assigner() {
|
||||
// set up the assigner
|
||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||
voicemap[i] = 0x80 + i;
|
||||
keymap[i] = 0xff;
|
||||
keymap[i] = 0x3c | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,18 +49,6 @@ void Assigner::handleMidi(const MidiEvent *ev) {
|
||||
case 0x90:
|
||||
noteOn(ev->data[1]);
|
||||
break;
|
||||
case 0xb0:
|
||||
switch (ev->data[1]) {
|
||||
case 0x01:
|
||||
_ic29->modWheel = ev->data[2];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break; // nothing to do here except in special cases where we don't expect the host to pass on controls
|
||||
case 0xe0: // pitch bend;
|
||||
_ic29->pitchBend = ev->data[2] << 7 | ev->data[1];
|
||||
break;
|
||||
default:
|
||||
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
|
||||
break;
|
||||
@ -74,8 +63,7 @@ void Assigner::noteOff(uint8_t note) {
|
||||
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
|
||||
voicemap[NUM_VOICES - 1] = voice;
|
||||
keymap[NUM_VOICES - 1] = note | 0x80;
|
||||
_ic29->voiceOff(voice);
|
||||
printMap();
|
||||
ic29.voiceOff(voice);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,8 +79,7 @@ void Assigner::noteOn(uint8_t note) {
|
||||
memmove(keymap + 1, keymap, i);
|
||||
keymap[0] = note; // show note as on
|
||||
voicemap[0] = voice;
|
||||
_ic29->voiceOn(voice, note);
|
||||
printMap();
|
||||
ic29.voiceOn(voice, note);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -106,8 +93,7 @@ void Assigner::noteOn(uint8_t note) {
|
||||
memmove(keymap + 1, keymap, i);
|
||||
keymap[0] = note; // show note as on
|
||||
voicemap[0] = voice;
|
||||
_ic29->voiceOn(voice, note);
|
||||
printMap();
|
||||
ic29.voiceOn(voice, note);
|
||||
return;
|
||||
}
|
||||
}
|
@ -18,27 +18,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DistrhoPlugin.hpp"
|
||||
#define NUM_VOICES 8
|
||||
|
||||
#include "voiceboard.hpp"
|
||||
#include "peacock.hpp"
|
||||
|
||||
class Assigner {
|
||||
public:
|
||||
Assigner();
|
||||
void setVoiceBoard(Synth& s) {
|
||||
_ic29 = &s;
|
||||
}
|
||||
void handleMidi(const MidiEvent *ev);
|
||||
|
||||
|
||||
private:
|
||||
void noteOn(uint8_t note);
|
||||
void noteOff(uint8_t note);
|
||||
void printMap();
|
||||
|
||||
Synth* _ic29;
|
||||
|
||||
uint8_t keymap[NUM_VOICES];
|
||||
uint8_t voicemap[NUM_VOICES];
|
||||
};
|
||||
|
||||
extern Assigner ic1;
|
@ -16,16 +16,20 @@
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "voiceboard.hpp"
|
||||
#include "ic29.hpp"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "DistrhoPlugin.hpp"
|
||||
#include "ic29tables.hpp"
|
||||
|
||||
#define DEBUG
|
||||
Synth ic29;
|
||||
|
||||
Synth::Synth() {
|
||||
d_debug("initialising synth\n");
|
||||
envAtk = 0x00;
|
||||
envDcy = 0x1f;
|
||||
envStn = 0x00;
|
||||
envRls = 0x1f;
|
||||
portaCoeff = 0x0;
|
||||
lfo.speed = 0x06;
|
||||
}
|
||||
|
||||
void Synth::buildTables(double sampleRate) {
|
||||
@ -35,64 +39,42 @@ void Synth::buildTables(double sampleRate) {
|
||||
// on the real synth the tuning knob is tweaked a little off to pull it in
|
||||
pitchTable[i] = 260.15f * powf(2, (i - 36) / 12.0f) / sampleRate;
|
||||
}
|
||||
|
||||
// precompute a table of values to map the filter value to a filter coefficient
|
||||
// the ROM preset for adjusting the VCF scale and centre presets cutoff to $31
|
||||
// and key scale to $7f, which corresponds to C4 = 248Hz and C6 = 992Hz, B3 and B5
|
||||
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::run() {
|
||||
// handle a "loop" worth of envelopes, pitch calculations, etc
|
||||
// callled once every 4.3ms block of samples
|
||||
|
||||
lfo.run();
|
||||
ic29.lfo.run();
|
||||
|
||||
masterPitch = 0x1818;
|
||||
uint16_t vcoLfoDepth = lfoDepthTable[patchRam.vcoLfoMod] + modWheel;
|
||||
vcoLfoDepth = (vcoLfoDepth < 0xff) ? vcoLfoDepth : 0xff;
|
||||
masterPitch += (lfo.lfoOut * vcoLfoDepth) >> 11;
|
||||
|
||||
if (pitchBend < 0x0100) pitchBend = 0x0100;
|
||||
masterPitch += (((pitchBend >> 8) - 0x20) * vcoBend) / 1.281;
|
||||
|
||||
// need to calculate VCF "base" setting
|
||||
// need to calculate PWM
|
||||
// various on/off switches
|
||||
|
||||
// PWM is bit 0 sw2, 0 = fixed 1 = lfo
|
||||
// 0 sets EA to 0x3fff, 1 adds
|
||||
uint16_t pwmVal = 0x2000 - lfo.lfoOut;
|
||||
if (patchRam.switch2 & 0x01) pwmVal = 0x3fff;
|
||||
pwm = 0.5 - pwmVal / 32768.0f * (patchRam.pwmLfoMod / 106.0f);
|
||||
uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
|
||||
if (0) pwmVal = 0x3fff;
|
||||
|
||||
// generate the voices, then
|
||||
for (uint32_t i = 0; i < bufferSize; i++) {
|
||||
tr21 = (tr21 * 519) + 3;
|
||||
// noise[i] = (1 - (tr21 & 0x00ffffff) / 8388608.0f) * (patchRam.noiseLevel * 0.0063);
|
||||
}
|
||||
|
||||
// printf("updating\n");
|
||||
ic29.pwm = pwmVal / 40960.0f * (1); // 0.5 is knob val
|
||||
|
||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||
voices[i].update();
|
||||
ic29.voices[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::voiceOn(uint8_t voice, uint8_t note) {
|
||||
// enable synth voice, start it all running
|
||||
voice &= 0x7f;
|
||||
|
||||
d_debug("voiceOn %02x %02x", voice, note);
|
||||
|
||||
voices[voice].on(note);
|
||||
ic29.voices[voice].on(note);
|
||||
}
|
||||
|
||||
void Synth::voiceOff(uint8_t voice) {
|
||||
// enable synth voice, start it all running
|
||||
voice &= 0x7f;
|
||||
voices[voice].off();
|
||||
ic29.voices[voice].off();
|
||||
}
|
||||
|
||||
LFO::LFO() {
|
||||
@ -110,7 +92,7 @@ void LFO::run() {
|
||||
// slightly different from the real synth code which does not use signed
|
||||
// variables, since the CPU doesn't support them
|
||||
|
||||
lfoOut += phase ? lfoRateTable[rate] : -lfoRateTable[rate];
|
||||
lfoOut += phase ? lfoRateTable[speed] : -lfoRateTable[speed];
|
||||
if (lfoOut > 0x1fff) {
|
||||
lfoOut = 0x1fff;
|
||||
phase = 0;
|
||||
@ -123,35 +105,30 @@ void LFO::run() {
|
||||
// printf("lfoOut=%04x\n", lfoOut);
|
||||
}
|
||||
|
||||
uint16_t Envelope::atk = 0x10;
|
||||
uint16_t Envelope::dcy = 0x3f;
|
||||
uint16_t Envelope::stn = 0x3f;
|
||||
uint16_t Envelope::rls = 0x1f;
|
||||
|
||||
Envelope::Envelope() {
|
||||
level = 0;
|
||||
phase = ENV_RLS;
|
||||
phase = ENV_IDLE;
|
||||
}
|
||||
|
||||
void Envelope::run() {
|
||||
uint16_t temp = stn << 7;
|
||||
uint16_t tempStn = ic29.envStn << 7;
|
||||
switch (phase) {
|
||||
case ENV_ATK:
|
||||
level += atkTable[atk];
|
||||
level += atkTable[ic29.envAtk];
|
||||
if (level > 0x3fff) {
|
||||
level = 0x3fff;
|
||||
phase = ENV_DCY;
|
||||
}
|
||||
break;
|
||||
case ENV_DCY:
|
||||
if (level > temp) {
|
||||
level = (((level - temp) * dcyTable[dcy]) >> 16) + temp;
|
||||
if (level > tempStn) {
|
||||
level = (((level - tempStn) * dcyTable[ic29.envDcy]) >> 16) + tempStn;
|
||||
} else {
|
||||
level = temp;
|
||||
level = tempStn;
|
||||
}
|
||||
break;
|
||||
case ENV_RLS:
|
||||
level = (level * dcyTable[rls]) >> 16;
|
||||
level = (level * dcyTable[ic29.envRls]) >> 16;
|
||||
break;
|
||||
case ENV_IDLE:
|
||||
default:
|
||||
@ -160,10 +137,10 @@ void Envelope::run() {
|
||||
}
|
||||
|
||||
Voice::Voice() {
|
||||
subosc = .1;
|
||||
}
|
||||
|
||||
void Voice::calcPitch() {
|
||||
uint16_t ea;
|
||||
uint16_t target = note << 8;
|
||||
|
||||
// Portamento is a linear change of pitch - it'll take twice as long
|
||||
@ -188,17 +165,17 @@ void Voice::calcPitch() {
|
||||
pitch = target;
|
||||
}
|
||||
|
||||
ea = pitch + ic29.masterPitch;
|
||||
pitch += ic29.masterPitch;
|
||||
|
||||
if (ea < 0x3000) ea = 0x3000; // lowest note
|
||||
if (ea > 0x9700) ea = 0x6700; // highest note
|
||||
if (pitch < 0x3000) pitch = 0x3000; // lowest note
|
||||
if (pitch > 0x9700) pitch = 0x6700; // highest note
|
||||
|
||||
ea -= 0x3000;
|
||||
pitch -= 0x3000;
|
||||
|
||||
// interpolate between the two table values
|
||||
double o1 = ic29.pitchTable[ea >> 8];
|
||||
double o2 = ic29.pitchTable[(ea >> 8) + 1];
|
||||
double frac = (ea & 0xff) / 256.0f;
|
||||
double o1 = ic29.pitchTable[pitch >> 8];
|
||||
double o2 = ic29.pitchTable[(pitch >> 8) + 1];
|
||||
double frac = (pitch & 0xff) / 256.0f;
|
||||
|
||||
omega = ((o2 - o1) * frac) + o1;
|
||||
}
|
||||
@ -207,32 +184,20 @@ void Voice::update() {
|
||||
// calculate the once-per-block values
|
||||
env.run();
|
||||
calcPitch();
|
||||
// printf("env=%04x\n", env.level);
|
||||
/*
|
||||
pw = (patchRam.switch1 & 0x08) ? ic29.pwm : 0.0f;
|
||||
saw = (ic29.patchRam.switch1 & 0x10) ? 1.0f : 0.0f;
|
||||
sub = ic29.patchRam.subLevel / 128.0f;
|
||||
|
||||
ic29.lfo.rate = ic29.patchRam.lfoRate;
|
||||
*/
|
||||
pw = 0.5 - ic29.pwm;
|
||||
// do filter values
|
||||
}
|
||||
|
||||
void Voice::on(uint8_t key) {
|
||||
d_debug("Voice on key %02x\n", key);
|
||||
voiceState = V_ON;
|
||||
if (note != key) {
|
||||
phase = 0;
|
||||
}
|
||||
note = key;
|
||||
if (env.inRelease()) {
|
||||
env.on(); // FIXME move to synth update code
|
||||
}
|
||||
env.on(); // FIXME move to synth update code
|
||||
}
|
||||
|
||||
void Voice::off() {
|
||||
d_debug("Voice off\n");
|
||||
// sustain - I need to rethink this bit FIXME
|
||||
voiceState = V_OFF;
|
||||
env.off();
|
||||
if (!ic29.sustained) {
|
||||
env.off();
|
||||
}
|
||||
}
|
@ -18,19 +18,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "peacock.hpp"
|
||||
|
||||
#define NUM_VOICES 8
|
||||
class LFO {
|
||||
public:
|
||||
LFO();
|
||||
void run();
|
||||
int16_t lfoOut;
|
||||
uint8_t rate;
|
||||
uint8_t speed;
|
||||
|
||||
private:
|
||||
uint8_t
|
||||
phase;
|
||||
phase;
|
||||
uint16_t holdoff;
|
||||
uint16_t envelope;
|
||||
enum { LFO_RUN,
|
||||
@ -42,7 +41,6 @@ class LFO {
|
||||
class Envelope {
|
||||
public:
|
||||
Envelope();
|
||||
|
||||
void run();
|
||||
void on() {
|
||||
phase = ENV_ATK;
|
||||
@ -56,49 +54,41 @@ class Envelope {
|
||||
return phase == ENV_RLS;
|
||||
}
|
||||
|
||||
static uint16_t atk, dcy, stn, rls;
|
||||
|
||||
uint16_t level;
|
||||
|
||||
private:
|
||||
static const uint16_t atkTable[128];
|
||||
static const uint16_t dcyTable[128];
|
||||
|
||||
// private:
|
||||
enum {
|
||||
ENV_ATK,
|
||||
ENV_DCY,
|
||||
ENV_RLS,
|
||||
ENV_IDLE
|
||||
} phase;
|
||||
static const uint16_t atkTable[128];
|
||||
static const uint16_t dcyTable[128];
|
||||
};
|
||||
|
||||
class Voice {
|
||||
public:
|
||||
Voice();
|
||||
uint8_t note = 0x3c; // middle C
|
||||
void run(float *buffer, uint32_t pos, uint32_t samples);
|
||||
void filter(float *buffer, uint32_t pos, uint32_t samples);
|
||||
|
||||
void run(float *buffer, uint32_t samples);
|
||||
void update();
|
||||
void on(uint8_t note);
|
||||
void off();
|
||||
|
||||
// private:
|
||||
private:
|
||||
Envelope env; // calculated envelope value
|
||||
uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
|
||||
float delay = 0; // delay slot for polyblep
|
||||
bool pulseStage = 0;
|
||||
float pw = 0, lastpw = 0, pwrc = 0;
|
||||
float delay; // delay slot for polyblep
|
||||
bool pulseStage;
|
||||
float pw, lastpw, pwrc;
|
||||
float subosc = -1;
|
||||
float sub = 0, saw = 0;
|
||||
float phase = 0, omega = 0;
|
||||
enum { V_DONE,
|
||||
V_OFF,
|
||||
V_ON } voiceState;
|
||||
|
||||
void calcPitch();
|
||||
|
||||
float fb = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, cut = 0.1, reso = 4;
|
||||
};
|
||||
|
||||
class Synth {
|
||||
@ -111,53 +101,27 @@ class Synth {
|
||||
void voiceOn(uint8_t voice, uint8_t note);
|
||||
void voiceOff(uint8_t voice);
|
||||
void sustainSwitch(uint8_t val);
|
||||
void pitchBend(int16_t bend);
|
||||
void modWheel(uint8_t wheel);
|
||||
void buildTables(double sampleRate);
|
||||
|
||||
double sampleRate;
|
||||
uint32_t bufferSize;
|
||||
uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value
|
||||
// protected:
|
||||
uint16_t envAtk, envDcy, envStn, envRls;
|
||||
int8_t portaCoeff;
|
||||
bool sustained;
|
||||
double pitchTable[104];
|
||||
double filterTable[256];
|
||||
|
||||
float *noise;
|
||||
|
||||
// private:
|
||||
uint8_t vcoBend = 42;
|
||||
uint8_t vcfBend = 42;
|
||||
|
||||
int16_t lfoPitch;
|
||||
int16_t bendPitch;
|
||||
Voice voices[NUM_VOICES];
|
||||
LFO lfo;
|
||||
uint16_t pitchBend = 0x2000;
|
||||
uint8_t modWheel = 0x00;
|
||||
|
||||
float pwm;
|
||||
uint32_t tr21;
|
||||
|
||||
struct {
|
||||
uint8_t lfoRate = 0x3f;
|
||||
uint8_t lfoDelay = 0x00;
|
||||
uint8_t vcoLfoMod = 0x00;
|
||||
uint8_t pwmLfoMod = 0x27;
|
||||
uint8_t noiseLevel = 0x00;
|
||||
uint8_t vcfCutoff = 0x4d;
|
||||
uint8_t vcfReso = 0x14;
|
||||
uint8_t vcfEnvMod = 0x04;
|
||||
uint8_t vcfLfoMod = 0x00;
|
||||
uint8_t vcfKeyTrk = 0x6f;
|
||||
uint8_t vcaLevel = 0x22;
|
||||
uint8_t attack = 0x0d;
|
||||
uint8_t decay = 0x57;
|
||||
uint8_t sustain = 0x58;
|
||||
uint8_t release = 0x23;
|
||||
uint8_t subLevel = 0x0e;
|
||||
uint8_t switch1 = 0x1a;
|
||||
uint8_t switch2 = 0x10;
|
||||
} patchRam;
|
||||
const static uint16_t lfoDelayTable[8];
|
||||
const static uint8_t lfoDepthTable[128];
|
||||
const static uint8_t portaTable[128];
|
||||
};
|
||||
|
||||
// global
|
||||
extern Synth ic29;
|
@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ic29.hpp"
|
||||
|
||||
const uint16_t Envelope::atkTable[128] = {
|
||||
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
|
||||
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
|
||||
@ -52,7 +54,7 @@ const uint16_t Envelope::dcyTable[128] = {
|
||||
0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2,
|
||||
0xfff3, 0xfff4};
|
||||
|
||||
const uint8_t Synth::lfoDepthTable[128] = {
|
||||
extern const uint8_t lfoDepthTable[128] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
|
||||
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||
@ -65,27 +67,6 @@ const uint8_t Synth::lfoDepthTable[128] = {
|
||||
0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
|
||||
0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf8, 0xff, 0xff};
|
||||
|
||||
const uint16_t Synth::lfoDelayTable[8] = {
|
||||
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
|
||||
|
||||
const uint8_t Synth::portaTable[128] = {
|
||||
0x00, 0xff, 0xf7, 0xef, 0xe7, 0xdf, 0xd7, 0xcf,
|
||||
0xc7, 0xbf, 0xb7, 0xaf, 0xa7, 0x9f, 0x97, 0x8f,
|
||||
0x87, 0x7f, 0x77, 0x6f, 0x67, 0x5f, 0x57, 0x4f,
|
||||
0x47, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33,
|
||||
0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23,
|
||||
0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13,
|
||||
0x12, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
|
||||
0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a,
|
||||
0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
|
||||
|
||||
const uint16_t LFO::lfoRateTable[128] = {
|
||||
0x0005, 0x000f, 0x0019, 0x0028, 0x0037, 0x0046, 0x0050, 0x005a, 0x0064,
|
||||
0x006e, 0x0078, 0x0082, 0x008c, 0x0096, 0x00a0, 0x00aa, 0x00b4, 0x00be,
|
||||
@ -102,3 +83,24 @@ const uint16_t LFO::lfoRateTable[128] = {
|
||||
0x07a8, 0x07f8, 0x085c, 0x08c0, 0x0924, 0x0988, 0x09ec, 0x0a50, 0x0ab4,
|
||||
0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a,
|
||||
0x0fa0, 0x1000};
|
||||
|
||||
extern const uint16_t lfoDelayTable[8] = {
|
||||
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
|
||||
|
||||
extern const uint8_t portaTable[128] = {
|
||||
0x00, 0xff, 0xf7, 0xef, 0xe7, 0xdf, 0xd7, 0xcf,
|
||||
0xc7, 0xbf, 0xb7, 0xaf, 0xa7, 0x9f, 0x97, 0x8f,
|
||||
0x87, 0x7f, 0x77, 0x6f, 0x67, 0x5f, 0x57, 0x4f,
|
||||
0x47, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33,
|
||||
0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23,
|
||||
0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13,
|
||||
0x12, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
|
||||
0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a,
|
||||
0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
|
||||
|
@ -16,7 +16,7 @@
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "voiceboard.hpp"
|
||||
#include "ic29.hpp"
|
||||
|
||||
static inline float poly3blep0(float t) {
|
||||
float t2 = t * t;
|
||||
@ -27,12 +27,13 @@ static inline float poly3blep1(float t) {
|
||||
return -poly3blep0(1 - t);
|
||||
}
|
||||
|
||||
void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
|
||||
void Voice::run(float *buffer, uint32_t samples) {
|
||||
// generate a full block of samples for the oscillator
|
||||
|
||||
float y, t;
|
||||
float y, out, t;
|
||||
float saw = 0;
|
||||
float gain = env.level / 16384.0;
|
||||
|
||||
if (subosc > 0) subosc = sub; else subosc = -sub;
|
||||
|
||||
|
||||
// this uses an adaptation of Mystran's Polyblep oscillator
|
||||
@ -64,14 +65,14 @@ void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
|
||||
}
|
||||
|
||||
delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth
|
||||
delay += (0.63 - (pwrc * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level
|
||||
delay += (0.63 - (pw * 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 signal at about 10Hz or so, preventing any PWM rumble from leaking through!
|
||||
delay += subosc;
|
||||
//delay += ic29.noise[i+pos];
|
||||
|
||||
out = y * 0.15;
|
||||
lastpw = pwrc;
|
||||
buffer[i + pos] = y * 0.707;
|
||||
buffer[i] += out * gain;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,18 +18,18 @@
|
||||
|
||||
#include "peacock.hpp"
|
||||
|
||||
#include "assigner.hpp"
|
||||
#include "voiceboard.hpp"
|
||||
#include "ic1.hpp"
|
||||
#include "ic29.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
|
||||
printf("peacock constructor\n");
|
||||
ic1.setVoiceBoard(ic29);
|
||||
ic29.buildTables(getSampleRate());
|
||||
ic29.bufferSize = getBufferSize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
@ -51,6 +51,7 @@ void Peacock::initAudioPort(bool input, uint32_t index, AudioPort &port) {
|
||||
if (!input && index == 1) port.name = "Right Out";
|
||||
}
|
||||
|
||||
|
||||
void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
|
||||
// calculate an entire jack period's worth of samples
|
||||
// harder than it sounds because for short jack periods there may be many
|
||||
@ -71,7 +72,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
||||
runMidi(midiEvents, midiEventCount, blockLeft);
|
||||
|
||||
// generate a buffer's worth of samples
|
||||
memset(outputs[1], 0, sizeof(float) * frames);
|
||||
memset(outputs[0], 0, sizeof(float) * frames);
|
||||
|
||||
while (framePos < frames) {
|
||||
if (blockLeft == 0) {
|
||||
@ -85,11 +86,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
||||
|
||||
// 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);
|
||||
//ic29->voices[i].filter(outputs[0], framePos, sizeThisTime);
|
||||
for (uint8_t j=0; j<sizeThisTime; j++) {
|
||||
// outputs[1][framePos+j] += outputs[0][framePos+j] * ic29->voices[i].env.level/16384.0;
|
||||
}
|
||||
ic29.voices[i].run(outputs[0] + framePos, sizeThisTime);
|
||||
}
|
||||
|
||||
framePos += sizeThisTime; // move along the frame
|
||||
@ -97,7 +94,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
||||
blockLeft -= sizeThisTime;
|
||||
}
|
||||
// output processing goes here
|
||||
memcpy(outputs[0], outputs[1], sizeof(float) * frames);
|
||||
memcpy(outputs[1], outputs[0], sizeof(float) * frames);
|
||||
}
|
||||
|
||||
Plugin *createPlugin() { return new Peacock(); }
|
||||
|
@ -23,9 +23,6 @@
|
||||
|
||||
#include "DistrhoPlugin.hpp"
|
||||
|
||||
#include "assigner.hpp"
|
||||
#include "voiceboard.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
class Peacock : public Plugin {
|
||||
@ -55,8 +52,8 @@ class Peacock : public Plugin {
|
||||
p_sustain,
|
||||
p_release,
|
||||
p_chorus, // 0, 1, 2
|
||||
p_vcoBend,
|
||||
p_vcfBend,
|
||||
p_modWheel,
|
||||
p_holdPedal,
|
||||
paramCount
|
||||
};
|
||||
|
||||
@ -77,7 +74,7 @@ class Peacock : public Plugin {
|
||||
void initAudioPort(bool input, uint32_t index, AudioPort &port) 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;
|
||||
|
||||
// Processing
|
||||
@ -90,9 +87,6 @@ class Peacock : public Plugin {
|
||||
uint32_t blockLeft = 0;
|
||||
uint32_t lastEvent = 0;
|
||||
|
||||
Assigner ic1;
|
||||
Synth ic29;
|
||||
|
||||
void runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit);
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock);
|
||||
|
Loading…
Reference in New Issue
Block a user