diff --git a/papers/format.md b/papers/format.md index 7ab67800..e6d35e83 100644 --- a/papers/format.md +++ b/papers/format.md @@ -232,6 +232,9 @@ size | description 4f | A-4 tuning 1 | limit slides (>=36) or reserved 1 | linear pitch (>=36) or reserved + | - 0: non-linaer + | - 1: only pitch change (04xy/E5xx) linear + | - 2: full linear (>=94) 1 | loop modality (>=36) or reserved 1 | proper noise layout (>=42) or reserved 1 | wave duty is volume (>=42) or reserved @@ -286,7 +289,8 @@ size | description 1 | weird f-num/block-based chip pitch slides (>=85) or reserved 1 | SN duty macro always resets phase (>=86) or reserved 1 | pitch macro is linear (>=90) or reserved - 19 | reserved + 1 | pitch slide speed in full linear pitch mode (>=94) or reserved + 18 | reserved ``` # instrument diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 10f61c17..83bc56b8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -986,6 +986,9 @@ int DivEngine::calcBaseFreq(double clock, double divider, int note, bool period) }*/ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool period) { + if (song.linearPitch==2) { // full linear + return (note<<7); + } double base=(period?(song.tuning*0.0625):song.tuning)*pow(2.0,(float)(note+3)/12.0); return period? (clock/base)/divider: @@ -993,6 +996,9 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri } unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) { + if (song.linearPitch==2) { // full linear + return (note<<7); + } double tuning=song.tuning; if (tuning<400.0) tuning=400.0; if (tuning>500.0) tuning=500.0; @@ -1018,12 +1024,19 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in bf=(1<dispatch(DivCommand(DIV_CMD_GET_VOLMAX,dispatchChanOfChan[i]))<<8)|0xff; chan[i].volume=chan[i].volMax; - if (!song.linearPitch) chan[i].vibratoFine=4; + if (song.linearPitch==0) chan[i].vibratoFine=4; } extValue=0; extValuePresent=0; diff --git a/src/engine/engine.h b/src/engine/engine.h index 4b5614a6..9ac26d89 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev93" -#define DIV_ENGINE_VERSION 93 +#define DIV_VERSION "dev94" +#define DIV_ENGINE_VERSION 94 // for imports #define DIV_VERSION_MOD 0xff01 @@ -484,7 +484,7 @@ class DivEngine { unsigned short calcBaseFreqFNumBlock(double clock, double divider, int note, int bits); // calculate frequency/period - int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0); + int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0, double clock=1.0, double divider=1.0); // convert panning formats int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 4eb73e7e..e0f20794 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -139,7 +139,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { // compatibility flags ds.limitSlides=true; - ds.linearPitch=true; + ds.linearPitch=1; ds.loopModality=0; ds.properNoiseLayout=false; ds.waveDutyIsVol=false; @@ -950,7 +950,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<37) { // compat flags not stored back then ds.limitSlides=true; - ds.linearPitch=true; + ds.linearPitch=1; ds.loopModality=0; } if (ds.version<43) { @@ -1392,7 +1392,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<19; i++) { + if (ds.version>=94) { + ds.pitchSlideSpeed=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<18; i++) { reader.readC(); } } @@ -1649,7 +1654,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { DivSong ds; ds.tuning=436.0; ds.version=DIV_VERSION_MOD; - ds.linearPitch=false; + ds.linearPitch=0; ds.noSlidesOnFirstTick=true; ds.rowResetsArpPos=true; ds.ignoreJumpAtEnd=false; @@ -2702,7 +2707,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.fbPortaPause); w->writeC(song.snDutyReset); w->writeC(song.pitchMacroIsLinear); - for (int i=0; i<19; i++) { + w->writeC(song.pitchSlideSpeed); + for (int i=0; i<18; i++) { w->writeC(0); } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index e78c3a77..df742d35 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -220,7 +220,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 37108452..dbc5aa82 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -251,7 +251,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].keyOn) { //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 3fec732b..6fb34096 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -280,7 +280,7 @@ void DivPlatformAY8930::tick(bool sysTick) { immWrite(0x1a,ayNoiseOr); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { if (chan[i].insChanged) { diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index b12a92ed..52fb4a50 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -137,7 +137,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC); - chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].pitch2; + chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; k005289->load(i,chan[i].freq); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index c0efdd6f..17b8124d 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -217,7 +217,7 @@ void DivPlatformC64::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 34d614eb..48c33f05 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -61,7 +61,7 @@ void DivPlatformDummy::tick(bool sysTick) { if (chan[i].freqChanged) { chan[i].freqChanged=false; - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,0,chipClock,CHIP_FREQBASE); } } } diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index f3908148..5698f004 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -220,7 +220,7 @@ void DivPlatformFDS::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 1fc2b278..963d2fde 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -237,7 +237,7 @@ void DivPlatformGB::tick(bool sysTick) { if (ntPos>255) ntPos=255; chan[i].freq=noiseTable[ntPos]; } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 09571061..dd5a2be0 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -201,7 +201,7 @@ void DivPlatformLynx::tick(bool sysTick) { WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4)); chan[i].lfsr=-1; } - chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 8446643b..9775e639 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -146,7 +146,7 @@ void DivPlatformMMC5::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index ef368203..9318b4f8 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -357,7 +357,7 @@ void DivPlatformN163::tick(bool sysTick) { chan[i].waveUpdated=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 4b301839..ef3b51f0 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -308,7 +308,7 @@ void DivPlatformNES::tick(bool sysTick) { if (ntPos>252) ntPos=252; chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]); } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index ff31ad40..b95b42ed 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -511,7 +511,7 @@ void DivPlatformOPL::tick(bool sysTick) { bool updateDrums=false; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].freq>131071) chan[i].freq=131071; int freqt=toFreq(chan[i].freq)+chan[i].pitch2; chan[i].freqH=freqt>>8; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index b4f0916a..89b41317 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -300,7 +300,7 @@ void DivPlatformOPLL::tick(bool sysTick) { for (int i=0; i<11; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].pitch2; chan[i].freqL=freqt&0xff; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index c28b81db..9838f987 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -227,7 +227,7 @@ void DivPlatformPCE::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 434ab10b..8ecf6dae 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -223,7 +223,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 8ddee9e8..f2ee6add 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -118,7 +118,7 @@ void DivPlatformPET::tick(bool sysTick) { chan.freqChanged=true; } if (chan.freqChanged || chan.keyOn || chan.keyOff) { - chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2); + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER); if (chan.freq>257) chan.freq=257; if (chan.freq<2) chan.freq=2; rWrite(8,chan.freq-2); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 6e89e5b6..e9b4e8a4 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -24,7 +24,7 @@ #include #define CHIP_DIVIDER (1248*2) -#define QS_NOTE_FREQUENCY(x) parent->calcBaseFreq(440,0x1000,(x)-3,false) +#define QS_NOTE_FREQUENCY(x) parent->calcBaseFreq(440,4096,(x)-3,false) #define rWrite(a,v) {if(!skipRegisterWrites) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v); }} #define immWrite(a,v) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v);} @@ -296,14 +296,8 @@ void DivPlatformQSound::tick(bool sysTick) { uint16_t qsound_addr = 0; uint16_t qsound_loop = 0; uint16_t qsound_end = 0; - double off=1.0; if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[i].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=(double)s->centerRate/24038.0/16.0; - } qsound_bank = 0x8000 | (s->offQSound >> 16); qsound_addr = s->offQSound & 0xffff; @@ -322,15 +316,15 @@ void DivPlatformQSound::tick(bool sysTick) { if (chan[i].std.arp.had) { if (!chan[i].inPorta) { if (chan[i].std.arp.mode) { - chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].std.arp.val); + chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].std.arp.val); } else { - chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); + chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); } } chan[i].freqChanged=true; } else { if (chan[i].std.arp.mode && chan[i].std.arp.finished) { - chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].note); + chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note); chan[i].freqChanged=true; } } @@ -354,7 +348,16 @@ void DivPlatformQSound::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + double off=1.0; + if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { + DivSample* s=parent->getSample(chan[i].sample); + if (s->centerRate<1) { + off=1.0; + } else { + off=(double)s->centerRate/24038.0/16.0; + } + } + chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,440.0,4096.0); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); @@ -388,17 +391,8 @@ int DivPlatformQSound::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; - double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=(double)s->centerRate/24038.0/16.0; - } - } if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=off*QS_NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value); } if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { chan[c.chan].sample=-1; @@ -467,16 +461,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=(double)s->centerRate/24038.0/16.0; - } - } - int destFreq=off*QS_NOTE_FREQUENCY(c.value2); + int destFreq=QS_NOTE_FREQUENCY(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -499,16 +484,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=(double)s->centerRate/24038.0/16.0; - } - } - chan[c.chan].baseFreq=off*QS_NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 3ecf0a3e..b662b1e3 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -200,7 +200,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { rWrite(0x18+(i/3),saaEnv[i/3]); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>=32768) { chan[i].freqH=7; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index f1c66163..be2039cf 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -121,7 +121,7 @@ void DivPlatformSMS::tick(bool sysTick) { } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,64); if (chan[i].freq>1023) chan[i].freq=1023; if (chan[i].freq<8) chan[i].freq=1; //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; @@ -136,7 +136,7 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2); + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,isRealSN?60:64); if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index e80153f0..4630f90e 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -183,7 +183,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].pcm) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivSample* sample=parent->getSample(ins->amiga.initSample); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 97aa7b9e..b089394a 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -203,7 +203,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (i==1 && pcm && furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 4dcc8f6c..0c19ea54 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -147,6 +147,7 @@ void DivPlatformVERA::reset() { chan[16].pan=3; } +// TODO: linear pitch stuff int DivPlatformVERA::calcNoteFreq(int ch, int note) { if (ch<16) { return parent->calcBaseFreq(chipClock,2097152,note,false); @@ -208,7 +209,7 @@ void DivPlatformVERA::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2,chipClock,2097152); if (chan[i].freq>65535) chan[i].freq=65535; rWrite(i,0,chan[i].freq&0xff); rWrite(i,1,(chan[i].freq>>8)&0xff); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 33250bfc..1c8c1fe0 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -132,7 +132,7 @@ void DivPlatformVIC20::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (i<3) { chan[i].freq>>=(2-i); } else { diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 835acf4b..4e811794 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -197,9 +197,9 @@ void DivPlatformVRC6::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,14)-1; } else { // pulse - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,16)-1; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 03cd4f6b..35226f50 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -260,6 +260,7 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l } } +// TODO: linear pitch stuff double DivPlatformX1_010::NoteX1_010(int ch, int note) { if (chan[ch].pcm) { // PCM note double off=1.0; @@ -487,7 +488,7 @@ void DivPlatformX1_010::tick(bool sysTick) { chan[i].envChanged=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].pcm) { if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>255) chan[i].freq=255; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 224838e8..feec7d96 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -631,7 +631,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].scheduledSlideReset=false; chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); - dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote)); + dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote)); chan[i].portaNote=-1; chan[i].portaSpeed=-1; chan[i].inPorta=false; @@ -954,7 +954,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } if (!song.noSlidesOnFirstTick || !firstTick) { if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { - if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { + if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { chan[i].portaSpeed=0; chan[i].oldNote=chan[i].note; chan[i].note=chan[i].portaNote; diff --git a/src/engine/song.h b/src/engine/song.h index 7b823a8b..489b744b 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -301,7 +301,12 @@ struct DivSong { // compatibility flags bool limitSlides; - bool linearPitch; + // linear pitch + // 0: not linear + // 1: only pitch changes (04xy/E5xx) linear + // 2: full linear + unsigned char linearPitch; + unsigned char pitchSlideSpeed; // loop behavior // 0: reset on loop // 1: fake reset on loop @@ -412,7 +417,8 @@ struct DivSong { masterVol(1.0f), tuning(440.0f), limitSlides(false), - linearPitch(true), + linearPitch(1), + pitchSlideSpeed(4), loopModality(0), properNoiseLayout(false), waveDutyIsVol(false), diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 52f46502..461048d6 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -18,6 +18,8 @@ */ #include "gui.h" +#include "imgui.h" +#include "intConst.h" void FurnaceGUI::drawCompatFlags() { if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) { @@ -32,10 +34,6 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); } - ImGui::Checkbox("Linear pitch control",&e->song.linearPitch); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space"); - } ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine."); @@ -126,6 +124,35 @@ void FurnaceGUI::drawCompatFlags() { ImGui::SetTooltip("when enabled, the pitch macro of an instrument is in linear space."); } + ImGui::Text("Pitch linearity:"); + if (ImGui::RadioButton("None",e->song.linearPitch==0)) { + e->song.linearPitch=0; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("like ProTracker/FamiTracker"); + } + if (ImGui::RadioButton("Partial (only 04xy/E5xx)",e->song.linearPitch==1)) { + e->song.linearPitch=1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("like DefleMask"); + } + if (ImGui::RadioButton("Full",e->song.linearPitch==2)) { + e->song.linearPitch=2; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("like Impulse Tracker"); + } + + if (e->song.linearPitch==2) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + if (ImGui::InputScalar("Pitch slide speed multiplier",ImGuiDataType_U8,&e->song.pitchSlideSpeed,&_ONE,&_ONE)) { + if (e->song.pitchSlideSpeed<1) e->song.pitchSlideSpeed=1; + if (e->song.pitchSlideSpeed>64) e->song.pitchSlideSpeed=64; + } + } + ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { e->song.loopModality=0;