Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace:
  dev90 - linear pitch macro option
  dev89 - C64: add test/gate macro
  YMU759: update system definition
  more changes to autoNoteOn
  SoundUnit: ring modulation
  GUI: improve the maximize auto-detection a bit
  GUI: possibly remember maximized state
  NES: fix audio resolution being too low
  update to-do list
  SoundUnit: more stuff
  SoundUnit: max cutoff 16383
  effectRows -> effectCols
  GUI: rename Amiga/Sample to Sample
  This is not a sample properties table
  Define the compact wave editor
  Menu option, settings file

# Conflicts:
#	src/gui/insEdit.cpp
This commit is contained in:
cam900 2022-04-28 16:05:01 +09:00
commit 3956b2cd30
55 changed files with 348 additions and 158 deletions

View file

@ -140,7 +140,7 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
for (int j=nextRow; j<song.patLen; j++) {
nextRow=0;
for (int k=0; k<chans; k++) {
for (int l=0; l<song.pat[k].effectRows; l++) {
for (int l=0; l<song.pat[k].effectCols; l++) {
effectVal=pat[k]->data[j][5+(l<<1)];
if (effectVal<0) effectVal=0;
if (pat[k]->data[j][4+(l<<1)]==0x0d) {
@ -945,21 +945,28 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in
return bf|(block<<bits);
}
int DivEngine::calcFreq(int base, int pitch, bool period, int octave) {
int DivEngine::calcFreq(int base, int pitch, bool period, int octave, int pitch2) {
if (song.linearPitch) {
// global pitch multiplier
int whatTheFuck=(1024+(globalPitch<<6)-(globalPitch<0?globalPitch-6:0));
if (whatTheFuck<1) whatTheFuck=1; // avoids division by zero but please kill me
if (song.pitchMacroIsLinear) {
pitch+=pitch2;
}
pitch+=2048;
if (pitch<0) pitch=0;
if (pitch>4095) pitch=4095;
return period?
((base*(reversePitchTable[pitch]))/whatTheFuck):
(((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024;
int ret=period?
((base*(reversePitchTable[pitch]))/whatTheFuck):
(((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024;
if (!song.pitchMacroIsLinear) {
ret+=period?(-pitch2):pitch2;
}
return ret;
}
return period?
base-pitch:
base+((pitch*octave)>>1);
base-pitch-pitch2:
base+((pitch*octave)>>1)+pitch2;
}
void DivEngine::play() {
@ -2103,7 +2110,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
}
do {
if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) {
if ((ins==-1 || ins>=song.insLen || getPreferInsType(finalChan)==getIns(ins)->type || getPreferInsSecondType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) {
chan[finalChan].midiNote=note;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
break;

View file

@ -44,8 +44,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev88"
#define DIV_ENGINE_VERSION 88
#define DIV_VERSION "dev90"
#define DIV_ENGINE_VERSION 90
// for imports
#define DIV_VERSION_MOD 0xff01
@ -470,7 +470,7 @@ class DivEngine {
unsigned short calcBaseFreqFNumBlock(double clock, double divider, int note, int bits);
// calculate frequency/period
int calcFreq(int base, int pitch, bool period=false, int octave=0);
int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0);
// find song loop position
void walkSong(int& loopOrder, int& loopRow, int& loopEnd);
@ -525,6 +525,9 @@ class DivEngine {
// get preferred instrument type
DivInstrumentType getPreferInsType(int ch);
// get alternate instrument type
DivInstrumentType getPreferInsSecondType(int ch);
// get song system name
const char* getSongSystemName();

View file

@ -637,14 +637,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
for (int i=0; i<getChannelCount(ds.system[0]); i++) {
DivChannelData& chan=ds.pat[i];
if (ds.version<0x0a) {
chan.effectRows=1;
chan.effectCols=1;
} else {
chan.effectRows=reader.readC();
chan.effectCols=reader.readC();
}
logD("%d fx rows: %d",i,chan.effectRows);
if (chan.effectRows>4 || chan.effectRows<1) {
logE("invalid effect row count %d. are you sure everything is ok?",chan.effectRows);
lastError="file is corrupt or unreadable at effect rows";
logD("%d fx rows: %d",i,chan.effectCols);
if (chan.effectCols>4 || chan.effectCols<1) {
logE("invalid effect column count %d. are you sure everything is ok?",chan.effectCols);
lastError="file is corrupt or unreadable at effect columns";
delete[] file;
return false;
}
@ -694,7 +694,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
pat->data[k][3]=(pat->data[k][3]&3)*5;
}
}
for (int l=0; l<chan.effectRows; l++) {
for (int l=0; l<chan.effectCols; l++) {
// effect
pat->data[k][4+(l<<1)]=reader.readS();
pat->data[k][5+(l<<1)]=reader.readS();
@ -1011,6 +1011,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<86) {
ds.snDutyReset=true;
}
if (ds.version<90) {
ds.pitchMacroIsLinear=false;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1291,10 +1294,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
for (int i=0; i<tchans; i++) {
ds.pat[i].effectRows=reader.readC();
if (ds.pat[i].effectRows<1 || ds.pat[i].effectRows>8) {
logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectRows);
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectRows);
ds.pat[i].effectCols=reader.readC();
if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) {
logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols);
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectCols);
delete[] file;
return false;
}
@ -1372,7 +1375,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<20; i++) {
if (ds.version>=90) {
ds.pitchMacroIsLinear=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<19; i++) {
reader.readC();
}
}
@ -1572,7 +1580,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
pat->data[j][1]=reader.readS();
pat->data[j][2]=reader.readS();
pat->data[j][3]=reader.readS();
for (int k=0; k<ds.pat[chan].effectRows; k++) {
for (int k=0; k<ds.pat[chan].effectCols; k++) {
pat->data[j][4+(k<<1)]=reader.readS();
pat->data[j][5+(k<<1)]=reader.readS();
}
@ -1973,7 +1981,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
}
}
}
ds.pat[ch].effectRows=fxCols;
ds.pat[ch].effectCols=fxCols;
}
ds.pal=false;
@ -1990,7 +1998,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.chanShortName[i]=fmt::sprintf("C%d",i+1);
}
for(int i=chCount; i<ds.systemLen*4; i++) {
ds.pat[i].effectRows=1;
ds.pat[i].effectCols=1;
ds.chanShow[i]=false;
}
@ -2294,7 +2302,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
}
for (int i=0; i<chans; i++) {
w->writeC(song.pat[i].effectRows);
w->writeC(song.pat[i].effectCols);
}
for (int i=0; i<chans; i++) {
@ -2330,7 +2338,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.newSegaPCM);
w->writeC(song.fbPortaPause);
w->writeC(song.snDutyReset);
for (int i=0; i<20; i++) {
w->writeC(song.pitchMacroIsLinear);
for (int i=0; i<19; i++) {
w->writeC(0);
}
@ -2384,7 +2393,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeS(pat->data[j][1]); // octave
w->writeS(pat->data[j][2]); // instrument
w->writeS(pat->data[j][3]); // volume
w->write(&pat->data[j][4],2*song.pat[i>>16].effectRows*2); // effects
w->write(&pat->data[j][4],2*song.pat[i>>16].effectCols*2); // effects
}
w->writeString(pat->name,false);
@ -2730,7 +2739,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
}
for (int i=0; i<getChannelCount(sys); i++) {
w->writeC(song.pat[i].effectRows);
w->writeC(song.pat[i].effectCols);
for (int j=0; j<song.ordersLen; j++) {
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false);
@ -2738,7 +2747,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeS(pat->data[k][0]); // note
w->writeS(pat->data[k][1]); // octave
w->writeS(pat->data[k][3]); // volume
w->write(&pat->data[k][4],2*song.pat[i].effectRows*2); // effects
w->write(&pat->data[k][4],2*song.pat[i].effectCols*2); // effects
w->writeS(pat->data[k][2]); // instrument
}
}

View file

@ -507,6 +507,9 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(std.ex6Macro.mode);
w->writeC(std.ex7Macro.mode);
w->writeC(std.ex8Macro.mode);
// C64 no test
w->writeC(c64.noTest);
}
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
@ -1014,6 +1017,11 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
std.ex8Macro.mode=reader.readC();
}
// C64 no test
if (version>=89) {
c64.noTest=reader.readC();
}
return DIV_DATA_SUCCESS;
}

View file

@ -265,7 +265,7 @@ struct DivInstrumentC64 {
unsigned char a, d, s, r;
unsigned short duty;
unsigned char ringMod, oscSync;
bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs;
bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs, noTest;
unsigned char res;
unsigned short cut;
bool hp, lp, bp, ch3off;
@ -287,6 +287,7 @@ struct DivInstrumentC64 {
initFilter(false),
dutyIsAbs(false),
filterIsAbs(false),
noTest(false),
res(0),
cut(0),
hp(false),

View file

@ -130,6 +130,6 @@ SafeReader* DivPattern::compile(int len, int fxRows) {
}
DivChannelData::DivChannelData():
effectRows(1) {
effectCols(1) {
memset(data,0,256*sizeof(void*));
}

View file

@ -40,7 +40,7 @@ struct DivPattern {
};
struct DivChannelData {
unsigned char effectRows;
unsigned char effectCols;
// data goes as follows: data[ROW][TYPE]
// TYPE is:
// 0: note

View file

@ -207,7 +207,7 @@ void DivPlatformAmiga::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {

View file

@ -239,7 +239,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].keyOn) {
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));

View file

@ -261,7 +261,7 @@ void DivPlatformAY8930::tick(bool sysTick) {
immWrite(0x1a,ayNoiseOr);
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>65535) chan[i].freq=65535;
if (chan[i].keyOn) {
if (chan[i].insChanged) {

View file

@ -170,16 +170,17 @@ void DivPlatformC64::tick(bool sysTick) {
if (chan[i].testWhen>0) {
if (--chan[i].testWhen<1) {
if (!chan[i].resetMask && !chan[i].inPorta) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
rWrite(i*7+5,0);
rWrite(i*7+6,0);
rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1));
rWrite(i*7+4,(chan[i].wave<<4)|(ins->c64.noTest?0:8)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1));
}
}
}
}
if (chan[i].std.wave.had) {
chan[i].wave=chan[i].std.wave.val;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
@ -196,20 +197,25 @@ void DivPlatformC64::tick(bool sysTick) {
chan[i].sync=chan[i].std.ex3.val&1;
chan[i].ring=chan[i].std.ex3.val&2;
chan[i].freqChanged=true;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
}
if (chan[i].std.ex4.had) {
chan[i].test=chan[i].std.ex4.val&1;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val);
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
if (chan[i].keyOn) {
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
}
if (chan[i].keyOff) {
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0);
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|0);
}
rWrite(i*7,chan[i].freq&0xff);
rWrite(i*7+1,chan[i].freq>>8);
@ -231,6 +237,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].test=false;
if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) {
chan[c.chan].duty=ins->c64.duty;
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
@ -335,7 +342,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
@ -416,11 +423,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case 4:
chan[c.chan].ring=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
break;
case 5:
chan[c.chan].sync=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
break;
case 6:
filtControl&=7;

View file

@ -30,7 +30,7 @@ class DivPlatformC64: public DivDispatch {
unsigned char sweep, wave, attack, decay, sustain, release;
short duty;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter;
bool resetMask, resetFilter, resetDuty, ring, sync;
bool resetMask, resetFilter, resetDuty, ring, sync, test;
signed char vol, outVol;
DivMacroInt std;
Channel():
@ -61,6 +61,7 @@ class DivPlatformC64: public DivDispatch {
resetDuty(false),
ring(false),
sync(false),
test(false),
vol(15) {}
};
Channel chan[3];

View file

@ -436,7 +436,7 @@ void DivPlatformES5506::tick(bool sysTick) {
chan[i].envChanged.changed=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val);
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff;
if (chan[i].keyOn) {

View file

@ -177,7 +177,7 @@ void DivPlatformFDS::tick(bool sysTick) {
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val);
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {

View file

@ -227,7 +227,7 @@ void DivPlatformGB::tick(bool sysTick) {
if (ntPos>255) ntPos=255;
chan[i].freq=noiseTable[ntPos];
} else {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>2047) chan[i].freq=2047;
if (chan[i].freq<0) chan[i].freq=0;
}

View file

@ -412,7 +412,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val;
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].std.pitch.val);
int block=(chan[i].baseFreq&0xf800)>>11;
if (fNum<0) fNum=0;
if (fNum>2047) {

View file

@ -307,7 +307,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
unsigned char writeMask=2;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val;
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].std.pitch.val);
int block=(opChan[i].baseFreq&0xf800)>>11;
if (fNum<0) fNum=0;
if (fNum>2047) {

View file

@ -195,7 +195,7 @@ void DivPlatformLynx::tick(bool sysTick) {
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
chan[i].lfsr=-1;
}
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
WRITE_FEEDBACK(i, chan[i].duty.feedback);

View file

@ -134,7 +134,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1;
if (chan[i].freq>2047) chan[i].freq=2047;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {

View file

@ -347,7 +347,7 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].waveUpdated=false;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].std.pitch.val);
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
if (chan[i].keyOn) {

View file

@ -109,7 +109,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
if (nes->apu.clocked) {
nes->apu.clocked=false;
}
int sample=(pulse_output(nes)+tnd_output(nes));
int sample=(pulse_output(nes)+tnd_output(nes))<<6;
if (sample>32767) sample=32767;
if (sample<-32768) sample=-32768;
bufL[i]=sample;
@ -218,7 +218,7 @@ void DivPlatformNES::tick(bool sysTick) {
if (ntPos>252) ntPos=252;
chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]);
} else {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1;
if (chan[i].freq>2047) chan[i].freq=2047;
if (chan[i].freq<0) chan[i].freq=0;
}
@ -472,7 +472,7 @@ int DivPlatformNES::getRegisterPoolSize() {
}
float DivPlatformNES::getPostAmp() {
return 128.0f;
return 2.0f;
}
void DivPlatformNES::reset() {

View file

@ -417,7 +417,7 @@ void DivPlatformOPL::tick(bool sysTick) {
bool updateDrums=false;
for (int i=0; i<totalChans; i++) {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val);
if (chan[i].freq>131071) chan[i].freq=131071;
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
chan[i].freqH=freqt>>8;

View file

@ -261,7 +261,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
for (int i=0; i<11; i++) {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val);
if (chan[i].freq>262143) chan[i].freq=262143;
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
chan[i].freqL=freqt&0xff;

View file

@ -217,7 +217,7 @@ void DivPlatformPCE::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].furnaceDac) {
double off=1.0;
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {

View file

@ -193,7 +193,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>65535) chan[i].freq=65535;
if (chan[i].keyOn) {

View file

@ -116,7 +116,7 @@ void DivPlatformPET::tick(bool sysTick) {
chan.freqChanged=true;
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val;
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.std.pitch.val);
if (chan.freq>257) chan.freq=257;
if (chan.freq<2) chan.freq=2;
rWrite(8,chan.freq-2);

View file

@ -331,7 +331,7 @@ void DivPlatformQSound::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val);
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
if (chan[i].keyOn) {
rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank);

View file

@ -194,7 +194,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
rWrite(0x18+(i/3),saaEnv[i/3]);
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>65535) chan[i].freq=65535;
if (chan[i].freq>=32768) {
chan[i].freqH=7;

View file

@ -106,7 +106,7 @@ void DivPlatformSMS::tick(bool sysTick) {
}
for (int i=0; i<3; i++) {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (chan[i].freq>1023) chan[i].freq=1023;
if (chan[i].freq<8) chan[i].freq=1;
//if (chan[i].actualNote>0x5d) chan[i].freq=0x01;
@ -121,7 +121,7 @@ void DivPlatformSMS::tick(bool sysTick) {
}
}
if (chan[3].freqChanged || updateSNMode) {
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val;
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].std.pitch.val);
if (chan[3].freq>1023) chan[3].freq=1023;
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
if (snNoiseMode&2) { // take period from channel 3

View file

@ -33,6 +33,10 @@ void SoundUnit::NextSample(short* l, short* r) {
ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255];
break;
}
if (chan[i].flags.ring) {
ns[i]=(ns[i]*ns[(i+1)&7])>>7;
}
if (chan[i].flags.pcm) {
if (chan[i].freq>0x8000) {

View file

@ -32,6 +32,62 @@ const char** DivPlatformSoundUnit::getRegisterSheet() {
}
const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set waveform (0 to 7)";
break;
case 0x12:
return "12xx: Set pulse width (0 to 7F)";
break;
case 0x13:
return "13xx: Set resonance (0 to F)";
break;
case 0x14:
return "14xx: Set filter mode (bit 0: ring mod; bit 1: low pass; bit 2: band pass; bit 3: high pass)";
break;
case 0x15:
return "15xx: Set frequency sweep period low byte";
break;
case 0x16:
return "16xx: Set frequency sweep period high byte";
break;
case 0x17:
return "17xx: Set volume sweep period low byte";
break;
case 0x18:
return "18xx: Set volume sweep period high byte";
break;
case 0x19:
return "19xx: Set cutoff sweep period low byte";
break;
case 0x1a:
return "1Axx: Set cutoff sweep period high byte";
break;
case 0x1b:
return "1Bxx: Set frequency sweep boundary";
break;
case 0x1c:
return "1Cxx: Set volume sweep boundary";
break;
case 0x1d:
return "1Dxx: Set cutoff sweep boundary";
break;
case 0x20:
return "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)";
break;
case 0x21:
return "21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direciton is up; bit 6: loop; bit 7: alternate)";
break;
case 0x22:
return "22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)";
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b:
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
return "4xxx: Set cutoff (0 to FFF)";
break;
}
return NULL;
}
@ -50,6 +106,12 @@ void DivPlatformSoundUnit::writeControl(int ch) {
chWrite(ch,0x04,(chan[ch].wave&7)|(chan[ch].pcm<<3)|(chan[ch].control<<4));
}
void DivPlatformSoundUnit::writeControlUpper(int ch) {
chWrite(ch,0x05,((int)chan[ch].phaseReset)|(chan[ch].filterPhaseReset<<1)|(chan[ch].pcmLoop<<2)|(chan[ch].timerSync<<3)|(chan[ch].freqSweep<<4)|(chan[ch].volSweep<<5)|(chan[ch].cutSweep<<6));
chan[ch].phaseReset=false;
chan[ch].filterPhaseReset=false;
}
void DivPlatformSoundUnit::tick(bool sysTick) {
for (int i=0; i<8; i++) {
chan[i].std.next();
@ -80,6 +142,10 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
chan[i].wave=chan[i].std.wave.val&7;
writeControl(i);
}
if (chan[i].std.phaseReset.had) {
chan[i].phaseReset=chan[i].std.phaseReset.val;
writeControlUpper(i);
}
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val;
chWrite(i,0x03,chan[i].pan);
@ -88,7 +154,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
chan[i].cutoff=chan[i].std.ex1.val;
chan[i].cutoff=chan[i].std.ex1.val&16383;
chWrite(i,0x06,chan[i].cutoff&0xff);
chWrite(i,0x07,chan[i].cutoff>>8);
}
@ -102,7 +168,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val);
chWrite(i,0x00,chan[i].freq&0xff);
chWrite(i,0x01,chan[i].freq>>8);
if (chan[i].freq>65535) chan[i].freq=65535;

