diff options
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | core/qmpmidiplay.cpp | 2 | ||||
-rw-r--r-- | core/qmpmidiplay.hpp | 4 | ||||
-rw-r--r-- | core/qmpmidiread.cpp | 16 | ||||
-rw-r--r-- | include/qmpcorepublic.hpp | 3 | ||||
-rw-r--r-- | midifmt-plugin/midifmt-plugin.pro | 27 | ||||
-rw-r--r-- | midifmt-plugin/midifmtplugin.cpp | 111 | ||||
-rw-r--r-- | midifmt-plugin/midifmtplugin.hpp | 45 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpplugin.cpp | 1 | ||||
-rw-r--r-- | qmidiplayer.pro | 3 |
10 files changed, 212 insertions, 4 deletions
@@ -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<std::pair<IMidiFileReader*,std::string>> 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.time<b.time:a.iid<b.iid;} void CSMFReader::error(int fatal,const char* format,...) { va_list ap; @@ -240,7 +239,7 @@ CMidiFile* CSMFReader::readFile(const char* fn) chunkReader(1); for(uint32_t i=0;i<trk;i+=chunkReader(0)); fclose(f);f=NULL; - std::sort(ret->eventList.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<SEvent> &eventList=file->eventList; + for(uint32_t i=0;i<eventList.size();++i) + if(eventList[i].str.length()) + printf("type %x #%d @%d p1 %d p2 %d str %s\n",eventList[i].type, + eventList[i].iid,eventList[i].time,eventList[i].p1,eventList[i].p2,eventList[i].str.c_str()); + else + printf("type %x #%d @%d p1 %d p2 %d\n",eventList[i].type, + eventList[i].iid,eventList[i].time,eventList[i].p1,eventList[i].p2); +} CMidiFileReaderCollection::CMidiFileReaderCollection() { file=NULL;readers.clear();currentReader=NULL; @@ -301,6 +312,7 @@ void CMidiFileReaderCollection::readFile(const char* fn) for(unsigned i=0;i<readers.size();++i) { currentReader=readers[i].first; + CMidiPlayer::getInstance()->notes=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<b.time:a.iid<b.iid;} }; //This struct is used by event reader callbacks and event handler callbacks //as caller data struct @@ -120,6 +121,8 @@ class qmpPluginAPI //it is somehow dangerous -- other plugins might be unaware of the event change. //The design might be modified afterward. virtual void commitEventChange(SEventCallBackData d); + //This function should be called from a file reader when it has read a new event + virtual void callEventReaderCB(SEventCallBackData d); virtual int registerVisualizationIntf(qmpVisualizationIntf* intf); virtual void unregisterVisualizationIntf(int intfhandle); diff --git a/midifmt-plugin/midifmt-plugin.pro b/midifmt-plugin/midifmt-plugin.pro new file mode 100644 index 0000000..e063dac --- /dev/null +++ b/midifmt-plugin/midifmt-plugin.pro @@ -0,0 +1,27 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2017-02-09T09:24:43 +# +#------------------------------------------------- + +QT -= core gui + +CONFIG += c++11 + +TARGET = midifmt-plugin +TEMPLATE = lib + +DEFINES += MIDIFMTPLUGIN_LIBRARY + +SOURCES += midifmtplugin.cpp + +HEADERS += midifmtplugin.hpp + +unix { + QMAKE_CXXFLAGS_RELEASE -= -O2 + QMAKE_CXXFLAGS_RELEASE += -O3 + QMAKE_LFLAGS_RELEASE -= -O1 + QMAKE_LFLAGS_RELEASE += -O3 + target.path = $$PREFIX/lib/qmidiplayer + INSTALLS += target +} diff --git a/midifmt-plugin/midifmtplugin.cpp b/midifmt-plugin/midifmtplugin.cpp new file mode 100644 index 0000000..33e636e --- /dev/null +++ b/midifmt-plugin/midifmtplugin.cpp @@ -0,0 +1,111 @@ +#include <cstring> +#include <algorithm> +#include <stdexcept> +#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<cblocks;++i) + { + readDWLE(); + uint32_t blocksz=readDWLE(),cpos=ftell(f); + while(ftell(f)-cpos<blocksz) + { + cts+=readDWLE(); + if(!(fmt&1))readDWLE(); + uint32_t e=readDWLE(); + SEvent ev; + if(e>>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 <cstdio> +#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 |