From d985112164bd2bfeca0c895abb523d45f0e065bb Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Wed, 18 Oct 2017 11:40:51 +0800 Subject: API changes and crash fixes. Details in the changelog. --- ChangeLog | 10 +++ core/qmpmidioutrtmidi.cpp | 2 +- core/qmpmidiplay.cpp | 10 +-- core/qmpmidiplay.hpp | 24 +++---- core/qmpmidiread.cpp | 4 +- doc/APIdoc.md | 111 +++++++++++++++++++++++------- doc/miscellaneous.html | 11 +++ include/qmpcorepublic.hpp | 43 +++++------- midifmt-plugin/midifmtplugin.cpp | 2 +- midifmt-plugin/midifmtplugin.hpp | 2 +- qmidiplayer-desktop/main.cpp | 2 + qmidiplayer-desktop/qmpchannelswindow.hpp | 2 +- qmidiplayer-desktop/qmpmainwindow.cpp | 84 ++++++++++++++-------- qmidiplayer-desktop/qmpmainwindow.hpp | 34 +++++++-- qmidiplayer-desktop/qmpplugin.cpp | 18 ++--- qmidiplayer-desktop/qmpsettingswindow.cpp | 6 +- qmidiplayer-lite/main.qml | 14 ++-- visualization/qmpvisualization.cpp | 43 +++++++++--- visualization/qmpvisualization.hpp | 32 +++------ 19 files changed, 297 insertions(+), 157 deletions(-) diff --git a/ChangeLog b/ChangeLog index 55d3b5e..a46c569 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2017-10-18 0.8.6 alpha +API changes: + - Lots of renaming. + - qmpVisualizationIntf no longer exists. Visualization + is now just a normal functionality. + - Added UI Hooks. +Fixed a bunch of crashes and insanities in the +visualization plugin. +I think it's now time for 0.8.6 alpha. + 2017-08-22 0.8.6 indev Windows type of functionality windows is now Qt::Dialog. Initialization fluidsynth asynchronously. diff --git a/core/qmpmidioutrtmidi.cpp b/core/qmpmidioutrtmidi.cpp index 7f75fda..1b897df 100644 --- a/core/qmpmidioutrtmidi.cpp +++ b/core/qmpmidioutrtmidi.cpp @@ -99,7 +99,7 @@ void qmpRtMidiManager::createDevices() catch(RtMidiError &e) { printf("Failed to initialize the dummy device: %s\n",e.what()); - dummy=NULL; + dummy=NULL;return; } for(unsigned i=0;igetPortCount();++i) devices.push_back(std::make_pair(new qmpMidiOutRtMidi(i),dummy->getPortName(i))); diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index 55b3959..15e1331 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -276,7 +276,7 @@ bool CMidiPlayer::playerLoadFile(const char* fn) { notes=0;if(midiFile)delete midiFile; midiFile=midiReaders->readFile(fn); - if(!midiFile->valid)return false; + if(!midiFile||!midiFile->valid)return false; divs=midiFile->divs;maxtk=ecnt=0; for(CMidiTrack& i:midiFile->tracks) { @@ -481,7 +481,7 @@ void CMidiPlayer::setChannelOutput(int ch,int outid) } } uint8_t* CMidiPlayer::getChstates(){return chstate;} -int CMidiPlayer::setEventHandlerCB(IMidiCallBack *cb,void *userdata) +int CMidiPlayer::setEventHandlerCB(ICallBack *cb,void *userdata) { for(int i=0;i<16;++i) { @@ -496,7 +496,7 @@ int CMidiPlayer::setEventHandlerCB(IMidiCallBack *cb,void *userdata) } void CMidiPlayer::unsetEventHandlerCB(int id) {eventHandlerCB[id]=NULL;eventHandlerCBuserdata[id]=NULL;} -int CMidiPlayer::setEventReaderCB(IMidiCallBack *cb,void *userdata) +int CMidiPlayer::setEventReaderCB(ICallBack *cb,void *userdata) { for(int i=0;i<16;++i) { @@ -511,7 +511,7 @@ int CMidiPlayer::setEventReaderCB(IMidiCallBack *cb,void *userdata) } void CMidiPlayer::unsetEventReaderCB(int id) {eventReaderCB[id]=NULL;eventReaderCBuserdata[id]=NULL;} -int CMidiPlayer::setFileReadFinishedCB(IMidiCallBack *cb,void *userdata) +int CMidiPlayer::setFileReadFinishedCB(ICallBack *cb,void *userdata) { for(int i=0;i<16;++i) { @@ -526,7 +526,7 @@ int CMidiPlayer::setFileReadFinishedCB(IMidiCallBack *cb,void *userdata) } void CMidiPlayer::unsetFileReadFinishedCB(int id) {fileReadFinishCB[id]=NULL;fileReadFinishCBuserdata[id]=NULL;} -void CMidiPlayer::registerReader(IMidiFileReader *reader,std::string name) +void CMidiPlayer::registerReader(qmpFileReader *reader,std::string name) {midiReaders->registerReader(reader,name);} void CMidiPlayer::unregisterReader(std::string name) {midiReaders->unregisterReader(name);} diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index c935084..99e0d07 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -10,7 +10,7 @@ #include "qmpmidioutrtmidi.hpp" #include "qmpmidioutfluid.hpp" class CMidiPlayer; -class CSMFReader:public IMidiFileReader +class CSMFReader:public qmpFileReader { private: CMidiFile* ret; @@ -37,15 +37,15 @@ class CSMFReader:public IMidiFileReader }; class CMidiFileReaderCollection{ private: - std::vector> readers; - IMidiFileReader* currentReader; + std::vector> readers; + qmpFileReader* currentReader; public: CMidiFileReaderCollection(); ~CMidiFileReaderCollection(); - void registerReader(IMidiFileReader* reader,std::string name); + void registerReader(qmpFileReader* reader,std::string name); void unregisterReader(std::string name); CMidiFile* readFile(const char* fn); - IMidiFileReader* getCurrentReader(); + qmpFileReader* getCurrentReader(); }; class CMidiPlayer { @@ -77,9 +77,9 @@ class CMidiPlayer }; std::vector mididev; int mappedoutput[16]; - IMidiCallBack* eventHandlerCB[16]; - IMidiCallBack* eventReaderCB[16]; - IMidiCallBack* fileReadFinishCB[16]; + ICallBack* eventHandlerCB[16]; + ICallBack* eventReaderCB[16]; + ICallBack* fileReadFinishCB[16]; void* eventHandlerCBuserdata[16]; void* eventReaderCBuserdata[16]; void* fileReadFinishCBuserdata[16]; @@ -145,13 +145,13 @@ class CMidiPlayer int getChannelOutput(int ch); void setChannelOutput(int ch,int outid); uint8_t* getChstates(); - int setEventHandlerCB(IMidiCallBack *cb,void *userdata); + int setEventHandlerCB(ICallBack *cb,void *userdata); void unsetEventHandlerCB(int id); - int setEventReaderCB(IMidiCallBack *cb,void *userdata); + int setEventReaderCB(ICallBack *cb,void *userdata); void unsetEventReaderCB(int id); - int setFileReadFinishedCB(IMidiCallBack *cb,void *userdata); + int setFileReadFinishedCB(ICallBack *cb,void *userdata); void unsetFileReadFinishedCB(int id); - void registerReader(IMidiFileReader* reader,std::string name); + void registerReader(qmpFileReader* reader,std::string name); void unregisterReader(std::string name); void callEventReaderCB(SEventCallBackData d); diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp index 6089ac5..42cb88a 100644 --- a/core/qmpmidiread.cpp +++ b/core/qmpmidiread.cpp @@ -250,7 +250,7 @@ CMidiFileReaderCollection::~CMidiFileReaderCollection() { delete readers[0].first; } -void CMidiFileReaderCollection::registerReader(IMidiFileReader* reader,std::string name) +void CMidiFileReaderCollection::registerReader(qmpFileReader* reader,std::string name) { for(unsigned i=0;i eventList` Vector of SEvent's. @@ -152,12 +168,14 @@ Vector of SEvent's. Append an event to the end of the event list. - `SEvent& operator[](size_t sub)` Get the reference to the contained event with the given index. +** Check the validity of the index first! This operator doesn't do the check. ** ### class `CMidiFile` Describes a MIDI file. A MIDI file consists of multiple MIDI tracks. - `bool valid` -Is the MIDI file a valid one? +Is the MIDI file a valid one? If your file reader encountered an unrecognised +file, set it to false. - `char* title` Title of the MIDI file. - `char* copyright` @@ -176,30 +194,30 @@ Ticks per quarter note. SMTPE format is not supported by QMidiPlayer. - `~CMidiFile()` Frees memory occupied by the title and copyright string. -### class `IMidiCallBack` +### class `ICallBack` Generic callback function that can be used for hooking the core. -- `IMidiCallBack()` +- `ICallBack()` Default empty constructor. - `virtual void callBack(void* callerdata,void* userdata)=0;` Abstract function of the callback. callerdata is filled by the caller and userdata is set to whatever you asked for when registering the callback. -- `virtual ~IMidiCallBack()` +- `virtual ~ICallBack()` Virtual empty destructor. -### class `IMidiFileReader` +### class `qmpFileReader` MIDI file reader interface. Use this to implement your file importer. -- `IMidiFileReader()` +- `qmpFileReader()` Default empty constructor. -- `virtual ~IMidiFileReader()` +- `virtual ~qmpFileReader()` Virtual empty destructor. - `virtual CMidiFile* readFile(const char* fn)=0` Abstract function to be implemented by the plugin. This function should read file from the given path (fn) and return a -pointer to the resulting CMidiFile structure. You shoudn't handle the -destruction of the resulting structure as the core will handle it. -Read 5.1 for more details. +pointer to the resulting CMidiFile structure. Don't handle the +destruction of the resulting structure as the core will do it. +Read section 5.1 for more details. After reading each event, you should call qmpPluginAPI::callEventReaderCB to invoke event reader callbacks and process their requests. If a file not supported by the reader is provided, this function should @@ -209,9 +227,52 @@ Only called by event reader callbacks. Expected behavior: Sets the discard flag for the last event you have read. If an event has its discard flag set, it shouldn't be pushed into its track. discardCurrentEvent may be called multiple times by different event reader -callbacks. In such case, there's still only one event discarded. +callbacks. In such case, only the current event is discarded. - `virtual void commitEventChange(SEventCallBackData d)=0` Only called by event reader callbacks. Expected behavior: modifies the last event you have read to the provided data. commitEventChange may be called multiple times by different event reader callbacks. In such case, only the last modification to the current event counts. + +### class `qmpFuncBaseIntf` +This is the base class for all QMP functionalities. + +- `qmpFuncBaseIntf()` +Default empty constructor. +- `virtual ~qmpFuncBaseIntf()` +Virtual empty destructor. +- `virtual void show()=0` +Called by the core when the functionality is requested to show up. +- `virtual void close()=0` +Called by the core when the functionality is requested to be closed. +Never called for a functionality whose `checkable` attribute is registered as `false`. + +### class `qmpMidiOutDevice` +Interface for a midi output device. + +- `qmpMidiOutDevice()` +Default empty constructor. +- `virtual ~qmpMidiOutDevice()` +Virtual empty destructor. +- `virtual void deviceInit()=0` +Initializes the device. +- `virtual void deviceDeinit()=0` +Deinitializes the device. +- `virtual void basicMessage(uint8_t type,uint8_t p1,uint8_t p2)=0` +Sends a basic midi message. +Note that the core doesn't make use of running status. +- `virtual void extendedMessage(uint8_t length,const char* data)=0` +Sends an extended message. ("system exclusive message"). +- `virtual void rpnMessage(uint8_t ch,uint16_t type,uint16_t val)=0` +Sends a registered parameter number controller message. +- `virtual void nrpnMessage(uint8_t ch,uint16_t type,uint16_t val)=0` +Sends a non-registered parameter number controller message. +- `virtual void panic(uint8_t ch)=0` +Stops all voices on a channel. +- `virtual void reset(uint8_t ch)=0` +Reset the status of a channel. +- `virtual void onMapped(uint8_t ch,int refcnt)=0` +Called when the device is mapped. `ch` is the channel it is mapped to. +`refcnt` is the number of channels mapped to this device after the remapping. +- `virtual void onUnmapped(uint8_t ch,int refcnt)=0` +Called when the device is mapped. Parameters are the sams as `onMapped()`. diff --git a/doc/miscellaneous.html b/doc/miscellaneous.html index d8e90c3..7f46d33 100644 --- a/doc/miscellaneous.html +++ b/doc/miscellaneous.html @@ -55,6 +55,13 @@ Please download and install Visual Studio 2015 Runtime Library.

