aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2017-02-09 16:04:43 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2017-02-09 16:04:43 +0800
commit620aad63042e0f397ea6691fe92ac40fcc6b6fed (patch)
treeb36a0f0c00ffba03bfed9513a360b7ecf442e720
parent1976c49f835267d33ef88bd3fc20d18363e12c0b (diff)
downloadQMidiPlayer-620aad63042e0f397ea6691fe92ac40fcc6b6fed.tar.xz
Add extra midi formats support via the file reader API.
Fix note counting.
-rw-r--r--ChangeLog4
-rw-r--r--core/qmpmidiplay.cpp2
-rw-r--r--core/qmpmidiplay.hpp4
-rw-r--r--core/qmpmidiread.cpp16
-rw-r--r--include/qmpcorepublic.hpp3
-rw-r--r--midifmt-plugin/midifmt-plugin.pro27
-rw-r--r--midifmt-plugin/midifmtplugin.cpp111
-rw-r--r--midifmt-plugin/midifmtplugin.hpp45
-rw-r--r--qmidiplayer-desktop/qmpplugin.cpp1
-rw-r--r--qmidiplayer.pro3
10 files changed, 212 insertions, 4 deletions
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<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