Merge branch 'master' into ZSMv1

This commit is contained in:
ZeroByteOrg 2022-07-11 12:59:59 -05:00
commit 850508e1b8
70 changed files with 342 additions and 65 deletions

View file

@ -234,7 +234,7 @@ yup, it's real.
> where's the manual? > 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! > it doesn't open under macOS!

BIN
demos/Checknobankh.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/The Cheetahmen.fur Normal file

Binary file not shown.

BIN
demos/massive_x_opz.fur Normal file

Binary file not shown.

View file

@ -23,7 +23,12 @@ static NSArray *BuildAllowedFileTypes( const std::vector<std::string>& filterLis
NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; NSMutableArray *buildFilterList = [[NSMutableArray alloc] init];
std::string typebuf; std::string typebuf;
int index=-1;
for (const std::string& i: filterList) { for (const std::string& i: filterList) {
index++;
if (!(index&1)) {
continue;
}
typebuf=""; typebuf="";
for (const char& j: i) { for (const char& j: i) {
if (j==' ' || j==',' || j ==';') { if (j==' ' || j==',' || j ==';') {

View file

@ -40,6 +40,7 @@
#include "RtMidi.h" #include "RtMidi.h"
#include <sstream> #include <sstream>
#ifdef TARGET_OS_IPHONE
#if (TARGET_OS_IPHONE == 1) #if (TARGET_OS_IPHONE == 1)
#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
@ -66,6 +67,7 @@
#define EndianS32_BtoN(n) n #define EndianS32_BtoN(n) n
#endif #endif
#endif
// Default for Windows is to add an identifier to the port names; this // 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. // flag can be defined (e.g. in your project file) to disable this behaviour.
@ -814,7 +816,7 @@ MidiOutApi :: ~MidiOutApi( void )
// time values. // time values.
// These are not available on iOS. // These are not available on iOS.
#if (TARGET_OS_IPHONE == 0) #ifdef TARGET_OS_IPHONE
#include <CoreAudio/HostTime.h> #include <CoreAudio/HostTime.h>
#include <CoreServices/CoreServices.h> #include <CoreServices/CoreServices.h>
#endif #endif

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -22,6 +22,7 @@ this is a list of systems that Furnace supports, including each system's effects
- [Philips SAA1099](saa1099.md) - [Philips SAA1099](saa1099.md)
- [Microchip AY8930](ay8930.md) - [Microchip AY8930](ay8930.md)
- [VERA](vera.md) - [VERA](vera.md)
- [tildearrow Sound Unit](soundunit.md)
- [Seta/Allumer X1-010](x1-010.md) - [Seta/Allumer X1-010](x1-010.md)
- [WonderSwan](wonderswan.md) - [WonderSwan](wonderswan.md)
- [Bubble System WSG](bubblesystem.md) - [Bubble System WSG](bubblesystem.md)

View file

@ -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)

View file

@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are: the format versions are:
- 101: Furnace 0.6pre1 (dev101)
- 100: Furnace 0.6pre1 - 100: Furnace 0.6pre1
- 99: Furnace dev99 - 99: Furnace dev99
- 98: Furnace dev98 - 98: Furnace dev98
@ -235,6 +236,8 @@ size | description
| - 0xbc: reserved - 8 channels | - 0xbc: reserved - 8 channels
| - 0xbd: YM2612 extra features extended - 11 channels | - 0xbd: YM2612 extra features extended - 11 channels
| - 0xbe: YM2612 extra features - 7 channels | - 0xbe: YM2612 extra features - 7 channels
| - 0xbf: T6W28 - 4 channels
| - 0xc0: PCM DAC - 1 channel
| - 0xde: YM2610B extended - 19 channels | - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels | - 0xe0: QSound - 19 channels
| - 0xfd: Dummy System - 8 channels | - 0xfd: Dummy System - 8 channels
@ -316,7 +319,8 @@ size | description
1 | volume macro still applies after end (>=99) or reserved 1 | volume macro still applies after end (>=99) or reserved
1 | broken outVol (>=99) or reserved 1 | broken outVol (>=99) or reserved
1 | E1xy and E2xy stop on same note (>=100) 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** --- | **virtual tempo data**
2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo numerator of first song (>=96) or reserved
2 | virtual tempo denominator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved

View file

@ -45,8 +45,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 "0.6pre1" #define DIV_VERSION "0.6pre1 (dev101)"
#define DIV_ENGINE_VERSION 100 #define DIV_ENGINE_VERSION 101
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01

View file

@ -172,6 +172,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.volMacroLinger=false; ds.volMacroLinger=false;
ds.brokenOutVol=true; // ??? ds.brokenOutVol=true; // ???
ds.e1e2StopOnSameNote=true; ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false;
// 1.1 compat flags // 1.1 compat flags
if (ds.version>24) { if (ds.version>24) {
@ -1047,6 +1048,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<100) { if (ds.version<100) {
ds.e1e2StopOnSameNote=false; ds.e1e2StopOnSameNote=false;
} }
if (ds.version<101) {
ds.brokenPortaArp=true;
}
ds.isDMF=false; ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
@ -1448,7 +1452,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else { } else {
reader.readC(); 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(); reader.readC();
} }
} }
@ -2922,7 +2931,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.volMacroLinger); w->writeC(song.volMacroLinger);
w->writeC(song.brokenOutVol); w->writeC(song.brokenOutVol);
w->writeC(song.e1e2StopOnSameNote); 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); w->writeC(0);
} }

