OPZ: implement LFO2

issue #831

untested and I can't test now
This commit is contained in:
tildearrow 2023-02-03 17:00:15 -05:00
parent d546d135b9
commit 43ba2ff8f2
7 changed files with 104 additions and 16 deletions

View file

@ -45,6 +45,16 @@ no plans have been made for TX81Z MIDI passthrough, because:
- `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3.
- `1Dxx`: set attack of operator 4.
- `1Exx`: set LFO AM depth.
- `1Fxx`: set LFO PM depth.
- `24xx`: set LFO 2 speed.
- `25xx`: set LFO 2 waveform. `xx` may be one of the following:
- `00`: saw
- `01`: square
- `02`: triangle
- `03`: noise
- `26xx`: set LFO 2 AM depth.
- `27xx`: set LFO 2 PM depth.
- `28xy`: set reverb of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.

View file

@ -103,9 +103,8 @@ enum DivDispatchCmds {
DIV_CMD_FM_AM_DEPTH, // (depth)
DIV_CMD_FM_PM_DEPTH, // (depth)
DIV_CMD_GENESIS_LFO, // unused?
DIV_CMD_ARCADE_LFO, // unused?
DIV_CMD_FM_LFO2, // (speed)
DIV_CMD_FM_LFO2_WAVE, // (waveform)
DIV_CMD_STD_NOISE_FREQ, // (freq)
DIV_CMD_STD_NOISE_MODE, // (mode)
@ -215,6 +214,9 @@ enum DivDispatchCmds {
DIV_CMD_SURROUND_PANNING, // (out, val)
DIV_CMD_FM_AM2_DEPTH, // (depth)
DIV_CMD_FM_PM2_DEPTH, // (depth)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX

View file

@ -41,8 +41,14 @@ class DivPlatformOPM: public DivPlatformFMBase {
0x00, 0x08, 0x10, 0x18
};
unsigned char lfoValue, lfoValue2, lfoShape, lfoShape2;
DivPlatformOPM():
DivPlatformFMBase() {}
DivPlatformFMBase(),
lfoValue(0),
lfoValue2(0),
lfoShape(0),
lfoShape2(0) {}
};
#endif

View file

@ -26,6 +26,8 @@
#define ADDR_WS_FINE 0x100
// actually 0xc0 but bit 5 of data selects address
#define ADDR_EGS_REV 0x120
// actually 0x38 but bits 7 and 2 select address
#define ADDR_FMS2_AMS2 0x140
const char* regCheatSheetOPZ[]={
"Test", "00",
@ -139,7 +141,8 @@ void DivPlatformTX81Z::tick(bool sysTick) {
}
if (chan[i].std.wave.had) {
rWrite(0x1b,chan[i].std.wave.val&3);
lfoShape=chan[i].std.wave.val&3;
immWrite(0x1b,lfoShape|(lfoShape2<<2));
}
if (chan[i].std.pitch.had) {
@ -177,7 +180,28 @@ void DivPlatformTX81Z::tick(bool sysTick) {
}
if (chan[i].std.ex3.had) {
immWrite(0x18,chan[i].std.ex3.val);
lfoValue=chan[i].std.ex3.val;
immWrite(0x18,lfoValue);
}
if (chan[i].std.ex5.had) {
amDepth2=chan[i].std.ex5.val;
immWrite(0x17,amDepth2);
}
if (chan[i].std.ex6.had) {
pmDepth2=chan[i].std.ex6.val;
immWrite(0x17,0x80|pmDepth2);
}
if (chan[i].std.ex7.had) {
lfoValue2=chan[i].std.ex7.val;
immWrite(0x16,lfoValue2);
}
if (chan[i].std.ex8.had) {
lfoShape2=chan[i].std.ex8.val&3;
immWrite(0x1b,lfoShape|(lfoShape2<<2));
}
if (chan[i].std.alg.had) {
@ -286,6 +310,12 @@ void DivPlatformTX81Z::tick(bool sysTick) {
oldWrites[i]=pendingWrites[i];
}
}
for (int i=320; i<328; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
immWrite(0x38+(i&7),(0x84|pendingWrites[i]));
oldWrites[i]=pendingWrites[i];
}
}
int hardResetElapsed=0;
bool mustHardReset=false;
@ -405,7 +435,7 @@ void DivPlatformTX81Z::commitState(int ch, DivInstrument* ins) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}*/
rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3));
//rWrite(chanOffs[ch]+ADDR_FMS_AMS,0x84|((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3));
rWrite(chanOffs[ch]+ADDR_FMS2_AMS2,((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3));
}
}
@ -528,11 +558,23 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
rWrite(0x18,c.value);
lfoValue=c.value;
immWrite(0x18,lfoValue);
break;
}
case DIV_CMD_FM_LFO_WAVE: {
rWrite(0x1b,c.value&3);
lfoShape=c.value&3;
immWrite(0x1b,lfoShape|(lfoShape2<<2));
break;
}
case DIV_CMD_FM_LFO2: {
lfoValue2=c.value;
immWrite(0x16,lfoValue2);
break;
}
case DIV_CMD_FM_LFO2_WAVE: {
lfoShape2=c.value&3;
immWrite(0x1b,lfoShape|(lfoShape2<<2));
break;
}
case DIV_CMD_FM_FB: {
@ -810,6 +852,16 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
immWrite(0x19,0x80|pmDepth);
break;
}
case DIV_CMD_FM_AM2_DEPTH: {
amDepth2=c.value;
immWrite(0x17,amDepth);
break;
}
case DIV_CMD_FM_PM2_DEPTH: {
pmDepth2=c.value;
immWrite(0x17,0x80|pmDepth);
break;
}
case DIV_CMD_FM_HARD_RESET:
chan[c.chan].hardReset=c.value;
break;
@ -880,7 +932,7 @@ void DivPlatformTX81Z::forceIns() {
rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7));
}*/
rWrite(chanOffs[i]+ADDR_FMS_AMS,((chan[i].state.fms&7)<<4)|(chan[i].state.ams&3));
//rWrite(chanOffs[i]+ADDR_FMS_AMS,0x84|((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3));
rWrite(chanOffs[i]+ADDR_FMS2_AMS2,((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3));
if (chan[i].active) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
@ -888,6 +940,11 @@ void DivPlatformTX81Z::forceIns() {
}
immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth);
immWrite(0x17,amDepth2);
immWrite(0x17,0x80|pmDepth2);
immWrite(0x18,lfoValue);
immWrite(0x16,lfoValue2);
immWrite(0x1b,lfoShape|(lfoShape2<<2));
}
void DivPlatformTX81Z::notifyInsChange(int ins) {
@ -958,12 +1015,19 @@ void DivPlatformTX81Z::reset() {
delay=0;
amDepth=0x7f;
pmDepth=0x7f;
amDepth2=0x7f;
pmDepth2=0x7f;
lfoValue=0;
lfoValue2=0;
lfoShape=0;
lfoShape2=0;
//rWrite(0x18,0x10);
immWrite(0x18,0x00); // LFO Freq Off
immWrite(0x16,0x00);
immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth);
//rWrite(0x1b,0x00);
immWrite(0x17,amDepth2);
immWrite(0x17,0x80|pmDepth2);
extMode=false;
}

View file

@ -45,7 +45,7 @@ class DivPlatformTX81Z: public DivPlatformOPM {
DivDispatchOscBuffer* oscBuf[8];
int baseFreqOff;
int pcmL, pcmR, pcmCycles;
unsigned char amDepth, pmDepth;
unsigned char amDepth, pmDepth, amDepth2, pmDepth2;
ymfm::ym2414* fm_ymfm;
ymfm::ym2414::output_data out_ymfm;

View file

@ -103,9 +103,8 @@ const char* cmdName[]={
"FM_AM_DEPTH",
"FM_PM_DEPTH",
"GENESIS_LFO",
"ARCADE_LFO",
"FM_LFO2",
"FM_LFO2_WAVE",
"STD_NOISE_FREQ",
"STD_NOISE_MODE",
@ -215,6 +214,9 @@ const char* cmdName[]={
"SURROUND_PANNING",
"FM_AM2_DEPTH",
"FM_PM2_DEPTH",
"ALWAYS_SET_VOLUME"
};

View file

@ -504,6 +504,10 @@ void DivEngine::registerSystems() {
EffectHandlerMap fmOPZPostEffectHandlerMap(fmOPMPostEffectHandlerMap);
fmOPZPostEffectHandlerMap.insert({
{0x24, {DIV_CMD_FM_LFO2, "24xx: Set LFO 2 speed"}},
{0x25, {DIV_CMD_FM_LFO2_WAVE, "25xx: Set LFO 2 waveform (0 saw, 1 square, 2 triangle, 3 noise)"}},
{0x26, {DIV_CMD_FM_AM2_DEPTH, "26xx: Set AM 2 depth (0 to 7F)", effectValAnd<127>}},
{0x27, {DIV_CMD_FM_PM2_DEPTH, "27xx: Set PM 2 depth (0 to 7F)", effectValAnd<127>}},
{0x28, {DIV_CMD_FM_REV, "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)", effectOpVal<4>, effectValAnd<7>}},
{0x2a, {DIV_CMD_FM_WS, "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)", effectOpVal<4>, effectValAnd<7>}},
{0x2b, {DIV_CMD_FM_EG_SHIFT, "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)", effectOpVal<4>, effectValAnd<3>}},