From 5e14177e4e07d2d4246dfbc6c5d5f3d2fe68ce76 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 10 May 2022 02:22:08 -0500 Subject: [PATCH] YM2610(B): use f-num/block baseFreq calculation also allow for different octave boundary f-nums depending on chip clock --- src/engine/engine.cpp | 12 +++- src/engine/fileOps.cpp | 4 +- src/engine/platform/genesis.cpp | 10 +-- src/engine/platform/genesisext.cpp | 10 +-- src/engine/platform/ym2610.cpp | 99 +++++++++++++----------------- src/engine/platform/ym2610.h | 5 +- src/engine/platform/ym2610b.cpp | 99 +++++++++++++----------------- src/engine/platform/ym2610b.h | 5 +- src/engine/platform/ym2610bext.cpp | 56 ++++++++++++----- src/engine/platform/ym2610bext.h | 4 +- src/engine/platform/ym2610ext.cpp | 60 +++++++++++++----- src/engine/platform/ym2610ext.h | 4 +- 12 files changed, 198 insertions(+), 170 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 94c138a94..10f61c17a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -993,19 +993,24 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri } unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) { + double tuning=song.tuning; + if (tuning<400.0) tuning=400.0; + if (tuning>500.0) tuning=500.0; int bf=calcBaseFreq(clock,divider,note,false); + int boundaryBottom=tuning*pow(2.0,0.25)*(divider/clock); + int boundaryTop=2.0*tuning*pow(2.0,0.25)*(divider/clock); int block=note/12; if (block<0) block=0; if (block>7) block=7; bf>>=block; if (bf<0) bf=0; // octave boundaries - while (bf>0 && bf<644 && block>0) { + while (bf>0 && bf0) { bf<<=1; block--; } - if (bf>1288) { - while (block<7) { + if (bf>boundaryTop) { + while (block<7 && bf>boundaryTop) { bf>>=1; block++; } @@ -1013,6 +1018,7 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in bf=(1<0x0c) { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 0f665cba1..4b4771a3e 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -725,6 +725,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; } + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; @@ -747,13 +749,13 @@ int DivPlatformGenesis::dispatch(DivCommand c) { // check for octave boundary // what the heck! if (!chan[c.chan].portaPause) { - if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { - chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { + chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); chan[c.chan].portaPause=true; break; } - if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { - chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + if ((newFreq&0x7ff)0) { + chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; break; } diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index ae4d74cd9..3c9304a04 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -127,6 +127,8 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; @@ -148,18 +150,18 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } // what the heck! if (!opChan[ch].portaPause) { - if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { if (parent->song.fbPortaPause) { - opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); + opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; break; } else { newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); } } - if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { + if ((newFreq&0x7ff)0) { if (parent->song.fbPortaPause) { - opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; break; } else { diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 54bbbd213..46a66fe07 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -456,7 +456,7 @@ double DivPlatformYM2610::NOTE_OPNB(int ch, int note) { return NOTE_PERIODIC(note); } // FM - return NOTE_FREQUENCY(note); + return NOTE_FNUM_BLOCK(note,11); } double DivPlatformYM2610::NOTE_ADPCMB(int note) { @@ -559,15 +559,15 @@ void DivPlatformYM2610::tick(bool sysTick) { if (chan[i].std.arp.had) { if (!chan[i].inPorta) { if (chan[i].std.arp.mode) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11); } else { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11); } } chan[i].freqChanged=true; } else { if (chan[i].std.arp.mode && chan[i].std.arp.finished) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11); chan[i].freqChanged=true; } } @@ -742,11 +742,20 @@ void DivPlatformYM2610::tick(bool sysTick) { for (int i=0; i<4; i++) { if (i==1 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); - if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].pitch2; - immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); - immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2); + int block=(chan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + chan[i].freq=(block<<11)|fNum; + if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; + immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); + immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); chan[i].freqChanged=false; } if (chan[i].keyOn) { @@ -756,47 +765,6 @@ void DivPlatformYM2610::tick(bool sysTick) { } } -int DivPlatformYM2610::octave(int freq) { - if (freq>=622.0f*128) { - return 128; - } else if (freq>=622.0f*64) { - return 64; - } else if (freq>=622.0f*32) { - return 32; - } else if (freq>=622.0f*16) { - return 16; - } else if (freq>=622.0f*8) { - return 8; - } else if (freq>=622.0f*4) { - return 4; - } else if (freq>=622.0f*2) { - return 2; - } else { - return 1; - } - return 1; -} - -int DivPlatformYM2610::toFreq(int freq) { - if (freq>=622.0f*128) { - return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=622.0f*64) { - return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=622.0f*32) { - return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=622.0f*16) { - return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=622.0f*8) { - return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=622.0f*4) { - return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=622.0f*2) { - return 0x800|((freq>>1)&0x7ff); - } else { - return freq&0x7ff; - } -} - int DivPlatformYM2610::dispatch(DivCommand c) { if (c.chan>3 && c.chan<7) { c.chan-=4; @@ -928,7 +896,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].portaPause=false; chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -1048,33 +1016,48 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } break; } - - int destFreq=NOTE_FREQUENCY(c.value2); + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (chan[c.chan].portaPause) { + chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq; + } if (destFreq>chan[c.chan].baseFreq) { - newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // check for octave boundary + // what the heck! if (!chan[c.chan].portaPause) { - if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { + chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPause=true; + break; + } + if ((newFreq&0x7ff)0) { + chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; break; } } - chan[c.chan].baseFreq=newFreq; chan[c.chan].portaPause=false; chan[c.chan].freqChanged=true; - if (return2) return 2; + chan[c.chan].baseFreq=newFreq; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } break; } case DIV_CMD_SAMPLE_BANK: diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 45ecb335a..92f4ca077 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -61,7 +61,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, pitch2, note, ins; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins; unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; @@ -80,6 +80,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { baseFreq(0), pitch(0), pitch2(0), + portaPauseFreq(0), note(0), ins(-1), psgMode(1), @@ -125,8 +126,6 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { short oldWrites[512]; short pendingWrites[512]; - int octave(int freq); - int toFreq(int freq); double NOTE_OPNB(int ch, int note); double NOTE_ADPCMB(int note); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index b36cc87f5..03d8f3a26 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -436,7 +436,7 @@ double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) { return NOTE_PERIODIC(note); } // FM - return NOTE_FREQUENCY(note); + return NOTE_FNUM_BLOCK(note,11); } double DivPlatformYM2610B::NOTE_ADPCMB(int note) { @@ -538,15 +538,15 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (chan[i].std.arp.had) { if (!chan[i].inPorta) { if (chan[i].std.arp.mode) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11); } else { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11); } } chan[i].freqChanged=true; } else { if (chan[i].std.arp.mode && chan[i].std.arp.finished) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11); chan[i].freqChanged=true; } } @@ -720,11 +720,20 @@ void DivPlatformYM2610B::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); - if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].pitch2; - immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); - immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2); + int block=(chan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + chan[i].freq=(block<<11)|fNum; + if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; + immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); + immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); chan[i].freqChanged=false; } if (chan[i].keyOn) { @@ -734,47 +743,6 @@ void DivPlatformYM2610B::tick(bool sysTick) { } } -int DivPlatformYM2610B::octave(int freq) { - if (freq>=622.0f*128) { - return 128; - } else if (freq>=622.0f*64) { - return 64; - } else if (freq>=622.0f*32) { - return 32; - } else if (freq>=622.0f*16) { - return 16; - } else if (freq>=622.0f*8) { - return 8; - } else if (freq>=622.0f*4) { - return 4; - } else if (freq>=622.0f*2) { - return 2; - } else { - return 1; - } - return 1; -} - -int DivPlatformYM2610B::toFreq(int freq) { - if (freq>=622.0f*128) { - return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=622.0f*64) { - return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=622.0f*32) { - return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=622.0f*16) { - return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=622.0f*8) { - return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=622.0f*4) { - return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=622.0f*2) { - return 0x800|((freq>>1)&0x7ff); - } else { - return freq&0x7ff; - } -} - int DivPlatformYM2610B::dispatch(DivCommand c) { if (c.chan>5 && c.chan<9) { c.chan-=6; @@ -906,7 +874,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].portaPause=false; chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -1026,33 +994,48 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } break; } - - int destFreq=NOTE_FREQUENCY(c.value2); + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (chan[c.chan].portaPause) { + chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq; + } if (destFreq>chan[c.chan].baseFreq) { - newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // check for octave boundary + // what the heck! if (!chan[c.chan].portaPause) { - if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { + chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPause=true; + break; + } + if ((newFreq&0x7ff)0) { + chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; break; } } - chan[c.chan].baseFreq=newFreq; chan[c.chan].portaPause=false; chan[c.chan].freqChanged=true; - if (return2) return 2; + chan[c.chan].baseFreq=newFreq; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } break; } case DIV_CMD_SAMPLE_BANK: diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index d1af3069a..3a034028a 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -35,7 +35,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, pitch2, note, ins; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins; unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; @@ -54,6 +54,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { baseFreq(0), pitch(0), pitch2(0), + portaPauseFreq(0), note(0), ins(-1), psgMode(1), @@ -98,8 +99,6 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { short oldWrites[512]; short pendingWrites[512]; - int octave(int freq); - int toFreq(int freq); double NOTE_OPNB(int ch, int note); double NOTE_ADPCMB(int note); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 03b91fccc..ac991ffb8 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -120,31 +120,51 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (opChan[ch].portaPause) { + opChan[ch].baseFreq=opChan[ch].portaPauseFreq; + } if (destFreq>opChan[ch].baseFreq) { - newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // what the heck! if (!opChan[ch].portaPause) { - if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { - opChan[ch].portaPause=true; - break; + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); + } + } + if ((newFreq&0x7ff)0) { + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800); + } } } - opChan[ch].baseFreq=newFreq; opChan[ch].portaPause=false; opChan[ch].freqChanged=true; + opChan[ch].baseFreq=newFreq; if (return2) return 2; break; } @@ -364,14 +384,20 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); - if (opChan[i].freq>262143) opChan[i].freq=262143; - int freqt=toFreq(opChan[i].freq); - opChan[i].freqH=freqt>>8; - opChan[i].freqL=freqt&0xff; - immWrite(opChanOffsH[i],opChan[i].freqH); - immWrite(opChanOffsL[i],opChan[i].freqL); - opChan[i].freqChanged=false; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2); + int block=(opChan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + opChan[i].freq=(block<<11)|fNum; + if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; + immWrite(opChanOffsH[i],opChan[i].freq>>8); + immWrite(opChanOffsL[i],opChan[i].freq&0xff); } writeMask|=opChan[i].active<<(4+i); if (opChan[i].keyOn) { diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index b416b5c70..4e3f7c5ff 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -25,12 +25,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, pitch2, ins; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4]; diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 1ffbd74fe..98273bd9a 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -63,7 +63,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; opChan[ch].freqChanged=true; } @@ -120,36 +120,56 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); + int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (opChan[ch].portaPause) { + opChan[ch].baseFreq=opChan[ch].portaPauseFreq; + } if (destFreq>opChan[ch].baseFreq) { - newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // what the heck! if (!opChan[ch].portaPause) { - if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { - opChan[ch].portaPause=true; - break; + if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); + } + } + if ((newFreq&0x7ff)0) { + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800); + } } } - opChan[ch].baseFreq=newFreq; opChan[ch].portaPause=false; opChan[ch].freqChanged=true; + opChan[ch].baseFreq=newFreq; if (return2) return 2; break; } case DIV_CMD_LEGATO: { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; } @@ -364,14 +384,20 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); - if (opChan[i].freq>262143) opChan[i].freq=262143; - int freqt=toFreq(opChan[i].freq); - opChan[i].freqH=freqt>>8; - opChan[i].freqL=freqt&0xff; - immWrite(opChanOffsH[i],opChan[i].freqH); - immWrite(opChanOffsL[i],opChan[i].freqL); - opChan[i].freqChanged=false; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2); + int block=(opChan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + opChan[i].freq=(block<<11)|fNum; + if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; + immWrite(opChanOffsH[i],opChan[i].freq>>8); + immWrite(opChanOffsL[i],opChan[i].freq&0xff); } writeMask|=opChan[i].active<<(4+i); if (opChan[i].keyOn) { diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 10be6638d..287bf08b3 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -25,12 +25,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, pitch2, ins; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4];