diff --git a/README.md b/README.md index 241249df4..b75db0d05 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ yup, it's real. > where's the manual? -see [papers/](papers/README.md). it's kind of incomplete, but at least the systems (sound chips) section is there. +see [papers/](papers/doc/README.md). it's kind of incomplete, but at least the systems (sound chips) section is there. > it doesn't open under macOS! diff --git a/demos/Checknobankh.fur b/demos/Checknobankh.fur new file mode 100644 index 000000000..ac465df98 Binary files /dev/null and b/demos/Checknobankh.fur differ diff --git a/demos/Rise_against_the_ashes_to_the_new_dawn.fur b/demos/Rise_against_the_ashes_to_the_new_dawn.fur new file mode 100644 index 000000000..be28182fc Binary files /dev/null and b/demos/Rise_against_the_ashes_to_the_new_dawn.fur differ diff --git a/demos/The Cheetahmen.fur b/demos/The Cheetahmen.fur new file mode 100644 index 000000000..105b64b11 Binary files /dev/null and b/demos/The Cheetahmen.fur differ diff --git a/demos/massive_x_opz.fur b/demos/massive_x_opz.fur new file mode 100644 index 000000000..f32a1f26c Binary files /dev/null and b/demos/massive_x_opz.fur differ diff --git a/extern/nfd-modified/src/nfd_cocoa.mm b/extern/nfd-modified/src/nfd_cocoa.mm index 4e0f9e0bf..204d79f88 100644 --- a/extern/nfd-modified/src/nfd_cocoa.mm +++ b/extern/nfd-modified/src/nfd_cocoa.mm @@ -23,7 +23,12 @@ static NSArray *BuildAllowedFileTypes( const std::vector& filterLis NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; std::string typebuf; + int index=-1; for (const std::string& i: filterList) { + index++; + if (!(index&1)) { + continue; + } typebuf=""; for (const char& j: i) { if (j==' ' || j==',' || j ==';') { diff --git a/extern/rtmidi/RtMidi.cpp b/extern/rtmidi/RtMidi.cpp index 8958e6cca..b54da7e1d 100644 --- a/extern/rtmidi/RtMidi.cpp +++ b/extern/rtmidi/RtMidi.cpp @@ -40,6 +40,7 @@ #include "RtMidi.h" #include +#ifdef TARGET_OS_IPHONE #if (TARGET_OS_IPHONE == 1) #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime @@ -66,6 +67,7 @@ #define EndianS32_BtoN(n) n #endif +#endif // Default for Windows is to add an identifier to the port names; this // flag can be defined (e.g. in your project file) to disable this behaviour. @@ -814,7 +816,7 @@ MidiOutApi :: ~MidiOutApi( void ) // time values. // These are not available on iOS. -#if (TARGET_OS_IPHONE == 0) +#ifdef TARGET_OS_IPHONE #include #include #endif diff --git a/instruments/FM/bass/Acoustic Bass.dmp b/instruments/FM/bass/Acoustic Bass.dmp new file mode 100644 index 000000000..fb37c096d Binary files /dev/null and b/instruments/FM/bass/Acoustic Bass.dmp differ diff --git a/instruments/FM/bass/Basses.opm b/instruments/FM/bass/Basses.opm new file mode 100644 index 000000000..0b308cbf8 --- /dev/null +++ b/instruments/FM/bass/Basses.opm @@ -0,0 +1,87 @@ +//LFO: LFRQ AMD PMD WF NFRQ +//@:[Num] [Name] +//CH: PAN FL CON AMS PMS SLOT NE +//OP: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + +// vgm offset = 000001d3, channels used = 1------- +@:0 Acoustic Bass +LFO: 0 0 0 0 0 +CH: 64 0 0 0 0 120 0 +M1: 25 18 14 6 3 28 0 5 6 0 0 +C1: 28 26 8 6 7 31 0 4 2 0 0 +M2: 27 7 0 6 15 21 0 1 1 0 0 +C2: 29 11 10 8 2 0 0 1 3 0 0 + +// vgm offset = 0000024e, channels used = 12------ +@:1 StringSlapSFX +LFO: 0 0 0 0 0 +CH: 64 7 4 0 0 120 0 +M1: 19 16 0 10 15 3 0 0 5 0 0 +C1: 31 20 0 9 15 4 0 7 0 0 0 +M2: 31 18 0 1 15 16 0 2 3 0 0 +C2: 31 17 0 9 15 30 0 1 3 0 0 + +// vgm offset = 0000093e, channels used = 1------- +@:2 Finger Bass +LFO: 0 0 0 0 0 +CH: 64 0 0 0 0 120 0 +M1: 31 15 0 11 1 31 0 3 3 0 0 +C1: 31 13 0 10 1 46 0 2 3 0 0 +M2: 31 10 0 10 1 24 0 1 3 0 0 +C2: 31 4 0 11 15 0 0 1 3 0 0 + +// vgm offset = 00000b30, channels used = 1------- +@:3 Fretless Bass +LFO: 0 0 0 0 0 +CH: 64 0 0 0 0 120 0 +M1: 31 0 0 3 15 21 0 1 5 0 0 +C1: 31 0 0 6 15 54 0 1 0 0 0 +M2: 31 0 0 7 11 26 0 1 5 0 0 +C2: 31 4 0 12 15 2 0 1 0 0 0 + +// vgm offset = 00000d2b, channels used = 1------- +@:4 Picked Bass +LFO: 0 0 0 0 0 +CH: 64 0 0 0 0 120 0 +M1: 31 18 1 11 3 24 0 5 3 0 0 +C1: 31 18 2 10 1 33 0 3 3 0 0 +M2: 31 10 3 10 1 33 0 1 3 0 0 +C2: 31 4 0 11 15 0 0 1 3 0 0 + +// vgm offset = 00000f2c, channels used = 1------- +@:5 Slap Bass +LFO: 0 0 0 0 0 +CH: 64 0 2 0 0 120 0 +M1: 31 20 0 3 15 0 1 0 0 0 0 +C1: 31 23 0 4 0 43 0 15 3 0 0 +M2: 31 17 0 1 2 16 1 1 3 0 0 +C2: 31 9 0 10 15 2 0 1 3 0 0 + +// vgm offset = 00001444, channels used = 1------- +@:6 Synth Bass 1 +LFO: 0 0 0 0 0 +CH: 64 6 2 0 0 120 0 +M1: 31 12 0 3 15 16 2 1 3 0 0 +C1: 31 10 0 4 15 127 0 1 0 0 0 +M2: 31 4 0 1 0 33 0 1 3 0 0 +C2: 31 9 0 11 15 0 0 1 3 0 0 + +// vgm offset = 0000189b, channels used = 1------- +@:7 SynthBass101 +LFO: 0 0 0 0 0 +CH: 64 5 2 0 0 120 0 +M1: 31 11 0 3 15 24 2 2 3 0 0 +C1: 31 10 0 4 15 127 0 1 0 0 0 +M2: 31 0 0 1 15 24 0 1 3 0 0 +C2: 31 9 0 11 15 0 0 1 3 0 0 + +// vgm offset = 00001d22, channels used = 1------- +@:8 Synth Bass 2 +LFO: 0 0 0 0 0 +CH: 64 5 4 0 0 120 0 +M1: 27 10 5 11 7 21 1 1 3 0 0 +C1: 31 0 15 11 7 3 0 1 3 0 0 +M2: 22 13 13 8 6 21 3 12 3 0 0 +C2: 31 15 16 11 8 13 0 2 3 0 0 + + diff --git a/instruments/FM/bass/Electric Finger Bass.dmp b/instruments/FM/bass/Electric Finger Bass.dmp new file mode 100644 index 000000000..e189f5f5d Binary files /dev/null and b/instruments/FM/bass/Electric Finger Bass.dmp differ diff --git a/instruments/FM/bass/Electric Fretless Bass.dmp b/instruments/FM/bass/Electric Fretless Bass.dmp new file mode 100644 index 000000000..f191ec76e Binary files /dev/null and b/instruments/FM/bass/Electric Fretless Bass.dmp differ diff --git a/instruments/FM/bass/Electric Picked Bass.dmp b/instruments/FM/bass/Electric Picked Bass.dmp new file mode 100644 index 000000000..2d49e919c Binary files /dev/null and b/instruments/FM/bass/Electric Picked Bass.dmp differ diff --git a/instruments/FM/bass/Electric Slap Bass.dmp b/instruments/FM/bass/Electric Slap Bass.dmp new file mode 100644 index 000000000..2be2803ce Binary files /dev/null and b/instruments/FM/bass/Electric Slap Bass.dmp differ diff --git a/instruments/FM/bass/SC-55 Synth Bass 1.dmp b/instruments/FM/bass/SC-55 Synth Bass 1.dmp new file mode 100644 index 000000000..9ac496227 Binary files /dev/null and b/instruments/FM/bass/SC-55 Synth Bass 1.dmp differ diff --git a/instruments/FM/bass/SC-55 SynthBass101.dmp b/instruments/FM/bass/SC-55 SynthBass101.dmp new file mode 100644 index 000000000..2feefab3f Binary files /dev/null and b/instruments/FM/bass/SC-55 SynthBass101.dmp differ diff --git a/instruments/FM/bass/Yamaha MU Synth Bass 2.dmp b/instruments/FM/bass/Yamaha MU Synth Bass 2.dmp new file mode 100644 index 000000000..08f8faca4 Binary files /dev/null and b/instruments/FM/bass/Yamaha MU Synth Bass 2.dmp differ diff --git a/instruments/FM/effect/Acoustic String Slap SFX.dmp b/instruments/FM/effect/Acoustic String Slap SFX.dmp new file mode 100644 index 000000000..a60d21e9c Binary files /dev/null and b/instruments/FM/effect/Acoustic String Slap SFX.dmp differ diff --git a/instruments/other/(AY) Closed Hat.fui b/instruments/other/(AY) Closed Hat.fui new file mode 100644 index 000000000..237c8610e Binary files /dev/null and b/instruments/other/(AY) Closed Hat.fui differ diff --git a/instruments/other/(AY) Hat-EnvBass.fui b/instruments/other/(AY) Hat-EnvBass.fui new file mode 100644 index 000000000..00b661abe Binary files /dev/null and b/instruments/other/(AY) Hat-EnvBass.fui differ diff --git a/instruments/other/(AY) Kick E-1.fui b/instruments/other/(AY) Kick E-1.fui new file mode 100644 index 000000000..212289c95 Binary files /dev/null and b/instruments/other/(AY) Kick E-1.fui differ diff --git a/instruments/other/(AY) Open Hat.fui b/instruments/other/(AY) Open Hat.fui new file mode 100644 index 000000000..0466625b1 Binary files /dev/null and b/instruments/other/(AY) Open Hat.fui differ diff --git a/instruments/other/(AY) Snare A#3.fui b/instruments/other/(AY) Snare A#3.fui new file mode 100644 index 000000000..238199de8 Binary files /dev/null and b/instruments/other/(AY) Snare A#3.fui differ diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index fcf2e8860..6d428f3eb 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -22,6 +22,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Philips SAA1099](saa1099.md) - [Microchip AY8930](ay8930.md) - [VERA](vera.md) +- [tildearrow Sound Unit](soundunit.md) - [Seta/Allumer X1-010](x1-010.md) - [WonderSwan](wonderswan.md) - [Bubble System WSG](bubblesystem.md) diff --git a/papers/doc/7-systems/soundunit.md b/papers/doc/7-systems/soundunit.md new file mode 100644 index 000000000..d9d0abd70 --- /dev/null +++ b/papers/doc/7-systems/soundunit.md @@ -0,0 +1,45 @@ +# tildearrow Sound Unit +This is a fantasy sound chip, used in the specs2 fantasy computer designed by tildearrow. It includes native support for sample playback, but with only 8KB of sample data. Since 0.6pre1, this sound chip is no longer hidden by default and can be accessed through the module creation screen and can be added or removed. + +# effects + +- `10xx`: set waveform + - 0: pulse wave + - 1: sawtooth + - 2: sine wave + - 3: triangle wave + - 4: noise + - 5: periodic noise + - 6: XOR sine + - 7: XOR triangle +- `12xx`: set waveform (0 to 7F) +- `13xx`: set resonance of filter (0 to FF) + - despite what the internal effects list says (0 to F), you can use a resonance value from 0 to FF (255) +- `14xx`: set filter mode and ringmod + - bit 0: ring mod + - bit 1: low pass + - bit 2: high pass + - bit 3: band pass +- `15xx`: set frequency sweep period low byte +- `16xx`: set frequency sweep period high byte +- `17xx`: set volume sweep period low byte +- `18xx`: set volume sweep period high byte +- `19xx`: set cutoff sweep period low byte +- `1Axx`: set cutoff sweep period low byte +- `1Bxx`: set frequency sweep boundary +- `1Cxx`: set volume sweep boundary +- `1Dxx`: set cutoff sweep boundary +- `1Exx`: set phase reset period low byte +- `1Fxx`: set phase reset period high byte +- `20xx`: toggle frequency sweep + - bit 0-6: speed + - bit 7: up direction +- `21xx`: toggle volume sweep + - bit 0-4: speed + - bit 5: up direction + - bit 6: loop + - bit 7: alternate +- `22xx`: toggle cutoff sweep + - bit 0-6: speed + - bit 7: up direction +- `4xxx`: set cutoff (0 to FFF) diff --git a/papers/format.md b/papers/format.md index 04acc6182..3e3583463 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 101: Furnace 0.6pre1 (dev101) - 100: Furnace 0.6pre1 - 99: Furnace dev99 - 98: Furnace dev98 @@ -235,6 +236,8 @@ size | description | - 0xbc: reserved - 8 channels | - 0xbd: YM2612 extra features extended - 11 channels | - 0xbe: YM2612 extra features - 7 channels + | - 0xbf: T6W28 - 4 channels + | - 0xc0: PCM DAC - 1 channel | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfd: Dummy System - 8 channels @@ -316,7 +319,8 @@ size | description 1 | volume macro still applies after end (>=99) or reserved 1 | broken outVol (>=99) or reserved 1 | E1xy and E2xy stop on same note (>=100) or reserved - 8 | reserved + 1 | broken initial position of porta after arp (>=101) or reserved + 7 | reserved --- | **virtual tempo data** 2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved diff --git a/src/engine/engine.h b/src/engine/engine.h index d94b38b58..23290d3be 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 "0.6pre1" -#define DIV_ENGINE_VERSION 100 +#define DIV_VERSION "0.6pre1 (dev101)" +#define DIV_ENGINE_VERSION 101 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index d4c453239..ddc5edf6b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -172,6 +172,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.volMacroLinger=false; ds.brokenOutVol=true; // ??? ds.e1e2StopOnSameNote=true; + ds.brokenPortaArp=false; // 1.1 compat flags if (ds.version>24) { @@ -1047,6 +1048,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<100) { ds.e1e2StopOnSameNote=false; } + if (ds.version<101) { + ds.brokenPortaArp=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1448,7 +1452,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<8; i++) { + if (ds.version>=101) { + ds.brokenPortaArp=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<7; i++) { reader.readC(); } } @@ -2922,7 +2931,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.volMacroLinger); w->writeC(song.brokenOutVol); w->writeC(song.e1e2StopOnSameNote); - for (int i=0; i<8; i++) { + w->writeC(song.brokenPortaArp); + for (int i=0; i<7; i++) { w->writeC(0); } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 8037feafe..65b44136f 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -355,6 +355,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_SAMPLE_POS: diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 1be61fa23..41042cd5c 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -827,6 +827,7 @@ int DivPlatformArcade::dispatch(DivCommand c) { return 127; break; case DIV_CMD_PRE_PORTA: + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 073776105..8df5bd4ce 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -471,9 +471,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) { return 15; break; case DIV_CMD_PRE_PORTA: + // TODO: FIX wtr_envelope.dmf + // the brokenPortaArp update broke it if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 0fad4025b..581ebb9e8 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -506,6 +506,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 48a078803..5fe4462f4 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -250,6 +250,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index a55b53906..640df54e6 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -369,6 +369,7 @@ int DivPlatformC64::dispatch(DivCommand c) { chan[c.chan].keyOn=true; } } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index ea4b83e9d..f9fc5b50b 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -406,6 +406,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FDS)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 999d31b91..2b7dcf6df 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -393,6 +393,7 @@ int DivPlatformGB::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_GB)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GB_SWEEP_DIR: diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 4fd30db98..3f5e92c03 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -387,6 +387,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 2b97f46f6..dc9e5abad 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -324,6 +324,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 731bf8f28..dc81950ef 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -121,6 +121,7 @@ int DivPlatformMSM6258::dispatch(DivCommand c) { chan[c.chan].outVol=chan[c.chan].vol; } sample=ins->amiga.getSample(c.value); + samplePos=0; if (sample>=0 && samplesong.sampleLen) { //DivSample* s=parent->getSample(chan[c.chan].sample); if (c.value!=DIV_NOTE_NULL) { @@ -144,8 +145,8 @@ int DivPlatformMSM6258::dispatch(DivCommand c) { //DivSample* s=parent->getSample(12*sampleBank+c.value%12); sample=12*sampleBank+c.value%12; samplePos=0; - msm->ctrl_w(1); - msm->ctrl_w(2); + rWrite(0,1); + rWrite(0,2); } break; } diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 50f9d8363..b20b460e0 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -562,6 +562,7 @@ int DivPlatformN163::dispatch(DivCommand c) { chan[c.chan].keyOn=true; } } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 6bcf43d27..00b127930 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -442,6 +442,7 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 67ad28521..d801fe664 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -573,6 +573,7 @@ int DivPlatformNES::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 1d617355a..823e764bc 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1434,6 +1434,9 @@ int DivPlatformOPL::dispatch(DivCommand c) { return 63; break; case DIV_CMD_PRE_PORTA: + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) { + chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(chan[c.chan].note)):(NOTE_FREQUENCY(chan[c.chan].note)); + } chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: @@ -1539,7 +1542,7 @@ void DivPlatformOPL::reset() { } */ if (downsample) { - const unsigned int downsampledRate=(unsigned int)(49716.0*(double(rate)/chipRateBase)); + const unsigned int downsampledRate=(unsigned int)((double)rate*rate/chipRateBase); OPL3_Reset(&fm,downsampledRate); } else { OPL3_Reset(&fm,rate); @@ -1669,7 +1672,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) { slots=drums?slotsDrums:slotsNonDrums; chanMap=drums?chanMapOPL2Drums:chanMapOPL2; outChanMap=outChanMapOPL2; - chipFreqBase=9440540*0.25; + chipFreqBase=32768*72; chans=9; melodicChans=drums?6:9; totalChans=drums?11:9; @@ -1683,7 +1686,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) { slots=drums?slotsDrums:slotsNonDrums; chanMap=drums?chanMapOPL3Drums:chanMapOPL3; outChanMap=outChanMapOPL3; - chipFreqBase=9440540; + chipFreqBase=32768*288; chans=18; melodicChans=drums?15:18; totalChans=drums?20:18; @@ -1735,9 +1738,6 @@ void DivPlatformOPL::setFlags(unsigned int flags) { default: case 1: case 2: case 8950: switch (flags&0xff) { - case 0x00: - chipClock=COLOR_NTSC; - break; case 0x01: chipClock=COLOR_PAL*4.0/5.0; break; @@ -1753,15 +1753,15 @@ void DivPlatformOPL::setFlags(unsigned int flags) { case 0x05: chipClock=3500000.0; break; + default: + chipClock=COLOR_NTSC; + break; } rate=chipClock/72; - chipRateBase=double(rate); + chipRateBase=rate; break; case 3: switch (flags&0xff) { - case 0x00: - chipClock=COLOR_NTSC*4.0; - break; case 0x01: chipClock=COLOR_PAL*16.0/5.0; break; @@ -1774,28 +1774,31 @@ void DivPlatformOPL::setFlags(unsigned int flags) { case 0x04: chipClock=15000000.0; break; + default: + chipClock=COLOR_NTSC*4.0; + break; } rate=chipClock/288; - chipRateBase=double(rate); + chipRateBase=rate; break; case 4: switch (flags&0xff) { - case 0x02: - chipClock=33868800.0; - break; - case 0x00: - chipClock=COLOR_NTSC*8.0; - break; case 0x01: chipClock=COLOR_PAL*32.0/5.0; break; + case 0x02: + chipClock=33868800.0; + break; + default: + chipClock=COLOR_NTSC*8.0; + break; } - chipRateBase=double(chipClock)/684.0; rate=chipClock/768; + chipRateBase=chipClock/684; break; case 759: rate=48000; - chipRateBase=double(rate); + chipRateBase=rate; chipClock=rate*288; break; } diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index e949c47f2..3d6497367 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -95,7 +95,7 @@ class DivPlatformOPL: public DivDispatch { const unsigned char** slots; const unsigned short* chanMap; const unsigned char* outChanMap; - double chipFreqBase, chipRateBase; + int chipFreqBase, chipRateBase; int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank; unsigned char lastBusy; unsigned char drumState; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 08292be68..5c9611b43 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -831,6 +831,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (c.chan>=9 && !properDrums) return 0; + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index b8c580e51..9e1302a0f 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -445,6 +445,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 57c72b677..d40a297ae 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -471,6 +471,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 9b3c99886..493436271 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -239,6 +239,7 @@ int DivPlatformPET::dispatch(DivCommand c) { if (chan.active && c.value2) { if (parent->song.resetMacroOnPorta) chan.macroInit(parent->getIns(chan.ins,DIV_INS_PET)); } + if (!chan.inPorta && c.value && !parent->song.brokenPortaArp && chan.std.arp.will) chan.baseFreq=NOTE_PERIODIC(chan.note); chan.inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 8af565df4..6eb14be7f 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -496,6 +496,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index 2106a5a72..176b6e7c7 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -265,6 +265,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_SAMPLE_POS: diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index c562838a2..103c3348f 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -335,6 +335,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index 8175bc01b..485839bf1 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -267,6 +267,7 @@ int DivPlatformSCC::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 19b8eb82e..71d45298a 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -365,6 +365,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { return 127; break; case DIV_CMD_PRE_PORTA: + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=(chan[c.chan].note<<6); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index f16d53351..3d11a5ac9 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -133,7 +133,7 @@ void DivPlatformSMS::tick(bool sysTick) { if (i==3) CHIP_DIVIDER=noiseDivider; chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); + chan[i].outVol=VOL_SCALE_LOG(chan[i].std.vol.val,chan[i].vol,15); if (chan[i].outVol<0) chan[i].outVol=0; // old formula // ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4; @@ -353,9 +353,8 @@ int DivPlatformSMS::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; - // TODO: pre porta cancel arp compat flag - //if (chan[c.chan].inPorta) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); break; case DIV_CMD_GET_VOLMAX: return 15; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index c6860afbb..f3be01a78 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -454,6 +454,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SU)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 905fa82a5..b3e833198 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -431,6 +431,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index da3472446..ddb380844 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -259,6 +259,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TIA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=(chan[c.chan].note<<8); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 123d1193c..9d24e4db5 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -933,6 +933,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { return 127; break; case DIV_CMD_PRE_PORTA: + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 3e76dd1f8..a2dc17da0 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -361,6 +361,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=calcNoteFreq(c.chan,chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_STD_NOISE_MODE: diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 8475b0e53..771b87d1e 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -243,6 +243,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VIC)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 08c6d0538..88fcb37b4 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -399,6 +399,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index dc5c7ed22..e2e360b1b 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -732,6 +732,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_SAMPLE_FREQ: diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index 45631a940..d8d98478d 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -293,6 +293,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_SAMPLE_POS: diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp index 524d3c117..01702dc5d 100644 --- a/src/engine/platform/zxbeeper.cpp +++ b/src/engine/platform/zxbeeper.cpp @@ -224,6 +224,7 @@ int DivPlatformZXBeeper::dispatch(DivCommand c) { if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index d672c2d47..329c5b54d 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -334,6 +334,11 @@ void DivEngine::processRow(int i, bool afterDelay) { if (chan[i].lastIns!=pat->data[whatRow][2]) { chan[i].lastIns=pat->data[whatRow][2]; insChanged=true; + if (song.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) { + logV("forcing volume"); + chan[i].volume=chan[i].volMax; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + } } } // note diff --git a/src/engine/song.h b/src/engine/song.h index 2248292e3..26640fdd4 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -114,6 +114,8 @@ enum DivSystem { DIV_SYSTEM_YM2612_FRAC, DIV_SYSTEM_YM2612_FRAC_EXT, DIV_SYSTEM_RESERVED_8, + DIV_SYSTEM_T6W28, + DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_DUMMY }; @@ -351,7 +353,7 @@ struct DivSong { // - 1: stereo // - YM2203: // - bit 0-4: clock rate - // - 0: 3.58MHz (MTSC) + // - 0: 3.58MHz (NTSC) // - 1: 3.55MHz (PAL) // - 2: 4MHz // - 3: 3MHz @@ -371,7 +373,7 @@ struct DivSong { // - 2: FM: clock / 48, SSG: clock / 8 // - YM3526, YM3812, Y8950: // - bit 0-7: clock rate - // - 0: 3.58MHz (MTSC) + // - 0: 3.58MHz (NTSC) // - 1: 3.55MHz (PAL) // - 2: 4MHz // - 3: 3MHz @@ -379,7 +381,7 @@ struct DivSong { // - 5: 3.5MHz // - YMF262: // - bit 0-7: clock rate - // - 0: 14.32MHz (MTSC) + // - 0: 14.32MHz (NTSC) // - 1: 14.19MHz (PAL) // - 2: 14MHz // - 3: 16MHz @@ -387,7 +389,7 @@ struct DivSong { // - YMF289B: (TODO) // - bit 0-7: clock rate // - 0: 33.8688MHz - // - 1: 28.64MHz (MTSC) + // - 1: 28.64MHz (NTSC) // - 2: 28.38MHz (PAL) // - MSM6295: // - bit 0-6: clock rate @@ -418,7 +420,7 @@ struct DivSong { // - YMZ280B: // - bit 0-7: clock rate // - 0: 16.9344MHz - // - 1: 14.32MHz (MTSC) + // - 1: 14.32MHz (NTSC) // - 2: 14.19MHz (PAL) // - 3: 16MHz // - 4: 16.67MHz @@ -495,6 +497,7 @@ struct DivSong { bool volMacroLinger; bool brokenOutVol; bool e1e2StopOnSameNote; + bool brokenPortaArp; std::vector ins; std::vector wave; @@ -593,7 +596,8 @@ struct DivSong { newVolumeScaling(true), volMacroLinger(true), brokenOutVol(false), - e1e2StopOnSameNote(false) { + e1e2StopOnSameNote(false), + brokenPortaArp(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index e5a4888b9..a5b689b2b 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -165,25 +165,25 @@ String DivEngine::getSongSystemName(bool isMultiSystemAcceptable) { } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC6) { - return "NES + Konami VRC6"; + return "Famicom + Konami VRC6"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC7) { - return "NES + Konami VRC7"; + return "Famicom + Konami VRC7"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_OPLL) { - return "NES + Yamaha OPLL"; + return "Family Noraebang"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) { return "Famicom Disk System"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_N163) { - return "NES + Namco C163"; + return "Famicom + Namco C163"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_MMC5) { - return "NES + MMC5"; + return "Famicom + MMC5"; } if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_AY8910) { - return "NES + Sunsoft 5B"; + return "Famicom + Sunsoft 5B"; } if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) { @@ -2082,6 +2082,35 @@ void DivEngine::registerSystems() { fmPostEffectHandler ); + sysDefs[DIV_SYSTEM_T6W28]=new DivSysDef( + "T6W28", NULL, 0xbf, 0, 4, false, true, 0, false, + "an SN76489 derivative used in Neo Geo Pocket, has independent stereo volume and noise channel frequency.", + {"Square 1", "Square 2", "Square 3", "Noise"}, + {"S1", "S2", "S3", "NO"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // SN noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } + ); + + sysDefs[DIV_SYSTEM_PCM_DAC]=new DivSysDef( + "Generic PCM DAC", NULL, 0xc0, 0, 1, false, true, 0, false, + "as generic sample playback as it gets.", + {"Sample"}, + {"PCM"}, + {DIV_CH_PCM}, + {DIV_INS_AMIGA} + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, "this is a system designed for testing purposes.", diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8e5d75172..9024992e7 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -207,31 +207,35 @@ void FurnaceGUI::drawCompatFlags() { } ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("ExtCh channel status is shared among operators",&e->song.sharedExtStat); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("New SegaPCM features (macros and better panning)",&e->song.newSegaPCM); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("Old FM octave boundary behavior",&e->song.oldOctaveBoundary); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); } ImGui::Checkbox("No OPN2 DAC volume control",&e->song.noOPN2Vol); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); + ImGui::SetTooltip("behavior changed in 0.6pre1"); + } + ImGui::Checkbox("Broken initial position of portamento after arpeggio",&e->song.brokenPortaArp); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6pre1.5"); } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index 78abce7cb..e982882d4 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -447,7 +447,6 @@ void FurnaceGUI::doReplace() { } if (!us.pat.empty()) { - printf("pusher\n"); undoHist.push_back(us); redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); @@ -997,16 +996,16 @@ void FurnaceGUI::drawFindReplace() { ImGui::TableNextColumn(); ImGui::BeginDisabled(!queryReplaceEffectValDo[i]); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::Combo("##ERMode",&queryReplaceEffectValMode[i],queryReplaceModes,GUI_QUERY_REPLACE_MAX); + ImGui::Combo("##ERModeV",&queryReplaceEffectValMode[i],queryReplaceModes,GUI_QUERY_REPLACE_MAX); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_SET) { - if (ImGui::InputScalar("##ERValueH",ImGuiDataType_S32,&queryReplaceEffectVal[i],&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) { + if (ImGui::InputScalar("##ERValueVH",ImGuiDataType_S32,&queryReplaceEffectVal[i],&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) { if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0; if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; } } else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) { - if (ImGui::InputInt("##ERValue",&queryReplaceEffectVal[i],1,12)) { + if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,12)) { if (queryReplaceEffectVal[i]<-255) queryReplaceEffectVal[i]=-255; if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; } diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 154b44e07..3774f232c 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -589,49 +589,49 @@ void FurnaceGUI::initSystemPresets() { } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Konami VRC6", { + "Famicom with Konami VRC6", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_VRC6, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Konami VRC7", { + "Famicom with Konami VRC7", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_VRC7, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with MMC5", { + "Famicom with MMC5", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_MMC5, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Sunsoft 5B", { + "Famicom with Sunsoft 5B", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_AY8910, 64, 0, 32, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Namco C163", { + "Famicom with Namco C163", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_N163, 64, 0, 112, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Family Noraebang", { + "Comboy with Family Noraebang", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_OPLL, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Family Noraebang (drums mode)", { + "Comboy with Family Noraebang (drums mode)", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, 0 @@ -1282,6 +1282,29 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega System E", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega System E (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega System E (with FM expansion in drums mode)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "Sega Hang-On", { DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz @@ -2071,7 +2094,7 @@ void FurnaceGUI::initSystemPresets() { } )); cat.systems.push_back(FurnaceGUISysDef( - "NES with Konami VRC7", { + "Famicom with Konami VRC7", { DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_VRC7, 64, 0, 0, 0 diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 21fef35d1..9cd963cc3 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -569,7 +569,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool case DIV_SYSTEM_OPL3: case DIV_SYSTEM_OPL3_DRUMS: { ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("14.32MHz (MTSC)",(flags&255)==0)) { + if (ImGui::RadioButton("14.32MHz (NTSC)",(flags&255)==0)) { copyOfFlags=(flags&(~255))|0; } if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==1)) { @@ -591,7 +591,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (ImGui::RadioButton("16.9344MHz",(flags&255)==0)) { copyOfFlags=(flags&(~255))|0; } - if (ImGui::RadioButton("14.32MHz (MTSC)",(flags&255)==1)) { + if (ImGui::RadioButton("14.32MHz (NTSC)",(flags&255)==1)) { copyOfFlags=(flags&(~255))|1; } if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==3)) { @@ -608,12 +608,34 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } break; } + case DIV_SYSTEM_PCM_DAC: { + int sampRate=(flags&65535)+1; + int bitDepth=((flags>>16)&15)+1; + bool stereo=(flags>>20)&1; + ImGui::Text("Output rate:"); + if (CWSliderInt("##SampRate",&sampRate,1,65536)) { + if (sampRate<1) sampRate=1; + if (sampRate>65536) sampRate=65536; + copyOfFlags=(flags&(~65535))|(sampRate-1); + } rightClickable + ImGui::Text("Output depth:"); + if (CWSliderInt("##BitDepth",&bitDepth,1,16)) { + if (bitDepth<1) bitDepth=1; + if (bitDepth>16) bitDepth=16; + copyOfFlags=(flags&(~(15<<16)))|((bitDepth-1)<<16); + } rightClickable + if (ImGui::Checkbox("Stereo",&stereo)) { + copyOfFlags=(flags&(~(1<<20)))|(stereo<<20); + } + break; + } case DIV_SYSTEM_GB: case DIV_SYSTEM_SWAN: case DIV_SYSTEM_VERA: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_YMU759: case DIV_SYSTEM_PET: + case DIV_SYSTEM_T6W28: ImGui::Text("nothing to configure"); break; default: