mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-15 17:25:06 +00:00
Merge branch 'newpitch'
This commit is contained in:
commit
bb4bc01778
12 changed files with 118 additions and 107 deletions
|
@ -49,12 +49,27 @@ the coding style is described here:
|
||||||
- `size_t` are 32-bit or 64-bit, depending on architecture.
|
- `size_t` are 32-bit or 64-bit, depending on architecture.
|
||||||
- in float/double operations, always use decimal and `f` if single-precision.
|
- in float/double operations, always use decimal and `f` if single-precision.
|
||||||
- e.g. `1.0f` or `1.0` instead of `1`.
|
- e.g. `1.0f` or `1.0` instead of `1`.
|
||||||
|
- prefer `NULL` over `nullptr` or any other proprietary null.
|
||||||
- don't use `auto` unless needed.
|
- don't use `auto` unless needed.
|
||||||
|
- use `String` for `std::string` (this is typedef'd in ta-utils.h).
|
||||||
|
- prefer using operator for String (std::string) comparisons (a=="").
|
||||||
|
|
||||||
some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style.
|
some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style.
|
||||||
|
|
||||||
you don't have to follow this style. I will fix it after I accept your contribution.
|
you don't have to follow this style. I will fix it after I accept your contribution.
|
||||||
|
|
||||||
|
additional guidelines:
|
||||||
|
|
||||||
|
- in general **strongly** avoid breaking compatibility.
|
||||||
|
- do not touch loadFur/saveFur unless you know what you're doing!
|
||||||
|
- new fields must be at the end of each block to ensure forward compatibility
|
||||||
|
- likewise, the instrument read/write functions in DivInstrument have to be handled carefully
|
||||||
|
- any change to the format requires a version bump (see `src/engine/engine.h`).
|
||||||
|
- do not bump the version number under any circumstances!
|
||||||
|
- if you are making major changes to the playback routine, make sure to test with older songs to ensure nothing breaks.
|
||||||
|
- I will run a test suite to make sure this is the case.
|
||||||
|
- if something breaks, you might want to add a compatibility flag (this requires changing the format though).
|
||||||
|
|
||||||
## Demo Songs
|
## Demo Songs
|
||||||
|
|
||||||
just put your demo song in `demos/`!
|
just put your demo song in `demos/`!
|
||||||
|
|
|
@ -29,6 +29,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 85: Furnace dev85
|
||||||
|
- 84: Furnace dev84
|
||||||
- 83: Furnace dev83
|
- 83: Furnace dev83
|
||||||
- 82: Furnace dev82
|
- 82: Furnace dev82
|
||||||
- 81: Furnace dev81
|
- 81: Furnace dev81
|
||||||
|
@ -269,7 +271,9 @@ size | description
|
||||||
1 | ExtCh channel state is shared (>=78) or reserved
|
1 | ExtCh channel state is shared (>=78) or reserved
|
||||||
1 | ignore DAC mode change outside of intended channel (>=83) or reserved
|
1 | ignore DAC mode change outside of intended channel (>=83) or reserved
|
||||||
1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved
|
1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved
|
||||||
23 | reserved
|
1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved
|
||||||
|
1 | weird f-num/block-based chip pitch slides (>=85) or reserved
|
||||||
|
21 | reserved
|
||||||
```
|
```
|
||||||
|
|
||||||
# instrument
|
# instrument
|
||||||
|
|
|
@ -426,6 +426,7 @@ class DivDispatch {
|
||||||
#define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true))
|
#define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true))
|
||||||
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
|
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
|
||||||
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)
|
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)
|
||||||
|
#define NOTE_FNUM_BLOCK(x,bits) ((((int)parent->calcBaseFreq(chipClock,CHIP_FREQBASE,(x)%12,false))&((1<<bits)-1))|((MAX(x,0)/12)<<bits))
|
||||||
|
|
||||||
#define COLOR_NTSC (315000000.0/88.0)
|
#define COLOR_NTSC (315000000.0/88.0)
|
||||||
#define COLOR_PAL (283.75*15625.0+25.0)
|
#define COLOR_PAL (283.75*15625.0+25.0)
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev84"
|
#define DIV_VERSION "dev85"
|
||||||
#define DIV_ENGINE_VERSION 84
|
#define DIV_ENGINE_VERSION 85
|
||||||
|
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
|
|
|
@ -162,6 +162,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
ds.gbInsAffectsEnvelope=true;
|
ds.gbInsAffectsEnvelope=true;
|
||||||
ds.ignoreDACModeOutsideIntendedChannel=false;
|
ds.ignoreDACModeOutsideIntendedChannel=false;
|
||||||
ds.e1e2AlsoTakePriority=true;
|
ds.e1e2AlsoTakePriority=true;
|
||||||
|
ds.fbPortaPause=true;
|
||||||
|
|
||||||
// 1.1 compat flags
|
// 1.1 compat flags
|
||||||
if (ds.version>24) {
|
if (ds.version>24) {
|
||||||
|
@ -993,6 +994,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
if (ds.version<84) {
|
if (ds.version<84) {
|
||||||
ds.newSegaPCM=false;
|
ds.newSegaPCM=false;
|
||||||
}
|
}
|
||||||
|
if (ds.version<85) {
|
||||||
|
ds.fbPortaPause=true;
|
||||||
|
}
|
||||||
ds.isDMF=false;
|
ds.isDMF=false;
|
||||||
|
|
||||||
reader.readS(); // reserved
|
reader.readS(); // reserved
|
||||||
|
@ -1342,7 +1346,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
for (int i=0; i<22; i++) {
|
if (ds.version>=85) {
|
||||||
|
ds.fbPortaPause=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
for (int i=0; i<21; i++) {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2283,7 +2292,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeC(song.ignoreDACModeOutsideIntendedChannel);
|
w->writeC(song.ignoreDACModeOutsideIntendedChannel);
|
||||||
w->writeC(song.e1e2AlsoTakePriority);
|
w->writeC(song.e1e2AlsoTakePriority);
|
||||||
w->writeC(song.newSegaPCM);
|
w->writeC(song.newSegaPCM);
|
||||||
for (int i=0; i<22; i++) {
|
w->writeC(song.fbPortaPause);
|
||||||
|
for (int i=0; i<21; i++) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,15 +245,15 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
if (chan[i].std.arp.had) {
|
if (chan[i].std.arp.had) {
|
||||||
if (!chan[i].inPorta) {
|
if (!chan[i].inPorta) {
|
||||||
if (chan[i].std.arp.mode) {
|
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 {
|
} 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;
|
chan[i].freqChanged=true;
|
||||||
} else {
|
} else {
|
||||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
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;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,9 +394,9 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
for (int i=0; i<6; i++) {
|
for (int i=0; i<6; i++) {
|
||||||
if (i==2 && extMode) continue;
|
if (i==2 && extMode) continue;
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
|
chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val;
|
||||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||||
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
|
int freqt=chan[i].freq;
|
||||||
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);
|
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);
|
||||||
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff);
|
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff);
|
||||||
if (chan[i].furnaceDac && dacMode) {
|
if (chan[i].furnaceDac && dacMode) {
|
||||||
|
@ -422,47 +422,6 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformGenesis::octave(int freq) {
|
|
||||||
if (freq>=82432) {
|
|
||||||
return 128;
|
|
||||||
} else if (freq>=41216) {
|
|
||||||
return 64;
|
|
||||||
} else if (freq>=20608) {
|
|
||||||
return 32;
|
|
||||||
} else if (freq>=10304) {
|
|
||||||
return 16;
|
|
||||||
} else if (freq>=5152) {
|
|
||||||
return 8;
|
|
||||||
} else if (freq>=2576) {
|
|
||||||
return 4;
|
|
||||||
} else if (freq>=1288) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DivPlatformGenesis::toFreq(int freq) {
|
|
||||||
if (freq>=82432) {
|
|
||||||
return 0x3800|((freq>>7)&0x7ff);
|
|
||||||
} else if (freq>=41216) {
|
|
||||||
return 0x3000|((freq>>6)&0x7ff);
|
|
||||||
} else if (freq>=20608) {
|
|
||||||
return 0x2800|((freq>>5)&0x7ff);
|
|
||||||
} else if (freq>=10304) {
|
|
||||||
return 0x2000|((freq>>4)&0x7ff);
|
|
||||||
} else if (freq>=5152) {
|
|
||||||
return 0x1800|((freq>>3)&0x7ff);
|
|
||||||
} else if (freq>=2576) {
|
|
||||||
return 0x1000|((freq>>2)&0x7ff);
|
|
||||||
} else if (freq>=1288) {
|
|
||||||
return 0x800|((freq>>1)&0x7ff);
|
|
||||||
} else {
|
|
||||||
return freq&0x7ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
for (int j=0; j<4; j++) {
|
for (int j=0; j<4; j++) {
|
||||||
|
@ -509,7 +468,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
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].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
}
|
}
|
||||||
chan[c.chan].furnaceDac=true;
|
chan[c.chan].furnaceDac=true;
|
||||||
|
@ -576,7 +535,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
chan[c.chan].insChanged=false;
|
chan[c.chan].insChanged=false;
|
||||||
|
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
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].portaPause=false;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
@ -656,31 +615,42 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||||
int newFreq;
|
int newFreq;
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
|
if (chan[c.chan].portaPause) {
|
||||||
|
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||||
|
}
|
||||||
if (destFreq>chan[c.chan].baseFreq) {
|
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) {
|
if (newFreq>=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
newFreq=chan[c.chan].baseFreq-c.value;
|
||||||
if (newFreq<=destFreq) {
|
if (newFreq<=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check for octave boundary
|
||||||
|
// what the heck!
|
||||||
if (!chan[c.chan].portaPause) {
|
if (!chan[c.chan].portaPause) {
|
||||||
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
|
if ((newFreq&0x7ff)>1288) {
|
||||||
|
chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||||
|
chan[c.chan].portaPause=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((newFreq&0x7ff)<644) {
|
||||||
|
chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800);
|
||||||
chan[c.chan].portaPause=true;
|
chan[c.chan].portaPause=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan[c.chan].baseFreq=newFreq;
|
|
||||||
chan[c.chan].portaPause=false;
|
chan[c.chan].portaPause=false;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].baseFreq=newFreq;
|
||||||
if (return2) {
|
if (return2) {
|
||||||
chan[c.chan].inPorta=false;
|
chan[c.chan].inPorta=false;
|
||||||
return 2;
|
return 2;
|
||||||
|
@ -699,7 +669,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -36,7 +36,7 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, portaPauseFreq, note;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
@ -47,6 +47,7 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
freq(0),
|
freq(0),
|
||||||
baseFreq(0),
|
baseFreq(0),
|
||||||
pitch(0),
|
pitch(0),
|
||||||
|
portaPauseFreq(0),
|
||||||
note(0),
|
note(0),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
active(false),
|
active(false),
|
||||||
|
@ -93,9 +94,6 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
short oldWrites[512];
|
short oldWrites[512];
|
||||||
short pendingWrites[512];
|
short pendingWrites[512];
|
||||||
|
|
||||||
int octave(int freq);
|
|
||||||
int toFreq(int freq);
|
|
||||||
|
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||||
|
|
|
@ -72,7 +72,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
opChan[ch].insChanged=false;
|
opChan[ch].insChanged=false;
|
||||||
|
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
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].portaPause=false;
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
}
|
}
|
||||||
|
@ -127,31 +127,49 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||||
int newFreq;
|
int newFreq;
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
|
if (opChan[ch].portaPause) {
|
||||||
|
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||||
|
}
|
||||||
if (destFreq>opChan[ch].baseFreq) {
|
if (destFreq>opChan[ch].baseFreq) {
|
||||||
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq);
|
newFreq=opChan[ch].baseFreq+c.value;
|
||||||
if (newFreq>=destFreq) {
|
if (newFreq>=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq);
|
newFreq=opChan[ch].baseFreq-c.value;
|
||||||
if (newFreq<=destFreq) {
|
if (newFreq<=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// what the heck!
|
||||||
if (!opChan[ch].portaPause) {
|
if (!opChan[ch].portaPause) {
|
||||||
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) {
|
if ((newFreq&0x7ff)>1288) {
|
||||||
opChan[ch].portaPause=true;
|
if (parent->song.fbPortaPause) {
|
||||||
break;
|
opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||||
|
opChan[ch].portaPause=true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((newFreq&0x7ff)<644) {
|
||||||
|
if (parent->song.fbPortaPause) {
|
||||||
|
opChan[ch].portaPauseFreq=newFreq=(1287)|((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].portaPause=false;
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
|
opChan[ch].baseFreq=newFreq;
|
||||||
if (return2) return 2;
|
if (return2) return 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +190,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -289,35 +307,10 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
||||||
unsigned char writeMask=2;
|
unsigned char writeMask=2;
|
||||||
if (extMode) for (int i=0; i<4; i++) {
|
if (extMode) for (int i=0; i<4; i++) {
|
||||||
if (opChan[i].freqChanged) {
|
if (opChan[i].freqChanged) {
|
||||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch);
|
opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4);
|
||||||
if (opChan[i].freq>262143) opChan[i].freq=262143;
|
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||||
if (opChan[i].freq>=82432) {
|
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||||
opChan[i].freqH=((opChan[i].freq>>15)&7)|0x38;
|
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||||
opChan[i].freqL=(opChan[i].freq>>7)&0xff;
|
|
||||||
} else if (opChan[i].freq>=41216) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>14)&7)|0x30;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>6)&0xff;
|
|
||||||
} else if (opChan[i].freq>=20608) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>13)&7)|0x28;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>5)&0xff;
|
|
||||||
} else if (opChan[i].freq>=10304) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>12)&7)|0x20;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>4)&0xff;
|
|
||||||
} else if (opChan[i].freq>=5152) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>11)&7)|0x18;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>3)&0xff;
|
|
||||||
} else if (opChan[i].freq>=2576) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>10)&7)|0x10;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>2)&0xff;
|
|
||||||
} else if (opChan[i].freq>=1288) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>9)&7)|0x08;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>1)&0xff;
|
|
||||||
} else {
|
|
||||||
opChan[i].freqH=(opChan[i].freq>>8)&7;
|
|
||||||
opChan[i].freqL=opChan[i].freq&0xff;
|
|
||||||
}
|
|
||||||
immWrite(opChanOffsH[i],opChan[i].freqH);
|
|
||||||
immWrite(opChanOffsL[i],opChan[i].freqL);
|
|
||||||
}
|
}
|
||||||
writeMask|=opChan[i].active<<(4+i);
|
writeMask|=opChan[i].active<<(4+i);
|
||||||
if (opChan[i].keyOn) {
|
if (opChan[i].keyOn) {
|
||||||
|
|
|
@ -25,13 +25,28 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
||||||
struct OpChannel {
|
struct OpChannel {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch, portaPauseFreq;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||||
int vol;
|
int vol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(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),
|
||||||
|
portaPauseFreq(0),
|
||||||
|
ins(-1),
|
||||||
|
active(false),
|
||||||
|
insChanged(true),
|
||||||
|
freqChanged(false),
|
||||||
|
keyOn(false),
|
||||||
|
keyOff(false),
|
||||||
|
portaPause(false),
|
||||||
|
vol(0),
|
||||||
|
pan(3) {}
|
||||||
};
|
};
|
||||||
OpChannel opChan[4];
|
OpChannel opChan[4];
|
||||||
bool isOpMuted[4];
|
bool isOpMuted[4];
|
||||||
|
|
|
@ -324,6 +324,7 @@ struct DivSong {
|
||||||
bool ignoreDACModeOutsideIntendedChannel;
|
bool ignoreDACModeOutsideIntendedChannel;
|
||||||
bool e1e2AlsoTakePriority;
|
bool e1e2AlsoTakePriority;
|
||||||
bool newSegaPCM;
|
bool newSegaPCM;
|
||||||
|
bool fbPortaPause;
|
||||||
|
|
||||||
DivOrders orders;
|
DivOrders orders;
|
||||||
std::vector<DivInstrument*> ins;
|
std::vector<DivInstrument*> ins;
|
||||||
|
@ -406,7 +407,8 @@ struct DivSong {
|
||||||
sharedExtStat(true),
|
sharedExtStat(true),
|
||||||
ignoreDACModeOutsideIntendedChannel(false),
|
ignoreDACModeOutsideIntendedChannel(false),
|
||||||
e1e2AlsoTakePriority(false),
|
e1e2AlsoTakePriority(false),
|
||||||
newSegaPCM(true) {
|
newSegaPCM(true),
|
||||||
|
fbPortaPause(false) {
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
system[i]=DIV_SYSTEM_NULL;
|
system[i]=DIV_SYSTEM_NULL;
|
||||||
systemVol[i]=64;
|
systemVol[i]=64;
|
||||||
|
|
|
@ -101,6 +101,10 @@ void FurnaceGUI::drawCompatFlags() {
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding.");
|
ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding.");
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("FM pitch slide octave boundary odd behavior",&e->song.fbPortaPause);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("if this is on, a pitch slide that crosses the octave boundary will stop for one tick and then continue from the nearest octave boundary.\nfor .dmf compatibility.");
|
||||||
|
}
|
||||||
ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope);
|
ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope.");
|
ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope.");
|
||||||
|
@ -173,4 +177,4 @@ void FurnaceGUI::drawCompatFlags() {
|
||||||
}
|
}
|
||||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
|
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <SDL_keycode.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
Loading…
Reference in a new issue