View file

@ -355,6 +355,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_SAMPLE_POS: case DIV_CMD_SAMPLE_POS:

View file

@ -827,6 +827,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
return 127; return 127;
break; break;
case DIV_CMD_PRE_PORTA: 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -471,9 +471,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
return 15; return 15;
break; break;
case DIV_CMD_PRE_PORTA: case DIV_CMD_PRE_PORTA:
// TODO: FIX wtr_envelope.dmf
// the brokenPortaArp update broke it
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -506,6 +506,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -250,6 +250,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -369,6 +369,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
chan[c.chan].keyOn=true; 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -406,6 +406,7 @@ int DivPlatformFDS::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -393,6 +393,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GB_SWEEP_DIR: case DIV_CMD_GB_SWEEP_DIR:

View file

@ -387,6 +387,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -324,6 +324,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -121,6 +121,7 @@ int DivPlatformMSM6258::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
} }
sample=ins->amiga.getSample(c.value); sample=ins->amiga.getSample(c.value);
samplePos=0;
if (sample>=0 && sample<parent->song.sampleLen) { if (sample>=0 && sample<parent->song.sampleLen) {
//DivSample* s=parent->getSample(chan[c.chan].sample); //DivSample* s=parent->getSample(chan[c.chan].sample);
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
@ -144,8 +145,8 @@ int DivPlatformMSM6258::dispatch(DivCommand c) {
//DivSample* s=parent->getSample(12*sampleBank+c.value%12); //DivSample* s=parent->getSample(12*sampleBank+c.value%12);
sample=12*sampleBank+c.value%12; sample=12*sampleBank+c.value%12;
samplePos=0; samplePos=0;
msm->ctrl_w(1); rWrite(0,1);
msm->ctrl_w(2); rWrite(0,2);
} }
break; break;
} }

View file

