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`.
- `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.
- 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.

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)";
case 0xfc:
return "FCxx: Note release";
case 0xfd:
return "FDxx: Set virtual tempo numerator";
case 0xfe:
return "FExx: Set virtual tempo denominator";
case 0xff:
return "FFxx: Stop song";
default:
@ -1683,7 +1687,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
runMidiTime(cycles);
}
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);
if (goal>0 || goalRow>0) {
@ -2121,6 +2125,8 @@ void DivEngine::reset() {
extValue=0;
extValuePresent=0;
speeds=curSubSong->speeds;
virtualTempoN=curSubSong->virtualTempoN;
virtualTempoD=curSubSong->virtualTempoD;
firstTick=false;
shallStop=false;
shallStopSched=false;
@ -2368,6 +2374,21 @@ float DivEngine::getCurHz() {
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() {
return totalSeconds;
}

View file

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

View file

@ -487,6 +487,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal>0) speeds.val[0]=effectVal;
}
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
if (changeOrd==-1 || song.jumpTreatment==0) {
changeOrd=effectVal;
@ -1442,9 +1448,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
subticks=tickMult;
if (stepPlay!=1) {
tempoAccum+=(skipping && curSubSong->virtualTempoN<curSubSong->virtualTempoD)?curSubSong->virtualTempoD:curSubSong->virtualTempoN;
while (tempoAccum>=curSubSong->virtualTempoD) {
tempoAccum-=curSubSong->virtualTempoD;
tempoAccum+=(skipping && virtualTempoN<virtualTempoD)?virtualTempoD:virtualTempoN;
while (tempoAccum>=virtualTempoD) {
tempoAccum-=virtualTempoD;
if (--ticks<=0) {
ret=endOfSong;
if (shallStopSched) {
@ -1693,7 +1699,7 @@ void DivEngine::runMidiClock(int totalCycles) {
if (hl<=0.0) hl=4.0;
double timeBase=curSubSong->timeBase+1;
double speedSum=0;
double vD=curSubSong->virtualTempoD;
double vD=virtualTempoD;
for (int i=0; i<MIN(16,speeds.len); i++) {
speedSum+=speeds.val[i];
}
@ -1701,7 +1707,7 @@ void DivEngine::runMidiClock(int totalCycles) {
if (timeBase<1.0) timeBase=1.0;
if (speedSum<1.0) speedSum=1.0;
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;
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) {
if (loopPos==-1) {
if (loopOrder==curOrder && loopRow==curRow) {
if ((ticks-((tempoAccum+curSubSong->virtualTempoN)/curSubSong->virtualTempoD))<=0) {
if ((ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0) {
writeLoop=true;
}
}

View file

@ -4446,7 +4446,7 @@ bool FurnaceGUI::loop() {
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) {
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 (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
e->virtualTempoChanged();
}
if (ImGui::IsItemHovered()) {
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 (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
e->virtualTempoChanged();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Denominator (set to base tempo)");