VGM export: prepare for "direct stream mode"
this will eventually allow for DualPCM and MSM6258 export DO NOT USE YET
This commit is contained in:
parent
c3e2e902af
commit
684b5a928b
|
@ -324,7 +324,7 @@ class DivDispatch {
|
||||||
* @param rate stream rate (e.g. 44100 for VGM).
|
* @param rate stream rate (e.g. 44100 for VGM).
|
||||||
* @param len number of samples.
|
* @param len number of samples.
|
||||||
*/
|
*/
|
||||||
virtual void fillStream(std::vector<DivDelayedWrite>& stream, int rate, size_t len);
|
virtual void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send a command to this dispatch.
|
* send a command to this dispatch.
|
||||||
|
|
|
@ -431,7 +431,7 @@ class DivEngine {
|
||||||
void processRow(int i, bool afterDelay);
|
void processRow(int i, bool afterDelay);
|
||||||
void nextOrder();
|
void nextOrder();
|
||||||
void nextRow();
|
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.
|
// returns true if end of song.
|
||||||
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
||||||
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
||||||
|
@ -515,7 +515,7 @@ class DivEngine {
|
||||||
// specify system to build ROM for.
|
// specify system to build ROM for.
|
||||||
SafeWriter* buildROM(int sys);
|
SafeWriter* buildROM(int sys);
|
||||||
// dump to VGM.
|
// 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.
|
// dump to ZSM.
|
||||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
||||||
// dump command stream.
|
// dump command stream.
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivDispatch::fillStream(std::vector<DivDelayedWrite>& stream, int rate, size_t len) {
|
void DivDispatch::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivDispatch::tick(bool sysTick) {
|
void DivDispatch::tick(bool sysTick) {
|
||||||
|
|
|
@ -1553,7 +1553,7 @@ void DivEngine::registerSystems() {
|
||||||
);
|
);
|
||||||
|
|
||||||
sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef(
|
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.",
|
"an ADPCM sound chip manufactured by OKI and used in the Sharp X68000.",
|
||||||
{"Sample"},
|
{"Sample"},
|
||||||
{"PCM"},
|
{"PCM"},
|
||||||
|
@ -1632,7 +1632,7 @@ void DivEngine::registerSystems() {
|
||||||
);
|
);
|
||||||
|
|
||||||
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
|
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.",
|
"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"},
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2"},
|
||||||
{"F1", "F2", "F3", "F4", "F5", "P1", "P2"},
|
{"F1", "F2", "F3", "F4", "F5", "P1", "P2"},
|
||||||
|
@ -1644,7 +1644,7 @@ void DivEngine::registerSystems() {
|
||||||
);
|
);
|
||||||
|
|
||||||
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
|
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.",
|
"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"},
|
{"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"},
|
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "P1", "P2", "CSM"},
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
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 baseAddr1=isSecond?0xa0:0x50;
|
||||||
unsigned char baseAddr2=isSecond?0x80:0;
|
unsigned char baseAddr2=isSecond?0x80:0;
|
||||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||||
|
@ -515,7 +515,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (write.addr>=0xffff0000) { // Furnace special command
|
if (write.addr>=0xffff0000 && !directStream) { // Furnace special command
|
||||||
unsigned char streamID=streamOff+((write.addr&0xff00)>>8);
|
unsigned char streamID=streamOff+((write.addr&0xff00)>>8);
|
||||||
logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID);
|
logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID);
|
||||||
switch (write.addr&0xff) {
|
switch (write.addr&0xff) {
|
||||||
|
@ -843,7 +843,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \
|
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) {
|
if (version<0x150) {
|
||||||
lastError="VGM version is too low";
|
lastError="VGM version is too low";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -947,6 +947,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
int loopSample[DIV_MAX_CHANS];
|
int loopSample[DIV_MAX_CHANS];
|
||||||
bool sampleDir[DIV_MAX_CHANS];
|
bool sampleDir[DIV_MAX_CHANS];
|
||||||
std::vector<unsigned int> chipVol;
|
std::vector<unsigned int> chipVol;
|
||||||
|
std::vector<DivDelayedWrite> delayedWrites[32];
|
||||||
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
loopTimer[i]=0;
|
loopTimer[i]=0;
|
||||||
|
@ -1585,7 +1586,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
sampleSeek+=sample->length8;
|
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];
|
DivSample* sample=song.sample[i];
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
|
@ -1596,7 +1597,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];
|
DivSample* sample=song.sample[i];
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
|
@ -1607,7 +1608,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];
|
DivSample* sample=song.sample[i];
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
|
@ -1785,87 +1786,89 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
|
|
||||||
// initialize streams
|
// initialize streams
|
||||||
int streamID=0;
|
int streamID=0;
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
if (!directStream) {
|
||||||
if (!willExport[i]) continue;
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
streamIDs[i]=streamID;
|
if (!willExport[i]) continue;
|
||||||
switch (song.system[i]) {
|
streamIDs[i]=streamID;
|
||||||
case DIV_SYSTEM_YM2612:
|
switch (song.system[i]) {
|
||||||
case DIV_SYSTEM_YM2612_EXT:
|
case DIV_SYSTEM_YM2612:
|
||||||
w->writeC(0x90);
|
case DIV_SYSTEM_YM2612_EXT:
|
||||||
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(0x90);
|
||||||
w->writeC(streamID);
|
w->writeC(streamID);
|
||||||
w->writeC(27);
|
w->writeC(0x02);
|
||||||
w->writeC(j); // port
|
w->writeC(0); // port
|
||||||
w->writeC(0x06); // select+DAC
|
w->writeC(0x2a); // DAC
|
||||||
|
|
||||||
w->writeC(0x91);
|
w->writeC(0x91);
|
||||||
w->writeC(streamID);
|
w->writeC(streamID);
|
||||||
w->writeC(5);
|
w->writeC(0);
|
||||||
w->writeC(1);
|
w->writeC(1);
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
|
|
||||||
w->writeC(0x92);
|
w->writeC(0x92);
|
||||||
w->writeC(streamID);
|
w->writeC(streamID);
|
||||||
w->writeI(16000); // default
|
w->writeI(32000); // default
|
||||||
streamID++;
|
streamID++;
|
||||||
}
|
break;
|
||||||
break;
|
case DIV_SYSTEM_NES:
|
||||||
case DIV_SYSTEM_SWAN:
|
w->writeC(0x90);
|
||||||
w->writeC(0x90);
|
w->writeC(streamID);
|
||||||
w->writeC(streamID);
|
w->writeC(20);
|
||||||
w->writeC(isSecond[i]?0xa1:0x21);
|
w->writeC(0); // port
|
||||||
w->writeC(0); // port
|
w->writeC(0x11); // DAC
|
||||||
w->writeC(0x09); // DAC
|
|
||||||
|
|
||||||
w->writeC(0x91);
|
w->writeC(0x91);
|
||||||
w->writeC(streamID);
|
w->writeC(streamID);
|
||||||
w->writeC(0);
|
w->writeC(7);
|
||||||
w->writeC(1);
|
w->writeC(1);
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
|
|
||||||
w->writeC(0x92);
|
w->writeC(0x92);
|
||||||
w->writeC(streamID);
|
w->writeC(streamID);
|
||||||
w->writeI(24000); // default
|
w->writeI(32000); // default
|
||||||
streamID++;
|
streamID++;
|
||||||
break;
|
break;
|
||||||
default:
|
case DIV_SYSTEM_PCE:
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1894,10 +1897,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// stop all streams
|
// stop all streams
|
||||||
for (int i=0; i<streamID; i++) {
|
if (!directStream) {
|
||||||
w->writeC(0x94);
|
for (int i=0; i<streamID; i++) {
|
||||||
w->writeC(i);
|
w->writeC(0x94);
|
||||||
loopSample[i]=-1;
|
w->writeC(i);
|
||||||
|
loopSample[i]=-1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!playing) {
|
if (!playing) {
|
||||||
|
@ -1929,63 +1934,69 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
||||||
for (DivRegWrite& j: writes) {
|
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++;
|
writeCount++;
|
||||||
}
|
}
|
||||||
writes.clear();
|
writes.clear();
|
||||||
}
|
}
|
||||||
// check whether we need to loop
|
// check whether we need to loop
|
||||||
int totalWait=cycles>>MASTER_CLOCK_PREC;
|
int totalWait=cycles>>MASTER_CLOCK_PREC;
|
||||||
for (int i=0; i<streamID; i++) {
|
if (directStream) {
|
||||||
if (loopSample[i]>=0) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait;
|
disCont[i].dispatch->fillStream(delayedWrites[i],44100,totalWait);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
bool haveNegatives=false;
|
for (int i=0; i<streamID; i++) {
|
||||||
for (int i=0; i<streamID; i++) {
|
if (loopSample[i]>=0) {
|
||||||
if (loopSample[i]>=0) {
|
loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait;
|
||||||
if (loopTimer[i]<0) {
|
|
||||||
haveNegatives=true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
bool haveNegatives=false;
|
||||||
while (haveNegatives) {
|
|
||||||
// finish all negatives
|
|
||||||
int nextToTouch=-1;
|
|
||||||
for (int i=0; i<streamID; i++) {
|
for (int i=0; i<streamID; i++) {
|
||||||
if (loopSample[i]>=0) {
|
if (loopSample[i]>=0) {
|
||||||
if (loopTimer[i]<0) {
|
if (loopTimer[i]<0) {
|
||||||
if (nextToTouch>=0) {
|
haveNegatives=true;
|
||||||
if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i;
|
|
||||||
} else {
|
|
||||||
nextToTouch=i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextToTouch>=0) {
|
while (haveNegatives) {
|
||||||
double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch])));
|
// finish all negatives
|
||||||
if (waitTime>0) {
|
int nextToTouch=-1;
|
||||||
w->writeC(0x61);
|
for (int i=0; i<streamID; i++) {
|
||||||
w->writeS(waitTime);
|
if (loopSample[i]>=0) {
|
||||||
logV("wait is: %f",waitTime);
|
if (loopTimer[i]<0) {
|
||||||
totalWait-=waitTime;
|
if (nextToTouch>=0) {
|
||||||
tickCount+=waitTime;
|
if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i;
|
||||||
}
|
} else {
|
||||||
if (loopSample[nextToTouch]<song.sampleLen) {
|
nextToTouch=i;
|
||||||
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;
|
if (nextToTouch>=0) {
|
||||||
} else {
|
double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch])));
|
||||||
haveNegatives=false;
|
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
|
// write wait
|
||||||
|
|
|
@ -3390,6 +3390,14 @@ bool FurnaceGUI::loop() {
|
||||||
"pattern indexes are ordered as they appear in the song."
|
"pattern indexes are ordered as they appear in the song."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("direct stream mode",&vgmExportDirectStream);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip(
|
||||||
|
"required for DualPCM and MSM6258 export.\n\n"
|
||||||
|
"allows for volume/direction changes when playing samples,\n"
|
||||||
|
"at the cost of a massive increase in file size."
|
||||||
|
);
|
||||||
|
}
|
||||||
ImGui::Text("chips to export:");
|
ImGui::Text("chips to export:");
|
||||||
bool hasOneAtLeast=false;
|
bool hasOneAtLeast=false;
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
@ -4161,7 +4169,7 @@ bool FurnaceGUI::loop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_FILE_EXPORT_VGM: {
|
case GUI_FILE_EXPORT_VGM: {
|
||||||
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints);
|
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints,vgmExportDirectStream);
|
||||||
if (w!=NULL) {
|
if (w!=NULL) {
|
||||||
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
|
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
|
||||||
if (f!=NULL) {
|
if (f!=NULL) {
|
||||||
|
@ -5309,6 +5317,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
vgmExportLoop(true),
|
vgmExportLoop(true),
|
||||||
zsmExportLoop(true),
|
zsmExportLoop(true),
|
||||||
vgmExportPatternHints(false),
|
vgmExportPatternHints(false),
|
||||||
|
vgmExportDirectStream(false),
|
||||||
portrait(false),
|
portrait(false),
|
||||||
mobileMenuOpen(false),
|
mobileMenuOpen(false),
|
||||||
wantCaptureKeyboard(false),
|
wantCaptureKeyboard(false),
|
||||||
|
|
|
@ -1035,6 +1035,7 @@ class FurnaceGUI {
|
||||||
std::deque<String> recentFile;
|
std::deque<String> recentFile;
|
||||||
|
|
||||||
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
|
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
|
||||||
|
bool vgmExportDirectStream;
|
||||||
bool portrait, mobileMenuOpen;
|
bool portrait, mobileMenuOpen;
|
||||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||||
|
|
Loading…
Reference in a new issue