From 620aad63042e0f397ea6691fe92ac40fcc6b6fed Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Thu, 9 Feb 2017 16:04:43 +0800 Subject: Add extra midi formats support via the file reader API. Fix note counting. --- ChangeLog | 4 ++ core/qmpmidiplay.cpp | 2 +- core/qmpmidiplay.hpp | 4 +- core/qmpmidiread.cpp | 16 +++++- include/qmpcorepublic.hpp | 3 ++ midifmt-plugin/midifmt-plugin.pro | 27 ++++++++++ midifmt-plugin/midifmtplugin.cpp | 111 ++++++++++++++++++++++++++++++++++++++ midifmt-plugin/midifmtplugin.hpp | 45 ++++++++++++++++ qmidiplayer-desktop/qmpplugin.cpp | 1 + qmidiplayer.pro | 3 ++ 10 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 midifmt-plugin/midifmt-plugin.pro create mode 100644 midifmt-plugin/midifmtplugin.cpp create mode 100644 midifmt-plugin/midifmtplugin.hpp diff --git a/ChangeLog b/ChangeLog index c66874f..20dfb18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2017-02-09 0.8.5 indev +Add extra midi formats support via the file reader API. +Fix note counting. + 2017-02-08 0.8.5 indev Add API version verification. This breaks compatibility with old versions of plugins. diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index d5df23c..76c7d16 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -321,7 +321,7 @@ void CMidiPlayer::playerPanic(bool reset) } bool CMidiPlayer::playerLoadFile(const char* fn) { - midiReaders->readFile(fn);notes=0; + notes=0;midiReaders->readFile(fn); if(!midiReaders->isValid())return false; divs=midiReaders->getDivision(); for(int i=0;i<16;++i)if(fileReadFinishCB[i]) diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index b911844..ebc62d6 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -38,8 +38,9 @@ class CMidiFileReaderCollection{ std::vector> readers; CMidiFile* file; uint32_t maxtk; - void destructFile(CMidiFile*& f); IMidiFileReader* currentReader; + void destructFile(CMidiFile*& f); + void dumpFile(); public: CMidiFileReaderCollection(); ~CMidiFileReaderCollection(); @@ -59,6 +60,7 @@ public: }; class CMidiPlayer { + friend class CMidiFileReaderCollection; private: CMidiFileReaderCollection *midiReaders; uint32_t stamps[101],notes; diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp index b36fffc..92573d2 100644 --- a/core/qmpmidiread.cpp +++ b/core/qmpmidiread.cpp @@ -12,7 +12,6 @@ const char* GM1SysX={"\xF0\x7E\x7F\x09\x01\xF7"}; const char* GM2SysX={"\xF0\x7E\x7F\x09\x03\xF7"}; const char* GSSysEx={"\xF0\x41\x10\x42\x12\x40\x00\x7F\x00\x41\xF7"}; const char* XGSysEx={"\xF0\x43\x10\x4C\x00\x00\x7E\x00\xF7"}; -bool cmp(SEvent a,SEvent b){return a.time-b.time?a.timeeventList.begin(),ret->eventList.end(),cmp); + std::sort(ret->eventList.begin(),ret->eventList.end()); } catch(std::runtime_error&){fprintf(stderr,"E: %s is not a supported file.\n",fn);ret->valid=0;fclose(f);f=NULL;} return ret; @@ -270,6 +269,18 @@ void CMidiFileReaderCollection::destructFile(CMidiFile*& f) delete f; f=NULL; } +void CMidiFileReaderCollection::dumpFile() +{ + if(!file)return; + std::vector &eventList=file->eventList; + for(uint32_t i=0;inotes=0; CMidiFile* t=readers[i].first->readFile(fn); if(t->valid){file=t;break;} else destructFile(t); diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp index f42d42a..3ce0545 100644 --- a/include/qmpcorepublic.hpp +++ b/include/qmpcorepublic.hpp @@ -22,6 +22,7 @@ struct SEvent p1=_p1;p2=_p2; if(s)str=std::string(s);else str=""; } + friend bool operator <(const SEvent& a,const SEvent& b){return a.time-b.time?a.time +#include +#include +#include "midifmtplugin.hpp" +qmpPluginAPI* qmpMidiFmtPlugin::api=NULL; + +uint32_t CMidiStreamReader::readDWLE() +{ + uint32_t ret=0; + for(uint32_t i=0;i<4;++i)ret|=((uint32_t)fgetc(f))<<(i<<3); + return ret; +} +bool CMidiStreamReader::RIFFHeaderReader() +{ + char hdr[9]; + fread(hdr,1,4,f); + if(strncmp(hdr,"RIFF",4))return false; + fseek(f,4,SEEK_CUR); + fread(hdr,1,8,f); + if(strncmp(hdr,"MIDSfmt ",8))return false; + if(readDWLE()!=0x0C)return false; + ret->divs=readDWLE(); + readDWLE(); + fmt=readDWLE(); + return true; +} +bool CMidiStreamReader::midsBodyReader() +{ + char buf[9]; + fread(buf,1,4,f); + if(strncmp(buf,"data",4))return false; + readDWLE();//size + uint32_t cblocks=readDWLE(); + uint32_t curid=0,cts=0; + for(int i=0;i>24==1)//set tempo + ev=SEvent(curid,cts,0xFF,0x51,e&0x00FFFFFF); + else if(e>>24==0)//midishortmsg + ev=SEvent(curid,cts,e&0xFF,(e>>8)&0xFF,(e>>16)&0xFF); + else return false; + //fprintf(stderr,"ev: @ %x t %x p1 %x p2 %x\n",ev.time,ev.type,ev.p1,ev.p2); + if((ev.type&0xF0)==0x90&&ev.p2==0)//Note on with zero velo + ev.type=(ev.type&0x0F)|0x80; + if((ev.type&0xF0)==0xE0)//pitch wheel + {ev.p1=(ev.p1|(ev.p2<<7))&0x3FFF;ev.p2=0;} + ret->eventList.push_back(ev);eventdiscarded=0; + qmpMidiFmtPlugin::api->callEventReaderCB(SEventCallBackData(ev.type,ev.p1,ev.p2,ev.time)); + if(eventdiscarded)ret->eventList.pop_back(); + ++curid; + } + } + return true; +} +CMidiFile* CMidiStreamReader::readFile(const char *fn) +{ + ret=new CMidiFile; + ret->title=ret->copyright=NULL;ret->std=0;ret->valid=1; + try + { + if(!(f=fopen(fn,"rb")))throw std::runtime_error("File doesn't exist"); + if(!RIFFHeaderReader())throw std::runtime_error("Wrong RIFF header"); + if(!midsBodyReader())throw std::runtime_error("MIDS data error"); + std::sort(ret->eventList.begin(),ret->eventList.end()); + }catch(std::runtime_error& e){fprintf(stderr,"MIDI Format plugin: E: %s is not a supported file. Cause: %s.\n",fn,e.what());ret->valid=0;fclose(f);f=NULL;} + return ret; +} +void CMidiStreamReader::discardCurrentEvent() +{ + eventdiscarded=1; +} +void CMidiStreamReader::commitEventChange(SEventCallBackData d) +{ + ret->eventList[ret->eventList.size()-1].time=d.time; + ret->eventList[ret->eventList.size()-1].type=d.type; + ret->eventList[ret->eventList.size()-1].p1=d.p1; + ret->eventList[ret->eventList.size()-1].p2=d.p2; +} +CMidiStreamReader::CMidiStreamReader() +{ + ret=NULL;f=NULL; +} +CMidiStreamReader::~CMidiStreamReader() +{ +} + +qmpMidiFmtPlugin::qmpMidiFmtPlugin(qmpPluginAPI *_api) +{api=_api;} +qmpMidiFmtPlugin::~qmpMidiFmtPlugin() +{api=NULL;} +void qmpMidiFmtPlugin::init() +{ + api->registerFileReader(mdsreader=new CMidiStreamReader,"MIDS reader"); +} +void qmpMidiFmtPlugin::deinit() +{ + api->unregisterFileReader("MIDS reader"); + delete mdsreader; +} +const char* qmpMidiFmtPlugin::pluginGetName() +{return "QMidiPlayer extra midi formats plugin";} +const char* qmpMidiFmtPlugin::pluginGetVersion() +{return "0.8.5";} diff --git a/midifmt-plugin/midifmtplugin.hpp b/midifmt-plugin/midifmtplugin.hpp new file mode 100644 index 0000000..f6284dd --- /dev/null +++ b/midifmt-plugin/midifmtplugin.hpp @@ -0,0 +1,45 @@ +#ifndef MIDIFMTPLUGIN_HPP +#define MIDIFMTPLUGIN_HPP + +#include +#include "../include/qmpcorepublic.hpp" + +class CMidiStreamReader:public IMidiFileReader +{ + private: + CMidiFile* ret; + FILE* f; + int eventdiscarded,fmt; + uint32_t readDWLE(); + bool RIFFHeaderReader(); + bool midsBodyReader(); + public: + CMidiStreamReader(); + ~CMidiStreamReader(); + CMidiFile* readFile(const char *fn); + void discardCurrentEvent(); + void commitEventChange(SEventCallBackData d); +}; + +class qmpMidiFmtPlugin:public qmpPluginIntf +{ + private: + CMidiStreamReader* mdsreader; + public: + static qmpPluginAPI* api; + qmpMidiFmtPlugin(qmpPluginAPI* _api); + ~qmpMidiFmtPlugin(); + void init(); + void deinit(); + const char* pluginGetName(); + const char* pluginGetVersion(); +}; + +extern "C"{ + EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api) + {return new qmpMidiFmtPlugin(api);} + EXPORTSYM const char* qmpPluginGetAPIRev() + {return QMP_PLUGIN_API_REV;} +} + +#endif // MIDIFMTPLUGIN_HPP diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp index 96936bd..5de1dd5 100644 --- a/qmidiplayer-desktop/qmpplugin.cpp +++ b/qmidiplayer-desktop/qmpplugin.cpp @@ -177,6 +177,7 @@ std::string qmpPluginAPI::getChannelPresetString(int ch) void qmpPluginAPI::discardCurrentEvent(){if(qmw&&qmw->getPlayer())qmw->getPlayer()->discardCurrentEvent();} void qmpPluginAPI::commitEventChange(SEventCallBackData d){if(qmw&&qmw->getPlayer())qmw->getPlayer()->commitEventChange(d);} +void qmpPluginAPI::callEventReaderCB(SEventCallBackData d){if(qmw&&qmw->getPlayer())qmw->getPlayer()->callEventReaderCB(d);} int qmpPluginAPI::registerEventHandlerIntf(IMidiCallBack *cb,void *userdata) {return qmw->getPlayer()->setEventHandlerCB(cb,userdata);} diff --git a/qmidiplayer.pro b/qmidiplayer.pro index 0f4a262..c6f2ac6 100644 --- a/qmidiplayer.pro +++ b/qmidiplayer.pro @@ -11,3 +11,6 @@ android { SUBDIRS = \ qmidiplayer-lite } + +SUBDIRS += \ + midifmt-plugin -- cgit v1.2.3