proper envelope, noise, start working on inst editor UI

This commit is contained in:
LTVA1 2024-07-31 19:22:01 +03:00
parent 0d4d035c1f
commit 15725acbaa
8 changed files with 479 additions and 120 deletions

View file

@ -254,6 +254,36 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator&
);
}
bool DivInstrumentSID3::operator==(const DivInstrumentSID3& other) {
return (
_C(volume) &&
_C(sr) &&
_C(lfsr_taps) &&
_C(phase_mod) &&
_C(phase_mod_source) &&
_C(ring_mod_source) &&
_C(sync_source) &&
_C(specialWaveOn) &&
_C(special_wave) &&
_C(filter_matrix) &&
_C(filt[0]) &&
_C(filt[1]) &&
_C(filt[2]) &&
_C(filt[3])
);
}
bool DivInstrumentSID3::Filter::operator==(const DivInstrumentSID3::Filter& other) {
return (
_C(cutoff) &&
_C(resonance) &&
_C(output_volume) &&
_C(distortion_level) &&
_C(mode) &&
_C(enabled)
);
}
bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) {
return _C(octave);
}

View file

@ -861,6 +861,59 @@ struct DivInstrumentSID2 {
noiseMode(0) {}
};
struct DivInstrumentSID3
{
unsigned char volume;
unsigned char sr;
unsigned int lfsr_taps;
bool phase_mod;
unsigned char phase_mod_source, ring_mod_source, sync_source;
bool specialWaveOn;
unsigned char special_wave;
unsigned int filter_matrix;
struct Filter
{
unsigned short cutoff;
unsigned char resonance;
unsigned char output_volume;
unsigned char distortion_level;
unsigned char mode;
bool enabled;
bool operator==(const Filter& other);
bool operator!=(const Filter& other)
{
return !(*this==other);
}
Filter():
cutoff(0),
resonance(0),
output_volume(0),
distortion_level(0),
mode(0),
enabled(false) {}
} filt[4];
bool operator==(const DivInstrumentSID3& other);
bool operator!=(const DivInstrumentSID3& other)
{
return !(*this==other);
}
DivInstrumentSID3():
volume(255),
sr(0),
lfsr_taps(0),
phase_mod(false),
phase_mod_source(0),
ring_mod_source(0),
sync_source(0),
specialWaveOn(false),
special_wave(0),
filter_matrix(0) {}
};
struct DivInstrument {
String name;
DivInstrumentType type;
@ -880,6 +933,7 @@ struct DivInstrument {
DivInstrumentESFM esfm;
DivInstrumentPowerNoise powernoise;
DivInstrumentSID2 sid2;
DivInstrumentSID3 sid3;
/**
* these are internal functions.

View file

@ -94,7 +94,7 @@ void DivPlatformSID3::acquire(short** buf, size_t len)
for(int j = 0; j < SID3_NUM_CHANNELS; j++)
{
oscBuf[j]->data[oscBuf[j]->needle++] = sid3->channel_output[j] / 4;
oscBuf[j]->data[oscBuf[j]->needle++] = sid3->channel_output[j];
}
}
}
@ -120,12 +120,27 @@ void DivPlatformSID3::updateDuty(int channel)
rWrite(8 + channel*SID3_REGISTERS_PER_CHANNEL,chan[channel].duty & 0xff);
}
void DivPlatformSID3::updateEnvelope(int channel)
{
rWrite(1 + channel * SID3_REGISTERS_PER_CHANNEL, chan[channel].attack); //attack
rWrite(2 + channel * SID3_REGISTERS_PER_CHANNEL, chan[channel].decay); //decay
rWrite(3 + channel * SID3_REGISTERS_PER_CHANNEL, chan[channel].sustain); //sustain
rWrite(4 + channel * SID3_REGISTERS_PER_CHANNEL, chan[channel].sr); //sr
rWrite(5 + channel * SID3_REGISTERS_PER_CHANNEL, chan[channel].release); //release
}
void DivPlatformSID3::tick(bool sysTick)
{
for (int i=0; i<SID3_NUM_CHANNELS; i++)
{
chan[i].std.next();
if (chan[i].std.vol.had)
{
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&255,MIN(255,chan[i].std.vol.val),255);
rWrite(13 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].outVol);
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff)
{
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE * 64);
@ -133,13 +148,18 @@ void DivPlatformSID3::tick(bool sysTick)
//if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff;
if (chan[i].keyOn)
{
rWrite(i*SID3_REGISTERS_PER_CHANNEL,SID3_CHAN_ENABLE_GATE); //gate on
rWrite(6 + i*SID3_REGISTERS_PER_CHANNEL,SID3_WAVE_SPECIAL); //waveform
rWrite(9 + i*SID3_REGISTERS_PER_CHANNEL,10); //special wave
{
rWrite(6 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].wave); //waveform
rWrite(9 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].special_wave); //special wave
rWrite(13 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].outVol); //set volume
updateEnvelope(i);
chan[i].duty = 0x1000;
updateDuty(i);
rWrite(i * SID3_REGISTERS_PER_CHANNEL, SID3_CHAN_ENABLE_GATE); //gate on
}
if (chan[i].keyOff)
{
@ -174,7 +194,6 @@ int DivPlatformSID3::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].test=false;
if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) {
chan[c.chan].duty=ins->c64.duty;
@ -192,15 +211,25 @@ int DivPlatformSID3::dispatch(DivCommand c) {
chan[c.chan].noise_mode = ins->sid3.noiseMode;
chan[c.chan].mix_mode = ins->sid3.mixMode;*/
chan[c.chan].wave = (ins->c64.triOn ? SID3_WAVE_TRIANGLE : 0) | (ins->c64.sawOn ? SID3_WAVE_SAW : 0) |
(ins->c64.pulseOn ? SID3_WAVE_PULSE : 0) | (ins->c64.noiseOn ? SID3_WAVE_NOISE : 0) | (ins->sid3.specialWaveOn ? SID3_WAVE_SPECIAL : 0); //waveform
chan[c.chan].special_wave = ins->sid3.special_wave; //special wave
chan[c.chan].attack=ins->c64.a;
chan[c.chan].decay=ins->c64.d;
chan[c.chan].sustain=ins->c64.s;
chan[c.chan].sr=ins->sid3.sr;
chan[c.chan].release=ins->c64.r;
}
if (chan[c.chan].insChanged || chan[c.chan].resetFilter) {
chan[c.chan].filter=ins->c64.toFilter;
/*chan[c.chan].filter=ins->c64.toFilter;
if (ins->c64.initFilter) {
chan[c.chan].filtCut=ins->c64.cut;
chan[c.chan].filtRes=ins->c64.res;
chan[c.chan].filtControl=(int)(ins->c64.lp)|(ins->c64.bp<<1)|(ins->c64.hp<<2);
}
updateFilter(c.chan);
updateFilter(c.chan);*/
}
if (chan[c.chan].insChanged) {
chan[c.chan].insChanged=false;
@ -348,10 +377,7 @@ DivChannelModeHints DivPlatformSID3::getModeHints(int ch) {
ret.count=1;
ret.hint[0]=ICON_FA_BELL_SLASH_O;
ret.type[0]=0;
if (ch == 2 && (chan[ch].filtControl & 8)) {
ret.type[0] = 7;
}
else if (!chan[ch].gate) {
if (!chan[ch].gate) {
ret.type[0]=4;
}

View file

@ -27,12 +27,10 @@
class DivPlatformSID3: public DivDispatch {
struct Channel: public SharedChannel<signed short> {
int prevFreq;
unsigned char wave, attack, decay, sustain, release;
unsigned char wave, special_wave, attack, decay, sustain, sr, release;
short duty;
bool filter;
bool resetMask, resetFilter, resetDuty, gate, ring, sync, test;
bool resetMask, resetFilter, resetDuty, gate, ring, sync, phase;
unsigned char vol;
unsigned char filtControl, filtRes;
unsigned char noise_mode;
unsigned char mix_mode;
int filtCut;
@ -40,22 +38,21 @@ class DivPlatformSID3: public DivDispatch {
SharedChannel<signed short>(SID3_MAX_VOL),
prevFreq(0x1ffff),
wave(0),
special_wave(0),
attack(0),
decay(0),
sustain(0),
sr(0),
release(0),
duty(0),
filter(false),
resetMask(false),
resetFilter(false),
resetDuty(false),
gate(true),
ring(false),
sync(false),
test(false),
phase(false),
vol(SID3_MAX_VOL),
filtControl(0),
filtRes(0),
noise_mode(0),
mix_mode(0),
filtCut(0) {}
@ -83,6 +80,7 @@ class DivPlatformSID3: public DivDispatch {
void updateFilter(int channel);
void updateFreq(int channel);
void updateDuty(int channel);
void updateEnvelope(int channel);
public:
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);

View file

@ -4,12 +4,15 @@
#define SAFETY_HEADER if(sid3 == NULL) return;
enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; //for envelope
enum State { ATTACK, DECAY, SUSTAIN, RELEASE }; //for envelope
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#define envspd_adr(rate) ((uint64_t)0xff0000 / ((rate == 0 ? 1 : (rate * 16)) * (rate == 0 ? 1 : (rate * 16))))
#define envspd_sr(rate) (rate == 0 ? 0 : ((uint64_t)0xff0000 / ((256 - rate) * (256 - rate) * 256)))
//these 4 ones are util only
double square(double x) {
return fmod(x, (2 * M_PI)) >= M_PI ? -1 : 1;
@ -2243,53 +2246,91 @@ void sid3_reset(SID3* sid3)
for(int32_t i = 0; i < SID3_NUM_CHANNELS - 1; i++)
{
memset(&sid3->chan[i], 0, sizeof(sid3_channel));
sid3->chan[i].accumulator = 0;
sid3->chan[i].adsr.a = 0x8;
sid3->chan[i].adsr.d = 0x8;
/*sid3->chan[i].accumulator = 0;
sid3->chan[i].adsr.a = 0x20;
sid3->chan[i].adsr.d = 0x20;
sid3->chan[i].adsr.s = 0x80;
sid3->chan[i].adsr.r = 0x07;
sid3->chan[i].adsr.vol = 0xf0;
sid3->chan[i].adsr.r = 0x17;
sid3->chan[i].adsr.vol = 0xf0;*/
sid3->chan[i].adsr.hold_zero = true;
sid3->chan[i].lfsr = 0x1;
sid3->chan[i].lfsr_taps = (1 << 29) | (1 << 5) | (1 << 3) | 1; //https://docs.amd.com/v/u/en-US/xapp052 for 30 bits: 30, 6, 4, 1
//....
}
//TODO: wavetable chan
}
void sid3_gate_bit(uint8_t gate_next, uint8_t gate, sid3_channel_adsr* adsr)
void sid3_do_release_rate_period(sid3_channel_adsr* adsr)
{
uint32_t counter = adsr->envelope_counter >> 16;
if(counter >= 0xff)
{
adsr->rate_period = 1;
return;
}
if(counter >= 0x5d)
{
adsr->rate_period = 2;
return;
}
if(counter >= 0x36)
{
adsr->rate_period = 4;
return;
}
if(counter >= 0x1a)
{
adsr->rate_period = 8;
return;
}
if(counter >= 0x0e)
{
adsr->rate_period = 16;
return;
}
if(counter >= 0x06)
{
adsr->rate_period = 32;
return;
}
if(counter >= 0x02)
{
adsr->rate_period = 64;
return;
}
}
void sid3_gate_bit(uint8_t gate, sid3_channel_adsr* adsr)
{
// The rate counter is never reset, thus there will be a delay before the
// envelope counter starts counting up (attack) or down (release).
// Gate bit on: Start attack, decay, sustain.
if (!gate && gate_next)
if (gate)
{
adsr->state = ATTACK;
adsr->rate_period = adsr->a << 8; //todo: make it properly
adsr->envelope_speed = envspd_adr(adsr->a); //todo: make it properly
//adsr->envelope_counter = 0;
// Switching to attack state unlocks the zero freeze.
adsr->hold_zero = false;
adsr->rate_counter = 0;
adsr->exponential_counter = 0;
//envelope_counter = 0;
if(adsr->envelope_counter == 0xff)
{
adsr->envelope_counter--; //idk why it happens, but when envelope has max sustain and I retrigger with new note it just becomes silent so this is the only solution I found so far
}
adsr->rate_period = 1;
}
// Gate bit off: Start release.
else if (gate && !gate_next)
else
{
adsr->state = RELEASE;
adsr->rate_period = adsr->r << 8; //todo: make it properly
adsr->envelope_speed = envspd_adr(adsr->r); //todo: make it properly
adsr->rate_counter = 0;
adsr->exponential_counter = 0;
}
//adsr->rate_period = 1;
//gate = gate_next;
sid3_do_release_rate_period(adsr);
}
}
void sid3_adsr_clock(sid3_channel_adsr* adsr)
@ -2304,95 +2345,90 @@ void sid3_adsr_clock(sid3_channel_adsr* adsr)
adsr->rate_counter = adsr->rate_period; //so you can do alternating writes (e.g. writing attack 10-11-10-11-... results in the somewhat average envelope speed)
}
if (adsr->rate_counter != adsr->rate_period) {
if (adsr->rate_counter != adsr->rate_period)
{
return;
}
adsr->rate_counter = 0;
// The first envelope step in the attack state also resets the exponential
// counter. This has been verified by sampling ENV3.
//
if (adsr->state == ATTACK || ++adsr->exponential_counter == adsr->exponential_counter_period)
// Check whether the envelope counter is frozen at zero.
if (adsr->hold_zero)
{
adsr->exponential_counter = 0;
return;
}
// Check whether the envelope counter is frozen at zero.
if (adsr->hold_zero)
switch (adsr->state)
{
case ATTACK:
{
return;
}
adsr->envelope_counter += adsr->envelope_speed;
switch (adsr->state)
{
case ATTACK:
// The envelope counter can flip from 0xff to 0x00 by changing state to
// release, then to attack. The envelope counter is then frozen at
// zero; to unlock this situation the state must be changed to release,
// then to attack. This has been verified by sampling ENV3.
//
adsr->envelope_counter++;
adsr->envelope_counter &= 0xff;
if (adsr->envelope_counter == 0xff)
if (adsr->envelope_counter >= 0xff0000)
{
adsr->state = DECAY_SUSTAIN;
adsr->rate_period = (adsr->d << 8); //todo: do it properly
adsr->state = DECAY;
adsr->envelope_speed = envspd_adr(adsr->d); //todo: do it properly
}
break;
case DECAY_SUSTAIN:
if (adsr->envelope_counter != (adsr->s))
{
--adsr->envelope_counter;
}
break;
case RELEASE:
// The envelope counter can flip from 0x00 to 0xff by changing state to
// attack, then to release. The envelope counter will then continue
// counting down in the release state.
// This has been verified by sampling ENV3.
// NB! The operation below requires two's complement integer.
//
//--envelope_counter &= 0xff;
adsr->envelope_counter--;
break;
}
// Check for change of exponential counter period.
switch (adsr->envelope_counter)
{
case 0xff:
adsr->exponential_counter_period = 1;
break;
case 0x5d:
adsr->exponential_counter_period = 2;
break;
case 0x36:
adsr->exponential_counter_period = 4;
break;
case 0x1a:
adsr->exponential_counter_period = 8;
break;
case 0x0e:
adsr->exponential_counter_period = 16;
break;
case 0x06:
adsr->exponential_counter_period = 30;
break;
case 0x00:
adsr->exponential_counter_period = 1;
// When the envelope counter is changed to zero, it is frozen at zero.
// This has been verified by sampling ENV3.
adsr->hold_zero = true;
break;
return; //do not do exponential approximation of attack
}
break;
case DECAY:
{
adsr->envelope_counter -= adsr->envelope_speed;
if(adsr->envelope_counter <= ((uint32_t)adsr->s << 16) || adsr->envelope_counter > 0xff0000)
{
adsr->state = SUSTAIN;
adsr->envelope_counter = (uint32_t)adsr->s << 16;
adsr->envelope_speed = envspd_sr(adsr->sr); //todo: do it properly
}
}
break;
case SUSTAIN:
case RELEASE:
{
adsr->envelope_counter -= adsr->envelope_speed;
if(adsr->envelope_counter <= adsr->envelope_speed || adsr->envelope_counter > 0xff0000)
{
adsr->envelope_counter = 0;
adsr->hold_zero = true;
}
}
break;
}
// Check for change of exponential counter period.
switch (adsr->envelope_counter >> 16)
{
case 0xff:
adsr->rate_period = 1;
break;
case 0x5d:
adsr->rate_period = 2;
break;
case 0x36:
adsr->rate_period = 4;
break;
case 0x1a:
adsr->rate_period = 8;
break;
case 0x0e:
adsr->rate_period = 16;
break;
case 0x06:
adsr->rate_period = 32;
break;
case 0x02:
adsr->rate_period = 64;
break;
}
}
int32_t sid3_adsr_output(sid3_channel_adsr* adsr, int32_t input)
{
return (int32_t)((int64_t)input * (int64_t)adsr->envelope_counter * (int64_t)adsr->vol / (int64_t)SID3_MAX_VOL / (int64_t)SID3_MAX_VOL);
return (int32_t)((int64_t)input * (int64_t)adsr->envelope_counter / (int64_t)0xff0000 / (int64_t)4 * (int64_t)adsr->vol / (int64_t)SID3_MAX_VOL); //"/ (int64_t)4" so that there's enough amplitude for all 7 chans!
}
void sid3_write(SID3* sid3, uint8_t address, uint8_t data)
@ -2411,15 +2447,15 @@ void sid3_write(SID3* sid3, uint8_t address, uint8_t data)
{
if(channel != SID3_NUM_CHANNELS - 1)
{
uint8_t prev_flags = sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE;
//uint8_t prev_flags = sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE;
sid3->chan[channel].flags = data;
sid3_gate_bit(sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE, prev_flags, &sid3->chan[channel].adsr);
sid3_gate_bit(sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE, &sid3->chan[channel].adsr);
}
else
{
uint8_t prev_flags = sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE;
//uint8_t prev_flags = sid3->chan[channel].flags & SID3_CHAN_ENABLE_GATE;
sid3->wave_chan.flags = data;
sid3_gate_bit(sid3->wave_chan.flags & SID3_CHAN_ENABLE_GATE, prev_flags, &sid3->wave_chan.adsr);
sid3_gate_bit(sid3->wave_chan.flags & SID3_CHAN_ENABLE_GATE, &sid3->wave_chan.adsr);
}
break;
}
@ -2571,6 +2607,18 @@ void sid3_write(SID3* sid3, uint8_t address, uint8_t data)
}
break;
}
case 13:
{
if(channel != SID3_NUM_CHANNELS - 1)
{
sid3->chan[channel].adsr.vol = data;
}
else
{
sid3->wave_chan.adsr.vol = data;
}
break;
}
default: break;
}
}
@ -2592,12 +2640,18 @@ inline uint16_t sid3_triangle(uint32_t acc)
void sid3_clock_lfsr(sid3_channel* ch)
{
uint32_t feedback = ch->lfsr & 1;
ch->lfsr >>= 1;
if (feedback)
{
ch->lfsr ^= ch->lfsr_taps;
}
}
uint16_t sid3_noise(uint32_t lfsr, bool one_bit)
{
return 0;
return one_bit ? ((lfsr & 1) ? 0xffff : 0) : (lfsr & 0xffff);
}
inline uint16_t sid3_special_wave(SID3* sid3, uint32_t acc, uint8_t wave)
@ -2668,6 +2722,11 @@ void sid3_clock(SID3* sid3)
ch->accumulator &= SID3_ACC_MASK;
if((prev_acc & ((uint32_t)1 << (SID3_ACC_BITS - 6))) != (ch->accumulator & ((uint32_t)1 << (SID3_ACC_BITS - 6))))
{
sid3_clock_lfsr(ch);
}
//todo: phase mod
int32_t waveform = sid3_get_waveform(sid3, ch);

View file

@ -75,6 +75,8 @@ typedef struct
uint8_t mode;
uint8_t output_volume;
bool channel_output; //output to the channel master output
} sid3_filter;
@ -90,9 +92,8 @@ typedef struct
uint32_t rate_counter;
uint32_t rate_period;
uint16_t exponential_counter;
uint16_t exponential_counter_period;
uint8_t envelope_counter;
uint32_t envelope_counter;
uint32_t envelope_speed;
bool hold_zero;
} sid3_channel_adsr;