aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2018-06-18 22:33:48 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2018-06-18 22:33:48 +0800
commita25823bbc28bfb93f1330d7421bd2589c01386c1 (patch)
treeaa5ceffaff372b1f18d51bd5ab702ae977f2293c
parent9e228490ffc7deb2969fa5e2b5fd81d763986eed (diff)
downloadQMidiPlayer-a25823bbc28bfb93f1330d7421bd2589c01386c1.tar.xz
Precise playback (no more slowdown).
Also the visualization should now sync better.
-rw-r--r--ChangeLog4
-rw-r--r--core/qmpmidiplay.cpp41
-rw-r--r--core/qmpmidiplay.hpp5
-rw-r--r--core/qmpmidiread.cpp3
-rw-r--r--visualization/qmpvisualization.cpp3
5 files changed, 40 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 3a0d679..bd1a4b7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2018-06-18 0.8.6 alpha
+Precise playback (no more slowdown).
+Also the visualization should now sync better.
+
2018-03-19 0.8.6 alpha
Unified meta event reading code.
Fixed playlist dialog behaving oddly.
diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp
index 15e1331..6126f26 100644
--- a/core/qmpmidiplay.cpp
+++ b/core/qmpmidiplay.cpp
@@ -10,6 +10,9 @@
#include <windows.h>
uint64_t pf;
#endif
+//debug purpose
+uint32_t ttick;
+std::chrono::high_resolution_clock::time_point ttime;
CMidiPlayer* CMidiPlayer::ref=NULL;
bool CMidiPlayer::processEvent(const SEvent *e)
{
@@ -53,7 +56,9 @@ bool CMidiPlayer::processEvent(const SEvent *e)
switch(e->p1)
{
case 0x51:
- ctempo=e->p2;dpt=ctempo*1000/divs;
+ ctempo=e->p2;dpt=ctempo*1000./divs;
+ ttime=std::chrono::high_resolution_clock::now();
+ ttick=getTick();
break;
case 0x58:
ctsn=e->p2>>24;
@@ -106,8 +111,8 @@ void CMidiPlayer::processEventStub(const SEvent *e)
switch(e->p1)
{
case 0x51:
- ctempo=e->p2;dpt=ctempo*1000/divs;
- ccc[0][131]=dpt;
+ ctempo=e->p2;dpt=ctempo*1000./divs;
+ ccc[0][131]=ctempo;
break;
case 0x58:
ccc[0][132]=e->p2;
@@ -149,11 +154,19 @@ void CMidiPlayer::prePlayInit()
}
void CMidiPlayer::playEvents()
{
+ static uint32_t _lrtick;_lrtick=0;
+ ttick=getEvent(0)->time;ttime=std::chrono::high_resolution_clock::now();
for(ct=getEvent(0)->time;tceptr<ecnt;)
{
while(tcpaused)std::this_thread::sleep_for(std::chrono::milliseconds(100));
using namespace std::chrono;
high_resolution_clock::time_point b=high_resolution_clock::now();
+ if(getTick()-_lrtick>divs){
+ _lrtick=getTick();
+ //double _dt=(getTick()-ttick)*dpt-duration_cast<nanoseconds>((b-ttime)).count();
+ //<0: slower than expected, >0: faster than expected
+ //fprintf(stderr,"@ tick %u, dtime %.6fus",getTick(),_dt/1000.);
+ }
for(;!tcstop&&midiReaders&&tceptr<ecnt&&ct==getEvent(tceptr)->time;++tceptr)
if(processEvent(getEvent(tceptr)))
{
@@ -163,14 +176,20 @@ void CMidiPlayer::playEvents()
if(tcstop||!midiReaders||tceptr>=ecnt)break;
high_resolution_clock::time_point a=high_resolution_clock::now();
auto sendtime=a-b;
- if(resumed)resumed=false;
+ if(resumed){resumed=false;
+ ttick=getTick();ttime=high_resolution_clock::now();
+ }
else
if(sendtime.count()<(getEvent(tceptr)->time-ct)*dpt)
+ {
+ double ns_sleep=(getEvent(tceptr)->time-ct)*dpt-sendtime.count();
+ double correction=(getTick()-ttick)*dpt-(b-ttime).count();
#ifdef _WIN32
- w32usleep(((getEvent(tceptr)->time-ct)*dpt-sendtime.count())/1000);
+ w32usleep((uint64_t)(((getEvent(tceptr)->time-ct)*dpt-sendtime.count())/1000));
#else
- std::this_thread::sleep_for(std::chrono::nanoseconds((getEvent(tceptr)->time-ct)*dpt-sendtime.count()));
+ std::this_thread::sleep_for(std::chrono::nanoseconds((uint64_t)(ns_sleep+correction)));
#endif
+ }
if(tcstop||!midiReaders)break;
ct=getEvent(tceptr)->time;
}
@@ -179,7 +198,7 @@ void CMidiPlayer::playEvents()
}
void CMidiPlayer::fileTimer1Pass()
{
- ftime=.0;ctempo=0x7A120;dpt=ctempo*1000/divs;
+ ftime=.0;ctempo=0x7A120;dpt=ctempo*1000./divs;
for(uint32_t eptr=0,ct=getEvent(0)->time;eptr<ecnt;)
{
while(eptr<ecnt&&ct==getEvent(eptr)->time)
@@ -191,7 +210,7 @@ void CMidiPlayer::fileTimer1Pass()
}
void CMidiPlayer::fileTimer2Pass()
{
- double ctime=.0;uint32_t c=1;ctempo=0x7A120;dpt=ctempo*1000/divs;
+ double ctime=.0;uint32_t c=1;ctempo=0x7A120;dpt=ctempo*1000./divs;
memset(stamps,0,sizeof(stamps));memset(ccstamps,0,sizeof(ccstamps));
memset(ccc,0,sizeof(ccc));memset(rpnid,0xFF,sizeof(rpnid));memset(rpnval,0xFF,sizeof(rpnval));
for(int i=0;i<16;++i)
@@ -200,7 +219,7 @@ void CMidiPlayer::fileTimer2Pass()
ccc[i][11]=127;ccc[i][71]=64;ccc[i][72]=64;
ccc[i][73]=64;ccc[i][74]=64;ccc[i][75]=64;
ccc[i][76]=64;ccc[i][77]=64;ccc[i][78]=64;
- ccc[i][131]=dpt;ccc[i][132]=0x04021808;
+ ccc[i][131]=ctempo;ccc[i][132]=0x04021808;
ccc[i][133]=0;ccc[i][134]=2;
}if(midiFile->std!=4)ccc[9][0]=128;
for(int i=0;i<16;++i)for(int j=0;j<135;++j)
@@ -302,7 +321,7 @@ bool CMidiPlayer::playerLoadFile(const char* fn)
}
void CMidiPlayer::playerInit()
{
- ctempo=0x7A120;ctsn=4;ctsd=4;cks=0;dpt=ctempo*1000/divs;
+ ctempo=0x7A120;ctsn=4;ctsd=4;cks=0;dpt=ctempo*1000./divs;
tceptr=0;tcstop=0;tcpaused=0;finished=0;mute=solo=0;
for(int i=0;i<16;++i)pbr[i]=2,pbv[i]=8192;
sendSysEx=true;memset(rpnid,0xFF,sizeof(rpnid));memset(rpnval,0xFF,sizeof(rpnval));
@@ -346,7 +365,7 @@ void CMidiPlayer::setTCeptr(uint32_t ep,uint32_t st)
internalFluid->rpnMessage(i,0,ccstamps[st][i][134]<<7);
dest->rpnMessage(i,0,ccstamps[st][i][134]<<7);
pbr[i]=ccstamps[st][i][134];
- dpt=ccstamps[st][0][131];ctempo=dpt*divs/1000;
+ ctempo=ccstamps[st][0][131];dpt=ctempo*1000./divs;
ctsn=ccstamps[st][0][132]>>24;ctsd=1<<((ccstamps[st][0][132]>>16)&0xFF);
cks=ccstamps[st][0][133];
}
diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp
index 99e0d07..f054238 100644
--- a/core/qmpmidiplay.hpp
+++ b/core/qmpmidiplay.hpp
@@ -63,8 +63,9 @@ class CMidiPlayer
bool sendSysEx,waitvoice;
uint8_t chstate[16],chstatus[16][130];//0..127: cc 128: pc
qmpMidiOutFluid* internalFluid;
- uint32_t ctempo,ctsn,ctsd,dpt,divs,cks;
- //raw tempo, timesig num., timesig den., delay per tick, division, keysig
+ uint32_t ctempo,ctsn,ctsd,divs,cks;
+ double dpt;//time per tick
+ //raw tempo, timesig num., timesig den., division, keysig
//thread control
uint32_t tceptr,tcpaused,tcstop,ct;
uint32_t finished,resumed;
diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp
index e7d92d3..d5f20ff 100644
--- a/core/qmpmidiread.cpp
+++ b/core/qmpmidiread.cpp
@@ -100,7 +100,8 @@ int CSMFReader::eventReader()//returns 0 if End of Track encountered
break;
case 0x59://Key signature
assert(len==2);
- p1=(str[0]&0xffu)<<8u|(str[1]&0xffu);
+ if(len>=2)
+ p1=(str[0]&0xffu)<<8u|(str[1]&0xffu);else p1=0;
curTrack->appendEvent(SEvent(curid,curt,type,metatype,p1));
break;
case 0x01:case 0x02:case 0x03:
diff --git a/visualization/qmpvisualization.cpp b/visualization/qmpvisualization.cpp
index c1208f2..1452ca9 100644
--- a/visualization/qmpvisualization.cpp
+++ b/visualization/qmpvisualization.cpp
@@ -420,7 +420,6 @@ void qmpVisualization::updateVisualization3D()
fonthdpi.render(-49,stairpiano?56-i*7:63-i*8,stairpiano*i*2+0.1,0xFFFFFFFF,ALIGN_RIGHT,.008,0.01);
fonthdpi.render(-49.05,stairpiano?56.05-i*7:63.05-i*8,stairpiano*i*2+0.2,0xFF000000,ALIGN_RIGHT,.008,0.01);
}
- if(playing)ctk+=(int)1e6/((double)api->getRawTempo()/api->getDivision())*sm->smGetDelta();
while(pool.size()&&elb<pool.size()&&((double)ctk-pool[elb]->tce)*lpt>viewdist*2)++elb;
sm->smRenderEnd();
if(showparticle&&!horizontal)
@@ -662,7 +661,6 @@ void qmpVisualization::updateVisualization2D()
sm->smRenderQuad(&nq);
}
}
- if(playing)ctk+=(int)1e6/((double)api->getRawTempo()/api->getDivision())*sm->smGetDelta();
}
bool qmpVisualization::update()
{
@@ -671,6 +669,7 @@ bool qmpVisualization::update()
api->playerSeek(api->getCurrentPlaybackPercentage()+(sm->smGetKeyState(SMK_SHIFT)?5:1));
if(sm->smGetKeyState(SMK_LEFT)==SMKST_HIT)
api->playerSeek(api->getCurrentPlaybackPercentage()-(sm->smGetKeyState(SMK_SHIFT)?5:1));
+ if(playing)ctk+=1e6/api->getRawTempo()*api->getDivision()*sm->smGetDelta();
if(!flat)
updateVisualization3D();
sm->smRenderBegin2D();