From 3ee761fc878a52618f5fb337ffaff8af166fc127 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 7 Dec 2021 01:23:57 -0500 Subject: [PATCH] C64: finish it all all modules play correctly, bar: - motherfunksignal (almost) - filters are a bit weird --- src/engine/dispatch.h | 10 ++ src/engine/engine.cpp | 2 +- src/engine/macroInt.cpp | 4 +- src/engine/platform/abstract.cpp | 4 + src/engine/platform/c64.cpp | 170 ++++++++++++++++++++++++++----- src/engine/platform/c64.h | 24 ++++- src/engine/platform/nes.cpp | 4 + src/engine/platform/nes.h | 1 + src/engine/platform/pce.cpp | 5 + src/engine/platform/pce.h | 1 + src/engine/platform/sms.cpp | 4 + src/engine/platform/sms.h | 1 + src/engine/playback.cpp | 52 +++++++++- 13 files changed, 246 insertions(+), 36 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index e740552f0..3118eef2f 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -43,6 +43,15 @@ enum DivDispatchCmds { DIV_CMD_PCE_LFO_MODE, DIV_CMD_PCE_LFO_SPEED, + DIV_CMD_C64_CUTOFF, + DIV_CMD_C64_RESONANCE, + DIV_CMD_C64_FILTER_MODE, + DIV_CMD_C64_RESET_TIME, + DIV_CMD_C64_RESET_MASK, + DIV_CMD_C64_FILTER_RESET, + DIV_CMD_C64_DUTY_RESET, + DIV_CMD_C64_EXTENDED, + DIV_ALWAYS_SET_VOLUME, DIV_CMD_MAX @@ -90,6 +99,7 @@ class DivDispatch { virtual void tick(); virtual bool isStereo(); + virtual bool keyOffAffectsArp(); /** * initialize this DivDispatch. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 6d663664e..a697ca594 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -484,8 +484,8 @@ bool DivEngine::load(void* f, size_t slen) { ins->c64.res=reader.readC(); ins->c64.cut=reader.readC(); ins->c64.hp=reader.readC(); - ins->c64.lp=reader.readC(); ins->c64.bp=reader.readC(); + ins->c64.lp=reader.readC(); ins->c64.ch3off=reader.readC(); } diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index ffeef00ce..c401656dd 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -10,8 +10,8 @@ void DivMacroInt::next() { hadVol=hasVol; if (hasVol) { vol=ins->std.volMacro[volPos++]; - if (volPos>=ins->std.volMacroLen && ins->std.volMacroLoopstd.volMacroLen) { - if (ins->std.volMacroLoop>=0) { + if (volPos>=ins->std.volMacroLen) { + if (ins->std.volMacroLoopstd.volMacroLen && ins->std.volMacroLoop>=0) { volPos=ins->std.volMacroLoop; } else { hasVol=false; diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 4086edb00..b716c77b2 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -14,6 +14,10 @@ bool DivDispatch::isStereo() { return false; } +bool DivDispatch::keyOffAffectsArp() { + return false; +} + int DivDispatch::init(DivEngine* p, int channels, int sugRate) { return 0; } diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 7cc8c3b53..b4edc8262 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -11,13 +11,27 @@ void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +void DivPlatformC64::updateFilter() { + sid.write(0x15,filtCut&7); + sid.write(0x16,filtCut>>3); + sid.write(0x17,(filtRes<<4)|(chan[2].filter<<2)|(chan[1].filter<<1)|(chan[0].filter)); + sid.write(0x18,(filtControl<<4)|vol); +} + void DivPlatformC64::tick() { for (int i=0; i<3; i++) { chan[i].std.next(); if (chan[i].std.hadVol) { - // ok, why are the volumes like that? - chan[i].outVol=chan[i].std.vol-(15-chan[i].vol); - if (chan[i].outVol<0) chan[i].outVol=0; + DivInstrument* ins=parent->getIns(chan[i].ins); + if (ins->c64.volIsCutoff) { + filtCut-=((signed char)chan[i].std.vol-18)*7; + if (filtCut>2047) filtCut=2047; + if (filtCut<0) filtCut=0; + updateFilter(); + } else { + vol=chan[i].std.vol; + updateFilter(); + } } if (chan[i].std.hadArp) { if (i==3) { // noise @@ -33,7 +47,7 @@ void DivPlatformC64::tick() { if (chan[i].std.arpMode) { chan[i].baseFreq=round(FREQ_BASE*pow(2.0f,((float)(chan[i].std.arp)/12.0f))); } else { - chan[i].baseFreq=round(FREQ_BASE*pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f))); + chan[i].baseFreq=round(FREQ_BASE*pow(2.0f,((float)(chan[i].note+(signed char)chan[i].std.arp-12)/12.0f))); } } } @@ -49,30 +63,33 @@ void DivPlatformC64::tick() { sid.write(i*7+2,chan[i].duty&0xff); sid.write(i*7+3,chan[i].duty>>8); } - if (chan[i].std.hadWave) { - chan[i].wave=chan[i].std.wave; - sid.write(i*7+4,(chan[i].wave<<4)|chan[i].active); - } if (chan[i].testWhen>0) { if (--chan[i].testWhen<1) { - sid.write(i*7+5,0); - sid.write(i*7+6,0); - sid.write(i*7+4,(chan[i].wave<<4)|8); + if (!chan[i].resetMask) { + sid.write(i*7+5,0); + sid.write(i*7+6,0); + sid.write(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + } } } + if (chan[i].std.hadWave) { + chan[i].wave=chan[i].std.wave; + sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|chan[i].active); + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { DivInstrument* ins=parent->getIns(chan[i].ins); chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE; if (chan[i].freq>0xffff) chan[i].freq=0xffff; + if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { - sid.write(i*7+5,(ins->c64.a<<4)|(ins->c64.d)); - sid.write(i*7+6,(ins->c64.s<<4)|(ins->c64.r)); - sid.write(i*7+4,(chan[i].wave<<4)|1); + sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); + sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); + sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); } if (chan[i].keyOff) { - sid.write(i*7+5,(ins->c64.a<<4)|(ins->c64.d)); - sid.write(i*7+6,(ins->c64.s<<4)|(ins->c64.r)); - sid.write(i*7+4,(chan[i].wave<<4)|0); + sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); + sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); + sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); } sid.write(i*7,chan[i].freq&0xff); sid.write(i*7+1,chan[i].freq>>8); @@ -92,20 +109,43 @@ int DivPlatformC64::dispatch(DivCommand c) { chan[c.chan].note=c.value; chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].duty=(ins->c64.duty*4095)/100; - sid.write(c.chan*7+2,chan[c.chan].duty&0xff); - sid.write(c.chan*7+3,chan[c.chan].duty>>8); - chan[c.chan].wave=(ins->c64.noiseOn<<3)|(ins->c64.pulseOn<<2)|(ins->c64.sawOn<<1)|(ins->c64.triOn); + if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacroLen>0) { + chan[c.chan].duty=(ins->c64.duty*4095)/100; + sid.write(c.chan*7+2,chan[c.chan].duty&0xff); + sid.write(c.chan*7+3,chan[c.chan].duty>>8); + } + if (chan[c.chan].insChanged) { + chan[c.chan].wave=(ins->c64.noiseOn<<3)|(ins->c64.pulseOn<<2)|(ins->c64.sawOn<<1)|(ins->c64.triOn); + chan[c.chan].attack=ins->c64.a; + chan[c.chan].decay=ins->c64.d; + chan[c.chan].sustain=ins->c64.s; + chan[c.chan].release=ins->c64.r; + chan[c.chan].ring=ins->c64.ringMod; + chan[c.chan].sync=ins->c64.oscSync; + } + if (chan[c.chan].insChanged || chan[c.chan].resetFilter) { + chan[c.chan].filter=ins->c64.toFilter; + if (ins->c64.initFilter) { + filtCut=ins->c64.cut*2047/100; + filtRes=ins->c64.res; + filtControl=ins->c64.lp|(ins->c64.bp<<1)|(ins->c64.hp<<2)|(ins->c64.ch3off<<3); + updateFilter(); + } + } + if (chan[c.chan].insChanged) { + chan[c.chan].insChanged=false; + } chan[c.chan].std.init(ins); break; } case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + //chan[c.chan].std.init(NULL); break; case DIV_CMD_INSTRUMENT: if (chan[c.chan].ins!=c.value) { + chan[c.chan].insChanged=true; chan[c.chan].ins=c.value; } break; @@ -114,10 +154,11 @@ int DivPlatformC64::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.hasVol) { chan[c.chan].outVol=c.value; - } - if (c.chan==2) { + vol=chan[c.chan].outVol; } else { + vol=chan[c.chan].vol; } + updateFilter(); } break; case DIV_CMD_GET_VOLUME: @@ -151,7 +192,13 @@ int DivPlatformC64::dispatch(DivCommand c) { break; } case DIV_CMD_STD_NOISE_MODE: - chan[c.chan].duty=c.value; + chan[c.chan].duty=(c.value*4095)/100; + sid.write(c.chan*7+2,chan[c.chan].duty&0xff); + sid.write(c.chan*7+3,chan[c.chan].duty>>8); + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active); break; case DIV_CMD_LEGATO: chan[c.chan].baseFreq=round(FREQ_BASE*pow(2.0f,((float)c.value/12.0f))); @@ -164,11 +211,76 @@ int DivPlatformC64::dispatch(DivCommand c) { chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: - chan[c.chan].testWhen=c.value; + if (resetTime) chan[c.chan].testWhen=c.value-resetTime+1; break; case DIV_CMD_GET_VOLMAX: return 15; break; + case DIV_CMD_C64_CUTOFF: + if (c.value>100) c.value=100; + filtCut=c.value*2047/100; + updateFilter(); + break; + case DIV_CMD_C64_RESONANCE: + if (c.value>15) c.value=15; + filtRes=c.value; + updateFilter(); + break; + case DIV_CMD_C64_FILTER_MODE: + filtControl=c.value&7; + updateFilter(); + break; + case DIV_CMD_C64_RESET_TIME: + resetTime=c.value; + break; + case DIV_CMD_C64_RESET_MASK: + chan[c.chan].resetMask=c.value; + break; + case DIV_CMD_C64_FILTER_RESET: + if (c.value&15) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + filtCut=ins->c64.cut*2047/100; + updateFilter(); + } + chan[c.chan].resetFilter=c.value>>4; + break; + case DIV_CMD_C64_DUTY_RESET: + if (c.value&15) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + chan[c.chan].duty=(ins->c64.duty*4095)/100; + sid.write(c.chan*7+2,chan[c.chan].duty&0xff); + sid.write(c.chan*7+3,chan[c.chan].duty>>8); + } + chan[c.chan].resetDuty=c.value>>4; + break; + case DIV_CMD_C64_EXTENDED: + switch (c.value>>4) { + case 0: + chan[c.chan].attack=c.value&15; + break; + case 1: + chan[c.chan].decay=c.value&15; + break; + case 2: + chan[c.chan].sustain=c.value&15; + break; + case 3: + chan[c.chan].release=c.value&15; + break; + case 4: + chan[c.chan].ring=c.value; + sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active); + break; + case 5: + chan[c.chan].sync=c.value; + sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active); + break; + case 6: + filtControl&=7; + filtControl|=(!!c.value)<<3; + break; + } + break; case DIV_ALWAYS_SET_VOLUME: return 1; break; @@ -194,5 +306,11 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate) { sid.write(0x18,0x0f); + filtControl=0; + filtRes=0; + filtCut=0; + resetTime=1; + vol=15; + return 3; } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 39b2aeb30..ac939263a 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -7,10 +7,11 @@ class DivPlatformC64: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, testWhen; - unsigned char ins, note, sweep, wave; + int freq, baseFreq, pitch, prevFreq, testWhen, note; + unsigned char ins, sweep, wave, attack, decay, sustain, release; short duty; - bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta; + bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; + bool resetMask, resetFilter, resetDuty, ring, sync; signed char vol, outVol; DivMacroInt std; Channel(): @@ -19,10 +20,14 @@ class DivPlatformC64: public DivDispatch { pitch(0), prevFreq(65535), testWhen(0), - ins(-1), note(0), + ins(-1), sweep(0), wave(0), + attack(0), + decay(0), + sustain(0), + release(0), duty(0), active(false), insChanged(true), @@ -31,13 +36,22 @@ class DivPlatformC64: public DivDispatch { keyOn(false), keyOff(false), inPorta(false), + filter(false), + resetMask(false), + resetFilter(false), + resetDuty(false), + ring(false), + sync(false), vol(15) {} }; Channel chan[3]; + unsigned char filtControl, filtRes, vol; + int filtCut, resetTime; + SID sid; - void updateWave(); + void updateFilter(); public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 1a256fe4e..e015c2432 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -270,6 +270,10 @@ int DivPlatformNES::dispatch(DivCommand c) { return 1; } +bool DivPlatformNES::keyOffAffectsArp() { + return true; +} + int DivPlatformNES::init(DivEngine* p, int channels, int sugRate) { parent=p; rate=1789773; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index db4a0ef68..d0c858d5d 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -39,6 +39,7 @@ class DivPlatformNES: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void tick(); + bool keyOffAffectsArp(); int init(DivEngine* parent, int channels, int sugRate); }; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 883e41a48..3def20729 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -254,6 +254,11 @@ bool DivPlatformPCE::isStereo() { return true; } +bool DivPlatformPCE::keyOffAffectsArp() { + return true; +} + + int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate) { parent=p; rate=1789773; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 8ed9d8a7a..d9c265475 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -54,6 +54,7 @@ class DivPlatformPCE: public DivDispatch { int dispatch(DivCommand c); void tick(); bool isStereo(); + bool keyOffAffectsArp(); int init(DivEngine* parent, int channels, int sugRate); }; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 6abbbc20c..1748c5836 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -159,6 +159,10 @@ int DivPlatformSMS::dispatch(DivCommand c) { return 1; } +bool DivPlatformSMS::keyOffAffectsArp() { + return true; +} + int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate) { parent=p; rate=223722; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 33d4a9c40..6bb0ffb53 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -34,6 +34,7 @@ class DivPlatformSMS: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void tick(); + bool keyOffAffectsArp(); int init(DivEngine* parent, int channels, int sugRate); }; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index a60320cf7..de0bf1d7f 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1,3 +1,4 @@ +#include "dispatch.h" #include "engine.h" void DivEngine::nextOrder() { @@ -49,6 +50,15 @@ const char* cmdName[DIV_CMD_MAX]={ "PCE_LFO_MODE", "PCE_LFO_SPEED", + "C64_CUTOFF", + "C64_RESONANCE", + "C64_FILTER_MODE", + "C64_RESET_TIME", + "C64_RESET_MASK", + "C64_FILTER_RESET", + "C64_DUTY_RESET", + "C64_EXTENDED", + "ALWAYS_SET_VOLUME" }; @@ -199,6 +209,42 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char return false; } break; + case DIV_SYSTEM_C64_6581: + case DIV_SYSTEM_C64_8580: + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // cutoff + dispatchCmd(DivCommand(DIV_CMD_C64_CUTOFF,ch,effectVal)); + break; + case 0x12: // duty + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x13: // resonance + dispatchCmd(DivCommand(DIV_CMD_C64_RESONANCE,ch,effectVal)); + break; + case 0x14: // filter mode + dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_MODE,ch,effectVal)); + break; + case 0x15: // reset time + dispatchCmd(DivCommand(DIV_CMD_C64_RESET_TIME,ch,effectVal)); + break; + case 0x1a: // reset mask + dispatchCmd(DivCommand(DIV_CMD_C64_RESET_MASK,ch,effectVal)); + break; + case 0x1b: // cutoff reset + dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_RESET,ch,effectVal)); + break; + case 0x1c: // duty reset + dispatchCmd(DivCommand(DIV_CMD_C64_DUTY_RESET,ch,effectVal)); + break; + case 0x1e: // extended + dispatchCmd(DivCommand(DIV_CMD_C64_EXTENDED,ch,effectVal)); + break; + default: + return false; + } default: return false; } @@ -229,7 +275,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } // note if (pat->data[whatRow][0]==100) { - chan[i].note=-1; + //chan[i].note=-1; chan[i].keyOn=false; if (chan[i].stopOnOff) { chan[i].portaNote=-1; @@ -240,7 +286,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else if (!(pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0)) { chan[i].note=pat->data[whatRow][0]+pat->data[whatRow][1]*12; if (!chan[i].keyOn) { - chan[i].arp=0; + if (dispatch->keyOffAffectsArp()) { + chan[i].arp=0; + } } chan[i].doNote=true; if (chan[i].arp!=0) {