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
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,7 +8,6 @@ winbuild/
|
||||||
win32build/
|
win32build/
|
||||||
macbuild/
|
macbuild/
|
||||||
linuxbuild/
|
linuxbuild/
|
||||||
webbuild/
|
|
||||||
*.swp
|
*.swp
|
||||||
.cache/
|
.cache/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -558,9 +558,10 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
||||||
w->writeC(es5506.envelope.k2Slow);
|
w->writeC(es5506.envelope.k2Slow);
|
||||||
|
|
||||||
// SNES
|
// SNES
|
||||||
|
// @tildearrow please update this
|
||||||
w->writeC(snes.useEnv);
|
w->writeC(snes.useEnv);
|
||||||
w->writeC(snes.gainMode);
|
w->writeC(0);
|
||||||
w->writeC(snes.gain);
|
w->writeC(0);
|
||||||
w->writeC(snes.a);
|
w->writeC(snes.a);
|
||||||
w->writeC(snes.d);
|
w->writeC(snes.d);
|
||||||
w->writeC(snes.s);
|
w->writeC(snes.s);
|
||||||
|
@ -1250,8 +1251,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||||
// SNES
|
// SNES
|
||||||
if (version>=109) {
|
if (version>=109) {
|
||||||
snes.useEnv=reader.readC();
|
snes.useEnv=reader.readC();
|
||||||
snes.gainMode=(DivInstrumentSNES::GainMode)reader.readC();
|
reader.readC();
|
||||||
snes.gain=reader.readC();
|
reader.readC();
|
||||||
snes.a=reader.readC();
|
snes.a=reader.readC();
|
||||||
snes.d=reader.readC();
|
snes.d=reader.readC();
|
||||||
snes.s=reader.readC();
|
snes.s=reader.readC();
|
||||||
|
|
|
@ -501,25 +501,17 @@ struct DivInstrumentES5506 {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DivInstrumentSNES {
|
struct DivInstrumentSNES {
|
||||||
enum GainMode: unsigned char {
|
bool useEnv, applyFIR;
|
||||||
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;
|
|
||||||
unsigned char a, d, s, r;
|
unsigned char a, d, s, r;
|
||||||
|
signed char fir[8];
|
||||||
DivInstrumentSNES():
|
DivInstrumentSNES():
|
||||||
useEnv(true),
|
useEnv(true),
|
||||||
gainMode(GAIN_MODE_DIRECT),
|
applyFIR(false),
|
||||||
gain(127),
|
|
||||||
a(15),
|
a(15),
|
||||||
d(7),
|
d(7),
|
||||||
s(7),
|
s(7),
|
||||||
r(0) {}
|
r(0),
|
||||||
|
fir{127, 0, 0, 0, 0, 0, 0, 0} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DivInstrument {
|
struct DivInstrument {
|
||||||
|
|
|
@ -22,9 +22,12 @@
|
||||||
#include "../../ta-log.h"
|
#include "../../ta-log.h"
|
||||||
#include <math.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 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[]={
|
const char* regCheatSheetSNESDSP[]={
|
||||||
"VxVOLL", "x0",
|
"VxVOLL", "x0",
|
||||||
|
@ -62,16 +65,6 @@ const char** DivPlatformSNES::getRegisterSheet() {
|
||||||
return regCheatSheetSNESDSP;
|
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) {
|
void DivPlatformSNES::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
short out[2];
|
short out[2];
|
||||||
short chOut[16];
|
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) {
|
void DivPlatformSNES::tick(bool sysTick) {
|
||||||
// KON/KOFF can't be written several times per one sample
|
// KON/KOFF can't be written several times per one sample
|
||||||
// so they have to be accumulated
|
// 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 kon=0;
|
||||||
unsigned char koff=0;
|
unsigned char koff=0;
|
||||||
for (int i=0; i<8; i++) {
|
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();
|
chan[i].std.next();
|
||||||
if (chan[i].std.vol.had) {
|
if (ins->type==DIV_INS_AMIGA && chan[i].std.vol.had) {
|
||||||
// TODO handle gain writes
|
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].std.arp.had) {
|
||||||
if (!chan[i].inPorta) {
|
if (!chan[i].inPorta) {
|
||||||
|
@ -112,6 +116,12 @@ void DivPlatformSNES::tick(bool sysTick) {
|
||||||
chan[i].freqChanged=true;
|
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.had) {
|
||||||
if (chan[i].std.pitch.mode) {
|
if (chan[i].std.pitch.mode) {
|
||||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||||
|
@ -139,13 +149,46 @@ void DivPlatformSNES::tick(bool sysTick) {
|
||||||
} else {
|
} else {
|
||||||
chan[i].audPos=0;
|
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) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
DivSample* s=parent->getSample(chan[i].sample);
|
DivSample* s=parent->getSample(chan[i].sample);
|
||||||
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
|
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));
|
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].freq>16383) chan[i].freq=16383;
|
||||||
if (chan[i].keyOn) {
|
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);
|
kon|=(1<<i);
|
||||||
chan[i].keyOn=false;
|
chan[i].keyOn=false;
|
||||||
}
|
}
|
||||||
|
@ -154,8 +197,8 @@ void DivPlatformSNES::tick(bool sysTick) {
|
||||||
chan[i].keyOff=false;
|
chan[i].keyOff=false;
|
||||||
}
|
}
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
rWrite(2+i*16,chan[i].freq&0xff);
|
chWrite(i,2,chan[i].freq&0xff);
|
||||||
rWrite(3+i*16,chan[i].freq>>8);
|
chWrite(i,3,chan[i].freq>>8);
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,20 +211,36 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
|
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].sample=ins->amiga.getSample(c.value);
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
chan[c.chan].useWave=false;
|
||||||
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value));
|
|
||||||
}
|
}
|
||||||
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].sample=-1;
|
chan[c.chan].sample=-1;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value));
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
}
|
}
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
chan[c.chan].keyOn=true;
|
chan[c.chan].keyOn=true;
|
||||||
chan[c.chan].macroInit(ins);
|
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;
|
chan[c.chan].insChanged=false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -274,6 +333,20 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
||||||
return 1;
|
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) {
|
void DivPlatformSNES::writeOutVol(int ch) {
|
||||||
// TODO negative (inverted) panning support
|
// TODO negative (inverted) panning support
|
||||||
int outL=0;
|
int outL=0;
|
||||||
|
@ -282,8 +355,8 @@ void DivPlatformSNES::writeOutVol(int ch) {
|
||||||
outL=chan[ch].vol*chan[ch].panL/255;
|
outL=chan[ch].vol*chan[ch].panL/255;
|
||||||
outR=chan[ch].vol*chan[ch].panR/255;
|
outR=chan[ch].vol*chan[ch].panR/255;
|
||||||
}
|
}
|
||||||
rWrite(0+ch*16,outL);
|
chWrite(ch,0,outL);
|
||||||
rWrite(1+ch*16,outR);
|
chWrite(ch,1,outR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformSNES::muteChannel(int ch, bool mute) {
|
void DivPlatformSNES::muteChannel(int ch, bool mute) {
|
||||||
|
@ -296,6 +369,7 @@ void DivPlatformSNES::forceIns() {
|
||||||
chan[i].insChanged=true;
|
chan[i].insChanged=true;
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
chan[i].sample=-1;
|
chan[i].sample=-1;
|
||||||
|
updateWave(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,10 +408,14 @@ void DivPlatformSNES::reset() {
|
||||||
rWrite(0x5d,sampleTableBase>>8);
|
rWrite(0x5d,sampleTableBase>>8);
|
||||||
rWrite(0x0c,127); // global volume left
|
rWrite(0x0c,127); // global volume left
|
||||||
rWrite(0x1c,127); // global volume right
|
rWrite(0x1c,127); // global volume right
|
||||||
|
rWrite(0x6c,0); // get DSP out of reset
|
||||||
for (int i=0; i<8; i++) {
|
for (int i=0; i<8; i++) {
|
||||||
chan[i]=Channel();
|
chan[i]=Channel();
|
||||||
chan[i].std.setEngine(parent);
|
chan[i].std.setEngine(parent);
|
||||||
|
chan[i].ws.setEngine(parent);
|
||||||
|
chan[i].ws.init(NULL,32,255);
|
||||||
writeOutVol(i);
|
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) {
|
void DivPlatformSNES::notifyInsDeletion(void* ins) {
|
||||||
for (int i=0; i<8; i++) {
|
for (int i=0; i<8; i++) {
|
||||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||||
|
@ -383,21 +472,14 @@ size_t DivPlatformSNES::getSampleMemUsage(int index) {
|
||||||
void DivPlatformSNES::renderSamples() {
|
void DivPlatformSNES::renderSamples() {
|
||||||
memset(sampleMem,0,getSampleMemCapacity());
|
memset(sampleMem,0,getSampleMemCapacity());
|
||||||
|
|
||||||
// skip past sample table
|
// skip past sample table and wavetable buffer
|
||||||
size_t memPos=sampleTableBase+0x400;
|
size_t memPos=sampleTableBase+8*4+8*9*16;
|
||||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||||
DivSample* s=parent->song.sample[i];
|
DivSample* s=parent->song.sample[i];
|
||||||
int length=s->lengthBRR;
|
int length=s->lengthBRR;
|
||||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
|
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
|
||||||
if (actualLength>0) {
|
if (actualLength>0) {
|
||||||
size_t tabAddr=sampleTableBase+i*4;
|
|
||||||
size_t loopPos=memPos;
|
|
||||||
if (s->loopStart>=0) loopPos+=s->loopStart;
|
|
||||||
s->offSNES=memPos;
|
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);
|
memcpy(&sampleMem[memPos],s->data8,actualLength);
|
||||||
memPos+=actualLength;
|
memPos+=actualLength;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,19 +22,22 @@
|
||||||
|
|
||||||
#include "../dispatch.h"
|
#include "../dispatch.h"
|
||||||
#include "../macroInt.h"
|
#include "../macroInt.h"
|
||||||
|
#include "../waveSynth.h"
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include "sound/snes/SPC_DSP.h"
|
#include "sound/snes/SPC_DSP.h"
|
||||||
|
|
||||||
class DivPlatformSNES: public DivDispatch {
|
class DivPlatformSNES: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, pitch2;
|
int freq, baseFreq, pitch, pitch2;
|
||||||
unsigned int audPos;
|
unsigned int audPos, wtLen;
|
||||||
int sample, ins;
|
int sample, wave, ins;
|
||||||
int note;
|
int note;
|
||||||
int panL, panR;
|
int panL, panR;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos;
|
||||||
signed char vol;
|
signed char vol;
|
||||||
|
bool useEnv;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
DivWaveSynth ws;
|
||||||
void macroInit(DivInstrument* which) {
|
void macroInit(DivInstrument* which) {
|
||||||
std.init(which);
|
std.init(which);
|
||||||
pitch2=0;
|
pitch2=0;
|
||||||
|
@ -45,7 +48,9 @@ class DivPlatformSNES: public DivDispatch {
|
||||||
pitch(0),
|
pitch(0),
|
||||||
pitch2(0),
|
pitch2(0),
|
||||||
audPos(0),
|
audPos(0),
|
||||||
|
wtLen(16),
|
||||||
sample(-1),
|
sample(-1),
|
||||||
|
wave(-1),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
note(0),
|
note(0),
|
||||||
panL(255),
|
panL(255),
|
||||||
|
@ -58,7 +63,8 @@ class DivPlatformSNES: public DivDispatch {
|
||||||
inPorta(false),
|
inPorta(false),
|
||||||
useWave(false),
|
useWave(false),
|
||||||
setPos(false),
|
setPos(false),
|
||||||
vol(127) {}
|
vol(127),
|
||||||
|
useEnv(false) {}
|
||||||
};
|
};
|
||||||
Channel chan[8];
|
Channel chan[8];
|
||||||
DivDispatchOscBuffer* oscBuf[8];
|
DivDispatchOscBuffer* oscBuf[8];
|
||||||
|
@ -86,11 +92,11 @@ class DivPlatformSNES: public DivDispatch {
|
||||||
void muteChannel(int ch, bool mute);
|
void muteChannel(int ch, bool mute);
|
||||||
bool isStereo();
|
bool isStereo();
|
||||||
void notifyInsChange(int ins);
|
void notifyInsChange(int ins);
|
||||||
|
void notifyWaveChange(int wave);
|
||||||
void notifyInsDeletion(void* ins);
|
void notifyInsDeletion(void* ins);
|
||||||
void poke(unsigned int addr, unsigned short val);
|
void poke(unsigned int addr, unsigned short val);
|
||||||
void poke(std::vector<DivRegWrite>& wlist);
|
void poke(std::vector<DivRegWrite>& wlist);
|
||||||
const char** getRegisterSheet();
|
const char** getRegisterSheet();
|
||||||
const char* getEffectName(unsigned char effect);
|
|
||||||
const void* getSampleMem(int index = 0);
|
const void* getSampleMem(int index = 0);
|
||||||
size_t getSampleMemCapacity(int index = 0);
|
size_t getSampleMemCapacity(int index = 0);
|
||||||
size_t getSampleMemUsage(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);
|
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||||
void quit();
|
void quit();
|
||||||
private:
|
private:
|
||||||
|
void updateWave(int ch);
|
||||||
void writeOutVol(int ch);
|
void writeOutVol(int ch);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -306,6 +306,14 @@ const char* gbHWSeqCmdTypes[6]={
|
||||||
"Loop until Release"
|
"Loop until Release"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* snesGainModes[5]={
|
||||||
|
"Direct",
|
||||||
|
"Decrease (linear)",
|
||||||
|
"Decrease (logarithmic)",
|
||||||
|
"Increase (linear)",
|
||||||
|
"Increase (bent line)"
|
||||||
|
};
|
||||||
|
|
||||||
// do not change these!
|
// do not change these!
|
||||||
// anything other than a checkbox will look ugly!
|
// 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);
|
ImGui::BeginDisabled(ins->amiga.useWave);
|
||||||
P(ImGui::Checkbox("Use sample map (does not work yet!)",&ins->amiga.useNoteMap));
|
P(ImGui::Checkbox("Use sample map (does not work yet!)",&ins->amiga.useNoteMap));
|
||||||
if (ins->amiga.useNoteMap) {
|
if (ins->amiga.useNoteMap) {
|
||||||
|
@ -3868,7 +3888,6 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->type==DIV_INS_SNES) if (ImGui::BeginTabItem("SNES")) {
|
if (ins->type==DIV_INS_SNES) if (ImGui::BeginTabItem("SNES")) {
|
||||||
P(ImGui::Checkbox("Use envelope",&ins->snes.useEnv));
|
P(ImGui::Checkbox("Use envelope",&ins->snes.useEnv));
|
||||||
ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale);
|
ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale);
|
||||||
if (ins->snes.useEnv) {
|
|
||||||
if (ImGui::BeginTable("SNESEnvParams",5,ImGuiTableFlags_NoHostExtendX)) {
|
if (ImGui::BeginTable("SNESEnvParams",5,ImGuiTableFlags_NoHostExtendX)) {
|
||||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
|
||||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
|
||||||
|
@ -3907,56 +3926,24 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
} else {
|
P(ImGui::Checkbox("Apply echo filter",&ins->snes.applyFIR));
|
||||||
if (ImGui::BeginTable("SNESGainParams",3,ImGuiTableFlags_NoHostExtendX)) {
|
if (ins->snes.applyFIR) {
|
||||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
double inBuf[8];
|
||||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
|
fftw_complex outBuf[8];
|
||||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
|
float curve[5];
|
||||||
|
fftw_plan plan=fftw_plan_dft_r2c_1d(8,inBuf,outBuf,FFTW_ESTIMATE);
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::Text("Coefficients");
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
CENTER_TEXT("Gain Mode");
|
P(ImGui::DragScalarN("##FIRCoeff",ImGuiDataType_S8,ins->snes.fir,8,1,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable
|
||||||
ImGui::TextUnformatted("Gain Mode");
|
for (int i=0; i<8; i++) {
|
||||||
ImGui::TableNextColumn();
|
inBuf[i] = ins->snes.fir[i];
|
||||||
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)) {
|
fftw_execute(plan);
|
||||||
ins->snes.gainMode=DivInstrumentSNES::GAIN_MODE_DEC_LINEAR;
|
for (int i=0; i<5; i++) {
|
||||||
PARAMETER;
|
curve[i] = sqrtf(powf(outBuf[i][0],2)+powf(outBuf[i][1],2))/128.f;
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
ImGui::PlotLines("##FIRResponse",curve,5,0,"Frequency response",0.0,8.0,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale));
|
||||||
}
|
}
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
@ -4159,6 +4146,14 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->type==DIV_INS_ES5506) {
|
if (ins->type==DIV_INS_ES5506) {
|
||||||
volMax=65535;
|
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";
|
const char* dutyLabel="Duty/Noise";
|
||||||
int dutyMin=0;
|
int dutyMin=0;
|
||||||
|
@ -4203,7 +4198,7 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
dutyLabel="Noise";
|
dutyLabel="Noise";
|
||||||
dutyMax=8;
|
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;
|
dutyMax=0;
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_VERA) {
|
if (ins->type==DIV_INS_VERA) {
|
||||||
|
@ -4284,6 +4279,10 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ex1Max=65535;
|
ex1Max=65535;
|
||||||
ex2Max=65535;
|
ex2Max=65535;
|
||||||
}
|
}
|
||||||
|
if (ins->type==DIV_INS_SNES && !ins->snes.useEnv) {
|
||||||
|
ex1Max=4;
|
||||||
|
ex2Max=31;
|
||||||
|
}
|
||||||
|
|
||||||
int panMin=0;
|
int panMin=0;
|
||||||
int panMax=0;
|
int panMax=0;
|
||||||
|
@ -4373,7 +4372,8 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ins->type==DIV_INS_MULTIPCM ||
|
ins->type==DIV_INS_MULTIPCM ||
|
||||||
ins->type==DIV_INS_SU ||
|
ins->type==DIV_INS_SU ||
|
||||||
ins->type==DIV_INS_MIKEY ||
|
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));
|
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
|
||||||
}
|
}
|
||||||
if (ex1Max>0) {
|
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]));
|
macroList.push_back(FurnaceGUIMacroDesc("Cutoff",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||||
} else if (ins->type==DIV_INS_ES5506) {
|
} 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));
|
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 {
|
} else {
|
||||||
macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
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]));
|
macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||||
} else if (ins->type==DIV_INS_ES5506) {
|
} 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));
|
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 {
|
} else {
|
||||||
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits));
|
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