From 29688d1cc52192ea04bc386418cf6125c4ead015 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 10 May 2023 03:30:05 -0500 Subject: [PATCH] fix MTC/MIDI clock on playSub --- src/engine/engine.cpp | 17 ++- src/engine/engine.h | 2 + src/engine/playback.cpp | 274 +++++++++++++++++++++------------------- 3 files changed, 161 insertions(+), 132 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2ff58bde..60c46341 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2208,6 +2208,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { skipping=false; return; } + if (!preserveDrift) { + runMidiClock(cycles); + runMidiTime(cycles); + } } int oldOrder=curOrder; while (playing && (curRow1)) { @@ -2215,6 +2219,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { skipping=false; return; } + if (!preserveDrift) { + runMidiClock(cycles); + runMidiTime(cycles); + } if (oldOrder!=curOrder) break; if (ticks-((tempoAccum+curSubSong->virtualTempoN)/MAX(1,curSubSong->virtualTempoD))<1 && curRow>=goalRow) break; } @@ -2237,6 +2245,13 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { midiClockDrift=0; midiTimeCycles=0; midiTimeDrift=0; + if (curMidiTime>0) { + curMidiTime--; + } + if (curMidiClock>0) { + curMidiClock--; + } + curMidiTimePiece=0; } if (!preserveDrift) { ticks=1; @@ -2448,7 +2463,7 @@ void DivEngine::play() { // drop drop=((actualTime/(30*60))-(actualTime/(30*600)))*2; actualTime+=drop; - + msgData[5]=(actualTime/(60*60*30))%24; msgData[6]=(actualTime/(60*30))%60; msgData[7]=(actualTime/30)%60; diff --git a/src/engine/engine.h b/src/engine/engine.h index 2ef8dc4d..21b1c272 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -475,6 +475,8 @@ class DivEngine { void recalcChans(); void reset(); void playSub(bool preserveDrift, int goalRow=0); + void runMidiClock(int totalCycles=1); + void runMidiTime(int totalCycles=1); void testFunction(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index caaf9236..00b4a1d2 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1546,6 +1546,147 @@ int DivEngine::getBufferPos() { return bufferPos>>MASTER_CLOCK_PREC; } +void DivEngine::runMidiClock(int totalCycles) { + if (freelance) return; + midiClockCycles-=totalCycles; + while (midiClockCycles<=0) { + curMidiClock++; + if (output) if (!skipping && output->midiOut!=NULL && midiOutClock) { + output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0)); + } + + double hl=curSubSong->hilightA; + if (hl<=0.0) hl=4.0; + double timeBase=curSubSong->timeBase+1; + double speedSum=0; + double vD=curSubSong->virtualTempoD; + for (int i=0; ivirtualTempoN/vD; + + midiClockCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm); + midiClockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(bpm)); + if (midiClockDrift>=(bpm)) { + midiClockDrift-=(bpm); + midiClockCycles++; + } + } +} + +void DivEngine::runMidiTime(int totalCycles) { + if (freelance) return; + midiTimeCycles-=totalCycles; + while (midiTimeCycles<=0) { + if (curMidiTimePiece==0) { + curMidiTimeCode=curMidiTime; + } + if (!(curMidiTimePiece&3)) curMidiTime++; + + double frameRate=96.0; + int timeRate=midiOutTimeRate; + if (timeRate<1 || timeRate>4) { + if (curSubSong->hz>=47.98 && curSubSong->hz<=48.02) { + timeRate=1; + } else if (curSubSong->hz>=49.98 && curSubSong->hz<=50.02) { + timeRate=2; + } else if (curSubSong->hz>=59.9 && curSubSong->hz<=60.11) { + timeRate=4; + } else { + timeRate=4; + } + } + + int hour=0; + int minute=0; + int second=0; + int frame=0; + int drop=0; + int actualTime=curMidiTimeCode; + + switch (timeRate) { + case 1: // 24 + frameRate=96.0; + hour=(actualTime/(60*60*24))%24; + minute=(actualTime/(60*24))%60; + second=(actualTime/24)%60; + frame=actualTime%24; + break; + case 2: // 25 + frameRate=100.0; + hour=(actualTime/(60*60*25))%24; + minute=(actualTime/(60*25))%60; + second=(actualTime/25)%60; + frame=actualTime%25; + break; + case 3: // 29.97 (NTSC drop) + frameRate=120.0*(1000.0/1001.0); + + // drop + drop=((actualTime/(30*60))-(actualTime/(30*600)))*2; + actualTime+=drop; + + hour=(actualTime/(60*60*30))%24; + minute=(actualTime/(60*30))%60; + second=(actualTime/30)%60; + frame=actualTime%30; + break; + case 4: // 30 (NTSC non-drop) + default: + frameRate=120.0; + hour=(actualTime/(60*60*30))%24; + minute=(actualTime/(60*30))%60; + second=(actualTime/30)%60; + frame=actualTime%30; + break; + } + + if (output) if (!skipping && output->midiOut!=NULL && midiOutTime) { + unsigned char val=0; + switch (curMidiTimePiece) { + case 0: + val=frame&15; + break; + case 1: + val=frame>>4; + break; + case 2: + val=second&15; + break; + case 3: + val=second>>4; + break; + case 4: + val=minute&15; + break; + case 5: + val=minute>>4; + break; + case 6: + val=hour&15; + break; + case 7: + val=(hour>>4)|((timeRate-1)<<1); + break; + } + val|=curMidiTimePiece<<4; + output->midiOut->send(TAMidiMessage(TA_MIDI_MTC_FRAME,val,0)); + } + curMidiTimePiece=(curMidiTimePiece+1)&7; + + midiTimeCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(frameRate); + midiTimeDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(frameRate)); + if (midiTimeDrift>=(frameRate)) { + midiTimeDrift-=(frameRate); + midiTimeCycles++; + } + } +} + void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) { lastLoopPos=-1; @@ -1833,141 +1974,12 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi // 3. run MIDI clock int midiTotal=MIN(cycles,runLeftG); for (int i=0; imidiOut!=NULL && midiOutClock) { - output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0)); - } - - double hl=curSubSong->hilightA; - if (hl<=0.0) hl=4.0; - double timeBase=curSubSong->timeBase+1; - double speedSum=0; - double vD=curSubSong->virtualTempoD; - for (int i=0; ivirtualTempoN/vD; - - midiClockCycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm); - midiClockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(bpm)); - if (midiClockDrift>=(bpm)) { - midiClockDrift-=(bpm); - midiClockCycles++; - } - } + runMidiClock(); } // 4. run MIDI timecode for (int i=0; i4) { - if (curSubSong->hz>=47.98 && curSubSong->hz<=48.02) { - timeRate=1; - } else if (curSubSong->hz>=49.98 && curSubSong->hz<=50.02) { - timeRate=2; - } else if (curSubSong->hz>=59.9 && curSubSong->hz<=60.11) { - timeRate=4; - } else { - timeRate=4; - } - } - - int hour=0; - int minute=0; - int second=0; - int frame=0; - int drop=0; - int actualTime=curMidiTimeCode; - - switch (timeRate) { - case 1: // 24 - frameRate=96.0; - hour=(actualTime/(60*60*24))%24; - minute=(actualTime/(60*24))%60; - second=(actualTime/24)%60; - frame=actualTime%24; - break; - case 2: // 25 - frameRate=100.0; - hour=(actualTime/(60*60*25))%24; - minute=(actualTime/(60*25))%60; - second=(actualTime/25)%60; - frame=actualTime%25; - break; - case 3: // 29.97 (NTSC drop) - frameRate=120.0*(1000.0/1001.0); - - // drop - drop=((actualTime/(30*60))-(actualTime/(30*600)))*2; - actualTime+=drop; - - hour=(actualTime/(60*60*30))%24; - minute=(actualTime/(60*30))%60; - second=(actualTime/30)%60; - frame=actualTime%30; - break; - case 4: // 30 (NTSC non-drop) - default: - frameRate=120.0; - hour=(actualTime/(60*60*30))%24; - minute=(actualTime/(60*30))%60; - second=(actualTime/30)%60; - frame=actualTime%30; - break; - } - - if (output) if (!skipping && output->midiOut!=NULL && midiOutTime) { - unsigned char val=0; - switch (curMidiTimePiece) { - case 0: - val=frame&15; - break; - case 1: - val=frame>>4; - break; - case 2: - val=second&15; - break; - case 3: - val=second>>4; - break; - case 4: - val=minute&15; - break; - case 5: - val=minute>>4; - break; - case 6: - val=hour&15; - break; - case 7: - val=(hour>>4)|((timeRate-1)<<1); - break; - } - val|=curMidiTimePiece<<4; - output->midiOut->send(TAMidiMessage(TA_MIDI_MTC_FRAME,val,0)); - } - curMidiTimePiece=(curMidiTimePiece+1)&7; - - midiTimeCycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(frameRate); - midiTimeDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(frameRate)); - if (midiTimeDrift>=(frameRate)) { - midiTimeDrift-=(frameRate); - midiTimeCycles++; - } - } + runMidiTime(); } // 5. tick the clock and fill buffers as needed