From 719cec89b1634a90972c8f147b9923acfc6eff02 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 30 Jul 2024 18:15:08 +0300 Subject: [PATCH] port reSID envelope (requires further work...) --- src/engine/platform/sid3.cpp | 48 +++- src/engine/platform/sid3.h | 1 + src/engine/platform/sound/sid3.c | 457 ++++++++++++++++++++++++++++++- src/engine/platform/sound/sid3.h | 65 ++++- 4 files changed, 555 insertions(+), 16 deletions(-) diff --git a/src/engine/platform/sid3.cpp b/src/engine/platform/sid3.cpp index 455c18a58..baa9cd1e9 100644 --- a/src/engine/platform/sid3.cpp +++ b/src/engine/platform/sid3.cpp @@ -102,14 +102,43 @@ void DivPlatformSID3::acquire(short** buf, size_t len) void DivPlatformSID3::updateFilter(int channel) { - rWrite(0x15 + 3 * channel,(chan[channel].filtCut&15) | ((chan[channel].filtControl & 7) << 4) | (chan[channel].filter << 7)); - rWrite(0x16 + 3 * channel,(chan[channel].filtCut >> 4)); - rWrite(0x17 + 3 * channel,chan[channel].filtRes); + //rWrite(0x15 + 3 * channel,(chan[channel].filtCut&15) | ((chan[channel].filtControl & 7) << 4) | (chan[channel].filter << 7)); + //rWrite(0x16 + 3 * channel,(chan[channel].filtCut >> 4)); + //rWrite(0x17 + 3 * channel,chan[channel].filtRes); } -void DivPlatformSID3::tick(bool sysTick) { - for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,8,chan[i].pitch2,chipClock,CHIP_FREQBASE); + //if (chan[i].freq<0) chan[i].freq=0; + //if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff; + + if (chan[i].keyOn) + { + rWrite(i*SID3_REGISTERS_PER_CHANNEL,SID3_CHAN_ENABLE_GATE); + } + if (chan[i].keyOff) + { + rWrite(i*SID3_REGISTERS_PER_CHANNEL,0); + } + + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff; + + //rWrite(i*7,chan[i].freq&0xff); + //rWrite(i*7+1,chan[i].freq>>8); + //rWrite(0x1e, (chan[0].noise_mode) | (chan[1].noise_mode << 2) | (chan[2].noise_mode << 4) | ((chan[0].freq >> 16) << 6) | ((chan[1].freq >> 16) << 7)); + //rWrite(0x1f, (chan[0].mix_mode) | (chan[1].mix_mode << 2) | (chan[2].mix_mode << 4) | ((chan[2].freq >> 16) << 6)); + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } } } @@ -129,8 +158,8 @@ int DivPlatformSID3::dispatch(DivCommand c) { if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) { chan[c.chan].duty=ins->c64.duty; - rWrite(c.chan*7+2,chan[c.chan].duty&0xff); - rWrite(c.chan*7+3,(chan[c.chan].duty>>8) | (chan[c.chan].outVol << 4)); + //rWrite(c.chan*7+2,chan[c.chan].duty&0xff); + //rWrite(c.chan*7+3,(chan[c.chan].duty>>8) | (chan[c.chan].outVol << 4)); } if (chan[c.chan].insChanged) { /*chan[c.chan].wave = (ins->c64.noiseOn << 3) | (ins->c64.pulseOn << 2) | (ins->c64.sawOn << 1) | (int)(ins->c64.triOn); @@ -349,6 +378,11 @@ int DivPlatformSID3::getOutputCount() { return 2; } +bool DivPlatformSID3::getDCOffRequired() +{ + return false; +} + void DivPlatformSID3::poke(unsigned int addr, unsigned short val) { rWrite(addr,val); } diff --git a/src/engine/platform/sid3.h b/src/engine/platform/sid3.h index c1f60024a..f02e8d4b5 100644 --- a/src/engine/platform/sid3.h +++ b/src/engine/platform/sid3.h @@ -95,6 +95,7 @@ class DivPlatformSID3: public DivDispatch { void setFlags(const DivConfig& flags); void notifyInsChange(int ins); float getPostAmp(); + bool getDCOffRequired(); DivMacroInt* getChanMacroInt(int ch); DivChannelModeHints getModeHints(int chan); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/sound/sid3.c b/src/engine/platform/sound/sid3.c index 4617c8cd5..f561b16be 100644 --- a/src/engine/platform/sound/sid3.c +++ b/src/engine/platform/sound/sid3.c @@ -1,10 +1,172 @@ #include "sid3.h" +#include + #define SAFETY_HEADER if(sid3 == NULL) return; +enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; //for envelope + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +//these 4 ones are util only +double square(double x) { + return fmod(x, (2 * M_PI)) >= M_PI ? -1 : 1; +} +double triangle(double x) { + return asin(sin(x)) / (M_PI / 2); +} +double saw(double x) { + return atan(tan(x / 2)) / (M_PI / 2); +} +double rectSquare(double x) { + return square(x) > 0 ? square(x) : 0; +} +//=========================== + +double sinus(double x) { //taken from waveEdit.cpp of Furnace tracker + return sin(x); +} +double rectSin(double x) { + return sin(x) > 0 ? sin(x) : 0; +} +double absSin(double x) { + return fabs(sin(x)); +} + +double quartSin(double x) { + return absSin(x) * rectSquare(2 * x); +} +double squiSin(double x) { + return sin(x) >= 0 ? sin(2 * x) : 0; +} +double squiAbsSin(double x) { + return fabs(squiSin(x)); +} + +double rectSaw(double x) { + return saw(x) > 0 ? saw(x) : 0; +} +double absSaw(double x) { + return saw(x) < 0 ? saw(x) + 1 : saw(x); +} + + +double cubSaw(double x) { + return pow(saw(x), 3); +} +double rectCubSaw(double x) { + return pow(rectSaw(x), 3); +} +double absCubSaw(double x) { + return pow(absSaw(x), 3); +} + +double cubSine(double x) { + return pow(sin(x), 3); +} +double rectCubSin(double x) { + return pow(rectSin(x), 3); +} +double absCubSin(double x) { + return pow(absSin(x), 3); +} +double quartCubSin(double x) { + return pow(quartSin(x), 3); +} +double squishCubSin(double x) { + return pow(squiSin(x), 3); +} +double squishAbsCubSin(double x) { + return pow(squiAbsSin(x), 3); +} + +double rectTri(double x) { + return triangle(x) > 0 ? triangle(x) : 0; +} +double absTri(double x) { + return fabs(triangle(x)); +} +double quartTri(double x) { + return absTri(x) * rectSquare(2 * x); +} +double squiTri(double x) { + return sin(x) >= 0 ? triangle(2 * x) : 0; +} +double absSquiTri(double x) { + return fabs(squiTri(x)); +} + +double cubTriangle(double x) { + return pow(triangle(x), 3); +} +double cubRectTri(double x) { + return pow(rectTri(x), 3); +} +double cubAbsTri(double x) { + return pow(absTri(x), 3); +} +double cubQuartTri(double x) { + return pow(quartTri(x), 3); +} +double cubSquiTri(double x) { + return pow(squiTri(x), 3); +} +double absCubSquiTri(double x) { + return fabs(cubSquiTri(x)); +} + +typedef double (*WaveFunc) (double a); + +WaveFunc waveFuncs[]={ + sinus, + rectSin, + absSin, + quartSin, + squiSin, + squiAbsSin, + + rectSaw, + absSaw, + + cubSaw, + rectCubSaw, + absCubSaw, + + cubSine, + rectCubSin, + absCubSin, + quartCubSin, + squishCubSin, + squishAbsCubSin, + + rectTri, + absTri, + quartTri, + squiTri, + absSquiTri, + + cubTriangle, + cubRectTri, + cubAbsTri, + cubQuartTri, + cubSquiTri, + absCubSquiTri +}; + SID3* sid3_create() { SID3* sid3 = (SID3*)malloc(sizeof(SID3)); + + for(int i = 0; i < SID3_NUM_SPECIAL_WAVES; i++) + { + for(int j = 0; j < SID3_SPECIAL_WAVE_LENGTH; j++) + { + sid3->special_waves[i][j] = waveFuncs[i]((double)j / (double)SID3_SPECIAL_WAVE_LENGTH); + } + } + return sid3; } @@ -12,17 +174,310 @@ void sid3_reset(SID3* sid3) { SAFETY_HEADER - memset(sid3, 0, sizeof(SID3)); + for(int 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].adsr.s = 0x80; + sid3->chan[i].adsr.r = 0x07; + sid3->chan[i].adsr.vol = 0x20; + //.... + } + + //TODO: wavetable chan +} + +void sid3_gate_bit(uint8_t gate_next, 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) + { + adsr->state = ATTACK; + adsr->rate_period = adsr->a << 8; //todo: make it properly + + // 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 + } + } + // Gate bit off: Start release. + else if (gate && !gate_next) + { + adsr->state = RELEASE; + adsr->rate_period = adsr->r << 8; //todo: make it properly + + adsr->rate_counter = 0; + adsr->exponential_counter = 0; + } + + //gate = gate_next; +} + +void sid3_adsr_clock(sid3_channel_adsr* adsr) +{ + if(adsr->rate_counter < adsr->rate_period) + { + adsr->rate_counter++; + } + + if(adsr->rate_counter > adsr->rate_period) + { + 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) { + 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) + { + adsr->exponential_counter = 0; + + // Check whether the envelope counter is frozen at zero. + if (adsr->hold_zero) + { + return; + } + + 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) + { + adsr->state = DECAY_SUSTAIN; + adsr->rate_period = (adsr->d << 8); //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; + } + } +} + +int sid3_adsr_output(sid3_channel_adsr* adsr, int input) +{ + return (int)(input * ((int64_t)adsr->envelope_counter * (int64_t)adsr->vol / (int64_t)SID3_MAX_VOL)); } void sid3_write(SID3* sid3, uint8_t address, uint8_t data) { SAFETY_HEADER + + uint8_t channel = address / SID3_REGISTERS_PER_CHANNEL; + + if(channel >= SID3_NUM_CHANNELS) return; + + switch(address % SID3_REGISTERS_PER_CHANNEL) //NB: only works if registers for each channel are the same and their addresses + //are x + y*n, where x is address of channel 0 register, y is number of current channel and n is how many registers you have + //for each channel + { + case 0: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + 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); + } + else + { + 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); + } + break; + } + case 1: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].adsr.a = data; + } + else + { + sid3->wave_chan.adsr.a = data; + } + break; + } + case 2: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].adsr.d = data; + } + else + { + sid3->wave_chan.adsr.d = data; + } + break; + } + case 3: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].adsr.s = data; + } + else + { + sid3->wave_chan.adsr.s = data; + } + break; + } + case 4: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].adsr.sr = data; + } + else + { + sid3->wave_chan.adsr.sr = data; + } + break; + } + case 5: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].adsr.r = data; + } + else + { + sid3->wave_chan.adsr.r = data; + } + break; + } + case 6: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].waveform = data; + } + else + { + sid3->wave_chan.mode = data; + } + break; + } + case 7: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].pw &= 0x00ff; + sid3->chan[channel].pw |= data << 8; + } + else + { + sid3->wave_chan.wave_address = data; + } + break; + } + case 8: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].pw &= 0xff00; + sid3->chan[channel].pw |= data; + } + else + { + sid3->wave_chan.wavetable[sid3->wave_chan.wave_address] = data; + } + break; + } + case 9: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].special_wave = data; + } + break; + } + default: break; + } } void sid3_clock(SID3* sid3) { SAFETY_HEADER + + for(int i = 0; i < SID3_NUM_CHANNELS - 1; i++) + { + sid3_adsr_clock(&sid3->chan[i].adsr); + sid3->output_l = sid3_adsr_output(&sid3->chan[i].adsr, 0xff); + sid3->output_r = sid3_adsr_output(&sid3->chan[i].adsr, 0xff); + + sid3->channel_output[i] = sid3_adsr_output(&sid3->chan[i].adsr, 0xff); + } } void sid3_set_is_muted(SID3* sid3, uint8_t ch, bool mute) diff --git a/src/engine/platform/sound/sid3.h b/src/engine/platform/sound/sid3.h index 4be2d8964..78cbd22ea 100644 --- a/src/engine/platform/sound/sid3.h +++ b/src/engine/platform/sound/sid3.h @@ -18,8 +18,38 @@ extern "C" { #define SID3_WAVETABLE_LENGTH 256 -#define SID3_NUM_SINE_WAVES 8 -#define SID3_SINE_WAVE_LENGTH 4096 +#define SID3_NUM_SPECIAL_WAVES 28 +#define SID3_SPECIAL_WAVE_LENGTH 16384 + +enum Flags +{ + SID3_CHAN_ENABLE_GATE = 1, + SID3_CHAN_ENABLE_RING_MOD = 2, + SID3_CHAN_ENABLE_HARD_SYNC = 4, + SID3_CHAN_ENABLE_PHASE_MOD = 8, + SID3_CHAN_PHASE_RESET = 16, + SID3_CHAN_ENV_RESET = 32, + SID3_CHAN_NOISE_PHASE_RESET = 64, +}; + +enum Waveforms +{ + SID3_WAVE_TRIANGLE = 1, + SID3_WAVE_SAW = 2, + SID3_WAVE_PULSE = 4, + SID3_WAVE_NOISE = 8, + SID3_WAVE_SPECIAL = 16, +}; + +enum Mixmodes +{ + SID3_MIX_8580 = 0, + SID3_MIX_8580_WITH_NOISE = 1, + SID3_MIX_AND = 2, + SID3_MIX_OR = 3, + SID3_MIX_XOR = 4, + SID3_MIX_SUM = 5, +}; typedef struct { @@ -38,6 +68,10 @@ typedef struct uint16_t cutoff; uint8_t resonance; uint8_t distortion_level; + + uint8_t mode; + + bool channel_output; //output to the channel master output } sid3_filter; typedef struct @@ -48,7 +82,14 @@ typedef struct typedef struct { - uint8_t a, d, s, sr, r, vol; + uint8_t a, d, s, sr, r, vol, state; + + uint32_t rate_counter; + uint32_t rate_period; + uint16_t exponential_counter; + uint16_t exponential_counter_period; + uint8_t envelope_counter; + bool hold_zero; } sid3_channel_adsr; typedef struct @@ -61,21 +102,24 @@ typedef struct uint32_t lfsr, lfsr_taps; - uint16_t waveform; + uint8_t waveform; + uint8_t special_wave; + uint16_t pw; uint8_t mix_mode; sid3_channel_adsr adsr; - uint16_t flags; + uint8_t flags; + uint8_t ring_mod_src; uint8_t hard_sync_src; uint8_t phase_mod_source; sid3_filters_block filt; - uint8_t panning; + uint8_t panning_left, panning_right; } sid3_channel; typedef struct @@ -86,16 +130,21 @@ typedef struct uint16_t streamed_sample; uint8_t wavetable[SID3_WAVETABLE_LENGTH]; + uint8_t wave_address; + sid3_channel_adsr adsr; + uint8_t flags; + uint8_t mode; + sid3_filters_block filt; - uint8_t panning; + uint8_t panning_left, panning_right; } sid3_wavetable_chan; typedef struct { - uint16_t sine_waves[SID3_NUM_SINE_WAVES][SID3_SINE_WAVE_LENGTH]; //sine wave and OPL3-ish waves? + uint16_t special_waves[SID3_NUM_SPECIAL_WAVES][SID3_SPECIAL_WAVE_LENGTH]; //sine wave and OPL3-ish waves + OPZ and YMF825 waves! sid3_channel chan[SID3_NUM_CHANNELS - 1]; sid3_wavetable_chan wave_chan;