View file

@ -31,7 +31,8 @@ class DivPlatformSoundUnit: public DivDispatch {
int ins, cutoff, res, control;
signed char pan;
unsigned char duty;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, phaseReset, filterPhaseReset;
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep;
signed char vol, outVol, wave;
DivMacroInt std;
Channel():
@ -53,9 +54,16 @@ class DivPlatformSoundUnit: public DivDispatch {
inPorta(false),
noise(false),
pcm(false),
phaseReset(false),
filterPhaseReset(false),
pcmLoop(false),
timerSync(false),
freqSweep(false),
volSweep(false),
cutSweep(false),
vol(127),
outVol(127),
wave(-1) {}
wave(0) {}
};
Channel chan[8];
bool isMuted[8];
@ -74,6 +82,7 @@ class DivPlatformSoundUnit: public DivDispatch {
SoundUnit* su;
unsigned char regPool[128];
void writeControl(int ch);
void writeControlUpper(int ch);
friend void putDispatchChan(void*,int,int);
public:

View file

@ -194,7 +194,7 @@ void DivPlatformSwan::tick(bool sysTick) {
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (i==1 && pcm && furnaceDac) {
double off=1.0;
if (dacSample>=0 && dacSample<parent->song.sampleLen) {

View file

@ -194,7 +194,7 @@ void DivPlatformVERA::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val);
if (chan[i].freq>65535) chan[i].freq=65535;
rWrite(i,0,chan[i].freq&0xff);
rWrite(i,1,(chan[i].freq>>8)&0xff);

View file

@ -123,7 +123,7 @@ void DivPlatformVIC20::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val);
if (i<3) {
chan[i].freq>>=(2-i);
} else {

View file

@ -183,9 +183,9 @@ void DivPlatformVRC6::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (i==2) { // sawtooth
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1;
} else { // pulse
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1;
if (chan[i].furnaceDac) {
double off=1.0;
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {

View file

@ -476,7 +476,7 @@ void DivPlatformX1_010::tick(bool sysTick) {
chan[i].envChanged=false;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val);
if (chan[i].pcm) {
if (chan[i].freq<1) chan[i].freq=1;
if (chan[i].freq>255) chan[i].freq=255;

View file

@ -572,7 +572,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
for (int i=0; i<4; i++) {
if (i==1 && extMode) continue;
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val);
if (chan[i].freq>262143) chan[i].freq=262143;
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);

View file

@ -635,7 +635,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val);
if (chan[i].freq>262143) chan[i].freq=262143;
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);

View file

@ -955,7 +955,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
int whatRow=afterDelay?chan[i].delayRow:curRow;
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false);
// pre effects
if (!afterDelay) for (int j=0; j<song.pat[i].effectRows; j++) {
if (!afterDelay) for (int j=0; j<song.pat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)];
@ -1064,7 +1064,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
bool calledPorta=false;
// effects
for (int j=0; j<song.pat[i].effectRows; j++) {
for (int j=0; j<song.pat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)];
@ -1384,7 +1384,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].noteOnInhibit=false;
// post effects
for (int j=0; j<song.pat[i].effectRows; j++) {
for (int j=0; j<song.pat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)];
@ -1421,7 +1421,7 @@ void DivEngine::nextRow() {
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
strcat(pb3,pb2);
}
for (int j=0; j<song.pat[i].effectRows; j++) {
for (int j=0; j<song.pat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==-1) {
strcat(pb3,"\x1b[m--");
} else {
@ -1495,7 +1495,7 @@ void DivEngine::nextRow() {
if (song.oneTickCut) {
bool doPrepareCut=true;
for (int j=0; j<song.pat[i].effectRows; j++) {
for (int j=0; j<song.pat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==0x03) {
doPrepareCut=false;
break;

View file

@ -336,6 +336,7 @@ struct DivSong {
bool newSegaPCM;
bool fbPortaPause;
bool snDutyReset;
bool pitchMacroIsLinear;
DivOrders orders;
std::vector<DivInstrument*> ins;
@ -441,7 +442,8 @@ struct DivSong {
e1e2AlsoTakePriority(false),
newSegaPCM(true),
fbPortaPause(false),
snDutyReset(false) {
snDutyReset(false),
pitchMacroIsLinear(true) {
for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64;

View file

@ -305,6 +305,12 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][0];
}
DivInstrumentType DivEngine::getPreferInsSecondType(int chan) {
if (chan<0 || chan>chans) return DIV_INS_NULL;
if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_NULL;
return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][1];
}
int DivEngine::minVGMVersion(DivSystem which) {
switch (which) {
case DIV_SYSTEM_YM2612:
@ -365,10 +371,10 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef(
"Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false,
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // type
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} // ins
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} // ins
);
sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef(