mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-15 17:25:06 +00:00
implement Furnace-style PCM on Arcade/Gen/NES/PCE
This commit is contained in:
parent
812d0397a8
commit
0f28ae0fee
8 changed files with 132 additions and 39 deletions
|
@ -199,6 +199,16 @@ void DivPlatformArcade::tick() {
|
||||||
chan[i].keyOn=false;
|
chan[i].keyOn=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=8; i<13; i++) {
|
||||||
|
if (chan[i].freqChanged) {
|
||||||
|
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
|
||||||
|
if (chan[i].furnacePCM) {
|
||||||
|
chan[i].pcm.freq=MIN(255,((440.0*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250);
|
||||||
|
}
|
||||||
|
chan[i].freqChanged=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformArcade::muteChannel(int ch, bool mute) {
|
void DivPlatformArcade::muteChannel(int ch, bool mute) {
|
||||||
|
@ -216,18 +226,31 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
|
||||||
int DivPlatformArcade::dispatch(DivCommand c) {
|
int DivPlatformArcade::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);
|
||||||
if (c.chan>7) {
|
if (c.chan>7) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
chan[c.chan].pcm.sample=12*sampleBank+c.value%12;
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
chan[c.chan].pcm.sample=ins->amiga.initSample;
|
||||||
chan[c.chan].pcm.sample=-1;
|
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||||
break;
|
chan[c.chan].pcm.sample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[c.chan].pcm.pos=0;
|
||||||
|
chan[c.chan].baseFreq=c.value<<6;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].furnacePCM=true;
|
||||||
|
} else {
|
||||||
|
chan[c.chan].pcm.sample=12*sampleBank+c.value%12;
|
||||||
|
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||||
|
chan[c.chan].pcm.sample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[c.chan].pcm.pos=0;
|
||||||
|
chan[c.chan].pcm.freq=MIN(255,(parent->song.sample[chan[c.chan].pcm.sample]->rate*255)/31250);
|
||||||
|
chan[c.chan].furnacePCM=false;
|
||||||
}
|
}
|
||||||
chan[c.chan].pcm.pos=0;
|
|
||||||
chan[c.chan].pcm.freq=MIN(255,(parent->song.sample[chan[c.chan].pcm.sample]->rate*255)/31250);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||||
|
|
|
@ -16,7 +16,7 @@ class DivPlatformArcade: public DivDispatch {
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM;
|
||||||
int vol;
|
int vol;
|
||||||
unsigned char chVolL, chVolR;
|
unsigned char chVolL, chVolR;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class DivPlatformArcade: public DivDispatch {
|
||||||
unsigned char freq;
|
unsigned char freq;
|
||||||
PCMChannel(): sample(-1), pos(0), len(0), freq(0) {}
|
PCMChannel(): sample(-1), pos(0), len(0), freq(0) {}
|
||||||
} pcm;
|
} pcm;
|
||||||
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), chVolL(127), chVolR(127) {}
|
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), furnacePCM(false), vol(0), chVolL(127), chVolR(127) {}
|
||||||
};
|
};
|
||||||
Channel chan[13];
|
Channel chan[13];
|
||||||
struct QueuedWrite {
|
struct QueuedWrite {
|
||||||
|
|
|
@ -32,7 +32,7 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPeriod+=dacRate;
|
dacPeriod+=MAX(40,dacRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +102,9 @@ void DivPlatformGenesis::tick() {
|
||||||
int freqt=toFreq(chan[i].freq);
|
int freqt=toFreq(chan[i].freq);
|
||||||
immWrite(chanOffs[i]+0xa4,freqt>>8);
|
immWrite(chanOffs[i]+0xa4,freqt>>8);
|
||||||
immWrite(chanOffs[i]+0xa0,freqt&0xff);
|
immWrite(chanOffs[i]+0xa0,freqt&0xff);
|
||||||
|
if (chan[i].furnaceDac) {
|
||||||
|
dacRate=(1280000*1.25)/chan[i].baseFreq;
|
||||||
|
}
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
|
@ -171,20 +174,33 @@ int DivPlatformGenesis::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);
|
||||||
if (c.chan==5 && dacMode) {
|
if (c.chan==5 && dacMode) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
dacSample=12*sampleBank+c.value%12;
|
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
||||||
if (dacSample>=parent->song.sampleLen) {
|
dacSample=ins->amiga.initSample;
|
||||||
dacSample=-1;
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
break;
|
dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dacPos=0;
|
||||||
|
dacPeriod=0;
|
||||||
|
chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f));
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].furnaceDac=true;
|
||||||
|
} else { // compatible mode
|
||||||
|
dacSample=12*sampleBank+c.value%12;
|
||||||
|
if (dacSample>=parent->song.sampleLen) {
|
||||||
|
dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dacPos=0;
|
||||||
|
dacPeriod=0;
|
||||||
|
dacRate=1280000/parent->song.sample[dacSample]->rate;
|
||||||
|
chan[c.chan].furnaceDac=false;
|
||||||
}
|
}
|
||||||
dacPos=0;
|
|
||||||
dacPeriod=0;
|
|
||||||
dacRate=1280000/parent->song.sample[dacSample]->rate;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
|
||||||
|
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||||
|
|
|
@ -13,10 +13,10 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac;
|
||||||
int vol;
|
int vol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), furnaceDac(false), vol(0), pan(3) {}
|
||||||
};
|
};
|
||||||
Channel chan[10];
|
Channel chan[10];
|
||||||
bool isMuted[10];
|
bool isMuted[10];
|
||||||
|
|
|
@ -158,20 +158,49 @@ void DivPlatformNES::tick() {
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PCM
|
||||||
|
if (chan[4].freqChanged) {
|
||||||
|
chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,false);
|
||||||
|
if (chan[4].furnaceDac) {
|
||||||
|
dacRate=MIN(chan[4].freq,32000);
|
||||||
|
}
|
||||||
|
chan[4].freqChanged=false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformNES::dispatch(DivCommand c) {
|
int DivPlatformNES::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON:
|
case DIV_CMD_NOTE_ON:
|
||||||
if (c.chan==4) { // PCM
|
if (c.chan==4) { // PCM
|
||||||
dacSample=12*sampleBank+c.value%12;
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||||
if (dacSample>=parent->song.sampleLen) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
dacSample=-1;
|
dacSample=ins->amiga.initSample;
|
||||||
break;
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
|
dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dacPos=0;
|
||||||
|
dacPeriod=0;
|
||||||
|
chan[c.chan].baseFreq=440.0f*pow(2.0f,((float)(c.value+3)/12.0f));
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
}
|
||||||
|
chan[c.chan].active=true;
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
chan[c.chan].furnaceDac=true;
|
||||||
|
} else {
|
||||||
|
dacSample=12*sampleBank+c.value%12;
|
||||||
|
if (dacSample>=parent->song.sampleLen) {
|
||||||
|
dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dacPos=0;
|
||||||
|
dacPeriod=0;
|
||||||
|
dacRate=parent->song.sample[dacSample]->rate;
|
||||||
|
chan[c.chan].furnaceDac=false;
|
||||||
}
|
}
|
||||||
dacPos=0;
|
|
||||||
dacPeriod=0;
|
|
||||||
dacRate=parent->song.sample[dacSample]->rate;
|
|
||||||
break;
|
break;
|
||||||
} else if (c.chan==3) { // noise
|
} else if (c.chan==3) { // noise
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ class DivPlatformNES: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, prevFreq;
|
int freq, baseFreq, pitch, prevFreq;
|
||||||
unsigned char ins, note, duty, sweep;
|
unsigned char ins, note, duty, sweep;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -27,6 +27,7 @@ class DivPlatformNES: public DivDispatch {
|
||||||
keyOn(false),
|
keyOn(false),
|
||||||
keyOff(false),
|
keyOff(false),
|
||||||
inPorta(false),
|
inPorta(false),
|
||||||
|
furnaceDac(false),
|
||||||
vol(15),
|
vol(15),
|
||||||
outVol(15),
|
outVol(15),
|
||||||
wave(-1) {}
|
wave(-1) {}
|
||||||
|
|
|
@ -127,6 +127,9 @@ void DivPlatformPCE::tick() {
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
//DivInstrument* ins=parent->getIns(chan[i].ins);
|
//DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||||
|
if (chan[i].furnaceDac) {
|
||||||
|
chan[i].dacRate=chan[i].freq;
|
||||||
|
}
|
||||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||||
if (chan[i].note>0x5d) chan[i].freq=0x01;
|
if (chan[i].note>0x5d) chan[i].freq=0x01;
|
||||||
chWrite(i,0x02,chan[i].freq&0xff);
|
chWrite(i,0x02,chan[i].freq&0xff);
|
||||||
|
@ -153,14 +156,34 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON:
|
case DIV_CMD_NOTE_ON:
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
chan[c.chan].dacSample=12*sampleBank+c.value%12;
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||||
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=ins->amiga.initSample;
|
||||||
break;
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
|
chan[c.chan].dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[c.chan].dacPos=0;
|
||||||
|
chan[c.chan].dacPeriod=0;
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f)));
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
}
|
||||||
|
chan[c.chan].active=true;
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
chan[c.chan].furnaceDac=true;
|
||||||
|
} else {
|
||||||
|
chan[c.chan].dacSample=12*sampleBank+c.value%12;
|
||||||
|
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
|
chan[c.chan].dacSample=-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[c.chan].dacPos=0;
|
||||||
|
chan[c.chan].dacPeriod=0;
|
||||||
|
chan[c.chan].dacRate=1789773/parent->song.sample[chan[c.chan].dacSample]->rate;
|
||||||
|
chan[c.chan].furnaceDac=false;
|
||||||
}
|
}
|
||||||
chan[c.chan].dacPos=0;
|
|
||||||
chan[c.chan].dacPeriod=0;
|
|
||||||
chan[c.chan].dacRate=1789773/parent->song.sample[chan[c.chan].dacSample]->rate;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ class DivPlatformPCE: public DivDispatch {
|
||||||
unsigned int dacPos;
|
unsigned int dacPos;
|
||||||
int dacSample;
|
int dacSample;
|
||||||
unsigned char ins, pan;
|
unsigned char ins, pan;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -24,7 +24,7 @@ class DivPlatformPCE: public DivDispatch {
|
||||||
dacPeriod(0),
|
dacPeriod(0),
|
||||||
dacRate(0),
|
dacRate(0),
|
||||||
dacPos(0),
|
dacPos(0),
|
||||||
dacSample(0),
|
dacSample(-1),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
pan(255),
|
pan(255),
|
||||||
active(false),
|
active(false),
|
||||||
|
@ -35,6 +35,7 @@ class DivPlatformPCE: public DivDispatch {
|
||||||
inPorta(false),
|
inPorta(false),
|
||||||
noise(false),
|
noise(false),
|
||||||
pcm(false),
|
pcm(false),
|
||||||
|
furnaceDac(false),
|
||||||
vol(31),
|
vol(31),
|
||||||
outVol(31),
|
outVol(31),
|
||||||
wave(-1) {}
|
wave(-1) {}
|
||||||
|
|
Loading…
Reference in a new issue