+

+ Nighly builds are compiled with MSVC2017 (using Qt MSVC2015 x86, though). So you need two runtime libraries + to run it. Damn. +

+

+ Anyway, you are in the DLL Hell(TM), specific to Microsoft Windows(R). +

1. Can I help translate this program?

Of course! Please make a pull request or contact me. @@ -76,6 +83,10 @@

  • Windows 10 Insider Pro on the ThinkPad X1 yoga mentioned above.
+

+

4. The code is full of shit and has no comment at all!

+

+ I know. I got pissed off for that everytime.

Contact the author

diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp index 3607976..0994865 100644 --- a/include/qmpcorepublic.hpp +++ b/include/qmpcorepublic.hpp @@ -8,7 +8,7 @@ #else #define EXPORTSYM __attribute__ ((visibility ("default"))) #endif -#define QMP_PLUGIN_API_REV "1+indev5" +#define QMP_PLUGIN_API_REV "1+indev7" //MIDI Event structure struct SEvent { @@ -53,19 +53,21 @@ class CMidiFile{ }; //Generic callback function that can be used for hooking the core. //"userdata" is set when you register the callback function. -class IMidiCallBack +class ICallBack { public: - IMidiCallBack(){} + ICallBack(){} virtual void callBack(void* callerdata,void* userdata)=0; - virtual ~IMidiCallBack(){} + virtual ~ICallBack(){} }; +//alternative callback function type +typedef void (*callback_t)(void*,void*); //MIDI file reader interface. Use this to implement your file importer. -class IMidiFileReader +class qmpFileReader { public: - IMidiFileReader(){} - virtual ~IMidiFileReader(){} + qmpFileReader(){} + virtual ~qmpFileReader(){} virtual CMidiFile* readFile(const char* fn)=0; virtual void discardCurrentEvent()=0; virtual void commitEventChange(SEventCallBackData d)=0; @@ -79,20 +81,6 @@ class qmpFuncBaseIntf virtual void close()=0; virtual ~qmpFuncBaseIntf(){} }; -//Visualization plugin interface. If your plugin implements a visualization, -//you should implement this pinterface. -class qmpVisualizationIntf:public qmpFuncBaseIntf -{ - public: - qmpVisualizationIntf(){} - virtual void show()=0; - virtual void close()=0; - virtual void start()=0; - virtual void stop()=0; - virtual void pause()=0; - virtual void reset()=0; - virtual ~qmpVisualizationIntf(){} -}; //Midi mapper plugin interface. class qmpMidiOutDevice { @@ -166,17 +154,18 @@ class qmpPluginAPI virtual void registerFunctionality(qmpFuncBaseIntf* i,std::string name,std::string desc,const char* icon,int iconlen,bool checkable); virtual void unregisterFunctionality(std::string name); - virtual void registerVisualizationIntf(qmpVisualizationIntf* intf,std::string name,std::string desc,const char* icon,int iconlen); - virtual void unregisterVisualizationIntf(std::string name); + virtual int registerUIHook(std::string e,ICallBack* cb,void* userdat); + virtual int registerUIHook(std::string e,callback_t cb,void* userdat); + virtual void unregisterUIHook(std::string e,int hook); virtual void registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name); virtual void unregisterMidiOutDevice(std::string name); - virtual int registerEventReaderIntf(IMidiCallBack* cb,void* userdata); + virtual int registerEventReaderIntf(ICallBack* cb,void* userdata); virtual void unregisterEventReaderIntf(int intfhandle); - virtual int registerEventHandlerIntf(IMidiCallBack* cb,void* userdata); + virtual int registerEventHandlerIntf(ICallBack* cb,void* userdata); virtual void unregisterEventHandlerIntf(int intfhandle); - virtual int registerFileReadFinishedHandlerIntf(IMidiCallBack* cb,void* userdata); + virtual int registerFileReadFinishedHandlerIntf(ICallBack* cb,void* userdata); virtual void unregisterFileReadFinishedHandlerIntf(int intfhandle); - virtual void registerFileReader(IMidiFileReader* reader,std::string name); + virtual void registerFileReader(qmpFileReader* reader,std::string name); virtual void unregisterFileReader(std::string name); //if desc=="", the option won't be visible in the settings form. diff --git a/midifmt-plugin/midifmtplugin.cpp b/midifmt-plugin/midifmtplugin.cpp index a025d29..1882fe4 100644 --- a/midifmt-plugin/midifmtplugin.cpp +++ b/midifmt-plugin/midifmtplugin.cpp @@ -107,4 +107,4 @@ void qmpMidiFmtPlugin::deinit() const char* qmpMidiFmtPlugin::pluginGetName() {return "QMidiPlayer extra midi formats plugin";} const char* qmpMidiFmtPlugin::pluginGetVersion() -{return "0.8.5";} +{return "0.8.6";} diff --git a/midifmt-plugin/midifmtplugin.hpp b/midifmt-plugin/midifmtplugin.hpp index f6284dd..05c96ea 100644 --- a/midifmt-plugin/midifmtplugin.hpp +++ b/midifmt-plugin/midifmtplugin.hpp @@ -4,7 +4,7 @@ #include #include "../include/qmpcorepublic.hpp" -class CMidiStreamReader:public IMidiFileReader +class CMidiStreamReader:public qmpFileReader { private: CMidiFile* ret; diff --git a/qmidiplayer-desktop/main.cpp b/qmidiplayer-desktop/main.cpp index 2ac275e..c7167d0 100644 --- a/qmidiplayer-desktop/main.cpp +++ b/qmidiplayer-desktop/main.cpp @@ -23,6 +23,8 @@ int main(int argc,char **argv) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication a(argc,argv); QTranslator qtTranslator; qtTranslator.load("qt_"+QLocale::system().name(), diff --git a/qmidiplayer-desktop/qmpchannelswindow.hpp b/qmidiplayer-desktop/qmpchannelswindow.hpp index c7630fe..008598c 100644 --- a/qmidiplayer-desktop/qmpchannelswindow.hpp +++ b/qmidiplayer-desktop/qmpchannelswindow.hpp @@ -59,7 +59,7 @@ class QDCComboBox:public QComboBox void indexChangedSlot(int idx){emit(onChange(id,idx));} }; -class qmpCWNoteOnCB:public QObject,public IMidiCallBack +class qmpCWNoteOnCB:public QObject,public ICallBack { Q_OBJECT public: diff --git a/qmidiplayer-desktop/qmpmainwindow.cpp b/qmidiplayer-desktop/qmpmainwindow.cpp index b6e1267..dbec402 100644 --- a/qmidiplayer-desktop/qmpmainwindow.cpp +++ b/qmidiplayer-desktop/qmpmainwindow.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -22,19 +23,6 @@ char* wcsto8bit(const wchar_t* s) WideCharToMultiByte(CP_OEMCP,WC_NO_BEST_FIT_CHARS,s,-1,c,size,0,0); return c; } -#define LOAD_FILE \ - {\ - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->reset();\ - char* c=wcsto8bit(fns.toStdWString().c_str());\ - if(!player->playerLoadFile(c)){free(c);QMessageBox::critical(this,tr("Error"),tr("%1 is not a valid midi file.").arg(fns));return;}\ - free(c);\ - } -#else -#define LOAD_FILE \ - {\ - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->reset();\ - if(!player->playerLoadFile(fns.toStdString().c_str())){QMessageBox::critical(this,tr("Error"),tr("%1 is not a valid midi file.").arg(fns));return;}\ - } #endif #define UPDATE_INTERVAL 66 @@ -240,7 +228,7 @@ void qmpMainWindow::updateWidgets() if(!plistw->getRepeat()) { timer->stop();stopped=true;playing=false; - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->stop(); + invokeCallback("main.stop",NULL); setFuncEnabled("Render",stopped);setFuncEnabled("ReloadSynth",stopped); chnlw->resetAcitivity(); player->playerDeinit();playerTh->join(); @@ -288,7 +276,7 @@ void qmpMainWindow::switchTrack(QString s) setFuncEnabled("Render",stopped);setFuncEnabled("ReloadSynth",stopped); ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/pause.svg"))); timer->stop();player->playerDeinit(); - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->stop(); + invokeCallback("main.stop",NULL); if(playerTh){playerTh->join();delete playerTh;playerTh=NULL;} player->playerPanic(true); ui->hsTimer->setValue(0); @@ -296,12 +284,12 @@ void qmpMainWindow::switchTrack(QString s) QString fns=s;setWindowTitle(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.'))+" - QMidiPlayer"); ui->lbFileName->setText(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.'))); onfnChanged(); - LOAD_FILE; + if(!loadFile(fns))return; char ts[100]; sprintf(ts,"%02d:%02d",(int)player->getFtime()/60,(int)player->getFtime()%60); ui->lbFinTime->setText(ts); player->playerInit(); - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->start(); + invokeCallback("main.start",NULL); player->fluid()->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); player->setWaitVoice(qmpSettingsWindow::getSettingsIntf()->value("Midi/WaitVoice",1).toInt()); playerTh=new std::thread(&CMidiPlayer::playerThread,player); @@ -379,6 +367,23 @@ void qmpMainWindow::loadSoundFont(IFluidSettings *fs) #endif } } +int qmpMainWindow::loadFile(QString fns) +{ +#ifdef _WIN32 + char* c=wcsto8bit(fns.toStdWString().c_str()); +#else + std::string s=fns.toStdString(); + const char* c=s.c_str(); +#endif + int ret=1; + invokeCallback("main.reset",NULL); + if(!player->playerLoadFile(c)) + {QMessageBox::critical(this,tr("Error"),tr("%1 is not a valid midi file.").arg(fns));ret=0;} +#ifdef _WIN32 + free(c); +#endif + return ret; +} void qmpMainWindow::on_pbPlayPause_clicked() { @@ -394,12 +399,12 @@ void qmpMainWindow::on_pbPlayPause_clicked() }setWindowTitle(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.'))+" - QMidiPlayer"); ui->lbFileName->setText(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.'))); onfnChanged(); - LOAD_FILE; + if(!loadFile(fns))return; char ts[100]; sprintf(ts,"%02d:%02d",(int)player->getFtime()/60,(int)player->getFtime()%60); ui->lbFinTime->setText(ts); player->playerInit(); - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->start(); + invokeCallback("main.start",NULL); player->fluid()->setGain(ui->vsMasterVol->value()/250.);efxw->sendEfxChange(); player->setWaitVoice(qmpSettingsWindow::getSettingsIntf()->value("Midi/WaitVoice",1).toInt()); playerTh=new std::thread(&CMidiPlayer::playerThread,player); @@ -423,7 +428,7 @@ void qmpMainWindow::on_pbPlayPause_clicked() player->setResumed(); } player->setTCpaused(!playing); - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->pause(); + invokeCallback("main.pause",NULL); } ui->pbPlayPause->setIcon(QIcon(getThemedIcon(playing?":/img/pause.svg":":/img/play.svg"))); } @@ -490,7 +495,7 @@ void qmpMainWindow::on_pbStop_clicked() if(!stopped) { timer->stop();stopped=true;playing=false; - for(auto i=mfunc.begin();i!=mfunc.end();++i)if(i->second.isVisualization())((qmpVisualizationIntf*)(i->second.i()))->stop(); + invokeCallback("main.stop",NULL); player->playerDeinit(); setFuncEnabled("Render",stopped);setFuncEnabled("ReloadSynth",stopped); player->playerPanic(true);chnlw->resetAcitivity(); @@ -543,19 +548,32 @@ void qmpMainWindow::onfnChanged() ui->lbFileName->setFont(f); } -void qmpMainWindow::registerVisualizationIntf(qmpVisualizationIntf* intf,std::string name,std::string desc,const char* icon,int iconlen) +int qmpMainWindow::registerUIHook(std::string e,ICallBack *callback,void* userdat) +{ + std::map>& m=muicb[e]; + int id=0; + if(m.size())id=m.rbegin()->first+1; + m[id]=std::make_pair(qmpCallBack(callback),userdat); + return id; +} +int qmpMainWindow::registerUIHook(std::string e,callback_t callback,void *userdat) { - registerFunctionality(intf,name,desc,icon,iconlen,true,true); + std::map>& m=muicb[e]; + int id=0; + if(m.size())id=m.rbegin()->first+1; + m[id]=std::make_pair(qmpCallBack(callback),userdat); + return id; } -void qmpMainWindow::unregisterVisualizationIntf(std::string name) +void qmpMainWindow::unregisterUIHook(std::string e,int hook) { - unregisterFunctionality(name); + std::map>& m=muicb[e]; + m.erase(hook); } -void qmpMainWindow::registerFunctionality(qmpFuncBaseIntf *i,std::string name,std::string desc,const char *icon,int iconlen,bool checkable,bool isv) +void qmpMainWindow::registerFunctionality(qmpFuncBaseIntf *i,std::string name,std::string desc,const char *icon,int iconlen,bool checkable) { if(mfunc.find(name)!=mfunc.end())return; - mfunc[name]=qmpFuncPrivate(i,desc,icon,iconlen,checkable,isv); + mfunc[name]=qmpFuncPrivate(i,desc,icon,iconlen,checkable); } void qmpMainWindow::unregisterFunctionality(std::string name) @@ -676,6 +694,14 @@ void qmpMainWindow::setupWidget() ui->buttonwidget->layout()->setAlignment(Qt::AlignLeft); } +void qmpMainWindow::invokeCallback(std::string cat,void* callerdat) +{ + std::map> *mp; + mp=&muicb[cat]; + for(auto&i:*mp) + i.second.first(callerdat,i.second.second); +} + void qmpMainWindow::on_pbSettings_clicked() { if(ui->pbSettings->isChecked())settingsw->show();else settingsw->close(); @@ -699,8 +725,8 @@ void qmpMainWindow::on_pushButton_clicked() helpw->show(); } -qmpFuncPrivate::qmpFuncPrivate(qmpFuncBaseIntf *i,std::string _desc,const char *icon,int iconlen,bool checkable,bool _isv): - _i(i),des(_desc),_checkable(checkable),visual(_isv) +qmpFuncPrivate::qmpFuncPrivate(qmpFuncBaseIntf *i,std::string _desc,const char *icon,int iconlen,bool checkable): + _i(i),des(_desc),_checkable(checkable) { if(icon) { diff --git a/qmidiplayer-desktop/qmpmainwindow.hpp b/qmidiplayer-desktop/qmpmainwindow.hpp index c713ca3..a70083a 100644 --- a/qmidiplayer-desktop/qmpmainwindow.hpp +++ b/qmidiplayer-desktop/qmpmainwindow.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "../core/qmpmidiplay.hpp" #include "qmpplugin.hpp" #include "qmpplistwindow.hpp" @@ -95,19 +96,18 @@ class qmpFuncPrivate qmpFuncBaseIntf* _i; QIcon _icon; std::string des; - bool _checkable,checked,visual; + bool _checkable,checked; QReflectiveAction* asgna; QReflectivePushButton* asgnb; public: qmpFuncPrivate(){} - qmpFuncPrivate(qmpFuncBaseIntf* i,std::string _desc,const char* icon,int iconlen,bool checkable,bool _isv); + qmpFuncPrivate(qmpFuncBaseIntf* i,std::string _desc,const char* icon,int iconlen,bool checkable); ~qmpFuncPrivate(){asgna=NULL;asgnb=NULL;} qmpFuncBaseIntf* i(){return _i;} void setAssignedControl(QReflectiveAction* a){asgna=a;if(!a)return;asgna->setCheckable(_checkable);asgna->setChecked(checked);} void setAssignedControl(QReflectivePushButton* a){asgnb=a;if(!a)return;asgnb->setCheckable(_checkable);asgnb->setChecked(checked);} const QIcon& icon(){return _icon;} const std::string& desc(){return des;} - bool isVisualization(){return visual;} bool isCheckable(){return _checkable;} bool isChecked(){return checked;} void setEnabled(bool e){if(asgna)asgna->setEnabled(e);if(asgnb)asgnb->setEnabled(e);} @@ -118,6 +118,24 @@ class qmpRenderFunc; class qmpPanicFunc; class qmpReloadSynthFunc; +class qmpCallBack +{ + private: + int t; + ICallBack* cbc; + callback_t cbf; + public: + qmpCallBack(){t=-1;cbc=NULL;cbf=NULL;} + qmpCallBack(ICallBack* _cb){t=0;cbc=_cb;cbf=NULL;} + qmpCallBack(callback_t _cb){t=1;cbf=_cb;cbc=NULL;} + void operator ()(void* cbd,void* usrd) + { + if(t<0)return; + if(t)cbf(cbd,usrd); + else cbc->callBack(cbd,usrd); + } +}; + class qmpMainWindow:public QMainWindow { Q_OBJECT @@ -140,16 +158,18 @@ class qmpMainWindow:public QMainWindow uint32_t getPlaybackPercentage(); void playerSeek(uint32_t percentage); int pharseArgs(); - void registerVisualizationIntf(qmpVisualizationIntf* intf,std::string name,std::string desc,const char* icon,int iconlen); - void unregisterVisualizationIntf(std::string name); - void registerFunctionality(qmpFuncBaseIntf* i,std::string name,std::string desc,const char* icon,int iconlen,bool checkable,bool isv=false); + 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* callback,void* userdat); + int registerUIHook(std::string e,callback_t callback,void* userdat); + void unregisterUIHook(std::string e,int hook); void setFuncState(std::string name,bool state); void setFuncEnabled(std::string name,bool enable); bool isDarkTheme(); void startRender(); void reloadSynth(); void setupWidget(); + void invokeCallback(std::string cat,void *callerdat); std::vector& getWidgets(int w); std::map& getFunc(); @@ -191,6 +211,7 @@ class qmpMainWindow:public QMainWindow QPointer settingsw; QPointer helpw; std::map mfunc; + std::unordered_map>> muicb; qmpRenderFunc* renderf; qmpPanicFunc* panicf; qmpReloadSynthFunc* reloadsynf; @@ -199,6 +220,7 @@ class qmpMainWindow:public QMainWindow void onfnChanged(); void playerSetup(IFluidSettings *fs); void loadSoundFont(IFluidSettings *fs); + int loadFile(QString fns); private: static qmpMainWindow* ref; diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp index 6eca257..4c0ee7c 100644 --- a/qmidiplayer-desktop/qmpplugin.cpp +++ b/qmidiplayer-desktop/qmpplugin.cpp @@ -193,27 +193,29 @@ void qmpPluginAPI::registerMidiOutDevice(qmpMidiOutDevice *dev, std::string name {qmw->getPlayer()->registerMidiOutDevice(dev,name);} void qmpPluginAPI::unregisterMidiOutDevice(std::string name) {qmw->getPlayer()->unregisterMidiOutDevice(name);} -int qmpPluginAPI::registerEventHandlerIntf(IMidiCallBack *cb,void *userdata) +int qmpPluginAPI::registerEventHandlerIntf(ICallBack *cb,void *userdata) {return qmw->getPlayer()->setEventHandlerCB(cb,userdata);} void qmpPluginAPI::unregisterEventHandlerIntf(int intfhandle) {qmw->getPlayer()->unsetEventHandlerCB(intfhandle);} -int qmpPluginAPI::registerEventReaderIntf(IMidiCallBack *cb,void *userdata) +int qmpPluginAPI::registerEventReaderIntf(ICallBack *cb,void *userdata) {return qmw->getPlayer()->setEventReaderCB(cb,userdata);} void qmpPluginAPI::unregisterEventReaderIntf(int intfhandle) {qmw->getPlayer()->unsetEventReaderCB(intfhandle);} -void qmpPluginAPI::registerVisualizationIntf(qmpVisualizationIntf* intf,std::string name,std::string desc,const char* icon,int iconlen) -{qmw->registerVisualizationIntf(intf,name,desc,icon,iconlen);} -void qmpPluginAPI::unregisterVisualizationIntf(std::string name) -{qmw->unregisterVisualizationIntf(name);} +int qmpPluginAPI::registerUIHook(std::string e,ICallBack* cb,void* userdat) +{return qmw->registerUIHook(e,cb,userdat);} +int qmpPluginAPI::registerUIHook(std::string e,callback_t cb,void* userdat) +{return qmw->registerUIHook(e,cb,userdat);} +void qmpPluginAPI::unregisterUIHook(std::string e,int hook) +{qmw->unregisterUIHook(e,hook);} void qmpPluginAPI::registerFunctionality(qmpFuncBaseIntf *i,std::string name,std::string desc,const char *icon,int iconlen,bool checkable) {qmw->registerFunctionality(i,name,desc,icon,iconlen,checkable);} void qmpPluginAPI::unregisterFunctionality(std::string name) {qmw->unregisterFunctionality(name);} -int qmpPluginAPI::registerFileReadFinishedHandlerIntf(IMidiCallBack* cb,void* userdata) +int qmpPluginAPI::registerFileReadFinishedHandlerIntf(ICallBack* cb,void* userdata) {return qmw->getPlayer()->setFileReadFinishedCB(cb,userdata);} void qmpPluginAPI::unregisterFileReadFinishedHandlerIntf(int intfhandle) {qmw->getPlayer()->unsetFileReadFinishedCB(intfhandle);} -void qmpPluginAPI::registerFileReader(IMidiFileReader* reader,std::string name) +void qmpPluginAPI::registerFileReader(qmpFileReader* reader,std::string name) {qmw->getPlayer()->registerReader(reader,name);} void qmpPluginAPI::unregisterFileReader(std::string name) {qmw->getPlayer()->unregisterReader(name);} diff --git a/qmidiplayer-desktop/qmpsettingswindow.cpp b/qmidiplayer-desktop/qmpsettingswindow.cpp index ba460dd..0f5b66a 100644 --- a/qmidiplayer-desktop/qmpsettingswindow.cpp +++ b/qmidiplayer-desktop/qmpsettingswindow.cpp @@ -383,9 +383,9 @@ void qmpSettingsWindow::verifySF() if(((QCheckBox*)ui->twSoundfont->cellWidget(i,0))->isChecked())++sf; if(settings->value("Midi/DefaultOutput","Internal FluidSynth").toString()=="Internal FluidSynth"&&!sf) { - // blmark: show dialog at the current screen which user using now. - int curMonitor = QApplication::desktop()->screenNumber(this); - if(QMessageBox::question(QDesktopWidget().screen(curMonitor),//this, + // blmark: show dialog at the current screen which user using now. + int curMonitor = QApplication::desktop()->screenNumber(this); + if(QMessageBox::question(QDesktopWidget().screen(curMonitor),//this, tr("No soundfont loaded"), tr("Internal fluidsynth was chosen as the default output but it has no soundfont set. " "Would you like to setup soundfonts now? After that you may have to reload the internal synth."))==QMessageBox::Yes) diff --git a/qmidiplayer-lite/main.qml b/qmidiplayer-lite/main.qml index 2fcd1ea..098c910 100644 --- a/qmidiplayer-lite/main.qml +++ b/qmidiplayer-lite/main.qml @@ -33,8 +33,9 @@ Window { id: button2 x: 170 text: qsTr("Play") + anchors.horizontalCenterOffset: 0 anchors.top: parent.top - anchors.topMargin: 172 + anchors.topMargin: 213 anchors.horizontalCenter: parent.horizontalCenter onClicked: { if(!playing) @@ -58,14 +59,14 @@ Window { Slider { id: hsTimer - y: 210 + y: 190 height: 22 anchors.right: parent.right - anchors.rightMargin: 8 + anchors.rightMargin: 0 anchors.left: parent.left - anchors.leftMargin: 8 + anchors.leftMargin: 0 anchors.bottom: parent.bottom - anchors.bottomMargin: 8 + anchors.bottomMargin: 28 tickmarksEnabled: false stepSize: 1 maximumValue: 100 @@ -132,11 +133,14 @@ Window { Text { id: fileName text: qsTr("...") + horizontalAlignment: Text.AlignHCenter color: palette.text anchors.top: parent.top anchors.topMargin: 100 anchors.horizontalCenter: parent.horizontalCenter x: 139 + width: 31 + height: 11 } Button { diff --git a/visualization/qmpvisualization.cpp b/visualization/qmpvisualization.cpp index 30e4075..62534bd 100644 --- a/visualization/qmpvisualization.cpp +++ b/visualization/qmpvisualization.cpp @@ -159,18 +159,18 @@ void qmpVisualization::showThread() if(!api->getOptionString("Visualization/font2").length()||!font.loadTTF(api->getOptionString("Visualization/font2").c_str(),fontsize)) if(!font.loadTTF("/usr/share/fonts/truetype/freefont/FreeMono.ttf",fontsize)) if(!font.loadTTF("/usr/share/fonts/gnu-free/FreeMono.otf",fontsize)) - if(!font.loadTTF((std::string(getenv("windir"))+"/Fonts/cour.ttf").c_str(),fontsize)) + if(!font.loadTTF((std::string(getenv("windir")?getenv("windir"):"")+"/Fonts/cour.ttf").c_str(),fontsize)) printf("W: Font load failed.\n"); if(!api->getOptionString("Visualization/font2").length()||!fonthdpi.loadTTF(api->getOptionString("Visualization/font2").c_str(),180)) if(!fonthdpi.loadTTF("/usr/share/fonts/truetype/freefont/FreeMono.ttf",180)) if(!fonthdpi.loadTTF("/usr/share/fonts/gnu-free/FreeMono.otf",180)) - if(!fonthdpi.loadTTF((std::string(getenv("windir"))+"/Fonts/cour.ttf").c_str(),180)) + if(!fonthdpi.loadTTF((std::string(getenv("windir")?getenv("windir"):"")+"/Fonts/cour.ttf").c_str(),180)) printf("W: Font load failed.\n"); if(!api->getOptionString("Visualization/font1").length()||!font2.loadTTF(api->getOptionString("Visualization/font1").c_str(),fontsize)) if(!font2.loadTTF("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",fontsize)) if(!font2.loadTTF("/usr/share/fonts/wenquanyi/wqy-microhei/wqy-microhei.ttc",fontsize)) - if(!font2.loadTTF((std::string(getenv("windir"))+"/Fonts/msyh.ttc").c_str(),fontsize)) - if(!font2.loadTTF((std::string(getenv("windir"))+"/Fonts/segoeui.ttf").c_str(),fontsize)) + if(!font2.loadTTF((std::string(getenv("windir")?getenv("windir"):"")+"/Fonts/msyh.ttc").c_str(),fontsize)) + if(!font2.loadTTF((std::string(getenv("windir")?getenv("windir"):"")+"/Fonts/segoeui.ttf").c_str(),fontsize)) printf("W: Font load failed.\n"); if(horizontal) { @@ -751,14 +751,17 @@ void qmpVisualization::init() { cb=new CReaderCallBack(this); hcb=new CEventHandlerCallBack(this); - vi=new CDemoVisualization(this); h=new CMidiVisualHandler(this); frcb=new CFRFinishedCallBack(this); closeh=new CloseHandler(this); rendererTh=NULL;playing=false; memset(spectra,0,sizeof(spectra)); memset(spectrar,0,sizeof(spectrar)); - api->registerVisualizationIntf(vi,"Visualization","Visualization",api->isDarkTheme()?":/img/visualization_i.svg":":/img/visualization.svg",0); + api->registerFunctionality(this,"Visualization","Visualization",api->isDarkTheme()?":/img/visualization_i.svg":":/img/visualization.svg",0,true); + api->registerUIHook("main.start",qmpVisualization::cbstart,(void*)this); + api->registerUIHook("main.stop",qmpVisualization::cbstop,(void*)this); + api->registerUIHook("main.pause",qmpVisualization::cbpause,(void*)this); + api->registerUIHook("main.reset",qmpVisualization::cbreset,(void*)this); herif=api->registerEventReaderIntf(cb,NULL); hehif=api->registerEventHandlerIntf(hcb,NULL); hfrf=api->registerFileReadFinishedHandlerIntf(frcb,NULL); @@ -788,7 +791,7 @@ void qmpVisualization::init() api->registerOptionInt("Visualization-Appearance","Note stretch","Visualization/notestretch",20,500,100); api->registerOptionInt("Visualization-Appearance","Minimum note length","Visualization/minnotelen",20,500,100); api->registerOptionUint("Visualization-Appearance","Chequer board tint (AARRGGBB)","Visualization/chkrtint",0,0xFFFFFFFF,0xFF999999); - api->registerOptionString("Visualization-Appearance","Background Image","Visualization/background",""); + api->registerOptionString("Visualization-Appearance","Background Image","Visualization/background","",true); api->registerOptionDouble("","","Visualization/px",-999999999,999999999,0); api->registerOptionDouble("","","Visualization/py",-999999999,999999999,120); api->registerOptionDouble("","","Visualization/pz",-999999999,999999999,70); @@ -832,17 +835,37 @@ void qmpVisualization::deinit() { if(!api)return;close();tspool.clear(); for(unsigned i=0;iunregisterVisualizationIntf("Visualization"); + api->unregisterFunctionality("Visualization"); api->unregisterEventReaderIntf(herif); api->unregisterEventHandlerIntf(hehif); api->unregisterFileReadFinishedHandlerIntf(hfrf); - delete cb;delete hcb;delete vi;delete frcb; + delete cb;delete hcb;delete frcb; delete h;delete closeh; } const char* qmpVisualization::pluginGetName() {return "QMidiPlayer Default Visualization Plugin";} const char* qmpVisualization::pluginGetVersion() -{return "0.8.3";} +{return "0.8.6";} +void qmpVisualization::cbstart(void *,void *usrd) +{ + qmpVisualization* v=(qmpVisualization*)usrd; + v->start(); +} +void qmpVisualization::cbstop(void *,void *usrd) +{ + qmpVisualization* v=(qmpVisualization*)usrd; + v->stop(); +} +void qmpVisualization::cbpause(void *,void *usrd) +{ + qmpVisualization* v=(qmpVisualization*)usrd; + v->pause(); +} +void qmpVisualization::cbreset(void *,void *usrd) +{ + qmpVisualization* v=(qmpVisualization*)usrd; + v->reset(); +} void qmpVisualization::pushNoteOn(uint32_t tc,uint32_t ch,uint32_t key,uint32_t vel) { diff --git a/visualization/qmpvisualization.hpp b/visualization/qmpvisualization.hpp index f9e6062..9d62397 100644 --- a/visualization/qmpvisualization.hpp +++ b/visualization/qmpvisualization.hpp @@ -12,7 +12,7 @@ #include "../include/qmpcorepublic.hpp" class qmpVisualization; -class CReaderCallBack:public IMidiCallBack +class CReaderCallBack:public ICallBack { private: qmpVisualization *par; @@ -20,7 +20,7 @@ class CReaderCallBack:public IMidiCallBack CReaderCallBack(qmpVisualization *_par){par=_par;} void callBack(void *callerdata,void *userdata); }; -class CEventHandlerCallBack:public IMidiCallBack +class CEventHandlerCallBack:public ICallBack { private: qmpVisualization *par; @@ -28,7 +28,7 @@ class CEventHandlerCallBack:public IMidiCallBack CEventHandlerCallBack(qmpVisualization *_par){par=_par;} void callBack(void*,void*); }; -class CFRFinishedCallBack:public IMidiCallBack +class CFRFinishedCallBack:public ICallBack { private: qmpVisualization *par; @@ -42,7 +42,7 @@ struct MidiVisualEvent uint32_t key,vel; uint32_t ch; }; -class qmpVisualization:public qmpPluginIntf +class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf { friend class CEventHandlerCallBack; friend class CReaderCallBack; @@ -52,7 +52,6 @@ class qmpVisualization:public qmpPluginIntf qmpPluginAPI* api; CReaderCallBack* cb; CEventHandlerCallBack* hcb; - qmpVisualizationIntf* vi; CFRFinishedCallBack* frcb; std::thread* rendererTh; std::vectorpool; @@ -83,18 +82,23 @@ class qmpVisualization:public qmpPluginIntf public: qmpVisualization(qmpPluginAPI* _api); ~qmpVisualization(); + void show(); + void close(); bool update(); void start(); void stop(); void pause(); - void show(); - void close(); void reset(); void init(); void deinit(); const char* pluginGetName(); const char* pluginGetVersion(); + + static void cbstart(void* cbd,void* usrd); + static void cbstop(void* cbd,void* usrd); + static void cbpause(void* cbd,void* usrd); + static void cbreset(void* cbd,void* usrd); }; class CMidiVisualHandler:public smHandler @@ -122,20 +126,6 @@ class CloseHandler:public smHandler } }; -class CDemoVisualization:public qmpVisualizationIntf -{ - private: - qmpVisualization* par; - public: - CDemoVisualization(qmpVisualization *p){par=p;} - void show(){par->show();} - void close(){par->close();} - void start(){par->start();} - void stop(){par->stop();} - void pause(){par->pause();} - void reset(){par->reset();} -}; - extern "C"{ EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api) {return new qmpVisualization(api);} -- cgit v1.2.3