Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
* 'master' of https://github.com/tildearrow/furnace: (44 commits) GUI: now add ability to load VMEM Update sysDef.cpp GUI: add an option for it add preliminary TX81Z SysEx response OPL: drum volume OPL: implement hard reset GUI: add icon size setting GUI: macro UI refactor, part 4 Fix WIN32 define timing in IGFD Fix outdated calling convention in SAASound (maybe fix MinGW) GUI: macro UI refactor, part 3 GUI: macro UI refactor, part 2 Masochism Volatile Compiler GUI: is the macro UI back yet OPL: fix 4-op muting (I think) GUI: macro UI refactor, part 1 - PLEASE READ GUI: remove classic macro view Maybe just enforce for C++ Forbid standard extensions .ftm loading progress (incomplete, not working) ... # Conflicts: # src/engine/instrument.h # src/gui/insEdit.cpp Transwave Checkpoint
This commit is contained in:
commit
4efa711a2b
59 changed files with 5835 additions and 881 deletions
|
|
@ -135,6 +135,54 @@ const char* DivPlatformArcade::getEffectName(unsigned char effect) {
|
|||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to 1F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x5c:
|
||||
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x5d:
|
||||
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x5e:
|
||||
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5f:
|
||||
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -650,6 +698,134 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT2: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt2=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt2=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM_DEPTH: {
|
||||
amDepth=c.value;
|
||||
immWrite(0x19,amDepth);
|
||||
|
|
|
|||
|
|
@ -299,16 +299,16 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_ES5506(i,chan[i].std.arp.val);
|
||||
chan[i].nextNote=chan[i].std.arp.val;
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_ES5506(i,chan[i].note+chan[i].std.arp.val);
|
||||
chan[i].nextNote=chan[i].note+chan[i].std.arp.val;
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].noteChanged.note=1;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_ES5506(i,chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].nextNote=chan[i].note;
|
||||
chan[i].noteChanged.note=1;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
|
|
@ -535,12 +535,39 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].envChanged.changed=0;
|
||||
}
|
||||
if (chan[i].noteChanged.changed) {
|
||||
if (chan[i].noteChanged.offs) {
|
||||
if (chan[i].pcm.freqOffs!=chan[i].pcm.nextFreqOffs) {
|
||||
chan[i].pcm.freqOffs=chan[i].pcm.nextFreqOffs;
|
||||
const int nextFreq=NOTE_ES5506(i,chan[i].nextNote);
|
||||
if (chan[i].nextFreq!=nextFreq) {
|
||||
chan[i].nextFreq=nextFreq;
|
||||
chan[i].noteChanged.freq=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].noteChanged.note) { // note value changed or frequency offset is changed
|
||||
if (chan[i].prevNote!=chan[i].nextNote) {
|
||||
chan[i].prevNote=chan[i].nextNote;
|
||||
const int nextFreq=NOTE_ES5506(i,chan[i].nextNote);
|
||||
if (chan[i].nextFreq!=nextFreq) {
|
||||
chan[i].nextFreq=nextFreq;
|
||||
chan[i].noteChanged.freq=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].noteChanged.freq) {
|
||||
if (chan[i].baseFreq!=chan[i].nextFreq) {
|
||||
chan[i].baseFreq=chan[i].nextFreq;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
chan[i].noteChanged.changed=0;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
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;
|
||||
chan[i].freq=CLAMP_VAL(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2),0,0x1ffff);
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].pcm.index>=0) {
|
||||
if (chan[i].pcm.index>=0 && chan[i].pcm.index<parent->song.sampleLen) {
|
||||
chan[i].k1Prev=0xffff;
|
||||
chan[i].k2Prev=0xffff;
|
||||
pageWriteMask(0x00|i,0x5f,0x00,0x0303); // Wipeout CR
|
||||
|
|
@ -622,45 +649,87 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value];
|
||||
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind];
|
||||
chan[c.chan].sample=ins->amiga.useTransWave?transWaveInd.ind:
|
||||
(ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].ind:ins->amiga.initSample);
|
||||
double off=1.0;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.index=chan[c.chan].sample;
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
int sample=ins->amiga.initSample;
|
||||
if (ins->amiga.transWave.enable) {
|
||||
sample=transWaveInd.ind;
|
||||
} else if (ins->amiga.useNoteMap) {
|
||||
ins->amiga.noteMap[c.value].ind;
|
||||
}
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.index=sample;
|
||||
chan[c.chan].transWave.enable=ins->amiga.transWave.enable;
|
||||
chan[c.chan].transWave.sliceEnable=ins->amiga.transWave.sliceEnable;
|
||||
chan[c.chan].transWave.ind=ins->amiga.transWave.ind;
|
||||
DivSample* s=parent->getSample(sample);
|
||||
// get frequency offset
|
||||
double off=1.0;
|
||||
double center=s->centerRate;
|
||||
if (center<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(ins->amiga.useNoteMap?((double)ins->amiga.noteMap[c.value].freq/((double)s->centerRate*pow(2.0,((double)c.value-48.0)/12.0))):1.0)*((double)s->centerRate/8363.0);
|
||||
off=(double)center/8363.0;
|
||||
}
|
||||
if (ins->amiga.useNoteMap) {
|
||||
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)c.value-48.0)/12.0));
|
||||
chan[c.chan].pcm.note=c.value;
|
||||
}
|
||||
// get loop mode, transwave loop
|
||||
double loopStart=(double)s->loopStart;
|
||||
double loopEnd=(double)s->loopEnd;
|
||||
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
||||
if (ins->amiga.transWave.enable) {
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if (!s->isLoopable()) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
loopStart=(double)transWaveInd.loopStart;
|
||||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[c.chan].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[c.chan].transWave.slice=transWaveInd.slice;
|
||||
chan[c.chan].transWave.slicePos(transWaveInd.slice);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
}
|
||||
// get reversed
|
||||
bool reversed=ins->amiga.reversed;
|
||||
if (ins->amiga.transWave.enable&&transWaveInd.reversed!=2) {
|
||||
reversed=transWaveInd.reversed;
|
||||
} else if (ins->amiga.useNoteMap&¬eMapind.reversed!=2) {
|
||||
reversed=noteMapind.reversed;
|
||||
}
|
||||
const unsigned int start=s->offES5506<<10;
|
||||
const unsigned int length=s->samples-1;
|
||||
const unsigned int end=start+(length<<11);
|
||||
chan[c.chan].pcm.loopMode=(ins->amiga.useTransWave&&transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT)?transWaveInd.loopMode:(s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT);
|
||||
chan[c.chan].pcm.loopMode=loopMode;
|
||||
chan[c.chan].pcm.freqOffs=off;
|
||||
chan[c.chan].pcm.reversed=(ins->amiga.useTransWave&&transWaveInd.reversed!=2)?transWaveInd.reversed:ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].reversed:ins->amiga.reversed;
|
||||
chan[c.chan].pcm.reversed=reversed;
|
||||
chan[c.chan].pcm.bank=(s->offES5506>>22)&3;
|
||||
chan[c.chan].pcm.start=start;
|
||||
chan[c.chan].pcm.end=end;
|
||||
chan[c.chan].pcm.length=length;
|
||||
chan[c.chan].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
|
||||
chan[c.chan].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
|
||||
chan[c.chan].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
|
||||
chan[c.chan].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
|
||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xffff;
|
||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xffff;
|
||||
chan[c.chan].filter=ins->es5506.filter;
|
||||
chan[c.chan].envelope=ins->es5506.envelope;
|
||||
} else {
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].pcm.index=-1;
|
||||
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
||||
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].volChanged.changed=0xff;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].nextNote=chan[c.chan].note;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].noteChanged.changed=0xff;
|
||||
chan[c.chan].volChanged.changed=0xff;
|
||||
}
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=(0xffff*chan[c.chan].vol)/0xff;
|
||||
|
|
@ -679,7 +748,6 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
||||
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
|
|
@ -784,22 +852,24 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].envChanged.k2Ramp=1;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int nextFreq=chan[c.chan].baseFreq;
|
||||
const int destFreq=NOTE_ES5506(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
if (destFreq>nextFreq) {
|
||||
nextFreq+=c.value;
|
||||
if (nextFreq>=destFreq) {
|
||||
nextFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
nextFreq-=c.value;
|
||||
if (nextFreq<=destFreq) {
|
||||
nextFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].nextFreq=nextFreq;
|
||||
chan[c.chan].noteChanged.freq=1;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
|
|
@ -807,9 +877,9 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].nextNote=chan[c.chan].note+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0));
|
||||
chan[c.chan].noteChanged.note=1;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
|
|
@ -849,6 +919,7 @@ void DivPlatformES5506::forceIns() {
|
|||
for (int i=0; i<=chanMax; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].noteChanged.changed=0xff;
|
||||
chan[i].volChanged.changed=0xff;
|
||||
chan[i].filterChanged.changed=0xff;
|
||||
chan[i].envChanged.changed=0xff;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
struct Channel {
|
||||
struct PCM {
|
||||
int index, next;
|
||||
int note;
|
||||
double freqOffs;
|
||||
double nextFreqOffs;
|
||||
bool reversed;
|
||||
unsigned int bank;
|
||||
unsigned int start;
|
||||
|
|
@ -45,7 +47,9 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
PCM():
|
||||
index(-1),
|
||||
next(-1),
|
||||
note(0),
|
||||
freqOffs(1.0),
|
||||
nextFreqOffs(1.0),
|
||||
reversed(false),
|
||||
bank(0),
|
||||
start(0),
|
||||
|
|
@ -55,10 +59,25 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
loopEnd(0),
|
||||
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
|
||||
} pcm;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins, sample, wave;
|
||||
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
|
||||
unsigned int volMacroMax, panMacroMax;
|
||||
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
|
||||
|
||||
struct NoteChanged { // Note changed flags
|
||||
union { // pack flag bits in single byte
|
||||
struct { // flag bits
|
||||
unsigned char offs: 1; // frequency offset
|
||||
unsigned char note: 1; // note
|
||||
unsigned char freq: 1; // base frequency
|
||||
unsigned char dummy: 5; // dummy for bit padding
|
||||
};
|
||||
unsigned char changed; // Packed flags are stored here
|
||||
};
|
||||
|
||||
NoteChanged() :
|
||||
changed(0) {}
|
||||
} noteChanged;
|
||||
|
||||
struct VolChanged { // Volume changed flags
|
||||
union { // pack flag bits in single byte
|
||||
struct { // flag bits
|
||||
|
|
@ -114,6 +133,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
signed int lOut, rOut, oscOut;
|
||||
DivInstrumentES5506::Filter filter;
|
||||
DivInstrumentES5506::Envelope envelope;
|
||||
DivInstrumentAmiga::TransWave transWave;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
|
|
@ -130,11 +150,13 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
nextFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
note(0),
|
||||
nextNote(0),
|
||||
prevNote(0),
|
||||
ins(-1),
|
||||
sample(-1),
|
||||
wave(-1),
|
||||
volMacroMax(0xffff),
|
||||
panMacroMax(0xffff),
|
||||
|
|
|
|||
|
|
@ -77,6 +77,54 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
|
|||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to 1F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x5c:
|
||||
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x5d:
|
||||
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x5e:
|
||||
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5f:
|
||||
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -782,7 +830,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else {
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
|
|
@ -790,6 +838,134 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -235,6 +235,134 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
chan[c.chan].duty=c.value;
|
||||
rWrite(0x5000+c.chan*4,0x30|chan[c.chan].outVol|((chan[c.chan].duty&3)<<6));
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
|
|
|
|||
|
|
@ -521,6 +521,8 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].duty=c.value;
|
||||
if (c.chan==3) { // noise
|
||||
chan[c.chan].freqChanged=true;
|
||||
} else if (c.chan<2) {
|
||||
rWrite(0x4000+c.chan*4,0x30|chan[c.chan].outVol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_NES_SWEEP:
|
||||
|
|
|
|||
|
|
@ -197,6 +197,48 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) {
|
|||
case 0x1d:
|
||||
return "1Dxx: Set attack of operator 4 (0 to F; 4-op only)";
|
||||
break;
|
||||
case 0x2a:
|
||||
return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 3 in OPL2 and 0 to 7 in OPL3)";
|
||||
break;
|
||||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set vibrato (x: operator from 1 to 4 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set key scale level (x: operator from 1 to 4 (0 for all ops); y: level from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set envelope sustain (x: operator from 1 to 4 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to F; 4-op only)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to F; 4-op only)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -266,7 +308,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j]) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -393,7 +435,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j]) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -403,10 +445,34 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (i<melodicChans) {
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
oldWrites[baseAddr+ADDR_SL_RR]=-1;
|
||||
oldWrites[baseAddr+ADDR_KSL_TL]=-1;
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||
for (int k=0; k<5; k++) {
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,6 +588,9 @@ int DivPlatformOPL::toFreq(int freq) {
|
|||
|
||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (oplType<3 && ch<melodicChans) {
|
||||
fm.channel[outChanMap[ch]].muted=mute;
|
||||
}
|
||||
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
|
||||
chan[ch].fourOp=(ops==4);
|
||||
update4OpMask=true;
|
||||
|
|
@ -534,7 +603,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i]) {
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -589,7 +658,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i]) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -678,7 +747,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i]) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -811,7 +880,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value]) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -841,6 +910,222 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.dr=c.value2&15;
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.dr=c.value2&15;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.sl=c.value2&15;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.sl=c.value2&15;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.rr=c.value2&15;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.rr=c.value2&15;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.am=c.value2&1;
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.am=c.value2&1;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_VIB: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.vib=c.value2&1;
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.vib=c.value2&1;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SUS: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.sus=c.value2&1;
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.sus=c.value2&1;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_KSR: {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.ksr=c.value2&1;
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.ksr=c.value2&1;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_WS: {
|
||||
if (oplType<2) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.ws=c.value2&7;
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.ws=c.value2&7;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (oplType<2) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
op.ksl=c.value2&3;
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
op.ksl=c.value2&3;
|
||||
unsigned char slot=slots[c.value][c.chan];
|
||||
if (slot==255) break;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (!properDrumsSys) break;
|
||||
properDrums=c.value;
|
||||
|
|
@ -857,6 +1142,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
|
|
@ -900,7 +1188,7 @@ void DivPlatformOPL::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j]) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -968,10 +1256,12 @@ void DivPlatformOPL::reset() {
|
|||
properDrums=properDrumsSys;
|
||||
if (oplType==3) {
|
||||
chanMap=properDrums?chanMapOPL3Drums:chanMapOPL3;
|
||||
outChanMap=outChanMapOPL3;
|
||||
melodicChans=properDrums?15:18;
|
||||
totalChans=properDrums?20:18;
|
||||
} else {
|
||||
chanMap=properDrums?chanMapOPL2Drums:chanMapOPL2;
|
||||
outChanMap=outChanMapOPL2;
|
||||
melodicChans=properDrums?6:9;
|
||||
totalChans=properDrums?11:9;
|
||||
}
|
||||
|
|
@ -982,6 +1272,10 @@ void DivPlatformOPL::reset() {
|
|||
chan[i].vol=0x3f;
|
||||
chan[i].outVol=0x3f;
|
||||
}
|
||||
|
||||
if (oplType<3) for (int i=0; i<melodicChans; i++) {
|
||||
fm.channel[outChanMap[i]].muted=isMuted[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<512; i++) {
|
||||
oldWrites[i]=-1;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "../../../extern/Nuked-OPL3/opl3.h"
|
||||
#include "../../../extern/opl/opl3.h"
|
||||
|
||||
class DivPlatformOPL: public DivDispatch {
|
||||
protected:
|
||||
|
|
@ -31,7 +31,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp, hardReset;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
void macroInit(DivInstrument* which) {
|
||||
|
|
@ -56,6 +56,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
furnaceDac(false),
|
||||
inPorta(false),
|
||||
fourOp(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
pan(3) {
|
||||
state.ops=2;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,36 @@ const char* DivPlatformOPLL::getEffectName(unsigned char effect) {
|
|||
case 0x1b:
|
||||
return "1Bxx: Set attack of operator 2 (0 to F)";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 2 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 2 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 2 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set vibrato (x: operator from 1 to 2 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set key scale level (x: operator from 1 to 2 (0 for all ops); y: level from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set envelope sustain (x: operator from 1 to 2 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 2 (0 for all ops); y: enabled)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -447,6 +477,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
if (drums) {
|
||||
drums=false;
|
||||
immWrite(0x0e,0);
|
||||
drumState=0;
|
||||
}
|
||||
}
|
||||
if (c.chan<9) {
|
||||
|
|
@ -637,6 +668,150 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
rWrite(0x05,(car.ar<<4)|(car.dr));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.dr=c.value2&15;
|
||||
car.dr=c.value2&15;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.dr=c.value2&15;
|
||||
} else {
|
||||
car.dr=c.value2&15;
|
||||
}
|
||||
}
|
||||
rWrite(0x04,(mod.ar<<4)|(mod.dr));
|
||||
rWrite(0x05,(car.ar<<4)|(car.dr));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.sl=c.value2&15;
|
||||
car.sl=c.value2&15;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.sl=c.value2&15;
|
||||
} else {
|
||||
car.sl=c.value2&15;
|
||||
}
|
||||
}
|
||||
rWrite(0x06,(mod.sl<<4)|(mod.rr));
|
||||
rWrite(0x07,(car.sl<<4)|(car.rr));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.rr=c.value2&15;
|
||||
car.rr=c.value2&15;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.rr=c.value2&15;
|
||||
} else {
|
||||
car.rr=c.value2&15;
|
||||
}
|
||||
}
|
||||
rWrite(0x06,(mod.sl<<4)|(mod.rr));
|
||||
rWrite(0x07,(car.sl<<4)|(car.rr));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.am=c.value2&1;
|
||||
car.am=c.value2&1;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.am=c.value2&1;
|
||||
} else {
|
||||
car.am=c.value2&1;
|
||||
}
|
||||
}
|
||||
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
|
||||
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_VIB: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.vib=c.value2&1;
|
||||
car.vib=c.value2&1;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.vib=c.value2&1;
|
||||
} else {
|
||||
car.vib=c.value2&1;
|
||||
}
|
||||
}
|
||||
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
|
||||
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_KSR: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.ksr=c.value2&1;
|
||||
car.ksr=c.value2&1;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.ksr=c.value2&1;
|
||||
} else {
|
||||
car.ksr=c.value2&1;
|
||||
}
|
||||
}
|
||||
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
|
||||
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SUS: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.ssgEnv=c.value2?8:0;
|
||||
car.ssgEnv=c.value2?8:0;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.ssgEnv=c.value2?8:0;
|
||||
} else {
|
||||
car.ssgEnv=c.value2?8:0;
|
||||
}
|
||||
}
|
||||
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
|
||||
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
|
||||
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
|
||||
if (c.value<0) {
|
||||
mod.ksl=c.value2&3;
|
||||
car.ksl=c.value2&3;
|
||||
} else {
|
||||
if (c.value==0) {
|
||||
mod.ksl=c.value2&3;
|
||||
} else {
|
||||
car.ksl=c.value2&3;
|
||||
}
|
||||
}
|
||||
rWrite(0x02,(mod.ksl<<6)|(mod.tl&63));
|
||||
rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH:
|
||||
if (!properDrumsSys) break;
|
||||
if ((int)properDrums==c.value) break;
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ if (m_choffs == 0)
|
|||
#endif
|
||||
|
||||
// early out if the envelope is effectively off
|
||||
if (m_env_attenuation > EG_QUIET)
|
||||
if (m_env_attenuation > EG_QUIET && m_cache.eg_shift == 0)
|
||||
return 0;
|
||||
|
||||
// get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
|
||||
|
|
|
|||
|
|
@ -70,16 +70,24 @@
|
|||
// OPZ supports a "fixed frequency" mode for each operator, with a 3-bit
|
||||
// range and 4-bit frequency value, plus a 1-bit enable. Not sure how that
|
||||
// works at all, so it's not implemented.
|
||||
// note by tildearrow:
|
||||
// - I have verified behavior of this mode against real hardware.
|
||||
// after applying a small fix on the existing early implementation, it matches hardware.
|
||||
// this means fixed frequency is fully implemented and working.
|
||||
//
|
||||
// There are also several mystery fields in the operators which I have no
|
||||
// clue about: "fine" (4 bits), "eg_shift" (2 bits), and "rev" (3 bits).
|
||||
// eg_shift is some kind of envelope generator effect, but how it works is
|
||||
// unknown.
|
||||
// note by tildearrow:
|
||||
// - behavior of "fine" is now confirmed and matches hardware.
|
||||
//
|
||||
// Also, according to the site above, the panning controls are changed from
|
||||
// OPM, with a "mono" bit and only one control bit for the right channel.
|
||||
// Current implementation is just a guess.
|
||||
//
|
||||
// additional modifications by tildearrow for Furnace
|
||||
//
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
|
|
@ -409,9 +417,6 @@ uint32_t opz_registers::lfo_am_offset(uint32_t choffs) const
|
|||
|
||||
void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
|
||||
{
|
||||
// TODO: how does fixed frequency mode work? appears to be enabled by
|
||||
// op_fix_mode(), and controlled by op_fix_range(), op_fix_frequency()
|
||||
|
||||
// TODO: what is op_rev()?
|
||||
|
||||
// set up the easy stuff
|
||||
|
|
@ -467,8 +472,8 @@ void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata
|
|||
if (reverb != 0)
|
||||
cache.eg_rate[EG_REVERB] = std::min<uint32_t>(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]);
|
||||
|
||||
// set the envelope shift; TX81Z manual says operator 1 shift is fixed at "off"
|
||||
cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs);
|
||||
// set the envelope shift; TX81Z manual says operator 1 (actually operator 4) shift is fixed at "off"
|
||||
cache.eg_shift = ((opoffs & 0x18) == 0x18) ? 0 : op_eg_shift(opoffs);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,84 @@ const char* DivPlatformTX81Z::getEffectName(unsigned char effect) {
|
|||
case 0x1f:
|
||||
return "1Fxx: Set PM depth (0 to 7F)";
|
||||
break;
|
||||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
case 0x28:
|
||||
return "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)";
|
||||
break;
|
||||
case 0x2a:
|
||||
return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)";
|
||||
break;
|
||||
case 0x2b:
|
||||
return "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)";
|
||||
break;
|
||||
case 0x2c:
|
||||
return "2Cxy: Set fine multiplier (x: operator from 1 to 4 (0 for all ops); y: fine)";
|
||||
break;
|
||||
case 0x2f:
|
||||
return "2Fxx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x30: case 0x31: case 0x32: case 0x33:
|
||||
case 0x34: case 0x35: case 0x36: case 0x37:
|
||||
return "3xyy: Set fixed frequency of operator 1 (x: octave from 0 to 7; y: frequency)";
|
||||
break;
|
||||
case 0x38: case 0x39: case 0x3a: case 0x3b:
|
||||
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
|
||||
return "3xyy: Set fixed frequency of operator 2 (x: octave from 8 to F; y: frequency)";
|
||||
break;
|
||||
case 0x40: case 0x41: case 0x42: case 0x43:
|
||||
case 0x44: case 0x45: case 0x46: case 0x47:
|
||||
return "4xyy: Set fixed frequency of operator 3 (x: octave from 0 to 7; y: frequency)";
|
||||
break;
|
||||
case 0x48: case 0x49: case 0x4a: case 0x4b:
|
||||
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
|
||||
return "4xyy: Set fixed frequency of operator 4 (x: octave from 8 to F; y: frequency)";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to 1F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x5c:
|
||||
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x5d:
|
||||
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x5e:
|
||||
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5f:
|
||||
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -604,8 +680,10 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
if (!op.egt) {
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: {
|
||||
|
|
@ -639,6 +717,222 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT2: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt2=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt2=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (!op.egt) {
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
}
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
if (!op.egt) {
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EG_SHIFT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.ksl=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ksl=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_REV: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dam=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dam=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_WS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.ws=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ws=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FINE: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (!op.egt) {
|
||||
op.dvb=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
|
||||
}
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
if (!op.egt) {
|
||||
op.dvb=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FIXFREQ: {
|
||||
if (c.value<0 || c.value>3) break;
|
||||
printf("fixfreq %x\n",c.value2);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.egt=(c.value2>0);
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
|
||||
if (op.egt) {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|(((c.value2>>8)&7)<<4));
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(c.value2&15)|(op.ws<<4));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM_DEPTH: {
|
||||
amDepth=c.value;
|
||||
immWrite(0x19,amDepth);
|
||||
|
|
|
|||
|
|
@ -397,6 +397,54 @@ const char* DivPlatformYM2610::getEffectName(unsigned char effect) {
|
|||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to 1F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x5c:
|
||||
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x5d:
|
||||
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x5e:
|
||||
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5f:
|
||||
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1088,6 +1136,134 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -377,6 +377,54 @@ const char* DivPlatformYM2610B::getEffectName(unsigned char effect) {
|
|||
case 0x30:
|
||||
return "30xx: Toggle hard envelope reset on new notes";
|
||||
break;
|
||||
case 0x50:
|
||||
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
|
||||
break;
|
||||
case 0x51:
|
||||
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
|
||||
break;
|
||||
case 0x52:
|
||||
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
|
||||
break;
|
||||
case 0x53:
|
||||
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
|
||||
break;
|
||||
case 0x54:
|
||||
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
|
||||
break;
|
||||
case 0x55:
|
||||
return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)";
|
||||
break;
|
||||
case 0x56:
|
||||
return "56xx: Set decay of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x57:
|
||||
return "57xx: Set decay of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x58:
|
||||
return "58xx: Set decay of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x59:
|
||||
return "59xx: Set decay of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5a:
|
||||
return "5Axx: Set decay of operator 4 (0 to 1F)";
|
||||
break;
|
||||
case 0x5b:
|
||||
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
|
||||
break;
|
||||
case 0x5c:
|
||||
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
|
||||
break;
|
||||
case 0x5d:
|
||||
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
|
||||
break;
|
||||
case 0x5e:
|
||||
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
|
||||
break;
|
||||
case 0x5f:
|
||||
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1066,6 +1114,134 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -175,17 +175,146 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AR: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator op=ins->fm.op[i];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else {
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,17 +175,146 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AR: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator op=ins->fm.op[i];
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else {
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue