Add instrument for OPL4 PCM, Macros and Effects

TODO: Phase reset, Keyon/off
This commit is contained in:
cam900 2024-07-12 17:37:27 +09:00
parent 221fa5aa42
commit 65f48cc574
13 changed files with 423 additions and 88 deletions

View file

@ -263,6 +263,22 @@ enum DivDispatchCmds {
DIV_CMD_BIFURCATOR_STATE_LOAD,
DIV_CMD_BIFURCATOR_PARAMETER,
DIV_CMD_OPL4_PCM_MIX_FM, // (value)
DIV_CMD_OPL4_PCM_MIX_PCM, // (value)
DIV_CMD_OPL4_PCM_LFO, // (value)
DIV_CMD_OPL4_PCM_VIB, // (value)
DIV_CMD_OPL4_PCM_AM, // (value)
DIV_CMD_OPL4_PCM_AR, // (value)
DIV_CMD_OPL4_PCM_D1R, // (value)
DIV_CMD_OPL4_PCM_DL, // (value)
DIV_CMD_OPL4_PCM_D2R, // (value)
DIV_CMD_OPL4_PCM_RC, // (value)
DIV_CMD_OPL4_PCM_RR, // (value)
DIV_CMD_OPL4_PCM_DAMP, // (value)
DIV_CMD_OPL4_PCM_PSEUDO_REVERB, // (value)
DIV_CMD_OPL4_PCM_LFO_RESET, // (value)
DIV_CMD_OPL4_PCM_LEVEL_DIRECT, // (value)
DIV_CMD_MAX
};

View file

