parent
0a63399dcf
commit
2343cdecc5
|
@ -100,6 +100,7 @@ struct DivChannelState {
|
||||||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
|
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
|
||||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||||
|
bool wentThroughNote, goneThroughNote;
|
||||||
|
|
||||||
int midiNote, curMidiNote, midiPitch;
|
int midiNote, curMidiNote, midiPitch;
|
||||||
size_t midiAge;
|
size_t midiAge;
|
||||||
|
@ -152,6 +153,8 @@ struct DivChannelState {
|
||||||
wasShorthandPorta(false),
|
wasShorthandPorta(false),
|
||||||
noteOnInhibit(false),
|
noteOnInhibit(false),
|
||||||
resetArp(false),
|
resetArp(false),
|
||||||
|
wentThroughNote(false),
|
||||||
|
goneThroughNote(false),
|
||||||
midiNote(-1),
|
midiNote(-1),
|
||||||
curMidiNote(-1),
|
curMidiNote(-1),
|
||||||
midiPitch(-1),
|
midiPitch(-1),
|
||||||
|
@ -524,7 +527,12 @@ 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, bool directStream=false);
|
// set trailingTicks to:
|
||||||
|
// - 0 to add one tick of trailing
|
||||||
|
// - x to add x+1 ticks of trailing
|
||||||
|
// - -1 to auto-determine trailing
|
||||||
|
// - -2 to add a whole loop of trailing
|
||||||
|
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
|
||||||
// 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.
|
||||||
|
|
|
@ -957,6 +957,8 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
}
|
}
|
||||||
} else if (!chan[i].noteOnInhibit) {
|
} else if (!chan[i].noteOnInhibit) {
|
||||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
||||||
|
chan[i].goneThroughNote=true;
|
||||||
|
chan[i].wentThroughNote=true;
|
||||||
keyHit[i]=true;
|
keyHit[i]=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -952,7 +952,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, bool directStream) {
|
SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints, bool directStream, int trailingTicks) {
|
||||||
if (version<0x150) {
|
if (version<0x150) {
|
||||||
lastError="VGM version is too low";
|
lastError="VGM version is too low";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1037,7 +1037,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
int chipAccounting=0;
|
int chipAccounting=0;
|
||||||
|
|
||||||
int loopPos=-1;
|
int loopPos=-1;
|
||||||
int loopTick=-1;
|
int loopTickSong=-1;
|
||||||
|
int songTick=0;
|
||||||
|
|
||||||
unsigned int sampleOff8[256];
|
unsigned int sampleOff8[256];
|
||||||
unsigned int sampleOffSegaPCM[256];
|
unsigned int sampleOffSegaPCM[256];
|
||||||
|
@ -1063,6 +1064,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
|
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
|
||||||
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
||||||
std::vector<size_t> tickPos;
|
std::vector<size_t> tickPos;
|
||||||
|
std::vector<int> tickSample;
|
||||||
|
|
||||||
|
bool trailing=false;
|
||||||
|
bool beenOneLoopAlready=false;
|
||||||
|
int countDown=MAX(0,trailingTicks)+1;
|
||||||
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
loopTimer[i]=0;
|
loopTimer[i]=0;
|
||||||
|
@ -2103,11 +2109,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
playSub(false);
|
playSub(false);
|
||||||
size_t tickCount=0;
|
size_t tickCount=0;
|
||||||
bool writeLoop=false;
|
bool writeLoop=false;
|
||||||
|
bool alreadyWroteLoop=false;
|
||||||
int ord=-1;
|
int ord=-1;
|
||||||
int exportChans=0;
|
int exportChans=0;
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
if (!willExport[dispatchOfChan[i]]) continue;
|
if (!willExport[dispatchOfChan[i]]) continue;
|
||||||
exportChans++;
|
exportChans++;
|
||||||
|
chan[i].wentThroughNote=false;
|
||||||
|
chan[i].goneThroughNote=false;
|
||||||
}
|
}
|
||||||
while (!done) {
|
while (!done) {
|
||||||
if (loopPos==-1) {
|
if (loopPos==-1) {
|
||||||
|
@ -2115,8 +2124,45 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
writeLoop=true;
|
writeLoop=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
songTick++;
|
||||||
tickPos.push_back(w->tell());
|
tickPos.push_back(w->tell());
|
||||||
if (nextTick(false,true) || !playing) {
|
tickSample.push_back(tickCount);
|
||||||
|
if (nextTick(false,true)) {
|
||||||
|
if (trailing) beenOneLoopAlready=true;
|
||||||
|
trailing=true;
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
if (!willExport[dispatchOfChan[i]]) continue;
|
||||||
|
chan[i].wentThroughNote=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trailing) {
|
||||||
|
switch (trailingTicks) {
|
||||||
|
case -1: { // automatic
|
||||||
|
bool stillHaveTo=false;
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
if (!willExport[dispatchOfChan[i]]) continue;
|
||||||
|
if (!chan[i].goneThroughNote) continue;
|
||||||
|
if (!chan[i].wentThroughNote) {
|
||||||
|
stillHaveTo=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!stillHaveTo) countDown=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -2: // one loop
|
||||||
|
break;
|
||||||
|
default: // custom
|
||||||
|
countDown--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (song.loopModality!=2) countDown=0;
|
||||||
|
|
||||||
|
if (countDown>0 && !beenOneLoopAlready) {
|
||||||
|
loopTickSong++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (countDown<=0 || !playing || beenOneLoopAlready) {
|
||||||
done=true;
|
done=true;
|
||||||
if (!loop) {
|
if (!loop) {
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
|
@ -2275,10 +2321,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
}
|
}
|
||||||
tickCount+=totalWait;
|
tickCount+=totalWait;
|
||||||
}
|
}
|
||||||
if (writeLoop) {
|
if (writeLoop && !alreadyWroteLoop) {
|
||||||
writeLoop=false;
|
writeLoop=false;
|
||||||
|
alreadyWroteLoop=true;
|
||||||
loopPos=w->tell();
|
loopPos=w->tell();
|
||||||
loopTick=tickCount;
|
loopTickSong=songTick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end of song
|
// end of song
|
||||||
|
@ -2333,9 +2380,16 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
if (loopPos==-1) {
|
if (loopPos==-1) {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
} else if (loopTickSong<0 || loopTickSong>(int)tickPos.size()) {
|
||||||
|
logW("loopTickSong out of range! %d>%d",loopTickSong,(int)tickPos.size());
|
||||||
|
w->writeI(0);
|
||||||
|
w->writeI(0);
|
||||||
} else {
|
} else {
|
||||||
w->writeI(loopPos-0x1c);
|
int realLoopTick=tickSample[loopTickSong];
|
||||||
w->writeI(tickCount-loopTick);
|
int realLoopPos=tickPos[loopTickSong];
|
||||||
|
logI("tickCount-realLoopTick: %d. realLoopPos: %d",tickCount-realLoopTick,realLoopPos);
|
||||||
|
w->writeI(realLoopPos-0x1c);
|
||||||
|
w->writeI(tickCount-realLoopTick);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
|
@ -3530,6 +3530,24 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::Checkbox("loop",&vgmExportLoop);
|
ImGui::Checkbox("loop",&vgmExportLoop);
|
||||||
|
if (vgmExportLoop && e->song.loopModality==2) {
|
||||||
|
ImGui::Text("trailing ticks:");
|
||||||
|
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
|
||||||
|
vgmExportTrailingTicks=-1;
|
||||||
|
}
|
||||||
|
if (ImGui::RadioButton("one loop",vgmExportTrailingTicks==-2)) {
|
||||||
|
vgmExportTrailingTicks=-2;
|
||||||
|
}
|
||||||
|
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
|
||||||
|
vgmExportTrailingTicks=0;
|
||||||
|
}
|
||||||
|
if (vgmExportTrailingTicks>=0) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) {
|
||||||
|
if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints);
|
ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip(
|
ImGui::SetTooltip(
|
||||||
|
@ -4463,7 +4481,7 @@ bool FurnaceGUI::loop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_FILE_EXPORT_VGM: {
|
case GUI_FILE_EXPORT_VGM: {
|
||||||
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints,vgmExportDirectStream);
|
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints,vgmExportDirectStream,vgmExportTrailingTicks);
|
||||||
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) {
|
||||||
|
@ -5837,6 +5855,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
snesFilterHex(false),
|
snesFilterHex(false),
|
||||||
mobileEdit(false),
|
mobileEdit(false),
|
||||||
vgmExportVersion(0x171),
|
vgmExportVersion(0x171),
|
||||||
|
vgmExportTrailingTicks(-1),
|
||||||
drawHalt(10),
|
drawHalt(10),
|
||||||
zsmExportTickRate(60),
|
zsmExportTickRate(60),
|
||||||
macroPointSize(16),
|
macroPointSize(16),
|
||||||
|
|
|
@ -1118,6 +1118,7 @@ class FurnaceGUI {
|
||||||
bool mobileEdit;
|
bool mobileEdit;
|
||||||
bool willExport[DIV_MAX_CHIPS];
|
bool willExport[DIV_MAX_CHIPS];
|
||||||
int vgmExportVersion;
|
int vgmExportVersion;
|
||||||
|
int vgmExportTrailingTicks;
|
||||||
int drawHalt;
|
int drawHalt;
|
||||||
int zsmExportTickRate;
|
int zsmExportTickRate;
|
||||||
int macroPointSize;
|
int macroPointSize;
|
||||||
|
|
Loading…
Reference in New Issue