Compare commits

..

39 Commits

Author SHA1 Message Date
Gordon JC Pearce 4bf634a767 bunch of stuff to do with controls 2026-01-09 23:56:07 +00:00
Gordon JC Pearce d0a259a960 rescaled lookup table 2026-01-09 00:44:19 +00:00
Gordon JC Pearce 6a8e0686a6 pitch bender for oscillator, no range setting 2026-01-09 00:29:54 +00:00
Gordon JC Pearce c5ca40d22c calibration 2026-01-08 16:23:26 +00:00
Gordon JC Pearce 58ee2d4ca8 Merge branch 'gui' into calibration 2026-01-08 16:19:59 +00:00
Gordon JC Pearce 4077447102 levels balanced a bit better 2026-01-08 16:17:42 +00:00
Gordon JC Pearce 38ddbf91b1 fixed levels 2026-01-08 15:31:12 +00:00
Gordon JC Pearce e3c54e3ef1 noise level 2026-01-08 15:29:10 +00:00
Gordon JC Pearce 8ca166a662 fix level 2026-01-08 15:27:14 +00:00
Gordon JC Pearce 7d60cac407 update windows and linux runners 2026-01-08 10:45:22 +00:00
Gordon JC Pearce 1199cc1fc0 update macos workflow 2026-01-07 12:37:40 +00:00
Gordon JC Pearce 168fe2912d add github workflow 2026-01-07 12:30:40 +00:00
Gordon JC Pearce d2e8e6126d adjustments to timing 2026-01-07 12:25:46 +00:00
Gordon JC Pearce e452e282f4 fixed button update bug in Windows 2026-01-07 11:38:04 +00:00
Gordon JC Pearce a3a2e0dd04 fix lfo and pitch calculation 2026-01-06 22:16:39 +00:00
Gordon JC Pearce 7ff0bf0659 better LFO and PW 2026-01-06 20:20:30 +00:00
Gordon JC Pearce 8c2263c129 chorus a bit more like the real thing 2026-01-05 23:08:50 +00:00
Gordon JC Pearce ffe4026b18 fix module being updated at the wrong time causing incorrect LFO speed 2026-01-05 21:34:30 +00:00
Gordon JC Pearce 8d987fa5b0 handle midi, reject >3 byte, but falktx has explained it better 2026-01-04 18:57:42 +00:00
Gordon JC Pearce a8464e6d12 better noise, sticking with original filter, lfo cleanup 2026-01-01 01:06:51 +00:00
Gordon JC Pearce 2ebf7fac1c fix CLAP features macro 2025-12-31 21:18:21 +00:00
Gordon JC Pearce 9f86360611 more make options for different plugin formats 2025-12-31 20:39:06 +00:00
Gordon JC Pearce 8fb00c1499 nice bass patch to test with 2025-12-30 01:11:57 +00:00
Gordon JC Pearce fd68c59d0c Merge branch 'chorusboard' 2025-12-30 01:04:41 +00:00
Gordon JC Pearce bad4e6b018 tune lfo speeds a bit 2025-12-30 01:02:55 +00:00
Gordon JC Pearce 4317f15f4e updated with gui fixes 2025-12-30 00:42:34 +00:00
Gordon JC Pearce dc89ca6cf7 Merge branch 'gui' 2025-12-30 00:24:19 +00:00
Gordon JC Pearce 4982d27019 chorus buttons 2025-12-29 23:45:20 +00:00
Gordon JC Pearce ad7b7f4405 formatting cleanup 2025-12-28 12:08:42 +00:00
Gordon JC Pearce 36c27f14d8 mystran filter 2025-12-26 22:08:52 +00:00
Gordon JC Pearce 2d53d3c5da parameter MIDI value 2025-12-26 00:28:58 +00:00
Gordon JC Pearce fd392ecf56 proper parameter for chorus 2025-12-25 23:21:12 +00:00
Gordon JC Pearce 13ff93a0a5 LFO delay, basically feature-complete 2025-12-24 01:46:37 +00:00
Gordon JC Pearce d6e5f21e5b vcf lfo 2025-12-24 00:34:52 +00:00
Gordon JC Pearce 7a4ebded58 grr, argh, peacock.xcf.final.final.reallythistime.xcf.gz how many times will I cock this up? 2025-12-23 23:53:41 +00:00
Gordon JC Pearce 80c8f409f5 are you kidding, how often will I forget to put the 'off' LEDs back in? 2025-12-23 23:46:10 +00:00
Gordon JC Pearce cef0957d6e lfo fix, chorus fix 2025-12-23 23:36:58 +00:00
Gordon JC Pearce e38cf3da59 fixed some blemishes 2025-12-23 23:11:53 +00:00
Gordon JC Pearce 5c5f8f2229 filter and oscillator tuning 2025-12-23 22:54:08 +00:00
18 changed files with 15712 additions and 36525 deletions

43
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: build
on: [push, pull_request]
jobs:
linux:
strategy:
matrix:
target: [linux-arm64, linux-armhf, linux-i686, linux-riscv64, linux-x86_64]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: distrho/dpf-makefile-action@v1
with:
target: ${{ matrix.target }}
macos:
strategy:
matrix:
target: [macos-intel, macos-universal]
runs-on: macos-15
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: distrho/dpf-makefile-action@v1
with:
target: ${{ matrix.target }}
windows:
strategy:
matrix:
target: [win32, win64]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: distrho/dpf-makefile-action@v1
with:
target: ${{ matrix.target }}

View File

@ -23,6 +23,15 @@
#define DISTRHO_PLUGIN_NAME "peacock-8" #define DISTRHO_PLUGIN_NAME "peacock-8"
#define DISTRHO_PLUGIN_URI "https://gjcp.net/plugins/peacock" #define DISTRHO_PLUGIN_URI "https://gjcp.net/plugins/peacock"
#define DISTRHO_PLUGIN_CLAP_ID "net.gjcp.peacock"
#define DISTRHO_PLUGIN_CLAP_FEATURES "instrument","synthesizer","stereo"
#define DISTRHO_PLUGIN_BRAND_ID GJCP
#define DISTRHO_PLUGIN_UNIQUE_ID Pfau
#define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:InstrumentPlugin"
#define DISTRHO_PLUGIN_VST_CATEGORY "Fx|Instrument"
#define DISTRHO_PLUGIN_NUM_INPUTS 0 #define DISTRHO_PLUGIN_NUM_INPUTS 0
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2
#define DISTRHO_PLUGIN_IS_SYNTH 1 #define DISTRHO_PLUGIN_IS_SYNTH 1
@ -63,10 +72,14 @@
pSustain, pSustain,
pRelease, pRelease,
pChorus, pChorusMode,
pVcoBend,
pVcfBend,
pModDepth,
parameterCount parameterCount
}; };
#endif #endif

View File

