diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index dfb5bca4..9cc47fae 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -93,24 +93,15 @@ void DivPlatformSNES::tick(bool sysTick) { // so they have to be accumulated unsigned char kon=0; unsigned char koff=0; + bool writeControl=false; + bool writeNoise=false; + bool writePitchMod=false; + bool writeEcho=false; for (int i=0; i<8; i++) { - //bool hadGain=chan[i].std.vol.had || chan[i].std.ex1.had || chan[i].std.ex2.had; chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&127,MIN(127,chan[i].std.vol.val),127); } - /* - if (chan[i].std.vol.had) { - chWrite(i,7,MIN(127,chan[i].std.vol.val*2)); - } else if (!chan[i].state.useEnv && hadGain) { - if (chan[i].std.ex1.val==0) { - // direct gain - chWrite(i,7,chan[i].std.vol.val); - } else { - // inc/dec - chWrite(i,7,chan[i].std.ex2.val|((chan[i].std.ex1.val-1)<<5)|0x80); - } - }*/ if (chan[i].std.arp.had) { if (!chan[i].inPorta) { if (chan[i].std.arp.mode) { @@ -126,6 +117,10 @@ void DivPlatformSNES::tick(bool sysTick) { chan[i].freqChanged=true; } } + if (chan[i].std.duty.had) { + noiseFreq=chan[i].std.duty.val; + writeControl=true; + } if (chan[i].useWave && chan[i].std.wave.had) { if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { chan[i].wave=chan[i].std.wave.val; @@ -149,7 +144,30 @@ void DivPlatformSNES::tick(bool sysTick) { int val=chan[i].std.panR.val&0x7f; chan[i].panR=(val<<1)|(val>>6); } - if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { + bool hasInverted=false; + if (chan[i].std.ex1.had) { + if (chan[i].invertL!=(chan[i].std.ex1.val&16)) { + chan[i].invertL=chan[i].std.ex1.val&16; + hasInverted=true; + } + if (chan[i].invertR!=(chan[i].std.ex1.val&8)) { + chan[i].invertR=chan[i].std.ex1.val&8; + hasInverted=true; + } + if (chan[i].pitchMod!=(chan[i].std.ex1.val&4)) { + chan[i].pitchMod=chan[i].std.ex1.val&4; + writePitchMod=true; + } + if (chan[i].echo!=(chan[i].std.ex1.val&2)) { + chan[i].echo=chan[i].std.ex1.val&2; + writeEcho=true; + } + if (chan[i].noise!=(chan[i].std.ex1.val&1)) { + chan[i].noise=chan[i].std.ex1.val&1; + writeNoise=true; + } + } + if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had || hasInverted) { writeOutVol(i); } if (chan[i].setPos) { @@ -173,29 +191,6 @@ void DivPlatformSNES::tick(bool sysTick) { if (chan[i].keyOn) { unsigned int start, end, loop; unsigned short tabAddr=sampleTableAddr(i); - if (chan[i].state.useEnv) { - chWrite(i,5,chan[i].state.a|(chan[i].state.d<<4)|0x80); - chWrite(i,6,chan[i].state.r|(chan[i].state.s<<5)); - } else { - chWrite(i,5,0); - switch (chan[i].state.gainMode) { - case DivInstrumentSNES::GAIN_MODE_DIRECT: - chWrite(i,7,chan[i].state.gain&127); - break; - case DivInstrumentSNES::GAIN_MODE_DEC_LINEAR: - chWrite(i,7,0x80|(chan[i].state.gain&31)); - break; - case DivInstrumentSNES::GAIN_MODE_INC_LINEAR: - chWrite(i,7,0xc0|(chan[i].state.gain&31)); - break; - case DivInstrumentSNES::GAIN_MODE_DEC_LOG: - chWrite(i,7,0xa0|(chan[i].state.gain&31)); - break; - case DivInstrumentSNES::GAIN_MODE_INC_INVLOG: - chWrite(i,7,0xe0|(chan[i].state.gain&31)); - break; - } - } if (chan[i].useWave) { start=waveTableAddr(i); loop=start; @@ -228,6 +223,49 @@ void DivPlatformSNES::tick(bool sysTick) { } } } + if (writeControl) { + unsigned char control=noiseFreq&0x1f; + rWrite(0x6c,control); + } + if (writeNoise) { + unsigned char noiseBits=( + (chan[0].noise?1:0)| + (chan[1].noise?2:0)| + (chan[2].noise?4:0)| + (chan[3].noise?8:0)| + (chan[4].noise?0x10:0)| + (chan[5].noise?0x20:0)| + (chan[6].noise?0x40:0)| + (chan[7].noise?0x80:0) + ); + rWrite(0x3d,noiseBits); + } + if (writePitchMod) { + unsigned char pitchModBits=( + (chan[0].pitchMod?1:0)| + (chan[1].pitchMod?2:0)| + (chan[2].pitchMod?4:0)| + (chan[3].pitchMod?8:0)| + (chan[4].pitchMod?0x10:0)| + (chan[5].pitchMod?0x20:0)| + (chan[6].pitchMod?0x40:0)| + (chan[7].pitchMod?0x80:0) + ); + rWrite(0x2d,pitchModBits); + } + if (writeEcho) { + unsigned char echoBits=( + (chan[0].echo?1:0)| + (chan[1].echo?2:0)| + (chan[2].echo?4:0)| + (chan[3].echo?8:0)| + (chan[4].echo?0x10:0)| + (chan[5].echo?0x20:0)| + (chan[6].echo?0x40:0)| + (chan[7].echo?0x80:0) + ); + rWrite(0x4d,echoBits); + } if (kon!=0) { rWrite(0x4c,kon); } @@ -260,6 +298,29 @@ int DivPlatformSNES::dispatch(DivCommand c) { if (chan[c.chan].insChanged) { chan[c.chan].state=ins->snes; } + if (chan[c.chan].state.useEnv) { + chWrite(c.chan,5,chan[c.chan].state.a|(chan[c.chan].state.d<<4)|0x80); + chWrite(c.chan,6,chan[c.chan].state.r|(chan[c.chan].state.s<<5)); + } else { + chWrite(c.chan,5,0); + switch (chan[c.chan].state.gainMode) { + case DivInstrumentSNES::GAIN_MODE_DIRECT: + chWrite(c.chan,7,chan[c.chan].state.gain&127); + break; + case DivInstrumentSNES::GAIN_MODE_DEC_LINEAR: + chWrite(c.chan,7,0x80|(chan[c.chan].state.gain&31)); + break; + case DivInstrumentSNES::GAIN_MODE_INC_LINEAR: + chWrite(c.chan,7,0xc0|(chan[c.chan].state.gain&31)); + break; + case DivInstrumentSNES::GAIN_MODE_DEC_LOG: + chWrite(c.chan,7,0xa0|(chan[c.chan].state.gain&31)); + break; + case DivInstrumentSNES::GAIN_MODE_INC_INVLOG: + chWrite(c.chan,7,0xe0|(chan[c.chan].state.gain&31)); + break; + } + } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value)); chan[c.chan].freqChanged=true; @@ -375,6 +436,8 @@ void DivPlatformSNES::writeOutVol(int ch) { if (!isMuted[ch]) { outL=(globalVolL*((chan[ch].outVol*chan[ch].panL)/127))/127; outR=(globalVolR*((chan[ch].outVol*chan[ch].panR)/127))/127; + if (chan[ch].invertL) outL=-outL; + if (chan[ch].invertR) outR=-outR; } chWrite(ch,0,outL); chWrite(ch,1,outR); diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index 44efef92..a2fec4b2 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -33,7 +33,7 @@ class DivPlatformSNES: public DivDispatch { int sample, wave, ins; int note; int panL, panR; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, noise, echo, pitchMod, invertL, invertR; int vol, outVol; int wtLen; DivInstrumentSNES state; @@ -63,6 +63,7 @@ class DivPlatformSNES: public DivDispatch { inPorta(false), useWave(false), setPos(false), + noise(false), vol(127), outVol(127), wtLen(16) {} @@ -71,6 +72,7 @@ class DivPlatformSNES: public DivDispatch { DivDispatchOscBuffer* oscBuf[8]; bool isMuted[8]; signed char globalVolL, globalVolR; + unsigned char noiseFreq; size_t sampleTableBase; struct QueuedWrite { diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f6036ca7..15434a80 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -227,6 +227,10 @@ const char* saaEnvBits[9]={ "mirror", "loop", "cut", "direction", "resolution", "fixed", "N/A","enabled", NULL }; +const char* snesModeBits[6]={ + "noise", "echo", "pitch mod", "invert right", "invert left", NULL +}; + const char* filtModeBits[5]={ "low", "band", "high", "ch3off", NULL }; @@ -4380,9 +4384,12 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Noise"; dutyMax=ins->amiga.useSample?0:8; } + if (ins->type==DIV_INS_SNES) { + dutyLabel="Noise Freq"; + dutyMax=31; + } if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || - ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_FDS || ins->type==DIV_INS_MULTIPCM || - ins->type==DIV_INS_SNES) { + ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_FDS || ins->type==DIV_INS_MULTIPCM) { dutyMax=0; } if (ins->type==DIV_INS_VERA) { @@ -4504,9 +4511,9 @@ void FurnaceGUI::drawInsEdit() { ex1Max=65535; ex2Max=65535; } - if (ins->type==DIV_INS_SNES && !ins->snes.useEnv) { - ex1Max=4; - ex2Max=31; + if (ins->type==DIV_INS_SNES) { + ex1Max=5; + ex2Max=5; } int panMin=0; @@ -4557,6 +4564,10 @@ void FurnaceGUI::drawInsEdit() { panMax=127; panSingleNoBit=true; } + if (ins->type==DIV_INS_SNES) { + panMin=0; + panMax=127; + } if (ins->type==DIV_INS_ES5506) { panMax=65535; } @@ -4649,7 +4660,7 @@ void FurnaceGUI::drawInsEdit() { } else if (ins->type==DIV_INS_QSOUND) { macroList.push_back(FurnaceGUIMacroDesc("Echo Feedback",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SNES) { - macroList.push_back(FurnaceGUIMacroDesc("Gain Mode",&ins->std.ex1Macro,0,ex1Max,64,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,snesGainModes)); + macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,snesModeBits)); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } @@ -4668,7 +4679,7 @@ void FurnaceGUI::drawInsEdit() { } else if (ins->type==DIV_INS_QSOUND) { macroList.push_back(FurnaceGUIMacroDesc("Echo Length",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SNES) { - macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + macroList.push_back(FurnaceGUIMacroDesc("Gain Mode",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,snesGainModes)); } else { macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); } @@ -4708,6 +4719,9 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Envelope mode",&ins->std.ex8Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,es5506EnvelopeModes)); macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,es5506ControlModes)); } + if (ins->type==DIV_INS_SNES) { + macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + } drawMacros(macroList); ImGui::EndTabItem();