From a1b7e524676bfa0777d7b6acad94e6e94fe3b503 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 21 Oct 2023 14:35:20 -0300 Subject: [PATCH] Implementing pattern effects, detune on FM preview, default instrument --- src/engine/dispatch.h | 5 + src/engine/instrument.h | 1 - src/engine/platform/esfm.cpp | 201 ++++++++++++++++++++++++++--------- src/engine/playback.cpp | 7 +- src/engine/song.h | 37 ++++--- src/engine/sysDef.cpp | 58 +++++++++- src/gui/fmPreview.cpp | 22 +++- 7 files changed, 258 insertions(+), 73 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 3f2dda6b1..72cb8a3a8 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -240,6 +240,11 @@ enum DivDispatchCmds { DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol + DIV_CMD_ESFM_OP_PANNING, // (op, value) + DIV_CMD_ESFM_OUTLVL, // (op, value) + DIV_CMD_ESFM_MODIN, // (op, value) + DIV_CMD_ESFM_ENV_DELAY, // (op, value) + DIV_CMD_MAX }; diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 80c5dc3a6..0f9329a59 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -85,7 +85,6 @@ enum DivInstrumentType: unsigned short { DIV_INS_TED=52, DIV_INS_C140=53, DIV_INS_C219=54, - // TODO: Ask tilde to standardize this!!! DIV_INS_ESFM=55, DIV_INS_MAX, DIV_INS_NULL diff --git a/src/engine/platform/esfm.cpp b/src/engine/platform/esfm.cpp index 92f32c78d..a54043a8a 100644 --- a/src/engine/platform/esfm.cpp +++ b/src/engine/platform/esfm.cpp @@ -230,6 +230,7 @@ void DivPlatformESFM::tick(bool sysTick) { } } + // detune/fixed pitch if (opE.fixed) { if (m.ssg.had) { opE.ct=(opE.ct&(~(7<<2)))|((m.ssg.val&7)<<2); @@ -351,44 +352,21 @@ void DivPlatformESFM::tick(bool sysTick) { } int DivPlatformESFM::octave(int freq) { - if (freq>=0x3ff<<6) { - return 1<<7; - } else if (freq>=0x3ff<<5) { - return 1<<6; - } else if (freq>=0x3ff<<4) { - return 1<<5; - } else if (freq>=0x3ff<<3) { - return 1<<4; - } else if (freq>=0x3ff<<2) { - return 1<<3; - } else if (freq>=0x3ff<<1) { - return 1<<2; - } else if (freq>=0x3ff) { - return 1<<1; - } else { - return 1<<0; + int result=1; + while (freq>0x3ff) { + freq>>=1; + result<<=1; } - return 1<<0; + return result; } int DivPlatformESFM::toFreq(int freq) { - if (freq>=0x3ff<<6) { - return 0x1c00|((freq>>7)&0x3ff); - } else if (freq>=0x3ff<<5) { - return 0x1800|((freq>>6)&0x3ff); - } else if (freq>=0x3ff<<4) { - return 0x1400|((freq>>5)&0x3ff); - } else if (freq>=0x3ff<<3) { - return 0x1000|((freq>>4)&0x3ff); - } else if (freq>=0x3ff<<2) { - return 0xc00|((freq>>3)&0x3ff); - } else if (freq>=0x3ff<<1) { - return 0x800|((freq>>2)&0x3ff); - } else if (freq>=0x3ff<<0) { - return 0x400|((freq>>1)&0x3ff); - } else { - return freq&0x3ff; + int block=0; + while (freq>0x3ff) { + freq>>=1; + block++; } + return ((block&7)<<10)|(freq&0x3ff); } void DivPlatformESFM::muteChannel(int ch, bool mute) { @@ -510,7 +488,6 @@ int DivPlatformESFM::dispatch(DivCommand c) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; - rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); } break; @@ -585,7 +562,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -603,7 +580,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_DR: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -621,7 +598,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_SL: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -639,7 +616,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_RR: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -657,7 +634,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AM: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -675,7 +652,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_VIB: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -693,7 +670,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_SUS: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -711,7 +688,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_KSR: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -729,7 +706,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { break; } case DIV_CMD_FM_WS: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -760,7 +737,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { } // KSL case DIV_CMD_FM_RS: { - if (c.value<0) { + if (c.value<0) { for (int o=0; o<4; o++) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; @@ -785,6 +762,134 @@ int DivPlatformESFM::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_AM_DEPTH: { + unsigned int o = c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + op.dam=c.value2&1; + rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); + break; + } + case DIV_CMD_FM_PM_DEPTH: { + unsigned int o = c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + op.dvb=c.value2&1; + rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); + break; + } + case DIV_CMD_FM_FIXFREQ: { + unsigned int o=c.value&3; + bool isFNum=c.value&4; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + if (!opE.fixed) break; + if (isFNum) { + opE.dt=(c.value2)&0xff; + opE.ct=(opE.ct&(~3))|((c.value2>>8)&3); + chan[c.chan].freqChanged=true; + } else { + opE.ct=(opE.ct&(~(7<<2)))|((c.value2&7)<<2); + chan[c.chan].freqChanged=true; + } + break; + } + case DIV_CMD_ESFM_OP_PANNING: { + unsigned int o=c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + opE.left=c.value2&1; + opE.right=(c.value2&2)>>1; + rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); + break; + } + case DIV_CMD_ESFM_OUTLVL: { + if (c.value<0) { + for (int o=0; o<4; o++) { + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + unsigned char noise=chan[c.chan].state.esfm.noise&3; + opE.outLvl=c.value2&7; + if (isMuted[c.chan]) { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|((o==3?noise:0)<<3)|0); + } else { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|((o==3?noise:0)<<3)|((opE.outLvl&7)<<5)); + } + } + } else { + unsigned int o = c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + unsigned char noise=chan[c.chan].state.esfm.noise&3; + opE.outLvl=c.value2&7; + if (isMuted[c.chan]) { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|((o==3?noise:0)<<3)|0); + } else { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|((o==3?noise:0)<<3)|((opE.outLvl&7)<<5)); + } + } + break; + } + case DIV_CMD_ESFM_MODIN: { + if (c.value<0) { + for (int o=0; o<4; o++) { + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + opE.modIn=c.value2&7; + rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); + } + } else { + unsigned int o = c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + opE.modIn=c.value2&7; + rWrite(baseAddr+OFFSET_DAM_DVB_LEFT_RIGHT_MODIN,((opE.modIn&7)<<1)|(((opE.left&chan[c.chan].globalPan)&1)<<4)|(((opE.right&(chan[c.chan].globalPan>>1))&1)<<5)|((op.dvb&1)<<6)|(op.dam<<7)); + } + break; + } + case DIV_CMD_ESFM_ENV_DELAY: { + if (c.value<0) { + for (int o=0; o<4; o++) { + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + opE.delay=c.value2&7; + rWrite(baseAddr+OFFSET_FREQH_BLOCK_DELAY,chan[c.chan].freqH[o]|(opE.delay<<5)); + } + } else { + unsigned int o = c.value; + if (o >= 4) break; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + opE.delay=c.value2&7; + rWrite(baseAddr+OFFSET_FREQH_BLOCK_DELAY,chan[c.chan].freqH[o]|(opE.delay<<5)); + } + break; + } + case DIV_CMD_STD_NOISE_MODE: { + unsigned int o=3; + unsigned short baseAddr=c.chan*32 + o*8; + DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; + DivInstrumentESFM::Operator& opE=chan[c.chan].state.esfm.op[o]; + DivInstrumentESFM insE=chan[c.chan].state.esfm; + insE.noise=c.value&3; + if (isMuted[c.chan]) { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|(insE.noise<<3)|0); + } else { + rWrite(baseAddr+OFFSET_OUTLVL_NOISE_WS,(op.ws&7)|(insE.noise<<3)|((opE.outLvl&7)<<5)); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; @@ -843,10 +948,10 @@ DivDispatchOscBuffer* DivPlatformESFM::getOscBuffer(int ch) { } unsigned char* DivPlatformESFM::getRegisterPool() { - // TODO: DEBUG, remove this, it impacts performance - for (int i=0; i int effectValAnd(unsigned char, unsigned char val) { return val&mask; }; @@ -612,6 +616,52 @@ void DivEngine::registerSystems() { {0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set PCM frequency"}} }; + EffectHandlerMap fmESFMPostEffectHandlerMap={ + {0x12, {DIV_CMD_FM_TL, "12xx: Set level of operator 1 (0 highest, 3F lowest)", constVal<0>, effectVal}}, + {0x13, {DIV_CMD_FM_TL, "13xx: Set level of operator 2 (0 highest, 3F lowest)", constVal<1>, effectVal}}, + {0x14, {DIV_CMD_FM_TL, "14xx: Set level of operator 3 (0 highest, 3F lowest)", constVal<2>, effectVal}}, + {0x15, {DIV_CMD_FM_TL, "15xx: Set level of operator 4 (0 highest, 3F lowest)", constVal<3>, effectVal}}, + {0x16, {DIV_CMD_FM_MULT, "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)", effectOpValNoZero<4>, effectValAnd<15>}}, + {0x19, {DIV_CMD_FM_AR, "19xx: Set attack of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, + {0x1a, {DIV_CMD_FM_AR, "1Axx: Set attack of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, + {0x1b, {DIV_CMD_FM_AR, "1Bxx: Set attack of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, + {0x1c, {DIV_CMD_FM_AR, "1Cxx: Set attack of operator 3 (0 to F)", constVal<2>, effectValAnd<15>}}, + {0x1d, {DIV_CMD_FM_AR, "1Dxx: Set attack of operator 4 (0 to F)", constVal<3>, effectValAnd<15>}}, + {0x1e, {DIV_CMD_FM_AM_DEPTH, "1Exy: Set AM depth (x: operator from 1 to 4 (0 for all ops); y: depth (0: 1dB, 1: 4.8dB))", effectOpVal<4>, effectValAnd<1>}}, + {0x1f, {DIV_CMD_FM_PM_DEPTH, "1Fxy: Set vibrato depth (x: operator from 1 to 4 (0 for all ops); y: depth (0: normal, 1: double))", effectOpVal<4>, effectValAnd<1>}}, + {0x20, {DIV_CMD_ESFM_OP_PANNING, "20xy: Set panning of operator 1 (x: left; y: right)", constVal<0>, effectValNibbleFlagPackReversed}}, + {0x21, {DIV_CMD_ESFM_OP_PANNING, "21xy: Set panning of operator 2 (x: left; y: right)", constVal<1>, effectValNibbleFlagPackReversed}}, + {0x22, {DIV_CMD_ESFM_OP_PANNING, "22xy: Set panning of operator 3 (x: left; y: right)", constVal<2>, effectValNibbleFlagPackReversed}}, + {0x23, {DIV_CMD_ESFM_OP_PANNING, "23xy: Set panning of operator 4 (x: left; y: right)", constVal<3>, effectValNibbleFlagPackReversed}}, + {0x24, {DIV_CMD_ESFM_OUTLVL, "24xy: Set output level register (x: operator from 1 to 4 (0 for all ops); y: level from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, + {0x25, {DIV_CMD_ESFM_MODIN, "25xy: Set modulation input level (x: operator from 1 to 4 (0 for all ops); y: level from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, + {0x26, {DIV_CMD_ESFM_ENV_DELAY, "26xy: Set envelope delay (x: operator from 1 to 4 (0 for all ops); y: delay from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, + {0x27, {DIV_CMD_STD_NOISE_MODE, "27xx: Set noise mode for operator 4 (x: mode from 0 to 3)", effectValAnd<3>}}, + {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>}}, + {0x2f, {DIV_CMD_FM_FIXFREQ, "2Fxy: Set fixed frequency block (x: operator from 1 to 4; y: octave from 0 to 7)", effectOpValNoZero<4>, effectValAnd<7>}}, + {0x50, {DIV_CMD_FM_AM, "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)", effectOpVal<4>, effectValAnd<1>}}, + {0x51, {DIV_CMD_FM_SL, "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)", effectOpVal<4>, effectValAnd<15>}}, + {0x52, {DIV_CMD_FM_RR, "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)", effectOpVal<4>, effectValAnd<15>}}, + {0x53, {DIV_CMD_FM_VIB, "53xy: Set vibrato (x: operator from 1 to 4 (0 for all ops); y: enabled)", effectOpVal<4>, effectValAnd<1>}}, + {0x54, {DIV_CMD_FM_RS, "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, + {0x55, {DIV_CMD_FM_SUS, "55xy: Set envelope sustain (x: operator from 1 to 4 (0 for all ops); y: enabled)", effectOpVal<4>, effectValAnd<1>}}, + {0x56, {DIV_CMD_FM_DR, "56xx: Set decay of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, + {0x57, {DIV_CMD_FM_DR, "57xx: Set decay of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, + {0x58, {DIV_CMD_FM_DR, "58xx: Set decay of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, + {0x59, {DIV_CMD_FM_DR, "59xx: Set decay of operator 3 (0 to F)", constVal<2>, effectValAnd<15>}}, + {0x5a, {DIV_CMD_FM_DR, "5Axx: Set decay of operator 4 (0 to F)", constVal<3>, effectValAnd<15>}}, + {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>}} + }; + const EffectHandler fmESFMFixFreqFNumHandler[4]={ + {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency F-num of operator 1 (x: high 2 bits from 0 to 3; y: low 8 bits of F-num)", constVal<4>, effectValLong<10>}, + {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency F-num of operator 2 (x: high 2 bits from 4 to 7; y: low 8 bits of F-num)", constVal<5>, effectValLong<10>}, + {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency F-num of operator 3 (x: high 2 bits from 8 to B; y: low 8 bits of F-num)", constVal<6>, effectValLong<10>}, + {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency F-num of operator 4 (x: high 2 bits from C to F; y: low 8 bits of F-num)", constVal<7>, effectValLong<10>}, + }; + for (int i=0; i<16; i++) { + fmESFMPostEffectHandlerMap.emplace(0x30+i,fmESFMFixFreqFNumHandler[i/4]); + } + // SysDefs // this chip uses YMZ ADPCM, but the emulator uses ADPCM-B because I got it wrong back then. @@ -1917,8 +1967,7 @@ void DivEngine::registerSystems() { {0x12, {DIV_CMD_SNES_INVERT, "12xy: Set invert mode (x: surround; y: invert)"}}, } ); - - // TODO: ask Tilde to standardize! + sysDefs[DIV_SYSTEM_ESFM]=new DivSysDef( "ESS ES1xxx-series (ESFM)", NULL, 0xd0, 0, 18, true, false, 0, false, 0, "A unique FM synth featured in PC sound cards.\nBased on the OPL3 design, but with lots of its features extended.", @@ -1927,7 +1976,10 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM, DIV_INS_ESFM}, {}, - {} + { + {0x2e, {DIV_CMD_FM_HARD_RESET, "2Exx: Toggle hard envelope reset on new notes"}}, + }, + fmESFMPostEffectHandlerMap ); sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( diff --git a/src/gui/fmPreview.cpp b/src/gui/fmPreview.cpp index 14b21e018..290175e23 100644 --- a/src/gui/fmPreview.cpp +++ b/src/gui/fmPreview.cpp @@ -366,15 +366,31 @@ void FurnaceGUI::renderFMPreviewESFM(const DivInstrumentFM& params, const DivIns const DivInstrumentFM::Operator& op=params.op[i]; const DivInstrumentESFM::Operator& opE=esfmParams.op[i]; unsigned short baseAddr=i*8; + unsigned char freqL, freqH; + if (opE.fixed) { + freqL=opE.dt; + freqH=opE.ct&0x1f; + } else { + // perform detune calculation + int offset=(opE.ct<<7)+opE.dt; + double fbase=(mult0?2048.0:1024.0)*pow(2.0,(float)offset/(128.0*12.0)); + int bf=round(fbase); + int block=0; + while (bf>0x3ff) { + bf>>=1; + block++; + } + freqL=bf&0xff; + freqH=((block&7)<<2)|((bf>>8)&3); + } ESFM_WRITE(baseAddr+0,(op.am<<7)|((op.vib&1)<<6)|((op.sus&1)<<5)|((op.ksr&1)<<4)|(op.mult&0x0f)); ESFM_WRITE(baseAddr+1,(op.ksl<<6)|(op.tl&0x3f)); ESFM_WRITE(baseAddr+2,(op.ar<<4)|(op.dr&0x0f)); ESFM_WRITE(baseAddr+3,(op.sl<<4)|(op.rr&0x0f)); - // TODO: implement ct/dt detune... how will we do that? - ESFM_WRITE(baseAddr+4,0); - ESFM_WRITE(baseAddr+5,(opE.delay<<5)|(mult0?0x0a:0x06)); + ESFM_WRITE(baseAddr+4,freqL); + ESFM_WRITE(baseAddr+5,(opE.delay<<5)|freqH); ESFM_WRITE(baseAddr+6,(op.dam<<7)|((op.dvb&1)<<6)|((opE.right&1)<<5)|((opE.left&1)<<4)|((opE.modIn&7)<<1)); ESFM_WRITE(baseAddr+7,(opE.outLvl<<5)|((i==3?esfmParams.noise:0)<<3)|(op.ws&7));