diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | core/qmpmidiplay.cpp | 105 | ||||
-rw-r--r-- | core/qmpmidiplay.hpp | 79 | ||||
-rw-r--r-- | core/qmpmidiread.cpp | 199 | ||||
-rw-r--r-- | include/qmpcorepublic.hpp | 37 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmphelpwindow.hpp | 2 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpmainwindow.cpp | 2 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpplugin.cpp | 6 |
8 files changed, 276 insertions, 159 deletions
@@ -1,3 +1,8 @@ +2017-02-07 0.8.5 indev +First steps for the file reader API. +API additions and changes. +Fixed wrong button shown when started from file. + 2017-02-07 0.8.3 alpha Fixed 2D visualization broken by commit 8a0d671. diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index 9cd7782..d5df23c 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -8,6 +8,7 @@ #include <windows.h> uint64_t pf; #endif +CMidiPlayer* CMidiPlayer::ref=NULL; void CMidiPlayer::fluidPreInitialize() { settings=new_fluid_settings(); @@ -27,9 +28,9 @@ void CMidiPlayer::fluidInitialize() #ifndef _WIN32 if(!singleInstance) { - if(midiFile->getStandard()==4) + if(midiReaders->getStandard()==4) fluid_synth_set_channel_type(synth,9,CHANNEL_TYPE_MELODIC); - else if(midiFile->getStandard()==1) + else if(midiReaders->getStandard()==1) fluid_synth_set_channel_type(synth,9,CHANNEL_TYPE_DRUM); else { @@ -125,8 +126,8 @@ void CMidiPlayer::processEvent(const SEvent *e) int io=0; if(sendSysEx) { - for(int i=0;i<16;++i)if(deviceusage[i])mapper->sysEx(i,e->p1,e->str); - fluid_synth_sysex(synth,e->str,e->p1,NULL,&io,NULL,0); + for(int i=0;i<16;++i)if(deviceusage[i])mapper->sysEx(i,e->p1,e->str.c_str()); + fluid_synth_sysex(synth,e->str.c_str(),e->p1,NULL,&io,NULL,0); } } break; @@ -198,22 +199,22 @@ void CMidiPlayer::prePlayInit() } void CMidiPlayer::playEvents() { - for(ct=midiFile->getEvent(0)->time;tceptr<midiFile->getEventCount();) + for(ct=midiReaders->getEvent(0)->time;tceptr<midiReaders->getEventCount();) { while(tcpaused)std::this_thread::sleep_for(std::chrono::milliseconds(100)); using namespace std::chrono; high_resolution_clock::time_point b=high_resolution_clock::now(); - while(!tcstop&&midiFile&&tceptr<midiFile->getEventCount()&&ct==midiFile->getEvent(tceptr)->time) - processEvent(midiFile->getEvent(tceptr++)); - if(tcstop||!midiFile||tceptr>=midiFile->getEventCount())break; + while(!tcstop&&midiReaders&&tceptr<midiReaders->getEventCount()&&ct==midiReaders->getEvent(tceptr)->time) + processEvent(midiReaders->getEvent(tceptr++)); + if(tcstop||!midiReaders||tceptr>=midiReaders->getEventCount())break; high_resolution_clock::time_point a=high_resolution_clock::now(); auto sendtime=a-b; if(resumed)resumed=false; else - if(sendtime.count()<(midiFile->getEvent(tceptr)->time-ct)*dpt) - std::this_thread::sleep_for(std::chrono::nanoseconds((midiFile->getEvent(tceptr)->time-ct)*dpt-sendtime.count())); - if(tcstop||!midiFile)break; - ct=midiFile->getEvent(tceptr)->time; + if(sendtime.count()<(midiReaders->getEvent(tceptr)->time-ct)*dpt) + std::this_thread::sleep_for(std::chrono::nanoseconds((midiReaders->getEvent(tceptr)->time-ct)*dpt-sendtime.count())); + if(tcstop||!midiReaders)break; + ct=midiReaders->getEvent(tceptr)->time; } while(!tcstop&&synth&&(waitvoice&&fluid_synth_get_active_voice_count(synth)>0))std::this_thread::sleep_for(std::chrono::milliseconds(2)); finished=1; @@ -221,13 +222,13 @@ void CMidiPlayer::playEvents() void CMidiPlayer::fileTimer1Pass() { ftime=.0;ctempo=0x7A120;dpt=ctempo*1000/divs; - for(uint32_t eptr=0,ct=midiFile->getEvent(0)->time;eptr<midiFile->getEventCount();) + for(uint32_t eptr=0,ct=midiReaders->getEvent(0)->time;eptr<midiReaders->getEventCount();) { - while(eptr<midiFile->getEventCount()&&ct==midiFile->getEvent(eptr)->time) - processEventStub(midiFile->getEvent(eptr++)); - if(eptr>=midiFile->getEventCount())break; - ftime+=(midiFile->getEvent(eptr)->time-ct)*dpt/1e9; - ct=midiFile->getEvent(eptr)->time; + while(eptr<midiReaders->getEventCount()&&ct==midiReaders->getEvent(eptr)->time) + processEventStub(midiReaders->getEvent(eptr++)); + if(eptr>=midiReaders->getEventCount())break; + ftime+=(midiReaders->getEvent(eptr)->time-ct)*dpt/1e9; + ct=midiReaders->getEvent(eptr)->time; } } void CMidiPlayer::fileTimer2Pass() @@ -243,15 +244,15 @@ void CMidiPlayer::fileTimer2Pass() ccc[i][76]=64;ccc[i][77]=64;ccc[i][78]=64; ccc[0][131]=dpt;ccc[0][132]=0x04021808; ccc[0][133]=0;ccc[0][134]=2; - }if(midiFile->getStandard()!=4)ccc[9][0]=128; + }if(midiReaders->getStandard()!=4)ccc[9][0]=128; for(int i=0;i<16;++i)for(int j=0;j<135;++j) ccstamps[0][i][j]=ccc[i][j]; - for(uint32_t eptr=0,ct=midiFile->getEvent(0)->time;eptr<midiFile->getEventCount();) + for(uint32_t eptr=0,ct=midiReaders->getEvent(0)->time;eptr<midiReaders->getEventCount();) { - while(eptr<midiFile->getEventCount()&&ct==midiFile->getEvent(eptr)->time) - processEventStub(midiFile->getEvent(eptr++)); - if(eptr>=midiFile->getEventCount())break; - ctime+=(midiFile->getEvent(eptr)->time-ct)*dpt/1e9; + while(eptr<midiReaders->getEventCount()&&ct==midiReaders->getEvent(eptr)->time) + processEventStub(midiReaders->getEvent(eptr++)); + if(eptr>=midiReaders->getEventCount())break; + ctime+=(midiReaders->getEvent(eptr)->time-ct)*dpt/1e9; while(ctime>ftime*c/100.) { for(int i=0;i<16;++i)for(int j=0;j<135;++j) @@ -259,18 +260,19 @@ void CMidiPlayer::fileTimer2Pass() stamps[c++]=eptr; if(c>100)break; } - ct=midiFile->getEvent(eptr)->time; + ct=midiReaders->getEvent(eptr)->time; } while(c<101) { for(int i=0;i<16;++i)for(int j=0;j<135;++j) ccstamps[c][i][j]=ccc[i][j]; - stamps[c++]=midiFile->getEventCount(); + stamps[c++]=midiReaders->getEventCount(); } } CMidiPlayer::CMidiPlayer(bool singleInst) { - midiFile=NULL;resumed=false;singleInstance=singleInst; + midiReaders=new CMidiFileReaderCollection(); + resumed=false;singleInstance=singleInst; settings=NULL;synth=NULL;adriver=NULL;waitvoice=true; memset(eventHandlerCB,0,sizeof(eventHandlerCB)); memset(eventHandlerCBuserdata,0,sizeof(eventHandlerCBuserdata)); @@ -288,11 +290,12 @@ CMidiPlayer::CMidiPlayer(bool singleInst) #ifdef _WIN32 QueryPerformanceFrequency((LARGE_INTEGER*)&pf); #endif + ref=this; } CMidiPlayer::~CMidiPlayer() { - if(singleInstance)fluidDeinitialize(); - delete mapper; + if(singleInstance||settings||synth||adriver)fluidDeinitialize(); + delete midiReaders;delete mapper; } void CMidiPlayer::playerPanic(bool reset) { @@ -318,9 +321,9 @@ void CMidiPlayer::playerPanic(bool reset) } bool CMidiPlayer::playerLoadFile(const char* fn) { - midiFile=new CMidiFile(fn,this); - if(!midiFile->isValid())return false; - divs=midiFile->getDivision(); + midiReaders->readFile(fn);notes=0; + if(!midiReaders->isValid())return false; + divs=midiReaders->getDivision(); for(int i=0;i<16;++i)if(fileReadFinishCB[i]) fileReadFinishCB[i]->callBack(NULL,fileReadFinishCBuserdata[i]); fileTimer1Pass(); @@ -343,7 +346,7 @@ void CMidiPlayer::playerInit() void CMidiPlayer::playerDeinit() { tceptr=0;tcstop=1;tcpaused=0; - delete midiFile;midiFile=NULL; + midiReaders->destructFile(); if(!singleInstance)fluidDeinitialize(); } void CMidiPlayer::playerThread() @@ -387,7 +390,7 @@ uint32_t CMidiPlayer::getTCeptr(){return tceptr;} void CMidiPlayer::setTCeptr(uint32_t ep,uint32_t st) { resumed=true; - if(ep==midiFile->getEventCount())tcstop=1;else tceptr=ep; + if(ep==midiReaders->getEventCount())tcstop=1;else tceptr=ep; for(int i=0;i<16;++i) { for(int j=0;j<120;++j)fluid_synth_cc(synth,i,j,ccstamps[st][i][j]); @@ -401,15 +404,15 @@ void CMidiPlayer::setTCeptr(uint32_t ep,uint32_t st) double CMidiPlayer::getFtime(){return ftime;} void CMidiPlayer::getCurrentTimeSignature(int *n,int *d){*n=ctsn;*d=ctsd;} int CMidiPlayer::getCurrentKeySignature(){return cks;} -uint32_t CMidiPlayer::getFileNoteCount(){return midiFile?midiFile->getNoteCount():0;} -uint32_t CMidiPlayer::getFileStandard(){return midiFile?midiFile->getStandard():0;} -const char* CMidiPlayer::getTitle(){return midiFile?midiFile->getTitle():"";} -const char* CMidiPlayer::getCopyright(){return midiFile?midiFile->getCopyright():"";} +uint32_t CMidiPlayer::getFileNoteCount(){return notes;} +uint32_t CMidiPlayer::getFileStandard(){return midiReaders?midiReaders->getStandard():0;} +const char* CMidiPlayer::getTitle(){return midiReaders?midiReaders->getTitle():"";} +const char* CMidiPlayer::getCopyright(){return midiReaders?midiReaders->getCopyright():"";} double CMidiPlayer::getTempo(){return 60./(ctempo/1e6);} uint32_t CMidiPlayer::getTick(){return ct;} uint32_t CMidiPlayer::getRawTempo(){return ctempo;} uint32_t CMidiPlayer::getDivision(){return divs;} -uint32_t CMidiPlayer::getMaxTick(){return maxtk;} +uint32_t CMidiPlayer::getMaxTick(){return midiReaders->getMaxTick();} 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;} @@ -594,5 +597,25 @@ int CMidiPlayer::setFileReadFinishedCB(IMidiCallBack *cb,void *userdata) } void CMidiPlayer::unsetFileReadFinishedCB(int id) {fileReadFinishCB[id]=NULL;fileReadFinishCBuserdata[id]=NULL;} -void CMidiPlayer::discardLastEvent(){midiFile?midiFile->discardLastEvent():(void)0;} -void CMidiPlayer::commitEventChange(SEventCallBackData d){midiFile?midiFile->commitEventChange(d):(void)0;} +void CMidiPlayer::registerReader(IMidiFileReader *reader,std::string name) +{midiReaders->registerReader(reader,name);} +void CMidiPlayer::unregisterReader(std::string name) +{midiReaders->unregisterReader(name);} +void CMidiPlayer::callEventReaderCB(SEventCallBackData d) +{ + if((d.type&0xF0)==0x90)++notes; + for(int i=0;i<16;++i)if(eventReaderCB[i]) + eventReaderCB[i]->callBack(&d,eventReaderCBuserdata[i]); +} +void CMidiPlayer::discardCurrentEvent() +{ + if(midiReaders->getCurrentReader()) + midiReaders->getCurrentReader()->discardCurrentEvent(); +} +void CMidiPlayer::commitEventChange(SEventCallBackData d) +{ + if(midiReaders->getCurrentReader()) + midiReaders->getCurrentReader()->commitEventChange(d); +} + +CMidiPlayer* CMidiPlayer::getInstance(){return ref;} diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index 5b6c5a7..8d78c4e 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -3,37 +3,20 @@ #define QMPMIDIPLAY_H #include <cstring> #include <cstdlib> +#include <utility> #include <vector> #include <fluidsynth.h> #include "../include/qmpcorepublic.hpp" #include "qmpmidimappers.hpp" -struct SEvent -{ - uint32_t iid,time,p1,p2; - uint8_t type; - char *str; - SEvent(){time=p1=p2=0;type=0;str=NULL;} - ~SEvent(){if(str){delete[] str;str=NULL;}} - SEvent(uint32_t _iid,uint32_t _t,char _tp,uint32_t _p1,uint32_t _p2,const char* s=NULL) - { - iid=_iid;time=_t;type=_tp; - p1=_p1;p2=_p2; - if(s){str=new char[strlen(s)+2];strcpy(str,s);}else str=NULL; - } -}; class CMidiPlayer; -class CMidiFile +class CSMFReader:public IMidiFileReader { private: - std::vector<SEvent*>eventList; - char *title,*copyright; - uint32_t std;//standard 0=? 1=GM 2=GM2 3=GS 4=XG - uint32_t fmt,trk,divs; + CMidiFile* ret; + uint32_t fmt,trk; FILE *f; int byteread,valid,eventdiscarded; - uint32_t notes,curt,curid; - IMidiCallBack* eventReaderCB[16]; - void* eventReaderCBuserdata[16]; + uint32_t curt,curid; void error(int fatal,const char* format,...); uint32_t readSW(); @@ -43,27 +26,41 @@ class CMidiFile void trackChunkReader(); void headerChunkReader(); int chunkReader(int hdrXp); - void dumpEvents(); public: - CMidiFile(const char* fn,CMidiPlayer* par); - ~CMidiFile(); - const SEvent* getEvent(uint32_t id); - uint32_t getEventCount(); - uint32_t getDivision(); - uint32_t getNoteCount(); - uint32_t getStandard(); - const char* getTitle(); - const char* getCopyright(); - bool isValid(); - void discardLastEvent(); + CSMFReader(); + ~CSMFReader(); + CMidiFile* readFile(const char* fn); + void discardCurrentEvent(); void commitEventChange(SEventCallBackData d); }; +class CMidiFileReaderCollection{ + std::vector<std::pair<IMidiFileReader*,std::string>> readers; + CMidiFile* file; + uint32_t maxtk; + void destructFile(CMidiFile*& f); + IMidiFileReader* currentReader; +public: + CMidiFileReaderCollection(); + ~CMidiFileReaderCollection(); + void registerReader(IMidiFileReader* reader,std::string name); + void unregisterReader(std::string name); + void readFile(const char* fn); + void destructFile(); + IMidiFileReader* getCurrentReader(); + bool isValid(); + const char* getTitle(); + const char* getCopyright(); + const SEvent* getEvent(uint32_t id); + uint32_t getEventCount(); + uint32_t getDivision(); + uint32_t getMaxTick(); + uint32_t getStandard(); +}; class CMidiPlayer { - friend class CMidiFile; private: - CMidiFile *midiFile; - uint32_t stamps[101],maxtk; + CMidiFileReaderCollection *midiReaders; + uint32_t stamps[101],notes; uint32_t ccstamps[101][16][135],ccc[16][135]; //0..127:cc 128:pc 129:cp 130:pb 131:tempo 132:ts 133:ks 134:pbr int32_t rpnid[16],rpnval[16]; @@ -89,6 +86,7 @@ class CMidiPlayer void* eventHandlerCBuserdata[16]; void* eventReaderCBuserdata[16]; void* fileReadFinishCBuserdata[16]; + static CMidiPlayer* ref; void setBit(uint16_t &n,uint16_t bn,uint16_t b); void processEvent(const SEvent *e); @@ -171,8 +169,13 @@ class CMidiPlayer void unsetEventReaderCB(int id); int setFileReadFinishedCB(IMidiCallBack *cb,void *userdata); void unsetFileReadFinishedCB(int id); + void registerReader(IMidiFileReader* reader,std::string name); + void unregisterReader(std::string name); + void callEventReaderCB(SEventCallBackData d); - void discardLastEvent(); + void discardCurrentEvent(); void commitEventChange(SEventCallBackData d); + + static CMidiPlayer* getInstance(); }; #endif diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp index dc58ecc..4ac2ac8 100644 --- a/core/qmpmidiread.cpp +++ b/core/qmpmidiread.cpp @@ -12,29 +12,29 @@ 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 CMidiFile::error(int fatal,const char* format,...) +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; va_start(ap,format);vfprintf(stderr,format,ap);va_end(ap); fprintf(stderr," at %#lx\n",ftell(f)); if(fatal)throw std::runtime_error("fatal error"); } -uint32_t CMidiFile::readSW() +uint32_t CSMFReader::readSW() { byteread+=2; uint32_t ret=0; for(int i=0;i<2;++i){ret<<=8;ret|=((uint32_t)fgetc(f))&0xFF;} return ret; } -uint32_t CMidiFile::readDW() +uint32_t CSMFReader::readDW() { byteread+=4; uint32_t ret=0; for(int i=0;i<4;++i){ret<<=8;ret|=((uint32_t)fgetc(f))&0xFF;} return ret; } -uint32_t CMidiFile::readVL() +uint32_t CSMFReader::readVL() { uint32_t ret=0,t,c=0; do @@ -46,7 +46,7 @@ uint32_t CMidiFile::readVL() byteread+=c; return ret; } -int CMidiFile::eventReader()//returns 0 if End of Track encountered +int CSMFReader::eventReader()//returns 0 if End of Track encountered { uint32_t delta=readVL();curt+=delta; char type=fgetc(f);++byteread;uint32_t p1,p2; @@ -56,37 +56,34 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered { case 0x80://Note Off p1=fgetc(f);p2=fgetc(f);byteread+=2; - eventList.push_back(new SEvent(curid,curt,type,p1,p2)); + ret->eventList.push_back(SEvent(curid,curt,type,p1,p2)); break; case 0x90://Note On p1=fgetc(f);p2=fgetc(f);byteread+=2; if(p2) - { - ++notes; - eventList.push_back(new SEvent(curid,curt,type,p1,p2)); - } + ret->eventList.push_back(SEvent(curid,curt,type,p1,p2)); else - eventList.push_back(new SEvent(curid,curt,(type&0x0F)|0x80,p1,p2)); + ret->eventList.push_back(SEvent(curid,curt,(type&0x0F)|0x80,p1,p2)); break; case 0xA0://Note Aftertouch p1=fgetc(f);p2=fgetc(f);byteread+=2; - eventList.push_back(new SEvent(curid,curt,type,p1,p2)); + ret->eventList.push_back(SEvent(curid,curt,type,p1,p2)); break; case 0xB0://Controller Change p1=fgetc(f);p2=fgetc(f);byteread+=2; - eventList.push_back(new SEvent(curid,curt,type,p1,p2)); + ret->eventList.push_back(SEvent(curid,curt,type,p1,p2)); break; case 0xC0://Patch Change p1=fgetc(f);++byteread; - eventList.push_back(new SEvent(curid,curt,type,p1,0)); + ret->eventList.push_back(SEvent(curid,curt,type,p1,0)); break; case 0xD0://Channel Aftertouch p1=fgetc(f);++byteread; - eventList.push_back(new SEvent(curid,curt,type,p1,0)); + ret->eventList.push_back(SEvent(curid,curt,type,p1,0)); break; case 0xE0://Pitch wheel p1=fgetc(f);p2=fgetc(f);byteread+=2; - eventList.push_back(new SEvent(curid,curt,type,(p1|(p2<<7))&0x3FFF,0)); + ret->eventList.push_back(SEvent(curid,curt,type,(p1|(p2<<7))&0x3FFF,0)); break; case 0xF0: if((type&0x0F)==0x0F)//Meta Event @@ -107,7 +104,7 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered break; case 0x51://Set Tempo p1=readDW();p1&=0x00FFFFFF; - eventList.push_back(new SEvent(curid,curt,type,metatype,p1)); + ret->eventList.push_back(SEvent(curid,curt,type,metatype,p1)); break; case 0x54://SMTPE offset, not handled. fgetc(f);fgetc(f);fgetc(f); @@ -117,12 +114,12 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered case 0x58://Time signature fgetc(f);++byteread; p1=readDW(); - eventList.push_back(new SEvent(curid,curt,type,metatype,p1)); + ret->eventList.push_back(SEvent(curid,curt,type,metatype,p1)); break; case 0x59://Key signature fgetc(f);++byteread; p1=readSW(); - eventList.push_back(new SEvent(curid,curt,type,metatype,p1)); + ret->eventList.push_back(SEvent(curid,curt,type,metatype,p1)); break; case 0x01:case 0x02:case 0x03: case 0x04:case 0x05:case 0x06: @@ -134,16 +131,16 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered { ++byteread;if(str)str[c]=fgetc(f);else fgetc(f); } - if(str)str[c]='\0';eventList.push_back(new SEvent(curid,curt,type,metatype,0,str)); - if(str&&metatype==0x03&&!title) + if(str)str[c]='\0';ret->eventList.push_back(SEvent(curid,curt,type,metatype,0,str)); + if(str&&metatype==0x03&&!ret->title) { - title=new char[len+8]; - strcpy(title,str); + ret->title=new char[len+8]; + strcpy(ret->title,str); } - if(str&&metatype==0x02&&!copyright) + if(str&&metatype==0x02&&!ret->copyright) { - copyright=new char[len+8]; - strcpy(copyright,str); + ret->copyright=new char[len+8]; + strcpy(ret->copyright,str); } if(len<=1024&&len>0)delete[] str; } @@ -159,11 +156,11 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered for(c=1;c<len;++c){++byteread;str[c]=fgetc(f);} } else for(c=0;c<len;++c){++byteread;str[c]=fgetc(f);} - eventList.push_back(new SEvent(curid,curt,type,len,0,str)); - if(!strcmp(str,GM1SysX))std=1; - if(!strcmp(str,GM2SysX))std=2; - if(!strcmp(str,GSSysEx))std=3; - if(!strcmp(str,XGSysEx))std=4; + ret->eventList.push_back(SEvent(curid,curt,type,len,0,str)); + if(!strcmp(str,GM1SysX))ret->std=1; + if(!strcmp(str,GM2SysX))ret->std=2; + if(!strcmp(str,GSSysEx))ret->std=3; + if(!strcmp(str,XGSysEx))ret->std=4; delete[] str; } else error(0,"W: Unknown event type %#x",type); @@ -172,16 +169,15 @@ int CMidiFile::eventReader()//returns 0 if End of Track encountered error(0,"W: Unknown event type %#x",type); } lasttype=type;++curid; - if(eventList.size()) + if(ret->eventList.size()) { - SEvent* le=eventList[eventList.size()-1]; - SEventCallBackData cbd(le->type,le->p1,le->p2,le->time); - for(int i=0;i<16;++i)if(eventReaderCB[i]) - eventReaderCB[i]->callBack(&cbd,eventReaderCBuserdata[i]); + SEvent& le=ret->eventList[ret->eventList.size()-1]; + SEventCallBackData cbd(le.type,le.p1,le.p2,le.time); + CMidiPlayer::getInstance()->callEventReaderCB(cbd); } return 1; } -void CMidiFile::trackChunkReader() +void CSMFReader::trackChunkReader() { int chnklen=readDW();byteread=0;curt=0;curid=0; while(/*byteread<chnklen&&*/eventReader()); @@ -195,16 +191,16 @@ void CMidiFile::trackChunkReader() error(1,"E: Read past end of track."); }*/ } -void CMidiFile::headerChunkReader() +void CSMFReader::headerChunkReader() { int chnklen=readDW();byteread=0; if(chnklen<6)error(1,"E: Header chunk too short."); if(chnklen>6)error(0,"W: Header chunk length longer than expected. Ignoring extra bytes."); - fmt=readSW();trk=readSW();divs=readSW(); - if(divs&0x8000)error(1,"E: SMTPE format is not supported."); + fmt=readSW();trk=readSW();ret->divs=readSW(); + if(ret->divs&0x8000)error(1,"E: SMTPE format is not supported."); for(;byteread<chnklen;++byteread){fgetc(f);} } -int CMidiFile::chunkReader(int hdrXp) +int CSMFReader::chunkReader(int hdrXp) { char hdr[6]; if(!fgets(hdr,5,f))error(1,"E: Unexpected EOF."); @@ -219,55 +215,106 @@ int CMidiFile::chunkReader(int hdrXp) } else return trackChunkReader(),1; } -void CMidiFile::dumpEvents() +CSMFReader::CSMFReader() { - for(uint32_t i=0;i<eventList.size();++i) - if(eventList[i]->str) - 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); - 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); + f=NULL; } -CMidiFile::CMidiFile(const char* fn,CMidiPlayer* par) +CMidiFile* CSMFReader::readFile(const char* fn) { - title=copyright=NULL;notes=0;std=0;valid=1; - memcpy(eventReaderCB,par->eventReaderCB,sizeof(eventReaderCB)); - memcpy(eventReaderCBuserdata,par->eventReaderCBuserdata,sizeof(eventReaderCBuserdata)); + ret=new CMidiFile; + ret->title=ret->copyright=NULL;ret->std=0;ret->valid=1; try { if(!(f=fopen(fn,"rb")))throw (fprintf(stderr,"E: file %s doesn't exist!\n",fn),std::runtime_error("File doesn't exist")); chunkReader(1); for(uint32_t i=0;i<trk;i+=chunkReader(0)); - fclose(f); - std::sort(eventList.begin(),eventList.end(),cmp); - par->maxtk=eventList[eventList.size()-1]->time; + fclose(f);f=NULL; + std::sort(ret->eventList.begin(),ret->eventList.end(),cmp); } - catch(std::runtime_error&){fprintf(stderr,"E: %s is not a supported file.\n",fn);valid=0;} + catch(std::runtime_error&){fprintf(stderr,"E: %s is not a supported file.\n",fn);ret->valid=0;fclose(f);f=NULL;} + return ret; } -CMidiFile::~CMidiFile() +CSMFReader::~CSMFReader() { - for(uint32_t i=0;i<eventList.size();++i)delete eventList[i];eventList.clear(); - if(title)delete[] title;if(copyright)delete[] copyright; } -const SEvent* CMidiFile::getEvent(uint32_t id){return id<eventList.size()?eventList[id]:NULL;} -uint32_t CMidiFile::getEventCount(){return eventList.size();} -uint32_t CMidiFile::getDivision(){return divs;} -uint32_t CMidiFile::getNoteCount(){return notes;} -uint32_t CMidiFile::getStandard(){return std;} -bool CMidiFile::isValid(){return valid;} -const char* CMidiFile::getTitle(){return title;} -const char* CMidiFile::getCopyright(){return copyright;} -void CMidiFile::discardLastEvent() +void CSMFReader::discardCurrentEvent() { if(eventdiscarded)return;eventdiscarded=1; - delete eventList[eventList.size()-1];eventList.pop_back(); + ret->eventList.pop_back(); +} +void CSMFReader::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; +} + +void CMidiFileReaderCollection::destructFile(CMidiFile*& f) +{ + if(!f)return; + if(f->copyright)delete[] f->copyright; + if(f->title)delete[] f->title; + delete f; + f=NULL; +} +CMidiFileReaderCollection::CMidiFileReaderCollection() +{ + file=NULL;readers.clear();currentReader=NULL; + registerReader(new CSMFReader(),"Default SMF Reader"); } -void CMidiFile::commitEventChange(SEventCallBackData d) +CMidiFileReaderCollection::~CMidiFileReaderCollection() { - eventList[eventList.size()-1]->time=d.time; - eventList[eventList.size()-1]->type=d.type; - eventList[eventList.size()-1]->p1=d.p1; - eventList[eventList.size()-1]->p2=d.p2; + if(file)destructFile(file); + delete readers[0].first; +} +void CMidiFileReaderCollection::registerReader(IMidiFileReader* reader,std::string name) +{ + for(unsigned i=0;i<readers.size();++i) + if(readers[i].second==name)return; + readers.push_back(std::make_pair(reader,name)); +} +void CMidiFileReaderCollection::unregisterReader(std::string name) +{ + for(auto i=readers.begin();i!=readers.end();++i) + if(i->second==name) + { + readers.erase(i); + return; + } +} +void CMidiFileReaderCollection::readFile(const char* fn) +{ + if(file)destructFile(file); + for(unsigned i=0;i<readers.size();++i) + { + currentReader=readers[i].first; + CMidiFile* t=readers[i].first->readFile(fn); + if(t->valid){file=t;break;} + else destructFile(t); + } + currentReader=NULL; + if(file) + maxtk=file->eventList[file->eventList.size()-1].time; } +void CMidiFileReaderCollection::destructFile() +{destructFile(file);} +IMidiFileReader* CMidiFileReaderCollection::getCurrentReader() +{return currentReader;} +bool CMidiFileReaderCollection::isValid() +{return file&&file->valid;} +const char* CMidiFileReaderCollection::getTitle() +{return file?file->title:NULL;} +const char* CMidiFileReaderCollection::getCopyright() +{return file?file->copyright:NULL;} +const SEvent* CMidiFileReaderCollection::getEvent(uint32_t id) +{return file?&(file->eventList[id]):NULL;} +uint32_t CMidiFileReaderCollection::getEventCount() +{return file?file->eventList.size():0;} +uint32_t CMidiFileReaderCollection::getDivision() +{return file?file->divs:0;} +uint32_t CMidiFileReaderCollection::getMaxTick() +{return maxtk;} +uint32_t CMidiFileReaderCollection::getStandard() +{return file?file->std:0;} diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp index 850fddd..6e56310 100644 --- a/include/qmpcorepublic.hpp +++ b/include/qmpcorepublic.hpp @@ -1,5 +1,6 @@ #ifndef QMPCOREPUBLIC_H #define QMPCOREPUBLIC_H +#include <cstring> #include <cstdint> #include <vector> #include <string> @@ -8,6 +9,20 @@ #else #define EXPORTSYM __attribute__ ((visibility ("default"))) #endif +//MIDI Event structure +struct SEvent +{ + uint32_t iid,time,p1,p2; + uint8_t type; + std::string str; + SEvent(){time=p1=p2=0;type=0;str="";} + SEvent(uint32_t _iid,uint32_t _t,char _tp,uint32_t _p1,uint32_t _p2,const char* s=NULL) + { + iid=_iid;time=_t;type=_tp; + p1=_p1;p2=_p2; + if(s)str=std::string(s);else str=""; + } +}; //This struct is used by event reader callbacks and event handler callbacks //as caller data struct class SEventCallBackData @@ -16,6 +31,14 @@ public: uint32_t time,type,p1,p2; SEventCallBackData(uint32_t _t,uint32_t _p1,uint32_t _p2,uint32_t _tm){type=_t;p1=_p1;p2=_p2;time=_tm;} }; +//MIDI File class +class CMidiFile{ + public: + bool valid; + char *title,*copyright; + std::vector<SEvent> eventList; + uint32_t std,divs; +}; //Generic callback function that can be used for hooking the core. //"userdata" is set when you register the callback function. class IMidiCallBack @@ -25,6 +48,16 @@ class IMidiCallBack virtual void callBack(void* callerdata,void* userdata)=0; virtual ~IMidiCallBack(){} }; +//MIDI file reader interface. Use this to implement your file importer. +class IMidiFileReader +{ + public: + IMidiFileReader(){} + virtual ~IMidiFileReader(){} + virtual CMidiFile* readFile(const char* fn)=0; + virtual void discardCurrentEvent()=0; + virtual void commitEventChange(SEventCallBackData d)=0; +}; //Main plugin interface. class qmpPluginIntf { @@ -82,7 +115,7 @@ class qmpPluginAPI //WARNING!!: This function should be called from event reader callbacks only and //it is somehow dangerous -- other plugins might be unaware of the removal of the //event. The design might be modified afterward. - virtual void discardLastEvent(); + virtual void discardCurrentEvent(); //WARNING!!: This function should be called from event reader callbacks only and //it is somehow dangerous -- other plugins might be unaware of the event change. //The design might be modified afterward. @@ -96,6 +129,8 @@ class qmpPluginAPI virtual void unregisterEventHandlerIntf(int intfhandle); virtual int registerFileReadFinishedHandlerIntf(IMidiCallBack* cb,void* userdata); virtual void unregisterFileReadFinishedHandlerIntf(int intfhandle); + virtual void registerFileReader(IMidiFileReader* reader,std::string name); + virtual void unregisterFileReader(std::string name); //if desc=="", the option won't be visible in the settings form. //it will only show up in the configuration file. diff --git a/qmidiplayer-desktop/qmphelpwindow.hpp b/qmidiplayer-desktop/qmphelpwindow.hpp index 61d9963..75f8679 100644 --- a/qmidiplayer-desktop/qmphelpwindow.hpp +++ b/qmidiplayer-desktop/qmphelpwindow.hpp @@ -2,7 +2,7 @@ #define QMPHELPWINDOW_H #include <QDialog> -#define APP_VERSION "0.8.3" +#define APP_VERSION "0.8.5" #ifndef BUILD_MACHINE #define BUILD_MACHINE UNKNOWN #endif diff --git a/qmidiplayer-desktop/qmpmainwindow.cpp b/qmidiplayer-desktop/qmpmainwindow.cpp index 1927350..876b0e9 100644 --- a/qmidiplayer-desktop/qmpmainwindow.cpp +++ b/qmidiplayer-desktop/qmpmainwindow.cpp @@ -125,7 +125,6 @@ void qmpMainWindow::init() connect(timer,SIGNAL(timeout()),this,SLOT(updateWidgets())); connect(timer,SIGNAL(timeout()),chnlw,SLOT(channelWindowsUpdate())); connect(timer,SIGNAL(timeout()),infow,SLOT(updateInfo())); - if(havemidi)on_pbPlayPause_clicked(); ui->pbNext->setIcon(QIcon(getThemedIcon(":/img/next.png"))); ui->pbPrev->setIcon(QIcon(getThemedIcon(":/img/prev.png"))); ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/play.png"))); @@ -135,6 +134,7 @@ void qmpMainWindow::init() ui->pbPList->setIcon(QIcon(getThemedIcon(":/img/list.png"))); ui->pbVisualization->setIcon(QIcon(getThemedIcon(":/img/visualization.png"))); ui->pbSettings->setIcon(QIcon(getThemedIcon(":/img/settings.png"))); + if(havemidi)on_pbPlayPause_clicked(); } int qmpMainWindow::pharseArgs() diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp index 0c8c04d..5f5d82e 100644 --- a/qmidiplayer-desktop/qmpplugin.cpp +++ b/qmidiplayer-desktop/qmpplugin.cpp @@ -165,7 +165,7 @@ std::string qmpPluginAPI::getChannelPresetString(int ch) return std::string(ret); } -void qmpPluginAPI::discardLastEvent(){if(qmw&&qmw->getPlayer())qmw->getPlayer()->discardLastEvent();} +void qmpPluginAPI::discardCurrentEvent(){if(qmw&&qmw->getPlayer())qmw->getPlayer()->discardCurrentEvent();} void qmpPluginAPI::commitEventChange(SEventCallBackData d){if(qmw&&qmw->getPlayer())qmw->getPlayer()->commitEventChange(d);} int qmpPluginAPI::registerEventHandlerIntf(IMidiCallBack *cb,void *userdata) @@ -184,6 +184,10 @@ int qmpPluginAPI::registerFileReadFinishedHandlerIntf(IMidiCallBack* cb,void* us {qmw->getPlayer()->setFileReadFinishedCB(cb,userdata);} void qmpPluginAPI::unregisterFileReadFinishedHandlerIntf(int intfhandle) {qmw->getPlayer()->unsetFileReadFinishedCB(intfhandle);} +void qmpPluginAPI::registerFileReader(IMidiFileReader* reader,std::string name) +{qmw->getPlayer()->registerReader(reader,name);} +void qmpPluginAPI::unregisterFileReader(std::string name) +{qmw->getPlayer()->unregisterReader(name);} void qmpPluginAPI::registerOptionInt(std::string tab,std::string desc,std::string key,int min,int max,int defaultval) {qsw->registerOptionInt(tab,desc,key,min,max,defaultval);} |