mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-23 13:05:11 +00:00
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:
parent
73c6adb821
commit
7956d41f1b
6 changed files with 224 additions and 139 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,7 +8,6 @@ winbuild/
|
|||
win32build/
|
||||
macbuild/
|
||||
linuxbuild/
|
||||
webbuild/
|
||||
*.swp
|
||||
.cache/
|
||||
.DS_Store
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue