diff options
author | Chris Xiong <chirs241097@gmail.com> | 2016-04-06 09:46:52 +0800 |
---|---|---|
committer | Chris Xiong <chirs241097@gmail.com> | 2016-04-06 09:46:52 +0800 |
commit | cf7eafca1fbc9aae495d7b7d7e1fb4bc92e6819f (patch) | |
tree | d1881d513983a345936a547cbc25b8bd1a1f056e /common | |
parent | ffd5bf0c99be3e4089886ab4757100474010edb8 (diff) | |
download | QMidiPlayer-cf7eafca1fbc9aae495d7b7d7e1fb4bc92e6819f.tar.xz |
Sometimes Naïve!
Diffstat (limited to 'common')
-rw-r--r-- | common/qmpmidiplay.cpp | 404 | ||||
-rw-r--r-- | common/qmpmidiplay.hpp | 137 | ||||
-rw-r--r-- | common/qmpmidiread.cpp | 239 |
3 files changed, 780 insertions, 0 deletions
diff --git a/common/qmpmidiplay.cpp b/common/qmpmidiplay.cpp new file mode 100644 index 0000000..6f69e33 --- /dev/null +++ b/common/qmpmidiplay.cpp @@ -0,0 +1,404 @@ +#include <cstdio> +#include <chrono> +#include <thread> +#include <fluidsynth.h> +#include "qmpmidiplay.hpp" +#ifdef _WIN32 +#include <windows.h> +uint64_t pf; +#endif +void CMidiPlayer::fluidPreInitialize() +{ + settings=new_fluid_settings(); +} +void CMidiPlayer::fluidInitialize() +{ + synth=new_fluid_synth(settings); + adriver=new_fluid_audio_driver(settings,synth); + fluid_synth_set_chorus(synth,FLUID_CHORUS_DEFAULT_N,FLUID_CHORUS_DEFAULT_LEVEL, + FLUID_CHORUS_DEFAULT_SPEED,FLUID_CHORUS_DEFAULT_DEPTH, + FLUID_CHORUS_DEFAULT_TYPE); +#ifndef _WIN32 + if(!singleInstance) + { + if(midiFile->getStandard()==4) + fluid_synth_set_channel_type(synth,9,CHANNEL_TYPE_MELODIC); + else if(midiFile->getStandard()==1) + fluid_synth_set_channel_type(synth,9,CHANNEL_TYPE_DRUM); + else + { + fluid_synth_set_channel_type(synth,9,CHANNEL_TYPE_DRUM); + fluid_synth_bank_select(synth,9,128); + } + } +#endif +} +void CMidiPlayer::fluidDeinitialize() +{ + if(!synth||!adriver||!settings)return; + delete_fluid_settings(settings); + delete_fluid_audio_driver(adriver); + delete_fluid_synth(synth); + settings=NULL;synth=NULL;adriver=NULL; +} +void CMidiPlayer::processEvent(const SEvent *e) +{ + switch(e->type&0xF0) + { + case 0x80://Note off + fluid_synth_noteoff(synth,e->type&0x0F,e->p1); + break; + case 0x90://Note on + if((mute>>(e->type&0x0F))&1)break;//muted + if(solo&&!((solo>>(e->type&0x0F))&1))break; + fluid_synth_noteon(synth,e->type&0x0F,e->p1,e->p2); + break; + case 0xB0://CC + if(e->p1==100)rpnid=e->p2; + if(e->p1==6)rpnval=e->p2; + if(~rpnid&&~rpnval) + { + if(rpnid==0)fluid_synth_pitch_wheel_sens(synth,e->type&0x0F,rpnval); + rpnid=rpnval=-1; + } + fluid_synth_cc(synth,e->type&0x0F,e->p1,e->p2); + break; + case 0xC0://PC + fluid_synth_program_change(synth,e->type&0x0F,e->p1); + break; + case 0xE0://PW + fluid_synth_pitch_bend(synth,e->type&0x0F,e->p1); + break; + case 0xF0://Meta/SysEx + if((e->type&0x0F)==0x0F) + { + switch(e->p1) + { + case 0x51: + ctempo=e->p2;dpt=ctempo*1000/divs; + break; + case 0x58: + ctsn=e->p2>>24; + ctsd=1<<((e->p2>>16)&0xFF); + break; + case 0x59: + cks=e->p2; + break; + case 0x01:case 0x02:case 0x03: + case 0x04:case 0x05:case 0x06: + case 0x07: + //if(e->str)puts(e->str); + break; + } + } + if((e->type&0x0F)==0x00||(e->type&0x0F)==07) + { + int io=0; + if(sendSysEx)fluid_synth_sysex(synth,e->str,e->p1,NULL,&io,NULL,0); + } + break; + } +} +void CMidiPlayer::processEventStub(const SEvent *e) +{ + switch(e->type&0xF0) + { + case 0xB0://CC + if(e->p1==100)rpnid=e->p2; + if(e->p1==6)rpnval=e->p2; + if(~rpnid&&~rpnval) + { + if(rpnid==0)ccc[e->type&0x0F][134]=rpnval; + rpnid=rpnval=-1; + } + ccc[e->type&0x0F][e->p1]=e->p2; + break; + case 0xC0://PC + ccc[e->type&0x0F][128]=e->p1; + break; + case 0xD0://CP + ccc[e->type&0x0F][129]=e->p1; + break; + case 0xE0://PW + ccc[e->type&0x0F][130]=e->p1; + break; + case 0xF0://Meta/SysEx + if((e->type&0x0F)==0x0F) + { + switch(e->p1) + { + case 0x51: + ctempo=e->p2;dpt=ctempo*1000/divs; + ccc[0][131]=dpt; + break; + case 0x58: + ccc[0][132]=e->p2; + break; + case 0x59: + ccc[0][133]=e->p2; + break; + } + } + break; + } +} +#ifdef _WIN32 +void w32usleep(uint64_t t) +{ + uint64_t st=0,ct=0; + timeBeginPeriod(1); + QueryPerformanceCounter((LARGE_INTEGER*)&st); + do{ + if(t>10000+(ct-st)*1000000/pf)Sleep((t-(ct-st)*1000000/pf)/2000); + else if(t>5000+(ct-st)*1000000/pf)Sleep(1); + else std::this_thread::yield(); + QueryPerformanceCounter((LARGE_INTEGER*)&ct); + }while((ct-st)*1000000<t*pf); + timeEndPeriod(1); +} +#endif +void CMidiPlayer::playEvents() +{ + for(uint32_t ct=midiFile->getEvent(0)->time;tceptr<midiFile->getEventCount();) + { + while(tcpaused)std::this_thread::sleep_for(std::chrono::milliseconds(100)); + while(!tcstop&&midiFile&&tceptr<midiFile->getEventCount()&&ct==midiFile->getEvent(tceptr)->time) + processEvent(midiFile->getEvent(tceptr++)); + if(tcstop||!midiFile||tceptr>=midiFile->getEventCount())break; + if(resumed)resumed=false; + else +#if 0 + w32usleep((midiFile->getEvent(tceptr)->time-ct)*(dpt/1000)); +#else + std::this_thread::sleep_for(std::chrono::nanoseconds(midiFile->getEvent(tceptr)->time-ct)*dpt); +#endif + if(tcstop||!midiFile)break; + ct=midiFile->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; +} +void CMidiPlayer::fileTimer1Pass() +{ + ftime=.0;ctempo=0x7A120;dpt=ctempo*1000/divs; + for(uint32_t eptr=0,ct=midiFile->getEvent(0)->time;eptr<midiFile->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; + } +} +void CMidiPlayer::fileTimer2Pass() +{ + double ctime=.0;uint32_t c=1;ctempo=0x7A120;dpt=ctempo*1000/divs; + memset(stamps,0,sizeof(stamps));memset(ccstamps,0,sizeof(ccstamps)); + memset(ccc,0,sizeof(ccc));rpnid=rpnval=-1;for(int i=0;i<16;++i) + { + ccc[i][7]=100;ccc[i][10]=64;ccc[i][11]=127; + ccc[i][11]=127;ccc[i][71]=64;ccc[i][72]=64; + ccc[i][73]=64;ccc[i][74]=64;ccc[i][75]=64; + 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; + 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();) + { + 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(ctime>ftime*c/100.) + { + for(int i=0;i<16;++i)for(int j=0;j<135;++j) + ccstamps[c][i][j]=ccc[i][j]; + stamps[c++]=eptr; + if(c>100)break; + } + ct=midiFile->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(); + } +} +CMidiPlayer::CMidiPlayer(bool singleInst) +{ + midiFile=NULL;resumed=false;singleInstance=singleInst; + settings=NULL;synth=NULL;adriver=NULL;waitvoice=true; +#ifdef _WIN32 + QueryPerformanceFrequency((LARGE_INTEGER*)&pf); +#endif +} +CMidiPlayer::~CMidiPlayer() +{ + if(singleInstance)fluidDeinitialize(); +} +void CMidiPlayer::playerPanic(bool reset) +{ + if(reset)for(int i=0;i<16;++i) + { + fluid_synth_pitch_bend(synth,i,8192); + fluid_synth_cc(synth,i,7,100); + fluid_synth_cc(synth,i,10,64); + fluid_synth_cc(synth,i,11,127); + } + for(int i=0;i<16;++i)fluid_synth_all_notes_off(synth,i); +} +bool CMidiPlayer::playerLoadFile(const char* fn) +{ + midiFile=new CMidiFile(fn); + if(!midiFile->isValid())return false; + divs=midiFile->getDivision(); + fileTimer1Pass(); + fileTimer2Pass(); + return true; +} +void CMidiPlayer::playerInit() +{ + ctempo=0x7A120;ctsn=4;ctsd=4;cks=0;dpt=ctempo*1000/divs; + tceptr=0;tcstop=0;tcpaused=0;finished=0;mute=solo=0; + sendSysEx=true;rpnid=rpnval=-1; + if(!singleInstance)fluidPreInitialize(); +} +void CMidiPlayer::playerDeinit() +{ + tceptr=0;tcstop=1;tcpaused=0; + delete midiFile;midiFile=NULL; + if(!singleInstance)fluidDeinitialize(); +} +void CMidiPlayer::playerThread() +{ + playEvents(); +} + +void CMidiPlayer::rendererLoadFile(const char* ofn) +{ + settings=new_fluid_settings(); + fluid_settings_setstr(settings,"audio.file.name",ofn); +} +void CMidiPlayer::rendererInit(const char* fn) +{ + finished=0; + synth=new_fluid_synth(settings); + player=new_fluid_player(synth); + fluid_player_add(player,fn); +} +void CMidiPlayer::rendererThread() +{ + fluid_file_renderer_t* renderer=new_fluid_file_renderer(synth); + fluid_player_play(player); + while(fluid_player_get_status(player)==FLUID_PLAYER_PLAYING) + if(fluid_file_renderer_process_block(renderer)!=FLUID_OK)break; + delete_fluid_file_renderer(renderer); + finished=1; +} +void CMidiPlayer::rendererDeinit() +{ + delete_fluid_player(player); + delete_fluid_synth(synth); + delete_fluid_settings(settings); + player=NULL;synth=NULL;settings=NULL; +} + +void CMidiPlayer::sendSysX(bool send){sendSysEx=send;} +uint32_t CMidiPlayer::getStamp(int id){return stamps[id];} +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; + for(int i=0;i<16;++i) + { + for(int j=0;j<120;++j)fluid_synth_cc(synth,i,j,ccstamps[st][i][j]); + fluid_synth_program_change(synth,i,ccstamps[st][i][128]); + //fluid_synth_pitch_bend(synth,i,ccstamps[st][i][130]); + dpt=ccstamps[st][0][131];ctempo=dpt*divs/1000; + ctsn=ccstamps[st][0][132]>>24;ctsd=1<<((ccstamps[st][0][132]>>16)&0xFF); + cks=ccstamps[st][0][133]; + } +} +double CMidiPlayer::getFtime(){return ftime;} +void CMidiPlayer::getCurrentTimeSignature(int *n,int *d){*n=ctsn;*d=ctsd;} +void CMidiPlayer::getCurrentKeySignature(int *ks){*ks=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():"";} +double CMidiPlayer::getTempo(){return 60./(ctempo/1e6)/**ctsd/4.*/;} +uint32_t CMidiPlayer::getTCpaused(){return tcpaused;} +void CMidiPlayer::setTCpaused(uint32_t ps){tcpaused=ps;} +uint32_t CMidiPlayer::isFinished(){return finished;} +void CMidiPlayer::setResumed(){resumed=true;} +void CMidiPlayer::setWaitVoice(bool wv){waitvoice=wv;} +void CMidiPlayer::setGain(double gain){if(settings)fluid_settings_setnum(settings,"synth.gain",gain);} +int CMidiPlayer::getPolyphone(){return synth?fluid_synth_get_active_voice_count(synth):0;} +int CMidiPlayer::getMaxPolyphone(){return synth?fluid_synth_get_polyphony(synth):0;} +void CMidiPlayer::setMaxPolyphone(int p){if(synth)fluid_synth_set_polyphony(synth,p);} + +void CMidiPlayer::getChannelPreset(int ch,int *b,int *p,char *name) +{ + if(!synth)return(void)(b=0,p=0,strcpy(name,"")); + fluid_synth_channel_info_t info; + fluid_synth_get_channel_info(synth,ch,&info); + *b=info.bank;*p=info.program; + strcpy(name,info.name); +} +void CMidiPlayer::setChannelPreset(int ch,int b,int p) +{ + if(!synth)return; + fluid_synth_bank_select(synth,ch,b); + fluid_synth_program_change(synth,ch,p); +} +//16MSB..LSB1 +void CMidiPlayer::setBit(uint16_t &n, uint16_t bn, uint16_t b) +{n^=(-b^n)&(1<<bn);} +void CMidiPlayer::setMute(int ch,bool m) +{setBit(mute,ch,m?1:0);} +void CMidiPlayer::setSolo(int ch,bool s) +{setBit(solo,ch,s?1:0);} +int CMidiPlayer::getCC(int ch, int id) +{int ret=0;synth?fluid_synth_get_cc(synth,ch,id,&ret):0;return ret;} +void CMidiPlayer::setCC(int ch, int id, int val) +{synth?fluid_synth_cc(synth,ch,id,val):0;} +void CMidiPlayer::getReverbPara(double *r,double *d,double *w,double *l) +{ + if(!synth)return; + *r=fluid_synth_get_reverb_roomsize(synth); + *d=fluid_synth_get_reverb_damp(synth); + *w=fluid_synth_get_reverb_width(synth); + *l=fluid_synth_get_reverb_level(synth); +} +void CMidiPlayer::setReverbPara(int e,double r,double d,double w,double l) +{ + if(!synth)return; + fluid_synth_set_reverb_on(synth,e); + fluid_synth_set_reverb(synth,r,d,w,l); +} +void CMidiPlayer::getChorusPara(int *fb,double *l,double *r,double *d,int *type) +{ + if(!synth)return; + *fb=fluid_synth_get_chorus_nr(synth); + *l=fluid_synth_get_chorus_level(synth); + *r=fluid_synth_get_chorus_speed_Hz(synth); + *d=fluid_synth_get_chorus_depth_ms(synth); + *type=fluid_synth_get_chorus_type(synth); +} +void CMidiPlayer::setChorusPara(int e,int fb,double l,double r,double d,int type) +{ + if(!synth)return; + fluid_synth_set_chorus_on(synth,e); + fluid_synth_set_chorus(synth,fb,l,r,d,type); +} +fluid_settings_t* CMidiPlayer::getFluidSettings(){return settings;} +void CMidiPlayer::pushSoundFont(const char *sf) +{fluid_synth_sfload(synth,sf,1);} +int CMidiPlayer::getSFCount() +{return synth?fluid_synth_sfcount(synth):0;} +fluid_sfont_t* CMidiPlayer::getSFPtr(int sfid) +{return synth&&sfid<getSFCount()?fluid_synth_get_sfont(synth,sfid):NULL;} diff --git a/common/qmpmidiplay.hpp b/common/qmpmidiplay.hpp new file mode 100644 index 0000000..fafd677 --- /dev/null +++ b/common/qmpmidiplay.hpp @@ -0,0 +1,137 @@ +//sorry for the stupid C-like code... +#ifndef QMPMIDIPLAY_H +#define QMPMIDIPLAY_H +#include <cstring> +#include <cstdint> +#include <cstdlib> +#include <fluidsynth.h> +struct SEvent +{ + uint32_t iid,time,p1,p2; + uint8_t type; + char *str; + SEvent(){time=p1=p2=0;type=0;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 CMidiFile +{ + private: + SEvent *eventList[10000000]; + char *title,*copyright; + uint32_t eventc,std;//standard 0=? 1=GM 2=GM2 3=GS 4=XG + uint32_t fmt,trk,divs; + FILE *f; + int byteread,valid; + uint32_t notes,curt,curid; + + void error(int fatal,const char* format,...); + uint32_t readSW(); + uint32_t readDW(); + uint32_t readVL(); + int eventReader(); + void trackChunkReader(); + void headerChunkReader(); + int chunkReader(int hdrXp); + public: + CMidiFile(const char* fn); + ~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(); +}; +class CMidiPlayer +{ + private: + CMidiFile *midiFile; + uint32_t stamps[101]; + 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,rpnval; + uint16_t mute,solo; + double ftime; + bool sendSysEx,singleInstance,waitvoice; + fluid_settings_t* settings; + fluid_synth_t* synth; + fluid_audio_driver_t* adriver; + fluid_player_t* player; + uint32_t ctempo,ctsn,ctsd,dpt,divs,cks; + //raw tempo, timesig num., timesig den., delay per tick, division, keysig + //thread control + uint32_t tceptr,tcpaused,tcstop; + uint32_t finished,resumed; + + void setBit(uint16_t &n,uint16_t bn,uint16_t b); + void processEvent(const SEvent *e); + void processEventStub(const SEvent *e); + void playEvents(); + void fileTimer1Pass(); + void fileTimer2Pass(); + public: + CMidiPlayer(bool singleInst=false); + ~CMidiPlayer(); + bool playerLoadFile(const char* fn); + void playerInit(); + void fluidPreInitialize(); + void fluidInitialize(); + void fluidDeinitialize(); + void playerDeinit(); + void playerThread(); + void playerPanic(bool reset=false); + + void rendererLoadFile(const char* ofn); + void rendererInit(const char *fn); + void rendererThread(); + void rendererDeinit(); + + //playing control methods + uint32_t getStamp(int id); + uint32_t getTCeptr(); + void setTCeptr(uint32_t ep,uint32_t st); + uint32_t getTCpaused(); + void setTCpaused(uint32_t ps); + uint32_t isFinished(); + void setResumed(); + void setWaitVoice(bool wv); + + double getFtime(); + void getCurrentTimeSignature(int *n,int *d); + void getCurrentKeySignature(int *ks); + uint32_t getFileNoteCount(); + uint32_t getFileStandard(); + double getTempo(); + const char* getTitle(); + const char* getCopyright(); + + void setGain(double gain); + void sendSysX(bool send); + int getPolyphone(); + int getMaxPolyphone(); + void setMaxPolyphone(int p); + + void setChannelPreset(int ch,int b,int p); + void getChannelPreset(int ch,int *b,int *p,char *name); + void setMute(int ch,bool m); + void setSolo(int ch,bool s); + int getCC(int ch,int id); + void setCC(int ch,int id,int val); + void getReverbPara(double *r,double *d,double *w,double *l); + void setReverbPara(int e,double r,double d,double w,double l); + void getChorusPara(int *fb,double *l,double *r,double *d,int *type); + void setChorusPara(int e,int fb,double l,double r,double d,int type); + + fluid_settings_t* getFluidSettings(); + void pushSoundFont(const char* sf); + int getSFCount(); + fluid_sfont_t* getSFPtr(int sfid); +}; +#endif diff --git a/common/qmpmidiread.cpp b/common/qmpmidiread.cpp new file mode 100644 index 0000000..fd2e0d9 --- /dev/null +++ b/common/qmpmidiread.cpp @@ -0,0 +1,239 @@ +//CLI Midi file player based on libfluidsynth +//Midi file reading module +//Written by Chris Xiong, 2015 +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <cstdint> +#include <cstdarg> +#include <algorithm> +#include "qmpmidiplay.hpp" +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,...) +{ + va_list ap; + va_start(ap,format);vfprintf(stderr,format,ap);va_end(ap); + fprintf(stderr," at %#lx\n",ftell(f)); + if(fatal)throw 2; +} +uint32_t CMidiFile::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() +{ + 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 ret=0,t,c=0; + do + { + t=fgetc(f); + if(++c>4)error(1,"E: Variable length type overflow."); + ret<<=7;ret|=(t&0x7F); + }while(t&0x80); + byteread+=c; + return ret; +} +int CMidiFile::eventReader()//returns 0 if End of Track encountered +{ + uint32_t delta=readVL();curt+=delta; + char type=fgetc(f);++byteread;uint32_t p1,p2; + static char lasttype; +retry: + switch(type&0xF0) + { + case 0x80://Note Off + p1=fgetc(f);p2=fgetc(f);byteread+=2; + eventList[eventc++]=new SEvent(curid,curt,type,p1,p2); + break; + case 0x90://Note On + p1=fgetc(f);p2=fgetc(f);byteread+=2; + if(p2) + { + ++notes; + eventList[eventc++]=new SEvent(curid,curt,type,p1,p2); + } + else + eventList[eventc++]=new SEvent(curid,curt,(type&0x0F)|0x80,p1,p2); + break; + case 0xA0://Note Aftertouch + p1=fgetc(f);p2=fgetc(f);byteread+=2; + eventList[eventc++]=new SEvent(curid,curt,type,p1,p2); + break; + case 0xB0://Controller Change + p1=fgetc(f);p2=fgetc(f);byteread+=2; + eventList[eventc++]=new SEvent(curid,curt,type,p1,p2); + break; + case 0xC0://Patch Change + p1=fgetc(f);++byteread; + eventList[eventc++]=new SEvent(curid,curt,type,p1,0); + break; + case 0xD0://Channel Aftertouch + p1=fgetc(f);++byteread; + eventList[eventc++]=new SEvent(curid,curt,type,p1,0); + break; + case 0xE0://Pitch wheel + p1=fgetc(f);p2=fgetc(f);byteread+=2; + eventList[eventc++]=new SEvent(curid,curt,type,(p1|(p2<<7))&0x3FFF,0); + break; + case 0xF0: + if((type&0x0F)==0x0F)//Meta Event + { + char metatype=fgetc(f);++byteread; + switch(metatype) + { + case 0x00://Sequence Number + fgetc(f);fgetc(f);fgetc(f); + byteread+=3; + break; + case 0x20://Channel Prefix + fgetc(f);fgetc(f);byteread+=2; + break; + case 0x2F://End of Track + fgetc(f);++byteread; + return 0; + break; + case 0x51://Set Tempo + p1=readDW();p1&=0x00FFFFFF; + eventList[eventc++]=new SEvent(curid,curt,type,metatype,p1); + break; + case 0x54://SMTPE offset, not handled. + fgetc(f);fgetc(f);fgetc(f); + fgetc(f);fgetc(f);fgetc(f); + byteread+=6; + break; + case 0x58://Time signature + fgetc(f);++byteread; + p1=readDW(); + eventList[eventc++]=new SEvent(curid,curt,type,metatype,p1); + break; + case 0x59://Key signature + fgetc(f);++byteread; + p1=readSW(); + eventList[eventc++]=new SEvent(curid,curt,type,metatype,p1); + break; + case 0x01:case 0x02:case 0x03: + case 0x04:case 0x05:case 0x06: + case 0x07:case 0x7F:default://text-like meta + { + uint32_t len=readVL(),c;char* str=NULL; + if(len<=1024&&len>0)str=new char[len+8]; + for(c=0;c<len;++c) + { + ++byteread;if(str)str[c]=fgetc(f);else fgetc(f); + } + if(str)str[c]='\0';eventList[eventc++]=new SEvent(curid,curt,type,metatype,0,str); + if(str&&metatype==0x03&&!title) + { + title=new char[len+8]; + strcpy(title,str); + } + if(str&&metatype==0x02&&!copyright) + { + copyright=new char[len+8]; + strcpy(copyright,str); + } + if(len<=1024&&len>0)delete[] str; + } + } + } + else if((type&0x0F)==0x00||(type&0x0F)==0x07)//SysEx + { + uint32_t len=readVL(),c;char* str=NULL; + str=new char[len+8]; + if((type&0x0F)==0x00) + { + str[0]=0xF0;++len; + for(c=1;c<len;++c){++byteread;str[c]=fgetc(f);} + } + else for(c=0;c<len;++c){++byteread;str[c]=fgetc(f);} + eventList[eventc++]=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; + } + else error(0,"W: Unknown event type %#x",type); + break; + default: + fseek(f,-1,SEEK_CUR);--byteread;type=lasttype;goto retry; + } + lasttype=type;++curid; + return 1; +} +void CMidiFile::trackChunkReader() +{ + int chnklen=readDW();byteread=0;curt=0;curid=0; + while(/*byteread<chnklen&&*/eventReader()); + if(byteread<chnklen) + { + error(0,"W: Extra bytes after EOT event."); + while(byteread<chnklen){fgetc(f);++byteread;} + } + /*if(byteread>chnklen) + { + error(1,"E: Read past end of track."); + }*/ +} +void CMidiFile::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."); + for(;byteread<chnklen;++byteread){fgetc(f);} +} +int CMidiFile::chunkReader(int hdrXp) +{ + char hdr[6]; + if(!fgets(hdr,5,f))error(1,"E: Unexpected EOF."); + if(hdrXp) + if(strncmp(hdr,"MThd",4)){error(1,"E: Wrong MIDI header.");throw;} + else return headerChunkReader(),0; + else + if(strncmp(hdr,"MTrk",4)) + { + error(0,"W: Wrong track chunk header. Ignoring the whole chunk."); + for(int chnklen=readDW();chnklen>0;--chnklen)fgetc(f);return 0; + } + else return trackChunkReader(),1; +} +CMidiFile::CMidiFile(const char* fn) +{ + title=copyright=NULL;notes=eventc=0;std=0;valid=1; + try + { + if(!(f=fopen(fn,"rb")))throw (fprintf(stderr,"E: file %s doesn't exist!\n",fn),2); + chunkReader(1); + for(uint32_t i=0;i<trk;i+=chunkReader(0)); + fclose(f); + std::sort(eventList,eventList+eventc,cmp); + } + catch(int){fprintf(stderr,"E: %s is not a supported file.\n",fn);valid=0;} +} +CMidiFile::~CMidiFile() +{ + for(uint32_t i=0;i<eventc;++i)delete eventList[i]; + if(title)delete[] title;if(copyright)delete[] copyright; +} +const SEvent* CMidiFile::getEvent(uint32_t id){return id<eventc?eventList[id]:NULL;} +uint32_t CMidiFile::getEventCount(){return eventc;} +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;} |