Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5914d49cdb | ||
|
be894c562e | ||
|
e806584a46 | ||
|
18dd0947d4 | ||
|
b8265f6938 | ||
|
34800af7a1 | ||
|
84eef25f8d | ||
|
effdce42be |
@ -11,7 +11,9 @@ NAME = peacock
|
|||||||
|
|
||||||
FILES_DSP = \
|
FILES_DSP = \
|
||||||
peacock.cpp \
|
peacock.cpp \
|
||||||
|
controls.cpp \
|
||||||
ic1.cpp \
|
ic1.cpp \
|
||||||
|
oscillator.cpp \
|
||||||
ic29.cpp
|
ic29.cpp
|
||||||
|
|
||||||
include ../dpf/Makefile.plugins.mk
|
include ../dpf/Makefile.plugins.mk
|
||||||
|
313
plugin/controls.cpp
Normal file
313
plugin/controls.cpp
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
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 "peacock.hpp"
|
||||||
|
|
||||||
|
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
||||||
|
switch (index) {
|
||||||
|
case p_lfoRate:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "LFO Rate";
|
||||||
|
parameter.symbol = "pfau_lforate";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 48.0f;
|
||||||
|
parameter.midiCC = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_lfoDelay:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "LFO Delay";
|
||||||
|
parameter.symbol = "pfau_lfodelay";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 9;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcoRange:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
|
||||||
|
parameter.name = "Range";
|
||||||
|
parameter.symbol = "pfau_vcorange";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 2.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 12;
|
||||||
|
parameter.enumValues.count = 3;
|
||||||
|
parameter.enumValues.restrictedMode = true;
|
||||||
|
{
|
||||||
|
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3];
|
||||||
|
enumValues[0].value = 0.0f;
|
||||||
|
enumValues[0].label = "16'";
|
||||||
|
enumValues[1].value = 1.0f;
|
||||||
|
enumValues[1].label = "8'";
|
||||||
|
enumValues[2].value = 2.0f;
|
||||||
|
enumValues[2].label = "4'";
|
||||||
|
parameter.enumValues.values = enumValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcoLfoMod:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "LFO";
|
||||||
|
parameter.symbol = "pfau_lfo";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 10.0f;
|
||||||
|
parameter.midiCC = 13;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_pwmLfoMod:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "PWM";
|
||||||
|
parameter.symbol = "pfau_pwm";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 48.0f;
|
||||||
|
parameter.midiCC = 14;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_pwmMode:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
|
||||||
|
parameter.name = "PWM Mode";
|
||||||
|
parameter.symbol = "pfau_pwmmode";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 1.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 15;
|
||||||
|
parameter.enumValues.count = 2;
|
||||||
|
parameter.enumValues.restrictedMode = true;
|
||||||
|
{
|
||||||
|
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
|
||||||
|
enumValues[0].value = 0.0f;
|
||||||
|
enumValues[0].label = "LFO";
|
||||||
|
enumValues[1].value = 1.0f;
|
||||||
|
enumValues[1].label = "MAN";
|
||||||
|
parameter.enumValues.values = enumValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_sawOn:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
|
||||||
|
parameter.name = "Saw";
|
||||||
|
parameter.symbol = "pfau_saw";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 1.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 17;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_sqrOn:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
|
||||||
|
parameter.name = "Square";
|
||||||
|
parameter.symbol = "pfau_sqr";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 1.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_subLevel:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Sub Osc";
|
||||||
|
parameter.symbol = "pfau_sub";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 18;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_noiseLevel:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Noise";
|
||||||
|
parameter.symbol = "pfau_noise";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 19;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_hpfMode:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
|
||||||
|
parameter.name = "HPF";
|
||||||
|
parameter.symbol = "pfau_hpf";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 3.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 20;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcfCutoff:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Freq";
|
||||||
|
parameter.symbol = "pfau_freq";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 60.0f;
|
||||||
|
parameter.midiCC = 74;
|
||||||
|
break;
|
||||||
|
case p_vcfReso:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Res";
|
||||||
|
parameter.symbol = "pfau_reso";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 71;
|
||||||
|
break;
|
||||||
|
case p_vcfEnvPol:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
|
||||||
|
parameter.name = "Polarity";
|
||||||
|
parameter.symbol = "pfau_vcfmode";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 1.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 21;
|
||||||
|
parameter.enumValues.count = 2;
|
||||||
|
parameter.enumValues.restrictedMode = true;
|
||||||
|
{
|
||||||
|
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
|
||||||
|
enumValues[0].value = 0.0f;
|
||||||
|
enumValues[0].label = "POS";
|
||||||
|
enumValues[1].value = 1.0f;
|
||||||
|
enumValues[1].label = "INV";
|
||||||
|
parameter.enumValues.values = enumValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case p_vcfEnvMod:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Env";
|
||||||
|
parameter.symbol = "pfau_vcfenvmod";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 46.0f;
|
||||||
|
parameter.midiCC = 22;
|
||||||
|
break;
|
||||||
|
case p_vcfLFoMod:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "LFO";
|
||||||
|
parameter.symbol = "pfau_vcflfomod";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 0.0f;
|
||||||
|
parameter.midiCC = 23;
|
||||||
|
break;
|
||||||
|
case p_vcfKeyTrk:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Kybd";
|
||||||
|
parameter.symbol = "pfau_vcfkeymod";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 71.0f;
|
||||||
|
parameter.midiCC = 24;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_attack:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Attack";
|
||||||
|
parameter.symbol = "pfau_attack";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 27.0f;
|
||||||
|
parameter.midiCC = 73;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_decay:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Decay";
|
||||||
|
parameter.symbol = "pfau_decay";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 57.0f;
|
||||||
|
parameter.midiCC = 75;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_sustain:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Sustain";
|
||||||
|
parameter.symbol = "pfau_sustain";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 57.0f;
|
||||||
|
parameter.midiCC = 27;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_release:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "Release";
|
||||||
|
parameter.symbol = "pfau_release";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 48.0f;
|
||||||
|
parameter.midiCC = 72;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcaEnvGate:
|
||||||
|
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean;
|
||||||
|
parameter.name = "Gate";
|
||||||
|
parameter.symbol = "pfau_envgate";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 1.0f;
|
||||||
|
parameter.ranges.def = 1.0f;
|
||||||
|
parameter.midiCC = 25;
|
||||||
|
parameter.enumValues.count = 2;
|
||||||
|
parameter.enumValues.restrictedMode = true;
|
||||||
|
{
|
||||||
|
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
|
||||||
|
enumValues[0].value = 0.0f;
|
||||||
|
enumValues[0].label = "ENV";
|
||||||
|
enumValues[1].value = 1.0f;
|
||||||
|
enumValues[1].label = "GATE";
|
||||||
|
parameter.enumValues.values = enumValues;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case p_vcaLevel:
|
||||||
|
parameter.hints = kParameterIsAutomatable;
|
||||||
|
parameter.name = "VCA Level";
|
||||||
|
parameter.symbol = "pfau_vcalevel";
|
||||||
|
parameter.ranges.min = 0.0f;
|
||||||
|
parameter.ranges.max = 127.0f;
|
||||||
|
parameter.ranges.def = 40.0f;
|
||||||
|
parameter.midiCC = 26;
|
||||||
|
break;
|
||||||
|
|
||||||
|
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 = 0.0f;
|
||||||
|
parameter.midiCC = 1;
|
||||||
|
break;
|
||||||
|
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 = 0.0f;
|
||||||
|
parameter.midiCC = 64;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// chorus, porta, bend range, key mode still to do
|
||||||
|
}
|
103
plugin/ic29.cpp
103
plugin/ic29.cpp
@ -18,14 +18,18 @@
|
|||||||
|
|
||||||
#include "ic29.hpp"
|
#include "ic29.hpp"
|
||||||
|
|
||||||
|
#include "ic29tables.hpp"
|
||||||
|
|
||||||
Synth ic29;
|
Synth ic29;
|
||||||
|
|
||||||
Synth::Synth() {
|
Synth::Synth() {
|
||||||
d_debug("initialising synth\n");
|
d_debug("initialising synth\n");
|
||||||
envAtk = 0x007f;
|
envAtk = 0x00;
|
||||||
envDcy = envRls = 0xfe90;
|
envDcy = 0x1f;
|
||||||
envStn = 0x1fff;
|
envStn = 0x00;
|
||||||
|
envRls = 0x1f;
|
||||||
portaCoeff = 0x0;
|
portaCoeff = 0x0;
|
||||||
|
lfo.speed = 0x06;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::buildTables(double sampleRate) {
|
void Synth::buildTables(double sampleRate) {
|
||||||
@ -33,7 +37,7 @@ void Synth::buildTables(double sampleRate) {
|
|||||||
// slightly flat middle C from ROM divider table
|
// slightly flat middle C from ROM divider table
|
||||||
// actually adjusted a little so that the notes are bang on
|
// actually adjusted a little so that the notes are bang on
|
||||||
// on the real synth the tuning knob is tweaked a little off to pull it in
|
// 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;
|
pitchTable[i] = 260.15f * powf(2, (i - 36) / 12.0f) / sampleRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +45,21 @@ void Synth::run() {
|
|||||||
// handle a "loop" worth of envelopes, pitch calculations, etc
|
// handle a "loop" worth of envelopes, pitch calculations, etc
|
||||||
// callled once every 4.3ms block of samples
|
// callled once every 4.3ms block of samples
|
||||||
|
|
||||||
|
ic29.lfo.run();
|
||||||
|
|
||||||
|
masterPitch = 0x1818;
|
||||||
|
|
||||||
|
// 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 - ic29.lfo.lfoOut;
|
||||||
|
if (0) pwmVal = 0x3fff;
|
||||||
|
|
||||||
|
ic29.pwm = pwmVal / 40960.0f * (1); // 0.5 is knob val
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
@ -58,14 +77,32 @@ void Synth::voiceOff(uint8_t voice) {
|
|||||||
ic29.voices[voice].off();
|
ic29.voices[voice].off();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::basePitch() {
|
LFO::LFO() {
|
||||||
uint16_t pitch = 0x1818;
|
lfoOut = 0;
|
||||||
|
phase = 0;
|
||||||
|
|
||||||
pitch += lfoPitch;
|
// phase is where we are in the LFO delay cycle
|
||||||
pitch += bendPitch;
|
// the delay envelope sets the depth of pitch and VCF modulation
|
||||||
// tuning too but that's zero by default;
|
// running normally the amplitude is maxed out, and when the first
|
||||||
|
// key is struck the holdoff timer and envelope will be reset to zero
|
||||||
|
delayPhase = LFO_RUN;
|
||||||
|
}
|
||||||
|
|
||||||
masterPitch = pitch;
|
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[speed] : -lfoRateTable[speed];
|
||||||
|
if (lfoOut > 0x1fff) {
|
||||||
|
lfoOut = 0x1fff;
|
||||||
|
phase = 0;
|
||||||
|
}
|
||||||
|
if (lfoOut < -0x1fff) {
|
||||||
|
lfoOut = -0x1fff;
|
||||||
|
phase = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("lfoOut=%04x\n", lfoOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
Envelope::Envelope() {
|
Envelope::Envelope() {
|
||||||
@ -74,25 +111,24 @@ Envelope::Envelope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Envelope::run() {
|
void Envelope::run() {
|
||||||
|
uint16_t tempStn = ic29.envStn << 7;
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case ENV_ATK:
|
case ENV_ATK:
|
||||||
level += ic29.envAtk;
|
level += atkTable[ic29.envAtk];
|
||||||
if (level > 0x3fff) {
|
if (level > 0x3fff) {
|
||||||
level = 0x3fff;
|
level = 0x3fff;
|
||||||
phase = ENV_DCY;
|
phase = ENV_DCY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ENV_DCY:
|
case ENV_DCY:
|
||||||
if (level > ic29.envStn) {
|
if (level > tempStn) {
|
||||||
level -= ic29.envStn;
|
level = (((level - tempStn) * dcyTable[ic29.envDcy]) >> 16) + tempStn;
|
||||||
level = (level * ic29.envDcy) >> 16;
|
|
||||||
level += ic29.envStn;
|
|
||||||
} else {
|
} else {
|
||||||
level = ic29.envStn;
|
level = tempStn;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ENV_RLS:
|
case ENV_RLS:
|
||||||
level = (level * ic29.envRls) >> 16;
|
level = (level * dcyTable[ic29.envRls]) >> 16;
|
||||||
break;
|
break;
|
||||||
case ENV_IDLE:
|
case ENV_IDLE:
|
||||||
default:
|
default:
|
||||||
@ -101,18 +137,26 @@ void Envelope::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Voice::Voice() {
|
Voice::Voice() {
|
||||||
|
subosc = .1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::calcPitch() {
|
void Voice::calcPitch() {
|
||||||
uint16_t target = note << 8;
|
uint16_t target = note << 8;
|
||||||
|
|
||||||
|
// Portamento is a linear change of pitch - it'll take twice as long
|
||||||
|
// to jump two octaves as it takes to jump one
|
||||||
|
// By comparison "glide" is like an RC filter, for example in the TB303
|
||||||
|
// This is implemented here by adding on a step value until you pass
|
||||||
|
// the desired final pitch. Once that happens the value is clamped to the
|
||||||
|
// desired pitch.
|
||||||
|
|
||||||
if (ic29.portaCoeff != 0) {
|
if (ic29.portaCoeff != 0) {
|
||||||
// porta up
|
// portamento up
|
||||||
if (pitch < target) {
|
if (pitch < target) {
|
||||||
pitch += ic29.portaCoeff;
|
pitch += ic29.portaCoeff;
|
||||||
if (pitch > target) pitch = target;
|
if (pitch > target) pitch = target;
|
||||||
}
|
}
|
||||||
// porta down
|
// portamento down
|
||||||
if (pitch > target) {
|
if (pitch > target) {
|
||||||
pitch -= ic29.portaCoeff;
|
pitch -= ic29.portaCoeff;
|
||||||
if (pitch < target) pitch = target;
|
if (pitch < target) pitch = target;
|
||||||
@ -121,17 +165,17 @@ void Voice::calcPitch() {
|
|||||||
pitch = target;
|
pitch = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
pitch += 0x1818; //ic29.masterPitch;
|
pitch += ic29.masterPitch;
|
||||||
|
|
||||||
if (pitch < 0x3000) pitch = 0x3000; // lowest note
|
if (pitch < 0x3000) pitch = 0x3000; // lowest note
|
||||||
if (pitch > 0x9700) pitch = 0x6700; // highest note
|
if (pitch > 0x9700) pitch = 0x6700; // highest note
|
||||||
|
|
||||||
pitch -= 0x3000;
|
pitch -= 0x3000;
|
||||||
//pitch &= 0xff00;
|
|
||||||
|
|
||||||
|
// interpolate between the two table values
|
||||||
double o1 = ic29.pitchTable[pitch >> 8];
|
double o1 = ic29.pitchTable[pitch >> 8];
|
||||||
double o2 = ic29.pitchTable[(pitch >> 8) + 1];
|
double o2 = ic29.pitchTable[(pitch >> 8) + 1];
|
||||||
double frac = (pitch & 0xff) / 255.0;
|
double frac = (pitch & 0xff) / 256.0f;
|
||||||
|
|
||||||
omega = ((o2 - o1) * frac) + o1;
|
omega = ((o2 - o1) * frac) + o1;
|
||||||
}
|
}
|
||||||
@ -140,29 +184,20 @@ void Voice::update() {
|
|||||||
// calculate the once-per-block values
|
// calculate the once-per-block values
|
||||||
env.run();
|
env.run();
|
||||||
calcPitch();
|
calcPitch();
|
||||||
|
pw = 0.5 - ic29.pwm;
|
||||||
// do filter values
|
// do filter values
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::on(uint8_t key) {
|
void Voice::on(uint8_t key) {
|
||||||
voiceState = V_ON;
|
voiceState = V_ON;
|
||||||
note = key;
|
note = key;
|
||||||
env.on();
|
env.on(); // FIXME move to synth update code
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::off() {
|
void Voice::off() {
|
||||||
// I need to rethink this bit FIXME
|
// sustain - I need to rethink this bit FIXME
|
||||||
voiceState = V_OFF;
|
voiceState = V_OFF;
|
||||||
if (!ic29.sustained) {
|
if (!ic29.sustained) {
|
||||||
env.off();
|
env.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,6 +20,24 @@
|
|||||||
|
|
||||||
#include "peacock.hpp"
|
#include "peacock.hpp"
|
||||||
|
|
||||||
|
class LFO {
|
||||||
|
public:
|
||||||
|
LFO();
|
||||||
|
void run();
|
||||||
|
int16_t lfoOut;
|
||||||
|
uint8_t speed;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t
|
||||||
|
phase;
|
||||||
|
uint16_t holdoff;
|
||||||
|
uint16_t envelope;
|
||||||
|
enum { LFO_RUN,
|
||||||
|
LFO_HOLDOFF,
|
||||||
|
LFO_RAMP } delayPhase;
|
||||||
|
static const uint16_t lfoRateTable[128];
|
||||||
|
};
|
||||||
|
|
||||||
class Envelope {
|
class Envelope {
|
||||||
public:
|
public:
|
||||||
Envelope();
|
Envelope();
|
||||||
@ -45,6 +63,8 @@ class Envelope {
|
|||||||
ENV_RLS,
|
ENV_RLS,
|
||||||
ENV_IDLE
|
ENV_IDLE
|
||||||
} phase;
|
} phase;
|
||||||
|
static const uint16_t atkTable[128];
|
||||||
|
static const uint16_t dcyTable[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
class Voice {
|
class Voice {
|
||||||
@ -59,6 +79,10 @@ class Voice {
|
|||||||
private:
|
private:
|
||||||
Envelope env; // calculated envelope value
|
Envelope env; // calculated envelope value
|
||||||
uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
|
uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
|
||||||
|
float delay; // delay slot for polyblep
|
||||||
|
bool pulseStage;
|
||||||
|
float pw, lastpw, pwrc;
|
||||||
|
float subosc = -1;
|
||||||
float phase = 0, omega = 0;
|
float phase = 0, omega = 0;
|
||||||
enum { V_DONE,
|
enum { V_DONE,
|
||||||
V_OFF,
|
V_OFF,
|
||||||
@ -93,9 +117,10 @@ class Synth {
|
|||||||
int16_t lfoPitch;
|
int16_t lfoPitch;
|
||||||
int16_t bendPitch;
|
int16_t bendPitch;
|
||||||
Voice voices[NUM_VOICES];
|
Voice voices[NUM_VOICES];
|
||||||
|
LFO lfo;
|
||||||
|
|
||||||
|
float pwm;
|
||||||
|
|
||||||
void runLfo();
|
|
||||||
void basePitch();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// global
|
// global
|
||||||
|
106
plugin/ic29tables.hpp
Normal file
106
plugin/ic29tables.hpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
Chassis polysynth framework
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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,
|
||||||
|
0x01c7, 0x01af, 0x0199, 0x0186, 0x0174, 0x0164, 0x0155, 0x0148, 0x013b,
|
||||||
|
0x012f, 0x0124, 0x011a, 0x0111, 0x0108, 0x0100, 0x00f8, 0x00f1, 0x00ea,
|
||||||
|
0x00e4, 0x00dd, 0x00d8, 0x00d2, 0x00cd, 0x00c8, 0x00c3, 0x00bf, 0x00ba,
|
||||||
|
0x00b6, 0x00b2, 0x00ae, 0x00ab, 0x00a7, 0x00a4, 0x00a1, 0x009e, 0x009b,
|
||||||
|
0x0098, 0x0095, 0x0092, 0x0090, 0x008d, 0x008b, 0x0089, 0x0086, 0x0084,
|
||||||
|
0x0082, 0x007f, 0x007d, 0x007a, 0x0077, 0x0074, 0x0072, 0x006f, 0x006c,
|
||||||
|
0x0069, 0x0067, 0x0064, 0x0061, 0x005e, 0x005c, 0x0059, 0x0056, 0x0053,
|
||||||
|
0x0050, 0x004e, 0x004b, 0x0048, 0x0045, 0x0042, 0x0040, 0x003f, 0x003d,
|
||||||
|
0x003c, 0x003a, 0x0039, 0x0037, 0x0036, 0x0034, 0x0033, 0x0031, 0x0030,
|
||||||
|
0x002e, 0x002d, 0x002b, 0x002a, 0x0028, 0x0027, 0x0025, 0x0024, 0x0022,
|
||||||
|
0x0021, 0x0021, 0x0020, 0x0020, 0x001f, 0x001f, 0x001e, 0x001e, 0x001d,
|
||||||
|
0x001d, 0x001c, 0x001c, 0x001b, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017,
|
||||||
|
0x0016, 0x0015};
|
||||||
|
|
||||||
|
const uint16_t Envelope::dcyTable[128] = {
|
||||||
|
0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xa000, 0xa800, 0xb000, 0xb800,
|
||||||
|
0xc000, 0xc800, 0xd000, 0xd800, 0xe000, 0xe800, 0xf000, 0xf080, 0xf100,
|
||||||
|
0xf180, 0xf200, 0xf280, 0xf300, 0xf380, 0xf400, 0xf480, 0xf500, 0xf580,
|
||||||
|
0xf600, 0xf680, 0xf700, 0xf780, 0xf800, 0xf880, 0xf900, 0xf980, 0xfa00,
|
||||||
|
0xfa80, 0xfb00, 0xfb80, 0xfc00, 0xfc80, 0xfd00, 0xfd80, 0xfe00, 0xfe0c,
|
||||||
|
0xfe18, 0xfe24, 0xfe30, 0xfe3c, 0xfe48, 0xfe54, 0xfe60, 0xfe6c, 0xfe78,
|
||||||
|
0xfe84, 0xfe90, 0xfe9c, 0xfea8, 0xfeb4, 0xfec0, 0xfecc, 0xfed8, 0xfee4,
|
||||||
|
0xfef0, 0xfefc, 0xff08, 0xff0c, 0xff10, 0xff14, 0xff18, 0xff1c, 0xff20,
|
||||||
|
0xff24, 0xff28, 0xff2c, 0xff30, 0xff34, 0xff38, 0xff3c, 0xff40, 0xff44,
|
||||||
|
0xff48, 0xff4c, 0xff50, 0xff54, 0xff58, 0xff5c, 0xff60, 0xff64, 0xff68,
|
||||||
|
0xff6c, 0xff70, 0xff74, 0xff78, 0xff7c, 0xff80, 0xff84, 0xff88, 0xff8c,
|
||||||
|
0xff90, 0xff94, 0xff98, 0xff9c, 0xffa0, 0xffa4, 0xffa8, 0xffac, 0xffb0,
|
||||||
|
0xffb4, 0xffb8, 0xffbc, 0xffc0, 0xffc4, 0xffc8, 0xffcc, 0xffd0, 0xffd4,
|
||||||
|
0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2,
|
||||||
|
0xfff3, 0xfff4};
|
||||||
|
|
||||||
|
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,
|
||||||
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||||
|
0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||||
|
0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c,
|
||||||
|
0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64,
|
||||||
|
0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c,
|
||||||
|
0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c, 0xa0, 0xa4, 0xa8, 0xac,
|
||||||
|
0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
|
||||||
|
0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf8, 0xff, 0xff};
|
||||||
|
|
||||||
|
const uint16_t LFO::lfoRateTable[128] = {
|
||||||
|
0x0005, 0x000f, 0x0019, 0x0028, 0x0037, 0x0046, 0x0050, 0x005a, 0x0064,
|
||||||
|
0x006e, 0x0078, 0x0082, 0x008c, 0x0096, 0x00a0, 0x00aa, 0x00b4, 0x00be,
|
||||||
|
0x00c8, 0x00d2, 0x00dc, 0x00e6, 0x00f0, 0x00fa, 0x0104, 0x010e, 0x0118,
|
||||||
|
0x0122, 0x012c, 0x0136, 0x0140, 0x014a, 0x0154, 0x015e, 0x0168, 0x0172,
|
||||||
|
0x017c, 0x0186, 0x0190, 0x019a, 0x01a4, 0x01ae, 0x01b8, 0x01c2, 0x01cc,
|
||||||
|
0x01d6, 0x01e0, 0x01ea, 0x01f4, 0x01fe, 0x0208, 0x0212, 0x021c, 0x0226,
|
||||||
|
0x0230, 0x023a, 0x0244, 0x024e, 0x0258, 0x0262, 0x026c, 0x0276, 0x0280,
|
||||||
|
0x028a, 0x029a, 0x02aa, 0x02ba, 0x02ca, 0x02da, 0x02ea, 0x02fa, 0x030a,
|
||||||
|
0x031a, 0x032a, 0x033a, 0x034a, 0x035a, 0x036a, 0x037a, 0x038a, 0x039a,
|
||||||
|
0x03aa, 0x03ba, 0x03ca, 0x03da, 0x03ea, 0x03fa, 0x040a, 0x041a, 0x042a,
|
||||||
|
0x043a, 0x044a, 0x045a, 0x046a, 0x047a, 0x048a, 0x04be, 0x04f2, 0x0526,
|
||||||
|
0x055a, 0x058e, 0x05c2, 0x05f6, 0x062c, 0x0672, 0x06b8, 0x0708, 0x0758,
|
||||||
|
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};
|
78
plugin/oscillator.cpp
Normal file
78
plugin/oscillator.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
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 "ic29.hpp"
|
||||||
|
|
||||||
|
static inline float poly3blep0(float t) {
|
||||||
|
float t2 = t * t;
|
||||||
|
return 2 * (t * t2 - 0.5f * t2 * t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float poly3blep1(float t) {
|
||||||
|
return -poly3blep0(1 - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::run(float *buffer, uint32_t samples) {
|
||||||
|
// generate a full block of samples for the oscillator
|
||||||
|
|
||||||
|
float y, out, t;
|
||||||
|
float saw = 0;
|
||||||
|
float gain = env.level / 16384.0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// this uses an adaptation of Mystran's Polyblep oscillator
|
||||||
|
for (uint32_t i = 0; i < samples; i++) {
|
||||||
|
y = delay;
|
||||||
|
delay = 0;
|
||||||
|
phase += omega;
|
||||||
|
|
||||||
|
pwrc = ((pw - pwrc) *.01 ) + pwrc;
|
||||||
|
|
||||||
|
// this is the clever bit
|
||||||
|
while (true) {
|
||||||
|
if (!pulseStage) {
|
||||||
|
if (phase < pwrc) break; // it's not time for the PWM output to step
|
||||||
|
t = (phase - pwrc) / (lastpw - pwrc + omega); // calculate fractional sample allowing for PW amount
|
||||||
|
y -= 0.63 * poly3blep0(t); // magic numbers observed on oscilloscope from real synth
|
||||||
|
delay -= 0.63 * poly3blep1(t);
|
||||||
|
pulseStage = true;
|
||||||
|
}
|
||||||
|
if (pulseStage) {
|
||||||
|
if (phase < 1) break; // it's not time to reset the saw
|
||||||
|
t = (phase - 1) / omega;
|
||||||
|
y += poly3blep0(t) * (0.8 * saw + 0.63 - subosc);
|
||||||
|
delay += poly3blep1(t) * (0.8 * saw + 0.63 - subosc);
|
||||||
|
pulseStage = 0;
|
||||||
|
phase -= 1;
|
||||||
|
subosc = -subosc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
out = y * 0.15;
|
||||||
|
lastpw = pwrc;
|
||||||
|
buffer[i] += out * gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,8 @@ Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
|
|||||||
ic29.buildTables(getSampleRate());
|
ic29.buildTables(getSampleRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
|
void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
|
||||||
// handle MIDI events, starting at lastEvent and continuing until timeLimit
|
// handle MIDI events, starting at lastEvent and continuing until timeLimit
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
@ -41,6 +43,15 @@ void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
|
|||||||
lastEvent = i;
|
lastEvent = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Peacock::initAudioPort(bool input, uint32_t index, AudioPort &port) {
|
||||||
|
port.groupId = kPortGroupStereo;
|
||||||
|
Plugin::initAudioPort(input, index, port);
|
||||||
|
|
||||||
|
if (!input && index == 0) port.name = "Left Out";
|
||||||
|
if (!input && index == 1) port.name = "Right Out";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
|
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
|
// calculate an entire jack period's worth of samples
|
||||||
// harder than it sounds because for short jack periods there may be many
|
// harder than it sounds because for short jack periods there may be many
|
||||||
|
@ -28,6 +28,32 @@ START_NAMESPACE_DISTRHO
|
|||||||
class Peacock : public Plugin {
|
class Peacock : public Plugin {
|
||||||
public:
|
public:
|
||||||
enum Parameters {
|
enum Parameters {
|
||||||
|
p_lfoRate,
|
||||||
|
p_lfoDelay,
|
||||||
|
p_vcoRange, // 16'/8'/4'
|
||||||
|
p_vcoLfoMod,
|
||||||
|
p_pwmLfoMod,
|
||||||
|
p_pwmMode, // LFO/Fixed
|
||||||
|
p_sawOn,
|
||||||
|
p_sqrOn,
|
||||||
|
p_subLevel,
|
||||||
|
p_noiseLevel,
|
||||||
|
p_hpfMode, // 0 = bass boost, 1 = flat, 2 = 170Hz, 3 = 350Hz
|
||||||
|
p_vcfCutoff,
|
||||||
|
p_vcfReso,
|
||||||
|
p_vcfEnvPol, // positive / inverted
|
||||||
|
p_vcfEnvMod,
|
||||||
|
p_vcfLFoMod,
|
||||||
|
p_vcfKeyTrk,
|
||||||
|
p_vcaEnvGate, // env or gate
|
||||||
|
p_vcaLevel,
|
||||||
|
p_attack,
|
||||||
|
p_decay,
|
||||||
|
p_sustain,
|
||||||
|
p_release,
|
||||||
|
p_chorus, // 0, 1, 2
|
||||||
|
p_modWheel,
|
||||||
|
p_holdPedal,
|
||||||
paramCount
|
paramCount
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,7 +61,6 @@ class Peacock : public Plugin {
|
|||||||
float sampleRate;
|
float sampleRate;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
const char *getLabel() const override { return "peacock-8"; }
|
const char *getLabel() const override { return "peacock-8"; }
|
||||||
const char *getDescription() const override {
|
const char *getDescription() const override {
|
||||||
return "simple polysynth";
|
return "simple polysynth";
|
||||||
@ -46,8 +71,8 @@ class Peacock : public Plugin {
|
|||||||
int64_t getUniqueId() const override { return d_cconst('P', 'f', 'a', 'u'); }
|
int64_t getUniqueId() const override { return d_cconst('P', 'f', 'a', 'u'); }
|
||||||
|
|
||||||
// Initialisation
|
// Initialisation
|
||||||
// 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user