From 650beebe99fdee4b20da4baddf9356dd188844de Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 18 Feb 2022 12:58:36 -0500 Subject: [PATCH] split VGM ops code --- CMakeLists.txt | 1 + src/engine/engine.cpp | 1136 --------------------------------------- src/engine/engine.h | 7 + src/engine/fileOps.cpp | 7 - src/engine/vgmOps.cpp | 1151 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1159 insertions(+), 1143 deletions(-) create mode 100644 src/engine/vgmOps.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bb17f1268..ef82bf7a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,7 @@ src/engine/playback.cpp src/engine/sample.cpp src/engine/song.cpp src/engine/wavetable.cpp +src/engine/vgmOps.cpp src/engine/platform/abstract.cpp src/engine/platform/genesis.cpp src/engine/platform/genesisext.cpp diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1b2f18b5e..5ddfd4bdb 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -24,9 +24,7 @@ #include "instrument.h" #include "safeReader.h" #include "../ta-log.h" -#include "../ta-log.h" #include "../fileutils.h" -#include "../utfutils.h" #include "../audio/sdl.h" #include #ifndef _WIN32 @@ -1450,373 +1448,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan) { return "Invalid effect"; } -#define addWarning(x) \ - if (warnings.empty()) { \ - warnings+=x; \ - } else { \ - warnings+=(String("\n")+x); \ - } - -void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond) { - if (write.addr==0xffffffff) { // Furnace fake reset - switch (sys) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612: - for (int i=0; i<3; i++) { // set SL and RR to highest - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x80+i); - w->writeC(0xff); - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x84+i); - w->writeC(0xff); - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x88+i); - w->writeC(0xff); - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x8c+i); - w->writeC(0xff); - - w->writeC(isSecond?0xa3:0x53); - w->writeC(0x80+i); - w->writeC(0xff); - w->writeC(isSecond?0xa3:0x53); - w->writeC(0x84+i); - w->writeC(0xff); - w->writeC(isSecond?0xa3:0x53); - w->writeC(0x88+i); - w->writeC(0xff); - w->writeC(isSecond?0xa3:0x53); - w->writeC(0x8c+i); - w->writeC(0xff); - } - for (int i=0; i<3; i++) { // note off - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x28); - w->writeC(i); - w->writeC(isSecond?0xa2:0x52); - w->writeC(0x28); - w->writeC(4+i); - } - w->writeC(isSecond?0xa2:0x52); // disable DAC - w->writeC(0x2b); - w->writeC(0); - if (sys!=DIV_SYSTEM_YM2612) { - for (int i=0; i<4; i++) { - w->writeC(isSecond?0x30:0x50); - w->writeC(0x90|(i<<5)|15); - } - } - break; - case DIV_SYSTEM_SMS: - for (int i=0; i<4; i++) { - w->writeC(isSecond?0x30:0x50); - w->writeC(0x90|(i<<5)|15); - } - break; - case DIV_SYSTEM_GB: - // square 1 - w->writeC(0xb3); - w->writeC(isSecond?0x82:2); - w->writeC(0); - w->writeC(0xb3); - w->writeC(isSecond?0x84:4); - w->writeC(0x80); - - // square 2 - w->writeC(0xb3); - w->writeC(isSecond?0x87:7); - w->writeC(0); - w->writeC(0xb3); - w->writeC(isSecond?0x89:9); - w->writeC(0x80); - - // wave - w->writeC(0xb3); - w->writeC(isSecond?0x8c:0x0c); - w->writeC(0); - w->writeC(0xb3); - w->writeC(isSecond?0x8e:0x0e); - w->writeC(0x80); - - // noise - w->writeC(0xb3); - w->writeC(isSecond?0x91:0x11); - w->writeC(0); - w->writeC(0xb3); - w->writeC(isSecond?0x93:0x13); - w->writeC(0x80); - break; - case DIV_SYSTEM_PCE: - for (int i=0; i<6; i++) { - w->writeC(0xb9); - w->writeC(isSecond?0x80:0); - w->writeC(i); - w->writeC(0xb9); - w->writeC(isSecond?0x84:4); - w->writeC(0); - } - break; - case DIV_SYSTEM_NES: - w->writeC(0xb4); - w->writeC(isSecond?0x95:0x15); - w->writeC(0); - break; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_YM2151: - for (int i=0; i<8; i++) { - w->writeC(isSecond?0xa4:0x54); - w->writeC(0xe0+i); - w->writeC(0xff); - w->writeC(isSecond?0xa4:0x54); - w->writeC(0xe8+i); - w->writeC(0xff); - w->writeC(isSecond?0xa4:0x54); - w->writeC(0xf0+i); - w->writeC(0xff); - w->writeC(isSecond?0xa4:0x54); - w->writeC(0xf8+i); - w->writeC(0xff); - - w->writeC(isSecond?0xa4:0x54); - w->writeC(0x08); - w->writeC(i); - } - if (sys==DIV_SYSTEM_ARCADE) { - for (int i=0; i<5; i++) { - w->writeC(0xc0); - w->writeS((isSecond?0x8086:0x86)+(i<<3)); - w->writeC(3); - } - } - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - for (int i=0; i<2; i++) { // set SL and RR to highest - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x81+i); - w->writeC(0xff); - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x85+i); - w->writeC(0xff); - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x89+i); - w->writeC(0xff); - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x8d+i); - w->writeC(0xff); - - w->writeC(isSecond?0xa9:0x59); - w->writeC(0x81+i); - w->writeC(0xff); - w->writeC(isSecond?0xa9:0x59); - w->writeC(0x85+i); - w->writeC(0xff); - w->writeC(isSecond?0xa9:0x59); - w->writeC(0x89+i); - w->writeC(0xff); - w->writeC(isSecond?0xa9:0x59); - w->writeC(0x8d+i); - w->writeC(0xff); - } - for (int i=0; i<2; i++) { // note off - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x28); - w->writeC(1+i); - w->writeC(isSecond?0xa8:0x58); - w->writeC(0x28); - w->writeC(5+i); - } - - // reset AY - w->writeC(isSecond?0xa8:0x58); - w->writeC(7); - w->writeC(0x3f); - - w->writeC(isSecond?0xa8:0x58); - w->writeC(8); - w->writeC(0); - - w->writeC(isSecond?0xa8:0x58); - w->writeC(9); - w->writeC(0); - - w->writeC(isSecond?0xa8:0x58); - w->writeC(10); - w->writeC(0); - - // reset sample - w->writeC(isSecond?0xa9:0x59); - w->writeC(0); - w->writeC(0xbf); - break; - case DIV_SYSTEM_AY8910: - w->writeC(0xa0); - w->writeC(isSecond?0x87:7); - w->writeC(0x3f); - - w->writeC(0xa0); - w->writeC(isSecond?0x88:8); - w->writeC(0); - - w->writeC(0xa0); - w->writeC(isSecond?0x89:9); - w->writeC(0); - - w->writeC(0xa0); - w->writeC(isSecond?0x8a:10); - w->writeC(0); - break; - case DIV_SYSTEM_AY8930: - w->writeC(0xa0); - w->writeC(isSecond?0x8d:0x0d); - w->writeC(0); - w->writeC(0xa0); - w->writeC(isSecond?0x8d:0x0d); - w->writeC(0xa0); - break; - case DIV_SYSTEM_SAA1099: - w->writeC(0xbd); - w->writeC(isSecond?0x9c:0x1c); - w->writeC(0x02); - w->writeC(0xbd); - w->writeC(isSecond?0x94:0x14); - w->writeC(0); - w->writeC(0xbd); - w->writeC(isSecond?0x95:0x15); - w->writeC(0); - - for (int i=0; i<6; i++) { - w->writeC(0xbd); - w->writeC((isSecond?0x80:0)+i); - w->writeC(0); - } - break; - default: - break; - } - } - if (write.addr>=0xffff0000) { // Furnace special command - unsigned char streamID=streamOff+((write.addr&0xff00)>>8); - switch (write.addr&0xff) { - case 0: // play sample - if (write.valwriteC(0x95); - w->writeC(streamID); - w->writeS(write.val); // sample number - w->writeC((sample->loopStart==0)); // flags - if (sample->loopStart>0) { - loopTimer[streamID]=sample->rendLength; - 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; - } - return; - } - switch (sys) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612: - switch (write.addr>>8) { - case 0: // port 0 - w->writeC(isSecond?0xa2:0x52); - w->writeC(write.addr&0xff); - w->writeC(write.val); - break; - case 1: // port 1 - w->writeC(isSecond?0xa3:0x53); - w->writeC(write.addr&0xff); - w->writeC(write.val); - break; - case 2: // PSG - w->writeC(isSecond?0x30:0x50); - w->writeC(write.val); - break; - } - break; - case DIV_SYSTEM_SMS: - w->writeC(isSecond?0x30:0x50); - w->writeC(write.val); - break; - case DIV_SYSTEM_GB: - w->writeC(0xb3); - w->writeC((isSecond?0x80:0)|((write.addr-16)&0xff)); - w->writeC(write.val); - break; - case DIV_SYSTEM_PCE: - w->writeC(0xb9); - w->writeC((isSecond?0x80:0)|(write.addr&0xff)); - w->writeC(write.val); - break; - case DIV_SYSTEM_NES: - w->writeC(0xb4); - w->writeC((isSecond?0x80:0)|(write.addr&0xff)); - w->writeC(write.val); - break; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_YM2151: - switch (write.addr>>16) { - case 0: // YM2151 - w->writeC(isSecond?0xa4:0x54); - w->writeC(write.addr&0xff); - w->writeC(write.val); - break; - case 1: // SegaPCM - w->writeC(0xc0); - w->writeS((isSecond?0x8000:0)|(write.addr&0xffff)); - w->writeC(write.val); - break; - } - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - switch (write.addr>>8) { - case 0: // port 0 - w->writeC(isSecond?0xa8:0x58); - w->writeC(write.addr&0xff); - w->writeC(write.val); - break; - case 1: // port 1 - w->writeC(isSecond?0xa9:0x59); - w->writeC(write.addr&0xff); - w->writeC(write.val); - break; - } - break; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - w->writeC(0xa0); - w->writeC((isSecond?0x80:0)|(write.addr&0xff)); - w->writeC(write.val); - break; - case DIV_SYSTEM_SAA1099: - w->writeC(0xbd); - w->writeC((isSecond?0x80:0)|(write.addr&0xff)); - w->writeC(write.val); - break; - default: - logW("write not handled!\n"); - break; - } -} - void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { loopOrder=0; loopRow=0; @@ -1863,773 +1494,6 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { } } -SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { - stop(); - setOrder(0); - isBusy.lock(); - double origRate=got.rate; - got.rate=44100; - // determine loop point - int loopOrder=0; - int loopRow=0; - int loopEnd=0; - walkSong(loopOrder,loopRow,loopEnd); - logI("loop point: %d %d\n",loopOrder,loopRow); - warnings=""; - - curOrder=0; - freelance=false; - playing=false; - extValuePresent=false; - remainingLoops=-1; - - // play the song ourselves - bool done=false; - int writeCount=0; - - int gd3Off=0; - - int hasSN=0; - int snNoiseConfig=9; - int snNoiseSize=16; - int snFlags=0; - int hasOPLL=0; - int hasOPN2=0; - int hasOPM=0; - int hasSegaPCM=0; - int segaPCMOffset=0xf8000d; - int hasRFC=0; - int hasOPN=0; - int hasOPNA=0; - int hasOPNB=0; - int hasOPL2=0; - int hasOPL=0; - int hasY8950=0; - int hasOPL3=0; - int hasOPL4=0; - int hasOPX=0; - int hasZ280=0; - int hasRFC1=0; - int hasPWM=0; - int hasAY=0; - int ayConfig=0; - int ayFlags=0; - int hasGB=0; - int hasNES=0; - int hasMultiPCM=0; - int hasuPD7759=0; - int hasOKIM6258=0; - int hasK054539=0; - int hasOKIM6295=0; - int hasK051649=0; - int hasPCE=0; - int hasNamco=0; - int hasK053260=0; - int hasPOKEY=0; - int hasQSound=0; - int hasSCSP=0; - int hasSwan=0; - int hasVSU=0; - int hasSAA=0; - int hasES5503=0; - int hasES5505=0; - int hasX1=0; - int hasC352=0; - int hasGA20=0; - - int howManyChips=0; - - int loopPos=-1; - int loopTick=-1; - - SafeWriter* w=new SafeWriter; - w->init(); - - // write header - w->write("Vgm ",4); - w->writeI(0); // will be written later - w->writeI(0x171); // VGM 1.71 - - bool willExport[32]; - bool isSecond[32]; - int streamIDs[32]; - double loopTimer[DIV_MAX_CHANS]; - double loopFreq[DIV_MAX_CHANS]; - int loopSample[DIV_MAX_CHANS]; - - for (int i=0; ichipClock; - willExport[i]=true; - } else if (!(hasOPN2&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasOPN2|=0x40000000; - howManyChips++; - addWarning("adding a compound system two times is experimental!"); - } - if (!hasSN) { - hasSN=3579545; - willExport[i]=true; - } else if (!(hasSN&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasSN|=0x40000000; - howManyChips++; - addWarning("adding a compound system two times is experimental!"); - } - break; - case DIV_SYSTEM_SMS: - if (!hasSN) { - hasSN=disCont[i].dispatch->chipClock; - willExport[i]=true; - switch ((song.systemFlags[i]>>2)&3) { - case 1: // real SN - snNoiseConfig=3; - snNoiseSize=15; - break; - case 2: // real SN atari bass (seemingly unsupported) - snNoiseConfig=3; - snNoiseSize=15; - break; - default: // Sega VDP - snNoiseConfig=9; - snNoiseSize=16; - break; - } - } else if (!(hasSN&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasSN|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_GB: - if (!hasGB) { - hasGB=disCont[i].dispatch->chipClock; - willExport[i]=true; - } else if (!(hasGB&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasGB|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_PCE: - if (!hasPCE) { - hasPCE=disCont[i].dispatch->chipClock; - willExport[i]=true; - writePCESamples=true; - } else if (!(hasPCE&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasPCE|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_NES: - if (!hasNES) { - hasNES=disCont[i].dispatch->chipClock; - willExport[i]=true; - writeNESSamples=true; - } else if (!(hasNES&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasNES|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_ARCADE: - if (!hasOPM) { - hasOPM=disCont[i].dispatch->chipClock; - willExport[i]=true; - } else if (!(hasOPM&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasOPM|=0x40000000; - howManyChips++; - addWarning("adding a compound system two times is experimental!"); - } - if (!hasSegaPCM) { - hasSegaPCM=4000000; - willExport[i]=true; - writeSegaPCM=true; - } else if (!(hasSegaPCM&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasSegaPCM|=0x40000000; - howManyChips++; - addWarning("adding a compound system two times is experimental!"); - } - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - if (!hasOPNB) { - hasOPNB=disCont[i].dispatch->chipClock; - willExport[i]=true; - writeADPCM=true; - } else if (!(hasOPNB&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasOPNB|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - if (!hasAY) { - hasAY=disCont[i].dispatch->chipClock; - ayConfig=(song.system[i]==DIV_SYSTEM_AY8930)?3:0; - ayFlags=1; - willExport[i]=true; - } else if (!(hasAY&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasAY|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_SAA1099: - if (!hasSAA) { - hasSAA=disCont[i].dispatch->chipClock; - willExport[i]=true; - } else if (!(hasSAA&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasSAA|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_YM2612: - if (!hasOPN2) { - hasOPN2=disCont[i].dispatch->chipClock; - willExport[i]=true; - writeDACSamples=true; - } else if (!(hasOPN2&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasOPN2|=0x40000000; - howManyChips++; - } - break; - case DIV_SYSTEM_YM2151: - if (!hasOPM) { - hasOPM=disCont[i].dispatch->chipClock; - willExport[i]=true; - } else if (!(hasOPM&0x40000000)) { - isSecond[i]=true; - willExport[i]=true; - hasOPM|=0x40000000; - howManyChips++; - } - break; - default: - break; - } - if (willExport[i]) { - disCont[i].dispatch->toggleRegisterDump(true); - } - } - - //bool wantsExtraHeader=false; - /*for (int i=0; iwriteI(hasSN); - w->writeI(hasOPLL); - w->writeI(0); - w->writeI(0); // length. will be written later - w->writeI(0); // loop. will be written later - w->writeI(0); // loop length. why is this necessary? - w->writeI(0); // tick rate - w->writeS(snNoiseConfig); - w->writeC(snNoiseSize); - w->writeC(snFlags); - w->writeI(hasOPN2); - w->writeI(hasOPM); - w->writeI(0); // data pointer. will be written later - w->writeI(hasSegaPCM); - w->writeI(segaPCMOffset); - w->writeI(hasRFC); - w->writeI(hasOPN); - w->writeI(hasOPNA); - w->writeI(hasOPNB); - w->writeI(hasOPL2); - w->writeI(hasOPL); - w->writeI(hasY8950); - w->writeI(hasOPL3); - w->writeI(hasOPL4); - w->writeI(hasOPX); - w->writeI(hasZ280); - w->writeI(hasRFC1); - w->writeI(hasPWM); - w->writeI(hasAY); - w->writeC(ayConfig); - w->writeC(ayFlags); - w->writeC(ayFlags); // OPN - w->writeC(ayFlags); // OPNA - w->writeC(0); // volume - w->writeC(0); // reserved - w->writeC(0); // loop count - w->writeC(0); // loop modifier - w->writeI(hasGB); - w->writeI(hasNES); - w->writeI(hasMultiPCM); - w->writeI(hasuPD7759); - w->writeI(hasOKIM6258); - w->writeC(0); // flags - w->writeC(0); // K flags - w->writeC(0); // C140 chip type - w->writeC(0); // reserved - w->writeI(hasOKIM6295); - w->writeI(hasK051649); - w->writeI(hasK054539); - w->writeI(hasPCE); - w->writeI(hasNamco); - w->writeI(hasK053260); - w->writeI(hasPOKEY); - w->writeI(hasQSound); - w->writeI(hasSCSP); - w->writeI(0); // extra header - w->writeI(hasSwan); - w->writeI(hasVSU); - w->writeI(hasSAA); - w->writeI(hasES5503); - w->writeI(hasES5505); - w->writeC(0); // 5503 chans - w->writeC(0); // 5505 chans - w->writeC(0); // C352 clock divider - w->writeC(0); // reserved - w->writeI(hasX1); - w->writeI(hasC352); - w->writeI(hasGA20); - for (int i=0; i<7; i++) { // reserved - w->writeI(0); - } - - /* TODO - unsigned int exHeaderOff=w->tell(); - if (wantsExtraHeader) { - w->writeI(4); - w->writeI(4); - - // write clocks - w->writeC(howManyChips); - }*/ - - unsigned int songOff=w->tell(); - - // write samples - unsigned int sampleSeek=0; - for (int i=0; irendOffContiguous=sampleSeek; - sampleSeek+=sample->rendLength; - } - - if (writeDACSamples) for (int i=0; iwriteC(0x67); - w->writeC(0x66); - w->writeC(0); - w->writeI(sample->rendLength); - if (sample->depth==8) { - for (unsigned int j=0; jrendLength; j++) { - w->writeC((unsigned char)sample->rendData[j]+0x80); - } - } else { - for (unsigned int j=0; jrendLength; j++) { - w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>8); - } - } - } - - if (writeNESSamples) for (int i=0; iwriteC(0x67); - w->writeC(0x66); - w->writeC(7); - w->writeI(sample->rendLength); - if (sample->depth==8) { - for (unsigned int j=0; jrendLength; j++) { - w->writeC(((unsigned char)sample->rendData[j]+0x80)>>1); - } - } else { - for (unsigned int j=0; jrendLength; j++) { - w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>9); - } - } - } - - if (writePCESamples) for (int i=0; iwriteC(0x67); - w->writeC(0x66); - w->writeC(5); - w->writeI(sample->rendLength); - if (sample->depth==8) { - for (unsigned int j=0; jrendLength; j++) { - w->writeC(((unsigned char)sample->rendData[j]+0x80)>>3); - } - } else { - for (unsigned int j=0; jrendLength; j++) { - w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>11); - } - } - } - - if (writeSegaPCM) { - unsigned char* pcmMem=new unsigned char[16777216]; - - size_t memPos=0; - for (int i=0; irendLength)&0xff0000)) { - memPos=(memPos+0xffff)&0xff0000; - } - if (memPos>=16777216) break; - sample->rendOffP=memPos; - unsigned int alignedSize=(sample->rendLength+0xff)&(~0xff); - unsigned int readPos=0; - if (alignedSize>65536) alignedSize=65536; - if (sample->depth==8) { - for (unsigned int j=0; j=sample->rendLength) { - if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) { - readPos=sample->loopStart; - pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80); - } else { - pcmMem[memPos++]=0x80; - } - } else { - pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80); - } - readPos++; - if (memPos>=16777216) break; - } - sample->loopOffP=readPos-sample->loopStart; - } else { - for (unsigned int j=0; j=sample->rendLength) { - if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) { - readPos=sample->loopStart; - pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8); - } else { - pcmMem[memPos++]=0x80; - } - } else { - pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8); - } - readPos++; - if (memPos>=16777216) break; - } - sample->loopOffP=readPos-sample->loopStart; - } - if (memPos>=16777216) break; - } - - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x80); - w->writeI(memPos+8); - w->writeI(memPos); - w->writeI(0); - w->write(pcmMem,memPos); - - delete[] pcmMem; - } - - if (writeADPCM && adpcmMemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x82); - w->writeI(adpcmMemLen+8); - w->writeI(adpcmMemLen); - w->writeI(0); - w->write(adpcmMem,adpcmMemLen); - } - - // initialize streams - int streamID=0; - for (int i=0; iwriteC(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++) { - 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; - default: - break; - } - } - - // write song data - playSub(false); - size_t tickCount=0; - bool writeLoop=false; - while (!done) { - if (loopPos==-1) { - if (loopOrder==curOrder && loopRow==curRow && ticks==1) { - writeLoop=true; - } - } - if (nextTick()) { - done=true; - if (!loop) { - for (int i=0; igetRegisterWrites().clear(); - } - break; - } - // stop all streams - for (int i=0; iwriteC(0x94); - w->writeC(i); - loopSample[i]=-1; - } - } - // get register dumps - for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); - for (DivRegWrite& j: writes) { - performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,isSecond[i]); - writeCount++; - } - writes.clear(); - } - // check whether we need to loop - int totalWait=cycles>>MASTER_CLOCK_PREC; - for (int i=0; i=0) { - loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait; - } - } - bool haveNegatives=false; - for (int i=0; i=0) { - if (loopTimer[i]<0) { - haveNegatives=true; - } - } - } - while (haveNegatives) { - // finish all negatives - int nextToTouch=-1; - for (int i=0; i=0) { - if (loopTimer[i]<0) { - if (nextToTouch>=0) { - if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i; - } else { - nextToTouch=i; - } - } - } - } - if (nextToTouch>=0) { - double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch]))); - if (waitTime>0) { - w->writeC(0x61); - w->writeS(waitTime); - printf("wait is: %f\n",waitTime); - totalWait-=waitTime; - tickCount+=waitTime; - } - if (loopSample[nextToTouch]loopStart<(int)sample->rendLength) { - w->writeC(0x93); - w->writeC(nextToTouch); - w->writeI(sample->rendOffContiguous+sample->loopStart); - w->writeC(0x81); - w->writeI(sample->rendLength-sample->loopStart); - } - } - loopSample[nextToTouch]=-1; - } else { - haveNegatives=false; - } - } - // write wait - if (totalWait>0) { - if (totalWait==735) { - w->writeC(0x62); - } else if (totalWait==882) { - w->writeC(0x63); - } else { - w->writeC(0x61); - w->writeS(totalWait); - } - tickCount+=totalWait; - } - if (writeLoop) { - writeLoop=false; - loopPos=w->tell(); - loopTick=tickCount; - } - } - // end of song - w->writeC(0x66); - - got.rate=origRate; - - for (int i=0; itoggleRegisterDump(false); - } - - // write GD3 tag - gd3Off=w->tell(); - w->write("Gd3 ",4); - w->writeI(0x100); - w->writeI(0); // length. will be written later - - WString ws; - ws=utf8To16(song.name.c_str()); - w->writeWString(ws,false); // name - w->writeS(0); // japanese name - w->writeS(0); // game name - w->writeS(0); // japanese game name - if (song.systemLen>1) { - ws=L"Multiple Systems"; - } else { - ws=utf8To16(getSystemName(song.system[0])); - } - w->writeWString(ws,false); // system name - if (song.systemLen>1) { - ws=L"複数システム"; - } else { - ws=utf8To16(getSystemNameJ(song.system[0])); - } - w->writeWString(ws,false); // japanese system name - ws=utf8To16(song.author.c_str()); - w->writeWString(ws,false); // author name - w->writeS(0); // japanese author name - w->writeS(0); // date - w->writeWString(L"Furnace Tracker",false); // ripper - w->writeS(0); // notes - - int gd3Len=w->tell()-gd3Off-12; - - w->seek(gd3Off+8,SEEK_SET); - w->writeI(gd3Len); - - // finish file - size_t len=w->size()-4; - w->seek(4,SEEK_SET); - w->writeI(len); - w->seek(0x14,SEEK_SET); - w->writeI(gd3Off-0x14); - w->writeI(tickCount); - if (loop) { - w->writeI(loopPos-0x1c); - w->writeI(tickCount-loopTick-1); - } else { - w->writeI(0); - w->writeI(0); - } - w->seek(0x34,SEEK_SET); - w->writeI(songOff-0x34); - /*if (wantsExtraHeader) { - w->seek(0xbc,SEEK_SET); - w->writeI(exHeaderOff-0xbc); - }*/ - - remainingLoops=-1; - playing=false; - freelance=false; - extValuePresent=false; - - logI("%d register writes total.\n",writeCount); - - isBusy.unlock(); - return w; -} - void _runExportThread(DivEngine* caller) { caller->runExportThread(); } diff --git a/src/engine/engine.h b/src/engine/engine.h index 46ccf9536..25e7c306d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -30,6 +30,13 @@ #include #include +#define addWarning(x) \ + if (warnings.empty()) { \ + warnings+=x; \ + } else { \ + warnings+=(String("\n")+x); \ + } + #define DIV_VERSION "0.5.7pre2" #define DIV_ENGINE_VERSION 50 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 2e79e1f07..5d3ea5330 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -41,13 +41,6 @@ struct InflateBlock { } }; -#define addWarning(x) \ - if (warnings.empty()) { \ - warnings+=x; \ - } else { \ - warnings+=(String("\n")+x); \ - } - bool DivEngine::loadDMF(unsigned char* file, size_t len) { SafeReader reader=SafeReader(file,len); warnings=""; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp new file mode 100644 index 000000000..84c673e57 --- /dev/null +++ b/src/engine/vgmOps.cpp @@ -0,0 +1,1151 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "engine.h" +#include "../ta-log.h" +#include "../utfutils.h" + +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 isSecond) { + if (write.addr==0xffffffff) { // Furnace fake reset + switch (sys) { + case DIV_SYSTEM_GENESIS: + case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_YM2612: + for (int i=0; i<3; i++) { // set SL and RR to highest + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x80+i); + w->writeC(0xff); + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x84+i); + w->writeC(0xff); + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x88+i); + w->writeC(0xff); + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x8c+i); + w->writeC(0xff); + + w->writeC(isSecond?0xa3:0x53); + w->writeC(0x80+i); + w->writeC(0xff); + w->writeC(isSecond?0xa3:0x53); + w->writeC(0x84+i); + w->writeC(0xff); + w->writeC(isSecond?0xa3:0x53); + w->writeC(0x88+i); + w->writeC(0xff); + w->writeC(isSecond?0xa3:0x53); + w->writeC(0x8c+i); + w->writeC(0xff); + } + for (int i=0; i<3; i++) { // note off + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x28); + w->writeC(i); + w->writeC(isSecond?0xa2:0x52); + w->writeC(0x28); + w->writeC(4+i); + } + w->writeC(isSecond?0xa2:0x52); // disable DAC + w->writeC(0x2b); + w->writeC(0); + if (sys!=DIV_SYSTEM_YM2612) { + for (int i=0; i<4; i++) { + w->writeC(isSecond?0x30:0x50); + w->writeC(0x90|(i<<5)|15); + } + } + break; + case DIV_SYSTEM_SMS: + for (int i=0; i<4; i++) { + w->writeC(isSecond?0x30:0x50); + w->writeC(0x90|(i<<5)|15); + } + break; + case DIV_SYSTEM_GB: + // square 1 + w->writeC(0xb3); + w->writeC(isSecond?0x82:2); + w->writeC(0); + w->writeC(0xb3); + w->writeC(isSecond?0x84:4); + w->writeC(0x80); + + // square 2 + w->writeC(0xb3); + w->writeC(isSecond?0x87:7); + w->writeC(0); + w->writeC(0xb3); + w->writeC(isSecond?0x89:9); + w->writeC(0x80); + + // wave + w->writeC(0xb3); + w->writeC(isSecond?0x8c:0x0c); + w->writeC(0); + w->writeC(0xb3); + w->writeC(isSecond?0x8e:0x0e); + w->writeC(0x80); + + // noise + w->writeC(0xb3); + w->writeC(isSecond?0x91:0x11); + w->writeC(0); + w->writeC(0xb3); + w->writeC(isSecond?0x93:0x13); + w->writeC(0x80); + break; + case DIV_SYSTEM_PCE: + for (int i=0; i<6; i++) { + w->writeC(0xb9); + w->writeC(isSecond?0x80:0); + w->writeC(i); + w->writeC(0xb9); + w->writeC(isSecond?0x84:4); + w->writeC(0); + } + break; + case DIV_SYSTEM_NES: + w->writeC(0xb4); + w->writeC(isSecond?0x95:0x15); + w->writeC(0); + break; + case DIV_SYSTEM_ARCADE: + case DIV_SYSTEM_YM2151: + for (int i=0; i<8; i++) { + w->writeC(isSecond?0xa4:0x54); + w->writeC(0xe0+i); + w->writeC(0xff); + w->writeC(isSecond?0xa4:0x54); + w->writeC(0xe8+i); + w->writeC(0xff); + w->writeC(isSecond?0xa4:0x54); + w->writeC(0xf0+i); + w->writeC(0xff); + w->writeC(isSecond?0xa4:0x54); + w->writeC(0xf8+i); + w->writeC(0xff); + + w->writeC(isSecond?0xa4:0x54); + w->writeC(0x08); + w->writeC(i); + } + if (sys==DIV_SYSTEM_ARCADE) { + for (int i=0; i<5; i++) { + w->writeC(0xc0); + w->writeS((isSecond?0x8086:0x86)+(i<<3)); + w->writeC(3); + } + } + break; + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL_EXT: + for (int i=0; i<2; i++) { // set SL and RR to highest + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x81+i); + w->writeC(0xff); + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x85+i); + w->writeC(0xff); + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x89+i); + w->writeC(0xff); + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x8d+i); + w->writeC(0xff); + + w->writeC(isSecond?0xa9:0x59); + w->writeC(0x81+i); + w->writeC(0xff); + w->writeC(isSecond?0xa9:0x59); + w->writeC(0x85+i); + w->writeC(0xff); + w->writeC(isSecond?0xa9:0x59); + w->writeC(0x89+i); + w->writeC(0xff); + w->writeC(isSecond?0xa9:0x59); + w->writeC(0x8d+i); + w->writeC(0xff); + } + for (int i=0; i<2; i++) { // note off + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x28); + w->writeC(1+i); + w->writeC(isSecond?0xa8:0x58); + w->writeC(0x28); + w->writeC(5+i); + } + + // reset AY + w->writeC(isSecond?0xa8:0x58); + w->writeC(7); + w->writeC(0x3f); + + w->writeC(isSecond?0xa8:0x58); + w->writeC(8); + w->writeC(0); + + w->writeC(isSecond?0xa8:0x58); + w->writeC(9); + w->writeC(0); + + w->writeC(isSecond?0xa8:0x58); + w->writeC(10); + w->writeC(0); + + // reset sample + w->writeC(isSecond?0xa9:0x59); + w->writeC(0); + w->writeC(0xbf); + break; + case DIV_SYSTEM_AY8910: + w->writeC(0xa0); + w->writeC(isSecond?0x87:7); + w->writeC(0x3f); + + w->writeC(0xa0); + w->writeC(isSecond?0x88:8); + w->writeC(0); + + w->writeC(0xa0); + w->writeC(isSecond?0x89:9); + w->writeC(0); + + w->writeC(0xa0); + w->writeC(isSecond?0x8a:10); + w->writeC(0); + break; + case DIV_SYSTEM_AY8930: + w->writeC(0xa0); + w->writeC(isSecond?0x8d:0x0d); + w->writeC(0); + w->writeC(0xa0); + w->writeC(isSecond?0x8d:0x0d); + w->writeC(0xa0); + break; + case DIV_SYSTEM_SAA1099: + w->writeC(0xbd); + w->writeC(isSecond?0x9c:0x1c); + w->writeC(0x02); + w->writeC(0xbd); + w->writeC(isSecond?0x94:0x14); + w->writeC(0); + w->writeC(0xbd); + w->writeC(isSecond?0x95:0x15); + w->writeC(0); + + for (int i=0; i<6; i++) { + w->writeC(0xbd); + w->writeC((isSecond?0x80:0)+i); + w->writeC(0); + } + break; + default: + break; + } + } + if (write.addr>=0xffff0000) { // Furnace special command + unsigned char streamID=streamOff+((write.addr&0xff00)>>8); + switch (write.addr&0xff) { + case 0: // play sample + if (write.valwriteC(0x95); + w->writeC(streamID); + w->writeS(write.val); // sample number + w->writeC((sample->loopStart==0)); // flags + if (sample->loopStart>0) { + loopTimer[streamID]=sample->rendLength; + 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; + } + return; + } + switch (sys) { + case DIV_SYSTEM_GENESIS: + case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_YM2612: + switch (write.addr>>8) { + case 0: // port 0 + w->writeC(isSecond?0xa2:0x52); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; + case 1: // port 1 + w->writeC(isSecond?0xa3:0x53); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; + case 2: // PSG + w->writeC(isSecond?0x30:0x50); + w->writeC(write.val); + break; + } + break; + case DIV_SYSTEM_SMS: + w->writeC(isSecond?0x30:0x50); + w->writeC(write.val); + break; + case DIV_SYSTEM_GB: + w->writeC(0xb3); + w->writeC((isSecond?0x80:0)|((write.addr-16)&0xff)); + w->writeC(write.val); + break; + case DIV_SYSTEM_PCE: + w->writeC(0xb9); + w->writeC((isSecond?0x80:0)|(write.addr&0xff)); + w->writeC(write.val); + break; + case DIV_SYSTEM_NES: + w->writeC(0xb4); + w->writeC((isSecond?0x80:0)|(write.addr&0xff)); + w->writeC(write.val); + break; + case DIV_SYSTEM_ARCADE: + case DIV_SYSTEM_YM2151: + switch (write.addr>>16) { + case 0: // YM2151 + w->writeC(isSecond?0xa4:0x54); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; + case 1: // SegaPCM + w->writeC(0xc0); + w->writeS((isSecond?0x8000:0)|(write.addr&0xffff)); + w->writeC(write.val); + break; + } + break; + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL_EXT: + switch (write.addr>>8) { + case 0: // port 0 + w->writeC(isSecond?0xa8:0x58); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; + case 1: // port 1 + w->writeC(isSecond?0xa9:0x59); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; + } + break; + case DIV_SYSTEM_AY8910: + case DIV_SYSTEM_AY8930: + w->writeC(0xa0); + w->writeC((isSecond?0x80:0)|(write.addr&0xff)); + w->writeC(write.val); + break; + case DIV_SYSTEM_SAA1099: + w->writeC(0xbd); + w->writeC((isSecond?0x80:0)|(write.addr&0xff)); + w->writeC(write.val); + break; + default: + logW("write not handled!\n"); + break; + } +} + +SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { + stop(); + setOrder(0); + isBusy.lock(); + double origRate=got.rate; + got.rate=44100; + // determine loop point + int loopOrder=0; + int loopRow=0; + int loopEnd=0; + walkSong(loopOrder,loopRow,loopEnd); + logI("loop point: %d %d\n",loopOrder,loopRow); + warnings=""; + + curOrder=0; + freelance=false; + playing=false; + extValuePresent=false; + remainingLoops=-1; + + // play the song ourselves + bool done=false; + int writeCount=0; + + int gd3Off=0; + + int hasSN=0; + int snNoiseConfig=9; + int snNoiseSize=16; + int snFlags=0; + int hasOPLL=0; + int hasOPN2=0; + int hasOPM=0; + int hasSegaPCM=0; + int segaPCMOffset=0xf8000d; + int hasRFC=0; + int hasOPN=0; + int hasOPNA=0; + int hasOPNB=0; + int hasOPL2=0; + int hasOPL=0; + int hasY8950=0; + int hasOPL3=0; + int hasOPL4=0; + int hasOPX=0; + int hasZ280=0; + int hasRFC1=0; + int hasPWM=0; + int hasAY=0; + int ayConfig=0; + int ayFlags=0; + int hasGB=0; + int hasNES=0; + int hasMultiPCM=0; + int hasuPD7759=0; + int hasOKIM6258=0; + int hasK054539=0; + int hasOKIM6295=0; + int hasK051649=0; + int hasPCE=0; + int hasNamco=0; + int hasK053260=0; + int hasPOKEY=0; + int hasQSound=0; + int hasSCSP=0; + int hasSwan=0; + int hasVSU=0; + int hasSAA=0; + int hasES5503=0; + int hasES5505=0; + int hasX1=0; + int hasC352=0; + int hasGA20=0; + + int howManyChips=0; + + int loopPos=-1; + int loopTick=-1; + + SafeWriter* w=new SafeWriter; + w->init(); + + // write header + w->write("Vgm ",4); + w->writeI(0); // will be written later + w->writeI(0x171); // VGM 1.71 + + bool willExport[32]; + bool isSecond[32]; + int streamIDs[32]; + double loopTimer[DIV_MAX_CHANS]; + double loopFreq[DIV_MAX_CHANS]; + int loopSample[DIV_MAX_CHANS]; + + for (int i=0; ichipClock; + willExport[i]=true; + } else if (!(hasOPN2&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPN2|=0x40000000; + howManyChips++; + addWarning("adding a compound system two times is experimental!"); + } + if (!hasSN) { + hasSN=3579545; + willExport[i]=true; + } else if (!(hasSN&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasSN|=0x40000000; + howManyChips++; + addWarning("adding a compound system two times is experimental!"); + } + break; + case DIV_SYSTEM_SMS: + if (!hasSN) { + hasSN=disCont[i].dispatch->chipClock; + willExport[i]=true; + switch ((song.systemFlags[i]>>2)&3) { + case 1: // real SN + snNoiseConfig=3; + snNoiseSize=15; + break; + case 2: // real SN atari bass (seemingly unsupported) + snNoiseConfig=3; + snNoiseSize=15; + break; + default: // Sega VDP + snNoiseConfig=9; + snNoiseSize=16; + break; + } + } else if (!(hasSN&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasSN|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_GB: + if (!hasGB) { + hasGB=disCont[i].dispatch->chipClock; + willExport[i]=true; + } else if (!(hasGB&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasGB|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_PCE: + if (!hasPCE) { + hasPCE=disCont[i].dispatch->chipClock; + willExport[i]=true; + writePCESamples=true; + } else if (!(hasPCE&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasPCE|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_NES: + if (!hasNES) { + hasNES=disCont[i].dispatch->chipClock; + willExport[i]=true; + writeNESSamples=true; + } else if (!(hasNES&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasNES|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_ARCADE: + if (!hasOPM) { + hasOPM=disCont[i].dispatch->chipClock; + willExport[i]=true; + } else if (!(hasOPM&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPM|=0x40000000; + howManyChips++; + addWarning("adding a compound system two times is experimental!"); + } + if (!hasSegaPCM) { + hasSegaPCM=4000000; + willExport[i]=true; + writeSegaPCM=true; + } else if (!(hasSegaPCM&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasSegaPCM|=0x40000000; + howManyChips++; + addWarning("adding a compound system two times is experimental!"); + } + break; + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL_EXT: + if (!hasOPNB) { + hasOPNB=disCont[i].dispatch->chipClock; + willExport[i]=true; + writeADPCM=true; + } else if (!(hasOPNB&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPNB|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_AY8910: + case DIV_SYSTEM_AY8930: + if (!hasAY) { + hasAY=disCont[i].dispatch->chipClock; + ayConfig=(song.system[i]==DIV_SYSTEM_AY8930)?3:0; + ayFlags=1; + willExport[i]=true; + } else if (!(hasAY&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasAY|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_SAA1099: + if (!hasSAA) { + hasSAA=disCont[i].dispatch->chipClock; + willExport[i]=true; + } else if (!(hasSAA&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasSAA|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_YM2612: + if (!hasOPN2) { + hasOPN2=disCont[i].dispatch->chipClock; + willExport[i]=true; + writeDACSamples=true; + } else if (!(hasOPN2&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPN2|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_YM2151: + if (!hasOPM) { + hasOPM=disCont[i].dispatch->chipClock; + willExport[i]=true; + } else if (!(hasOPM&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPM|=0x40000000; + howManyChips++; + } + break; + default: + break; + } + if (willExport[i]) { + disCont[i].dispatch->toggleRegisterDump(true); + } + } + + //bool wantsExtraHeader=false; + /*for (int i=0; iwriteI(hasSN); + w->writeI(hasOPLL); + w->writeI(0); + w->writeI(0); // length. will be written later + w->writeI(0); // loop. will be written later + w->writeI(0); // loop length. why is this necessary? + w->writeI(0); // tick rate + w->writeS(snNoiseConfig); + w->writeC(snNoiseSize); + w->writeC(snFlags); + w->writeI(hasOPN2); + w->writeI(hasOPM); + w->writeI(0); // data pointer. will be written later + w->writeI(hasSegaPCM); + w->writeI(segaPCMOffset); + w->writeI(hasRFC); + w->writeI(hasOPN); + w->writeI(hasOPNA); + w->writeI(hasOPNB); + w->writeI(hasOPL2); + w->writeI(hasOPL); + w->writeI(hasY8950); + w->writeI(hasOPL3); + w->writeI(hasOPL4); + w->writeI(hasOPX); + w->writeI(hasZ280); + w->writeI(hasRFC1); + w->writeI(hasPWM); + w->writeI(hasAY); + w->writeC(ayConfig); + w->writeC(ayFlags); + w->writeC(ayFlags); // OPN + w->writeC(ayFlags); // OPNA + w->writeC(0); // volume + w->writeC(0); // reserved + w->writeC(0); // loop count + w->writeC(0); // loop modifier + w->writeI(hasGB); + w->writeI(hasNES); + w->writeI(hasMultiPCM); + w->writeI(hasuPD7759); + w->writeI(hasOKIM6258); + w->writeC(0); // flags + w->writeC(0); // K flags + w->writeC(0); // C140 chip type + w->writeC(0); // reserved + w->writeI(hasOKIM6295); + w->writeI(hasK051649); + w->writeI(hasK054539); + w->writeI(hasPCE); + w->writeI(hasNamco); + w->writeI(hasK053260); + w->writeI(hasPOKEY); + w->writeI(hasQSound); + w->writeI(hasSCSP); + w->writeI(0); // extra header + w->writeI(hasSwan); + w->writeI(hasVSU); + w->writeI(hasSAA); + w->writeI(hasES5503); + w->writeI(hasES5505); + w->writeC(0); // 5503 chans + w->writeC(0); // 5505 chans + w->writeC(0); // C352 clock divider + w->writeC(0); // reserved + w->writeI(hasX1); + w->writeI(hasC352); + w->writeI(hasGA20); + for (int i=0; i<7; i++) { // reserved + w->writeI(0); + } + + /* TODO + unsigned int exHeaderOff=w->tell(); + if (wantsExtraHeader) { + w->writeI(4); + w->writeI(4); + + // write clocks + w->writeC(howManyChips); + }*/ + + unsigned int songOff=w->tell(); + + // write samples + unsigned int sampleSeek=0; + for (int i=0; irendOffContiguous=sampleSeek; + sampleSeek+=sample->rendLength; + } + + if (writeDACSamples) for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0); + w->writeI(sample->rendLength); + if (sample->depth==8) { + for (unsigned int j=0; jrendLength; j++) { + w->writeC((unsigned char)sample->rendData[j]+0x80); + } + } else { + for (unsigned int j=0; jrendLength; j++) { + w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>8); + } + } + } + + if (writeNESSamples) for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(7); + w->writeI(sample->rendLength); + if (sample->depth==8) { + for (unsigned int j=0; jrendLength; j++) { + w->writeC(((unsigned char)sample->rendData[j]+0x80)>>1); + } + } else { + for (unsigned int j=0; jrendLength; j++) { + w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>9); + } + } + } + + if (writePCESamples) for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(5); + w->writeI(sample->rendLength); + if (sample->depth==8) { + for (unsigned int j=0; jrendLength; j++) { + w->writeC(((unsigned char)sample->rendData[j]+0x80)>>3); + } + } else { + for (unsigned int j=0; jrendLength; j++) { + w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>11); + } + } + } + + if (writeSegaPCM) { + unsigned char* pcmMem=new unsigned char[16777216]; + + size_t memPos=0; + for (int i=0; irendLength)&0xff0000)) { + memPos=(memPos+0xffff)&0xff0000; + } + if (memPos>=16777216) break; + sample->rendOffP=memPos; + unsigned int alignedSize=(sample->rendLength+0xff)&(~0xff); + unsigned int readPos=0; + if (alignedSize>65536) alignedSize=65536; + if (sample->depth==8) { + for (unsigned int j=0; j=sample->rendLength) { + if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) { + readPos=sample->loopStart; + pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80); + } else { + pcmMem[memPos++]=0x80; + } + } else { + pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80); + } + readPos++; + if (memPos>=16777216) break; + } + sample->loopOffP=readPos-sample->loopStart; + } else { + for (unsigned int j=0; j=sample->rendLength) { + if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) { + readPos=sample->loopStart; + pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8); + } else { + pcmMem[memPos++]=0x80; + } + } else { + pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8); + } + readPos++; + if (memPos>=16777216) break; + } + sample->loopOffP=readPos-sample->loopStart; + } + if (memPos>=16777216) break; + } + + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0x80); + w->writeI(memPos+8); + w->writeI(memPos); + w->writeI(0); + w->write(pcmMem,memPos); + + delete[] pcmMem; + } + + if (writeADPCM && adpcmMemLen>0) { + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0x82); + w->writeI(adpcmMemLen+8); + w->writeI(adpcmMemLen); + w->writeI(0); + w->write(adpcmMem,adpcmMemLen); + } + + // initialize streams + int streamID=0; + for (int i=0; iwriteC(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++) { + 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; + default: + break; + } + } + + // write song data + playSub(false); + size_t tickCount=0; + bool writeLoop=false; + while (!done) { + if (loopPos==-1) { + if (loopOrder==curOrder && loopRow==curRow && ticks==1) { + writeLoop=true; + } + } + if (nextTick()) { + done=true; + if (!loop) { + for (int i=0; igetRegisterWrites().clear(); + } + break; + } + // stop all streams + for (int i=0; iwriteC(0x94); + w->writeC(i); + loopSample[i]=-1; + } + } + // get register dumps + for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); + for (DivRegWrite& j: writes) { + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,isSecond[i]); + writeCount++; + } + writes.clear(); + } + // check whether we need to loop + int totalWait=cycles>>MASTER_CLOCK_PREC; + for (int i=0; i=0) { + loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait; + } + } + bool haveNegatives=false; + for (int i=0; i=0) { + if (loopTimer[i]<0) { + haveNegatives=true; + } + } + } + while (haveNegatives) { + // finish all negatives + int nextToTouch=-1; + for (int i=0; i=0) { + if (loopTimer[i]<0) { + if (nextToTouch>=0) { + if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i; + } else { + nextToTouch=i; + } + } + } + } + if (nextToTouch>=0) { + double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch]))); + if (waitTime>0) { + w->writeC(0x61); + w->writeS(waitTime); + printf("wait is: %f\n",waitTime); + totalWait-=waitTime; + tickCount+=waitTime; + } + if (loopSample[nextToTouch]loopStart<(int)sample->rendLength) { + w->writeC(0x93); + w->writeC(nextToTouch); + w->writeI(sample->rendOffContiguous+sample->loopStart); + w->writeC(0x81); + w->writeI(sample->rendLength-sample->loopStart); + } + } + loopSample[nextToTouch]=-1; + } else { + haveNegatives=false; + } + } + // write wait + if (totalWait>0) { + if (totalWait==735) { + w->writeC(0x62); + } else if (totalWait==882) { + w->writeC(0x63); + } else { + w->writeC(0x61); + w->writeS(totalWait); + } + tickCount+=totalWait; + } + if (writeLoop) { + writeLoop=false; + loopPos=w->tell(); + loopTick=tickCount; + } + } + // end of song + w->writeC(0x66); + + got.rate=origRate; + + for (int i=0; itoggleRegisterDump(false); + } + + // write GD3 tag + gd3Off=w->tell(); + w->write("Gd3 ",4); + w->writeI(0x100); + w->writeI(0); // length. will be written later + + WString ws; + ws=utf8To16(song.name.c_str()); + w->writeWString(ws,false); // name + w->writeS(0); // japanese name + w->writeS(0); // game name + w->writeS(0); // japanese game name + if (song.systemLen>1) { + ws=L"Multiple Systems"; + } else { + ws=utf8To16(getSystemName(song.system[0])); + } + w->writeWString(ws,false); // system name + if (song.systemLen>1) { + ws=L"複数システム"; + } else { + ws=utf8To16(getSystemNameJ(song.system[0])); + } + w->writeWString(ws,false); // japanese system name + ws=utf8To16(song.author.c_str()); + w->writeWString(ws,false); // author name + w->writeS(0); // japanese author name + w->writeS(0); // date + w->writeWString(L"Furnace Tracker",false); // ripper + w->writeS(0); // notes + + int gd3Len=w->tell()-gd3Off-12; + + w->seek(gd3Off+8,SEEK_SET); + w->writeI(gd3Len); + + // finish file + size_t len=w->size()-4; + w->seek(4,SEEK_SET); + w->writeI(len); + w->seek(0x14,SEEK_SET); + w->writeI(gd3Off-0x14); + w->writeI(tickCount); + if (loop) { + w->writeI(loopPos-0x1c); + w->writeI(tickCount-loopTick-1); + } else { + w->writeI(0); + w->writeI(0); + } + w->seek(0x34,SEEK_SET); + w->writeI(songOff-0x34); + /*if (wantsExtraHeader) { + w->seek(0xbc,SEEK_SET); + w->writeI(exHeaderOff-0xbc); + }*/ + + remainingLoops=-1; + playing=false; + freelance=false; + extValuePresent=false; + + logI("%d register writes total.\n",writeCount); + + isBusy.unlock(); + return w; +}