Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
commit
487607b6ae
110 changed files with 1707 additions and 854 deletions
|
|
@ -275,6 +275,14 @@ struct DivRegWrite {
|
|||
addr(a), val(v) {}
|
||||
};
|
||||
|
||||
struct DivDelayedWrite {
|
||||
int time;
|
||||
DivRegWrite write;
|
||||
DivDelayedWrite(int t, unsigned int a, unsigned short v):
|
||||
time(t),
|
||||
write(a,v) {}
|
||||
};
|
||||
|
||||
struct DivDispatchOscBuffer {
|
||||
bool follow;
|
||||
unsigned int rate;
|
||||
|
|
@ -327,6 +335,14 @@ class DivDispatch {
|
|||
*/
|
||||
virtual void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
|
||||
/**
|
||||
* fill a write stream with data (e.g. for software-mixed PCM).
|
||||
* @param stream the write stream.
|
||||
* @param rate stream rate (e.g. 44100 for VGM).
|
||||
* @param len number of samples.
|
||||
*/
|
||||
virtual void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
|
||||
/**
|
||||
* send a command to this dispatch.
|
||||
* @param c a DivCommand.
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev121"
|
||||
#define DIV_ENGINE_VERSION 121
|
||||
#define DIV_VERSION "dev122"
|
||||
#define DIV_ENGINE_VERSION 122
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -431,7 +431,7 @@ class DivEngine {
|
|||
void processRow(int i, bool afterDelay);
|
||||
void nextOrder();
|
||||
void nextRow();
|
||||
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond);
|
||||
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, bool directStream);
|
||||
// returns true if end of song.
|
||||
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
||||
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
||||
|
|
@ -515,7 +515,7 @@ class DivEngine {
|
|||
// specify system to build ROM for.
|
||||
SafeWriter* buildROM(int sys);
|
||||
// dump to VGM.
|
||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false);
|
||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false);
|
||||
// dump to ZSM.
|
||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
||||
// dump command stream.
|
||||
|
|
|
|||
|
|
@ -937,13 +937,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.systemLen=2;
|
||||
ds.system[0]=DIV_SYSTEM_YM2612;
|
||||
ds.system[1]=DIV_SYSTEM_SMS;
|
||||
ds.systemVol[1]=24;
|
||||
ds.systemVol[1]=32;
|
||||
}
|
||||
if (ds.system[0]==DIV_SYSTEM_GENESIS_EXT) {
|
||||
ds.systemLen=2;
|
||||
ds.system[0]=DIV_SYSTEM_YM2612_EXT;
|
||||
ds.system[1]=DIV_SYSTEM_SMS;
|
||||
ds.systemVol[1]=24;
|
||||
ds.systemVol[1]=32;
|
||||
}
|
||||
if (ds.system[0]==DIV_SYSTEM_ARCADE) {
|
||||
ds.systemLen=2;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
}
|
||||
|
||||
void DivDispatch::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
}
|
||||
|
||||
void DivDispatch::tick(bool sysTick) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@
|
|||
|
||||
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
|
||||
|
||||
void DivPlatformGenesis::processDAC() {
|
||||
void DivPlatformGenesis::processDAC(int iRate) {
|
||||
if (softPCM) {
|
||||
softPCMTimer+=chipClock/576;
|
||||
if (softPCMTimer>rate) {
|
||||
softPCMTimer-=rate;
|
||||
if (softPCMTimer>iRate) {
|
||||
softPCMTimer-=iRate;
|
||||
|
||||
int sample=0;
|
||||
for (int i=5; i<7; i++) {
|
||||
|
|
@ -75,14 +75,14 @@ void DivPlatformGenesis::processDAC() {
|
|||
} else {
|
||||
if (!chan[5].dacReady) {
|
||||
chan[5].dacDelay+=32000;
|
||||
if (chan[5].dacDelay>=rate) {
|
||||
chan[5].dacDelay-=rate;
|
||||
if (chan[5].dacDelay>=iRate) {
|
||||
chan[5].dacDelay-=iRate;
|
||||
chan[5].dacReady=true;
|
||||
}
|
||||
}
|
||||
if (chan[5].dacMode && chan[5].dacSample!=-1) {
|
||||
chan[5].dacPeriod+=chan[5].dacRate;
|
||||
if (chan[5].dacPeriod>=rate) {
|
||||
if (chan[5].dacPeriod>=iRate) {
|
||||
DivSample* s=parent->getSample(chan[5].dacSample);
|
||||
if (s->samples>0) {
|
||||
if (!isMuted[5]) {
|
||||
|
|
@ -106,7 +106,7 @@ void DivPlatformGenesis::processDAC() {
|
|||
rWrite(0x2b,0);
|
||||
}
|
||||
}
|
||||
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;
|
||||
while (chan[5].dacPeriod>=iRate) chan[5].dacPeriod-=iRate;
|
||||
} else {
|
||||
chan[5].dacSample=-1;
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
|||
static int os[2];
|
||||
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
processDAC();
|
||||
processDAC(rate);
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
for (int i=0; i<6; i++) {
|
||||
|
|
@ -180,7 +180,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
|||
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
|
||||
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
processDAC();
|
||||
processDAC(rate);
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty()) {
|
||||
|
|
@ -237,6 +237,20 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
for (size_t i=0; i<len; i++) {
|
||||
processDAC(sRate);
|
||||
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
|
||||
writes.pop_front();
|
||||
}
|
||||
}
|
||||
regWrites.clear();
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::tick(bool sysTick) {
|
||||
for (int i=0; i<(softPCM?7:6); i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
|
|
|
|||
|
|
@ -124,12 +124,13 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
inline void processDAC();
|
||||
inline void processDAC(int iRate);
|
||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -416,12 +416,10 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (!parent->song.brokenOutVol2) {
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
} else {
|
||||
rWrite(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
} else if (!parent->song.brokenOutVol2) {
|
||||
rWrite(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@
|
|||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 16
|
||||
|
||||
const char* regCheatSheetT6W28[]={
|
||||
"Data0", "0",
|
||||
"Data1", "1",
|
||||
|
|
@ -72,14 +70,21 @@ void DivPlatformT6W28::acquire(short* bufL, short* bufR, size_t start, size_t le
|
|||
}
|
||||
|
||||
void DivPlatformT6W28::writeOutVol(int ch) {
|
||||
int left=15-CLAMP(chan[ch].outVol+chan[ch].panL-15,0,15);
|
||||
int right=15-CLAMP(chan[ch].outVol+chan[ch].panR-15,0,15);
|
||||
rWrite(0,0x90|(ch<<5)|(isMuted[ch]?15:left));
|
||||
rWrite(1,0x90|(ch<<5)|(isMuted[ch]?15:right));
|
||||
if (chan[ch].active) {
|
||||
int left=15-CLAMP(chan[ch].outVol+chan[ch].panL-15,0,15);
|
||||
int right=15-CLAMP(chan[ch].outVol+chan[ch].panR-15,0,15);
|
||||
rWrite(0,0x90|(ch<<5)|(isMuted[ch]?15:left));
|
||||
rWrite(1,0x90|(ch<<5)|(isMuted[ch]?15:right));
|
||||
} else {
|
||||
rWrite(0,0x9f|(ch<<5));
|
||||
rWrite(1,0x9f|(ch<<5));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformT6W28::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
double CHIP_DIVIDER=16;
|
||||
if (i==3) CHIP_DIVIDER=15;
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
|
|
@ -91,6 +96,12 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (i==3 && chan[i].std.duty.had) {
|
||||
if (chan[i].duty!=chan[i].std.duty.val) {
|
||||
chan[i].duty=((chan[i].std.duty.val==1)?4:0)|3;
|
||||
rWrite(1,0xe0+chan[i].duty);
|
||||
}
|
||||
}
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].panL=chan[i].std.panL.val&15;
|
||||
}
|
||||
|
|
@ -109,12 +120,13 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
rWrite(1,0xe0+chan[i].duty);
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
if (i==3) {
|
||||
rWrite(1,0xe7);
|
||||
rWrite(1,0x80|(2<<5)|(chan[3].freq&15));
|
||||
rWrite(1,chan[3].freq>>4);
|
||||
} else {
|
||||
|
|
@ -129,6 +141,8 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
int DivPlatformT6W28::dispatch(DivCommand c) {
|
||||
double CHIP_DIVIDER=16;
|
||||
if (c.chan==3) CHIP_DIVIDER=15;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE);
|
||||
|
|
@ -142,6 +156,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
writeOutVol(c.chan);
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
|
|
@ -150,6 +165,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
writeOutVol(c.chan);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
|
|
@ -166,8 +182,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
}
|
||||
writeOutVol(c.chan);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -205,7 +220,9 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
chan[c.chan].noise=c.value;
|
||||
if (c.chan!=3) break;
|
||||
chan[c.chan].duty=(((c.value&15)==1)?4:0)|3;
|
||||
rWrite(1,0xe0+chan[c.chan].duty);
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
chan[c.chan].panL=c.value>>4;
|
||||
|
|
@ -226,7 +243,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 31;
|
||||
return 15;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
|
|
@ -289,6 +306,8 @@ void DivPlatformT6W28::reset() {
|
|||
cycles=0;
|
||||
curChan=-1;
|
||||
delay=0;
|
||||
// default noise mode
|
||||
rWrite(1,0xe7);
|
||||
}
|
||||
|
||||
bool DivPlatformT6W28::isStereo() {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ class DivPlatformT6W28: public DivDispatch {
|
|||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note;
|
||||
int ins;
|
||||
unsigned char panL, panR;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise;
|
||||
unsigned char panL, panR, duty;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||
signed char vol, outVol;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
|
|
@ -46,13 +46,13 @@ class DivPlatformT6W28: public DivDispatch {
|
|||
ins(-1),
|
||||
panL(15),
|
||||
panR(15),
|
||||
duty(7),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
noise(false),
|
||||
vol(15),
|
||||
outVol(15) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ const char* regCheatSheetVB[]={
|
|||
"S6EV0", "550",
|
||||
"S6EV1", "554",
|
||||
"S6RAM", "558",
|
||||
|
||||
"RESET", "580",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -148,6 +150,13 @@ void DivPlatformVB::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (i==5 && chan[i].std.duty.had) {
|
||||
if ((chan[i].std.duty.val&7)!=((chan[i].envHigh>>4)&7)) {
|
||||
chan[i].envHigh&=~0x70;
|
||||
chan[i].envHigh|=(chan[i].std.duty.val&7)<<4;
|
||||
writeEnv(i,true);
|
||||
}
|
||||
}
|
||||
if (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;
|
||||
|
|
@ -176,7 +185,7 @@ void DivPlatformVB::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
|
||||
// ???
|
||||
chWrite(i,0x00,0x80);
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) {
|
||||
|
|
@ -310,12 +319,12 @@ int DivPlatformVB::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_FDS_MOD_DEPTH: // set modulation
|
||||
if (c.chan!=4) break;
|
||||
modulation=(c.value<<4)&15;
|
||||
modulation=(c.value&15)<<4;
|
||||
modType=true;
|
||||
chWrite(4,0x07,modulation);
|
||||
if (modulation!=0) {
|
||||
chan[c.chan].envHigh&=~0x70;
|
||||
chan[c.chan].envHigh|=0x40|((c.value&15)<<4);
|
||||
chan[c.chan].envHigh|=0x40|(c.value&0xf0);
|
||||
} else {
|
||||
chan[c.chan].envHigh&=~0x70;
|
||||
}
|
||||
|
|
@ -328,7 +337,7 @@ int DivPlatformVB::dispatch(DivCommand c) {
|
|||
chWrite(4,0x07,modulation);
|
||||
if (modulation!=0) {
|
||||
chan[c.chan].envHigh&=~0x70;
|
||||
chan[c.chan].envHigh|=0x10;
|
||||
chan[c.chan].envHigh|=0x40;
|
||||
} else {
|
||||
chan[c.chan].envHigh&=~0x70;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ void DivPlatformVERA::tick(bool sysTick) {
|
|||
if (i<16) {
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan=chan[i].std.panL.val&3;
|
||||
chan[i].pan=((chan[i].pan&1)<<1)|((chan[i].pan&2)>>1);
|
||||
rWriteHi(i,2,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +330,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=calcNoteFreq(c.chan,chan[c.chan].note);
|
||||
if (!chan[c.chan].inPorta && c.value && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=calcNoteFreq(c.chan,chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
|
|
|
|||
|
|
@ -796,6 +796,7 @@ void DivEngine::registerSystems() {
|
|||
{0x10, {DIV_CMD_AMIGA_FILTER, "10xx: Toggle filter (0 disables; 1 enables)"}},
|
||||
{0x11, {DIV_CMD_AMIGA_AM, "11xx: Toggle AM with next channel"}},
|
||||
{0x12, {DIV_CMD_AMIGA_PM, "12xx: Toggle period modulation with next channel"}},
|
||||
{0x13, {DIV_CMD_WAVE, "13xx: Set waveform"}}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -1594,7 +1595,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef(
|
||||
"OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_VOX,
|
||||
"OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_VOX,
|
||||
"an ADPCM sound chip manufactured by OKI and used in the Sharp X68000.",
|
||||
{"Sample"},
|
||||
{"PCM"},
|
||||
|
|
@ -1673,7 +1674,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
|
||||
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbe, 0, 7, true, false, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbe, 0, 7, true, false, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.",
|
||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2"},
|
||||
{"F1", "F2", "F3", "F4", "F5", "P1", "P2"},
|
||||
|
|
@ -1685,7 +1686,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
|
||||
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM and CSM", NULL, 0xbd, 0, 11, true, false, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM and CSM", NULL, 0xbd, 0, 11, true, false, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.\nthis one is in Extended Channel mode, which turns the third FM channel into four operators with independent notes/frequencies.",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
|
||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "P1", "P2", "CSM"},
|
||||
|
|
@ -1697,7 +1698,8 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_T6W28]=new DivSysDef(
|
||||
"T6W28", NULL, 0xbf, 0, 4, false, true, 0, false, 0,
|
||||
// 0x0a = wild guess. it may as well be 0x83
|
||||
"T6W28", NULL, 0xbf, 0x0a, 4, false, true, 0x160, false, 0,
|
||||
"an SN76489 derivative used in Neo Geo Pocket, has independent stereo volume and noise channel frequency.",
|
||||
{"Square 1", "Square 2", "Square 3", "Noise"},
|
||||
{"S1", "S2", "S3", "NO"},
|
||||
|
|
@ -1705,7 +1707,7 @@ void DivEngine::registerSystems() {
|
|||
{DIV_INS_T6W28, DIV_INS_T6W28, DIV_INS_T6W28, DIV_INS_T6W28},
|
||||
{},
|
||||
{
|
||||
{0x20, {DIV_CMD_STD_NOISE_MODE, "20xy: Set noise mode (x: preset/variable; y: thin pulse/noise)"}}
|
||||
{0x20, {DIV_CMD_STD_NOISE_MODE, "20xx: Set noise length (0: short, 1: long)"}}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||
|
||||
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond) {
|
||||
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, bool directStream) {
|
||||
unsigned char baseAddr1=isSecond?0xa0:0x50;
|
||||
unsigned char baseAddr2=isSecond?0x80:0;
|
||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||
|
|
@ -34,6 +34,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
switch (sys) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
for (int i=0; i<3; i++) { // set SL and RR to highest
|
||||
w->writeC(2|baseAddr1);
|
||||
w->writeC(0x80+i);
|
||||
|
|
@ -79,6 +81,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0x90|(i<<5)|15);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_T6W28:
|
||||
for (int i=0; i<4; i++) {
|
||||
w->writeC(0x30);
|
||||
w->writeC(0x90|(i<<5)|15);
|
||||
w->writeC(0x50);
|
||||
w->writeC(0x90|(i<<5)|15);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_GB:
|
||||
// square 1
|
||||
w->writeC(0xb3);
|
||||
|
|
@ -531,47 +541,57 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(baseAddr2|12);
|
||||
w->writeC(1);
|
||||
break;
|
||||
case DIV_SYSTEM_VBOY:
|
||||
// isn't it amazing when a chip has a built-in reset command?
|
||||
w->writeC(0xc7);
|
||||
w->writeS_BE(baseAddr2S|(0x580>>2));
|
||||
w->writeC(0xff);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (write.addr>=0xffff0000) { // Furnace special command
|
||||
unsigned char streamID=streamOff+((write.addr&0xff00)>>8);
|
||||
logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID);
|
||||
switch (write.addr&0xff) {
|
||||
case 0: // play sample
|
||||
if (write.val<song.sampleLen) {
|
||||
DivSample* sample=song.sample[write.val];
|
||||
w->writeC(0x95);
|
||||
w->writeC(streamID);
|
||||
w->writeS(write.val); // sample number
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
|
||||
if (sample->isLoopable() && !sampleDir[streamID]) {
|
||||
loopTimer[streamID]=sample->length8;
|
||||
loopSample[streamID]=write.val;
|
||||
if (!directStream) {
|
||||
unsigned char streamID=streamOff+((write.addr&0xff00)>>8);
|
||||
logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID);
|
||||
switch (write.addr&0xff) {
|
||||
case 0: // play sample
|
||||
if (write.val<song.sampleLen) {
|
||||
DivSample* sample=song.sample[write.val];
|
||||
w->writeC(0x95);
|
||||
w->writeC(streamID);
|
||||
w->writeS(write.val); // sample number
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
|
||||
if (sample->isLoopable() && !sampleDir[streamID]) {
|
||||
loopTimer[streamID]=sample->length8;
|
||||
loopSample[streamID]=write.val;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: // set sample freq
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(write.val);
|
||||
loopFreq[streamID]=write.val;
|
||||
break;
|
||||
case 2: // stop sample
|
||||
w->writeC(0x94);
|
||||
w->writeC(streamID);
|
||||
loopSample[streamID]=-1;
|
||||
break;
|
||||
case 3: // set sample direction
|
||||
sampleDir[streamID]=write.val;
|
||||
break;
|
||||
break;
|
||||
case 1: // set sample freq
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(write.val);
|
||||
loopFreq[streamID]=write.val;
|
||||
break;
|
||||
case 2: // stop sample
|
||||
w->writeC(0x94);
|
||||
w->writeC(streamID);
|
||||
loopSample[streamID]=-1;
|
||||
break;
|
||||
case 3: // set sample direction
|
||||
sampleDir[streamID]=write.val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (sys) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
switch (write.addr>>8) {
|
||||
case 0: // port 0
|
||||
w->writeC(2|baseAddr1);
|
||||
|
|
@ -593,6 +613,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(smsAddr);
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
case DIV_SYSTEM_T6W28:
|
||||
if (write.addr) {
|
||||
w->writeC(0x30);
|
||||
} else {
|
||||
w->writeC(0x50);
|
||||
}
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
case DIV_SYSTEM_GB:
|
||||
w->writeC(0xb3);
|
||||
w->writeC(baseAddr2|((write.addr-16)&0xff));
|
||||
|
|
@ -847,20 +875,20 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
}
|
||||
|
||||
#define CHIP_VOL(_id,_mult) { \
|
||||
double _vol=fabs(song.systemVol[i])*4.0*_mult; \
|
||||
double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \
|
||||
if (_vol<0.0) _vol=0.0; \
|
||||
if (_vol>32767.0) _vol=32767.0; \
|
||||
chipVol.push_back((_id)|(0x80000000)|(((unsigned int)_vol)<<16)); \
|
||||
}
|
||||
|
||||
#define CHIP_VOL_SECOND(_id,_mult) { \
|
||||
double _vol=fabs(song.systemVol[i])*4.0*_mult; \
|
||||
double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \
|
||||
if (_vol<0.0) _vol=0.0; \
|
||||
if (_vol>32767.0) _vol=32767.0; \
|
||||
chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \
|
||||
}
|
||||
|
||||
SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints) {
|
||||
SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints, bool directStream) {
|
||||
if (version<0x150) {
|
||||
lastError="VGM version is too low";
|
||||
return NULL;
|
||||
|
|
@ -964,6 +992,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
int loopSample[DIV_MAX_CHANS];
|
||||
bool sampleDir[DIV_MAX_CHANS];
|
||||
std::vector<unsigned int> chipVol;
|
||||
std::vector<DivDelayedWrite> delayedWrites[32];
|
||||
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
loopTimer[i]=0;
|
||||
|
|
@ -1162,6 +1192,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
break;
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
if (!hasOPN2) {
|
||||
hasOPN2=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
|
|
@ -1416,6 +1448,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_T6W28:
|
||||
if (!hasSN) {
|
||||
hasSN=0xc0000000|disCont[i].dispatch->chipClock;
|
||||
CHIP_VOL(0,1.0);
|
||||
snNoiseConfig=3;
|
||||
snNoiseSize=15;
|
||||
willExport[i]=true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1580,7 +1621,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
unsigned int exHeaderOff=w->tell();
|
||||
if (version>=0x170) {
|
||||
logD("writing extended header...");
|
||||
w->writeI(8);
|
||||
w->writeI(12);
|
||||
w->writeI(0);
|
||||
w->writeI(4);
|
||||
|
||||
|
|
@ -1608,7 +1649,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
sampleSeek+=sample->length8;
|
||||
}
|
||||
|
||||
if (writeDACSamples) for (int i=0; i<song.sampleLen; i++) {
|
||||
if (writeDACSamples && !directStream) for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
|
|
@ -1619,7 +1660,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
}
|
||||
}
|
||||
|
||||
if (writeNESSamples) for (int i=0; i<song.sampleLen; i++) {
|
||||
if (writeNESSamples && !directStream) for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
|
|
@ -1630,7 +1671,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
}
|
||||
}
|
||||
|
||||
if (writePCESamples) for (int i=0; i<song.sampleLen; i++) {
|
||||
if (writePCESamples && !directStream) for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
|
|
@ -1821,87 +1862,91 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
|
||||
// initialize streams
|
||||
int streamID=0;
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
if (!willExport[i]) continue;
|
||||
streamIDs[i]=streamID;
|
||||
switch (song.system[i]) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(0x02);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x2a); // DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(0);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(32000); // default
|
||||
streamID++;
|
||||
break;
|
||||
case DIV_SYSTEM_NES:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(20);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x11); // DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(7);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(32000); // default
|
||||
streamID++;
|
||||
break;
|
||||
case DIV_SYSTEM_PCE:
|
||||
for (int j=0; j<6; j++) {
|
||||
if (!directStream) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
if (!willExport[i]) continue;
|
||||
streamIDs[i]=streamID;
|
||||
switch (song.system[i]) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(27);
|
||||
w->writeC(j); // port
|
||||
w->writeC(0x06); // select+DAC
|
||||
w->writeC(0x02);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x2a); // DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(5);
|
||||
w->writeC(0);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(16000); // default
|
||||
w->writeI(32000); // default
|
||||
streamID++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_SWAN:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(isSecond[i]?0xa1:0x21);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x09); // DAC
|
||||
break;
|
||||
case DIV_SYSTEM_NES:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(20);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x11); // DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(0);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(7);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(24000); // default
|
||||
streamID++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(32000); // default
|
||||
streamID++;
|
||||
break;
|
||||
case DIV_SYSTEM_PCE:
|
||||
for (int j=0; j<6; j++) {
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(27);
|
||||
w->writeC(j); // port
|
||||
w->writeC(0x06); // select+DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(5);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(16000); // default
|
||||
streamID++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_SWAN:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(isSecond[i]?0xa1:0x21);
|
||||
w->writeC(0); // port
|
||||
w->writeC(0x09); // DAC
|
||||
|
||||
w->writeC(0x91);
|
||||
w->writeC(streamID);
|
||||
w->writeC(0);
|
||||
w->writeC(1);
|
||||
w->writeC(0);
|
||||
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(24000); // default
|
||||
streamID++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1930,10 +1975,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
break;
|
||||
}
|
||||
// stop all streams
|
||||
for (int i=0; i<streamID; i++) {
|
||||
w->writeC(0x94);
|
||||
w->writeC(i);
|
||||
loopSample[i]=-1;
|
||||
if (!directStream) {
|
||||
for (int i=0; i<streamID; i++) {
|
||||
w->writeC(0x94);
|
||||
w->writeC(i);
|
||||
loopSample[i]=-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!playing) {
|
||||
|
|
@ -1965,63 +2012,103 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
for (int i=0; i<song.systemLen; i++) {
|
||||
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
||||
for (DivRegWrite& j: writes) {
|
||||
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i]);
|
||||
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],directStream);
|
||||
writeCount++;
|
||||
}
|
||||
writes.clear();
|
||||
}
|
||||
// check whether we need to loop
|
||||
int totalWait=cycles>>MASTER_CLOCK_PREC;
|
||||
for (int i=0; i<streamID; i++) {
|
||||
if (loopSample[i]>=0) {
|
||||
loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait;
|
||||
if (directStream) {
|
||||
// render stream of all chips
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].dispatch->fillStream(delayedWrites[i],44100,totalWait);
|
||||
for (DivDelayedWrite& j: delayedWrites[i]) {
|
||||
sortedWrites.push_back(std::pair<int,DivDelayedWrite>(i,j));
|
||||
}
|
||||
delayedWrites[i].clear();
|
||||
}
|
||||
}
|
||||
bool haveNegatives=false;
|
||||
for (int i=0; i<streamID; i++) {
|
||||
if (loopSample[i]>=0) {
|
||||
if (loopTimer[i]<0) {
|
||||
haveNegatives=true;
|
||||
|
||||
if (!sortedWrites.empty()) {
|
||||
// sort if more than one chip
|
||||
if (song.systemLen>1) {
|
||||
std::sort(sortedWrites.begin(),sortedWrites.end(),[](const std::pair<int,DivDelayedWrite>& a, const std::pair<int,DivDelayedWrite>& b) -> bool {
|
||||
return a.second.time<b.second.time;
|
||||
});
|
||||
}
|
||||
|
||||
// write it out
|
||||
int lastOne=0;
|
||||
for (std::pair<int,DivDelayedWrite>& i: sortedWrites) {
|
||||
if (i.second.time>lastOne) {
|
||||
// write delay
|
||||
int delay=i.second.time-lastOne;
|
||||
if (delay>16) {
|
||||
w->writeC(0x61);
|
||||
w->writeS(totalWait);
|
||||
} else if (delay>0) {
|
||||
w->writeC(0x70+delay-1);
|
||||
}
|
||||
lastOne=i.second.time;
|
||||
}
|
||||
// write write
|
||||
performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],directStream);
|
||||
}
|
||||
sortedWrites.clear();
|
||||
totalWait-=lastOne;
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<streamID; i++) {
|
||||
if (loopSample[i]>=0) {
|
||||
loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (haveNegatives) {
|
||||
// finish all negatives
|
||||
int nextToTouch=-1;
|
||||
bool haveNegatives=false;
|
||||
for (int i=0; i<streamID; i++) {
|
||||
if (loopSample[i]>=0) {
|
||||
if (loopTimer[i]<0) {
|
||||
if (nextToTouch>=0) {
|
||||
if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i;
|
||||
} else {
|
||||
nextToTouch=i;
|
||||
}
|
||||
haveNegatives=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextToTouch>=0) {
|
||||
double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch])));
|
||||
if (waitTime>0) {
|
||||
w->writeC(0x61);
|
||||
w->writeS(waitTime);
|
||||
logV("wait is: %f",waitTime);
|
||||
totalWait-=waitTime;
|
||||
tickCount+=waitTime;
|
||||
}
|
||||
if (loopSample[nextToTouch]<song.sampleLen) {
|
||||
DivSample* sample=song.sample[loopSample[nextToTouch]];
|
||||
// insert loop
|
||||
if (sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)<sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
|
||||
w->writeC(0x93);
|
||||
w->writeC(nextToTouch);
|
||||
w->writeI(sampleOff8[loopSample[nextToTouch]]+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
w->writeC(0x81);
|
||||
w->writeI(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
while (haveNegatives) {
|
||||
// finish all negatives
|
||||
int nextToTouch=-1;
|
||||
for (int i=0; i<streamID; i++) {
|
||||
if (loopSample[i]>=0) {
|
||||
if (loopTimer[i]<0) {
|
||||
if (nextToTouch>=0) {
|
||||
if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i;
|
||||
} else {
|
||||
nextToTouch=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loopSample[nextToTouch]=-1;
|
||||
} else {
|
||||
haveNegatives=false;
|
||||
if (nextToTouch>=0) {
|
||||
double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch])));
|
||||
if (waitTime>0) {
|
||||
w->writeC(0x61);
|
||||
w->writeS(waitTime);
|
||||
logV("wait is: %f",waitTime);
|
||||
totalWait-=waitTime;
|
||||
tickCount+=waitTime;
|
||||
}
|
||||
if (loopSample[nextToTouch]<song.sampleLen) {
|
||||
DivSample* sample=song.sample[loopSample[nextToTouch]];
|
||||
// insert loop
|
||||
if (sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)<sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
|
||||
w->writeC(0x93);
|
||||
w->writeC(nextToTouch);
|
||||
w->writeI(sampleOff8[loopSample[nextToTouch]]+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
w->writeC(0x81);
|
||||
w->writeI(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
}
|
||||
}
|
||||
loopSample[nextToTouch]=-1;
|
||||
} else {
|
||||
haveNegatives=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// write wait
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue