add effects to change virtual tempo

This commit is contained in:
tildearrow 2024-03-15 14:56:55 -05:00
parent 779d1aeb61
commit 3512591fd1
7 changed files with 50 additions and 8 deletions

View file

@ -70,6 +70,9 @@ not all chips support these effects.
- `xxx` may be from `000` to `3FF`. - `xxx` may be from `000` to `3FF`.
- `F0xx`: **Set BPM.** changes tick rate according to beats per minute. range is `01` to `FF`. - `F0xx`: **Set BPM.** changes tick rate according to beats per minute. range is `01` to `FF`.
- --- - ---
- `FDxx`: **Set virtual tempo numerator.** sets the virtual tempo's numerator to the effect value.
- `FExx`: **Set virtual tempo denominator.** sets the virtual tempo's denominator to the effect value.
- ---
- `0Bxx`: **Jump to order.** `x` is the order to play after the current row. - `0Bxx`: **Jump to order.** `x` is the order to play after the current row.
- this marks the end of a loop with order `x` as the loop start. - this marks the end of a loop with order `x` as the loop start.
- `0Dxx`: **Jump to next pattern.** skips the current row and remainder of current order. `x` is the row at which to start playing the next pattern. - `0Dxx`: **Jump to next pattern.** skips the current row and remainder of current order. `x` is the row at which to start playing the next pattern.

View file

@ -141,6 +141,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "FAxx: Fast volume slide (0y: down; x0: up)"; return "FAxx: Fast volume slide (0y: down; x0: up)";
case 0xfc: case 0xfc:
return "FCxx: Note release"; return "FCxx: Note release";
case 0xfd:
return "FDxx: Set virtual tempo numerator";
case 0xfe:
return "FExx: Set virtual tempo denominator";
case 0xff: case 0xff:
return "FFxx: Stop song"; return "FFxx: Stop song";
default: default:
@ -1683,7 +1687,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
runMidiTime(cycles); runMidiTime(cycles);
} }
if (oldOrder!=curOrder) break; if (oldOrder!=curOrder) break;
if (ticks-((tempoAccum+curSubSong->virtualTempoN)/MAX(1,curSubSong->virtualTempoD))<1 && curRow>=goalRow) break; if (ticks-((tempoAccum+virtualTempoN)/MAX(1,virtualTempoD))<1 && curRow>=goalRow) break;
} }
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(false); for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(false);
if (goal>0 || goalRow>0) { if (goal>0 || goalRow>0) {
@ -2121,6 +2125,8 @@ void DivEngine::reset() {
extValue=0; extValue=0;
extValuePresent=0; extValuePresent=0;
speeds=curSubSong->speeds; speeds=curSubSong->speeds;
virtualTempoN=curSubSong->virtualTempoN;
virtualTempoD=curSubSong->virtualTempoD;
firstTick=false; firstTick=false;
shallStop=false; shallStop=false;
shallStopSched=false; shallStopSched=false;
@ -2368,6 +2374,21 @@ float DivEngine::getCurHz() {
return divider; return divider;
} }
short DivEngine::getVirtualTempoN() {
return virtualTempoN;
}
short DivEngine::getVirtualTempoD() {
return virtualTempoD;
}
void DivEngine::virtualTempoChanged() {
BUSY_BEGIN;
virtualTempoN=curSubSong->virtualTempoN;
virtualTempoD=curSubSong->virtualTempoD;
BUSY_END;
}
int DivEngine::getTotalSeconds() { int DivEngine::getTotalSeconds() {
return totalSeconds; return totalSeconds;
} }

View file

@ -442,6 +442,7 @@ class DivEngine {
int curMidiTimePiece, curMidiTimeCode; int curMidiTimePiece, curMidiTimeCode;
unsigned char extValue, pendingMetroTick; unsigned char extValue, pendingMetroTick;
DivGroovePattern speeds; DivGroovePattern speeds;
short virtualTempoN, virtualTempoD;
short tempoAccum; short tempoAccum;
DivStatusView view; DivStatusView view;
DivHaltPositions haltOn; DivHaltPositions haltOn;
@ -891,6 +892,13 @@ class DivEngine {
// get current Hz // get current Hz
float getCurHz(); float getCurHz();
// get virtual tempo
short getVirtualTempoN();
short getVirtualTempoD();
// tell engine about virtual tempo changes
void virtualTempoChanged();
// get time // get time
int getTotalTicks(); // 1/1000000th of a second int getTotalTicks(); // 1/1000000th of a second
int getTotalSeconds(); int getTotalSeconds();
@ -1334,6 +1342,8 @@ class DivEngine {
curMidiTimeCode(0), curMidiTimeCode(0),
extValue(0), extValue(0),
pendingMetroTick(0), pendingMetroTick(0),
virtualTempoN(150),
virtualTempoD(150),
tempoAccum(0), tempoAccum(0),
view(DIV_STATUS_NOTHING), view(DIV_STATUS_NOTHING),
haltOn(DIV_HALT_NONE), haltOn(DIV_HALT_NONE),

View file

@ -487,6 +487,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal>0) speeds.val[0]=effectVal; if (effectVal>0) speeds.val[0]=effectVal;
} }
break; break;
case 0xfd: // virtual tempo num
if (effectVal>0) virtualTempoN=effectVal;
break;
case 0xfe: // virtual tempo den
if (effectVal>0) virtualTempoD=effectVal;
break;
case 0x0b: // change order case 0x0b: // change order
if (changeOrd==-1 || song.jumpTreatment==0) { if (changeOrd==-1 || song.jumpTreatment==0) {
changeOrd=effectVal; changeOrd=effectVal;
@ -1442,9 +1448,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
subticks=tickMult; subticks=tickMult;
if (stepPlay!=1) { if (stepPlay!=1) {
tempoAccum+=(skipping && curSubSong->virtualTempoN<curSubSong->virtualTempoD)?curSubSong->virtualTempoD:curSubSong->virtualTempoN; tempoAccum+=(skipping && virtualTempoN<virtualTempoD)?virtualTempoD:virtualTempoN;
while (tempoAccum>=curSubSong->virtualTempoD) { while (tempoAccum>=virtualTempoD) {
tempoAccum-=curSubSong->virtualTempoD; tempoAccum-=virtualTempoD;
if (--ticks<=0) { if (--ticks<=0) {
ret=endOfSong; ret=endOfSong;
if (shallStopSched) { if (shallStopSched) {
@ -1693,7 +1699,7 @@ void DivEngine::runMidiClock(int totalCycles) {
if (hl<=0.0) hl=4.0; if (hl<=0.0) hl=4.0;
double timeBase=curSubSong->timeBase+1; double timeBase=curSubSong->timeBase+1;
double speedSum=0; double speedSum=0;
double vD=curSubSong->virtualTempoD; double vD=virtualTempoD;
for (int i=0; i<MIN(16,speeds.len); i++) { for (int i=0; i<MIN(16,speeds.len); i++) {
speedSum+=speeds.val[i]; speedSum+=speeds.val[i];
} }
@ -1701,7 +1707,7 @@ void DivEngine::runMidiClock(int totalCycles) {
if (timeBase<1.0) timeBase=1.0; if (timeBase<1.0) timeBase=1.0;
if (speedSum<1.0) speedSum=1.0; if (speedSum<1.0) speedSum=1.0;
if (vD<1) vD=1; if (vD<1) vD=1;
double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)curSubSong->virtualTempoN/vD; double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)virtualTempoN/vD;
if (bpm<1.0) bpm=1.0; if (bpm<1.0) bpm=1.0;
int increment=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm); int increment=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);

View file

@ -2424,7 +2424,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
while (!done) { while (!done) {
if (loopPos==-1) { if (loopPos==-1) {
if (loopOrder==curOrder && loopRow==curRow) { if (loopOrder==curOrder && loopRow==curRow) {
if ((ticks-((tempoAccum+curSubSong->virtualTempoN)/curSubSong->virtualTempoD))<=0) { if ((ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0) {
writeLoop=true; writeLoop=true;
} }
} }

View file

@ -4446,7 +4446,7 @@ bool FurnaceGUI::loop() {
info="| Groove"; info="| Groove";
} }
info+=fmt::sprintf(" @ %gHz (%g BPM) ",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); info+=fmt::sprintf(" @ %gHz (%g BPM) ",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->getVirtualTempoN(),e->getVirtualTempoD()));
if (settings.orderRowsBase) { if (settings.orderRowsBase) {
info+=fmt::sprintf("| Order %.2X/%.2X ",playOrder,e->curSubSong->ordersLen-1); info+=fmt::sprintf("| Order %.2X/%.2X ",playOrder,e->curSubSong->ordersLen-1);

View file

@ -171,6 +171,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_TEN)) { MARK_MODIFIED if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_TEN)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
e->virtualTempoChanged();
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Numerator"); ImGui::SetTooltip("Numerator");
@ -180,6 +181,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_TEN)) { MARK_MODIFIED if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_TEN)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
e->virtualTempoChanged();
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Denominator (set to base tempo)"); ImGui::SetTooltip("Denominator (set to base tempo)");