From dbc2e6285fa6a3b1158b2329c46853ef166cd4c6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 16 May 2021 03:03:23 -0500 Subject: [PATCH] a lot of work - round to nearest instead of round to zero in SMS period calc - implement arpeggio - implement arp macro (kinda) - polish the SMS platform - correct the DIV_CMD_PITCH range to 1 semitone - fix PSG volume in Genesis - use a better register write strat in Genesis - fix a bug caused by legacy code - implement ECxx command - implement EDxx command - implement SN noise mode command - vibrato table is now 64 positions long (instead of 60) --- src/engine/dispatch.h | 4 +- src/engine/engine.cpp | 4 +- src/engine/engine.h | 12 +- src/engine/platform/abstract.cpp | 2 +- src/engine/platform/dummy.cpp | 2 +- src/engine/platform/dummy.h | 2 +- src/engine/platform/genesis.cpp | 50 +++-- src/engine/platform/genesis.h | 5 +- src/engine/platform/sms.cpp | 39 +++- src/engine/platform/sms.h | 2 +- src/engine/playback.cpp | 310 ++++++++++++++++++------------- src/main.cpp | 5 + 12 files changed, 264 insertions(+), 173 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 37a48d531..8fa679a8a 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -1,6 +1,8 @@ #ifndef _DISPATCH_H #define _DISPATCH_H +#define ONE_SEMITONE 2200 + enum DivDispatchCmds { DIV_CMD_NOTE_ON=0, DIV_CMD_NOTE_OFF, @@ -64,7 +66,7 @@ class DivDispatch { * the engine shall resample to the output rate. */ int rate; - virtual void acquire(short& l, short& r); + virtual void acquire(int& l, int& r); virtual int dispatch(DivCommand c); virtual void tick(); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index e7ab2165f..650afb419 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -668,8 +668,8 @@ bool DivEngine::init() { bbOut[0]=new short[got.bufsize]; bbOut[1]=new short[got.bufsize]; - for (int i=0; i<60; i++) { - vibTable[i]=127*sin(((double)i/60.0)*(2*M_PI)); + for (int i=0; i<64; i++) { + vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); } switch (song.system) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 57b3c8457..d31653603 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -8,9 +8,10 @@ struct DivChannelState { std::vector delayed; int note, pitch, portaSpeed, portaNote; - int volume, volSpeed; + int volume, volSpeed, cut, rowDelay; int vibratoDepth, vibratoRate, vibratoPos; int tremoloDepth, tremoloRate, tremoloPos; + unsigned char arp, arpStage; bool doNote, legato; DivChannelState(): @@ -20,12 +21,16 @@ struct DivChannelState { portaNote(-1), volume(0x7f00), volSpeed(0), + cut(-1), + rowDelay(0), vibratoDepth(0), vibratoRate(0), vibratoPos(0), tremoloDepth(0), tremoloRate(0), tremoloPos(0), + arp(0), + arpStage(-1), doNote(false), legato(false) {} }; @@ -40,12 +45,13 @@ class DivEngine { int changeOrd, changePos; DivChannelState chan[17]; - short vibTable[60]; + short vibTable[64]; blip_buffer_t* bb[2]; - short temp[2], prevSample[2]; + int temp[2], prevSample[2]; short* bbOut[2]; + void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); void nextTick(); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 7153ac17e..344e1c230 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -1,6 +1,6 @@ #include "../dispatch.h" -void DivDispatch::acquire(short& l, short& r) { +void DivDispatch::acquire(int& l, int& r) { l=0; r=0; } diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 13506dcd3..e2ab7c226 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -1,7 +1,7 @@ #include "dummy.h" #include -void DivPlatformDummy::acquire(short& l, short& r) { +void DivPlatformDummy::acquire(int& l, int& r) { l=0; for (unsigned char i=0; i=rate) { psg.acquire(psgOut,psgOut); psgClocks-=rate; - psgOut>>=2; + psgOut=(psgOut>>2)+(psgOut>>3); } l=(o[0]<<7)+psgOut; @@ -89,9 +89,9 @@ void DivPlatformGenesis::tick() { } for (int i=0; i<512; i++) { - if (pendingWrites[i]!=-1) { + if (pendingWrites[i]!=oldWrites[i]) { writes.emplace(i,pendingWrites[i]&0xff); - pendingWrites[i]=-1; + oldWrites[i]=pendingWrites[i]; } } @@ -156,28 +156,25 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } DivInstrument* ins=parent->song.ins[chan[c.chan].ins]; - if (chan[c.chan].insChanged) { - chan[c.chan].insChanged=false; - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator op=ins->fm.op[i]; - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - if (isOutput[ins->fm.alg][i]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*chan[c.chan].vol)/127)); - } else { - rWrite(baseAddr+0x40,op.tl); - } - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + DivInstrumentFM::Operator op=ins->fm.op[i]; + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + if (isOutput[ins->fm.alg][i]) { + rWrite(baseAddr+0x40,127-(((127-op.tl)*chan[c.chan].vol)/127)); + } else { + rWrite(baseAddr+0x40,op.tl); } - rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); - rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); } + rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); + rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); - chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freq=(chan[c.chan].baseFreq*(ONE_SEMITONE+chan[c.chan].pitch))/ONE_SEMITONE; chan[c.chan].freqChanged=true; chan[c.chan].keyOn=true; chan[c.chan].active=true; @@ -225,7 +222,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } case DIV_CMD_PITCH: { chan[c.chan].pitch=c.value; - chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freq=(chan[c.chan].baseFreq*(ONE_SEMITONE+chan[c.chan].pitch))/ONE_SEMITONE; chan[c.chan].freqChanged=true; break; } @@ -245,7 +242,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { return2=true; } } - chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freq=(chan[c.chan].baseFreq*(ONE_SEMITONE+chan[c.chan].pitch))/ONE_SEMITONE; chan[c.chan].freqChanged=true; if (return2) return 2; break; @@ -259,7 +256,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); - chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freq=(chan[c.chan].baseFreq*(ONE_SEMITONE+chan[c.chan].pitch))/ONE_SEMITONE; chan[c.chan].freqChanged=true; break; } @@ -312,6 +309,7 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate) { } for (int i=0; i<512; i++) { + oldWrites[i]=-1; pendingWrites[i]=-1; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index d5c2c254a..9577c92d0 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -26,7 +26,7 @@ class DivPlatformGenesis: public DivDispatch { ym3438_t fm; DivPlatformSMS psg; int psgClocks; - short psgOut; + int psgOut; int delay; unsigned char lastBusy; @@ -36,10 +36,11 @@ class DivPlatformGenesis: public DivDispatch { int dacPos; int dacSample; + short oldWrites[512]; short pendingWrites[512]; public: - void acquire(short& l, short& r); + void acquire(int& l, int& r); int dispatch(DivCommand c); void tick(); int init(DivEngine* parent, int channels, int sugRate); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index af030eb37..957dd0a33 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -2,7 +2,7 @@ #include "../engine.h" #include -void DivPlatformSMS::acquire(short& l, short& r) { +void DivPlatformSMS::acquire(int& l, int& r) { short v; sn->sound_stream_update(&v,1); l=v; @@ -13,10 +13,14 @@ void DivPlatformSMS::tick() { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.hadVol) sn->write(0x90|(i<<5)|(15-((chan[i].vol*chan[i].std.vol)>>4))); + if (chan[i].std.hadArp) { + chan[i].baseFreq=round(1712.0f/pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f))); + chan[i].freqChanged=true; + } } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=(chan[i].baseFreq*(2048-chan[i].pitch))>>11; + chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE-chan[i].pitch))/ONE_SEMITONE; sn->write(0x80|i<<5|(chan[i].freq&15)); sn->write(chan[i].freq>>4); chan[i].freqChanged=false; @@ -24,7 +28,7 @@ void DivPlatformSMS::tick() { } if (chan[3].freqChanged || updateSNMode) { updateSNMode=false; - chan[3].freq=(chan[3].baseFreq*(2048-chan[3].pitch))>>11; + chan[3].freq=(chan[3].baseFreq*(ONE_SEMITONE-chan[3].pitch))/ONE_SEMITONE; chan[3].freqChanged=false; if (snNoiseMode&2) { // take period from channel 3 if (snNoiseMode&1) { @@ -47,7 +51,7 @@ void DivPlatformSMS::tick() { int DivPlatformSMS::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: - chan[c.chan].baseFreq=1712/pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=round(1712.0f/pow(2.0f,((float)c.value/12.0f))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; chan[c.chan].active=true; @@ -61,7 +65,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - chan[c.chan].std.init(parent->song.ins[chan[c.chan].ins]); + //chan[c.chan].std.init(parent->song.ins[chan[c.chan].ins]); break; case DIV_CMD_VOLUME: chan[c.chan].vol=c.value; @@ -71,10 +75,35 @@ int DivPlatformSMS::dispatch(DivCommand c) { chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=round(1712.0f/pow(2.0f,((float)c.value2/12.0f))); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) return 2; + break; + } case DIV_CMD_STD_NOISE_MODE: snNoiseMode=(c.value&1)|((c.value&16)>>3); updateSNMode=true; break; + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=round(1712.0f/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; default: break; } diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 393f4cb0e..132e4b59b 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -19,7 +19,7 @@ class DivPlatformSMS: public DivDispatch { bool updateSNMode; sn76496_device* sn; public: - void acquire(short& l, short& r); + void acquire(int& l, int& r); int dispatch(DivCommand c); void tick(); int init(DivEngine* parent, int channels, int sugRate); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 6fcaa530b..9a629245b 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -29,6 +29,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case 0x17: // DAC enable dispatch->dispatch(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); break; + case 0x20: // SN noise mode + dispatch->dispatch(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; default: return false; } @@ -100,6 +103,155 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char return true; } +void DivEngine::processRow(int i, bool afterDelay) { + DivPattern* pat=song.pat[i]->data[curOrder]; + // pre effects + if (!afterDelay) for (int j=0; jeffectRows; j++) { + short effect=pat->data[curRow][4+(j<<1)]; + short effectVal=pat->data[curRow][5+(j<<1)]; + + if (effectVal==-1) effectVal=0; + if (effect==0xed) { + chan[i].rowDelay=effectVal+1; + return; + } + } + + // instrument + if (pat->data[curRow][2]!=-1) { + dispatch->dispatch(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[curRow][2])); + } + // note + if (pat->data[curRow][0]==100) { + chan[i].note=-1; + dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i)); + } else if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { + chan[i].note=pat->data[curRow][0]+pat->data[curRow][1]*12; + chan[i].doNote=true; + } + + // volume + if (pat->data[curRow][3]!=-1) { + chan[i].volume=pat->data[curRow][3]<<8; + dispatch->dispatch(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + } + + // effects + for (int j=0; jeffectRows; j++) { + short effect=pat->data[curRow][4+(j<<1)]; + short effectVal=pat->data[curRow][5+(j<<1)]; + + if (effectVal==-1) effectVal=0; + + // per-system effect + if (!perSystemEffect(i,effect,effectVal)) switch (effect) { + case 0x09: // speed 1 + song.speed1=effectVal; + break; + case 0x0f: // speed 2 + song.speed2=effectVal; + break; + case 0x0b: // change order + changeOrd=effectVal; + changePos=0; + break; + case 0x0d: // next order + changeOrd=curOrder+1; + changePos=effectVal; + break; + case 0x08: // panning + dispatch->dispatch(DivCommand(DIV_CMD_PANNING,i,effectVal)); + break; + case 0x01: // ramp up + if (effectVal==0) { + chan[i].portaNote=-1; + chan[i].portaSpeed=-1; + } else { + chan[i].portaNote=0x60; + chan[i].portaSpeed=effectVal; + } + break; + case 0x02: // ramp down + if (effectVal==0) { + chan[i].portaNote=-1; + chan[i].portaSpeed=-1; + } else { + chan[i].portaNote=0x00; + chan[i].portaSpeed=effectVal; + } + break; + case 0x03: // portamento + if (effectVal==0) { + chan[i].portaNote=-1; + chan[i].portaSpeed=-1; + } else { + chan[i].portaNote=chan[i].note; + chan[i].portaSpeed=effectVal; + chan[i].doNote=false; + } + break; + case 0x04: // vibrato + chan[i].vibratoDepth=effectVal&15; + chan[i].vibratoRate=effectVal>>4; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4))); + break; + case 0x0a: // volume ramp + if (effectVal!=0) { + if ((effectVal&15)!=0) { + chan[i].volSpeed=-(effectVal&15)*64; + } else { + chan[i].volSpeed=(effectVal>>4)*64; + } + } else { + chan[i].volSpeed=0; + } + break; + case 0x00: // arpeggio + chan[i].arp=effectVal; + break; + + case 0xe1: // portamento up + chan[i].portaNote=chan[i].note+(effectVal&15); + chan[i].portaSpeed=(effectVal>>4)*3; + break; + case 0xe2: // portamento down + chan[i].portaNote=chan[i].note-(effectVal&15); + chan[i].portaSpeed=(effectVal>>4)*3; + break; + case 0xe5: // pitch + chan[i].pitch=effectVal-0x80; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>2))); + break; + case 0xea: // legato mode + chan[i].legato=effectVal; + break; + case 0xec: // delayed note cut + chan[i].cut=effectVal; + break; + } + } + + if (chan[i].doNote) { + chan[i].vibratoPos=0; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4))); + if (chan[i].legato) { + dispatch->dispatch(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + } else { + dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note)); + } + chan[i].doNote=false; + } + + // post effects + for (int j=0; jeffectRows; j++) { + short effect=pat->data[curRow][4+(j<<1)]; + short effectVal=pat->data[curRow][5+(j<<1)]; + + if (effectVal==-1) effectVal=0; + perSystemPostEffect(i,effect,effectVal); + } +} + void DivEngine::nextRow() { static char pb[4096]; static char pb1[4096]; @@ -156,135 +308,7 @@ void DivEngine::nextRow() { printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3); for (int i=0; idata[curOrder]; - - // instrument - if (pat->data[curRow][2]!=-1) { - dispatch->dispatch(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[curRow][2])); - } - // note - if (pat->data[curRow][0]==100) { - chan[i].note=-1; - dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i)); - } else if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { - chan[i].note=pat->data[curRow][0]+pat->data[curRow][1]*12; - chan[i].doNote=true; - } - - // volume - if (pat->data[curRow][3]!=-1) { - chan[i].volume=pat->data[curRow][3]<<8; - dispatch->dispatch(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); - } - - // effects - for (int j=0; jeffectRows; j++) { - short effect=pat->data[curRow][4+(j<<1)]; - short effectVal=pat->data[curRow][5+(j<<1)]; - - if (effectVal==-1) effectVal=0; - - // per-system effect - if (!perSystemEffect(i,effect,effectVal)) switch (effect) { - case 0x09: // speed 1 - song.speed1=effectVal; - break; - case 0x0f: // speed 2 - song.speed2=effectVal; - break; - case 0x0b: // change order - changeOrd=effectVal; - changePos=0; - break; - case 0x0d: // next order - changeOrd=curOrder+1; - changePos=effectVal; - break; - case 0x08: // panning - dispatch->dispatch(DivCommand(DIV_CMD_PANNING,i,effectVal)); - break; - case 0x01: // ramp up - if (effectVal==0) { - chan[i].portaNote=-1; - chan[i].portaSpeed=-1; - } else { - chan[i].portaNote=0x60; - chan[i].portaSpeed=effectVal; - } - break; - case 0x02: // ramp down - if (effectVal==0) { - chan[i].portaNote=-1; - chan[i].portaSpeed=-1; - } else { - chan[i].portaNote=0x00; - chan[i].portaSpeed=effectVal; - } - break; - case 0x03: // portamento - if (effectVal==0) { - chan[i].portaNote=-1; - chan[i].portaSpeed=-1; - } else { - chan[i].portaNote=chan[i].note; - chan[i].portaSpeed=effectVal; - chan[i].doNote=false; - } - break; - case 0x04: // vibrato - chan[i].vibratoDepth=effectVal&15; - chan[i].vibratoRate=effectVal>>4; - dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4))); - break; - case 0x0a: // volume ramp - if (effectVal!=0) { - if ((effectVal&15)!=0) { - chan[i].volSpeed=-(effectVal&15)*64; - } else { - chan[i].volSpeed=(effectVal>>4)*64; - } - } else { - chan[i].volSpeed=0; - } - break; - - case 0xe1: // portamento up - chan[i].portaNote=chan[i].note+(effectVal&15); - chan[i].portaSpeed=(effectVal>>4)*3; - break; - case 0xe2: // portamento down - chan[i].portaNote=chan[i].note-(effectVal&15); - chan[i].portaSpeed=(effectVal>>4)*3; - break; - case 0xe5: // pitch - chan[i].pitch=effectVal-0x80; - dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>2))); - break; - case 0xea: // legato mode - chan[i].legato=effectVal; - break; - } - } - - if (chan[i].doNote) { - chan[i].vibratoPos=0; - dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4))); - if (chan[i].legato) { - dispatch->dispatch(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); - } else { - dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note)); - } - chan[i].doNote=false; - } - - // post effects - for (int j=0; jeffectRows; j++) { - short effect=pat->data[curRow][4+(j<<1)]; - short effectVal=pat->data[curRow][5+(j<<1)]; - - if (effectVal==-1) effectVal=0; - perSystemPostEffect(i,effect,effectVal); - } + processRow(i,false); } } @@ -309,6 +333,11 @@ void DivEngine::nextTick() { } // process stuff for (int i=0; i0) { + if (--chan[i].rowDelay==0) { + processRow(i,true); + } + } if (chan[i].volSpeed!=0) { chan[i].volume+=chan[i].volSpeed; if (chan[i].volume>0x7f00) chan[i].volume=0x7f00; @@ -317,7 +346,7 @@ void DivEngine::nextTick() { } if (chan[i].vibratoDepth>0) { chan[i].vibratoPos+=chan[i].vibratoRate; - if (chan[i].vibratoPos>=60) chan[i].vibratoPos-=60; + if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64; dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4))); } if (chan[i].portaSpeed>0) { @@ -325,6 +354,27 @@ void DivEngine::nextTick() { chan[i].portaSpeed=0; } } + if (chan[i].cut>0) { + if (--chan[i].cut<1) { + chan[i].note=-1; + dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i)); + } + } + if (chan[i].arp!=0) { + chan[i].arpStage++; + if (chan[i].arpStage>2) chan[i].arpStage=0; + switch (chan[i].arpStage) { + case 0: + dispatch->dispatch(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + break; + case 1: + dispatch->dispatch(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4))); + break; + case 2: + dispatch->dispatch(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15))); + break; + } + } } // system tick diff --git a/src/main.cpp b/src/main.cpp index 52f7e28f6..6c493f8e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,11 @@ int main(int argc, char** argv) { return 1; } ssize_t len=ftell(f); + if (len==0x7fffffffffffffff) { + perror("could not get file length"); + fclose(f); + return 1; + } if (len<1) { if (len==0) { printf("that file is empty!\n");