diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index dc2b07b75..1970afed6 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -213,6 +213,8 @@ enum DivDispatchCmds { DIV_CMD_MACRO_OFF, // (which) DIV_CMD_MACRO_ON, // (which) + DIV_CMD_SURROUND_PANNING, // (out, val) + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_CMD_MAX diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 73765d468..fb447e32f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -77,6 +77,15 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul return "81xx: Set panning (left channel)"; case 0x82: return "82xx: Set panning (right channel)"; + case 0x88: + return "88xx: Set panning (rear channels; x: left; y: right)"; + break; + case 0x89: + return "89xx: Set panning (rear left channel)"; + break; + case 0x8a: + return "8Axx: Set panning (rear right channel)"; + break; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return "Cxxx: Set tick rate (hz)"; case 0xe0: @@ -435,6 +444,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { case DIV_CMD_FM_FINE: case DIV_CMD_AY_IO_WRITE: case DIV_CMD_AY_AUTO_PWM: + case DIV_CMD_SURROUND_PANNING: w->writeC(2); // length w->writeC(c.value); w->writeC(c.value2); diff --git a/src/engine/engine.h b/src/engine/engine.h index bcf478536..8bced933d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "0.6pre3" -#define DIV_ENGINE_VERSION 133 +#define DIV_VERSION "dev134" +#define DIV_ENGINE_VERSION 134 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -97,7 +97,7 @@ struct DivChannelState { int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; - unsigned char arp, arpStage, arpTicks, panL, panR; + unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp; @@ -135,6 +135,8 @@ struct DivChannelState { arpTicks(1), panL(255), panR(255), + panRL(0), + panRR(0), doNote(false), legato(false), portaStop(false), diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index d5180c80e..99e4e2236 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -2543,6 +2543,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // OPL3 pan compat + if (ds.version<134) { + for (int i=0; i0)|((c.value2>0)<<1); @@ -999,6 +999,33 @@ int DivPlatformOPL::dispatch(DivCommand c) { } break; } + case DIV_CMD_SURROUND_PANNING: { + if (oplType!=3) break; + if (c.chan==adpcmChan) break; + + if (c.value==2) { + chan[c.chan].pan&=3; + if (c.value2>0) chan[c.chan].pan|=4; + } else if (c.value==3) { + if (c.value2>0) chan[c.chan].pan|=8; + } else { + break; + } + + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (isMuted[c.chan]) { + rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)); + if (ops==4) { + rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)); + } + } else { + rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); + if (ops==4) { + rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); + } + } + break; + } case DIV_CMD_PITCH: { if (c.chan==adpcmChan) { if (!chan[c.chan].furnacePCM) break; @@ -1705,6 +1732,8 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { rate=chipClock/36; }*/ + compatPan=false; + switch (chipType) { default: case 1: case 2: case 8950: @@ -1753,6 +1782,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; rate=chipClock/288; chipRateBase=rate; + compatPan=flags.getBool("compatPan",false); break; case 4: switch (flags.getInt("clockSel",0)) { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 90ff0bb73..5e6b53162 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -92,7 +92,7 @@ class DivPlatformOPL: public DivDispatch { unsigned char lfoValue; - bool useYMFM, update4OpMask, pretendYMU, downsample; + bool useYMFM, update4OpMask, pretendYMU, downsample, compatPan; short oldWrites[512]; short pendingWrites[512]; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 54aef382b..73ec8df53 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -213,6 +213,8 @@ const char* cmdName[]={ "MACRO_OFF", "MACRO_ON", + "SURROUND_PANNING", + "ALWAYS_SET_VOLUME" }; @@ -555,6 +557,7 @@ void DivEngine::processRow(int i, bool afterDelay) { short lastSlide=-1; bool calledPorta=false; bool panChanged=false; + bool surroundPanChanged=false; // effects for (int j=0; j>4)|(effectVal&0xf0); + chan[i].panRR=(effectVal&15)|((effectVal&15)<<4); + surroundPanChanged=true; + break; + case 0x89: // panning left (split 8-bit) + chan[i].panRL=effectVal; + surroundPanChanged=true; + break; + case 0x8a: // panning right (split 8-bit) + chan[i].panRR=effectVal; + surroundPanChanged=true; + break; case 0x01: // ramp up if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break; lastSlide=0x01; @@ -866,6 +882,10 @@ void DivEngine::processRow(int i, bool afterDelay) { if (panChanged) { dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR)); } + if (surroundPanChanged) { + dispatchCmd(DivCommand(DIV_CMD_SURROUND_PANNING,i,2,chan[i].panRL)); + dispatchCmd(DivCommand(DIV_CMD_SURROUND_PANNING,i,3,chan[i].panRR)); + } if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 0c8a6d029..f90921799 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -319,9 +319,9 @@ const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 8e41fa00c..b33aff8a5 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1182,6 +1182,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo case DIV_SYSTEM_OPL3: case DIV_SYSTEM_OPL3_DRUMS: { int clockSel=flags.getInt("clockSel",0); + bool compatPan=flags.getBool("compatPan",false); ImGui::Text("Clock rate:"); if (ImGui::RadioButton("14.32MHz (NTSC)",clockSel==0)) { @@ -1205,9 +1206,14 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } + if (ImGui::Checkbox("Compatible panning (0800)",&compatPan)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); + flags.set("compatPan",compatPan); }); } break;