diff --git a/.gitignore b/.gitignore index 52fc43f4b..25bd6d0e0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ winbuild/ win32build/ macbuild/ linuxbuild/ -webbuild/ *.swp .cache/ .DS_Store diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 22cdddf4b..b6470fc5f 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -558,9 +558,10 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(es5506.envelope.k2Slow); // SNES + // @tildearrow please update this w->writeC(snes.useEnv); - w->writeC(snes.gainMode); - w->writeC(snes.gain); + w->writeC(0); + w->writeC(0); w->writeC(snes.a); w->writeC(snes.d); w->writeC(snes.s); @@ -1250,8 +1251,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { // SNES if (version>=109) { snes.useEnv=reader.readC(); - snes.gainMode=(DivInstrumentSNES::GainMode)reader.readC(); - snes.gain=reader.readC(); + reader.readC(); + reader.readC(); snes.a=reader.readC(); snes.d=reader.readC(); snes.s=reader.readC(); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index e5372933d..d0dc78f31 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -501,25 +501,17 @@ struct DivInstrumentES5506 { }; struct DivInstrumentSNES { - enum GainMode: unsigned char { - GAIN_MODE_DIRECT=0, - GAIN_MODE_DEC_LINEAR=4, - GAIN_MODE_DEC_LOG=5, - GAIN_MODE_INC_LINEAR=6, - GAIN_MODE_INC_INVLOG=7 - }; - bool useEnv; - GainMode gainMode; - unsigned char gain; + bool useEnv, applyFIR; unsigned char a, d, s, r; + signed char fir[8]; DivInstrumentSNES(): useEnv(true), - gainMode(GAIN_MODE_DIRECT), - gain(127), + applyFIR(false), a(15), d(7), s(7), - r(0) {} + r(0), + fir{127, 0, 0, 0, 0, 0, 0, 0} {} }; struct DivInstrument { diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index c13fa6bf8..c4239c603 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -22,9 +22,12 @@ #include "../../ta-log.h" #include -#define CHIP_FREQBASE 4096 +#define CHIP_FREQBASE 131072 #define rWrite(a,v) {dsp.write(a,v); regPool[(a)&0x7f]=v; } +#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)} +#define sampleTableAddr(c) (sampleTableBase+(c)*4) +#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16) const char* regCheatSheetSNESDSP[]={ "VxVOLL", "x0", @@ -62,16 +65,6 @@ const char** DivPlatformSNES::getRegisterSheet() { return regCheatSheetSNESDSP; } -const char* DivPlatformSNES::getEffectName(unsigned char effect) { - switch (effect) { - case 0x10: - return "10xx: Set echo feedback level (signed 8-bit)"; - break; - // TODO - } - return NULL; -} - void DivPlatformSNES::acquire(short* bufL, short* bufR, size_t start, size_t len) { short out[2]; short chOut[16]; @@ -90,12 +83,23 @@ void DivPlatformSNES::acquire(short* bufL, short* bufR, size_t start, size_t len void DivPlatformSNES::tick(bool sysTick) { // KON/KOFF can't be written several times per one sample // so they have to be accumulated + // TODO due to pipelining, KON/KOFF writes need to be delayed to accomodate sample address changes in the table unsigned char kon=0; unsigned char koff=0; for (int i=0; i<8; i++) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); + 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) { - // TODO handle gain writes + if (ins->type==DIV_INS_AMIGA && chan[i].std.vol.had) { + chWrite(i,7,MIN(127,chan[i].std.vol.val*2)); + } else if (!chan[i].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) { @@ -112,6 +116,12 @@ void DivPlatformSNES::tick(bool sysTick) { chan[i].freqChanged=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; + chan[i].ws.changeWave1(chan[i].wave); + } + } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { chan[i].pitch2+=chan[i].std.pitch.val; @@ -139,13 +149,46 @@ void DivPlatformSNES::tick(bool sysTick) { } else { chan[i].audPos=0; } + if (chan[i].useWave && chan[i].active) { + if (chan[i].ws.tick()) { + updateWave(i); + } + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { DivSample* s=parent->getSample(chan[i].sample); double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0; chan[i].freq=(unsigned int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE)); if (chan[i].freq>16383) chan[i].freq=16383; if (chan[i].keyOn) { - // TODO handle sample offsets + unsigned int start, end, loop; + size_t tabAddr=sampleTableAddr(i); + if (chan[i].useEnv) { + chWrite(i,5,ins->snes.a|(ins->snes.d<<4)|0x80); + chWrite(i,6,ins->snes.r|(ins->snes.s<<5)); + } else { + chWrite(i,5,0); + } + if (chan[i].useWave) { + start=waveTableAddr(i); + loop=start; + } else { + start=s->offSNES; + end=MIN(start+MAX(s->lengthBRR,1),getSampleMemCapacity()); + loop=MAX(start,end-1); + if (chan[i].audPos>0) { + start=start+MIN(chan[i].audPos,s->lengthBRR-1)/16*9; + } + if (s->loopStart>=0) { + loop=start+s->loopStart/16*9; + } + } + sampleMem[tabAddr+0]=start&0xff; + sampleMem[tabAddr+1]=start>>8; + sampleMem[tabAddr+2]=loop&0xff; + sampleMem[tabAddr+3]=loop>>8; + if (!hadGain) { + chWrite(i,7,0x7f); + } kon|=(1<>8); + chWrite(i,2,chan[i].freq&0xff); + chWrite(i,3,chan[i].freq>>8); chan[i].freqChanged=false; } } @@ -168,20 +211,36 @@ int DivPlatformSNES::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); - chan[c.chan].sample=ins->amiga.getSample(c.value); - if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value)); + if (ins->amiga.useWave) { + chan[c.chan].useWave=true; + chan[c.chan].wtLen=(unsigned int)(ins->amiga.waveLen)+1; + if (chan[c.chan].insChanged) { + if (chan[c.chan].wave<0) { + chan[c.chan].wave=0; + chan[c.chan].ws.setWidth(chan[c.chan].wtLen); + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + } + } + } else { + chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].useWave=false; } if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { chan[c.chan].sample=-1; } if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value)); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].macroInit(ins); + if (ins->type==DIV_INS_SNES) { + // initialize to max gain in case of direct gain mode macro without gain level macro + chan[c.chan].std.vol.val=0x7f; + chan[c.chan].useEnv=ins->snes.useEnv; + } chan[c.chan].insChanged=false; break; } @@ -274,6 +333,20 @@ int DivPlatformSNES::dispatch(DivCommand c) { return 1; } +void DivPlatformSNES::updateWave(int ch) { + // Due to the overflow bug in hardware's resampler, the written amplitude here is half of maximum + size_t pos=waveTableAddr(ch); + for (int i=0; i>8); rWrite(0x0c,127); // global volume left rWrite(0x1c,127); // global volume right + rWrite(0x6c,0); // get DSP out of reset for (int i=0; i<8; i++) { chan[i]=Channel(); chan[i].std.setEngine(parent); + chan[i].ws.setEngine(parent); + chan[i].ws.init(NULL,32,255); writeOutVol(i); + chWrite(i,4,i); // source number } } @@ -353,6 +431,17 @@ void DivPlatformSNES::notifyInsChange(int ins) { } } +void DivPlatformSNES::notifyWaveChange(int wave) { + for (int i=0; i<8; i++) { + if (chan[i].useWave && chan[i].wave==wave) { + chan[i].ws.changeWave1(wave); + if (chan[i].active) { + updateWave(i); + } + } + } +} + void DivPlatformSNES::notifyInsDeletion(void* ins) { for (int i=0; i<8; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); @@ -383,21 +472,14 @@ size_t DivPlatformSNES::getSampleMemUsage(int index) { void DivPlatformSNES::renderSamples() { memset(sampleMem,0,getSampleMemCapacity()); - // skip past sample table - size_t memPos=sampleTableBase+0x400; + // skip past sample table and wavetable buffer + size_t memPos=sampleTableBase+8*4+8*9*16; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; int length=s->lengthBRR; int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length); if (actualLength>0) { - size_t tabAddr=sampleTableBase+i*4; - size_t loopPos=memPos; - if (s->loopStart>=0) loopPos+=s->loopStart; s->offSNES=memPos; - sampleMem[tabAddr+0]=memPos&0xff; - sampleMem[tabAddr+1]=memPos>>8; - sampleMem[tabAddr+2]=loopPos&0xff; - sampleMem[tabAddr+3]=loopPos>>8; memcpy(&sampleMem[memPos],s->data8,actualLength); memPos+=actualLength; } diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index 7e8d6882b..058df21fc 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -22,19 +22,22 @@ #include "../dispatch.h" #include "../macroInt.h" +#include "../waveSynth.h" #include #include "sound/snes/SPC_DSP.h" class DivPlatformSNES: public DivDispatch { struct Channel { int freq, baseFreq, pitch, pitch2; - unsigned int audPos; - int sample, ins; + unsigned int audPos, wtLen; + int sample, wave, ins; int note; int panL, panR; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos; signed char vol; + bool useEnv; DivMacroInt std; + DivWaveSynth ws; void macroInit(DivInstrument* which) { std.init(which); pitch2=0; @@ -45,7 +48,9 @@ class DivPlatformSNES: public DivDispatch { pitch(0), pitch2(0), audPos(0), + wtLen(16), sample(-1), + wave(-1), ins(-1), note(0), panL(255), @@ -58,7 +63,8 @@ class DivPlatformSNES: public DivDispatch { inPorta(false), useWave(false), setPos(false), - vol(127) {} + vol(127), + useEnv(false) {} }; Channel chan[8]; DivDispatchOscBuffer* oscBuf[8]; @@ -86,11 +92,11 @@ class DivPlatformSNES: public DivDispatch { void muteChannel(int ch, bool mute); bool isStereo(); void notifyInsChange(int ins); + void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); const char** getRegisterSheet(); - const char* getEffectName(unsigned char effect); const void* getSampleMem(int index = 0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); @@ -98,6 +104,7 @@ class DivPlatformSNES: public DivDispatch { int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); private: + void updateWave(int ch); void writeOutVol(int ch); }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 50fd286b8..a2558a604 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -306,6 +306,14 @@ const char* gbHWSeqCmdTypes[6]={ "Loop until Release" }; +const char* snesGainModes[5]={ + "Direct", + "Decrease (linear)", + "Decrease (logarithmic)", + "Increase (linear)", + "Increase (bent line)" +}; + // do not change these! // anything other than a checkbox will look ugly! // @@ -3603,6 +3611,18 @@ void FurnaceGUI::drawInsEdit() { } } } + if (ins->type==DIV_INS_SNES) { + P(ImGui::Checkbox("Use wavetable",&ins->amiga.useWave)); + if (ins->amiga.useWave) { + int len=ins->amiga.waveLen+1; + if (ImGui::InputInt("Width",&len,16,64)) { + if (len<16) len=16; + if (len>256) len=256; + ins->amiga.waveLen=(len&(~15))-1; + PARAMETER + } + } + } ImGui::BeginDisabled(ins->amiga.useWave); P(ImGui::Checkbox("Use sample map (does not work yet!)",&ins->amiga.useNoteMap)); if (ins->amiga.useNoteMap) { @@ -3868,95 +3888,62 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SNES) if (ImGui::BeginTabItem("SNES")) { P(ImGui::Checkbox("Use envelope",&ins->snes.useEnv)); ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale); - if (ins->snes.useEnv) { - if (ImGui::BeginTable("SNESEnvParams",5,ImGuiTableFlags_NoHostExtendX)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); - ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch); + if (ImGui::BeginTable("SNESEnvParams",5,ImGuiTableFlags_NoHostExtendX)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - CENTER_TEXT("A"); - ImGui::TextUnformatted("A"); - ImGui::TableNextColumn(); - CENTER_TEXT("D"); - ImGui::TextUnformatted("D"); - ImGui::TableNextColumn(); - CENTER_TEXT("S"); - ImGui::TextUnformatted("S"); - ImGui::TableNextColumn(); - CENTER_TEXT("R"); - ImGui::TextUnformatted("R"); - ImGui::TableNextColumn(); - CENTER_TEXT("Envelope"); - ImGui::TextUnformatted("Envelope"); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + CENTER_TEXT("A"); + ImGui::TextUnformatted("A"); + ImGui::TableNextColumn(); + CENTER_TEXT("D"); + ImGui::TextUnformatted("D"); + ImGui::TableNextColumn(); + CENTER_TEXT("S"); + ImGui::TextUnformatted("S"); + ImGui::TableNextColumn(); + CENTER_TEXT("R"); + ImGui::TextUnformatted("R"); + ImGui::TableNextColumn(); + CENTER_TEXT("Envelope"); + ImGui::TextUnformatted("Envelope"); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - P(CWVSliderScalar("##Attack",sliderSize,ImGuiDataType_U8,&ins->snes.a,&_ZERO,&_FIFTEEN)); - ImGui::TableNextColumn(); - P(CWVSliderScalar("##Decay",sliderSize,ImGuiDataType_U8,&ins->snes.d,&_ZERO,&_SEVEN)); - ImGui::TableNextColumn(); - P(CWVSliderScalar("##Sustain",sliderSize,ImGuiDataType_U8,&ins->snes.s,&_ZERO,&_SEVEN)); - ImGui::TableNextColumn(); - P(CWVSliderScalar("##Release",sliderSize,ImGuiDataType_U8,&ins->snes.r,&_ZERO,&_THIRTY_ONE)); - ImGui::TableNextColumn(); - drawFMEnv(0,ins->snes.a+1,1+ins->snes.d*2,ins->snes.r,ins->snes.r,(14-ins->snes.s*2),(ins->snes.r==0),0,0,7,16,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Attack",sliderSize,ImGuiDataType_U8,&ins->snes.a,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay",sliderSize,ImGuiDataType_U8,&ins->snes.d,&_ZERO,&_SEVEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Sustain",sliderSize,ImGuiDataType_U8,&ins->snes.s,&_ZERO,&_SEVEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Release",sliderSize,ImGuiDataType_U8,&ins->snes.r,&_ZERO,&_THIRTY_ONE)); + ImGui::TableNextColumn(); + drawFMEnv(0,ins->snes.a+1,1+ins->snes.d*2,ins->snes.r,ins->snes.r,(14-ins->snes.s*2),(ins->snes.r==0),0,0,7,16,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); - ImGui::EndTable(); + ImGui::EndTable(); + } + P(ImGui::Checkbox("Apply echo filter",&ins->snes.applyFIR)); + if (ins->snes.applyFIR) { + double inBuf[8]; + fftw_complex outBuf[8]; + float curve[5]; + fftw_plan plan=fftw_plan_dft_r2c_1d(8,inBuf,outBuf,FFTW_ESTIMATE); + + ImGui::Text("Coefficients"); + ImGui::SameLine(); + P(ImGui::DragScalarN("##FIRCoeff",ImGuiDataType_S8,ins->snes.fir,8,1,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable + for (int i=0; i<8; i++) { + inBuf[i] = ins->snes.fir[i]; } - } else { - if (ImGui::BeginTable("SNESGainParams",3,ImGuiTableFlags_NoHostExtendX)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - CENTER_TEXT("Gain Mode"); - ImGui::TextUnformatted("Gain Mode"); - ImGui::TableNextColumn(); - CENTER_TEXT("Gain"); - ImGui::TextUnformatted("Gain"); - ImGui::TableNextColumn(); - CENTER_TEXT("Envelope"); - ImGui::TextUnformatted("Envelope"); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::RadioButton("Direct",ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DIRECT)) { - ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_DIRECT; - PARAMETER; - } - if (ImGui::RadioButton("Decrease (linear)",ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DEC_LINEAR)) { - ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_DEC_LINEAR; - PARAMETER; - } - if (ImGui::RadioButton("Decrease (logarithmic)",ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DEC_LOG)) { - ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_DEC_LOG; - PARAMETER; - } - if (ImGui::RadioButton("Increase (linear)",ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_INC_LINEAR)) { - ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_INC_LINEAR; - PARAMETER; - } - if (ImGui::RadioButton("Increase (bent line)",ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_INC_INVLOG)) { - ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_INC_INVLOG; - PARAMETER; - } - - ImGui::TableNextColumn(); - unsigned char gainMax=(ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DIRECT)?127:31; - if (ins->snes.gain>gainMax) ins->snes.gain=gainMax; - P(CWVSliderScalar("##Gain",sliderSize,ImGuiDataType_U8,&ins->snes.gain,&_ZERO,&gainMax)); - - ImGui::TableNextColumn(); - ImGui::Text("Envelope goes here..."); - - ImGui::EndTable(); + fftw_execute(plan); + for (int i=0; i<5; i++) { + curve[i] = sqrtf(powf(outBuf[i][0],2)+powf(outBuf[i][1],2))/128.f; } + ImGui::PlotLines("##FIRResponse",curve,5,0,"Frequency response",0.0,8.0,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale)); } ImGui::EndTabItem(); } @@ -4159,6 +4146,14 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { volMax=65535; } + if (ins->type==DIV_INS_SNES) { + if (ins->snes.useEnv) { + volMax=0; + } else { + volumeLabel="Gain Level"; + volMax=127; + } + } const char* dutyLabel="Duty/Noise"; int dutyMin=0; @@ -4203,7 +4198,7 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Noise"; dutyMax=8; } - 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) { + 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) { dutyMax=0; } if (ins->type==DIV_INS_VERA) { @@ -4244,7 +4239,7 @@ void FurnaceGUI::drawInsEdit() { waveMax=8; bitMode=true; } - + if (ins->type==DIV_INS_OPLL) { waveLabel="Patch"; } @@ -4284,6 +4279,10 @@ void FurnaceGUI::drawInsEdit() { ex1Max=65535; ex2Max=65535; } + if (ins->type==DIV_INS_SNES && !ins->snes.useEnv) { + ex1Max=4; + ex2Max=31; + } int panMin=0; int panMax=0; @@ -4373,7 +4372,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || ins->type==DIV_INS_MIKEY || - ins->type==DIV_INS_ES5506) { + ins->type==DIV_INS_ES5506 || + ins->type==DIV_INS_SNES) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -4391,6 +4391,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Cutoff",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_ES5506) { macroList.push_back(FurnaceGUIMacroDesc("Filter K1",&ins->std.ex1Macro,((ins->std.ex1Macro.mode==1)?(-ex1Max):0),ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,macroRelativeMode)); + } 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)); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } @@ -4406,6 +4408,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_ES5506) { macroList.push_back(FurnaceGUIMacroDesc("Filter K2",&ins->std.ex2Macro,((ins->std.ex2Macro.mode==1)?(-ex2Max):0),ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,macroRelativeMode)); + } else if (ins->type==DIV_INS_SNES) { + macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_VOLUME])); } else { macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); }