@ -562,6 +562,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].keyOn=true; 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -442,6 +442,7 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -573,6 +573,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -1434,6 +1434,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
return 63; return 63;
break; break;
case DIV_CMD_PRE_PORTA: 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:
@ -1539,7 +1542,7 @@ void DivPlatformOPL::reset() {
} }
*/ */
if (downsample) { 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); OPL3_Reset(&fm,downsampledRate);
} else { } else {
OPL3_Reset(&fm,rate); OPL3_Reset(&fm,rate);
@ -1669,7 +1672,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
slots=drums?slotsDrums:slotsNonDrums; slots=drums?slotsDrums:slotsNonDrums;
chanMap=drums?chanMapOPL2Drums:chanMapOPL2; chanMap=drums?chanMapOPL2Drums:chanMapOPL2;
outChanMap=outChanMapOPL2; outChanMap=outChanMapOPL2;
chipFreqBase=9440540*0.25; chipFreqBase=32768*72;
chans=9; chans=9;
melodicChans=drums?6:9; melodicChans=drums?6:9;
totalChans=drums?11:9; totalChans=drums?11:9;
@ -1683,7 +1686,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
slots=drums?slotsDrums:slotsNonDrums; slots=drums?slotsDrums:slotsNonDrums;
chanMap=drums?chanMapOPL3Drums:chanMapOPL3; chanMap=drums?chanMapOPL3Drums:chanMapOPL3;
outChanMap=outChanMapOPL3; outChanMap=outChanMapOPL3;
chipFreqBase=9440540; chipFreqBase=32768*288;
chans=18; chans=18;
melodicChans=drums?15:18; melodicChans=drums?15:18;
totalChans=drums?20:18; totalChans=drums?20:18;
@ -1735,9 +1738,6 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
default: default:
case 1: case 2: case 8950: case 1: case 2: case 8950:
switch (flags&0xff) { switch (flags&0xff) {
case 0x00:
chipClock=COLOR_NTSC;
break;
case 0x01: case 0x01:
chipClock=COLOR_PAL*4.0/5.0; chipClock=COLOR_PAL*4.0/5.0;
break; break;
@ -1753,15 +1753,15 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
case 0x05: case 0x05:
chipClock=3500000.0; chipClock=3500000.0;
break; break;
default:
chipClock=COLOR_NTSC;
break;
} }
rate=chipClock/72; rate=chipClock/72;
chipRateBase=double(rate); chipRateBase=rate;
break; break;
case 3: case 3:
switch (flags&0xff) { switch (flags&0xff) {
case 0x00:
chipClock=COLOR_NTSC*4.0;
break;
case 0x01: case 0x01:
chipClock=COLOR_PAL*16.0/5.0; chipClock=COLOR_PAL*16.0/5.0;
break; break;
@ -1774,28 +1774,31 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
case 0x04: case 0x04:
chipClock=15000000.0; chipClock=15000000.0;
break; break;
default:
chipClock=COLOR_NTSC*4.0;
break;
} }
rate=chipClock/288; rate=chipClock/288;
chipRateBase=double(rate); chipRateBase=rate;
break; break;
case 4: case 4:
switch (flags&0xff) { switch (flags&0xff) {
case 0x02:
chipClock=33868800.0;
break;
case 0x00:
chipClock=COLOR_NTSC*8.0;
break;
case 0x01: case 0x01:
chipClock=COLOR_PAL*32.0/5.0; chipClock=COLOR_PAL*32.0/5.0;
break; break;
case 0x02:
chipClock=33868800.0;
break;
default:
chipClock=COLOR_NTSC*8.0;
break;
} }
chipRateBase=double(chipClock)/684.0;
rate=chipClock/768; rate=chipClock/768;
chipRateBase=chipClock/684;
break; break;
case 759: case 759:
rate=48000; rate=48000;
chipRateBase=double(rate); chipRateBase=rate;
chipClock=rate*288; chipClock=rate*288;
break; break;
} }

View file

