Merge branch 'master' of https://github.com/tildearrow/furnace into sample_macro

This commit is contained in:
cam900 2022-08-11 22:22:05 +09:00
commit 7d83cbb7d6
18 changed files with 290 additions and 58 deletions

View file

@ -21,8 +21,8 @@
#include "../engine.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {GB_apu_write(gb,a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) {GB_apu_write(gb,a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) {writes.emplace(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 16
@ -84,6 +84,12 @@ const char* DivPlatformGB::getEffectName(unsigned char effect) {
void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
GB_apu_write(gb,w.addr,w.val);
writes.pop();
}
GB_advance_cycles(gb,16);
bufL[i]=gb->apu_output.final_sample.left;
bufR[i]=gb->apu_output.final_sample.right;
@ -97,8 +103,8 @@ void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len)
void DivPlatformGB::updateWave() {
rWrite(0x1a,0);
for (int i=0; i<16; i++) {
int nibble1=15-ws.output[((i<<1)+antiClickWavePos)&31];
int nibble2=15-ws.output[((1+(i<<1))+antiClickWavePos)&31];
int nibble1=15-ws.output[((i<<1)+antiClickWavePos-1)&31];
int nibble2=15-ws.output[((1+(i<<1))+antiClickWavePos-1)&31];
rWrite(0x30+i,(nibble1<<4)|nibble2);
}
antiClickWavePos&=31;
@ -160,6 +166,25 @@ void DivPlatformGB::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].softEnv) {
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (i==2) {
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
chan[i].soundLen=64;
} else {
chan[i].envLen=0;
chan[i].envDir=1;
chan[i].envVol=chan[i].outVol;
chan[i].soundLen=64;
if (!chan[i].keyOn) chan[i].killIt=true;
chan[i].freqChanged=true;
}
}
}
if (chan[i].std.arp.had) {
if (i==3) { // noise
if (chan[i].std.arp.mode) {
@ -189,7 +214,7 @@ void DivPlatformGB::tick(bool sysTick) {
chan[i].duty=chan[i].std.duty.val;
if (i!=2) {
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
} else {
} else if (!chan[i].softEnv) {
if (parent->song.waveDutyIsVol) {
rWrite(16+i*5+2,gbVolMap[(chan[i].std.duty.val&3)<<2]);
}
@ -233,12 +258,63 @@ void DivPlatformGB::tick(bool sysTick) {
}
}
}
// run hardware sequence
if (chan[i].active) {
if (--chan[i].hwSeqDelay<=0) {
chan[i].hwSeqDelay=0;
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB);
int hwSeqCount=0;
while (chan[i].hwSeqPos<ins->gb.hwSeqLen && hwSeqCount<4) {
bool leave=false;
unsigned short data=ins->gb.hwSeq[chan[i].hwSeqPos].data;
switch (ins->gb.hwSeq[chan[i].hwSeqPos].cmd) {
case DivInstrumentGB::DIV_GB_HWCMD_ENVELOPE:
if (!chan[i].softEnv) {
chan[i].envLen=data&7;
chan[i].envDir=(data&8)?1:0;
chan[i].envVol=(data>>4)&15;
chan[i].soundLen=data>>8;
chan[i].keyOn=true;
}
break;
case DivInstrumentGB::DIV_GB_HWCMD_SWEEP:
chan[i].sweep=data;
chan[i].sweepChanged=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT:
chan[i].hwSeqDelay=data+1;
leave=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT_REL:
if (!chan[i].released) {
chan[i].hwSeqPos--;
leave=true;
}
break;
case DivInstrumentGB::DIV_GB_HWCMD_LOOP:
chan[i].hwSeqPos=data-1;
break;
case DivInstrumentGB::DIV_GB_HWCMD_LOOP_REL:
if (!chan[i].released) {
chan[i].hwSeqPos=data-1;
}
break;
}
chan[i].hwSeqPos++;
if (leave) break;
hwSeqCount++;
}
}
}
if (chan[i].sweepChanged) {
chan[i].sweepChanged=false;
if (i==0) {
rWrite(16+i*5,chan[i].sweep);
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (i==3) { // noise
int ntPos=chan[i].baseFreq;
@ -253,10 +329,11 @@ void DivPlatformGB::tick(bool sysTick) {
if (chan[i].keyOn) {
if (i==2) { // wave
rWrite(16+i*5,0x80);
rWrite(16+i*5+2,gbVolMap[chan[i].vol]);
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
} else {
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
rWrite(16+i*5+2,((chan[i].vol<<4))|(chan[i].envLen&7)|((chan[i].envDir&1)<<3));
rWrite(16+i*5+2,((chan[i].envVol<<4))|(chan[i].envLen&7)|((chan[i].envDir&1)<<3));
chan[i].lastKill=chan[i].envVol;
}
}
if (chan[i].keyOff) {
@ -276,6 +353,25 @@ void DivPlatformGB::tick(bool sysTick) {
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
if (chan[i].killIt) {
if (i!=2) {
//rWrite(16+i*5+2,8);
int killDelta=chan[i].lastKill-chan[i].outVol+1;
if (killDelta<0) killDelta+=16;
chan[i].lastKill=chan[i].outVol;
if (killDelta!=1) {
rWrite(16+i*5+2,((chan[i].envVol<<4))|8);
for (int j=0; j<killDelta; j++) {
rWrite(16+i*5+2,0x09);
rWrite(16+i*5+2,0x11);
rWrite(16+i*5+2,0x08);
}
}
}
chan[i].killIt=false;
}
}
}
}
@ -300,6 +396,10 @@ int DivPlatformGB::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].hwSeqPos=0;
chan[c.chan].hwSeqDelay=0;
chan[c.chan].released=false;
chan[c.chan].softEnv=ins->gb.softEnv;
chan[c.chan].macroInit(ins);
if (c.chan==2) {
if (chan[c.chan].wave<0) {
@ -308,23 +408,29 @@ int DivPlatformGB::dispatch(DivCommand c) {
}
ws.init(ins,32,15,chan[c.chan].insChanged);
}
if (chan[c.chan].insChanged) {
if ((chan[c.chan].insChanged || ins->gb.alwaysInit) && !chan[c.chan].softEnv) {
chan[c.chan].envVol=ins->gb.envVol;
chan[c.chan].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir;
chan[c.chan].soundLen=ins->gb.soundLen;
}
if (c.chan==2 && chan[c.chan].softEnv) {
chan[c.chan].soundLen=64;
}
chan[c.chan].insChanged=false;
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].hwSeqPos=0;
chan[c.chan].hwSeqDelay=0;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
chan[c.chan].released=true;
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
@ -332,21 +438,32 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].insChanged=true;
if (c.chan!=2) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB);
chan[c.chan].envVol=ins->gb.envVol;
chan[c.chan].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir;
chan[c.chan].soundLen=ins->gb.soundLen;
chan[c.chan].vol=chan[c.chan].envVol;
if (parent->song.gbInsAffectsEnvelope) {
rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(chan[c.chan].envLen&7)|((chan[c.chan].envDir&1)<<3));
if (!ins->gb.softEnv) {
chan[c.chan].envVol=ins->gb.envVol;
chan[c.chan].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir;
chan[c.chan].soundLen=ins->gb.soundLen;
chan[c.chan].vol=chan[c.chan].envVol;
chan[c.chan].outVol=chan[c.chan].vol;
if (parent->song.gbInsAffectsEnvelope) {
rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(chan[c.chan].envLen&7)|((chan[c.chan].envDir&1)<<3));
}
}
}
}
break;
case DIV_CMD_VOLUME:
chan[c.chan].vol=c.value;
chan[c.chan].outVol=c.value;
if (c.chan==2) {
rWrite(16+c.chan*5+2,gbVolMap[chan[c.chan].vol]);
rWrite(16+c.chan*5+2,gbVolMap[chan[c.chan].outVol]);
}
if (!chan[c.chan].softEnv) {
chan[c.chan].envVol=chan[c.chan].vol;
} else if (c.chan!=2) {
chan[c.chan].envVol=chan[c.chan].vol;
if (!chan[c.chan].keyOn) chan[c.chan].killIt=true;
chan[c.chan].freqChanged=true;
}
break;
case DIV_CMD_GET_VOLUME:
@ -481,7 +598,7 @@ void DivPlatformGB::reset() {
}
memset(gb,0,sizeof(GB_gameboy_t));
memset(regPool,0,128);
gb->model=GB_MODEL_DMG_B;
gb->model=model;
GB_apu_init(gb);
GB_set_sample_rate(gb,rate);
// enable all channels
@ -495,10 +612,18 @@ void DivPlatformGB::reset() {
antiClickWavePos=0;
}
int DivPlatformGB::getPortaFloor(int ch) {
return 24;
}
bool DivPlatformGB::isStereo() {
return true;
}
bool DivPlatformGB::getDCOffRequired() {
return (model==GB_MODEL_AGB);
}
void DivPlatformGB::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
@ -531,6 +656,20 @@ void DivPlatformGB::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformGB::setFlags(unsigned int flags) {
antiClickEnabled=!(flags&8);
switch (flags&3) {
case 0:
model=GB_MODEL_DMG_B;
break;
case 1:
model=GB_MODEL_CGB_C;
break;
case 2:
model=GB_MODEL_CGB_E;
break;
case 3:
model=GB_MODEL_AGB;
break;
}
}
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
@ -544,6 +683,7 @@ int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, unsigned int fl
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
model=GB_MODEL_DMG_B;
gb=new GB_gameboy_t;
setFlags(flags);
reset();

