Compare commits
No commits in common. "1c3c6adf3c8fb3795f83aea4008078cf5817d8c5" and "202ddac184febe971aa8445da2fabc389df1d64c" have entirely different histories.
1c3c6adf3c
...
202ddac184
1
Makefile
1
Makefile
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
include dpf/Makefile.base.mk
|
include dpf/Makefile.base.mk
|
||||||
|
|
||||||
CFLAGS += -ggdb
|
|
||||||
all: plugins gen
|
all: plugins gen
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
|
@ -12,10 +12,9 @@ NAME = peacock
|
|||||||
FILES_DSP = \
|
FILES_DSP = \
|
||||||
peacock.cpp \
|
peacock.cpp \
|
||||||
controls.cpp \
|
controls.cpp \
|
||||||
assigner.cpp \
|
ic1.cpp \
|
||||||
oscillator.cpp \
|
oscillator.cpp \
|
||||||
filter.cpp \
|
ic29.cpp
|
||||||
voiceboard.cpp
|
|
||||||
|
|
||||||
include ../dpf/Makefile.plugins.mk
|
include ../dpf/Makefile.plugins.mk
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "assigner.hpp"
|
#include "ic29.hpp"
|
||||||
#include "peacock.hpp"
|
#include "peacock.hpp"
|
||||||
|
|
||||||
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
void Peacock::initParameter(uint32_t index, Parameter& parameter) {
|
||||||
@ -316,7 +316,6 @@ void Peacock::setParameterValue(uint32_t index, float value) {
|
|||||||
if (value < 0.0f) value = 0.0f;
|
if (value < 0.0f) value = 0.0f;
|
||||||
if (value > 127.0f) value = 127.0f;
|
if (value > 127.0f) value = 127.0f;
|
||||||
|
|
||||||
#if 0
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case p_lfoRate:
|
case p_lfoRate:
|
||||||
ic29.patchRam.lfoRate = value;
|
ic29.patchRam.lfoRate = value;
|
||||||
@ -410,8 +409,6 @@ void Peacock::setParameterValue(uint32_t index, float value) {
|
|||||||
break;
|
break;
|
||||||
case p_vcoBend:
|
case p_vcoBend:
|
||||||
ic29.vcoBend = (uint8_t)value;
|
ic29.vcoBend = (uint8_t)value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
Peacock-8 VA polysynth
|
|
||||||
|
|
||||||
Copyright 2024 Gordon JC Pearce <gordonjcp@gjcp.net>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "voiceboard.hpp"
|
|
||||||
|
|
||||||
void Voice::filter(float *buffer, uint32_t pos, uint32_t frames) {
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
for (uint8_t ovs = 0; ovs < 4; ovs++) {
|
|
||||||
fb = b4;
|
|
||||||
// hard clip
|
|
||||||
if (fb > 1) fb = 1;
|
|
||||||
if (fb < -1) fb = -1;
|
|
||||||
|
|
||||||
fb = buffer[i+pos] - (fb * reso);
|
|
||||||
b1 = ((fb - b1) * cut) + b1;
|
|
||||||
b2 = ((b1 - b2) * cut) + b2;
|
|
||||||
b3 = ((b2 - b3) * cut) + b3;
|
|
||||||
b4 = ((b3 - b4) * cut) + b4;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[i+pos] = b4;
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,13 +16,14 @@
|
|||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "assigner.hpp"
|
#include "ic1.hpp"
|
||||||
|
|
||||||
#include "voiceboard.hpp"
|
#include "ic29.hpp"
|
||||||
#include "peacock.hpp"
|
#include "peacock.hpp"
|
||||||
|
|
||||||
|
Assigner ic1;
|
||||||
|
|
||||||
void Assigner::printMap() {
|
void Assigner::printMap() {
|
||||||
return;
|
|
||||||
printf("note memory:\n");
|
printf("note memory:\n");
|
||||||
for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]);
|
for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -34,7 +35,7 @@ Assigner::Assigner() {
|
|||||||
// set up the assigner
|
// set up the assigner
|
||||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||||
voicemap[i] = 0x80 + i;
|
voicemap[i] = 0x80 + i;
|
||||||
keymap[i] = 0xff;
|
keymap[i] = 0x3c | 0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,14 +52,14 @@ void Assigner::handleMidi(const MidiEvent *ev) {
|
|||||||
case 0xb0:
|
case 0xb0:
|
||||||
switch (ev->data[1]) {
|
switch (ev->data[1]) {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
_ic29->modWheel = ev->data[2];
|
ic29.modWheel = ev->data[2];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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 0xe0: // pitch bend;
|
case 0xe0: // pitch bend;
|
||||||
_ic29->pitchBend = ev->data[2] << 7 | ev->data[1];
|
ic29.pitchBend = ev->data[2] << 7 | ev->data[1];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
|
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
|
||||||
@ -74,8 +75,7 @@ void Assigner::noteOff(uint8_t note) {
|
|||||||
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
|
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
|
||||||
voicemap[NUM_VOICES - 1] = voice;
|
voicemap[NUM_VOICES - 1] = voice;
|
||||||
keymap[NUM_VOICES - 1] = note | 0x80;
|
keymap[NUM_VOICES - 1] = note | 0x80;
|
||||||
_ic29->voiceOff(voice);
|
ic29.voiceOff(voice);
|
||||||
printMap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,8 +91,7 @@ void Assigner::noteOn(uint8_t note) {
|
|||||||
memmove(keymap + 1, keymap, i);
|
memmove(keymap + 1, keymap, i);
|
||||||
keymap[0] = note; // show note as on
|
keymap[0] = note; // show note as on
|
||||||
voicemap[0] = voice;
|
voicemap[0] = voice;
|
||||||
_ic29->voiceOn(voice, note);
|
ic29.voiceOn(voice, note);
|
||||||
printMap();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,8 +105,7 @@ void Assigner::noteOn(uint8_t note) {
|
|||||||
memmove(keymap + 1, keymap, i);
|
memmove(keymap + 1, keymap, i);
|
||||||
keymap[0] = note; // show note as on
|
keymap[0] = note; // show note as on
|
||||||
voicemap[0] = voice;
|
voicemap[0] = voice;
|
||||||
_ic29->voiceOn(voice, note);
|
ic29.voiceOn(voice, note);
|
||||||
printMap();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,27 +18,20 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "peacock.hpp"
|
||||||
#define NUM_VOICES 8
|
|
||||||
|
|
||||||
#include "voiceboard.hpp"
|
|
||||||
|
|
||||||
class Assigner {
|
class Assigner {
|
||||||
public:
|
public:
|
||||||
Assigner();
|
Assigner();
|
||||||
void setVoiceBoard(Synth& s) {
|
|
||||||
_ic29 = &s;
|
|
||||||
}
|
|
||||||
void handleMidi(const MidiEvent *ev);
|
void handleMidi(const MidiEvent *ev);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void noteOn(uint8_t note);
|
void noteOn(uint8_t note);
|
||||||
void noteOff(uint8_t note);
|
void noteOff(uint8_t note);
|
||||||
void printMap();
|
void printMap();
|
||||||
|
|
||||||
Synth* _ic29;
|
|
||||||
|
|
||||||
uint8_t keymap[NUM_VOICES];
|
uint8_t keymap[NUM_VOICES];
|
||||||
uint8_t voicemap[NUM_VOICES];
|
uint8_t voicemap[NUM_VOICES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern Assigner ic1;
|
@ -16,16 +16,16 @@
|
|||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "voiceboard.hpp"
|
#include "ic29.hpp"
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "DistrhoPlugin.hpp"
|
|
||||||
#include "ic29tables.hpp"
|
#include "ic29tables.hpp"
|
||||||
|
|
||||||
#define DEBUG
|
Synth ic29;
|
||||||
|
|
||||||
Synth::Synth() {
|
Synth::Synth() {
|
||||||
|
d_debug("initialising synth\n");
|
||||||
|
portaCoeff = 0x0;
|
||||||
|
noise = new float [4096];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::buildTables(double sampleRate) {
|
void Synth::buildTables(double sampleRate) {
|
||||||
@ -35,23 +35,16 @@ void Synth::buildTables(double sampleRate) {
|
|||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// precompute a table of values to map the filter value to a filter coefficient
|
|
||||||
// the ROM preset for adjusting the VCF scale and centre presets cutoff to $31
|
|
||||||
// and key scale to $7f, which corresponds to C4 = 248Hz and C6 = 992Hz, B3 and B5
|
|
||||||
|
|
||||||
for (uint16_t i = 0; i < 256; i++) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::run() {
|
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
|
||||||
|
|
||||||
lfo.run();
|
ic29.lfo.run();
|
||||||
|
|
||||||
masterPitch = 0x1818;
|
masterPitch = 0x1818;
|
||||||
uint16_t vcoLfoDepth = lfoDepthTable[patchRam.vcoLfoMod] + modWheel;
|
uint16_t vcoLfoDepth = ic29.lfoDepthTable[ic29.patchRam.vcoLfoMod] + ic29.modWheel;
|
||||||
vcoLfoDepth = (vcoLfoDepth < 0xff) ? vcoLfoDepth : 0xff;
|
vcoLfoDepth = (vcoLfoDepth < 0xff) ? vcoLfoDepth : 0xff;
|
||||||
masterPitch += (lfo.lfoOut * vcoLfoDepth) >> 11;
|
masterPitch += (lfo.lfoOut * vcoLfoDepth) >> 11;
|
||||||
|
|
||||||
@ -63,36 +56,31 @@ void Synth::run() {
|
|||||||
|
|
||||||
// PWM is bit 0 sw2, 0 = fixed 1 = lfo
|
// PWM is bit 0 sw2, 0 = fixed 1 = lfo
|
||||||
// 0 sets EA to 0x3fff, 1 adds
|
// 0 sets EA to 0x3fff, 1 adds
|
||||||
uint16_t pwmVal = 0x2000 - lfo.lfoOut;
|
uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
|
||||||
if (patchRam.switch2 & 0x01) pwmVal = 0x3fff;
|
if (ic29.patchRam.switch2 & 0x01) pwmVal = 0x3fff;
|
||||||
pwm = 0.5 - pwmVal / 32768.0f * (patchRam.pwmLfoMod / 106.0f);
|
ic29.pwm = 0.5 - pwmVal / 32768.0f * (ic29.patchRam.pwmLfoMod / 106.0f);
|
||||||
|
|
||||||
// generate the voices, then
|
// generate the voices, then
|
||||||
for (uint32_t i = 0; i < bufferSize; i++) {
|
for (uint32_t i=0; i<bufferSize; i++) {
|
||||||
tr21 = (tr21 * 519) + 3;
|
tr21 = (tr21*519) + 3;
|
||||||
// noise[i] = (1 - (tr21 & 0x00ffffff) / 8388608.0f) * (patchRam.noiseLevel * 0.0063);
|
noise[i] = (1-(tr21 & 0x00ffffff) / 8388608.0f) * (ic29.patchRam.noiseLevel * 0.0063);
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("updating\n");
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||||
voices[i].update();
|
ic29.voices[i].update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::voiceOn(uint8_t voice, uint8_t note) {
|
void Synth::voiceOn(uint8_t voice, uint8_t note) {
|
||||||
// enable synth voice, start it all running
|
// enable synth voice, start it all running
|
||||||
voice &= 0x7f;
|
voice &= 0x7f;
|
||||||
|
ic29.voices[voice].on(note);
|
||||||
d_debug("voiceOn %02x %02x", voice, note);
|
|
||||||
|
|
||||||
voices[voice].on(note);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::voiceOff(uint8_t voice) {
|
void Synth::voiceOff(uint8_t voice) {
|
||||||
// enable synth voice, start it all running
|
// enable synth voice, start it all running
|
||||||
voice &= 0x7f;
|
voice &= 0x7f;
|
||||||
voices[voice].off();
|
ic29.voices[voice].off();
|
||||||
}
|
}
|
||||||
|
|
||||||
LFO::LFO() {
|
LFO::LFO() {
|
||||||
@ -123,35 +111,30 @@ void LFO::run() {
|
|||||||
// printf("lfoOut=%04x\n", lfoOut);
|
// printf("lfoOut=%04x\n", lfoOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Envelope::atk = 0x10;
|
|
||||||
uint16_t Envelope::dcy = 0x3f;
|
|
||||||
uint16_t Envelope::stn = 0x3f;
|
|
||||||
uint16_t Envelope::rls = 0x1f;
|
|
||||||
|
|
||||||
Envelope::Envelope() {
|
Envelope::Envelope() {
|
||||||
level = 0;
|
level = 0;
|
||||||
phase = ENV_RLS;
|
phase = ENV_RLS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Envelope::run() {
|
void Envelope::run() {
|
||||||
uint16_t temp = stn << 7;
|
uint16_t tempStn = ic29.patchRam.sustain << 7;
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case ENV_ATK:
|
case ENV_ATK:
|
||||||
level += atkTable[atk];
|
level += atkTable[ic29.patchRam.attack];
|
||||||
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 > temp) {
|
if (level > tempStn) {
|
||||||
level = (((level - temp) * dcyTable[dcy]) >> 16) + temp;
|
level = (((level - tempStn) * dcyTable[ic29.patchRam.decay]) >> 16) + tempStn;
|
||||||
} else {
|
} else {
|
||||||
level = temp;
|
level = tempStn;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ENV_RLS:
|
case ENV_RLS:
|
||||||
level = (level * dcyTable[rls]) >> 16;
|
level = (level * dcyTable[ic29.patchRam.release]) >> 16;
|
||||||
break;
|
break;
|
||||||
case ENV_IDLE:
|
case ENV_IDLE:
|
||||||
default:
|
default:
|
||||||
@ -160,6 +143,7 @@ void Envelope::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Voice::Voice() {
|
Voice::Voice() {
|
||||||
|
subosc = .1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::calcPitch() {
|
void Voice::calcPitch() {
|
||||||
@ -171,68 +155,66 @@ void Voice::calcPitch() {
|
|||||||
// This is implemented here by adding on a step value until you pass
|
// 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
|
// the desired final pitch. Once that happens the value is clamped to the
|
||||||
// desired pitch.
|
// desired pitch.
|
||||||
/*
|
|
||||||
if (ic29.portaCoeff != 0) {
|
if (ic29.portaCoeff != 0) {
|
||||||
// portamento 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;
|
||||||
}
|
|
||||||
// portamento down
|
|
||||||
if (pitch > target) {
|
|
||||||
pitch -= ic29.portaCoeff;
|
|
||||||
if (pitch < target) pitch = target;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pitch = target;
|
|
||||||
}
|
}
|
||||||
|
// portamento down
|
||||||
|
if (pitch > target) {
|
||||||
|
pitch -= ic29.portaCoeff;
|
||||||
|
if (pitch < target) pitch = target;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pitch = target;
|
||||||
|
}
|
||||||
|
|
||||||
pitch += 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;
|
||||||
|
|
||||||
// interpolate between the two table values
|
// 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) / 256.0f;
|
double frac = (pitch & 0xff) / 256.0f;
|
||||||
|
|
||||||
omega = ((o2 - o1) * frac) + o1;
|
omega = ((o2 - o1) * frac) + o1;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::update() {
|
void Voice::update() {
|
||||||
// calculate the once-per-block values
|
// calculate the once-per-block values
|
||||||
env.run();
|
env.run();
|
||||||
calcPitch();
|
calcPitch();
|
||||||
//printf("env=%04x\n", env.level);
|
|
||||||
/*
|
|
||||||
pw = (patchRam.switch1 & 0x08) ? ic29.pwm : 0.0f;
|
|
||||||
saw = (ic29.patchRam.switch1 & 0x10) ? 1.0f : 0.0f;
|
|
||||||
sub = ic29.patchRam.subLevel / 128.0f;
|
|
||||||
|
|
||||||
ic29.lfo.rate = ic29.patchRam.lfoRate;
|
pw = (ic29.patchRam.switch1 & 0x08) ? ic29.pwm : 0.0f;
|
||||||
*/
|
saw = (ic29.patchRam.switch1 & 0x10) ? 1.0f : 0.0f;
|
||||||
|
sub = ic29.patchRam.subLevel / 128.0f;
|
||||||
|
|
||||||
|
ic29.lfo.rate = ic29.patchRam.lfoRate;
|
||||||
|
|
||||||
// do filter values
|
// do filter values
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::on(uint8_t key) {
|
void Voice::on(uint8_t key) {
|
||||||
d_debug("Voice on key %02x\n", key);
|
|
||||||
voiceState = V_ON;
|
voiceState = V_ON;
|
||||||
if (note != key) {
|
if (note != key) {
|
||||||
phase = 0;
|
phase = 0;
|
||||||
}
|
}
|
||||||
note = key;
|
note = key;
|
||||||
if (env.inRelease()) {
|
if (env.phase == env.ENV_RLS) {
|
||||||
env.on(); // FIXME move to synth update code
|
env.on(); // FIXME move to synth update code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::off() {
|
void Voice::off() {
|
||||||
d_debug("Voice off\n");
|
|
||||||
// sustain - I need to rethink this bit FIXME
|
// sustain - I need to rethink this bit FIXME
|
||||||
voiceState = V_OFF;
|
voiceState = V_OFF;
|
||||||
env.off();
|
if (!ic29.sustained) {
|
||||||
|
env.off();
|
||||||
|
}
|
||||||
}
|
}
|
@ -18,9 +18,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include "peacock.hpp"
|
||||||
|
|
||||||
#define NUM_VOICES 8
|
|
||||||
class LFO {
|
class LFO {
|
||||||
public:
|
public:
|
||||||
LFO();
|
LFO();
|
||||||
@ -42,7 +41,6 @@ class LFO {
|
|||||||
class Envelope {
|
class Envelope {
|
||||||
public:
|
public:
|
||||||
Envelope();
|
Envelope();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void on() {
|
void on() {
|
||||||
phase = ENV_ATK;
|
phase = ENV_ATK;
|
||||||
@ -56,20 +54,18 @@ class Envelope {
|
|||||||
return phase == ENV_RLS;
|
return phase == ENV_RLS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t atk, dcy, stn, rls;
|
|
||||||
|
|
||||||
uint16_t level;
|
uint16_t level;
|
||||||
|
|
||||||
private:
|
|
||||||
static const uint16_t atkTable[128];
|
|
||||||
static const uint16_t dcyTable[128];
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ENV_ATK,
|
ENV_ATK,
|
||||||
ENV_DCY,
|
ENV_DCY,
|
||||||
ENV_RLS,
|
ENV_RLS,
|
||||||
ENV_IDLE
|
ENV_IDLE
|
||||||
} phase;
|
} phase;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint16_t atkTable[128];
|
||||||
|
static const uint16_t dcyTable[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
class Voice {
|
class Voice {
|
||||||
@ -77,13 +73,11 @@ class Voice {
|
|||||||
Voice();
|
Voice();
|
||||||
uint8_t note = 0x3c; // middle C
|
uint8_t note = 0x3c; // middle C
|
||||||
void run(float *buffer, uint32_t pos, uint32_t samples);
|
void run(float *buffer, uint32_t pos, uint32_t samples);
|
||||||
void filter(float *buffer, uint32_t pos, uint32_t samples);
|
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void on(uint8_t note);
|
void on(uint8_t note);
|
||||||
void off();
|
void off();
|
||||||
|
|
||||||
// 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 = 0; // delay slot for polyblep
|
float delay = 0; // delay slot for polyblep
|
||||||
@ -97,8 +91,6 @@ class Voice {
|
|||||||
V_ON } voiceState;
|
V_ON } voiceState;
|
||||||
|
|
||||||
void calcPitch();
|
void calcPitch();
|
||||||
|
|
||||||
float fb = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, cut = 0.1, reso = 4;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Synth {
|
class Synth {
|
||||||
@ -121,13 +113,12 @@ class Synth {
|
|||||||
int8_t portaCoeff;
|
int8_t portaCoeff;
|
||||||
bool sustained;
|
bool sustained;
|
||||||
double pitchTable[104];
|
double pitchTable[104];
|
||||||
double filterTable[256];
|
|
||||||
|
|
||||||
float *noise;
|
float *noise;
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
uint8_t vcoBend = 42;
|
uint8_t vcoBend=42;
|
||||||
uint8_t vcfBend = 42;
|
uint8_t vcfBend=42;
|
||||||
|
|
||||||
Voice voices[NUM_VOICES];
|
Voice voices[NUM_VOICES];
|
||||||
LFO lfo;
|
LFO lfo;
|
||||||
@ -161,3 +152,6 @@ class Synth {
|
|||||||
const static uint8_t lfoDepthTable[128];
|
const static uint8_t lfoDepthTable[128];
|
||||||
const static uint8_t portaTable[128];
|
const static uint8_t portaTable[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// global
|
||||||
|
extern Synth ic29;
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ic29.hpp"
|
||||||
|
|
||||||
const uint16_t Envelope::atkTable[128] = {
|
const uint16_t Envelope::atkTable[128] = {
|
||||||
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
|
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
|
||||||
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
|
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "voiceboard.hpp"
|
#include "ic29.hpp"
|
||||||
|
|
||||||
static inline float poly3blep0(float t) {
|
static inline float poly3blep0(float t) {
|
||||||
float t2 = t * t;
|
float t2 = t * t;
|
||||||
@ -30,7 +30,8 @@ static inline float poly3blep1(float t) {
|
|||||||
void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
|
void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
|
||||||
// generate a full block of samples for the oscillator
|
// generate a full block of samples for the oscillator
|
||||||
|
|
||||||
float y, t;
|
float y, out, t;
|
||||||
|
float gain = env.level / 16384.0;
|
||||||
|
|
||||||
if (subosc > 0) subosc = sub; else subosc = -sub;
|
if (subosc > 0) subosc = sub; else subosc = -sub;
|
||||||
|
|
||||||
@ -68,10 +69,11 @@ void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
|
|||||||
// the DC correction is important because the hardware synth is AC-coupled effectively high-passing
|
// 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!
|
// the signal at about 10Hz or so, preventing any PWM rumble from leaking through!
|
||||||
delay += subosc;
|
delay += subosc;
|
||||||
//delay += ic29.noise[i+pos];
|
delay += ic29.noise[i+pos];
|
||||||
|
|
||||||
|
out = y * 0.15;
|
||||||
lastpw = pwrc;
|
lastpw = pwrc;
|
||||||
buffer[i + pos] = y * 0.707;
|
buffer[i + pos] += out * gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,19 @@
|
|||||||
|
|
||||||
#include "peacock.hpp"
|
#include "peacock.hpp"
|
||||||
|
|
||||||
#include "assigner.hpp"
|
#include "ic1.hpp"
|
||||||
#include "voiceboard.hpp"
|
#include "ic29.hpp"
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
|
Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
|
||||||
printf("peacock constructor\n");
|
printf("peacock constructor\n");
|
||||||
ic1.setVoiceBoard(ic29);
|
|
||||||
ic29.buildTables(getSampleRate());
|
ic29.buildTables(getSampleRate());
|
||||||
ic29.bufferSize = getBufferSize();
|
ic29.bufferSize = getBufferSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
@ -51,6 +52,7 @@ void Peacock::initAudioPort(bool input, uint32_t index, AudioPort &port) {
|
|||||||
if (!input && index == 1) port.name = "Right 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
|
||||||
@ -71,7 +73,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
|||||||
runMidi(midiEvents, midiEventCount, blockLeft);
|
runMidi(midiEvents, midiEventCount, blockLeft);
|
||||||
|
|
||||||
// generate a buffer's worth of samples
|
// generate a buffer's worth of samples
|
||||||
memset(outputs[1], 0, sizeof(float) * frames);
|
memset(outputs[0], 0, sizeof(float) * frames);
|
||||||
|
|
||||||
while (framePos < frames) {
|
while (framePos < frames) {
|
||||||
if (blockLeft == 0) {
|
if (blockLeft == 0) {
|
||||||
@ -85,11 +87,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
|||||||
|
|
||||||
// run every synth voice into the buffer here FIXME
|
// run every synth voice into the buffer here FIXME
|
||||||
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
for (uint8_t i = 0; i < NUM_VOICES; i++) {
|
||||||
//ic29->voices[i].run(outputs[0], framePos, sizeThisTime);
|
ic29.voices[i].run(outputs[0], framePos, sizeThisTime);
|
||||||
//ic29->voices[i].filter(outputs[0], framePos, sizeThisTime);
|
|
||||||
for (uint8_t j=0; j<sizeThisTime; j++) {
|
|
||||||
// outputs[1][framePos+j] += outputs[0][framePos+j] * ic29->voices[i].env.level/16384.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
framePos += sizeThisTime; // move along the frame
|
framePos += sizeThisTime; // move along the frame
|
||||||
@ -97,7 +95,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
|||||||
blockLeft -= sizeThisTime;
|
blockLeft -= sizeThisTime;
|
||||||
}
|
}
|
||||||
// output processing goes here
|
// output processing goes here
|
||||||
memcpy(outputs[0], outputs[1], sizeof(float) * frames);
|
memcpy(outputs[1], outputs[0], sizeof(float) * frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin *createPlugin() { return new Peacock(); }
|
Plugin *createPlugin() { return new Peacock(); }
|
||||||
|
@ -23,9 +23,6 @@
|
|||||||
|
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "DistrhoPlugin.hpp"
|
||||||
|
|
||||||
#include "assigner.hpp"
|
|
||||||
#include "voiceboard.hpp"
|
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
class Peacock : public Plugin {
|
class Peacock : public Plugin {
|
||||||
@ -90,9 +87,6 @@ class Peacock : public Plugin {
|
|||||||
uint32_t blockLeft = 0;
|
uint32_t blockLeft = 0;
|
||||||
uint32_t lastEvent = 0;
|
uint32_t lastEvent = 0;
|
||||||
|
|
||||||
Assigner ic1;
|
|
||||||
Synth ic29;
|
|
||||||
|
|
||||||
void runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit);
|
void runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit);
|
||||||
|
|
||||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock);
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock);
|
||||||
|
Loading…
Reference in New Issue
Block a user