@ -21,11 +21,10 @@ FILES_DSP = \
FILES_UI = \ FILES_UI = \
orangebutton.cpp \ orangebutton.cpp \
whitebutton.cpp \
panel.cpp \ panel.cpp \
slider.cpp \ slider.cpp \
slideswitch.cpp \ slideswitch.cpp \
modelb.cpp \ artwork.cpp \
ui.cpp ui.cpp
UI_TYPE = generic UI_TYPE = generic
@ -35,7 +34,8 @@ include ../dpf/Makefile.plugins.mk
SKIP_NATIVE_AUDIO_FALLBACK = true SKIP_NATIVE_AUDIO_FALLBACK = true
TARGETS += jack lv2_sep # omitting LV2 for the moment until I figure out cross-compiling
TARGETS += jack vst2 vst3 clap
all: $(TARGETS) all: $(TARGETS)

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,9 @@ namespace Artwork {
extern const char *orngBtnUp; extern const char *orngBtnUp;
extern const char *orngBtnDn; extern const char *orngBtnDn;
extern const char *whiteBtnUp; const unsigned int orngBtnSize = 41 * 30 * 4;
extern const char *whiteBtnDn; const unsigned int orngBtnWidth = 41;
const unsigned int orngBtnHeight = 30;
const unsigned int whiteBtnSize = 41 * 30 * 4;
const unsigned int whiteBtnWidth = 41;
const unsigned int whiteBtnHeight = 30;
extern const char *ledOnData; extern const char *ledOnData;

View File

@ -43,7 +43,12 @@ Assigner::Assigner() {
} }
void Assigner::handleMidi(MidiEvent* ev) { void Assigner::handleMidi(MidiEvent* ev) {
uint8_t status = ev->data[0]; uint8_t status = ev->data[0];
//printf("called with event %04x (%02x): %02x %02x %02x\n", ev->frame, ev->size, ev->data[0], ev->data[1], ev->data[2]);
if (ev->size > 3) return; // sysex bug
switch (status & 0xf0) { switch (status & 0xf0) {
case 0x80: case 0x80:
noteOff(ev->data[1]); noteOff(ev->data[1]);
@ -56,9 +61,11 @@ void Assigner::handleMidi(MidiEvent* ev) {
break; break;
case 0xb0: case 0xb0:
switch (ev->data[1]) { switch (ev->data[1]) {
case 0x01: // modwheel
printf("mod wheel %02x\n", ev->data[2]);
m->modWheel = ev->data[2];
// handle the following // handle the following
// CC 1 - modwheel // CC 64 - sustain // FIXME sustain not implemented
// CC 64 - sustain
// possibly JU-06 CC values // possibly JU-06 CC values
default: default:
break; break;
@ -66,7 +73,8 @@ void Assigner::handleMidi(MidiEvent* ev) {
break; // nothing to do here except in special cases where we don't expect the host to pass on controls break; // nothing to do here except in special cases where we don't expect the host to pass on controls
case 0xc0: // program change case 0xc0: // program change
break; break;
case 0xe0: // pitch bend; case 0xe0: // pitch bend
m->bend = ((ev->data[1] + (ev->data[2]<<7))>>5)-0x100;
break; break;
case 0xf0: // sysex case 0xf0: // sysex
break; break;
@ -110,6 +118,10 @@ void Assigner::noteOn(uint8_t note) {
return; return;
} }
if ((noteTbl[voiceTbl[0]] & 0x80)) {
m->lfoRampOn();
}
// loop around the voices // loop around the voices
for (i = NUM_VOICES - 1; i >= 0; i--) { for (i = NUM_VOICES - 1; i >= 0; i--) {
v = voiceTbl[i]; v = voiceTbl[i];
@ -137,6 +149,6 @@ void Assigner::noteOn(uint8_t note) {
// printf("at end, l=%d e=%d\n", l,e); // printf("at end, l=%d e=%d\n", l,e);
noteTbl[v] = note; noteTbl[v] = note;
d_debug("send voice on %3d to voice %d", note, v); d_debug("send voice on %3d to voice %d", note, i);
voice[v].on(note); voice[v].on(note);
} }

View File

@ -27,6 +27,7 @@ class Assigner {
Assigner(); Assigner();
void handleMidi(MidiEvent* ev); void handleMidi(MidiEvent* ev);
Voice* voice; Voice* voice;
Module *m;
private: private:
void noteOn(uint8_t note); // incoming note on (or off, if velocity = 0) void noteOn(uint8_t note); // incoming note on (or off, if velocity = 0)

View File

@ -19,8 +19,8 @@
#include "chorus.hpp" #include "chorus.hpp"
#include <math.h> #include <math.h>
#include <stdio.h>
#include <string.h> #include <string.h>
Chorus::Chorus() { Chorus::Chorus() {
lpfOut1 = new float[bufferSize]; lpfOut1 = new float[bufferSize];
lpfOut2 = new float[bufferSize]; lpfOut2 = new float[bufferSize];
@ -29,7 +29,8 @@ Chorus::Chorus() {
lfoPhase = 1; lfoPhase = 1;
lfoSpeed = 6.283 * 10.7 / sampleRate; // plainly silly value to show if it hasn't been set lfoSpeed = 6.283 * 10.7 / sampleRate; // plainly silly value to show if it hasn't been set
gainTC = 1 - exp(-6.283 * 10 / sampleRate); gainTC = 1 - exp(-M_PI * 10 / sampleRate); // 1/10th of a second declick
bbdTC = 1 - exp(-M_PI * 60 / sampleRate); // hpf into BBD
// not quite Butterworth but you'd never hear the difference // not quite Butterworth but you'd never hear the difference
// these are calculated from the real-world component values // these are calculated from the real-world component values
@ -58,7 +59,7 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
// run highpass / bass boost and stereo chorus effect for one full block // run highpass / bass boost and stereo chorus effect for one full block
float s0 = 0, s1 = 0; float s0 = 0, s1 = 0;
float lfoMod, dly1, frac, flt; float dly1, frac, flt;
uint16_t tap, delay; uint16_t tap, delay;
for (uint32_t i = 0; i < frames; i++) { for (uint32_t i = 0; i < frames; i++) {
@ -75,11 +76,14 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
hpDelay = flt; hpDelay = flt;
input[i] += (flt * hpGain); input[i] += (flt * hpGain);
ram[delayptr] = input[i]; flt = ((input[i] - bbdRC) * bbdTC) + bbdRC;
bbdRC = flt;
ram[delayptr] = input[i] - flt;
// delays in milliseconds // delays in milliseconds
#define BASE 0.005 #define BASE 0.0035
#define AMT 0.00175 #define AMT 0.002
dly1 = (BASE + (AMT * lfoPhase)) * sampleRate; dly1 = (BASE + (AMT * lfoPhase)) * sampleRate;
delay = (int)dly1; delay = (int)dly1;
@ -102,7 +106,6 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
delayptr++; delayptr++;
delayptr &= 0x3ff; delayptr &= 0x3ff;
} }
postFilter1l->runSVF(lpfOut1, lpfOut1, frames); postFilter1l->runSVF(lpfOut1, lpfOut1, frames);
postFilter2l->runSVF(lpfOut1, lpfOut1, frames); postFilter2l->runSVF(lpfOut1, lpfOut1, frames);
postFilter1r->runSVF(lpfOut2, lpfOut2, frames); postFilter1r->runSVF(lpfOut2, lpfOut2, frames);
@ -111,7 +114,6 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
for (uint32_t i = 0; i < frames; i++) { for (uint32_t i = 0; i < frames; i++) {
float y = input[i]; float y = input[i];
gainRC = (gain - gainRC) * gainTC + gainRC; gainRC = (gain - gainRC) * gainTC + gainRC;
outputs[0][i] = y + (gainRC * lpfOut1[i]); outputs[0][i] = y + (gainRC * lpfOut1[i]);
outputs[1][i] = y + (gainRC * lpfOut2[i]); outputs[1][i] = y + (gainRC * lpfOut2[i]);
} }
@ -125,11 +127,11 @@ void Chorus::setHpf(uint8_t mode) {
// k = 1-exp(-2pi * Fc * sampleRate) // k = 1-exp(-2pi * Fc * sampleRate)
switch (mode) { switch (mode) {
case 0x00: case 0x00:
hpCut = 1 - exp(-6.283 * 720 / sampleRate); hpCut = 1 - exp(-M_PI * 720 / sampleRate);
hpGain = -1; hpGain = -1;
break; break;
case 0x08: case 0x08:
hpCut = 1 - exp(-6.283 * 225 / sampleRate); hpCut = 1 - exp(-M_PI * 225 / sampleRate);
hpGain = -1; hpGain = -1;
break; break;
case 0x10: case 0x10:
@ -138,7 +140,7 @@ void Chorus::setHpf(uint8_t mode) {
break; break;
case 0x18: case 0x18:
hpCut = 1 - exp(-6.283 * 85 / sampleRate); hpCut = 1 - exp(-6.283 * 85 / sampleRate);
hpGain = 1.707; hpGain = 1.0;
break; break;
} }
} }
@ -147,15 +149,16 @@ void Chorus::setChorus(uint8_t mode) {
// switch chorus mode // switch chorus mode
switch (mode) { switch (mode) {
case 0x60: case 0x60:
case 0x20:
gain = 0; gain = 0;
break; break;
case 0x40: case 0x40:
gain = 1.2; gain = 1.2;
lfoSpeed = 6.283 * 0.5 / sampleRate; lfoSpeed = M_PI * 0.525 / sampleRate;
break; break;
case 0x00: case 0x00:
gain = 1.2; gain = 1.2;
lfoSpeed = 6.283 * 0.9 / sampleRate; lfoSpeed = M_PI * 0.85 / sampleRate;
break; break;
} }
} }

View File

@ -46,6 +46,8 @@ class Chorus {
float gainRC = 0; float gainRC = 0;
float gainTC = 0; float gainTC = 0;
float bbdRC=0, bbdTC=0;
uint16_t delayptr = 0; uint16_t delayptr = 0;

File diff suppressed because it is too large Load Diff

View File

@ -25,13 +25,14 @@
Module::Module() { Module::Module() {
// cutoff frequencies for various RC networks // cutoff frequencies for various RC networks
vcaTC = 1 - exp(-6.283 * 159 / sampleRate); // VCA and VCF 10k/0.1u time constant vcaTC = 1 - exp(-M_PI * 159 / sampleRate); // VCA and VCF 10k/0.1u time constant
subTC = 1 - exp(-6.283 * 15 / sampleRate); // Main VCA and Sub Level 1k + 10u time constant subTC = 1 - exp(-M_PI * 15 / sampleRate); // Main VCA and Sub Level 1k + 10u time constant
pwmTC = 1 - exp(-6.283 * 40 / sampleRate); // integrator with 100k/0.047u time constant pwmTC = 1 - exp(-M_PI * 40 / sampleRate); // integrator with 100k/0.047u time constant
vcaBuf = new float[bufferSize]; vcaBuf = new float[bufferSize];
subBuf = new float[bufferSize]; subBuf = new float[bufferSize];
pwmBuf = new float[bufferSize]; pwmBuf = new float[bufferSize];
noiseBuf = new float[bufferSize];
} }
Module::~Module() { Module::~Module() {
@ -40,59 +41,115 @@ Module::~Module() {
delete subBuf; delete subBuf;
delete pwmBuf; delete pwmBuf;
} }
void Module::genNoise() {
for (uint32_t i = 0; i < bufferSize; i++) {
noiseRNG *= 0x8088405;
noiseRNG++;
noiseBuf[i] = 1 - (noiseRNG & 0xffff) / 32768.0f;
}
}
void Module::lfoRampOn() {
lfoDelayState = 1;
lfoDelayTimer = 0;
lfoDelay = 0;
}
void Module::runLFO() {
if (lfoDelayState == 1) {
lfoDelayTimer += attackTable[patchRam.lfoDelay];
if (lfoDelayTimer > 0x3fff) lfoDelayState = 2;
}
if ((lfoDelayState == 2)) {
lfoDelay += lfoDelayTable[patchRam.lfoDelay >> 4];
}
if (lfoDelay > 0xff) {
lfoDelayState = 0;
lfoDelay = 0xff;
}
lfoRate = lfoRateTable[patchRam.lfoRate]; // FIXME move to parameters
lfoPhase += (lfoState & 0x01) ? -lfoRate : lfoRate;
if (lfoPhase > 0x1fff) {
lfoPhase = 0x1fff;
lfoState++;
}
if (lfoPhase < 0x0000) {
lfoPhase = 0x0000;
lfoState++;
}
lfo = (lfoState & 0x02) ? -lfoPhase : lfoPhase;
pw = (lfoState & 0x02) ? lfoPhase + 0x2000 : 0x2000 - lfoPhase; // PW LFO is unipolar
pw = (patchRam.switch2 & 0x01) ? 0x3fff : pw; // either LFO or "all on"
pw = 0x3fff - ((pw * (int)(patchRam.pwmLfo*0.9125)) >> 7); // FIXME tidy up this bit
}
void Module::run(Voice* voices, uint32_t blockSize) { void Module::run(Voice* voices, uint32_t blockSize) {
// run updates for module board // run updates for module board
int16_t lfoToVco = 0, lfoToVcf = 0;
// FIXME break these out to the patch setter // FIXME break these out to the patch setter
a = attackTable[patchRam.env_a]; // attack time coeff looked up in table a = attackTable[patchRam.env_a]; // attack time coeff looked up in table
d = decayTable[patchRam.env_d]; // decay time coeff looked up in table d = decayTable[patchRam.env_d]; // decay time coeff looked up in table
r = decayTable[patchRam.env_r]; // release time coeff looked up in table r = decayTable[patchRam.env_r]; // release time coeff looked up in table
s = patchRam.env_s << 7; // scale 0x00-0x7f to 0x0000-0x3f80 s = patchRam.env_s << 7; // scale 0x00-0x7f to 0x0000-0x3f80
square = (patchRam.switch1 & 0x08) ? 0.63 : 0; master = powf(2, (patchRam.vca / 31.75 - 4.0f)) * 0.1;
saw = (patchRam.switch1 & 0x10) ? 0.8 : 0;
sub = patchRam.sub / 127.0f;
lfoPhase += lfoRateTable[patchRam.lfoRate];
res = patchRam.vcfReso / 127.0 * 5; square = (patchRam.switch1 & 0x08) ? 1 : 0;
noise = patchRam.noise / 127.0; saw = (patchRam.switch1 & 0x10) ? 1 : 0;
sub = (patchRam.sub / 127.0f) * 1.4;
res = patchRam.vcfReso / 127.0;
noise = (patchRam.noise / 127.0);
// FIXME the exp in these is expensive, don't call it all the time // FIXME the exp in these is expensive, don't call it all the time
chorus->setChorus(patchRam.switch1 & 0x60); chorus->setChorus(patchRam.switch1 & 0x60);
chorus->setHpf(patchRam.switch2 & 0x18); chorus->setHpf(patchRam.switch2 & 0x18);
if (lfoPhase & 0x4000) runLFO();
lfo = 0x1fff - (lfoPhase & 0x3fff);
else
lfo = (lfoPhase & 0x3fff) - 0x1fff;
// FIXME represent PW as int until we calculate the block?
pw = 0.5 - ((0x2000 + lfo) * patchRam.pwmLfo) / (32768.0f * 128);
pw = (patchRam.switch2 & 0x01) ? 0.5 - (patchRam.pwmLfo / 256.0f) : pw;
float master = powf(2, (patchRam.vca / 31.75 - 4.0f));
float sub = patchRam.sub/ 127.0f;
// calculate "smoothed" parameters
// these are single outputs with heavy RC smoothing
for (uint32_t i = 0; i < blockSize; i++) { for (uint32_t i = 0; i < blockSize; i++) {
vcaRC = (master - vcaRC) * subTC + vcaRC; vcaRC = (master - vcaRC) * subTC + vcaRC;
pwmRC = (pw - pwmRC) * pwmTC + pwmRC; pwmRC = ((pw / 32768.0f) - pwmRC) * pwmTC + pwmRC;
subRC = (sub - subRC) * vcaTC + subRC; subRC = (sub - subRC) * vcaTC + subRC;
vcaBuf[i] = vcaRC; vcaBuf[i] = vcaRC;
pwmBuf[i] = pwmRC; pwmBuf[i] = pwmRC;
subBuf[i] = subRC; subBuf[i] = subRC;
if (bufPtr < bufferSize) bufPtr++; if (bufPtr < bufferSize) bufPtr++;
} }
int16_t vcf = (patchRam.vcfEnv << 7) * ((patchRam.switch2 & 0x02) ? -1 : 1); lfoToVco = (lfoDepthTable[patchRam.vcoLfo] * lfoDelay) >> 8; // lookup table is 0-255
lfoToVco += ((int)(modWheel * modDepth));
int16_t pitchBase = 0x1818; if (lfoToVco > 0xff) lfoToVco = 0xff;
pitchBase += (lfo * lfoDepthTable[patchRam.vcoLfo]) >> 9; lfoToVco = (lfo * lfoToVco) >> 11; // 8 for normalisation plus three additional DSLR EA
lfoToVcf = (patchRam.vcfLfo * lfoDelay) >> 7; // value is 0-127
lfoToVcf = (lfo * lfoToVcf) >> 9; // 8 for normalisation plus one additional DSLR EA
int16_t pitchBase = 0x1818, vcfBase = 0;
pitchBase += lfoToVco;
pitchBase += vcoBendDepth * bend;
vcfBase = (patchRam.vcfFreq << 7);
vcfBase += lfoToVcf;
vcfBase += vcfBendDepth * bend;
if (vcfBase > 0x3fff) vcfBase = 0x3fff;
if (vcfBase < 0x0000) vcfBase = 0x0000;
// per-voice calculations
for (uint32_t i = 0; i < NUM_VOICES; i++) { for (uint32_t i = 0; i < NUM_VOICES; i++) {
// maybe move all this into voice.cpp FIXME // run one step of the envelope
Voice* v = &voices[i]; Voice* v = &voices[i];
switch (v->envPhase) { switch (v->envPhase) {
case 0: // release phase FIXME use an enum I guess case 0: // release phase FIXME use an enum I guess
@ -111,21 +168,30 @@ void Module::run(Voice* voices, uint32_t blockSize) {
} }
// pitch // pitch
// FIXME clean this all up a bit uint16_t pitch = pitchBase + (v->note << 8);
int16_t pitch = pitchBase + (v->note << 8); int8_t semi = pitch >> 8;
int16_t semi = pitch >> 8; semi -= 36;
float frac = (pitch & 0xff) / 256.0; float frac = (pitch & 0xff) / 256.0;
if (semi < 0) {
semi = 0;
frac = 0;
}
if (semi >= 103) {
semi = 103;
frac = 0;
};
float p1 = pitchTable[semi], p2 = pitchTable[semi + 1]; float p1 = pitchTable[semi], p2 = pitchTable[semi + 1];
int16_t px = ((p2 - p1) * frac + p1); int16_t px = ((p2 - p1) * frac + p1); // interpolated pitch from table
// octave divider
px *= (patchRam.switch1 & 0x07); px *= (patchRam.switch1 & 0x07);
v->omega = px / (sampleRate * 4.0f); // fixme use proper scaler v->omega = px / sampleRate; // FIXME recalculate table using proper scaler
// per voice we need to calculate the key follow amount and envelope amount // per voice we need to calculate the key follow amount and envelope amount
v->vcfCut = (patchRam.vcfFreq << 7) + ((vcf * v->env) >> 14); v->vcfCut = vcfBase + (((v->env * patchRam.vcfEnv) >> 7) * ((patchRam.switch2 & 0x02) ? -1 : 1));
v->vcfCut += (int)(v->note * (patchRam.vcfKey << 1) * 0.375);
v->vcfCut += (int)((v->note - 60) * (patchRam.vcfKey << 1) * 0.375);
if (v->vcfCut > 0x3fff) v->vcfCut = 0x3fff; if (v->vcfCut > 0x3fff) v->vcfCut = 0x3fff;
if (v->vcfCut < 0) v->vcfCut = 0; if (v->vcfCut < 0) v->vcfCut = 0;

View File

@ -33,40 +33,39 @@ class Module {
Module(); Module();
~Module(); ~Module();
void genNoise();
void lfoRampOn();
void run(Voice* voices, uint32_t blockLeft); void run(Voice* voices, uint32_t blockLeft);
float res = 0; float res = 0;
// precomputed values for all voices
float pw; //, saw, square, sub;
// "internal state" values for patch parameters // "internal state" values for patch parameters
uint16_t a, d, s, r; uint16_t a, d, s, r;
int16_t lfo;
uint32_t lfoPhase;
float saw = 0, square = 0, sub = 0, noise = 0;
float saw = 0, square = 0, sub = 0, noise = 0, master = 0;
int16_t bend = 0, modWheel=0;
float vcoBendDepth = 4, vcfBendDepth=1.5, modDepth=.5;
struct { struct {
uint8_t lfoRate = 0x18; uint8_t lfoRate = 0x1f;
uint8_t lfoDelay = 0x00; uint8_t lfoDelay = 0x00;
uint8_t vcoLfo = 0x00; uint8_t vcoLfo = 0x00;
uint8_t pwmLfo = 0x60; uint8_t pwmLfo = 0x3c;
uint8_t noise = 0x00; uint8_t noise = 0x00;
uint8_t vcfFreq = 0x30; // 1c; // 0x3f80 uint8_t vcfFreq = 0x25; // 1c; // 0x3f80
uint8_t vcfReso = 0x00; uint8_t vcfReso = 0x1d;
uint8_t vcfEnv = 0x40; // 4e; uint8_t vcfEnv = 0x1c; // 4e;
uint8_t vcfLfo = 0; uint8_t vcfLfo = 0x00;
uint8_t vcfKey = 0x7f; // 47; uint8_t vcfKey = 0x2b; // 47;
uint8_t vca = 0x28; uint8_t vca = 0x5c;
uint8_t env_a = 0x00; uint8_t env_a = 0x00;
uint8_t env_d = 0x39; uint8_t env_d = 0x2a;
uint8_t env_s = 0x30; // 0x3f80 uint8_t env_s = 0x23; // 0x3f80
uint8_t env_r = 0x30; uint8_t env_r = 0x00;
uint8_t sub = 0x40; uint8_t sub = 0x40;
uint8_t switch1 = 0x59; uint8_t switch1 = 0x19;
uint8_t switch2 = 0x18; uint8_t switch2 = 0x18;
} patchRam; } patchRam;
Chorus* chorus; Chorus* chorus;
float vcaTC; float vcaTC;
@ -75,13 +74,25 @@ class Module {
float* vcaBuf; float* vcaBuf;
float* subBuf; float* subBuf;
float* pwmBuf; float* pwmBuf;
float* noiseBuf;
private: private:
void runLFO();
// precalculated coefficients for RC networks // precalculated coefficients for RC networks
float pwmTC = 0, subTC = 0, mVcaTC = 0; float pwmTC = 0, subTC = 0, mVcaTC = 0;
float pwmRC = 0, subRC = 0, vcaRC = 0; float pwmRC = 0, subRC = 0, vcaRC = 0;
// controls int16_t lfo, pw;
int16_t lfoPhase;
uint8_t lfoState = 0;
uint16_t lfoRate;
uint32_t noiseRNG = 1;
uint16_t lfoDelay = 0;
uint8_t lfoDelayState = 0;
uint16_t lfoDelayTimer = 0;
}; };
class Voice { class Voice {
@ -91,7 +102,7 @@ class Voice {
Voice(); Voice();
void on(uint8_t midiNote); void on(uint8_t midiNote);
void off(); void off();
void run(Module* m, float* buffer, uint32_t samples); void run(Module* m, float* buffer, uint32_t framePos, uint32_t samples);
private: private:
float omega = 0, theta = 0; // phase increment and angle FIXME better names float omega = 0, theta = 0; // phase increment and angle FIXME better names
@ -99,16 +110,16 @@ class Voice {
uint8_t pulseStage = 1; // pulse wave phase uint8_t pulseStage = 1; // pulse wave phase
float subosc = 1; // sub oscillator flipflop output float subosc = 1; // sub oscillator flipflop output
uint8_t envPhase = 0; uint8_t envPhase = 0; // current running state of envelope
int16_t env = 0; // output amplitude int16_t env = 0; // calculated envelope amount
int16_t vcfCut; int16_t vcfCut; // calculated cutoff to filter
int16_t vcaEnv; int16_t vcaEnv; // calculated level to VCA (env/gate)
float vcaRC = 0, vcfRC = 0; float vcaRC = 0, vcfRC = 0; // RC circuit state values
uint8_t note = 0; uint8_t note = 0;
// filter // filter
float b1 = 0, b2 = 0, b3 = 0, b4 = 0; float y0 = 0, y1 = 0, y2 = 0, y3 = 0;
}; };
#endif #endif

View File

@ -23,7 +23,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pLFORate: case pLFORate:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO Rate"; parameter.name = "LFO Rate";
parameter.symbol = "ch_lforate"; parameter.symbol = "pfau_lforate";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f; parameter.ranges.def = 48.0f;
@ -33,7 +33,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pLFODelay: case pLFODelay:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO Delay"; parameter.name = "LFO Delay";
parameter.symbol = "ch_lfodelay"; parameter.symbol = "pfau_lfodelay";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -43,7 +43,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pVCORange: case pVCORange:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Range"; parameter.name = "Range";
parameter.symbol = "ch_vcorange"; parameter.symbol = "pfau_vcorange";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 2.0f; parameter.ranges.max = 2.0f;
parameter.ranges.def = 1.0f; parameter.ranges.def = 1.0f;
@ -60,13 +60,12 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
enumValues[2].label = "4'"; enumValues[2].label = "4'";
parameter.enumValues.values = enumValues; parameter.enumValues.values = enumValues;
} }
break; break;
case pLFODepth: case pLFODepth:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO"; parameter.name = "LFO";
parameter.symbol = "ch_lfo"; parameter.symbol = "pfau_lfo";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 10.0f; parameter.ranges.def = 10.0f;
@ -76,7 +75,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pPWMDepth: case pPWMDepth:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "PWM"; parameter.name = "PWM";
parameter.symbol = "ch_pwm"; parameter.symbol = "pfau_pwm";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f; parameter.ranges.def = 48.0f;
@ -86,7 +85,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pPWMMode: case pPWMMode:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "PWM Mode"; parameter.name = "PWM Mode";
parameter.symbol = "ch_pwmmode"; parameter.symbol = "pfau_pwmmode";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f; parameter.ranges.def = 1.0f;
@ -107,7 +106,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pSaw: case pSaw:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Saw"; parameter.name = "Saw";
parameter.symbol = "ch_saw"; parameter.symbol = "pfau_saw";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f; parameter.ranges.def = 1.0f;
@ -117,7 +116,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pSqr: case pSqr:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Square"; parameter.name = "Square";
parameter.symbol = "ch_sqr"; parameter.symbol = "pfau_sqr";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f; parameter.ranges.def = 1.0f;
@ -127,7 +126,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pSubLevel: case pSubLevel:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Sub Osc"; parameter.name = "Sub Osc";
parameter.symbol = "ch_sub"; parameter.symbol = "pfau_sub";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -137,7 +136,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pNoiseLevel: case pNoiseLevel:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Noise"; parameter.name = "Noise";
parameter.symbol = "ch_noise"; parameter.symbol = "pfau_noise";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -147,7 +146,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pHPF: case pHPF:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "HPF"; parameter.name = "HPF";
parameter.symbol = "ch_hpf"; parameter.symbol = "pfau_hpf";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 3.9f; parameter.ranges.max = 3.9f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -157,7 +156,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pCutoff: case pCutoff:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Freq"; parameter.name = "Freq";
parameter.symbol = "ch_freq"; parameter.symbol = "pfau_freq";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 60.0f; parameter.ranges.def = 60.0f;
@ -166,7 +165,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pRes: case pRes:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Res"; parameter.name = "Res";
parameter.symbol = "ch_reso"; parameter.symbol = "pfau_reso";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -175,7 +174,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pVCFPol: case pVCFPol:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Polarity"; parameter.name = "Polarity";
parameter.symbol = "ch_vcfmode"; parameter.symbol = "pfau_vcfmode";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -195,7 +194,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pEnv: case pEnv:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Env"; parameter.name = "Env";
parameter.symbol = "ch_vcfenv"; parameter.symbol = "pfau_vcfenv";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 46.0f; parameter.ranges.def = 46.0f;
@ -204,7 +203,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pLfo: case pLfo:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO"; parameter.name = "LFO";
parameter.symbol = "ch_vcflfo"; parameter.symbol = "pfau_vcflfo";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -213,7 +212,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pKyb: case pKyb:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Kybd"; parameter.name = "Kybd";
parameter.symbol = "ch_vcfkey"; parameter.symbol = "pfau_vcfkey";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 71.0f; parameter.ranges.def = 71.0f;
@ -223,7 +222,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pAttack: case pAttack:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Attack"; parameter.name = "Attack";
parameter.symbol = "ch_attack"; parameter.symbol = "pfau_attack";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 27.0f; parameter.ranges.def = 27.0f;
@ -233,7 +232,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pDecay: case pDecay:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Decay"; parameter.name = "Decay";
parameter.symbol = "ch_decay"; parameter.symbol = "pfau_decay";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 57.0f; parameter.ranges.def = 57.0f;
@ -243,7 +242,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pSustain: case pSustain:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Sustain"; parameter.name = "Sustain";
parameter.symbol = "ch_sustain"; parameter.symbol = "pfau_sustain";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 57.0f; parameter.ranges.def = 57.0f;
@ -253,7 +252,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pRelease: case pRelease:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Release"; parameter.name = "Release";
parameter.symbol = "ch_release"; parameter.symbol = "pfau_release";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f; parameter.ranges.def = 48.0f;
@ -263,7 +262,7 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pEnvGate: case pEnvGate:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean;
parameter.name = "Env-Gate"; parameter.name = "Env-Gate";
parameter.symbol = "ch_envgate"; parameter.symbol = "pfau_envgate";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
@ -283,25 +282,62 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
case pVCALevel: case pVCALevel:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "VCA Level"; parameter.name = "VCA Level";
parameter.symbol = "ch_vcalevel"; parameter.symbol = "pfau_vcalevel";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 40.0f; parameter.ranges.def = 40.0f;
parameter.midiCC = 26; parameter.midiCC = 26;
break; break;
/*
case pModWheel: case pChorusMode:
parameter.hints = kParameterIsAutomatable | kParameterIsHidden; parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Mod wheel"; parameter.name = "Chorus";
parameter.symbol = "ch_modwheel"; parameter.symbol = "pfau_chorus";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 2.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
parameter.midiCC = 1; parameter.midiCC = 93;
break; parameter.enumValues.count = 3;
*/ parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3];
enumValues[0].value = 0.0f;
enumValues[0].label = "Off";
enumValues[1].value = 1.0f;
enumValues[1].label = "Slow";
enumValues[2].value = 2.0f;
enumValues[2].label = "Fast";
parameter.enumValues.values = enumValues;
}
break;
case pVcoBend:
parameter.hints = kParameterIsAutomatable;
parameter.name = "VCO Bend";
parameter.symbol = "pfau_vcobend";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 12.0f;
parameter.ranges.def = 4.0f;
break;
case pVcfBend:
parameter.hints = kParameterIsAutomatable;
parameter.name = "VCF Bend";
parameter.symbol = "pfau_vcfbend";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 42.0f;
parameter.ranges.def = 4.0f;
break;
case pModDepth:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Mod Depth";
parameter.symbol = "pfau_moddepth";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 4.0f;
parameter.ranges.def = 0.5f;
break;
} }
// chorus, porta, bend range, key mode still to do
} }
void Peacock::setParameterValue(uint32_t index, float value) { void Peacock::setParameterValue(uint32_t index, float value) {
@ -321,7 +357,7 @@ void Peacock::setParameterValue(uint32_t index, float value) {
m->patchRam.vcoLfo = value; m->patchRam.vcoLfo = value;
break; break;
case pPWMDepth: case pPWMDepth:
m->patchRam.pwmLfo = value / 1.27; m->patchRam.pwmLfo = value;
break; break;
case pSubLevel: case pSubLevel:
m->patchRam.sub = value; m->patchRam.sub = value;
@ -379,9 +415,8 @@ void Peacock::setParameterValue(uint32_t index, float value) {
m->patchRam.switch1 |= (value >= 0.5) << 4; m->patchRam.switch1 |= (value >= 0.5) << 4;
break; break;
case pChorus: case pChorusMode:
m->patchRam.switch1 &= 0x9f; m->patchRam.switch1 &= 0x9f;
switch ((int)value) { switch ((int)value) {
case 0: case 0:
m->patchRam.switch1 |= 0x60; // both off m->patchRam.switch1 |= 0x60; // both off
@ -415,10 +450,15 @@ void Peacock::setParameterValue(uint32_t index, float value) {
m->patchRam.switch2 &= 0xe7; m->patchRam.switch2 &= 0xe7;
m->patchRam.switch2 |= (3 - (int)value) << 3; m->patchRam.switch2 |= (3 - (int)value) << 3;
break; break;
/* case pVcoBend:
case pModWheel: m->vcoBendDepth = value;
//s.ff64 = (int)value << 1; break;
break;*/ case pVcfBend:
m->vcfBendDepth = value / 2.66f;
break;
case pModDepth:
m->modDepth = value;
break;
} }
} }
@ -447,7 +487,7 @@ float Peacock::getParameterValue(uint32_t index) const {
return m->patchRam.vcoLfo; return m->patchRam.vcoLfo;
break; break;
case pPWMDepth: case pPWMDepth:
return m->patchRam.pwmLfo * 1.27f; return m->patchRam.pwmLfo;
break; break;
case pPWMMode: case pPWMMode:
@ -506,8 +546,7 @@ float Peacock::getParameterValue(uint32_t index) const {
case pVCALevel: case pVCALevel:
return m->patchRam.vca; return m->patchRam.vca;
break; break;
case pChorus: case pChorusMode:
switch (m->patchRam.switch1 & 0x60) { switch (m->patchRam.switch1 & 0x60) {
case 0x60: case 0x60:
return 0; return 0;
@ -518,8 +557,16 @@ float Peacock::getParameterValue(uint32_t index) const {
default: default:
break; break;
} }
break;
case pVcoBend:
return m->vcoBendDepth;
break;
case pVcfBend:
return m->vcfBendDepth * 2.66f;
break;
case pModDepth:
return m->modDepth;
break;
} }
return 0; return 0;
} }

View File

@ -28,10 +28,12 @@ Peacock::Peacock() : Plugin(parameterCount, 0, 0) {
sampleRate = getSampleRate(); sampleRate = getSampleRate();
bufferSize = getBufferSize(); bufferSize = getBufferSize();
chorus = new Chorus(); chorus = new Chorus();
m = new Module(); m = new Module();
ic1 = new Assigner; ic1 = new Assigner;
ic1->voice = voice; ic1->voice = voice;
ic1->m = m;
m->chorus = chorus; m->chorus = chorus;
} }
@ -69,27 +71,27 @@ void Peacock::run(const float**, float** outputs, uint32_t frames, const MidiEve
memset(outputs[0], 0, frames * sizeof(float)); memset(outputs[0], 0, frames * sizeof(float));
memset(outputs[1], 0, frames * sizeof(float)); memset(outputs[1], 0, frames * sizeof(float));
m->genNoise();
// if there were any events that happen between now and the end of this block, process them // if there were any events that happen between now and the end of this block, process them
lastEvent = 0; lastEvent = 0;
m->bufPtr = 0; m->bufPtr = 0; // reset the output buffer pointer
runMidi(midiEvents, midiEventCount, blockLeft); runMidi(midiEvents, midiEventCount, blockLeft);
while (framePos < frames) { while (framePos < frames) {
if (blockLeft == 0) { if (blockLeft == 0) {
// no more samples to calculate in this update period // no more samples to calculate in this update period
blockLeft = sampleRate / 238; // update rate in Hz blockLeft = sampleRate / 233.5; // update rate in Hz, measured
runMidi(midiEvents, midiEventCount, framePos + blockLeft); runMidi(midiEvents, midiEventCount, framePos + blockLeft);
m->run(voice, blockLeft);
} }
// how many frames to do? Are we about to run off an update block // how many frames to do? Are we about to run off an update block
sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft; sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft;
m->run(voice, sizeThisTime);
// now run all the voices for this chunk of samples // now run all the voices for this chunk of samples
for (uint32_t i = 0; i < NUM_VOICES; i++) { for (uint32_t i = 0; i < NUM_VOICES; i++) {
voice[i].run(m, outputs[0] + framePos, sizeThisTime); voice[i].run(m, outputs[0], framePos, sizeThisTime);
} }
framePos += sizeThisTime; framePos += sizeThisTime;
@ -98,7 +100,7 @@ void Peacock::run(const float**, float** outputs, uint32_t frames, const MidiEve
} }
// now we've assembled a full chunk of audio // now we've assembled a full chunk of audio
//memcpy(outputs[0], m->vcaBuf, sizeof(float)* frames); // memcpy(outputs[0], m->vcaBuf, sizeof(float)* frames);
chorus->run(outputs[0], outputs, frames); chorus->run(outputs[0], outputs, frames);
} }

View File

@ -89,19 +89,19 @@ uint16_t lfoDelayTable[8] = {
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100}; 0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
float pitchTable[104] = { float pitchTable[104] = {
32.494, 34.430, 36.486, 38.658, 40.962, 43.399, 45.990, 48.731, 8.123, 8.607, 9.122, 9.664, 10.240, 10.850, 11.497, 12.183,
51.633, 54.711, 57.969, 61.419, 65.072, 68.944, 73.059, 77.405, 12.908, 13.678, 14.492, 15.355, 16.268, 17.236, 18.265, 19.351,
82.014, 86.892, 92.077, 97.556, 103.365, 109.529, 116.043, 122.933, 20.504, 21.723, 23.019, 24.389, 25.841, 27.382, 29.011, 30.733,
130.242, 137.988, 146.220, 154.919, 164.136, 173.898, 184.264, 195.217, 32.561, 34.497, 36.555, 38.730, 41.034, 43.474, 46.066, 48.804,
206.847, 219.178, 232.207, 245.972, 260.586, 276.091, 292.569, 309.981, 51.712, 54.795, 58.052, 61.493, 65.147, 69.023, 73.142, 77.495,
328.407, 347.947, 368.664, 390.549, 413.822, 438.500, 464.576, 492.005, 82.102, 86.987, 92.166, 97.637, 103.455, 109.625, 116.144, 123.001,
521.241, 552.334, 585.309, 620.155, 657.030, 696.136, 737.463, 781.250, 130.310, 138.083, 146.327, 155.039, 164.258, 174.034, 184.366, 195.312,
827.815, 877.193, 929.368, 984.252, 1042.753, 1104.972, 1170.960, 1240.695, 206.954, 219.298, 232.342, 246.063, 260.688, 276.243, 292.740, 310.174,
1314.060, 1392.758, 1474.926, 1562.500, 1655.629, 1754.386, 1858.736, 1968.504, 328.515, 348.189, 368.732, 390.625, 413.907, 438.596, 464.684, 492.126,
2085.506, 2209.945, 2341.920, 2481.390, 2628.121, 2785.515, 2949.853, 3125.000, 521.376, 552.486, 585.480, 620.347, 657.030, 696.379, 737.463, 781.250,
3311.258, 3508.772, 3717.472, 3937.008, 4175.365, 4424.779, 4683.841, 4962.779, 827.815, 877.193, 929.368, 984.252, 1043.841, 1106.195, 1170.960, 1240.695,
5263.158, 5571.031, 5899.705, 6250.000, 6622.517, 7017.544, 7434.944, 7874.016, 1315.789, 1392.758, 1474.926, 1562.500, 1655.629, 1754.386, 1858.736, 1968.504,
8333.333, 8849.558, 9389.671, 9950.249, 10526.316, 11173.184, 11834.320, 12500.000 2083.333, 2212.389, 2347.418, 2487.562, 2631.579, 2793.296, 2958.580, 3125.000,
}; };
#endif #endif

View File

@ -46,56 +46,59 @@ DistrhoUIPeacock::DistrhoUIPeacock() : UI(Art::backgroundWidth, Art::backgroundH
fImgLedOn(Art::ledOnData, Art::ledWidth, Art::ledHeight, kImageFormatRGBA) fImgLedOn(Art::ledOnData, Art::ledWidth, Art::ledHeight, kImageFormatRGBA)
{ {
Image whiteSlider(Art::whiteData, Art::sliderWidth, Art::sliderHeight, kImageFormatRGBA); Image orangeSlider(Art::orangeData, Art::sliderWidth, Art::sliderHeight, kImageFormatRGBA);
Image greenSlider(Art::greenData, Art::sliderWidth, Art::sliderHeight, kImageFormatRGBA);
Image blueSlider(Art::blueData, Art::sliderWidth, Art::sliderHeight, kImageFormatRGBA);
Image whiteSlider(Art::whiteData, Art::sliderWidth, Art::sliderHeight, kImageFormatRGBA);
Image switchSlider(Art::switchData, Art::switchWidth, Art::switchHeight, kImageFormatRGBA); Image switchSlider(Art::switchData, Art::switchWidth, Art::switchHeight, kImageFormatRGBA);
xSliderLFORate = new ImageSlider(this, whiteSlider); xSliderLFORate = new ImageSlider(this, orangeSlider);
prepSlider(xSliderLFORate, pLFORate, 46, 72); prepSlider(xSliderLFORate, pLFORate, 46, 72);
xSliderLFORate->setCallback(this); xSliderLFORate->setCallback(this);
xSliderLFODelay = new ImageSlider(this, whiteSlider); xSliderLFODelay = new ImageSlider(this, orangeSlider);
prepSlider(xSliderLFODelay, pLFODelay, 84, 72); prepSlider(xSliderLFODelay, pLFODelay, 84, 72);
xSliderLFODelay->setCallback(this); xSliderLFODelay->setCallback(this);
xSliderLFODepth = new ImageSlider(this, whiteSlider); xSliderLFODepth = new ImageSlider(this, greenSlider);
prepSlider(xSliderLFODepth, pLFODepth, 290, 72); prepSlider(xSliderLFODepth, pLFODepth, 290, 72);
xSliderLFODepth->setCallback(this); xSliderLFODepth->setCallback(this);
xSliderPWMDepth = new ImageSlider(this, whiteSlider); xSliderPWMDepth = new ImageSlider(this, greenSlider);
prepSlider(xSliderPWMDepth, pPWMDepth, 329, 72); prepSlider(xSliderPWMDepth, pPWMDepth, 329, 72);
xSliderPWMDepth->setCallback(this); xSliderPWMDepth->setCallback(this);
xSliderSubLevel = new ImageSlider(this, whiteSlider); xSliderSubLevel = new ImageSlider(this, greenSlider);
prepSlider(xSliderSubLevel, pSubLevel, 510, 72); prepSlider(xSliderSubLevel, pSubLevel, 510, 72);
xSliderSubLevel->setCallback(this); xSliderSubLevel->setCallback(this);
xSliderNoiseLevel = new ImageSlider(this, whiteSlider); xSliderNoiseLevel = new ImageSlider(this, greenSlider);
prepSlider(xSliderNoiseLevel, pNoiseLevel, 551, 72); prepSlider(xSliderNoiseLevel, pNoiseLevel, 551, 72);
xSliderNoiseLevel->setCallback(this); xSliderNoiseLevel->setCallback(this);
xSliderHPF = new ImageSlider(this, whiteSlider); xSliderHPF = new ImageSlider(this, blueSlider);
prepSlider(xSliderHPF, pHPF, 618, 72); prepSlider(xSliderHPF, pHPF, 618, 72);
xSliderHPF->setStep(1); xSliderHPF->setStep(1);
xSliderHPF->setRange(0, 3); xSliderHPF->setRange(0, 3);
xSliderHPF->setCallback(this); xSliderHPF->setCallback(this);
xSliderCutoff = new ImageSlider(this, whiteSlider); xSliderCutoff = new ImageSlider(this, blueSlider);
prepSlider(xSliderCutoff, pCutoff, 26, 261); prepSlider(xSliderCutoff, pCutoff, 26, 261);
xSliderCutoff->setCallback(this); xSliderCutoff->setCallback(this);
xSliderRes = new ImageSlider(this, whiteSlider); xSliderRes = new ImageSlider(this, blueSlider);
prepSlider(xSliderRes, pRes, 65, 261); prepSlider(xSliderRes, pRes, 65, 261);
xSliderRes->setCallback(this); xSliderRes->setCallback(this);
xSliderEnv = new ImageSlider(this, whiteSlider); xSliderEnv = new ImageSlider(this, blueSlider);
prepSlider(xSliderEnv, pEnv, 149, 261); prepSlider(xSliderEnv, pEnv, 149, 261);
xSliderEnv->setCallback(this); xSliderEnv->setCallback(this);
xSliderLfo = new ImageSlider(this, whiteSlider); xSliderLfo = new ImageSlider(this, blueSlider);
prepSlider(xSliderLfo, pLfo, 189, 261); prepSlider(xSliderLfo, pLfo, 189, 261);
xSliderLfo->setCallback(this); xSliderLfo->setCallback(this);
xSliderKyb = new ImageSlider(this, whiteSlider); xSliderKyb = new ImageSlider(this, blueSlider);
prepSlider(xSliderKyb, pKyb, 228, 261); prepSlider(xSliderKyb, pKyb, 228, 261);
xSliderKyb->setCallback(this); xSliderKyb->setCallback(this);
@ -131,47 +134,47 @@ DistrhoUIPeacock::DistrhoUIPeacock() : UI(Art::backgroundWidth, Art::backgroundH
prepSwitch(xSwitchVCA, pEnvGate, 283, 292); prepSwitch(xSwitchVCA, pEnvGate, 283, 292);
xSwitchVCA->setCallback(this); xSwitchVCA->setCallback(this);
xBtn16ft = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtn16ft = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtn16ft->setAbsolutePos(149, 104); xBtn16ft->setAbsolutePos(149, 104);
xBtn16ft->setId(btn16); xBtn16ft->setId(btn16);
xBtn16ft->setCallback(this); xBtn16ft->setCallback(this);
xBtn8ft = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtn8ft = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtn8ft->setAbsolutePos(190, 104); xBtn8ft->setAbsolutePos(190, 104);
xBtn8ft->setId(btn8); xBtn8ft->setId(btn8);
xBtn8ft->setCallback(this); xBtn8ft->setCallback(this);
xBtn4ft = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtn4ft = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtn4ft->setAbsolutePos(231, 104); xBtn4ft->setAbsolutePos(231, 104);
xBtn4ft->setId(btn4); xBtn4ft->setId(btn4);
xBtn4ft->setCallback(this); xBtn4ft->setCallback(this);
// waveform // waveform
xBtnPls = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtnPls = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtnPls->setAbsolutePos(414, 104); xBtnPls->setAbsolutePos(414, 104);
xBtnPls->setId(btnPls); xBtnPls->setId(btnPls);
xBtnPls->setCallback(this); xBtnPls->setCallback(this);
xBtnSaw = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtnSaw = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtnSaw->setAbsolutePos(455, 104); xBtnSaw->setAbsolutePos(455, 104);
xBtnSaw->setId(btnSaw); xBtnSaw->setId(btnSaw);
xBtnSaw->setCallback(this); xBtnSaw->setCallback(this);
// Chorus // Chorus
xBtnCh0 = new ImageButton(this, Image(Art::whiteBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtnCh0 = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::whiteBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtnCh0->setAbsolutePos(562, 293); xBtnCh0->setAbsolutePos(562, 293);
xBtnCh0->setId(btnCh0); xBtnCh0->setId(btnCh0);
xBtnCh0->setCallback(this); xBtnCh0->setCallback(this);
xBtnCh1 = new ImageButton(this, Image(Art::orngBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtnCh1 = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::orngBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtnCh1->setAbsolutePos(603, 293); xBtnCh1->setAbsolutePos(603, 293);
xBtnCh1->setId(btnCh1); xBtnCh1->setId(btnCh1);
xBtnCh1->setCallback(this); xBtnCh1->setCallback(this);
xBtnCh2 = new ImageButton(this, Image(Art::orngBtnUp, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA), xBtnCh2 = new ImageButton(this, Image(Art::orngBtnUp, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA),
Image(Art::orngBtnDn, Art::whiteBtnWidth, Art::whiteBtnHeight, kImageFormatRGBA)); Image(Art::orngBtnDn, Art::orngBtnWidth, Art::orngBtnHeight, kImageFormatRGBA));
xBtnCh2->setAbsolutePos(644, 293); xBtnCh2->setAbsolutePos(644, 293);
xBtnCh2->setId(btnCh2); xBtnCh2->setId(btnCh2);
xBtnCh2->setCallback(this); xBtnCh2->setCallback(this);
@ -255,17 +258,20 @@ void DistrhoUIPeacock::parameterChanged(uint32_t index, float value) {
sw1 &= 0xf8; // mask sw1 &= 0xf8; // mask
if (value > 2) value = 2; if (value > 2) value = 2;
sw1 |= (1 << (int)value); sw1 |= (1 << (int)value);
repaint();
break; break;
case pSqr: case pSqr:
sw1 &= 0xf7; sw1 &= 0xf7;
sw1 |= ((value >= 0.5)) << 3; sw1 |= ((value >= 0.5)) << 3;
repaint();
break; break;
case pSaw: case pSaw:
sw1 &= 0xef; sw1 &= 0xef;
sw1 |= (value > 0.5) << 4; sw1 |= (value > 0.5) << 4;
repaint();
break; break;
case pChorus: case pChorusMode:
sw1 &= 0x9f; sw1 &= 0x9f;
// 60, 40, 00 // 60, 40, 00
switch ((int)value) { switch ((int)value) {
@ -279,6 +285,8 @@ void DistrhoUIPeacock::parameterChanged(uint32_t index, float value) {
sw1 |= 0x00; sw1 |= 0x00;
break; break;
} }
repaint();
break;
} }
} }
@ -322,20 +330,21 @@ void DistrhoUIPeacock::imageButtonClicked(ImageButton* imgBtn, int) {
break; break;
case btnCh0: case btnCh0:
sw1 = (sw1 & 0x9f) | 0x20; sw1 = (sw1 & 0x9f) | 0x20;
setParameterValue(pChorus, 0); setParameterValue(pChorusMode, 0);
break; break;
case btnCh1: case btnCh1:
sw1 = (sw1 & 0x9f) | 0x40; sw1 = (sw1 & 0x9f) | 0x40;
setParameterValue(pChorus, 1); setParameterValue(pChorusMode, 1);
break; break;
case btnCh2: case btnCh2:
sw1 = (sw1 & 0x9f); sw1 = (sw1 & 0x9f);
setParameterValue(pChorus, 2); setParameterValue(pChorusMode, 2);
break; break;
default: default:
break; break;
} }
repaint();
} }
void DistrhoUIPeacock::onDisplay() { void DistrhoUIPeacock::onDisplay() {

View File

@ -40,11 +40,9 @@ Voice::Voice() {
} }
void Voice::on(uint8_t midiNote) { void Voice::on(uint8_t midiNote) {
// omega = 261.63 * powf(2, (note - 60) / 12.0f) / 48000.0f; while (midiNote < 24) midiNote += 12; // limit lowest note to C1
if (midiNote > 24) while (midiNote > 108) midiNote -= 12; // limit highest note to C8
note = midiNote - 24; note = midiNote;
else
note = 24;
envPhase = 1; envPhase = 1;
} }
@ -52,17 +50,19 @@ void Voice::off() {
envPhase = 0; envPhase = 0;
} }
void Voice::run(Module* m, float* buffer, uint32_t samples) { void Voice::run(Module* m, float* buffer, uint32_t framePos, uint32_t samples) {
// carry out per-voice calculations for each block of samples // carry out per-voice calculations for each block of samples
float out, t, fb; float out, t, fb;
// FIXME incorrect
// calculate cutoff frequency // calculate cutoff frequency
float cut = 248.0f * (powf(2, (vcfCut - 0x1880) / 1143.0f)); float cut = 261.0f * (powf(2, (vcfCut - 0x1880) / 1143.0f)); // FIXME explain magic numbers
cut = 0.25 * 6.2832 * cut / 48000.0f; // FIXME hardcoded values cut = M_PI * cut / sampleRate;
cut = cut / (1 + cut); // correct tuning warp cut = cut / (1 + cut); // correct tuning warp
if (cut > 0.7) cut = 0.7;
float amp = vcaEnv / 4096.0f; float r = 6 * m->res;
float amp = vcaEnv / 32768.0f;
for (uint32_t i = 0; i < samples; i++) { for (uint32_t i = 0; i < samples; i++) {
out = delay; out = delay;
@ -94,36 +94,31 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
} }
} }
// FIXME DC offset removal
delay += m->saw * (1 - (2 * theta)); delay += m->saw * (1 - (2 * theta));
delay += m->square * ((pulseStage ? -1.f : 1.f) - m->pwmBuf[i] + 0.5); delay += m->square * ((pulseStage ? -1.f : 1.f) - m->pwmBuf[i] + 0.5);
delay += m->subBuf[i] * subosc ; delay += m->subBuf[i] * subosc;
out += m->noise * (0.8 - 1.6 * (rand() & 0xffff) / 65536.0); out += m->noise * m->noiseBuf[i + framePos];
out *= 0.5;
// same time constant for both VCF and VCF RC circuits // same time constant for both VCF and VCF RC circuits
vcfRC = (cut - vcfRC) * m->vcaTC + vcfRC; vcfRC = (cut - vcfRC) * m->vcaTC + vcfRC;
for (uint8_t ovs = 0; ovs < 4; ovs++) { for (uint8_t ovs = 0; ovs < 2; ovs++) {
fb = b4; fb = y3;
// hard clip // hard clip
fb = ((out * 0.5) - fb) * m->res; fb = ((out * 0.5) - fb) * r;
if (fb > 4) fb = 4; if (fb > 6) fb = 6;
if (fb < -4) fb = -4; if (fb < -6) fb = -6;
// fb = 1.5 * fb - 0.5 * fb * fb * fb;
//
b1 = ((out + fb - b1) * vcfRC) + b1; y0 = ((out + fb - y0) * vcfRC) + y0;
b2 = ((b1 - b2) * vcfRC) + b2; y1 = ((y0 - y1) * vcfRC) + y1;
b3 = ((b2 - b3) * vcfRC) + b3; y2 = ((y1 - y2) * vcfRC) + y2;
b4 = ((b3 - b4) * vcfRC) + b4; y3 = ((y2 - y3) * vcfRC) + y3;
} }
vcaRC = (amp - vcaRC) * m->vcaTC + vcaRC; vcaRC = (amp - vcaRC) * m->vcaTC + vcaRC;
buffer[i] += 0.09367 * m->vcaBuf[i] * vcaRC * b4; buffer[framePos + i] += m->vcaBuf[i] * vcaRC * y3;
lastpw = m->pwmBuf[i]; lastpw = m->pwmBuf[i];
} }
// buffer[0] += 1; // buffer[0] += 1; // buzzing noise to test
} }

View File

@ -1,446 +0,0 @@
/*
Peacock-8 VA polysynth
Copyright 2025 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 "artwork.hpp"
static const char tempWhiteUp[] = {
"VOF\377ri^\377|rf\377xnb\377|rf\377yoc\377{qe\377xnb\377|rf\377zpd\377}s"
"g\377yoc\377xnb\377|rf\377{qe\377\177th\377xnb\377zpd\377xnb\377yoc\377|"
"rf\377{qe\377{qe\377{qe\377yoc\377xnb\377}sg\377xnb\377xnb\377{qe\377|rf"
"\377xnb\377vl`\377yoc\377\177th\377}sg\377{qe\377xnb\377uk_\377\003\003\003\000\003"
"\003\003\000\205{m\377\301\273\263\377\264\255\244\377\264\255\244\377\263\254"
"\243\377\264\255\244\377\264\255\244\377\263\253\242\377\263\254\243\377"
"\263\254\243\377\264\255\244\377\263\254\243\377\263\254\243\377\265\256"
"\245\377\266\257\246\377\264\255\244\377\263\253\242\377\264\255\244\377"
"\264\255\244\377\262\252\240\377\263\254\243\377\264\255\244\377\264\255"
"\244\377\265\256\245\377\264\255\244\377\262\252\240\377\264\255\244\377"
"\263\253\242\377\264\255\244\377\263\254\243\377\263\254\243\377\264\255"
"\244\377\264\255\244\377\264\255\244\377\262\252\240\377\264\255\244\377"
"\264\255\244\377\226\213~\377\214\201r\377uk_\062\003\003\003\000\202xj\377\266\257"
"\246\377\241\230\215\377\240\226\212\377\241\230\215\377\240\226\212\377"
"\243\232\217\377\241\230\215\377\240\226\212\377\241\230\215\377\244\233"
"\220\377\237\225\211\377\241\230\215\377\241\230\215\377\240\226\212\377"
"\237\225\211\377\241\230\215\377\235\223\207\377\241\227\214\377\240\226"
"\212\377\241\227\214\377\237\225\211\377\242\231\216\377\241\230\215\377"
"\240\226\212\377\237\225\211\377\241\230\215\377\241\230\215\377\241\230"
"\215\377\241\230\215\377\240\226\212\377\237\225\211\377\240\226\212\377"
"\241\230\215\377\243\232\217\377\240\226\212\377\241\227\214\377\233\221"
"\205\377\220\206x\377\\UL\374\062.)\226\202xj\377\263\253\242\377\233\221"
"\205\377\225\213}\377\225\213}\377\232\220\204\377\223\211{\377\223\211{"
"\377\234\222\206\377\222\210z\377\226\213~\377\226\213~\377\224\212|\377"
"\230\216\202\377\232\220\204\377\230\215\201\377\224\212|\377\230\215\201"
"\377\223\211{\377\232\220\204\377\226\213~\377\223\211{\377\227\214\177\377"
"\222\210z\377\232\220\204\377\233\221\205\377\231\217\203\377\220\206x\377"
"\234\222\206\377\224\212|\377\230\216\202\377\224\212|\377\224\212|\377\230"
"\216\202\377\224\212|\377\224\212|\377\233\221\205\377\230\215\201\377\202"
"xj\377TMD\377\062.)Q\200uh\377\262\252\240\377\224\212|\377\230\216\202\377"
"\217\205w\377\224\212|\377\226\213~\377\227\214\177\377\230\216\202\377\230"
"\216\202\377\223\211{\377\230\215\201\377\230\216\202\377\223\211{\377\230"
"\216\202\377\220\206x\377\225\213}\377\224\212|\377\224\212|\377\230\216"
"\202\377\223\211{\377\230\215\201\377\232\220\204\377\231\217\203\377\221"
"\207y\377\225\213}\377\230\216\202\377\223\211{\377\225\213}\377\227\214"
"\177\377\223\211{\377\230\216\202\377\227\214\177\377\227\214\177\377\225"
"\213}\377\222\210z\377\227\214\177\377\230\216\202\377}sg\377QKC\377*&\""
"\273\201vi\377\264\255\244\377\234\222\206\377\230\215\201\377\224\212|\377"
"\232\220\204\377\226\213~\377\227\214\177\377\231\217\203\377\222\210z\377"
"\223\211{\377\226\213~\377\230\216\202\377\217\205w\377\224\212|\377\232"
"\220\204\377\232\220\204\377\230\215\201\377\224\212|\377\225\213}\377\222"
"\210z\377\230\216\202\377\222\210z\377\230\216\202\377\227\214\177\377\230"
"\216\202\377\232\220\204\377\226\213~\377\226\213~\377\225\213}\377\221\207"
"y\377\230\216\202\377\226\213~\377\220\206x\377\221\207y\377\232\220\204"
"\377\224\212|\377\230\215\201\377\200uh\377OIA\377*&\"\350\202xj\377\263"
"\253\242\377\234\222\206\377\227\214\177\377\220\206x\377\224\212|\377\233"
"\221\205\377\230\215\201\377\230\215\201\377\231\217\203\377\233\221\205"
"\377\225\213}\377\224\212|\377\233\221\205\377\230\216\202\377\227\214\177"
"\377\230\215\201\377\223\211{\377\226\213~\377\226\213~\377\230\215\201\377"
"\232\220\204\377\231\217\203\377\230\215\201\377\227\214\177\377\227\214"
"\177\377\230\216\202\377\230\216\202\377\226\213~\377\233\221\205\377\226"
"\213~\377\226\213~\377\231\217\203\377\233\221\205\377\230\215\201\377\217"
"\205w\377\234\222\206\377\233\221\205\377}sg\377PJB\377*&\"\350\202xj\377"
"\263\254\243\377\237\225\211\377\226\213~\377\222\210z\377\227\214\177\377"
"\230\216\202\377\230\215\201\377\234\222\206\377\231\217\203\377\227\214"
"\177\377\222\210z\377\223\211{\377\222\210z\377\227\214\177\377\230\215\201"
"\377\226\213~\377\230\215\201\377\223\211{\377\224\212|\377\226\213~\377"
"\223\211{\377\226\213~\377\224\212|\377\222\210z\377\230\215\201\377\223"
"\211{\377\230\215\201\377\233\221\205\377\231\217\203\377\227\214\177\377"
"\220\206x\377\233\221\205\377\233\221\205\377\227\214\177\377\230\216\202"
"\377\233\221\205\377\222\210z\377\206|n\377OIA\377*&\"\350\202xj\377\263"
"\253\242\377\235\223\207\377\224\212|\377\221\207y\377\222\210z\377\224\212"
"|\377\220\206x\377\223\211{\377\232\220\204\377\231\217\203\377\230\215\201"
"\377\231\217\203\377\225\213}\377\220\206x\377\232\220\204\377\227\214\177"
"\377\220\206x\377\224\212|\377\223\211{\377\233\221\205\377\227\214\177\377"
"\224\212|\377\226\213~\377\226\213~\377\225\213}\377\227\214\177\377\230"
"\215\201\377\223\211{\377\232\220\204\377\225\213}\377\224\212|\377\230\215"
"\201\377\230\215\201\377\230\215\201\377\226\213~\377\230\215\201\377\230"
"\215\201\377\201vi\377TMD\377*&\"\350\203yk\377\263\253\242\377\232\220\204"
"\377\233\221\205\377\231\217\203\377\230\216\202\377\223\211{\377\230\215"
"\201\377\226\213~\377\224\212|\377\227\214\177\377\222\210z\377\230\215\201"
"\377\230\216\202\377\226\213~\377\224\212|\377\230\215\201\377\224\212|\377"
"\222\210z\377\230\215\201\377\224\212|\377\233\221\205\377\230\216\202\377"
"\224\212|\377\222\210z\377\230\216\202\377\234\222\206\377\226\213~\377\225"
"\213}\377\227\214\177\377\230\216\202\377\223\211{\377\227\214\177\377\226"
"\213~\377\232\220\204\377\232\220\204\377\224\212|\377\230\215\201\377\202"
"xj\377QKC\377*&\"\350\202xj\377\264\255\244\377\230\215\201\377\217\205w"
"\377\224\212|\377\224\212|\377\226\213~\377\230\216\202\377\226\213~\377"
"\223\211{\377\226\213~\377\227\214\177\377\232\220\204\377\226\213~\377\227"
"\214\177\377\223\211{\377\222\210z\377\232\220\204\377\226\213~\377\220\206"
"x\377\221\207y\377\222\210z\377\233\221\205\377\230\215\201\377\227\214\177"
"\377\222\210z\377\232\220\204\377\226\213~\377\234\222\206\377\222\210z\377"
"\231\217\203\377\234\222\206\377\230\215\201\377\230\216\202\377\222\210"
"z\377\226\213~\377\226\213~\377\227\214\177\377\201vi\377QKC\377*&\"\350"
"\202xj\377\263\253\242\377\224\212|\377\226\213~\377\232\220\204\377\224"
"\212|\377\226\213~\377\220\206x\377\230\216\202\377\221\207y\377\222\210"
"z\377\223\211{\377\227\214\177\377\231\217\203\377\230\215\201\377\223\211"
"{\377\232\220\204\377\224\212|\377\230\216\202\377\230\215\201\377\230\216"
"\202\377\223\211{\377\230\215\201\377\231\217\203\377\221\207y\377\223\211"
"{\377\225\213}\377\221\207y\377\222\210z\377\230\215\201\377\231\217\203"
"\377\226\213~\377\232\220\204\377\231\217\203\377\226\213~\377\233\221\205"
"\377\227\214\177\377\223\211{\377\212\177q\377QKC\377*&\"\350\201vi\377\262"
"\252\240\377\231\217\203\377\232\220\204\377\230\215\201\377\225\213}\377"
"\224\212|\377\230\216\202\377\226\213~\377\223\211{\377\226\213~\377\230"
"\215\201\377\226\213~\377\221\207y\377\217\205w\377\231\217\203\377\221\207"
"y\377\230\215\201\377\227\214\177\377\233\221\205\377\220\206x\377\224\212"
"|\377\227\214\177\377\224\212|\377\230\215\201\377\227\214\177\377\232\220"
"\204\377\224\212|\377\223\211{\377\230\215\201\377\223\211{\377\226\213~"
"\377\220\206x\377\227\214\177\377\226\213~\377\232\220\204\377\230\215\201"
"\377\230\215\201\377\205{m\377VOF\377*&\"\350\177th\377\260\250\236\377\224"
"\212|\377\224\212|\377\223\211{\377\222\210z\377\223\211{\377\223\211{\377"
"\232\220\204\377\227\214\177\377\226\213~\377\227\214\177\377\230\215\201"
"\377\230\215\201\377\227\214\177\377\227\214\177\377\233\221\205\377\230"
"\216\202\377\223\211{\377\222\210z\377\225\213}\377\223\211{\377\220\206"
"x\377\224\212|\377\222\210z\377\234\222\206\377\230\215\201\377\223\211{"
"\377\222\210z\377\234\222\206\377\223\211{\377\224\212|\377\230\215\201\377"
"\221\207y\377\225\213}\377\221\207y\377\223\211{\377\230\216\202\377\205"
"{m\377SLD\377*&\"\350\200uh\377\261\251\237\377\226\213~\377\224\212|\377"
"\224\212|\377\230\215\201\377\230\215\201\377\223\211{\377\230\215\201\377"
"\227\214\177\377\230\216\202\377\231\217\203\377\223\211{\377\234\222\206"
"\377\230\215\201\377\230\215\201\377\220\206x\377\230\216\202\377\224\212"
"|\377\227\214\177\377\222\210z\377\222\210z\377\230\215\201\377\226\213~"
"\377\230\215\201\377\230\215\201\377\223\211{\377\230\215\201\377\230\215"
"\201\377\233\221\205\377\234\222\206\377\230\216\202\377\230\216\202\377"
"\227\214\177\377\230\216\202\377\220\206x\377\223\211{\377\230\216\202\377"
"}sg\377SLD\377*&\"\350\177th\377\261\251\237\377\231\217\203\377\221\207"
"y\377\227\214\177\377\224\212|\377\225\213}\377\223\211{\377\232\220\204"
"\377\224\212|\377\231\217\203\377\232\220\204\377\230\216\202\377\230\215"
"\201\377\227\214\177\377\230\216\202\377\230\215\201\377\227\214\177\377"
"\225\213}\377\231\217\203\377\223\211{\377\222\210z\377\222\210z\377\224"
"\212|\377\224\212|\377\224\212|\377\230\215\201\377\230\215\201\377\227\214"
"\177\377\226\213~\377\230\215\201\377\225\213}\377\222\210z\377\230\215\201"
"\377\224\212|\377\223\211{\377\230\215\201\377\230\215\201\377\203yk\377"
"OIA\377*&\"\350\200uh\377\257\247\235\377\233\221\205\377\232\220\204\377"
"\222\210z\377\230\216\202\377\224\212|\377\230\215\201\377\226\213~\377\224"
"\212|\377\222\210z\377\230\215\201\377\230\216\202\377\227\214\177\377\230"
"\216\202\377\230\215\201\377\221\207y\377\223\211{\377\230\215\201\377\224"
"\212|\377\223\211{\377\227\214\177\377\230\215\201\377\230\215\201\377\227"
"\214\177\377\222\210z\377\227\214\177\377\230\215\201\377\224\212|\377\233"
"\221\205\377\226\213~\377\223\211{\377\230\216\202\377\230\215\201\377\232"
"\220\204\377\225\213}\377\233\221\205\377\223\211{\377\205{m\377QKC\377*"
"&\"\350\177th\377\262\252\240\377\225\213}\377\221\207y\377\230\215\201\377"
"\224\212|\377\231\217\203\377\231\217\203\377\224\212|\377\230\215\201\377"
"\231\217\203\377\224\212|\377\230\215\201\377\232\220\204\377\227\214\177"
"\377\230\216\202\377\230\215\201\377\227\214\177\377\231\217\203\377\227"
"\214\177\377\227\214\177\377\221\207y\377\232\220\204\377\230\215\201\377"
"\226\213~\377\223\211{\377\226\213~\377\227\214\177\377\227\214\177\377\226"
"\213~\377\227\214\177\377\230\216\202\377\225\213}\377\225\213}\377\223\211"
"{\377\232\220\204\377\227\214\177\377\223\211{\377\205{m\377SLD\377*&\"\350"
"\177th\377\261\251\237\377\230\216\202\377\226\213~\377\230\215\201\377\226"
"\213~\377\217\205w\377\226\213~\377\230\215\201\377\230\215\201\377\224\212"
"|\377\221\207y\377\232\220\204\377\230\215\201\377\223\211{\377\230\215\201"
"\377\231\217\203\377\230\216\202\377\231\217\203\377\227\214\177\377\223"
"\211{\377\227\214\177\377\230\215\201\377\226\213~\377\223\211{\377\230\216"
"\202\377\230\215\201\377\232\220\204\377\227\214\177\377\230\215\201\377"
"\224\212|\377\230\215\201\377\226\213~\377\231\217\203\377\225\213}\377\231"
"\217\203\377\222\210z\377\231\217\203\377\206|n\377SLD\377*&\"\350\177th"
"\377\264\255\244\377\233\221\205\377\227\214\177\377\231\217\203\377\222"
"\210z\377\224\212|\377\226\213~\377\226\213~\377\226\213~\377\230\215\201"
"\377\224\212|\377\230\216\202\377\233\221\205\377\226\213~\377\223\211{\377"
"\227\214\177\377\221\207y\377\232\220\204\377\226\213~\377\230\216\202\377"
"\230\215\201\377\226\213~\377\232\220\204\377\222\210z\377\223\211{\377\230"
"\215\201\377\224\212|\377\227\214\177\377\224\212|\377\227\214\177\377\227"
"\214\177\377\226\213~\377\224\212|\377\226\213~\377\227\214\177\377\230\215"
"\201\377\231\217\203\377\205{m\377TMD\377*&\"\350\201vi\377\263\253\242\377"
"\222\210z\377\223\211{\377\227\214\177\377\220\206x\377\230\215\201\377\226"
"\213~\377\234\222\206\377\226\213~\377\233\221\205\377\225\213}\377\227\214"
"\177\377\224\212|\377\224\212|\377\223\211{\377\233\221\205\377\227\214\177"
"\377\225\213}\377\223\211{\377\227\214\177\377\221\207y\377\223\211{\377"
"\230\215\201\377\230\216\202\377\230\215\201\377\227\214\177\377\217\205"
"w\377\226\213~\377\230\216\202\377\230\216\202\377\223\211{\377\226\213~"
"\377\221\207y\377\232\220\204\377\223\211{\377\231\217\203\377\230\216\202"
"\377\201vi\377SLD\377*&\"\350\177th\377\261\251\237\377\230\216\202\377\225"
"\213}\377\231\217\203\377\221\207y\377\232\220\204\377\230\215\201\377\224"
"\212|\377\234\222\206\377\224\212|\377\226\213~\377\230\216\202\377\223\211"
"{\377\230\215\201\377\220\206x\377\232\220\204\377\223\211{\377\225\213}"
"\377\224\212|\377\227\214\177\377\225\213}\377\231\217\203\377\223\211{\377"
"\234\222\206\377\223\211{\377\226\213~\377\230\215\201\377\230\216\202\377"
"\230\215\201\377\225\213}\377\224\212|\377\222\210z\377\223\211{\377\223"
"\211{\377\220\206x\377\226\213~\377\234\222\206\377\202xj\377PJB\377*&\""
"\350\200uh\377\261\251\237\377\234\222\206\377\230\215\201\377\230\215\201"
"\377\230\216\202\377\226\213~\377\226\213~\377\230\216\202\377\233\221\205"
"\377\232\220\204\377\231\217\203\377\227\214\177\377\227\214\177\377\226"
"\213~\377\223\211{\377\230\215\201\377\230\216\202\377\227\214\177\377\230"
"\216\202\377\232\220\204\377\231\217\203\377\232\220\204\377\220\206x\377"
"\234\222\206\377\221\207y\377\230\216\202\377\223\211{\377\221\207y\377\223"
"\211{\377\223\211{\377\231\217\203\377\220\206x\377\224\212|\377\230\215"
"\201\377\230\215\201\377\224\212|\377\232\220\204\377\202xj\377QKC\377*&"
"\"\350\202xj\377\261\251\237\377\226\213~\377\235\223\207\377\230\215\201"
"\377\230\216\202\377\223\211{\377\222\210z\377\230\215\201\377\222\210z\377"
"\230\215\201\377\231\217\203\377\223\211{\377\225\213}\377\223\211{\377\226"
"\213~\377\232\220\204\377\231\217\203\377\220\206x\377\230\216\202\377\221"
"\207y\377\226\213~\377\230\215\201\377\230\216\202\377\230\215\201\377\224"
"\212|\377\227\214\177\377\230\215\201\377\235\223\207\377\226\213~\377\226"
"\213~\377\231\217\203\377\227\214\177\377\220\206x\377\230\216\202\377\222"
"\210z\377\224\212|\377\233\221\205\377\201vi\377QKC\377*&\"\350\177th\377"
"\261\251\237\377\236\224\210\377\223\211{\377\230\215\201\377\230\215\201"
"\377\225\213}\377\227\214\177\377\225\213}\377\230\216\202\377\230\215\201"
"\377\223\211{\377\230\216\202\377\235\223\207\377\230\215\201\377\230\216"
"\202\377\230\216\202\377\231\217\203\377\223\211{\377\224\212|\377\226\213"
"~\377\230\215\201\377\232\220\204\377\230\215\201\377\230\216\202\377\226"
"\213~\377\222\210z\377\223\211{\377\231\217\203\377\232\220\204\377\230\216"
"\202\377\230\216\202\377\235\223\207\377\230\216\202\377\223\211{\377\223"
"\211{\377\230\216\202\377\231\217\203\377\203yk\377QKC\377*&\"\350\222\210"
"z\377\301\273\263\377\235\223\207\377\221\207y\377\233\221\205\377\232\220"
"\204\377\230\215\201\377\226\213~\377\230\215\201\377\233\221\205\377\223"
"\211{\377\224\212|\377\227\214\177\377\233\221\205\377\234\222\206\377\230"
"\215\201\377\232\220\204\377\232\220\204\377\227\214\177\377\230\216\202"
"\377\232\220\204\377\230\216\202\377\227\214\177\377\225\213}\377\233\221"
"\205\377\231\217\203\377\230\215\201\377\223\211{\377\233\221\205\377\230"
"\215\201\377\225\213}\377\230\215\201\377\230\215\201\377\230\216\202\377"
"\232\220\204\377\234\222\206\377\234\222\206\377\222\210z\377\204zl\377Q"
"KC\377*&\"\350WPG\377of[\377\177th\377tj_\377ri^\377tj_\377tj_\377of[\377"
"neZ\377jaV\377ri^\377of[\377lcX\377of[\377uk_\377\177th\377xnb\377xnb\377"
"{qe\377ri^\377of[\377of[\377tj_\377yoc\377zpd\377|rf\377yoc\377|rf\377zp"
"d\377}sg\377{qe\377|rf\377xnb\377yoc\377xnb\377\177th\377}sg\377uk_\377p"
"g\\\377SLD\377*&\"\350\237\225\211\025HB;\377@;\064\377PJB\377TMD\377QKC\377"
"PJB\377QKC\377PJB\377OIA\377OIA\377PJB\377OIA\377OIA\377OIA\377OIA\377OI"
"A\377OIA\377OIA\377MG?\377NH@\377NH@\377OIA\377NH@\377NH@\377MG?\377LF>\377"
"LF>\377MG?\377LF>\377MG?\377MG?\377MG?\377MG?\377MG?\377NH@\377MG?\377OI"
"A\377HB;\377=\070\062\377*&\"\350\003\003\003\000OIA\377\067\063-\377\066\062,\377C>\067"
"\377E@\071\377C>\067\377C>\067\377D?\070\377C>\067\377C>\067\377C>\067\377C>\067"
"\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377A<\065"
"\377B=\066\377B=\066\377C>\067\377B=\066\377B=\066\377A<\065\377A<\065\377A<\065"
"\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377B=\066"
"\377A<\065\377@;\064\377\060-(\377*&\"\362\003\003\003\000:\066\060\271\064\060*\377=\070"
"\062\377?:\063\377E@\071\377C>\067\377C>\067\377D?\070\377C>\067\377C>\067\377C>"
"\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>\067\377C>"
"\067\377A<\065\377B=\066\377B=\066\377C>\067\377B=\066\377B=\066\377A<\065\377A<"
"\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<\065\377A<"
"\065\377B=\066\377A<\065\377C>\067\377:\066\060\377*&\"\370",
};
static const char tempWhiteDn[] = {
"\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000"
"\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000"
"\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000"
"\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000"
"\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\003\003\003\000\060,'&\060,'\331\060,'\235\060,'\235"
"\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235"
"\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235"
"\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235"
"\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235\060,'\235"
"\060,'\235\060,'\235\003\003\003\000\003\003\003\000\003\003\003\000PIA\377LF>\377qh\\\377meZ\377q"
"h\\\377nfZ\377pg[\377ldY\377qh\\\377of[\377ri^\377nfZ\377meY\377qh\\\377"
"pg\\\377sj^\377meY\377of[\377ldY\377neZ\377qh\\\377pg\\\377pg[\377pg[\377"
"nfZ\377meY\377ri]\377meZ\377ldY\377pg[\377qh\\\377meY\377kcX\377neZ\377s"
"j^\377ri^\377pg[\377meY\377qh\\\360\003\003\003\000\003\003\003\000KF>\377?:\064\377f^S\377"
"mdY\377lcX\377mdY\377mdY\377lcX\377lcX\377lcX\377mdY\377lcX\377lcX\377ne"
"Z\377neZ\377mdY\377lcX\377mdY\377mdY\377kbX\377lcX\377mdY\377mdY\377neZ\377"
"mdY\377kbX\377mdY\377lcX\377mdY\377lcX\377lcX\377mdY\377mdY\377mdY\377kb"
"X\377mdY\377mdY\377XQH\375SMD\313\060,'+\060,'\000JD=\377g_T\377\223\210{\377"
"\222\207y\377\224\211|\377\222\207y\377\225\213~\377\224\211|\377\221\207"
"y\377\223\211{\377\226\214\177\377\221\206x\377\223\211{\377\224\211|\377"
"\222\207y\377\221\206x\377\224\211|\377\220\205w\377\223\210{\377\222\207"
"y\377\223\210{\377\221\206x\377\225\212}\377\224\211|\377\222\207y\377\221"
"\206x\377\224\211|\377\223\211{\377\224\211|\377\224\211|\377\222\207y\377"
"\221\206x\377\222\207y\377\224\211|\377\225\213~\377\222\207y\377\223\210"
"{\377\215\202t\377\202xj\360\060,'\036\060,'\000JD=\377sj^\377\227\215\200\377"
"\220\206x\377\220\206x\377\226\214\177\377\217\204v\377\217\204v\377\230"
"\216\201\377\216\204u\377\221\207y\377\221\207y\377\217\205w\377\224\212"
"|\377\226\214\177\377\223\211{\377\220\205w\377\223\211{\377\217\204v\377"
"\226\214\177\377\221\207y\377\217\204v\377\222\210z\377\216\203u\377\226"
"\214\177\377\227\215\200\377\225\213}\377\214\201s\377\230\216\201\377\220"
"\205w\377\224\212|\377\220\205w\377\220\205w\377\224\212|\377\220\205w\377"
"\220\205w\377\227\215\200\377\223\211{\377~tg\377)&!\262\003\003\003\000JD<\377s"
"j^\377\217\205w\377\224\212}\377\213\200r\377\217\205w\377\221\207y\377\222"
"\210z\377\224\212}\377\224\212|\377\217\204v\377\223\211{\377\224\212|\377"
"\217\204v\377\224\212|\377\214\201s\377\221\206y\377\220\205w\377\217\205"
"w\377\224\212}\377\217\204v\377\223\211{\377\226\214\177\377\225\213}\377"
"\215\202t\377\220\206x\377\224\212|\377\217\204v\377\220\206x\377\222\210"
"z\377\217\204v\377\224\212|\377\222\210z\377\222\210z\377\220\206x\377\216"
"\203u\377\222\210z\377\224\212}\377zpc\377)&!\262\003\003\003\000JD<\377ul`\377\230"
"\216\202\377\223\211{\377\217\205w\377\226\214\177\377\221\207y\377\222\210"
"z\377\225\213~\377\216\203u\377\217\204v\377\221\207y\377\224\212|\377\213"
"\200r\377\217\205w\377\226\214\177\377\226\214\177\377\223\211{\377\217\205"
"w\377\220\206x\377\216\203u\377\224\212}\377\216\204u\377\224\212|\377\222"
"\210z\377\224\212}\377\226\214\177\377\221\207y\377\221\207y\377\221\206"
"y\377\215\202t\377\224\212}\377\221\207y\377\214\201s\377\215\202t\377\226"
"\214\177\377\217\205w\377\223\211{\377|re\377)&!\262\003\003\003\000JD=\377sj^\377"
"\230\216\201\377\222\210z\377\214\201s\377\217\205w\377\227\215\200\377\223"
"\211{\377\223\211{\377\225\213~\377\227\215\200\377\220\206x\377\217\205"
"w\377\227\215\200\377\224\212|\377\222\210z\377\223\211{\377\217\204v\377"
"\221\207y\377\221\207y\377\223\211{\377\226\214\177\377\225\213~\377\223"
"\211{\377\222\210z\377\222\210z\377\224\212|\377\224\212}\377\221\207y\377"
"\227\215\200\377\221\207y\377\221\207y\377\225\213~\377\227\215\200\377\223"
"\211{\377\213\200r\377\230\216\201\377\227\215\200\377ypc\377)&!\262\003\003"
"\003\000JD=\377tk_\377\233\221\205\377\221\207y\377\216\204u\377\222\210z\377"
"\224\212|\377\223\211{\377\230\216\201\377\225\213~\377\222\210z\377\216"
"\204u\377\217\204v\377\216\203u\377\222\210z\377\223\211{\377\221\207y\377"
"\223\211{\377\217\204v\377\220\205w\377\221\207y\377\217\204v\377\221\207"
"y\377\220\205w\377\216\203u\377\223\211{\377\217\204v\377\223\211{\377\227"
"\215\200\377\225\213~\377\222\210z\377\214\201s\377\227\215\200\377\227\215"
"\200\377\222\210z\377\224\212}\377\227\215\200\377\216\203u\377\201wj\377"
")&!\262\003\003\003\000JD=\377sj^\377\231\217\203\377\220\205w\377\215\202t\377\216"
"\204u\377\220\205w\377\214\201s\377\217\204v\377\226\214\177\377\225\213"
"~\377\223\211{\377\225\213~\377\220\206x\377\214\201s\377\226\214\177\377"
"\222\210z\377\214\201s\377\217\205w\377\217\204v\377\227\215\200\377\222"
"\210z\377\220\205w\377\221\207y\377\221\207y\377\220\206x\377\222\210z\377"
"\223\211{\377\217\204v\377\226\214\177\377\221\206y\377\217\205w\377\223"
"\211{\377\223\211{\377\223\211{\377\221\207y\377\223\211{\377\223\211{\377"
"}sf\377)&!\262\003\003\003\000JD=\377sj^\377\226\214\177\377\227\215\200\377\225"
"\213~\377\224\212}\377\217\204v\377\223\211{\377\221\207y\377\217\205w\377"
"\222\210z\377\216\203u\377\223\211{\377\224\212}\377\221\207y\377\217\205"
"w\377\223\211{\377\217\205w\377\216\204u\377\223\211{\377\217\205w\377\227"
"\215\200\377\224\212}\377\217\205w\377\216\203u\377\224\212}\377\230\216"
"\201\377\221\207y\377\220\206x\377\222\210z\377\224\212|\377\217\204v\377"
"\222\210z\377\221\207y\377\226\214\177\377\226\214\177\377\220\205w\377\223"
"\211{\377~tg\377)&!\262\003\003\003\000JD=\377ul`\377\223\211{\377\213\200r\377\217"
"\205w\377\217\205w\377\221\207y\377\224\212}\377\221\207y\377\217\204v\377"
"\221\207y\377\222\210z\377\226\214\177\377\221\207y\377\222\210z\377\217"
"\204v\377\216\203u\377\226\214\177\377\221\207y\377\214\201s\377\215\202"
"t\377\216\203u\377\227\215\200\377\223\211{\377\222\210z\377\216\204u\377"
"\226\214\177\377\221\207y\377\230\216\202\377\216\204u\377\225\213~\377\230"
"\216\201\377\223\211{\377\224\212}\377\216\203u\377\221\207y\377\221\207"
"y\377\222\210z\377}sf\377)&!\262\003\003\003\000JD=\377sj^\377\220\205w\377\221\207"
"y\377\226\214\177\377\220\205w\377\221\207y\377\214\201s\377\224\212}\377"
"\215\202t\377\216\204u\377\217\204v\377\222\210z\377\225\213~\377\223\211"
"{\377\217\204v\377\226\214\177\377\217\205w\377\224\212|\377\223\211{\377"
"\224\212}\377\217\204v\377\223\211{\377\225\213~\377\215\202t\377\217\204"
"v\377\220\206x\377\215\202t\377\216\203u\377\223\211{\377\225\213~\377\221"
"\207y\377\226\214\177\377\225\213~\377\221\207y\377\227\215\200\377\222\210"
"z\377\217\204v\377\205{m\377)&!\262\003\003\003\000JD<\377sj^\377\225\213~\377\226"
"\214\177\377\223\211{\377\220\206x\377\217\205w\377\224\212|\377\221\207"
"y\377\217\204v\377\221\207y\377\223\211{\377\221\207y\377\215\202t\377\213"
"\200r\377\225\213~\377\215\202t\377\223\211{\377\222\210z\377\227\215\200"
"\377\214\201s\377\217\205w\377\222\210z\377\217\205w\377\223\211{\377\222"
"\210z\377\226\214\177\377\220\205w\377\217\204v\377\223\211{\377\217\204"
"v\377\221\207y\377\214\201s\377\222\210z\377\221\207y\377\226\214\177\377"
"\223\211{\377\223\211{\377\201vi\377)&!\262\003\003\003\000JD<\377qh\\\377\220\205"
"w\377\220\205w\377\217\204v\377\216\203u\377\217\204v\377\217\204v\377\226"
"\214\177\377\222\210z\377\221\207y\377\222\210z\377\223\211{\377\223\211"
"{\377\222\210z\377\222\210z\377\227\215\200\377\224\212}\377\217\204v\377"
"\216\204u\377\220\206x\377\217\204v\377\214\201s\377\220\205w\377\216\203"
"u\377\230\216\201\377\223\211{\377\217\204v\377\216\204u\377\230\216\201"
"\377\217\204v\377\217\205w\377\223\211{\377\215\202t\377\221\206y\377\215"
"\202t\377\217\204v\377\224\212|\377\200vi\377)&!\262\003\003\003\000JD<\377ri]\377"
"\221\207y\377\217\205w\377\217\205w\377\223\211{\377\223\211{\377\217\204"
"v\377\223\211{\377\222\210z\377\224\212}\377\225\213~\377\217\204v\377\230"
"\216\201\377\223\211{\377\223\211{\377\214\201s\377\224\212|\377\220\205"
"w\377\222\210z\377\216\204u\377\216\203u\377\223\211{\377\221\207y\377\223"
"\211{\377\223\211{\377\217\204v\377\223\211{\377\223\211{\377\227\215\200"
"\377\230\216\201\377\224\212}\377\224\212|\377\222\210z\377\224\212}\377"
"\214\201s\377\217\204v\377\224\212}\377zpc\377)&!\262\003\003\003\000JD<\377ri]\377"
"\225\213~\377\215\202t\377\222\210z\377\217\205w\377\220\206x\377\217\204"
"v\377\226\214\177\377\220\205w\377\225\213~\377\226\214\177\377\224\212}"
"\377\223\211{\377\222\210z\377\224\212|\377\223\211{\377\222\210z\377\220"
"\206x\377\225\213~\377\217\204v\377\216\204u\377\216\203u\377\220\205w\377"
"\217\205w\377\220\205x\377\223\211{\377\223\211{\377\222\210z\377\221\207"
"y\377\223\211{\377\220\206x\377\216\204u\377\223\211{\377\217\205w\377\217"
"\204v\377\223\211{\377\223\211{\377~tg\377)&!\262\003\003\003\000JD<\377pg\\\377"
"\227\215\200\377\226\214\177\377\216\204u\377\224\212}\377\220\205w\377\223"
"\211{\377\221\207y\377\217\205w\377\216\204u\377\223\211{\377\224\212}\377"
"\222\210z\377\224\212}\377\223\211{\377\215\202t\377\217\204v\377\223\211"
"{\377\220\205x\377\217\204v\377\222\210z\377\223\211{\377\223\211{\377\222"
"\210z\377\216\204u\377\222\210z\377\223\211{\377\220\205w\377\227\215\200"
"\377\221\207y\377\217\204v\377\224\212|\377\223\211{\377\226\214\177\377"
"\221\206y\377\227\215\200\377\217\204v\377\201vi\377)&!\262\003\003\003\000JD<\377"
"sj^\377\220\206x\377\215\202t\377\223\211{\377\217\205w\377\225\213~\377"
"\225\213~\377\220\205w\377\223\211{\377\225\213~\377\217\205w\377\223\211"
"{\377\226\214\177\377\222\210z\377\224\212}\377\223\211{\377\222\210z\377"
"\225\213~\377\222\210z\377\222\210z\377\216\203t\377\226\214\177\377\223"
"\211{\377\221\207y\377\217\204v\377\221\207y\377\222\210z\377\222\210z\377"
"\221\207y\377\222\210z\377\224\212|\377\220\206x\377\220\206x\377\217\204"
"v\377\226\214\177\377\222\210z\377\217\204v\377\200vi\377)&!\262\003\003\003\000"
"JD<\377ri]\377\224\212|\377\221\207y\377\223\211{\377\221\207y\377\213\200"
"r\377\221\207y\377\223\211{\377\223\211{\377\220\205w\377\215\202t\377\226"
"\214\177\377\223\211{\377\217\204v\377\223\211{\377\225\213~\377\224\212"
"|\377\225\213~\377\222\210z\377\217\204v\377\222\210z\377\223\211{\377\221"
"\207y\377\217\204v\377\224\212|\377\223\211{\377\226\214\177\377\222\210"
"z\377\223\211{\377\217\205w\377\223\211{\377\221\207y\377\225\213~\377\220"
"\206x\377\225\213~\377\216\204u\377\225\213~\377\202wj\377)&!\262\003\003\003\000"
"JD<\377uk`\377\227\215\200\377\222\210z\377\225\213~\377\216\204u\377\220"
"\205w\377\221\207y\377\221\207y\377\221\207y\377\223\211{\377\220\205w\377"
"\224\212|\377\227\215\200\377\221\207y\377\217\204v\377\222\210z\377\215"
"\202t\377\226\214\177\377\221\207y\377\224\212|\377\223\211{\377\221\207"
"y\377\226\214\177\377\216\203u\377\217\204v\377\223\211{\377\220\205w\377"
"\222\210z\377\220\205w\377\222\210z\377\222\210z\377\221\207y\377\217\205"
"w\377\221\207y\377\222\210z\377\223\211{\377\225\213}\377\200vi\377)&!\262"
"\003\003\003\000JD<\377sj^\377\216\204u\377\217\204v\377\222\210z\377\214\201s\377"
"\223\211{\377\221\207y\377\230\216\202\377\221\207y\377\227\215\200\377\220"
"\206x\377\222\210z\377\217\205w\377\220\205w\377\217\204v\377\227\215\200"
"\377\222\210z\377\220\206x\377\217\204v\377\222\210z\377\215\202t\377\217"
"\204v\377\223\211{\377\224\212|\377\223\211{\377\222\210z\377\213\200r\377"
"\221\207y\377\224\212|\377\224\212|\377\217\204v\377\221\207y\377\215\202"
"t\377\226\214\177\377\217\204v\377\225\213~\377\224\212}\377|re\377)&!\262"
"\003\003\003\000JD<\377ri]\377\224\212|\377\221\206y\377\225\213~\377\215\202t\377"
"\226\214\177\377\223\211{\377\217\205w\377\230\216\201\377\220\205w\377\221"
"\207y\377\224\212}\377\217\204v\377\223\211{\377\214\201s\377\226\214\177"
"\377\217\204v\377\220\206x\377\220\205w\377\222\210z\377\220\206x\377\225"
"\213~\377\217\204v\377\230\216\201\377\217\204v\377\221\207y\377\223\211"
"{\377\224\212}\377\223\211{\377\220\206x\377\220\205w\377\216\203u\377\217"
"\204v\377\217\204v\377\214\201s\377\221\207y\377\230\216\201\377~tg\377)"
"&!\262\003\003\003\000JD<\377ri]\377\230\216\201\377\223\211{\377\223\211{\377\224"
"\212}\377\221\207y\377\221\207y\377\224\212}\377\227\215\200\377\226\214"
"\177\377\225\213~\377\222\210z\377\222\210z\377\221\207y\377\217\204v\377"
"\223\211{\377\224\212}\377\222\210z\377\224\212|\377\226\214\177\377\225"
"\213~\377\226\214\177\377\214\201s\377\230\216\201\377\215\202t\377\224\212"
"|\377\217\204v\377\215\202t\377\217\204v\377\217\204v\377\225\213~\377\214"
"\201s\377\220\205w\377\223\211{\377\223\211{\377\220\205w\377\226\214\177"
"\377~tg\377)&!\262\003\003\003\000JD=\377ri]\377\221\207y\377\231\217\202\377\223"
"\211{\377\224\212|\377\217\204v\377\216\203u\377\223\211{\377\216\203u\377"
"\223\211{\377\225\213~\377\217\204v\377\220\206x\377\217\204v\377\221\207"
"y\377\226\214\177\377\225\213~\377\214\201s\377\224\212|\377\215\202t\377"
"\221\207y\377\223\211{\377\224\212}\377\223\211{\377\220\205w\377\222\210"
"z\377\223\211{\377\231\217\202\377\221\207y\377\221\207y\377\225\213~\377"
"\222\210z\377\214\201s\377\224\212|\377\216\203u\377\217\205w\377\227\215"
"\200\377}sf\377)&!\262\003\003\003\000JD<\377^WM\377\232\221\204\377\217\204v\377"
"\223\211{\377\223\211{\377\220\206x\377\222\210z\377\221\206y\377\224\212"
"}\377\223\211{\377\217\204v\377\224\212|\377\231\217\202\377\223\211{\377"
"\224\212}\377\224\212}\377\225\213}\377\217\204v\377\220\205w\377\221\207"
"y\377\223\211{\377\226\214\177\377\223\211{\377\224\212}\377\221\207y\377"
"\216\203u\377\217\204v\377\225\213}\377\226\214\177\377\224\212}\377\224"
"\212}\377\231\217\202\377\224\212|\377\217\204v\377\217\204v\377\224\212"
"|\377\225\213}\377~tg\377)&!\262\003\003\003\000<\067\061\377\060,'\377\231\217\203"
"\377\215\202t\377\227\215\200\377\226\214\177\377\223\211{\377\221\207y\377"
"\223\211{\377\227\215\200\377\217\204v\377\217\205w\377\222\210z\377\227"
"\215\200\377\230\216\201\377\223\211{\377\226\214\177\377\226\214\177\377"
"\222\210z\377\224\212}\377\226\214\177\377\224\212}\377\222\210z\377\221"
"\206y\377\227\215\200\377\225\213~\377\223\211{\377\217\204v\377\227\215"
"\200\377\223\211{\377\220\206x\377\223\211{\377\223\211{\377\224\212|\377"
"\226\214\177\377\230\216\201\377\230\216\201\377\216\203u\377\177uh\377)"
"&!\262\003\003\003\000PJA\377NG@\377{qd\377pg[\377of[\377pg[\377pg\\\377kcX\377j"
"bW\377f^T\377of[\377kcX\377i`V\377kcX\377qh\\\377{qd\377uk_\377sj^\377wm"
"a\377of[\377lcX\377kcX\377pg[\377vl`\377vl`\377xob\377ul`\377xnb\377vl`\377"
"ypc\377wna\377xob\377uk_\377ul`\377uk_\377zqd\377ypc\377qh\\\377ldY\377)"
"&!\262\003\003\003\000D?\070\177\066\062,\377=\071\062\377NG?\377PJB\377NH@\377NG@\377"
"OIA\377NG?\377MF?\377MG?\377NG?\377MG?\377MG?\377MG?\377MF?\377MF?\377MG"
"?\377MG?\377JD=\377KF>\377KF>\377MG?\377LF>\377KF>\377JD=\377JD<\377JD<\377"
"JD=\377JD<\377JD=\377JD=\377JD=\377JD=\377JD=\377KF>\377JD=\377MG?\377E@"
"\071\377)&!\324\003\003\003\000",
};
const char* Artwork::whiteBtnUp = (const char*)tempWhiteUp;
const char* Artwork::whiteBtnDn = (const char*)tempWhiteDn;