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) { bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) {
return _C(octave); return _C(octave);
} }

View file

@ -861,6 +861,59 @@ struct DivInstrumentSID2 {
noiseMode(0) {} 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 { struct DivInstrument {
String name; String name;
DivInstrumentType type; DivInstrumentType type;
@ -880,6 +933,7 @@ struct DivInstrument {
DivInstrumentESFM esfm; DivInstrumentESFM esfm;
DivInstrumentPowerNoise powernoise; DivInstrumentPowerNoise powernoise;
DivInstrumentSID2 sid2; DivInstrumentSID2 sid2;
DivInstrumentSID3 sid3;
/** /**
* these are internal functions. * 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++) 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); 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) void DivPlatformSID3::tick(bool sysTick)
{ {
for (int i=0; i<SID3_NUM_CHANNELS; i++) for (int i=0; i<SID3_NUM_CHANNELS; i++)
{ {
chan[i].std.next(); 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) 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); 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].freq>0x1ffff) chan[i].freq=0x1ffff;
if (chan[i].keyOn) if (chan[i].keyOn)
{ {
rWrite(i*SID3_REGISTERS_PER_CHANNEL,SID3_CHAN_ENABLE_GATE); //gate on rWrite(6 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].wave); //waveform
rWrite(6 + i*SID3_REGISTERS_PER_CHANNEL,SID3_WAVE_SPECIAL); //waveform rWrite(9 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].special_wave); //special wave
rWrite(9 + i*SID3_REGISTERS_PER_CHANNEL,10); //special wave
rWrite(13 + i * SID3_REGISTERS_PER_CHANNEL, chan[i].outVol); //set volume
updateEnvelope(i);
chan[i].duty = 0x1000; chan[i].duty = 0x1000;
updateDuty(i); updateDuty(i);
rWrite(i * SID3_REGISTERS_PER_CHANNEL, SID3_CHAN_ENABLE_GATE); //gate on
} }
if (chan[i].keyOff) if (chan[i].keyOff)
{ {
@ -174,7 +194,6 @@ int DivPlatformSID3::dispatch(DivCommand c) {
} }
chan[c.chan].active=true; chan[c.chan].active=true;
chan[c.chan].keyOn=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) { if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) {
chan[c.chan].duty=ins->c64.duty; 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].noise_mode = ins->sid3.noiseMode;
chan[c.chan].mix_mode = ins->sid3.mixMode;*/ 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) { 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) { if (ins->c64.initFilter) {
chan[c.chan].filtCut=ins->c64.cut; chan[c.chan].filtCut=ins->c64.cut;
chan[c.chan].filtRes=ins->c64.res; chan[c.chan].filtRes=ins->c64.res;
chan[c.chan].filtControl=(int)(ins->c64.lp)|(ins->c64.bp<<1)|(ins->c64.hp<<2); 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) { if (chan[c.chan].insChanged) {
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
@ -348,10 +377,7 @@ DivChannelModeHints DivPlatformSID3::getModeHints(int ch) {
ret.count=1; ret.count=1;
ret.hint[0]=ICON_FA_BELL_SLASH_O; ret.hint[0]=ICON_FA_BELL_SLASH_O;
ret.type[0]=0; ret.type[0]=0;
if (ch == 2 && (chan[ch].filtControl & 8)) { if (!chan[ch].gate) {
ret.type[0] = 7;
}
else if (!chan[ch].gate) {
ret.type[0]=4; ret.type[0]=4;
} }

View file

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

View file

@ -4,12 +4,15 @@
#define SAFETY_HEADER if(sid3 == NULL) return; #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 #ifndef M_PI
# define M_PI 3.14159265358979323846 # define M_PI 3.14159265358979323846
#endif #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 //these 4 ones are util only
double square(double x) { double square(double x) {
return fmod(x, (2 * M_PI)) >= M_PI ? -1 : 1; 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++) for(int32_t i = 0; i < SID3_NUM_CHANNELS - 1; i++)
{ {
memset(&sid3->chan[i], 0, sizeof(sid3_channel)); memset(&sid3->chan[i], 0, sizeof(sid3_channel));
sid3->chan[i].accumulator = 0; /*sid3->chan[i].accumulator = 0;
sid3->chan[i].adsr.a = 0x8; sid3->chan[i].adsr.a = 0x20;
sid3->chan[i].adsr.d = 0x8; sid3->chan[i].adsr.d = 0x20;
sid3->chan[i].adsr.s = 0x80; sid3->chan[i].adsr.s = 0x80;
sid3->chan[i].adsr.r = 0x07; sid3->chan[i].adsr.r = 0x17;
sid3->chan[i].adsr.vol = 0xf0; sid3->chan[i].adsr.vol = 0xf0;*/
sid3->chan[i].adsr.hold_zero = true; 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 //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 // The rate counter is never reset, thus there will be a delay before the
// envelope counter starts counting up (attack) or down (release). // envelope counter starts counting up (attack) or down (release).
// Gate bit on: Start attack, decay, sustain. // Gate bit on: Start attack, decay, sustain.
if (!gate && gate_next) if (gate)
{ {
adsr->state = ATTACK; 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. // Switching to attack state unlocks the zero freeze.
adsr->hold_zero = false; adsr->hold_zero = false;
adsr->rate_counter = 0; adsr->rate_counter = 0;
adsr->exponential_counter = 0; adsr->rate_period = 1;
//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
}
} }
// Gate bit off: Start release. // Gate bit off: Start release.
else if (gate && !gate_next) else
{ {
adsr->state = RELEASE; 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->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) 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) 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; return;
} }
adsr->rate_counter = 0; adsr->rate_counter = 0;
// The first envelope step in the attack state also resets the exponential // Check whether the envelope counter is frozen at zero.
// counter. This has been verified by sampling ENV3. if (adsr->hold_zero)
//
if (adsr->state == ATTACK || ++adsr->exponential_counter == adsr->exponential_counter_period)
{ {
adsr->exponential_counter = 0; return;
}
// Check whether the envelope counter is frozen at zero. switch (adsr->state)
if (adsr->hold_zero) {
case ATTACK:
{ {
return; adsr->envelope_counter += adsr->envelope_speed;
}
switch (adsr->state) if (adsr->envelope_counter >= 0xff0000)
{
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)
{ {
adsr->state = DECAY_SUSTAIN; adsr->state = DECAY;
adsr->rate_period = (adsr->d << 8); //todo: do it properly 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. return; //do not do exponential approximation of attack
// This has been verified by sampling ENV3.
adsr->hold_zero = true;
break;
} }
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) 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) 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) 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->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 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->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; break;
} }
@ -2571,6 +2607,18 @@ void sid3_write(SID3* sid3, uint8_t address, uint8_t data)
} }
break; 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; default: break;
} }
} }
@ -2592,12 +2640,18 @@ inline uint16_t sid3_triangle(uint32_t acc)
void sid3_clock_lfsr(sid3_channel* ch) 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) 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) 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; 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 //todo: phase mod
int32_t waveform = sid3_get_waveform(sid3, ch); int32_t waveform = sid3_get_waveform(sid3, ch);