View file

@ -24,15 +24,17 @@
#include "../macroInt.h"
#include "../waveSynth.h"
#include "sound/gb/gb.h"
#include <queue>
class DivPlatformGB: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, pitch2, note, ins;
unsigned char duty, sweep;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta;
signed char vol, outVol, wave;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, released, softEnv, killIt;
signed char vol, outVol, wave, lastKill;
unsigned char envVol, envDir, envLen, soundLen;
unsigned short hwSeqPos, hwSeqDelay;
unsigned short hwSeqPos;
short hwSeqDelay;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
@ -54,9 +56,13 @@ class DivPlatformGB: public DivDispatch {
keyOn(false),
keyOff(false),
inPorta(false),
released(false),
softEnv(false),
killIt(false),
vol(15),
outVol(15),
wave(-1),
lastKill(0),
envVol(0),
envDir(0),
envLen(0),
@ -70,10 +76,17 @@ class DivPlatformGB: public DivDispatch {
bool antiClickEnabled;
unsigned char lastPan;
DivWaveSynth ws;
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
int antiClickPeriodCount, antiClickWavePos;
GB_gameboy_t* gb;
GB_model_t model;
unsigned char regPool[128];
unsigned char procMute();
@ -91,7 +104,9 @@ class DivPlatformGB: public DivDispatch {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
int getPortaFloor(int ch);
bool isStereo();
bool getDCOffRequired();
void notifyInsChange(int ins);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View file

@ -115,7 +115,7 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len)
pce->ResetTS(0);
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1,-32768,32767);
}
tempL[0]=(tempL[0]>>1)+(tempL[0]>>2);

View file

@ -366,7 +366,7 @@ void DivPlatformQSound::tick(bool sysTick) {
rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop);
rWrite(q1_reg_map[Q1V_START][i], qsound_addr);
rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000);
//logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop);
logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop);
// Write sample address. Enable volume
if (!chan[i].std.vol.had) {
rWrite(q1_reg_map[Q1V_VOL][i], chan[i].vol << 4);