@ -938,7 +938,8 @@ void DivEngine::delUnusedSamples() {
i->type==DIV_INS_C219 ||
i->type==DIV_INS_NDS ||
i->type==DIV_INS_GBA_DMA ||
i->type==DIV_INS_GBA_MINMOD) {
i->type==DIV_INS_GBA_MINMOD ||
i->type==DIV_INS_OPL4PCM) {
if (i->amiga.initSample>=0 && i->amiga.initSample<song.sampleLen) {
isUsed[i->amiga.initSample]=true;
}

View file

@ -178,6 +178,15 @@ bool DivInstrumentMultiPCM::operator==(const DivInstrumentMultiPCM& other) {
);
}
bool DivInstrumentOPL4PCM::operator==(const DivInstrumentOPL4PCM& other) {
return (
_C(damp) &&
_C(pseudoReverb) &&
_C(lfoReset) &&
_C(levelDirect)
);
}
bool DivInstrumentWaveSynth::operator==(const DivInstrumentWaveSynth& other) {
return (
_C(wave1) &&
@ -848,6 +857,17 @@ void DivInstrument::writeFeatureS2(SafeWriter* w) {
FEATURE_END;
}
void DivInstrument::writeFeatureO4(SafeWriter* w) {
FEATURE_BEGIN("O4");
w->writeC(opl4pcm.damp);
w->writeC(opl4pcm.pseudoReverb);
w->writeC(opl4pcm.lfoReset);
w->writeC(opl4pcm.levelDirect);
FEATURE_END;
}
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
size_t blockStartSeek=0;
size_t blockEndSeek=0;
@ -894,6 +914,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
bool featureEF=false;
bool featurePN=false;
bool featureS2=false;
bool featureO4=false;
bool checkForWL=false;
@ -1137,6 +1158,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
feature64=true;
featureS2=true;
break;
case DIV_INS_OPL4PCM:
featureSM=true;
featureSL=true;
featureMP=true;
featureO4=true;
break;
case DIV_INS_MAX:
break;
case DIV_INS_NULL:
@ -1193,6 +1220,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
if (sid2!=defaultIns.sid2) {
featureS2=true;
}
if (opl4pcm!=defaultIns.opl4pcm) {
featureO4=true;
}
}
// check ins name
@ -1344,6 +1374,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
if (featureS2) {
writeFeatureS2(w);
}
if (featureO4) {
writeFeatureO4(w);
}
if (fui && (featureSL || featureWL)) {
w->write("EN",2);
@ -2172,6 +2205,17 @@ void DivInstrument::readFeatureS2(SafeReader& reader, short version) {
READ_FEAT_END;
}
void DivInstrument::readFeatureO4(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
opl4pcm.damp=reader.readC();
opl4pcm.pseudoReverb=reader.readC();
opl4pcm.lfoReset=reader.readC();
opl4pcm.levelDirect=reader.readC();
READ_FEAT_END;
}
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
unsigned char featCode[2];
bool volIsCutoff=false;
@ -2246,6 +2290,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
readFeaturePN(reader,version);
} else if (memcmp(featCode,"S2",2)==0) { // SID2
readFeatureS2(reader,version);
} else if (memcmp(featCode,"O4",2)==0) { // OPL4 PCM
readFeatureO4(reader,version);
} else {
if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
// nothing

View file

@ -94,6 +94,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_GBA_MINMOD=61,
DIV_INS_BIFURCATOR=62,
DIV_INS_SID2=63, // coincidence!
DIV_INS_OPL4PCM=64,
DIV_INS_MAX,
DIV_INS_NULL
};
@ -620,6 +621,22 @@ struct DivInstrumentMultiPCM {
}
};
struct DivInstrumentOPL4PCM {
bool damp, pseudoReverb, lfoReset, levelDirect;
bool operator==(const DivInstrumentOPL4PCM& other);
bool operator!=(const DivInstrumentOPL4PCM& other) {
return !(*this==other);
}
DivInstrumentOPL4PCM():
damp(false),
pseudoReverb(false),
lfoReset(false),
levelDirect(true) {
}
};
enum DivWaveSynthEffects {
DIV_WS_NONE=0,
// one waveform effects
@ -879,6 +896,7 @@ struct DivInstrument {
DivInstrumentESFM esfm;
DivInstrumentPowerNoise powernoise;
DivInstrumentSID2 sid2;
DivInstrumentOPL4PCM opl4pcm;
/**
* these are internal functions.
@ -906,6 +924,7 @@ struct DivInstrument {
void writeFeatureEF(SafeWriter* w);
void writeFeaturePN(SafeWriter* w);
void writeFeatureS2(SafeWriter* w);
void writeFeatureO4(SafeWriter* w);
void readFeatureNA(SafeReader& reader, short version);
void readFeatureFM(SafeReader& reader, short version);
@ -929,6 +948,7 @@ struct DivInstrument {
void readFeatureEF(SafeReader& reader, short version);
void readFeaturePN(SafeReader& reader, short version);
void readFeatureS2(SafeReader& reader, short version);
void readFeatureO4(SafeReader& reader, short version);
DivDataErrors readInsDataOld(SafeReader& reader, short version);
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);

View file

@ -1002,9 +1002,10 @@ void DivPlatformOPL::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.had) { // TODO: not working
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
chan[i].writeCtrl=true;
}
}
@ -1014,78 +1015,51 @@ void DivPlatformOPL::tick(bool sysTick) {
chan[i].writeCtrl=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
DivSample* s=parent->getSample(chan[i].sample);
unsigned char ctrl=0;
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,(524288*768)));
if (chan[i].freq<0x400) chan[i].freq=0x400;
if (chan[i].freq>0x4000000) chan[i].freq=0x4000000;
if (chan[i].freq>=0x2000000) {
chan[i].freqH=15;
} else if (chan[i].freq>=0x1000000) {
chan[i].freqH=14;
} else if (chan[i].freq>=0x800000) {
chan[i].freqH=13;
} else if (chan[i].freq>=0x400000) {
chan[i].freqH=12;
} else if (chan[i].freq>=0x200000) {
chan[i].freqH=11;
} else if (chan[i].freq>=0x100000) {
chan[i].freqH=10;
} else if (chan[i].freq>=0x80000) {
chan[i].freqH=9;
} else if (chan[i].freq>=0x40000) {
chan[i].freqH=8;
} else if (chan[i].freq>=0x20000) {
chan[i].freqH=7;
} else if (chan[i].freq>=0x10000) {
chan[i].freqH=6;
} else if (chan[i].freq>=0x8000) {
chan[i].freqH=5;
} else if (chan[i].freq>=0x4000) {
chan[i].freqH=4;
} else if (chan[i].freq>=0x2000) {
chan[i].freqH=3;
} else if (chan[i].freq>=0x1000) {
chan[i].freqH=2;
} else if (chan[i].freq>=0x800) {
chan[i].freqH=1;
} else {
chan[i].freqH=0;
}
chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff;
chan[i].freqH=8^chan[i].freqH;
ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(isMuted[i]?8:(chan[i].pan&0xf));
unsigned int waveNum=chan[i].sample;
if (ramSize<=0x200000) {
waveNum=MIN(waveNum,0x7f)|0x180;
}
if (chan[i].keyOn) {
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl&~0x80); // force keyoff first
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
rWrite(PCM_ADDR_WAVE_L+PCM_REG(i),waveNum&0xff);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
immWrite(PCM_ADDR_TL+(PCM_REG(i)),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
}
chan[i].writeCtrl=true;
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chan[i].writeCtrl=true;
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
rWrite(PCM_ADDR_FN_H_PR_OCT+PCM_REG(i),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7));
chan[i].freqChanged=false;
}
if (chan[i].writeCtrl) {
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl);
chan[i].writeCtrl=false;
}
if (chan[i].std.ex1.had) {
chan[i].lfo=chan[i].std.ex1.val&0x7;
rWrite(PCM_ADDR_LFO_VIB+PCM_REG(i),(chan[i].lfo<<3)|(chan[i].vib));
}
if (chan[i].std.fms.had) {
chan[i].vib=chan[i].std.fms.val&0x7;
rWrite(PCM_ADDR_LFO_VIB+PCM_REG(i),(chan[i].lfo<<3)|(chan[i].vib));
}
if (chan[i].std.ams.had) {
chan[i].am=chan[i].std.ams.val&0x7;
rWrite(PCM_ADDR_AM+PCM_REG(i),chan[i].am);
}
if (chan[i].std.ex2.had) {
chan[i].ar=chan[i].std.ex2.val&0xf;
rWrite(PCM_ADDR_AR_D1R+PCM_REG(i),(chan[i].ar<<4)|(chan[i].d1r));
}
if (chan[i].std.ex3.had) {
chan[i].d1r=chan[i].std.ex3.val&0xf;
rWrite(PCM_ADDR_AR_D1R+PCM_REG(i),(chan[i].ar<<4)|(chan[i].d1r));
}
if (chan[i].std.ex4.had) {
chan[i].dl=chan[i].std.ex4.val&0xf;
rWrite(PCM_ADDR_DL_D2R+PCM_REG(i),(chan[i].dl<<4)|(chan[i].d2r));
}
if (chan[i].std.ex5.had) {
chan[i].d2r=chan[i].std.ex5.val&0xf;
rWrite(PCM_ADDR_DL_D2R+PCM_REG(i),(chan[i].dl<<4)|(chan[i].d2r));
}
if (chan[i].std.ex6.had) {
chan[i].rc=chan[i].std.ex6.val&0xf;
rWrite(PCM_ADDR_RC_RR+PCM_REG(i),(chan[i].rc<<4)|(chan[i].rr));
}
if (chan[i].std.ex7.had) {
chan[i].rr=chan[i].std.ex7.val&0xf;
rWrite(PCM_ADDR_RC_RR+PCM_REG(i),(chan[i].rc<<4)|(chan[i].rr));
}
} else {
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
chan[i].std.next();
@ -1360,7 +1334,83 @@ void DivPlatformOPL::tick(bool sysTick) {
bool updateDrums=false;
for (int i=0; i<totalChans; i++) {
if (PCM_CHECK(i)) { // OPL4 PCM
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
DivSample* s=parent->getSample(chan[i].sample);
unsigned char ctrl=0;
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,(524288*768)));
if (chan[i].freq<0x400) chan[i].freq=0x400;
if (chan[i].freq>0x4000000) chan[i].freq=0x4000000;
if (chan[i].freq>=0x2000000) {
chan[i].freqH=15;
} else if (chan[i].freq>=0x1000000) {
chan[i].freqH=14;
} else if (chan[i].freq>=0x800000) {
chan[i].freqH=13;
} else if (chan[i].freq>=0x400000) {
chan[i].freqH=12;
} else if (chan[i].freq>=0x200000) {
chan[i].freqH=11;
} else if (chan[i].freq>=0x100000) {
chan[i].freqH=10;
} else if (chan[i].freq>=0x80000) {
chan[i].freqH=9;
} else if (chan[i].freq>=0x40000) {
chan[i].freqH=8;
} else if (chan[i].freq>=0x20000) {
chan[i].freqH=7;
} else if (chan[i].freq>=0x10000) {
chan[i].freqH=6;
} else if (chan[i].freq>=0x8000) {
chan[i].freqH=5;
} else if (chan[i].freq>=0x4000) {
chan[i].freqH=4;
} else if (chan[i].freq>=0x2000) {
chan[i].freqH=3;
} else if (chan[i].freq>=0x1000) {
chan[i].freqH=2;
} else if (chan[i].freq>=0x800) {
chan[i].freqH=1;
} else {
chan[i].freqH=0;
}
chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff;
chan[i].freqH=8^chan[i].freqH;
ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(chan[i].lfoReset?0x20:0)|(chan[i].ch?0x10:0)|(isMuted[i]?8:(chan[i].pan&0xf));
unsigned int waveNum=chan[i].sample;
if (ramSize<=0x200000) {
waveNum=CLAMP(waveNum,0,0x7f)|0x180;
}
if (chan[i].keyOn) {
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl&~0x80); // force keyoff first
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
rWrite(PCM_ADDR_WAVE_L+PCM_REG(i),waveNum&0xff);
rWrite(PCM_ADDR_LFO_VIB+PCM_REG(i),(chan[i].lfo<<3)|(chan[i].vib));
rWrite(PCM_ADDR_AR_D1R+PCM_REG(i),(chan[i].ar<<4)|(chan[i].d1r));
rWrite(PCM_ADDR_DL_D2R+PCM_REG(i),(chan[i].dl<<4)|(chan[i].d2r));
rWrite(PCM_ADDR_RC_RR+PCM_REG(i),(chan[i].rc<<4)|(chan[i].rr));
rWrite(PCM_ADDR_AM+PCM_REG(i),chan[i].am);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
immWrite(PCM_ADDR_TL+(PCM_REG(i)),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
}
chan[i].writeCtrl=true;
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chan[i].writeCtrl=true;
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
rWrite(PCM_ADDR_FN_H_PR_OCT+PCM_REG(i),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7));
chan[i].freqChanged=false;
}
if (chan[i].writeCtrl) {
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl);
chan[i].writeCtrl=false;
}
}
} else {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
@ -1655,6 +1705,35 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
if (ins->type==DIV_INS_OPL4PCM) {
chan[c.chan].lfo=ins->multipcm.lfo;
chan[c.chan].vib=ins->multipcm.vib;
chan[c.chan].am=ins->multipcm.am;
chan[c.chan].ar=ins->multipcm.ar;
chan[c.chan].d1r=ins->multipcm.d1r;
chan[c.chan].dl=ins->multipcm.dl;
chan[c.chan].d2r=ins->multipcm.d2r;
chan[c.chan].rc=ins->multipcm.rc;
chan[c.chan].rr=ins->multipcm.rr;
chan[c.chan].damp=ins->opl4pcm.damp;
chan[c.chan].pseudoReverb=ins->opl4pcm.pseudoReverb;
chan[c.chan].levelDirect=ins->opl4pcm.levelDirect;
chan[c.chan].lfoReset=ins->opl4pcm.lfoReset;
} else {
chan[c.chan].lfo=0;
chan[c.chan].vib=0;
chan[c.chan].am=0;
chan[c.chan].ar=15;
chan[c.chan].d1r=15;
chan[c.chan].dl=0;
chan[c.chan].d2r=0;
chan[c.chan].rc=15;
chan[c.chan].rr=15;
chan[c.chan].damp=false;
chan[c.chan].pseudoReverb=false;
chan[c.chan].levelDirect=true;
chan[c.chan].lfoReset=false;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
@ -1852,6 +1931,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
if (PCM_CHECK(c.chan)) {
chan[c.chan].ch=false;
chan[c.chan].pan=8^MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,15)+1,15);
chan[c.chan].freqChanged=true;
chan[c.chan].writeCtrl=true;
@ -1880,6 +1960,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_SURROUND_PANNING: {
if (PCM_CHECK(c.chan)) {
chan[c.chan].ch=true;
chan[c.chan].freqChanged=true;
chan[c.chan].writeCtrl=true;
break;
}
if (oplType!=3) break;
if (c.chan==adpcmChan) break;
@ -2331,9 +2417,99 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_HARD_RESET:
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_OPL4_PCM_MIX_FM:
if (chipType==4) {
rWrite(PCM_ADDR_MIX_FM,(CLAMP((0x70-(c.value&0x70)),0,0x70)>>1)|(CLAMP((7-(c.value&7)),0,7)));
}
break;
case DIV_CMD_OPL4_PCM_MIX_PCM:
if (chipType==4) {
rWrite(PCM_ADDR_MIX_PCM,(CLAMP((0x70-(c.value&0x70)),0,0x70)>>1)|(CLAMP((7-(c.value&7)),0,7)));
}
break;
case DIV_CMD_OPL4_PCM_LFO:
if (PCM_CHECK(c.chan)) {
chan[c.chan].lfo=c.value&7;
rWrite(PCM_ADDR_LFO_VIB+PCM_REG(c.chan),(chan[c.chan].lfo<<3)|(chan[c.chan].vib));
}
break;
case DIV_CMD_OPL4_PCM_VIB:
if (PCM_CHECK(c.chan)) {
chan[c.chan].vib=c.value&7;
rWrite(PCM_ADDR_LFO_VIB+PCM_REG(c.chan),(chan[c.chan].lfo<<3)|(chan[c.chan].vib));
}
break;
case DIV_CMD_OPL4_PCM_AM:
if (PCM_CHECK(c.chan)) {
chan[c.chan].am=c.value&7;
rWrite(PCM_ADDR_AM+PCM_REG(c.chan),chan[c.chan].am);
}
break;
case DIV_CMD_OPL4_PCM_AR:
if (PCM_CHECK(c.chan)) {
chan[c.chan].ar=c.value&0xf;
rWrite(PCM_ADDR_AR_D1R+PCM_REG(c.chan),(chan[c.chan].ar<<4)|(chan[c.chan].d1r));
}
break;
case DIV_CMD_OPL4_PCM_D1R:
if (PCM_CHECK(c.chan)) {
chan[c.chan].d1r=c.value&0xf;
rWrite(PCM_ADDR_AR_D1R+PCM_REG(c.chan),(chan[c.chan].ar<<4)|(chan[c.chan].d1r));
}
break;
case DIV_CMD_OPL4_PCM_DL:
if (PCM_CHECK(c.chan)) {
chan[c.chan].dl=c.value&0xf;
rWrite(PCM_ADDR_DL_D2R+PCM_REG(c.chan),(chan[c.chan].dl<<4)|(chan[c.chan].d2r));
}
break;
case DIV_CMD_OPL4_PCM_D2R:
if (PCM_CHECK(c.chan)) {
chan[c.chan].d2r=c.value&0xf;
rWrite(PCM_ADDR_DL_D2R+PCM_REG(c.chan),(chan[c.chan].dl<<4)|(chan[c.chan].d2r));
}
break;
case DIV_CMD_OPL4_PCM_RC:
if (PCM_CHECK(c.chan)) {
chan[c.chan].rc=c.value&0xf;
rWrite(PCM_ADDR_RC_RR+PCM_REG(c.chan),(chan[c.chan].rc<<4)|(chan[c.chan].rr));
}
break;
case DIV_CMD_OPL4_PCM_RR:
if (PCM_CHECK(c.chan)) {
chan[c.chan].rr=c.value&0xf;
rWrite(PCM_ADDR_RC_RR+PCM_REG(c.chan),(chan[c.chan].rc<<4)|(chan[c.chan].rr));
}
break;
case DIV_CMD_OPL4_PCM_DAMP:
if (PCM_CHECK(c.chan)) {
chan[c.chan].damp=c.value&1;
chan[c.chan].freqChanged=true;
chan[c.chan].writeCtrl=true;
}
break;
case DIV_CMD_OPL4_PCM_PSEUDO_REVERB:
if (PCM_CHECK(c.chan)) {
chan[c.chan].pseudoReverb=c.value&1;
chan[c.chan].freqChanged=true;
}
break;
case DIV_CMD_OPL4_PCM_LFO_RESET:
if (PCM_CHECK(c.chan)) {
chan[c.chan].lfoReset=c.value&1;
chan[c.chan].freqChanged=true;
chan[c.chan].writeCtrl=true;
}
break;
case DIV_CMD_OPL4_PCM_LEVEL_DIRECT:
if (PCM_CHECK(c.chan)) {
immWrite(PCM_ADDR_TL+PCM_REG(c.chan),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
@ -3019,6 +3195,7 @@ void DivPlatformOPL::renderSamples(int sysID) {
case DIV_SAMPLE_DEPTH_8BIT:
bitDepth=0;
break;
// TODO: 12 bit PCM
case DIV_SAMPLE_DEPTH_16BIT:
bitDepth=2;
break;

View file

@ -61,7 +61,8 @@ class DivPlatformOPL: public DivDispatch {
unsigned int freqH, freqL;
int sample, fixedFreq;
bool furnacePCM, fourOp, hardReset, writeCtrl;
bool levelDirect, damp, pseudoReverb;
bool levelDirect, damp, pseudoReverb, lfoReset, ch;
int lfo, vib, am, ar, d1r, d2r, dl, rc, rr;
int pan;
int macroVolMul;
Channel():
@ -77,6 +78,17 @@ class DivPlatformOPL: public DivDispatch {
levelDirect(true),
damp(false),
pseudoReverb(false),
lfoReset(false),
ch(false),
lfo(0),
vib(0),
am(0),
ar(15),
d1r(15),
d2r(0),
dl(0),
rc(15),
rr(15),
pan(3),
macroVolMul(64) {
state.ops=2;
@ -208,7 +220,6 @@ class DivPlatformOPL: public DivDispatch {
void renderSamples(int chipID);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
YMF278& getChip();
DivPlatformOPL():
pcmMemory(0x400000),
pcm(pcmMemory) {}

View file

@ -261,7 +261,23 @@ const char* cmdName[]={
"MINMOD_ECHO",
"BIFURCATOR_STATE_LOAD",
"BIFURCATOR_PARAMETER"
"BIFURCATOR_PARAMETER",
"OPL4_MIX_FM",
"OPL4_MIX_PCM",
"OPL4_LFO",
"OPL4_VIB",
"OPL4_AM",
"OPL4_AR",
"OPL4_D1R",
"OPL4_DL",
"OPL4_D2R",
"OPL4_RR",
"OPL4_RC",
"OPL4_DAMP",
"OPL4_PSEUDO_REVERB",
"OPL4_LFO_RESET",
"OPL4_LEVEL_DIRECT"
};
static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!");

View file

@ -599,6 +599,25 @@ void DivEngine::registerSystems() {
{0x5b, {DIV_CMD_FM_KSR, _("5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)"), effectOpVal<4>, effectValAnd<1>}},
};
EffectHandlerMap fmOPL4PostEffectHandlerMap(fmOPLPostEffectHandlerMap);
fmOPL4PostEffectHandlerMap.insert({
{0x1e, {DIV_CMD_OPL4_PCM_MIX_FM, _("1Exy: FM global level (left, right; 0 to 7)"), effectVal}},
{0x1f, {DIV_CMD_OPL4_PCM_MIX_PCM, _("1Fxy: PCM global level (left, right; 0 to 7)"), effectVal}},
{0x20, {DIV_CMD_OPL4_PCM_LFO, _("20xx: PCM LFO Rate (0 to 7)"), effectValAnd<7>}},
{0x21, {DIV_CMD_OPL4_PCM_VIB, _("21xx: PCM LFO PM Depth (0 to 7)"), effectValAnd<7>}},
{0x22, {DIV_CMD_OPL4_PCM_AM, _("22xx: PCM LFO AM Depth (0 to 7)"), effectValAnd<7>}},
{0x23, {DIV_CMD_OPL4_PCM_AR, _("23xx: PCM Attack Rate (0 to 15)"), effectValAnd<15>}},
{0x24, {DIV_CMD_OPL4_PCM_D1R, _("24xx: PCM Decay 1 Rate (0 to 15)"), effectValAnd<15>}},
{0x25, {DIV_CMD_OPL4_PCM_DL, _("25xx: PCM Decay Level (0 to 15)"), effectValAnd<15>}},
{0x26, {DIV_CMD_OPL4_PCM_D2R, _("26xx: PCM Decay 2 Rate (0 to 15)"), effectValAnd<15>}},
{0x27, {DIV_CMD_OPL4_PCM_RC, _("27xx: PCM Release Rate (0 to 15)"), effectValAnd<15>}},
{0x28, {DIV_CMD_OPL4_PCM_RR, _("28xx: PCM Rate Correction (0 to 15)"), effectValAnd<15>}},
{0x2c, {DIV_CMD_OPL4_PCM_DAMP, _("2Cxx: PCM Damp"), effectValAnd<1>}},
{0x2d, {DIV_CMD_OPL4_PCM_PSEUDO_REVERB, _("2Dxx: PCM Pseudo Reverb"), effectValAnd<1>}},
{0x2e, {DIV_CMD_OPL4_PCM_LFO_RESET, _("2Exx: PCM LFO Reset"), effectValAnd<1>}},
{0x2f, {DIV_CMD_OPL4_PCM_LEVEL_DIRECT, _("2Fxx: PCM Level Direct"), effectValAnd<1>}},
});
EffectHandlerMap c64PostEffectHandlerMap={
{0x10, {DIV_CMD_WAVE, _("10xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise)")}},
{0x11, {DIV_CMD_C64_CUTOFF, _("11xx: Set coarse cutoff (not recommended; use 4xxx instead)")}},
@ -1616,28 +1635,28 @@ void DivEngine::registerSystems() {
// to Grauw: feel free to change this to 24 during development of OPL4's PCM part.
// TODO: add 12-bit and 16-bit big-endian sample formats
sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef(
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
_("like OPL3, but this time it also has a 24-channel version of MultiPCM."),
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("FM 16"), _("FM 17"), _("FM 18"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
fmEffectHandlerMap,
fmOPLPostEffectHandlerMap
fmOPL4PostEffectHandlerMap
);
// TODO: same here
sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef(
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
_("the OPL4 but with drums mode turned on."),
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("Kick/FM 16"), _("Snare"), _("Tom"), _("Top"), _("HiHat"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM, DIV_INS_OPL4PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
fmOPLDrumsEffectHandlerMap,
fmOPLPostEffectHandlerMap
fmOPL4PostEffectHandlerMap
);
EffectHandlerMap es5506PreEffectHandlerMap={