View file

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

View file

@ -2699,6 +2699,7 @@ class FurnaceGUI {
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
void drawESFMAlgorithm(DivInstrumentESFM& esfm, const ImVec2& size); void drawESFMAlgorithm(DivInstrumentESFM& esfm, const ImVec2& size);
void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType);
void drawSID3Env(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType);
void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size);
bool drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false); bool drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false);
void kvsConfig(DivInstrument* ins, bool supportsKVS=true); void kvsConfig(DivInstrument* ins, bool supportsKVS=true);
@ -2796,6 +2797,7 @@ class FurnaceGUI {
void drawPattern(); void drawPattern();
void drawInsList(bool asChild=false); void drawInsList(bool asChild=false);
void drawInsEdit(); void drawInsEdit();
void drawInsSID3(DivInstrument* ins);
void drawWaveList(bool asChild=false); void drawWaveList(bool asChild=false);
void drawWaveEdit(); void drawWaveEdit();
void drawSampleList(bool asChild=false); void drawSampleList(bool asChild=false);

View file

@ -22,6 +22,7 @@
#include "../ta-log.h" #include "../ta-log.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "../engine/macroInt.h" #include "../engine/macroInt.h"
#include "../engine/platform/sound/sid3.h"
#include "IconsFontAwesome4.h" #include "IconsFontAwesome4.h"
#include "furIcons.h" #include "furIcons.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
@ -1523,6 +1524,80 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr,
} }
} }
void FurnaceGUI::drawSID3Env(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType) {
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImGuiStyle& style=ImGui::GetStyle();
ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]);
ImU32 colorR=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_RELEASE]); // Relsease triangle
ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("fmEnv"))) {
ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding);
//Adjust for OPLL global sustain setting
if (instType==DIV_INS_OPLL && algOrGlobalSus==1.0){
rr = 5.0;
}
//calculate x positions
float arPos=float(maxArDr-(float)ar)/maxArDr; //peak of AR, start of DR
float drPos=arPos+(((float)sl/255.0)*(float(maxArDr-(float)dr)/maxArDr)); //end of DR, start of D2R
float d2rPos=drPos+(((255.0-(float)sl)/255.0)*(float(255.0-(float)d2r)/255.0)); //End of D2R
float rrPos=(float(maxRr-(float)rr)/float(maxRr)); //end of RR
//shrink all the x positions horizontally
arPos/=2.0;
drPos/=2.0;
d2rPos/=2.0;
rrPos/=1.0;
ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.0,1.0)); //the bottom corner
ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(arPos,((float)tl/maxTl))); //peak of AR, start of DR
ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(drPos,(float)(((float)tl/maxTl)+((float)sl/255.0)-(((float)tl/maxTl)*((float)sl/255.0))))); //end of DR, start of D2R
ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(d2rPos,1.0)); //end of D2R
ImVec2 posRStart=ImLerp(rect.Min,rect.Max,ImVec2(0.0,((float)tl/maxTl))); //release start
ImVec2 posREnd=ImLerp(rect.Min,rect.Max,ImVec2(rrPos,1.0));//release end
ImVec2 posSLineHEnd=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)(((float)tl/maxTl)+((float)sl/255.0)-(((float)tl/maxTl)*((float)sl/255.0))))); //sustain horizontal line end
ImVec2 posSLineVEnd=ImLerp(rect.Min,rect.Max,ImVec2(drPos,1.0)); //sustain vertical line end
ImVec2 posDecayRate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,((float)tl/maxTl))); //Height of the peak of AR, forever
ImVec2 posDecay2Rate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)(((float)tl/maxTl)+((float)sl/255.0)-(((float)tl/maxTl)*((float)sl/255.0))))); //Height of the peak of SR, forever
//dl->Flags=ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex;
if ((float)ar==0.0) { //if AR = 0, the envelope never starts
dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything
addAALine(dl,pos1,pos4,color); //draw line on ground
} else if ((float)dr==0.0 && (float)sl!=0.0) { //if DR = 0 and SL is not 0, then the envelope stays at max volume forever
dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything
//addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level
//addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level
addAALine(dl,pos1,pos2,color); //A
addAALine(dl,pos2,posDecayRate0Pt,color); //Line from A to end of graph
} else if ((float)d2r==0.0 || ((instType==DIV_INS_OPL || instType==DIV_INS_SNES || instType == DIV_INS_ESFM) && sus==1.0) || (instType==DIV_INS_OPLL && egt!=0.0)) { //envelope stays at the sustain level forever
dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything
addAALine(dl,pos3,posSLineHEnd,colorR); //draw horiz line through sustain level
addAALine(dl,pos3,posSLineVEnd,colorR); //draw vert. line through sustain level
addAALine(dl,pos1,pos2,color); //A
addAALine(dl,pos2,pos3,color); //D
addAALine(dl,pos3,posDecay2Rate0Pt,color); //Line from D to end of graph
} else { //draw graph normally
dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything
addAALine(dl,pos3,posSLineHEnd,colorR); //draw horiz line through sustain level
addAALine(dl,pos3,posSLineVEnd,colorR); //draw vert. line through sustain level
addAALine(dl,pos1,pos2,color); //A
addAALine(dl,pos2,pos3,color); //D
addAALine(dl,pos3,pos4,color); //D2
}
//dl->Flags^=ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex;
}
}
void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size) { void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size) {
ImDrawList* dl=ImGui::GetWindowDrawList(); ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow(); ImGuiWindow* window=ImGui::GetCurrentWindow();
@ -5234,6 +5309,112 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
} }
} }
void FurnaceGUI::drawInsSID3(DivInstrument* ins)
{
if (ImGui::BeginTabItem("SID3"))
{
ImGui::AlignTextToFramePadding();
ImGui::Text(_("Waveform"));
ImGui::SameLine();
pushToggleColors(ins->c64.triOn);
if (ImGui::Button(_("tri"))) { PARAMETER
ins->c64.triOn=!ins->c64.triOn;
}
popToggleColors();
ImGui::SameLine();
pushToggleColors(ins->c64.sawOn);
if (ImGui::Button(_("saw"))) { PARAMETER
ins->c64.sawOn=!ins->c64.sawOn;
}
popToggleColors();
ImGui::SameLine();
pushToggleColors(ins->c64.pulseOn);
if (ImGui::Button(_("pulse"))) { PARAMETER
ins->c64.pulseOn=!ins->c64.pulseOn;
}
popToggleColors();
ImGui::SameLine();
pushToggleColors(ins->c64.noiseOn);
if (ImGui::Button(_("noise"))) { PARAMETER
ins->c64.noiseOn=!ins->c64.noiseOn;
}
popToggleColors();
popToggleColors();
ImGui::SameLine();
pushToggleColors(ins->sid3.specialWaveOn);
if (ImGui::Button(_("special"))) { PARAMETER
ins->sid3.specialWaveOn=!ins->sid3.specialWaveOn;
}
popToggleColors();
ImVec2 sliderSize=ImVec2(30.0f*dpiScale,256.0*dpiScale);
if (ImGui::BeginTable("FZTEnvParams",6,ImGuiTableFlags_NoHostExtendX))
{
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
CENTER_TEXT(_("A"));
ImGui::TextUnformatted(_("A"));
ImGui::TableNextColumn();
CENTER_TEXT(_("D"));
ImGui::TextUnformatted(_("D"));
ImGui::TableNextColumn();
CENTER_TEXT(_("S"));
ImGui::TextUnformatted(_("S"));
ImGui::TableNextColumn();
CENTER_TEXT(_("SR"));
ImGui::TextUnformatted(_("SR"));
ImGui::TableNextColumn();
CENTER_TEXT(_("R"));
ImGui::TextUnformatted(_("R"));
ImGui::TableNextColumn();
CENTER_TEXT(_("Envelope"));
ImGui::TextUnformatted(_("Envelope"));
ImGui::TableNextRow();
ImGui::TableNextColumn();
P(CWVSliderScalar("##Attack",sliderSize,ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); rightClickable
ImGui::TableNextColumn();
P(CWVSliderScalar("##Decay",sliderSize,ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); rightClickable
ImGui::TableNextColumn();
P(CWVSliderScalar("##Sustain",sliderSize,ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); rightClickable
ImGui::TableNextColumn();
P(CWVSliderScalar("##SustainRate",sliderSize,ImGuiDataType_U8,&ins->sid3.sr,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); rightClickable
ImGui::TableNextColumn();
P(CWVSliderScalar("##Release",sliderSize,ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); rightClickable
ImGui::TableNextColumn();
drawSID3Env(0,(ins->c64.a == 0 ? (255) : (256-ins->c64.a)),(ins->c64.d == 0 ? (255) : (256-ins->c64.d)),ins->sid3.sr,255-(ins->c64.r == 255 ? (ins->c64.r - 1) : ins->c64.r),255-ins->c64.s,0,0,0,255,256,255,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); //the (ins->c64.r == 15 ? (ins->c64.r - 1) : ins->c64.r) is used so release part never becomes horizontal (which isn't the case with SID3 envelope)
ImGui::EndTable();
}
ImGui::EndTabItem();
}
std::vector<FurnaceGUIMacroDesc> macroList;
if (ImGui::BeginTabItem(_("Macros")))
{
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,255,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL));
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
drawMacros(macroList,macroEditStateMacros);
ImGui::EndTabItem();
}
}
void FurnaceGUI::drawInsEdit() { void FurnaceGUI::drawInsEdit() {
if (nextWindow==GUI_WINDOW_INS_EDIT) { if (nextWindow==GUI_WINDOW_INS_EDIT) {
insEditOpen=true; insEditOpen=true;
@ -5449,6 +5630,14 @@ void FurnaceGUI::drawInsEdit() {
if (ImGui::BeginTabBar("insEditTab")) { if (ImGui::BeginTabBar("insEditTab")) {
if(ins->type == DIV_INS_SID3)
{
drawInsSID3(ins);
ImGui::EndTabBar();
return;
}
std::vector<FurnaceGUIMacroDesc> macroList; std::vector<FurnaceGUIMacroDesc> macroList;
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPM || ins->type==DIV_INS_ESFM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPM || ins->type==DIV_INS_ESFM) {
char label[32]; char label[32];