From bd165c0254b9095bb9e5ea54def56b6404033ebe Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Thu, 30 Apr 2020 01:12:38 +0800 Subject: Add visualization renderer. Add API for getting raw pitch bend values. Fix non-compliant RPN handling. The visualization renderer is still at the "proof-of-concept" stage. It's not very usable (yet). --- core/qmpmidiplay.cpp | 7 +- core/qmpmidiplay.hpp | 1 + include/qmpcorepublic.hpp | 1 + qmidiplayer-desktop/qmpplugin.cpp | 5 + qmidiplayer-desktop/qmpplugin.hpp | 1 + visualization/CMakeLists.txt | 2 + visualization/qmpvisualization.cpp | 227 +++++++++++++++++++--------- visualization/qmpvisualization.hpp | 19 ++- visualization/renderer/CMakeLists.txt | 26 ++++ visualization/renderer/main.cpp | 22 +++ visualization/renderer/qmppluginapistub.cpp | 198 ++++++++++++++++++++++++ visualization/renderer/qmppluginapistub.hpp | 85 +++++++++++ visualization/renderer/qmpsettingsro.cpp | 167 ++++++++++++++++++++ visualization/renderer/qmpsettingsro.hpp | 73 +++++++++ visualization/renderer/qmpvisrendercore.cpp | 88 +++++++++++ visualization/renderer/qmpvisrendercore.hpp | 44 ++++++ 16 files changed, 892 insertions(+), 74 deletions(-) create mode 100644 visualization/renderer/CMakeLists.txt create mode 100644 visualization/renderer/main.cpp create mode 100644 visualization/renderer/qmppluginapistub.cpp create mode 100644 visualization/renderer/qmppluginapistub.hpp create mode 100644 visualization/renderer/qmpsettingsro.cpp create mode 100644 visualization/renderer/qmpsettingsro.hpp create mode 100644 visualization/renderer/qmpvisrendercore.cpp create mode 100644 visualization/renderer/qmpvisrendercore.hpp diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index 01045c1..a354d6e 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -106,7 +106,7 @@ void CMidiPlayer::processEventStub(const SEvent *e) if(~rpnid[e->type&0x0F]&&~rpnval[e->type&0x0F]) { if(rpnid[e->type&0x0F]==0)ccc[e->type&0x0F][134]=rpnval[e->type&0x0F]; - rpnid[e->type&0x0F]=rpnval[e->type&0x0F]=-1; + rpnval[e->type&0x0F]=-1; } ccc[e->type&0x0F][e->p1]=e->p2; break; @@ -404,6 +404,11 @@ uint32_t CMidiPlayer::getRawTempo(){return ctempo;} uint32_t CMidiPlayer::getDivision(){return divs;} uint32_t CMidiPlayer::getMaxTick(){return maxtk;} double CMidiPlayer::getPitchBend(int ch){return((int)pbv[ch]-8192)/8192.*pbr[ch];} +double CMidiPlayer::getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr) +{ + if(pb)*pb=this->pbv[ch]; + if(pbr)*pbr=this->pbr[ch]; +} uint32_t CMidiPlayer::getTCpaused(){return tcpaused;} void CMidiPlayer::setTCpaused(uint32_t ps){tcpaused=ps;} uint32_t CMidiPlayer::isFinished(){return finished;} diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index 83a7128..471c1db 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -135,6 +135,7 @@ class CMidiPlayer uint32_t getDivision(); uint32_t getMaxTick(); double getPitchBend(int ch); + double getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr); const char* getTitle(); const char* getCopyright(); diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp index bebde30..c6caed5 100644 --- a/include/qmpcorepublic.hpp +++ b/include/qmpcorepublic.hpp @@ -136,6 +136,7 @@ class qmpPluginAPI virtual int getChannelPreset(int ch)=0; virtual void playerSeek(uint32_t percentage)=0; virtual double getPitchBend(int ch)=0; + virtual void getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr)=0; virtual bool getChannelMask(int ch)=0; virtual std::string getTitle()=0; virtual std::wstring getWTitle()=0; diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp index 4785b16..9cd1f35 100644 --- a/qmidiplayer-desktop/qmpplugin.cpp +++ b/qmidiplayer-desktop/qmpplugin.cpp @@ -167,6 +167,11 @@ void qmpPluginAPIImpl::playerSeek(uint32_t percentage) {if(qmw)qmw->playerSeek(percentage);} double qmpPluginAPIImpl::getPitchBend(int ch) {return qmw&&qmw->getPlayer()?qmw->getPlayer()->getPitchBend(ch):0;} +void qmpPluginAPIImpl::getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr) +{ + if(qmw&&qmw->getPlayer()) + qmw->getPlayer()->getPitchBendRaw(ch,pb,pbr); +} bool qmpPluginAPIImpl::getChannelMask(int ch) {return qmw&&qmw->getPlayer()?qmw->getPlayer()->getChannelMask(ch):false;} std::string qmpPluginAPIImpl::getTitle() diff --git a/qmidiplayer-desktop/qmpplugin.hpp b/qmidiplayer-desktop/qmpplugin.hpp index 08343cd..99dedcd 100644 --- a/qmidiplayer-desktop/qmpplugin.hpp +++ b/qmidiplayer-desktop/qmpplugin.hpp @@ -35,6 +35,7 @@ public: int getChannelPreset(int ch); void playerSeek(uint32_t percentage); double getPitchBend(int ch); + void getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr); bool getChannelMask(int ch); std::string getTitle(); std::wstring getWTitle(); diff --git a/visualization/CMakeLists.txt b/visualization/CMakeLists.txt index 678f4a7..6d474e0 100644 --- a/visualization/CMakeLists.txt +++ b/visualization/CMakeLists.txt @@ -17,6 +17,8 @@ set(BUILD_DUMB ON) set(BUILD_EXAMPLE OFF) add_subdirectory(SMELT) +add_subdirectory(renderer) + find_package(glfw3 REQUIRED) find_package(GLEW REQUIRED) find_package(DevIL REQUIRED) diff --git a/visualization/qmpvisualization.cpp b/visualization/qmpvisualization.cpp index 013e615..e4e4428 100644 --- a/visualization/qmpvisualization.cpp +++ b/visualization/qmpvisualization.cpp @@ -72,7 +72,7 @@ void qmpVisualization::showThread() iccolors[i]=api->getOptionUint("Visualization/chInactiveColor"+std::to_string(i)); } sm=smGetInterface(SMELT_APILEVEL); - sm->smVidMode(wwidth,wheight,true); + sm->smVidMode(wwidth,wheight,true,!hidewindow); sm->smUpdateFunc(h);sm->smQuitFunc(closeh); sm->smWinTitle("QMidiPlayer Visualization"); sm->smSetFPS(vsync?FPS_VSYNC:tfps); @@ -86,6 +86,8 @@ void qmpVisualization::showThread() particletex=sm->smTextureLoad("particle.png");if(!particletex) particletex=sm->smTextureLoad("/usr/share/qmidiplayer/img/particle.png"); bgtex=sm->smTextureLoad(api->getOptionString("Visualization/background").c_str()); + if(rendermode) + fbcont=new DWORD[wwidth*wheight]; if(showparticle&&!horizontal) { smParticleSystemInfo psinfo; @@ -178,6 +180,8 @@ void qmpVisualization::close() api->setOptionDouble("Visualization/ry",rot[1]); api->setOptionDouble("Visualization/rz",rot[2]); } + if(rendermode) + delete[] fbcont; font.releaseTTF(); font2.releaseTTF(); fonthdpi.releaseTTF(); @@ -192,8 +196,10 @@ void qmpVisualization::close() void qmpVisualization::reset() { for(unsigned i=0;istopPS(); @@ -201,55 +207,65 @@ void qmpVisualization::reset() while(!pendingv[i][j].empty())pendingv[i][j].pop(); } } + +void qmpVisualization::switchToRenderMode(void(*frameCallback)(void*,size_t),bool _hidewindow) +{ + rendermode=true; + framecb=frameCallback; + hidewindow=_hidewindow; +} void qmpVisualization::start(){playing=true;} void qmpVisualization::stop(){playing=false;} void qmpVisualization::pause(){playing=!playing;} void qmpVisualization::updateVisualization3D() { smQuad q; - if(sm->smGetKeyState(SMK_D))pos[0]+=cos(smMath::deg2rad(rot[2]-90)),pos[1]+=sin(smMath::deg2rad(rot[2]-90)); - if(sm->smGetKeyState(SMK_A))pos[0]-=cos(smMath::deg2rad(rot[2]-90)),pos[1]-=sin(smMath::deg2rad(rot[2]-90)); - if(sm->smGetKeyState(SMK_S))pos[0]+=cos(smMath::deg2rad(rot[2])),pos[1]+=sin(smMath::deg2rad(rot[2])); - if(sm->smGetKeyState(SMK_W))pos[0]-=cos(smMath::deg2rad(rot[2])),pos[1]-=sin(smMath::deg2rad(rot[2])); - if(sm->smGetKeyState(SMK_Q))pos[2]+=1; - if(sm->smGetKeyState(SMK_E))pos[2]-=1; - if(sm->smGetKeyState(SMK_R)) + if(!rendermode) { - if(horizontal) + if(sm->smGetKeyState(SMK_D))pos[0]+=cos(smMath::deg2rad(rot[2]-90)),pos[1]+=sin(smMath::deg2rad(rot[2]-90)); + if(sm->smGetKeyState(SMK_A))pos[0]-=cos(smMath::deg2rad(rot[2]-90)),pos[1]-=sin(smMath::deg2rad(rot[2]-90)); + if(sm->smGetKeyState(SMK_S))pos[0]+=cos(smMath::deg2rad(rot[2])),pos[1]+=sin(smMath::deg2rad(rot[2])); + if(sm->smGetKeyState(SMK_W))pos[0]-=cos(smMath::deg2rad(rot[2])),pos[1]-=sin(smMath::deg2rad(rot[2])); + if(sm->smGetKeyState(SMK_Q))pos[2]+=1; + if(sm->smGetKeyState(SMK_E))pos[2]-=1; + if(sm->smGetKeyState(SMK_R)) { - pos[0]=-20;pos[1]=45;pos[2]=0; - rot[0]=0;rot[1]=90;rot[2]=90; + if(horizontal) + { + pos[0]=-20;pos[1]=45;pos[2]=0; + rot[0]=0;rot[1]=90;rot[2]=90; + } + else + { + pos[0]=0;pos[1]=120;pos[2]=70; + rot[0]=0;rot[1]=75;rot[2]=90; + } } - else + if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_HIT) + sm->smSetMouseGrab(true),sm->smGetMouse2f(&lastx,&lasty); + if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_KEEP) { - pos[0]=0;pos[1]=120;pos[2]=70; - rot[0]=0;rot[1]=75;rot[2]=90; + float x,y; + sm->smGetMouse2f(&x,&y); + rot[1]-=(y-lasty)*0.01; + rot[2]+=(x-lastx)*0.01; + while(rot[1]>360)rot[1]-=360; + while(rot[1]<0)rot[1]+=360; + while(rot[2]>360)rot[2]-=360; + while(rot[2]<0)rot[2]+=360; } + if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_RELEASE) + { + sm->smSetMouseGrab(false); + sm->smSetMouse2f(wwidth/2,wheight/2); + } + if(sm->smGetKeyState(SMK_I))rot[1]+=1; + if(sm->smGetKeyState(SMK_K))rot[1]-=1; + if(sm->smGetKeyState(SMK_L))rot[0]+=1; + if(sm->smGetKeyState(SMK_J))rot[0]-=1; + if(sm->smGetKeyState(SMK_U))rot[2]+=1; + if(sm->smGetKeyState(SMK_O))rot[2]-=1; } - if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_HIT) - sm->smSetMouseGrab(true),sm->smGetMouse2f(&lastx,&lasty); - if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_KEEP) - { - float x,y; - sm->smGetMouse2f(&x,&y); - rot[1]-=(y-lasty)*0.01; - rot[2]+=(x-lastx)*0.01; - while(rot[1]>360)rot[1]-=360; - while(rot[1]<0)rot[1]+=360; - while(rot[2]>360)rot[2]-=360; - while(rot[2]<0)rot[2]+=360; - } - if(sm->smGetKeyState(SMK_LBUTTON)==SMKST_RELEASE) - { - sm->smSetMouseGrab(false); - sm->smSetMouse2f(wwidth/2,wheight/2); - } - if(sm->smGetKeyState(SMK_I))rot[1]+=1; - if(sm->smGetKeyState(SMK_K))rot[1]-=1; - if(sm->smGetKeyState(SMK_L))rot[0]+=1; - if(sm->smGetKeyState(SMK_J))rot[0]-=1; - if(sm->smGetKeyState(SMK_U))rot[2]+=1; - if(sm->smGetKeyState(SMK_O))rot[2]-=1; for(int i=0;i<4;++i) {q.v[i].col=chkrtint;q.v[i].z=(showpiano&&!horizontal)?-5:0;} q.tex=chequer;q.blend=BLEND_ALPHABLEND; @@ -298,21 +314,27 @@ void qmpVisualization::updateVisualization3D() nebuf->addTransformedEntity(&c,I,smvec3d(0,0,0)); continue; } - if(pool[i]->ch>=996)continue; + if(pool[i]->ch>=990)continue; if(api->getChannelMask(pool[i]->ch))continue; smvec3d a(0.63*((double)pool[i]->key-64)+.1,(stairpiano?(56-pool[i]->ch*7.):(64-pool[i]->ch*8.)),((double)pool[i]->tce-ctk)*lpt+(stairpiano&&showpiano&&!horizontal)*pool[i]->ch*2.); smvec3d b(0.63*((double)pool[i]->key-64)+.7,(stairpiano?(56-pool[i]->ch*7.):(64-pool[i]->ch*8.))+.4,((double)pool[i]->tcs-ctk)*lpt+(stairpiano&&showpiano&&!horizontal)*pool[i]->ch*2.); - bool isnoteon=pool[i]->tcs<=ctk&&pool[i]->tce>=ctk;if(isnoteon) - a.x=0.63*((double)pool[i]->key-64+api->getPitchBend(pool[i]->ch))+.1, - b.x=0.63*((double)pool[i]->key-64+api->getPitchBend(pool[i]->ch))+.7; + bool isnoteon=pool[i]->tcs<=ctk&&pool[i]->tce>=ctk; + double pb=((int)cpw[pool[i]->ch]-8192)/8192.*cpbr[pool[i]->ch]; + if(isnoteon) + { + a.x=0.63*((double)pool[i]->key-64+pb)+.1; + b.x=0.63*((double)pool[i]->key-64+pb)+.7; + } notestatus[pool[i]->ch][pool[i]->key]|=isnoteon;a.x*=1.2;b.x*=1.2; if(horizontal) { a=smvec3d(((double)pool[i]->tcs-ctk)*lpt-20,(16-pool[i]->ch*2.),0.63*((double)pool[i]->key-64)+.1); b=smvec3d(((double)pool[i]->tce-ctk)*lpt-20,(16-pool[i]->ch*2.)+.4,0.63*((double)pool[i]->key-64)+.7); if(isnoteon) - a.z=0.63*((double)pool[i]->key-64+api->getPitchBend(pool[i]->ch))+.1, - b.z=0.63*((double)pool[i]->key-64+api->getPitchBend(pool[i]->ch))+.7; + { + a.z=0.63*((double)pool[i]->key-64+pb)+.1; + b.z=0.63*((double)pool[i]->key-64+pb)+.7; + } } if(showparticle&&!horizontal) { @@ -354,10 +376,11 @@ void qmpVisualization::updateVisualization3D() else spectrar[i][j]=spectra[i][j]; if(spectrar[i][j]) { - smvec3d a(0.756*((double)j-64+api->getPitchBend(i))+.12, + double pb=((int)cpw[i]-8192)/8192.*cpbr[i]; + smvec3d a(0.756*((double)j-64+pb)+.12, (stairpiano?(56-i*7.):(64-i*8.)), spectrar[i][j]*1.2*(1+0.02*sin(sm->smGetTime()*32))+(stairpiano&&showpiano&&!horizontal)*i*2.); - smvec3d b(0.756*((double)j-64+api->getPitchBend(i))+.84, + smvec3d b(0.756*((double)j-64+pb)+.84, (stairpiano?(56-i*7.):(64-i*8.))+.4, (stairpiano&&showpiano&&!horizontal)*i*2.); drawCube(a,b,SETA(iccolors[i],204),0); @@ -375,7 +398,8 @@ void qmpVisualization::updateVisualization3D() if(traveld[i][j]>0)traveld[i][j]-=2;else traveld[i][j]=0; p3d[i]->setKeyTravelDist(j,traveld[i][j]/10.); } - p3d[i]->render(smvec3d(0.756*api->getPitchBend(i),stairpiano?55-i*7:62-i*8,stairpiano*i*2)); + double pb=((int)cpw[i]-8192)/8192.*cpbr[i]; + p3d[i]->render(smvec3d(0.756*pb,stairpiano?55-i*7:62-i*8,stairpiano*i*2)); } for(int i=0;i<16;++i) if(showlabel) @@ -435,7 +459,7 @@ void qmpVisualization::updateVisualization2D() if(showmeasure)sm->smRenderQuad(&nq); continue; } - if(pool[i]->ch>=996)continue; + if(pool[i]->ch>=990)continue; if(api->getChannelMask(pool[i]->ch))continue; smvec2d a((froffsets[12]*(pool[i]->key/12)+froffsets[pool[i]->key%12])*wwidth/2048.,((double)pool[i]->tce-ctk)*lpt+wheight-nh); smvec2d b(a.x+notew*0.9,((double)pool[i]->tcs-ctk)*lpt+wheight-nh); @@ -444,10 +468,12 @@ void qmpVisualization::updateVisualization2D() a=smvec2d(((double)pool[i]->tce-ctk)*lpt+nh,(froffsets[12]*(pool[i]->key/12)+froffsets[pool[i]->key%12])*wheight/2048.); b=smvec2d(((double)pool[i]->tcs-ctk)*lpt+nh,a.y+notew*0.9); } - bool isnoteon=pool[i]->tcs<=ctk&&pool[i]->tce>=ctk;if(isnoteon) + bool isnoteon=pool[i]->tcs<=ctk&&pool[i]->tce>=ctk; + if(isnoteon) { - uint32_t newkey=pool[i]->key+(int)floor(api->getPitchBend(pool[i]->ch)); - double fpb=api->getPitchBend(pool[i]->ch)-floor(api->getPitchBend(pool[i]->ch)); + double pb=((int)cpw[pool[i]->ch]-8192)/8192.*cpbr[pool[i]->ch]; + uint32_t newkey=pool[i]->key+(int)floor(pb); + double fpb=pb-floor(pb); if(horizontal) { a.y=(froffsets[12]*(newkey/12)+froffsets[newkey%12])*wheight/2048.+notew*fpb; @@ -607,8 +633,9 @@ void qmpVisualization::updateVisualization2D() { smvec2d a((froffsets[12]*(j/12)+froffsets[j%12])*wwidth/2048.,spectrar[i][j]/-128.*(wheight-nh)*(1+0.02*sin(sm->smGetTime()*32))+wheight-nh); smvec2d b(a.x+notew*0.9,lpt+wheight-nh); - uint32_t newkey=j+(int)floor(api->getPitchBend(i)); - double fpb=api->getPitchBend(i)-floor(api->getPitchBend(i)); + double pb=((int)cpw[i]-8192)/8192.*cpbr[i]; + uint32_t newkey=j+(int)floor(pb); + double fpb=pb-floor(pb); if(horizontal) { a=smvec2d(spectrar[i][j]/128.*(wwidth-nh)*(1+0.02*sin(sm->smGetTime()*32))+nh,(froffsets[12]*(j/12)+froffsets[j%12])*wheight/2048.); @@ -632,18 +659,21 @@ void qmpVisualization::updateVisualization2D() bool qmpVisualization::update() { smQuad q; - if(sm->smGetKeyState(SMK_RIGHT)==SMKST_HIT) - 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(sm->smGetKeyState(SMK_B)==SMKST_HIT) - debug^=1; + if(!rendermode) + { + if(sm->smGetKeyState(SMK_RIGHT)==SMKST_HIT) + 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(sm->smGetKeyState(SMK_B)==SMKST_HIT) + debug^=1; + } if(playing) { - if(internal_clock_source) + if(rendermode) { - ctk=1.*lstk/tfps/api->getRawTempo()*api->getDivision(); - ++lstk; + ctk=1e6*(cfr)/tfps/ctp*api->getDivision()+lstk; + ++cfr; } else ctk=lstk+std::chrono::duration_cast>(std::chrono::steady_clock::now()-lst).count()/api->getRawTempo()*api->getDivision(); @@ -684,7 +714,17 @@ bool qmpVisualization::update() if(pool[i]->tcs>ctk)break; if(pool[i]->ch==998)cts=pool[i]->key; if(pool[i]->ch==997)cks=pool[i]->key; - if(pool[i]->ch==996)ctp=pool[i]->key; + if(pool[i]->ch==996) + { + ctp=pool[i]->key; + if(rendermode) + { + lstk=ctk; + cfr=1; + } + } + if(pool[i]->ch==995)cpbr[pool[i]->vel]=pool[i]->key; + if(pool[i]->ch==994)cpw[pool[i]->vel]=pool[i]->key; } int t,r;t=cks;r=(int8_t)((t>>8)&0xFF)+7;t&=0xFF; std::wstring ts(t?minors:majors,2*r,2); @@ -707,9 +747,12 @@ bool qmpVisualization::update() font.updateString(L"Current tick: %d",ctk); font.render(xp,yp+=step,0.5,0xFFFFFFFF,align); font.render(xp-1,yp-1,0.5,0xFF000000,align); - font.updateString(L"FPS: %.2f",sm->smGetFPS()); - font.render(xp,yp+=step,0.5,0xFFFFFFFF,align); - font.render(xp-1,yp-1,0.5,0xFF000000,align); + if(!rendermode) + { + font.updateString(L"FPS: %.2f",sm->smGetFPS()); + font.render(xp,yp+=step,0.5,0xFFFFFFFF,align); + font.render(xp-1,yp-1,0.5,0xFF000000,align); + } if(debug) { int dosdpos=(osdpos+1)%4; @@ -726,11 +769,21 @@ bool qmpVisualization::update() font.render(xp,yp+=step,0.5,0xFFFFFFFF,align); font.render(xp-1,yp-1,0.5,0xFF000000,align); tstr=std::string(sm->smGetDispDriver()); - font.updateString(L"Display: %ls",std::wstring({std::begin(tstr),std::end(tstr)}).c_str()); + font.updateString(L"Display %ls",std::wstring({std::begin(tstr),std::end(tstr)}).c_str()); font.render(xp,yp+=3*step,0.5,0xFFFFFFFF,align); font.render(xp-1,yp-1,0.5,0xFF000000,align); } sm->smRenderEnd(); + if(rendermode) + { + if(ctk>api->getMaxTick()) + framecb(nullptr,0); + else + { + sm->smPixelCopy(0,0,wwidth,wheight,4*wwidth*wheight,fbcont); + framecb(fbcont,4*wwidth*wheight); + } + } return shouldclose; } @@ -753,13 +806,27 @@ void qmpVisualization::drawCube(smvec3d a,smvec3d b,DWORD col,SMTEX tex,int face } } -qmpVisualization::qmpVisualization(qmpPluginAPI* _api){api=_api;} -qmpVisualization::~qmpVisualization(){api=nullptr;} +qmpVisualization* qmpVisualization::inst=nullptr; + +qmpVisualization::qmpVisualization(qmpPluginAPI* _api) +{ + api=_api; + inst=this; + rendermode=false; +} +qmpVisualization::~qmpVisualization() +{ + api=nullptr; + inst=nullptr; +} void qmpVisualization::init() { h=new CMidiVisualHandler(this); closeh=new CloseHandler(this); rendererTh=nullptr;playing=false; + hidewindow=false; + memset(rpnid,0xFF,sizeof(rpnid)); + memset(rpnval,0xFF,sizeof(rpnval)); memset(spectra,0,sizeof(spectra)); memset(spectrar,0,sizeof(spectrar)); api->registerFunctionality(this,"Visualization","Visualization",api->isDarkTheme()?":/img/visualization_i.svg":":/img/visualization.svg",0,true); @@ -771,6 +838,8 @@ void qmpVisualization::init() cts=api->getTimeSig(); cks=api->getKeySig(); ctp=api->getRawTempo(); + for(int i=0;i<16;++i) + api->getPitchBendRaw(i,&cpw[i],&cpbr[i]); },nullptr); herh=api->registerEventReadHandler( [this](const void *ee,void*){ @@ -786,6 +855,19 @@ void qmpVisualization::init() else this->pushNoteOff(e->time,e->type&0x0F,e->p1); break; + case 0xB0: + if(e->p1==100)rpnid[e->type&0x0F]=e->p2; + if(e->p1==6)rpnval[e->type&0x0F]=e->p2; + if(~rpnid[e->type&0x0F]&&~rpnval[e->type&0x0F]) + { + if(rpnid[e->type&0x0F]==0) + this->pool.push_back(new MidiVisualEvent{e->time,e->time,rpnval[e->type&0x0F],e->type&0x0Fu,995}); + rpnval[e->type&0x0F]=~0u; + } + break; + case 0xE0: + this->pool.push_back(new MidiVisualEvent{e->time,e->time,(e->p1|(e->p2<<7))&0x3FFFu,e->type&0x0Fu,994}); + break; case 0xF0: if(e->type==0xFF&&e->p1==0x58) { @@ -813,6 +895,8 @@ void qmpVisualization::init() ,nullptr); hfrf=api->registerFileReadFinishHook( [this](const void*,void*){ + memset(rpnval,0xFF,sizeof(rpnval)); + memset(rpnid,0xFF,sizeof(rpnid)); std::sort(this->tspool.begin(),this->tspool.end()); for(uint32_t tk=0,n=4,s=0;tk<=this->api->getMaxTick();){ while(tk<(s>=this->tspool.size()?this->api->getMaxTick():this->tspool[s].first)){ @@ -914,6 +998,9 @@ const char* qmpVisualization::pluginGetName() const char* qmpVisualization::pluginGetVersion() {return PLUGIN_VERSION;} +qmpVisualization *qmpVisualization::instance() +{return inst;} + void qmpVisualization::pushNoteOn(uint32_t tc,uint32_t ch,uint32_t key,uint32_t vel) { pendingt[ch][key].push(tc); diff --git a/visualization/qmpvisualization.hpp b/visualization/qmpvisualization.hpp index a5dad02..824093a 100644 --- a/visualization/qmpvisualization.hpp +++ b/visualization/qmpvisualization.hpp @@ -37,14 +37,17 @@ class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf smParticleSystem* pss[16][128]; smPSEmissionPositionGenerator* psepg; float pos[3],rot[3],lastx,lasty; - uint32_t ctc,ctk,elb,lstk; - uint32_t cts,cks,ctp; + uint32_t ctc,ctk,elb,lstk,cfr; + uint32_t cts,cks,ctp,cpbr[16],cpw[16]; + uint32_t rpnid[16],rpnval[16]; std::chrono::steady_clock::time_point lst; double etps; bool shouldclose,playing,debug; - bool internal_clock_source; + bool rendermode,hidewindow; int herh,heh,hfrf; int uihb,uihs,uihp,uihr,uihk; + void(*framecb)(void*,size_t); + DWORD* fbcont; std::vector>tspool; int traveld[16][128];bool notestatus[16][128],lastnotestatus[16][128]; int spectra[16][128],spectrar[16][128]; @@ -54,6 +57,8 @@ class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf void pushNoteOff(uint32_t tc,uint32_t ch,uint32_t key); void updateVisualization3D(); void updateVisualization2D(); + + static qmpVisualization* inst; public: qmpVisualization(qmpPluginAPI* _api); ~qmpVisualization(); @@ -64,11 +69,14 @@ class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf void stop(); void pause(); void reset(); + void switchToRenderMode(void(*frameCallback)(void*,size_t),bool _hidewindow); void init(); void deinit(); const char* pluginGetName(); const char* pluginGetVersion(); + + static qmpVisualization* instance(); }; class CMidiVisualHandler:public smHandler @@ -101,6 +109,11 @@ extern "C"{ {return new qmpVisualization(api);} EXPORTSYM const char* qmpPluginGetAPIRev() {return QMP_PLUGIN_API_REV;} + EXPORTSYM void switchToRenderMode(void(*frameCallback)(void*,size_t),bool hidewindow) + { + if(qmpVisualization::instance()) + qmpVisualization::instance()->switchToRenderMode(frameCallback,hidewindow); + } } #endif // QMPVISUALIZATION_H diff --git a/visualization/renderer/CMakeLists.txt b/visualization/renderer/CMakeLists.txt new file mode 100644 index 0000000..90a4704 --- /dev/null +++ b/visualization/renderer/CMakeLists.txt @@ -0,0 +1,26 @@ +set(qmpvisrender_SOURCES + qmpsettingsro.hpp + qmppluginapistub.hpp + qmpvisrendercore.hpp + main.cpp + qmpsettingsro.cpp + qmppluginapistub.cpp + qmpvisrendercore.cpp +) + +set(CMAKE_AUTOMOC ON) + +include_directories(${PROJECT_SOURCE_DIR}/core/) +include_directories(${PROJECT_SOURCE_DIR}/include/) + +add_executable(qmpvisrender + ${qmpvisrender_SOURCES} +) + +target_link_libraries(qmpvisrender + Qt5::Core + qmpcore + ${CMAKE_DL_LIBS} +) + +install(TARGETS qmpvisrender) diff --git a/visualization/renderer/main.cpp b/visualization/renderer/main.cpp new file mode 100644 index 0000000..ec4dd1d --- /dev/null +++ b/visualization/renderer/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +#include "qmpvisrendercore.hpp" + +int main(int argc,char **argv) +{ + QCoreApplication::setApplicationName("qmpvisrender"); + QCoreApplication a(argc,argv); + QCommandLineParser clp; + clp.setApplicationDescription("Renderer a visualization of a midi file."); + clp.addHelpOption(); + clp.parse(a.arguments()); + qmpVisRenderCore core; + core.loadVisualizationLibrary(); + if(clp.positionalArguments().size()) + core.setMIDIFile(clp.positionalArguments().front().toStdString().c_str()); + core.startRender(); + int retval=a.exec(); + core.unloadVisualizationLibrary(); + return retval; +} diff --git a/visualization/renderer/qmppluginapistub.cpp b/visualization/renderer/qmppluginapistub.cpp new file mode 100644 index 0000000..d37e191 --- /dev/null +++ b/visualization/renderer/qmppluginapistub.cpp @@ -0,0 +1,198 @@ +#include "qmpmidiplay.hpp" +#include "qmpvisrendercore.hpp" +#include "qmpsettingsro.hpp" +#include "qmppluginapistub.hpp" + +#include + +qmpPluginAPIStub::qmpPluginAPIStub(qmpVisRenderCore *_core): + core(_core) +{ +} + +qmpPluginAPIStub::~qmpPluginAPIStub() +{ + core=nullptr; +} + +uint32_t qmpPluginAPIStub::getDivision() +{ + return core->player->getDivision(); +} +uint32_t qmpPluginAPIStub::getRawTempo(){return 0;} +double qmpPluginAPIStub::getRealTempo(){return 0;} +uint32_t qmpPluginAPIStub::getTimeSig(){return 0;} +int qmpPluginAPIStub::getKeySig(){return 0;} +uint32_t qmpPluginAPIStub::getNoteCount(){return 0;} +uint32_t qmpPluginAPIStub::getMaxTick() +{ + return core->player->getMaxTick(); +} +uint32_t qmpPluginAPIStub::getCurrentPolyphone(){return 0;} +uint32_t qmpPluginAPIStub::getMaxPolyphone(){return 0;} +uint32_t qmpPluginAPIStub::getCurrentTimeStamp(){return 0;} +uint32_t qmpPluginAPIStub::getCurrentPlaybackPercentage(){return 0;} +int qmpPluginAPIStub::getChannelCC(int ch, int cc){return 0;} +int qmpPluginAPIStub::getChannelPreset(int ch){return 0;} +void qmpPluginAPIStub::playerSeek(uint32_t percentage){} +double qmpPluginAPIStub::getPitchBend(int ch){return 0;} +void qmpPluginAPIStub::getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr){} +bool qmpPluginAPIStub::getChannelMask(int ch){return 0;} +std::string qmpPluginAPIStub::getTitle() +{ + if(core->settings()->getOptionEnumIntOptName("Midi/TextEncoding")=="Unicode") + return std::string(core->player->getTitle()); + return QTextCodec::codecForName( + core->settings()->getOptionEnumIntOptName("Midi/TextEncoding").c_str())-> + toUnicode(core->player->getTitle()).toStdString(); +} +std::wstring qmpPluginAPIStub::getWTitle() +{ + if(core->settings()->getOptionEnumIntOptName("Midi/TextEncoding")=="Unicode") + return QString(core->player->getTitle()).toStdWString(); + return QTextCodec::codecForName( + core->settings()->getOptionEnumIntOptName("Midi/TextEncoding").c_str())-> + toUnicode(core->player->getTitle()).toStdWString(); +} +std::string qmpPluginAPIStub::getChannelPresetString(int ch){return std::string();} +bool qmpPluginAPIStub::isDarkTheme(){return false;} +void *qmpPluginAPIStub::getMainWindow(){return nullptr;} +void qmpPluginAPIStub::discardCurrentEvent(){} +void qmpPluginAPIStub::commitEventChange(SEvent d){} +void qmpPluginAPIStub::callEventReaderCB(SEvent d){} +void qmpPluginAPIStub::setFuncState(std::string name,bool state){} +void qmpPluginAPIStub::setFuncEnabled(std::string name, bool enable) +{} +void qmpPluginAPIStub::registerFunctionality(qmpFuncBaseIntf *i, std::string name, std::string desc, const char *icon, int iconlen, bool checkable) +{ + if(name=="Visualization") + core->vf=i; +} +void qmpPluginAPIStub::unregisterFunctionality(std::string name) +{ + if(name=="Visualization") + core->vf=nullptr; +} + +int qmpPluginAPIStub::registerUIHook(std::string e, ICallBack *cb, void *userdat){} +int qmpPluginAPIStub::registerUIHook(std::string e, callback_t cb, void *userdat) +{ + if(e=="main.start") + core->startcb=cb; + return 0; +} +void qmpPluginAPIStub::unregisterUIHook(std::string e, int hook) +{ + if(e=="main.start") + core->startcb=nullptr; +} + +void qmpPluginAPIStub::registerMidiOutDevice(qmpMidiOutDevice *dev, std::string name){} +void qmpPluginAPIStub::unregisterMidiOutDevice(std::string name){} + +int qmpPluginAPIStub::registerEventReaderIntf(ICallBack *cb, void *userdata){} +void qmpPluginAPIStub::unregisterEventReaderIntf(int intfhandle){} +int qmpPluginAPIStub::registerEventHandlerIntf(ICallBack *cb, void *userdata){} +void qmpPluginAPIStub::unregisterEventHandlerIntf(int intfhandle){} +int qmpPluginAPIStub::registerFileReadFinishedHandlerIntf(ICallBack *cb, void *userdata){} +void qmpPluginAPIStub::unregisterFileReadFinishedHandlerIntf(int intfhandle){} + +int qmpPluginAPIStub::registerEventHandler(callback_t cb, void *userdata, bool post){} +void qmpPluginAPIStub::unregisterEventHandler(int id){} +int qmpPluginAPIStub::registerEventReadHandler(callback_t cb, void *userdata) +{ + return core->player->registerEventReadHandler(cb,userdata); +} +void qmpPluginAPIStub::unregisterEventReadHandler(int id) +{ + core->player->unregisterEventReadHandler(id); +} +int qmpPluginAPIStub::registerFileReadFinishHook(callback_t cb, void *userdata) +{ + return core->player->registerFileReadFinishHook(cb,userdata); +} +void qmpPluginAPIStub::unregisterFileReadFinishHook(int id) +{ + core->player->unregisterFileReadFinishHook(id); +} + +void qmpPluginAPIStub::registerFileReader(qmpFileReader *reader, std::string name){} +void qmpPluginAPIStub::unregisterFileReader(std::string name){} + +void qmpPluginAPIStub::registerOptionInt(std::string tab, std::string desc, std::string key, int min, int max, int defaultval) +{ + core->settings()->registerOptionInt(tab,desc,key,min,max,defaultval); +} +int qmpPluginAPIStub::getOptionInt(std::string key) +{ + return core->settings()->getOptionInt(key); +} +void qmpPluginAPIStub::setOptionInt(std::string key, int val) +{ + core->settings()->setOptionInt(key,val); +} + +void qmpPluginAPIStub::registerOptionUint(std::string tab, std::string desc, std::string key, unsigned min, unsigned max, unsigned defaultval) +{ + core->settings()->registerOptionUint(tab,desc,key,min,max,defaultval); +} +unsigned qmpPluginAPIStub::getOptionUint(std::string key) +{ + return core->settings()->getOptionUint(key); +} +void qmpPluginAPIStub::setOptionUint(std::string key, unsigned val) +{ + return core->settings()->setOptionUint(key,val); +} + +void qmpPluginAPIStub::registerOptionBool(std::string tab, std::string desc, std::string key, bool defaultval) +{ + core->settings()->registerOptionBool(tab,desc,key,defaultval); +} +bool qmpPluginAPIStub::getOptionBool(std::string key) +{ + return core->settings()->getOptionBool(key); +} +void qmpPluginAPIStub::setOptionBool(std::string key, bool val) +{ + core->settings()->setOptionBool(key,val); +} + +void qmpPluginAPIStub::registerOptionDouble(std::string tab, std::string desc, std::string key, double min, double max, double defaultval) +{ + core->settings()->registerOptionDouble(tab,desc,key,min,max,defaultval); +} +double qmpPluginAPIStub::getOptionDouble(std::string key) +{ + return core->settings()->getOptionDouble(key); +} +void qmpPluginAPIStub::setOptionDouble(std::string key, double val) +{ + core->settings()->setOptionDouble(key,val); +} + +void qmpPluginAPIStub::registerOptionString(std::string tab, std::string desc, std::string key, std::string defaultval, bool ispath) +{ + core->settings()->registerOptionString(tab,desc,key,defaultval,ispath); +} +std::string qmpPluginAPIStub::getOptionString(std::string key) +{ + return core->settings()->getOptionString(key); +} +void qmpPluginAPIStub::setOptionString(std::string key, std::string val) +{ + core->settings()->setOptionString(key,val); +} + +void qmpPluginAPIStub::registerOptionEnumInt(std::string tab, std::string desc, std::string key, std::vector options, int defaultval) +{ + core->settings()->registerOptionEnumInt(tab,desc,key,options,defaultval); +} +int qmpPluginAPIStub::getOptionEnumInt(std::string key) +{ + return core->settings()->getOptionEnumInt(key); +} +void qmpPluginAPIStub::setOptionEnumInt(std::string key, int val) +{ + core->settings()->setOptionEnumInt(key,val); +} diff --git a/visualization/renderer/qmppluginapistub.hpp b/visualization/renderer/qmppluginapistub.hpp new file mode 100644 index 0000000..d96cfca --- /dev/null +++ b/visualization/renderer/qmppluginapistub.hpp @@ -0,0 +1,85 @@ +#ifndef QMPPLUGINAPISTUB_HPP +#define QMPPLUGINAPISTUB_HPP + +#include "qmpcorepublic.hpp" + +class qmpVisRenderCore; +class qmpPluginAPIStub:public qmpPluginAPI +{ +public: + qmpPluginAPIStub(qmpVisRenderCore *_core); + ~qmpPluginAPIStub(); + uint32_t getDivision(); + uint32_t getRawTempo(); + double getRealTempo(); + uint32_t getTimeSig(); + int getKeySig(); + uint32_t getNoteCount(); + uint32_t getMaxTick(); + uint32_t getCurrentPolyphone(); + uint32_t getMaxPolyphone(); + uint32_t getCurrentTimeStamp(); + uint32_t getCurrentPlaybackPercentage(); + int getChannelCC(int ch,int cc); + int getChannelPreset(int ch); + void playerSeek(uint32_t percentage); + double getPitchBend(int ch); + void getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr); + bool getChannelMask(int ch); + std::string getTitle(); + std::wstring getWTitle(); + std::string getChannelPresetString(int ch); + bool isDarkTheme(); + void* getMainWindow(); + + void discardCurrentEvent(); + void commitEventChange(SEvent d); + void callEventReaderCB(SEvent d); + void setFuncState(std::string name,bool state); + void setFuncEnabled(std::string name,bool enable); + + void registerFunctionality(qmpFuncBaseIntf* i,std::string name,std::string desc,const char* icon,int iconlen,bool checkable); + void unregisterFunctionality(std::string name); + int registerUIHook(std::string e,ICallBack* cb,void* userdat); + int registerUIHook(std::string e,callback_t cb,void* userdat); + void unregisterUIHook(std::string e,int hook); + void registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name); + void unregisterMidiOutDevice(std::string name); + int registerEventReaderIntf(ICallBack* cb,void* userdata); + void unregisterEventReaderIntf(int intfhandle); + int registerEventHandlerIntf(ICallBack* cb,void* userdata); + void unregisterEventHandlerIntf(int intfhandle); + int registerFileReadFinishedHandlerIntf(ICallBack* cb,void* userdata); + void unregisterFileReadFinishedHandlerIntf(int intfhandle); + int registerEventHandler(callback_t cb,void *userdata,bool post=false); + void unregisterEventHandler(int id); + int registerEventReadHandler(callback_t cb,void *userdata); + void unregisterEventReadHandler(int id); + int registerFileReadFinishHook(callback_t cb,void *userdata); + void unregisterFileReadFinishHook(int id); + void registerFileReader(qmpFileReader* reader,std::string name); + void unregisterFileReader(std::string name); + + void registerOptionInt(std::string tab,std::string desc,std::string key,int min,int max,int defaultval); + int getOptionInt(std::string key); + void setOptionInt(std::string key,int val); + void registerOptionUint(std::string tab,std::string desc,std::string key,unsigned min,unsigned max,unsigned defaultval); + unsigned getOptionUint(std::string key); + void setOptionUint(std::string key,unsigned val); + void registerOptionBool(std::string tab,std::string desc,std::string key,bool defaultval); + bool getOptionBool(std::string key); + void setOptionBool(std::string key,bool val); + void registerOptionDouble(std::string tab,std::string desc,std::string key,double min,double max,double defaultval); + double getOptionDouble(std::string key); + void setOptionDouble(std::string key,double val); + void registerOptionString(std::string tab,std::string desc,std::string key,std::string defaultval,bool ispath=false); + std::string getOptionString(std::string key); + void setOptionString(std::string key,std::string val); + void registerOptionEnumInt(std::string tab,std::string desc,std::string key,std::vector options,int defaultval); + int getOptionEnumInt(std::string key); + void setOptionEnumInt(std::string key,int val); +private: + qmpVisRenderCore* core; +}; + +#endif // QMPPLUGINAPISTUB_HPP diff --git a/visualization/renderer/qmpsettingsro.cpp b/visualization/renderer/qmpsettingsro.cpp new file mode 100644 index 0000000..cc6e0bf --- /dev/null +++ b/visualization/renderer/qmpsettingsro.cpp @@ -0,0 +1,167 @@ +#include +#include + +#include "qmpsettingsro.hpp" + +qmpSettingsRO::qmpSettingsRO() +{ +} + +void qmpSettingsRO::registerOptionInt(std::string tab,std::string desc,std::string key,int min,int max,int defaultval) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc,qmpOptionR::ParameterType::parameter_int,defaultval,min,max); +} +int qmpSettingsRO::getOptionInt(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_int) + return settings.value(QString(key.c_str()),options[key].defaultval).toInt(); + return options[key].defaultval.toInt(); +} +void qmpSettingsRO::setOptionInt(std::string key,int val) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_int) + settings.insert(QString(key.c_str()),val); +} + +void qmpSettingsRO::registerOptionUint(std::string tab,std::string desc,std::string key,unsigned min,unsigned max,unsigned defaultval) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc,qmpOptionR::ParameterType::parameter_uint,defaultval,min,max); +} +unsigned qmpSettingsRO::getOptionUint(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_uint) + return settings.value(QString(key.c_str()),options[key].defaultval).toUInt(); + return options[key].defaultval.toUInt(); +} +void qmpSettingsRO::setOptionUint(std::string key,unsigned val) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_uint) + settings.insert(QString(key.c_str()),val); +} + +void qmpSettingsRO::registerOptionBool(std::string tab,std::string desc,std::string key,bool defaultval) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc,qmpOptionR::ParameterType::parameter_bool,defaultval); +} +bool qmpSettingsRO::getOptionBool(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_bool) + return settings.value(QString(key.c_str()),options[key].defaultval).toBool(); + return options[key].defaultval.toBool(); +} +void qmpSettingsRO::setOptionBool(std::string key,bool val) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_bool) + settings.insert(QString(key.c_str()),val); +} + +void qmpSettingsRO::registerOptionDouble(std::string tab,std::string desc,std::string key,double min,double max,double defaultval) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc,qmpOptionR::ParameterType::parameter_double,defaultval,min,max); +} +double qmpSettingsRO::getOptionDouble(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_double) + return settings.value(QString(key.c_str()),options[key].defaultval).toDouble(); + return options[key].defaultval.toDouble(); +} +void qmpSettingsRO::setOptionDouble(std::string key,double val) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_double) + settings.insert(QString(key.c_str()),val); +} + +void qmpSettingsRO::registerOptionString(std::string tab,std::string desc,std::string key,std::string defaultval,bool is_url) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc, + is_url?qmpOptionR::ParameterType::parameter_url:qmpOptionR::ParameterType::parameter_str, + QString(defaultval.c_str())); +} +std::string qmpSettingsRO::getOptionString(std::string key) +{ + if(options.find(key)!=options.end()&& + (options[key].type==qmpOptionR::ParameterType::parameter_str||options[key].type==qmpOptionR::ParameterType::parameter_url)) + return settings.value(QString(key.c_str()),options[key].defaultval).toString().toStdString(); + return options[key].defaultval.toString().toStdString(); +} +void qmpSettingsRO::setOptionString(std::string key,std::string val) +{ + if(options.find(key)!=options.end()&& + (options[key].type==qmpOptionR::ParameterType::parameter_str||options[key].type==qmpOptionR::ParameterType::parameter_url)) + settings.insert(QString(key.c_str()),QString(val.c_str())); +} + +void qmpSettingsRO::registerOptionEnumInt(std::string tab,std::string desc,std::string key,std::vector enumlist,int defaultval) +{ + Q_UNUSED(tab) + optionlist.push_back(key); + options[key]=qmpOptionR(desc,qmpOptionR::ParameterType::parameter_enum,defaultval); + options[key].enumlist=enumlist; +} +int qmpSettingsRO::getOptionEnumInt(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_enum) + { + std::string curitm=settings.value(QString(key.c_str()),options[key].defaultval).toString().toStdString(); + auto curidx=std::find(options[key].enumlist.begin(),options[key].enumlist.end(),curitm); + if(curidx!=options[key].enumlist.end()) + return static_cast(curidx-options[key].enumlist.begin()); + else + { + return options[key].defaultval.toInt(); + } + } + return options[key].defaultval.toInt(); +} +std::string qmpSettingsRO::getOptionEnumIntOptName(std::string key) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_enum) + { + std::string curitm=settings.value(QString(key.c_str()),options[key].defaultval).toString().toStdString(); + auto curidx=std::find(options[key].enumlist.begin(),options[key].enumlist.end(),curitm); + if(curidx!=options[key].enumlist.end()) + return curitm; + else + { + return options[key].enumlist[static_cast(options[key].defaultval.toInt())]; + } + } + return options[key].enumlist[static_cast(options[key].defaultval.toInt())]; +} +void qmpSettingsRO::setOptionEnumInt(std::string key,int val) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_enum) + { + if(static_cast(val)(val)].c_str())); + } +} +void qmpSettingsRO::setOptionEnumIntOptName(std::string key,std::string valname) +{ + if(options.find(key)!=options.end()&&options[key].type==qmpOptionR::ParameterType::parameter_enum) + { + auto curidx=std::find(options[key].enumlist.begin(),options[key].enumlist.end(),valname); + if(curidx!=options[key].enumlist.end()) + settings.insert(QString(key.c_str()),QString(valname.c_str())); + } +} + +void qmpSettingsRO::load(const char *path) +{ + QScopedPointer qsettings(new QSettings(path,QSettings::Format::IniFormat)); + settings.clear(); + for(QString&k:qsettings->allKeys()) + { + settings.insert(k,qsettings->value(k)); + } +} diff --git a/visualization/renderer/qmpsettingsro.hpp b/visualization/renderer/qmpsettingsro.hpp new file mode 100644 index 0000000..30ab9b6 --- /dev/null +++ b/visualization/renderer/qmpsettingsro.hpp @@ -0,0 +1,73 @@ +#ifndef QMPSETTINGSRO_H +#define QMPSETTINGSRO_H + +#include +#include +#include + +#include +#include + +struct qmpOptionR +{ + enum ParameterType{ + parameter_int=0, + parameter_uint, + parameter_bool, + parameter_double, + parameter_str, + parameter_enum, + parameter_url, + parameter_custom=0x100 + }; + + std::string desc; + ParameterType type; + QVariant defaultval,minv,maxv; + std::vector enumlist; + + qmpOptionR(){} + qmpOptionR( + std::string _desc, + ParameterType _t,QVariant _def=QVariant(), + QVariant _min=QVariant(),QVariant _max=QVariant()): + desc(_desc), + type(_t), + defaultval(_def), + minv(_min), + maxv(_max){} +}; + +class qmpSettingsRO +{ +public: + qmpSettingsRO(); + void registerOptionInt(std::string tab,std::string desc,std::string key,int min,int max,int defaultval); + int getOptionInt(std::string key); + void setOptionInt(std::string key,int val); + void registerOptionUint(std::string tab,std::string desc,std::string key,unsigned min,unsigned max,unsigned defaultval); + unsigned getOptionUint(std::string key); + void setOptionUint(std::string key,unsigned val); + void registerOptionBool(std::string tab,std::string desc,std::string key,bool defaultval); + bool getOptionBool(std::string key); + void setOptionBool(std::string key,bool val); + void registerOptionDouble(std::string tab,std::string desc,std::string key,double min,double max,double defaultval); + double getOptionDouble(std::string key); + void setOptionDouble(std::string key,double val); + void registerOptionString(std::string tab,std::string desc,std::string key,std::string defaultval,bool is_url); + std::string getOptionString(std::string key); + void setOptionString(std::string key,std::string val); + void registerOptionEnumInt(std::string tab,std::string desc,std::string key,std::vector enumlist,int defaultval); + int getOptionEnumInt(std::string key); + std::string getOptionEnumIntOptName(std::string key); + void setOptionEnumInt(std::string key,int val); + void setOptionEnumIntOptName(std::string key,std::string valname); + + void load(const char* path); +private: + std::map options; + std::vector optionlist; + QVariantMap settings; +}; + +#endif diff --git a/visualization/renderer/qmpvisrendercore.cpp b/visualization/renderer/qmpvisrendercore.cpp new file mode 100644 index 0000000..fb1a7ef --- /dev/null +++ b/visualization/renderer/qmpvisrendercore.cpp @@ -0,0 +1,88 @@ +#include "qmpvisrendercore.hpp" +#include "qmppluginapistub.hpp" +#include "qmpsettingsro.hpp" +#include "qmpmidiplay.hpp" +#include "qmpcorepublic.hpp" + +#include +#include + +#include +#include +#include +qmpVisRenderCore *qmpVisRenderCore::inst=nullptr; + +qmpVisRenderCore::qmpVisRenderCore():QObject(nullptr) +{ + inst=this; + player=new CMidiPlayer(); + api=new qmpPluginAPIStub(this); + msettings=new qmpSettingsRO(); + msettings->registerOptionEnumInt("MIDI","Text encoding","Midi/TextEncoding",{"Unicode","Big5","Big5-HKSCS","CP949","EUC-JP","EUC-KR","GB18030","KOI8-R","KOI8-U","Macintosh","Shift-JIS"},0); +} + +void qmpVisRenderCore::loadVisualizationLibrary() +{ + mp=dlopen("libvisualization.so",RTLD_LAZY); + if(!mp)fprintf(stderr,"failed to load visualization module!\n"); + GetInterface_func getintf=reinterpret_cast(dlsym(mp,"qmpPluginGetInterface")); + SwitchMode_func switchmode=reinterpret_cast(dlsym(mp,"switchToRenderMode")); + vf=nullptr; + vp=getintf(api); + switchmode(&qmpVisRenderCore::framefunc,false); + vp->init(); + msettings->load("/home/chrisoft/.config/qmprc"); +} + +void qmpVisRenderCore::unloadVisualizationLibrary() +{ + vp->deinit(); + dlclose(mp); +} + +void qmpVisRenderCore::setMIDIFile(const char *url) +{ + player->playerLoadFile(url); +} + +void qmpVisRenderCore::startRender() +{ + assert(vf); + ffmpegproc=new QProcess(); + ffmpegproc->setProgram("ffmpeg"); + QStringList arguments; + arguments + <<"-f"<<"rawvideo" + <<"-pixel_format"<<"rgba" + <<"-video_size"<<"1600x900" + <<"-framerate"<<"60" + <<"-i"<<"pipe:"; + arguments + <<"-vf"<<"vflip" + <<"-pix_fmt"<<"yuv420p" + <<"-c:v"<<"libx264" + <<"-preset"<<"fast" + <<"-crf"<<"22"; + arguments<<"output.mp4"; + ffmpegproc->setArguments(arguments); + ffmpegproc->start(); + connect(ffmpegproc,QOverload::of(&QProcess::finished), + [this](int x,QProcess::ExitStatus){qDebug("%d",x);qDebug()<ffmpegproc->readAllStandardError();}); + vf->show(); + startcb(nullptr,nullptr); +} + +void qmpVisRenderCore::framefunc(void *px, size_t sz) +{ + if(sz) + { + inst->ffmpegproc->write((const char*)px,sz); + while(inst->ffmpegproc->bytesToWrite()>1<<26) + { + inst->ffmpegproc->waitForBytesWritten(); + QThread::yieldCurrentThread(); + } + } + else + inst->ffmpegproc->closeWriteChannel(); +} diff --git a/visualization/renderer/qmpvisrendercore.hpp b/visualization/renderer/qmpvisrendercore.hpp new file mode 100644 index 0000000..0c024f1 --- /dev/null +++ b/visualization/renderer/qmpvisrendercore.hpp @@ -0,0 +1,44 @@ +#ifndef QMPVISRENDERCORE_HPP +#define QMPVISRENDERCORE_HPP + +#include + +#include +#include "qmpcorepublic.hpp" + +class qmpPluginAPIStub; +class CMidiPlayer; +class qmpSettingsRO; + +class QProcess; + +class qmpVisRenderCore:public QObject +{ + Q_OBJECT +public: + qmpVisRenderCore(); + void loadVisualizationLibrary(); + void unloadVisualizationLibrary(); + void setMIDIFile(const char* url); + void startRender(); + + qmpSettingsRO* settings(){return msettings;} + +private: + qmpPluginIntf *vp; + qmpFuncBaseIntf *vf; + callback_t startcb; + void *mp; + qmpPluginAPIStub *api; + CMidiPlayer *player; + qmpSettingsRO *msettings; + QProcess *ffmpegproc; + typedef qmpPluginIntf*(*GetInterface_func)(qmpPluginAPI*); + typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t),bool hidewindow); + + friend class qmpPluginAPIStub; + static void framefunc(void* px,size_t sz); + static qmpVisRenderCore *inst; +}; + +#endif // QMPVISRENDERCORE_HPP -- cgit v1.2.3