SNES: Get wavesynth and envelope working

No samples, inverted volumes and E/P/N yet
It's been 3 months...
This commit is contained in:
Natt Akuma 2022-09-18 17:59:58 +07:00
parent 73c6adb821
commit 7956d41f1b
6 changed files with 224 additions and 139 deletions

1
.gitignore vendored
View file

@ -8,7 +8,6 @@ winbuild/
win32build/
macbuild/
linuxbuild/
webbuild/
*.swp
.cache/
.DS_Store

View file

@ -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();

View file

@ -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 {

View file

@ -22,9 +22,12 @@
#include "../../ta-log.h"
#include <math.h>
#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<<i);
chan[i].keyOn=false;
}
@ -154,8 +197,8 @@ void DivPlatformSNES::tick(bool sysTick) {
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
rWrite(2+i*16,chan[i].freq&0xff);
rWrite(3+i*16,chan[i].freq>>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<chan[ch].wtLen/16; i++) {
sampleMem[pos++]=0xb0;
for (int j=0; j<8; j++) {
int nibble1=(chan[ch].ws.output[i*16+j*2]-8)&15;
int nibble2=(chan[ch].ws.output[i*16+j*2+1]-8)&15;
sampleMem[pos++]=(nibble1<<4)|nibble2;
}
}
sampleMem[pos-9]=0xb3; // mark loop
}
void DivPlatformSNES::writeOutVol(int ch) {
// TODO negative (inverted) panning support
int outL=0;
@ -282,8 +355,8 @@ void DivPlatformSNES::writeOutVol(int ch) {
outL=chan[ch].vol*chan[ch].panL/255;
outR=chan[ch].vol*chan[ch].panR/255;
}
rWrite(0+ch*16,outL);
rWrite(1+ch*16,outR);
chWrite(ch,0,outL);
chWrite(ch,1,outR);
}
void DivPlatformSNES::muteChannel(int ch, bool mute) {
@ -296,6 +369,7 @@ void DivPlatformSNES::forceIns() {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
updateWave(i);
}
}
@ -334,10 +408,14 @@ void DivPlatformSNES::reset() {
rWrite(0x5d,sampleTableBase>>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; i<parent->song.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;
}

View file

@ -22,19 +22,22 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include "../waveSynth.h"
#include <queue>
#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<DivRegWrite>& 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);
};

View file

@ -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));
}