From 65a8bde4e4beca860a13491e94631dc16b5e71af Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Wed, 29 Apr 2020 11:25:58 +0800 Subject: Decouple CMidiPlayer and qmpMidiOutFluid. Visualization renderer should no longer depend on the fluidsynth library. Reworked the "wait voice" option: now it only takes effect if an automatic track switch happens. Actually process events while waiting for async operation to finish. --- core/qmpmidiplay.cpp | 39 ++++----------- core/qmpmidiplay.hpp | 13 ++--- qmidiplayer-desktop/CMakeLists.txt | 1 + qmidiplayer-desktop/qmpefxwindow.cpp | 2 +- qmidiplayer-desktop/qmpmainwindow.cpp | 88 ++++++++++++++++++++++++--------- qmidiplayer-desktop/qmpmainwindow.hpp | 7 ++- qmidiplayer-desktop/qmpplugin.cpp | 4 +- qmidiplayer-desktop/qmppresetselect.cpp | 3 +- 8 files changed, 88 insertions(+), 69 deletions(-) diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index cb9e726..01045c1 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "qmpmidiplay.hpp" #ifdef _WIN32 #define NOMINMAX @@ -40,7 +39,6 @@ bool CMidiPlayer::processEvent(const SEvent *e) { if(rpnid[ch]==0) { - internalFluid->rpnMessage(ch,0,rpnval[ch]<<7); mididev[mappedoutput[ch]].dev->rpnMessage(ch,0,rpnval[ch]<<7); pbr[ch]=rpnval[ch]; } @@ -230,8 +228,8 @@ void CMidiPlayer::playEvents() if(tcstop||!midiReaders)break; ct=getEvent(tceptr)->time; } - while(!tcstop&&(waitvoice&&internalFluid->getPolyphone()>0))std::this_thread::sleep_for(std::chrono::milliseconds(2)); - finished=1; + if(tceptr>=ecnt) + finished=1; } void CMidiPlayer::fileTimer1Pass() { @@ -284,8 +282,7 @@ void CMidiPlayer::fileTimer2Pass() CMidiPlayer::CMidiPlayer() { midiReaders=new CMidiFileReaderCollection(); - resumed=false;midiFile=nullptr;internalFluid=new qmpMidiOutFluid(); - registerMidiOutDevice(internalFluid,"Internal FluidSynth"); + resumed=false;midiFile=nullptr; waitvoice=true; event_handlers_id=event_read_handlers_id=file_read_finish_hooks_id=0; memset(eventHandlerCB,0,sizeof(eventHandlerCB)); @@ -294,8 +291,7 @@ CMidiPlayer::CMidiPlayer() memset(eventReaderCBuserdata,0,sizeof(eventReaderCBuserdata)); memset(fileReadFinishCB,0,sizeof(fileReadFinishCB)); memset(fileReadFinishCBuserdata,0,sizeof(fileReadFinishCBuserdata)); - memset(mappedoutput,0,sizeof(mappedoutput)); - mididev[0].refcnt=16; + memset(mappedoutput,0xff,sizeof(mappedoutput)); memset(chstatus,0,sizeof(chstatus)); for(int i=0;i<16;++i) memset(chstatus[i],0xff,128*sizeof(uint8_t)); @@ -306,12 +302,6 @@ CMidiPlayer::CMidiPlayer() } CMidiPlayer::~CMidiPlayer() { - if(internalFluid) - { - internalFluid->deviceDeinit(); - delete internalFluid; - internalFluid=nullptr; - } if(midiFile)delete midiFile;delete midiReaders; } void CMidiPlayer::playerPanic(bool reset) @@ -356,7 +346,7 @@ bool CMidiPlayer::playerLoadFile(const char* fn) void CMidiPlayer::playerInit() { ctempo=0x7A120;ctsn=4;ctsd=4;cks=0;dpt=ctempo*1000./divs; - tceptr=0;tcstop=0;tcpaused=0;finished=0;mute=solo=0; + tceptr=0;tcstop=false;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)); memset(chstatus,0,sizeof(chstatus)); @@ -365,7 +355,7 @@ void CMidiPlayer::playerInit() } void CMidiPlayer::playerDeinit() { - tceptr=0;tcstop=1;tcpaused=0; + tceptr=0;tcstop=true;tcpaused=0; delete midiFile;midiFile=nullptr; } void CMidiPlayer::playerThread() @@ -380,7 +370,7 @@ uint32_t CMidiPlayer::getTCeptr(){return tceptr;} void CMidiPlayer::setTCeptr(uint32_t ep,uint32_t st) { resumed=true; - if(ep==ecnt)tcstop=1;else tceptr=ep; + if(ep==ecnt)tcstop=true;else tceptr=ep; for(int i=0;i<16;++i) { qmpMidiOutDevice* dest=mididev[mappedoutput[i]].dev; @@ -388,16 +378,12 @@ void CMidiPlayer::setTCeptr(uint32_t ep,uint32_t st) { if(~ccstamps[st][i][j]) { - internalFluid->basicMessage(0xB0|i,j,ccstamps[st][i][j]); dest->basicMessage(0xB0|i,j,ccstamps[st][i][j]); chstatus[i][j]=ccstamps[st][i][j]; } } - internalFluid->basicMessage(0xC0|i,ccstamps[st][i][128],0); dest->basicMessage(0xC0|i,ccstamps[st][i][128],0); chstatus[i][128]=ccstamps[st][i][128]; - //fluid_synth_pitch_bend(synth,i,ccstamps[st][i][130]); - internalFluid->rpnMessage(i,0,ccstamps[st][i][134]<<7); dest->rpnMessage(i,0,ccstamps[st][i][134]<<7); pbr[i]=ccstamps[st][i][134]; ctempo=ccstamps[st][0][131];dpt=ctempo*1000./divs; @@ -421,13 +407,8 @@ double CMidiPlayer::getPitchBend(int ch){return((int)pbv[ch]-8192)/8192.*pbr[ch] uint32_t CMidiPlayer::getTCpaused(){return tcpaused;} void CMidiPlayer::setTCpaused(uint32_t ps){tcpaused=ps;} uint32_t CMidiPlayer::isFinished(){return finished;} +bool CMidiPlayer::stopFlag(){return tcstop;} void CMidiPlayer::setResumed(){resumed=true;} -void CMidiPlayer::setWaitVoice(bool wv){waitvoice=wv;} - -void CMidiPlayer::registerFluidOptions(qmpPluginAPI *coreapi) -{ - internalFluid->registerOptions(coreapi); -} void CMidiPlayer::setChannelPreset(int ch,int b,int p) { @@ -471,8 +452,6 @@ void CMidiPlayer::setCC(int ch,int id,int val) mididev[mappedoutput[ch]].dev->basicMessage(0xB0|ch,id,val); } -qmpMidiOutFluid* CMidiPlayer::fluid(){return internalFluid;} - void CMidiPlayer::registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name) { SMidiDev d; @@ -508,6 +487,8 @@ qmpMidiOutDevice* CMidiPlayer::getChannelOutputDevice(int ch) void CMidiPlayer::setChannelOutput(int ch,int outid) { int origoutput=mappedoutput[ch]; + if(origoutput==outid) + return; SMidiDev& dnew=mididev[outid]; dnew.dev->onMapped(ch,++dnew.refcnt); for(int i=0;i<124;++i) diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index f896338..83a7128 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -9,9 +9,7 @@ #include #include #define QMP_MAIN -#include "../include/qmpcorepublic.hpp" -#include "qmpmidioutrtmidi.hpp" -#include "qmpmidioutfluid.hpp" +#include "qmpcorepublic.hpp" class CMidiPlayer; class CSMFReader:public qmpFileReader { @@ -66,12 +64,12 @@ class CMidiPlayer double ftime; bool sendSysEx,waitvoice; uint8_t chstatus[16][130];//0..127: cc 128: pc - qmpMidiOutFluid* internalFluid; 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 tceptr,tcpaused,ct; + bool tcstop; uint32_t finished,resumed; uint32_t pbr[16],pbv[16]; //playback correction @@ -123,9 +121,8 @@ class CMidiPlayer uint32_t getTCpaused(); void setTCpaused(uint32_t ps); uint32_t isFinished(); + bool stopFlag(); void setResumed(); - void setWaitVoice(bool wv); - void registerFluidOptions(qmpPluginAPI *coreapi); double getFtime(); void getCurrentTimeSignature(int *n,int *d); @@ -150,8 +147,6 @@ class CMidiPlayer uint16_t getCC(int ch,int id); void setCC(int ch,int id,int val); - qmpMidiOutFluid* fluid(); - void registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name); void unregisterMidiOutDevice(std::string name); std::vector getMidiOutDevices(); diff --git a/qmidiplayer-desktop/CMakeLists.txt b/qmidiplayer-desktop/CMakeLists.txt index c34f258..419bb05 100644 --- a/qmidiplayer-desktop/CMakeLists.txt +++ b/qmidiplayer-desktop/CMakeLists.txt @@ -58,6 +58,7 @@ set(CMAKE_AUTOUIC ON) include_directories(${fluidsynth_INCLUDE_DIRS}) include_directories(${rtmidi_INCLUDE_DIRS}) include_directories(${PROJECT_SOURCE_DIR}/include/) +include_directories(${PROJECT_SOURCE_DIR}/core/) cmake_host_system_information(RESULT build_host QUERY HOSTNAME) add_definitions(-DBUILD_MACHINE=${build_host}) diff --git a/qmidiplayer-desktop/qmpefxwindow.cpp b/qmidiplayer-desktop/qmpefxwindow.cpp index f8731ba..7691756 100644 --- a/qmidiplayer-desktop/qmpefxwindow.cpp +++ b/qmidiplayer-desktop/qmpefxwindow.cpp @@ -103,7 +103,7 @@ void qmpEfxWindow::sendEfxChange(void *_fs) cfb=ui->sbFeedBack->value();cl=ui->sbLevelC->value()/100.; cr=ui->sbRate->value();cd=ui->sbDepth->value(); IFluidSettings* fs=(IFluidSettings*)_fs; - if(!_fs)fs=qmpMainWindow::getInstance()->getPlayer()->fluid(); + if(!_fs)fs=qmpMainWindow::getInstance()->getFluid(); fs->setReverbPara(ui->cbEnabledR->isChecked()?1:0,rr,rd,rw,rl); fs->setChorusPara(ui->cbEnabledC->isChecked()?1:0,cfb,cl,cr,cd,ct); diff --git a/qmidiplayer-desktop/qmpmainwindow.cpp b/qmidiplayer-desktop/qmpmainwindow.cpp index 50c92b4..ea90493 100644 --- a/qmidiplayer-desktop/qmpmainwindow.cpp +++ b/qmidiplayer-desktop/qmpmainwindow.cpp @@ -67,6 +67,8 @@ qmpMainWindow::~qmpMainWindow() delete renderf;renderf=nullptr; delete reloadsynf;reloadsynf=nullptr; if(player)delete player; + internalfluid->deviceDeinit(); + delete internalfluid; delete ui; } @@ -82,17 +84,24 @@ void qmpMainWindow::init() [this] { player=new CMidiPlayer(); + internalfluid=new qmpMidiOutFluid(); + player->registerMidiOutDevice(internalfluid,"Internal FluidSynth"); reloadsynf=new qmpReloadSynthFunc(this); - player->registerFluidOptions(pmgr->pluginAPI); - playerSetup(player->fluid()); - player->fluid()->deviceInit(); - loadSoundFont(player->fluid()); + + internalfluid->registerOptions(pmgr->pluginAPI); + playerSetup(internalfluid); + internalfluid->deviceInit(); + loadSoundFont(internalfluid); + for(int i=0;i<16;++i) + player->setChannelOutput(i,0); + auto rtdev=qmpRtMidiManager::getDevices(); for(auto &i:rtdev) player->registerMidiOutDevice(i.first,i.second); } ); - while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout); + while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout) + QApplication::processEvents(); ui->centralWidget->setEnabled(true); settingsw->registerSoundFontOption(); @@ -231,7 +240,14 @@ void qmpMainWindow::updateWidgets() timer->stop();stopped=true;playing=false; invokeCallback("main.stop",nullptr); setFuncEnabled("Render",stopped);setFuncEnabled("ReloadSynth",stopped); - player->playerDeinit();playerTh->join(); + player->playerDeinit(); + auto f=std::async([this]{playerTh->join();}); + while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout) + { + QApplication::processEvents(); + ui->lbCurPoly->setText(QString("%1").arg(internalfluid->getPolyphone(),5,10,QChar('0'))); + ui->lbMaxPoly->setText(QString("%1").arg(internalfluid->getMaxPolyphone(),5,10,QChar('0'))); + } delete playerTh;playerTh=nullptr; player->playerPanic(true); chnlw->on_pbUnmute_clicked();chnlw->on_pbUnsolo_clicked(); @@ -241,7 +257,7 @@ void qmpMainWindow::updateWidgets() ui->lbCurTime->setText("00:00"); } else - switchTrack(plistw->getNextItem()); + switchTrack(plistw->getNextItem(),false); } if(renderTh) { @@ -264,20 +280,35 @@ void qmpMainWindow::updateWidgets() char ts[100]; sprintf(ts,"%02d:%02d",(int)(elapsed.count()+offset)/60,(int)(elapsed.count()+offset)%60); ui->lbCurTime->setText(ts); - ui->lbCurPoly->setText(QString("%1").arg(player->fluid()->getPolyphone(),5,10,QChar('0'))); - ui->lbMaxPoly->setText(QString("%1").arg(player->fluid()->getMaxPolyphone(),5,10,QChar('0'))); + ui->lbCurPoly->setText(QString("%1").arg(internalfluid->getPolyphone(),5,10,QChar('0'))); + ui->lbMaxPoly->setText(QString("%1").arg(internalfluid->getMaxPolyphone(),5,10,QChar('0'))); } } QString qmpMainWindow::getFileName(){return ui->lbFileName->text();} -void qmpMainWindow::switchTrack(QString s) +void qmpMainWindow::switchTrack(QString s,bool interrupt) { stopped=false;playing=true; setFuncEnabled("Render",stopped);setFuncEnabled("ReloadSynth",stopped); ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/pause.svg"))); - timer->stop();player->playerDeinit(); + if(interrupt) + { + player->playerDeinit(); + player->playerPanic(); + } invokeCallback("main.stop",nullptr); - if(playerTh){playerTh->join();delete playerTh;playerTh=nullptr;} + if(playerTh) + { + auto f=std::async([this]{playerTh->join();}); + while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout) + { + QApplication::processEvents(); + ui->lbCurPoly->setText(QString("%1").arg(internalfluid->getPolyphone(),5,10,QChar('0'))); + ui->lbMaxPoly->setText(QString("%1").arg(internalfluid->getMaxPolyphone(),5,10,QChar('0'))); + } + delete playerTh;playerTh=nullptr; + } + timer->stop(); player->playerPanic(true); ui->hsTimer->setValue(0); chnlw->on_pbUnmute_clicked();chnlw->on_pbUnsolo_clicked(); @@ -290,9 +321,13 @@ void qmpMainWindow::switchTrack(QString s) ui->lbFinTime->setText(ts); player->playerInit(); invokeCallback("main.start",nullptr); - player->fluid()->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); - player->setWaitVoice(settings->getOptionBool("Midi/WaitVoice")); - playerTh=new std::thread(&CMidiPlayer::playerThread,player); + internalfluid->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); + playerTh=new std::thread([this]{ + player->playerThread(); + if(settings->getOptionBool("Midi/WaitVoice")&&player->isFinished()) + while(internalfluid->getPolyphone()>0) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }); #ifdef _WIN32 SetThreadPriority((void*)playerTh->native_handle(),THREAD_PRIORITY_TIME_CRITICAL); #endif @@ -415,9 +450,13 @@ void qmpMainWindow::on_pbPlayPause_clicked() ui->lbFinTime->setText(ts); player->playerInit(); invokeCallback("main.start",nullptr); - player->fluid()->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); - player->setWaitVoice(settings->getOptionBool("Midi/WaitVoice")); - playerTh=new std::thread(&CMidiPlayer::playerThread,player); + internalfluid->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); + playerTh=new std::thread([this]{ + player->playerThread(); + if(settings->getOptionBool("Midi/WaitVoice")&&player->isFinished()) + while(internalfluid->getPolyphone()>0) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }); #ifdef _WIN32 SetThreadPriority((void*)playerTh->native_handle(),THREAD_PRIORITY_TIME_CRITICAL); #endif @@ -498,7 +537,7 @@ void qmpMainWindow::playerSeek(uint32_t percentage) void qmpMainWindow::on_vsMasterVol_valueChanged() { - if(!stopped)player->fluid()->setGain(ui->vsMasterVol->value()/250.); + if(!stopped)internalfluid->setGain(ui->vsMasterVol->value()/250.); settings->setOptionRaw("FluidSynth/Gain",ui->vsMasterVol->value()); } @@ -642,13 +681,14 @@ void qmpMainWindow::reloadSynth() std::future f=std::async(std::launch::async, [this] { - player->fluid()->deviceDeinit(true); - playerSetup(player->fluid()); - player->fluid()->deviceInit(); - loadSoundFont(player->fluid()); + internalfluid->deviceDeinit(true); + playerSetup(internalfluid); + internalfluid->deviceInit(); + loadSoundFont(internalfluid); } ); - while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout); + while(f.wait_for(std::chrono::milliseconds(100))==std::future_status::timeout) + QApplication::processEvents(); ui->centralWidget->setEnabled(true); } diff --git a/qmidiplayer-desktop/qmpmainwindow.hpp b/qmidiplayer-desktop/qmpmainwindow.hpp index 700b501..d98029d 100644 --- a/qmidiplayer-desktop/qmpmainwindow.hpp +++ b/qmidiplayer-desktop/qmpmainwindow.hpp @@ -20,7 +20,8 @@ #include #include #include -#include "../core/qmpmidiplay.hpp" +#include "qmpmidioutfluid.hpp" +#include "qmpmidiplay.hpp" #include "qmpplugin.hpp" #include "qmpplistwindow.hpp" #include "qmpchannelswindow.hpp" @@ -141,11 +142,12 @@ class qmpMainWindow:public QMainWindow void dragEnterEvent(QDragEnterEvent *event); ~qmpMainWindow(); CMidiPlayer* getPlayer(){return player;} + qmpMidiOutFluid* getFluid(){return internalfluid;} qmpSettings* getSettings(){return settings.get();} QTimer* getTimer(){return timer;} bool isFinalizing(){return fin;} QString getFileName(); - void switchTrack(QString s); + void switchTrack(QString s,bool interrupt=true); std::string getTitle(); std::wstring getWTitle(); uint32_t getPlaybackPercentage(); @@ -193,6 +195,7 @@ class qmpMainWindow:public QMainWindow std::chrono::steady_clock::time_point st; double offset; CMidiPlayer *player; + qmpMidiOutFluid *internalfluid; qmpFileRendererFluid *fluidrenderer; qmpPluginManager *pmgr; QPointer plistw; diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp index 8a156ba..6e53302 100644 --- a/qmidiplayer-desktop/qmpplugin.cpp +++ b/qmidiplayer-desktop/qmpplugin.cpp @@ -144,9 +144,9 @@ uint32_t qmpPluginAPI::getNoteCount() uint32_t qmpPluginAPI::getMaxTick() {return qmw&&qmw->getPlayer()?qmw->getPlayer()->getMaxTick():0;} uint32_t qmpPluginAPI::getCurrentPolyphone() -{return qmw&&qmw->getPlayer()?qmw->getPlayer()->fluid()->getPolyphone():0;} +{return qmw&&qmw->getPlayer()?qmw->getFluid()->getPolyphone():0;} uint32_t qmpPluginAPI::getMaxPolyphone() -{return qmw&&qmw->getPlayer()?qmw->getPlayer()->fluid()->getMaxPolyphone():0;} +{return qmw&&qmw->getPlayer()?qmw->getFluid()->getMaxPolyphone():0;} uint32_t qmpPluginAPI::getCurrentTimeStamp() {return qmw&&qmw->getPlayer()?qmw->getPlayer()->getTick():0;} uint32_t qmpPluginAPI::getCurrentPlaybackPercentage() diff --git a/qmidiplayer-desktop/qmppresetselect.cpp b/qmidiplayer-desktop/qmppresetselect.cpp index 52b0b66..821eced 100644 --- a/qmidiplayer-desktop/qmppresetselect.cpp +++ b/qmidiplayer-desktop/qmppresetselect.cpp @@ -18,8 +18,7 @@ qmpPresetSelector::~qmpPresetSelector() void qmpPresetSelector::showEvent(QShowEvent *e) { memset(presets,0,sizeof(presets)); - CMidiPlayer *plyr=qmpMainWindow::getInstance()->getPlayer(); - if(!plyr->fluid()->getSFCount())return e->ignore(); + if(!qmpMainWindow::getInstance()->getFluid()->getSFCount())return e->ignore(); ui->lwBankSelect->clear(); ui->lwPresetSelect->clear(); e->accept(); -- cgit v1.2.3