@ -95,7 +95,7 @@ class DivPlatformOPL: public DivDispatch {
const unsigned char** slots; const unsigned char** slots;
const unsigned short* chanMap; const unsigned short* chanMap;
const unsigned char* outChanMap; const unsigned char* outChanMap;
double chipFreqBase, chipRateBase; int chipFreqBase, chipRateBase;
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank; int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
unsigned char lastBusy; unsigned char lastBusy;
unsigned char drumState; unsigned char drumState;

View file

@ -831,6 +831,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PRE_PORTA: case DIV_CMD_PRE_PORTA:
if (c.chan>=9 && !properDrums) return 0; 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -445,6 +445,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -471,6 +471,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -239,6 +239,7 @@ int DivPlatformPET::dispatch(DivCommand c) {
if (chan.active && c.value2) { if (chan.active && c.value2) {
if (parent->song.resetMacroOnPorta) chan.macroInit(parent->getIns(chan.ins,DIV_INS_PET)); 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; chan.inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -496,6 +496,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -265,6 +265,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_SAMPLE_POS: case DIV_CMD_SAMPLE_POS:

View file

@ -335,6 +335,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -267,6 +267,7 @@ int DivPlatformSCC::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -365,6 +365,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
return 127; return 127;
break; break;
case DIV_CMD_PRE_PORTA: 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -133,7 +133,7 @@ void DivPlatformSMS::tick(bool sysTick) {
if (i==3) CHIP_DIVIDER=noiseDivider; if (i==3) CHIP_DIVIDER=noiseDivider;
chan[i].std.next(); chan[i].std.next();
if (chan[i].std.vol.had) { 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; if (chan[i].outVol<0) chan[i].outVol=0;
// old formula // old formula
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4; // ((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 (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); 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; 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; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
return 15; return 15;

View file

@ -454,6 +454,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -431,6 +431,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -259,6 +259,7 @@ int DivPlatformTIA::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -933,6 +933,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
return 127; return 127;
break; break;
case DIV_CMD_PRE_PORTA: 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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:

View file

@ -361,6 +361,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_STD_NOISE_MODE: case DIV_CMD_STD_NOISE_MODE:

View file

@ -243,6 +243,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -399,6 +399,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -732,6 +732,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_SAMPLE_FREQ: case DIV_CMD_SAMPLE_FREQ:

View file

@ -293,6 +293,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_SAMPLE_POS: case DIV_CMD_SAMPLE_POS:

View file

@ -224,6 +224,7 @@ int DivPlatformZXBeeper::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:

View file

@ -334,6 +334,11 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].lastIns!=pat->data[whatRow][2]) { if (chan[i].lastIns!=pat->data[whatRow][2]) {
chan[i].lastIns=pat->data[whatRow][2]; chan[i].lastIns=pat->data[whatRow][2];
insChanged=true; 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 // note

View file

@ -114,6 +114,8 @@ enum DivSystem {
DIV_SYSTEM_YM2612_FRAC, DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT, DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_RESERVED_8, DIV_SYSTEM_RESERVED_8,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_DUMMY DIV_SYSTEM_DUMMY
}; };
@ -351,7 +353,7 @@ struct DivSong {
// - 1: stereo // - 1: stereo
// - YM2203: // - YM2203:
// - bit 0-4: clock rate // - bit 0-4: clock rate
// - 0: 3.58MHz (MTSC) // - 0: 3.58MHz (NTSC)
// - 1: 3.55MHz (PAL) // - 1: 3.55MHz (PAL)
// - 2: 4MHz // - 2: 4MHz
// - 3: 3MHz // - 3: 3MHz
@ -371,7 +373,7 @@ struct DivSong {
// - 2: FM: clock / 48, SSG: clock / 8 // - 2: FM: clock / 48, SSG: clock / 8
// - YM3526, YM3812, Y8950: // - YM3526, YM3812, Y8950:
// - bit 0-7: clock rate // - bit 0-7: clock rate
// - 0: 3.58MHz (MTSC) // - 0: 3.58MHz (NTSC)
// - 1: 3.55MHz (PAL) // - 1: 3.55MHz (PAL)
// - 2: 4MHz // - 2: 4MHz
// - 3: 3MHz // - 3: 3MHz
@ -379,7 +381,7 @@ struct DivSong {
// - 5: 3.5MHz // - 5: 3.5MHz
// - YMF262: // - YMF262:
// - bit 0-7: clock rate // - bit 0-7: clock rate
// - 0: 14.32MHz (MTSC) // - 0: 14.32MHz (NTSC)
// - 1: 14.19MHz (PAL) // - 1: 14.19MHz (PAL)
// - 2: 14MHz // - 2: 14MHz
// - 3: 16MHz // - 3: 16MHz
@ -387,7 +389,7 @@ struct DivSong {
// - YMF289B: (TODO) // - YMF289B: (TODO)
// - bit 0-7: clock rate // - bit 0-7: clock rate
// - 0: 33.8688MHz // - 0: 33.8688MHz
// - 1: 28.64MHz (MTSC) // - 1: 28.64MHz (NTSC)
// - 2: 28.38MHz (PAL) // - 2: 28.38MHz (PAL)
// - MSM6295: // - MSM6295:
// - bit 0-6: clock rate // - bit 0-6: clock rate
@ -418,7 +420,7 @@ struct DivSong {
// - YMZ280B: // - YMZ280B:
// - bit 0-7: clock rate // - bit 0-7: clock rate
// - 0: 16.9344MHz // - 0: 16.9344MHz
// - 1: 14.32MHz (MTSC) // - 1: 14.32MHz (NTSC)
// - 2: 14.19MHz (PAL) // - 2: 14.19MHz (PAL)
// - 3: 16MHz // - 3: 16MHz
// - 4: 16.67MHz // - 4: 16.67MHz
@ -495,6 +497,7 @@ struct DivSong {
bool volMacroLinger; bool volMacroLinger;
bool brokenOutVol; bool brokenOutVol;
bool e1e2StopOnSameNote; bool e1e2StopOnSameNote;
bool brokenPortaArp;
std::vector<DivInstrument*> ins; std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave; std::vector<DivWavetable*> wave;
@ -593,7 +596,8 @@ struct DivSong {
newVolumeScaling(true), newVolumeScaling(true),
volMacroLinger(true), volMacroLinger(true),
brokenOutVol(false), brokenOutVol(false),
e1e2StopOnSameNote(false) { e1e2StopOnSameNote(false),
brokenPortaArp(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;

View file

@ -165,25 +165,25 @@ String DivEngine::getSongSystemName(bool isMultiSystemAcceptable) {
} }
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC6) { 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) { 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) { 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) { if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) {
return "Famicom Disk System"; return "Famicom Disk System";
} }
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_N163) { 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) { 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) { 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) { if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) {
@ -2082,6 +2082,35 @@ void DivEngine::registerSystems() {
fmPostEffectHandler 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( sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
"this is a system designed for testing purposes.", "this is a system designed for testing purposes.",

View file

@ -207,31 +207,35 @@ void FurnaceGUI::drawCompatFlags() {
} }
ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("ExtCh channel status is shared among operators",&e->song.sharedExtStat);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("New SegaPCM features (macros and better panning)",&e->song.newSegaPCM);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("Old FM octave boundary behavior",&e->song.oldOctaveBoundary);
if (ImGui::IsItemHovered()) { 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); ImGui::Checkbox("No OPN2 DAC volume control",&e->song.noOPN2Vol);
if (ImGui::IsItemHovered()) { 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; if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;

View file

@ -447,7 +447,6 @@ void FurnaceGUI::doReplace() {
} }
if (!us.pat.empty()) { if (!us.pat.empty()) {
printf("pusher\n");
undoHist.push_back(us); undoHist.push_back(us);
redoHist.clear(); redoHist.clear();
if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front();
@ -997,16 +996,16 @@ void FurnaceGUI::drawFindReplace() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::BeginDisabled(!queryReplaceEffectValDo[i]); ImGui::BeginDisabled(!queryReplaceEffectValDo[i]);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); 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::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_SET) { 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]<0) queryReplaceEffectVal[i]=0;
if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
} }
} else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) { } 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;
if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
} }

View file

@ -589,49 +589,49 @@ void FurnaceGUI::initSystemPresets() {
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Konami VRC6", { "Famicom with Konami VRC6", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_VRC6, 64, 0, 0, DIV_SYSTEM_VRC6, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Konami VRC7", { "Famicom with Konami VRC7", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_VRC7, 64, 0, 0, DIV_SYSTEM_VRC7, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with MMC5", { "Famicom with MMC5", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_MMC5, 64, 0, 0, DIV_SYSTEM_MMC5, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Sunsoft 5B", { "Famicom with Sunsoft 5B", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 32, DIV_SYSTEM_AY8910, 64, 0, 32,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Namco C163", { "Famicom with Namco C163", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_N163, 64, 0, 112, DIV_SYSTEM_N163, 64, 0, 112,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Family Noraebang", { "Comboy with Family Noraebang", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_OPLL, 64, 0, 0, DIV_SYSTEM_OPLL, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( 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_NES, 64, 0, 0,
DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0,
0 0
@ -1282,6 +1282,29 @@ void FurnaceGUI::initSystemPresets() {
0 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( cat.systems.push_back(FurnaceGUISysDef(
"Sega Hang-On", { "Sega Hang-On", {
DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz
@ -2071,7 +2094,7 @@ void FurnaceGUI::initSystemPresets() {
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Konami VRC7", { "Famicom with Konami VRC7", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_VRC7, 64, 0, 0, DIV_SYSTEM_VRC7, 64, 0, 0,
0 0

View file

@ -569,7 +569,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
case DIV_SYSTEM_OPL3: case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL3_DRUMS: { case DIV_SYSTEM_OPL3_DRUMS: {
ImGui::Text("Clock rate:"); 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; copyOfFlags=(flags&(~255))|0;
} }
if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==1)) { 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)) { if (ImGui::RadioButton("16.9344MHz",(flags&255)==0)) {
copyOfFlags=(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; copyOfFlags=(flags&(~255))|1;
} }
if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==3)) { 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; 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_GB:
case DIV_SYSTEM_SWAN: case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_VERA: case DIV_SYSTEM_VERA:
case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_YMU759: case DIV_SYSTEM_YMU759:
case DIV_SYSTEM_PET: case DIV_SYSTEM_PET:
case DIV_SYSTEM_T6W28:
ImGui::Text("nothing to configure"); ImGui::Text("nothing to configure");
break; break;
default: default: