From a7407edaf81c685d4a389785a405a53a5de4b148 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Tue, 12 May 2020 00:58:40 +0800 Subject: Format EVERYTHING. Hopefully this will make the source code less horrendous and actually readable. The command used was: ``` astyle --suffix=none --style=allman --attach-extern-c --attach-closing-while --indent-switches --indent-after-parens --pad-oper --pad-header --unpad-paren --align-pointer=name --recursive './*.cpp,*.hpp' ``` --- core/qmpmidioutfluid.cpp | 640 +++++++++++++------------ core/qmpmidioutfluid.hpp | 148 +++--- core/qmpmidioutrtmidi.cpp | 636 +++++++++++++------------ core/qmpmidioutrtmidi.hpp | 72 +-- core/qmpmidiplay.cpp | 1157 ++++++++++++++++++++++++++------------------- core/qmpmidiplay.hpp | 301 ++++++------ core/qmpmidiread.cpp | 552 +++++++++++---------- 7 files changed, 1931 insertions(+), 1575 deletions(-) (limited to 'core') diff --git a/core/qmpmidioutfluid.cpp b/core/qmpmidioutfluid.cpp index 649cb1c..bfa6016 100644 --- a/core/qmpmidioutfluid.cpp +++ b/core/qmpmidioutfluid.cpp @@ -4,375 +4,431 @@ #include "qmpmidioutfluid.hpp" qmpMidiOutFluid::qmpMidiOutFluid() { - settings=new_fluid_settings(); - synth=nullptr;adriver=nullptr; + settings = new_fluid_settings(); + synth = nullptr; + adriver = nullptr; } qmpMidiOutFluid::~qmpMidiOutFluid() { - delete_fluid_settings(settings); - settings=nullptr; + delete_fluid_settings(settings); + settings = nullptr; } void qmpMidiOutFluid::registerOptions(qmpPluginAPI *coreapi) { - default_driver=-1; - fluid_settings_t *fsettings=new_fluid_settings(); - auto insert_driver=[](void* d,const char*,const char* driver)->void{ - qmpMidiOutFluid *me=static_cast(d); - me->drivers.push_back(std::string(driver)); + default_driver = -1; + fluid_settings_t *fsettings = new_fluid_settings(); + auto insert_driver = [](void *d, const char *, const char *driver)->void + { + qmpMidiOutFluid *me = static_cast(d); + me->drivers.push_back(std::string(driver)); #ifdef WIN32 - if(std::string(driver)=="waveout") + if (std::string(driver) == "waveout") #else - if(std::string(driver)=="pulseaudio") - me->default_driver=static_cast(me->drivers.size()-1); + if (std::string(driver) == "pulseaudio") + me->default_driver = static_cast(me->drivers.size() - 1); #endif - }; - fluid_settings_foreach_option(fsettings,"audio.driver",this,insert_driver); - delete_fluid_settings(fsettings); - coreapi->registerOptionEnumInt("Audio","Audio Driver","FluidSynth/AudioDriver",drivers,default_driver); - coreapi->registerOptionInt("Audio","Audio Buffer Size","FluidSynth/BufSize",64,8192, + }; + fluid_settings_foreach_option(fsettings, "audio.driver", this, insert_driver); + delete_fluid_settings(fsettings); + coreapi->registerOptionEnumInt("Audio", "Audio Driver", "FluidSynth/AudioDriver", drivers, default_driver); + coreapi->registerOptionInt("Audio", "Audio Buffer Size", "FluidSynth/BufSize", 64, 8192, #ifdef WIN32 - 512 + 512 #else - 64 + 64 #endif - ); - coreapi->registerOptionInt("Audio","Audio Buffer Count","FluidSynth/BufCnt",2,64, + ); + coreapi->registerOptionInt("Audio", "Audio Buffer Count", "FluidSynth/BufCnt", 2, 64, #ifdef WIN32 - 8 + 8 #else - 16 + 16 #endif - ); - coreapi->registerOptionEnumInt("Audio","Sample Format","FluidSynth/SampleFormat",{"16bits","float"},0); - coreapi->registerOptionInt("Audio","Sample Rate","FluidSynth/SampleRate",8000,96000,48000); - coreapi->registerOptionInt("Audio","Max Polyphony","FluidSynth/Polyphony",1,65535,256); - coreapi->registerOptionInt("Audio","CPU Cores","FluidSynth/Threads",1,256,1); - coreapi->registerOptionBool("Audio","Auto Bank Select Mode","FluidSynth/AutoBS",true); - coreapi->registerOptionEnumInt("Audio","Bank Select Mode","FluidSynth/BankSelect",{"GM","GS","XG","MMA"},1); + ); + coreapi->registerOptionEnumInt("Audio", "Sample Format", "FluidSynth/SampleFormat", {"16bits", "float"}, 0); + coreapi->registerOptionInt("Audio", "Sample Rate", "FluidSynth/SampleRate", 8000, 96000, 48000); + coreapi->registerOptionInt("Audio", "Max Polyphony", "FluidSynth/Polyphony", 1, 65535, 256); + coreapi->registerOptionInt("Audio", "CPU Cores", "FluidSynth/Threads", 1, 256, 1); + coreapi->registerOptionBool("Audio", "Auto Bank Select Mode", "FluidSynth/AutoBS", true); + coreapi->registerOptionEnumInt("Audio", "Bank Select Mode", "FluidSynth/BankSelect", {"GM", "GS", "XG", "MMA"}, 1); } void qmpMidiOutFluid::deviceInit() { - synth=new_fluid_synth(settings); - if(!synth){fputs("Error creating fluidsynth instance!",stderr);return;} - fluid_set_log_function(FLUID_DBG,nullptr,nullptr); - fluid_set_log_function(FLUID_INFO,nullptr,nullptr); - fluid_set_log_function(FLUID_WARN,nullptr,nullptr); - fluid_set_log_function(FLUID_ERR,fluid_default_log_function,nullptr); - fluid_set_log_function(FLUID_PANIC,fluid_default_log_function,nullptr); - adriver=new_fluid_audio_driver(settings,synth); - if(!adriver) - { - fputs("Error creating fluidsynth audio driver!",stderr); - delete_fluid_synth(synth);synth=nullptr; - return; - } - fluid_synth_set_chorus(synth,3,2.0,0.3,8.0, - FLUID_CHORUS_MOD_SINE); - bnk.clear();pst.clear(); -} -void qmpMidiOutFluid::deviceDeinit(){deviceDeinit(false);} + synth = new_fluid_synth(settings); + if (!synth) + { + fputs("Error creating fluidsynth instance!", stderr); + return; + } + fluid_set_log_function(FLUID_DBG, nullptr, nullptr); + fluid_set_log_function(FLUID_INFO, nullptr, nullptr); + fluid_set_log_function(FLUID_WARN, nullptr, nullptr); + fluid_set_log_function(FLUID_ERR, fluid_default_log_function, nullptr); + fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, nullptr); + adriver = new_fluid_audio_driver(settings, synth); + if (!adriver) + { + fputs("Error creating fluidsynth audio driver!", stderr); + delete_fluid_synth(synth); + synth = nullptr; + return; + } + fluid_synth_set_chorus(synth, 3, 2.0, 0.3, 8.0, FLUID_CHORUS_MOD_SINE); + bnk.clear(); + pst.clear(); +} +void qmpMidiOutFluid::deviceDeinit() +{ + deviceDeinit(false); +} void qmpMidiOutFluid::deviceDeinit(bool freshsettings) { - if(!synth||!adriver)return; - delete_fluid_audio_driver(adriver); - delete_fluid_synth(synth); - synth=nullptr;adriver=nullptr; - bnk.clear();pst.clear(); - if(freshsettings) - { - delete_fluid_settings(settings); - settings=new_fluid_settings(); - } -} -void qmpMidiOutFluid::basicMessage(uint8_t type,uint8_t p1,uint8_t p2) -{ - uint8_t chan=type&0x0F; - switch(type&0xF0) - { - case 0x80: - fluid_synth_noteoff(synth,chan,p1); - break; - case 0x90: - if(p2)fluid_synth_noteon(synth,chan,p1,p2); - else fluid_synth_noteoff(synth,chan,p1); - break; - case 0xB0: - fluid_synth_cc(synth,chan,p1,p2); - break; - case 0xC0: - fluid_synth_program_change(synth,chan,p1); - break; - case 0xE0: - fluid_synth_pitch_bend(synth,chan,(p1|p2<<7)&0x3fff); - break; - } -} -void qmpMidiOutFluid::extendedMessage(uint32_t length,const char *data) -{ - int rlen=0; - fluid_synth_sysex(synth,data,int(length),nullptr,&rlen,nullptr,0); -} -void qmpMidiOutFluid::rpnMessage(uint8_t ch,uint16_t type,uint16_t val) -{ - if(type==0)fluid_synth_pitch_wheel_sens(synth,ch,val>>7); - else - { - fluid_synth_cc(synth,ch,0x64,type&0x7F); - fluid_synth_cc(synth,ch,0x65,type>>7); - fluid_synth_cc(synth,ch,0x06,val>>7); - fluid_synth_cc(synth,ch,0x26,val&0x7F); - } -} -void qmpMidiOutFluid::nrpnMessage(uint8_t ch,uint16_t type,uint16_t val) -{ - fluid_synth_cc(synth,ch,0x62,type&0x7F); - fluid_synth_cc(synth,ch,0x63,type>>7); - fluid_synth_cc(synth,ch,0x06,val>>7); - fluid_synth_cc(synth,ch,0x26,val&0x7F); + if (!synth || !adriver) + return; + delete_fluid_audio_driver(adriver); + delete_fluid_synth(synth); + synth = nullptr; + adriver = nullptr; + bnk.clear(); + pst.clear(); + if (freshsettings) + { + delete_fluid_settings(settings); + settings = new_fluid_settings(); + } +} +void qmpMidiOutFluid::basicMessage(uint8_t type, uint8_t p1, uint8_t p2) +{ + uint8_t chan = type & 0x0F; + switch (type & 0xF0) + { + case 0x80: + fluid_synth_noteoff(synth, chan, p1); + break; + case 0x90: + if (p2) + fluid_synth_noteon(synth, chan, p1, p2); + else + fluid_synth_noteoff(synth, chan, p1); + break; + case 0xB0: + fluid_synth_cc(synth, chan, p1, p2); + break; + case 0xC0: + fluid_synth_program_change(synth, chan, p1); + break; + case 0xE0: + fluid_synth_pitch_bend(synth, chan, (p1 | p2 << 7) & 0x3fff); + break; + } +} +void qmpMidiOutFluid::extendedMessage(uint32_t length, const char *data) +{ + int rlen = 0; + fluid_synth_sysex(synth, data, int(length), nullptr, &rlen, nullptr, 0); +} +void qmpMidiOutFluid::rpnMessage(uint8_t ch, uint16_t type, uint16_t val) +{ + if (type == 0) + fluid_synth_pitch_wheel_sens(synth, ch, val >> 7); + else + { + fluid_synth_cc(synth, ch, 0x64, type & 0x7F); + fluid_synth_cc(synth, ch, 0x65, type >> 7); + fluid_synth_cc(synth, ch, 0x06, val >> 7); + fluid_synth_cc(synth, ch, 0x26, val & 0x7F); + } +} +void qmpMidiOutFluid::nrpnMessage(uint8_t ch, uint16_t type, uint16_t val) +{ + fluid_synth_cc(synth, ch, 0x62, type & 0x7F); + fluid_synth_cc(synth, ch, 0x63, type >> 7); + fluid_synth_cc(synth, ch, 0x06, val >> 7); + fluid_synth_cc(synth, ch, 0x26, val & 0x7F); } void qmpMidiOutFluid::panic(uint8_t ch) { - fluid_synth_cc(synth,ch,64,0); - fluid_synth_pitch_bend(synth,ch,8192); - fluid_synth_all_notes_off(synth,ch); + fluid_synth_cc(synth, ch, 64, 0); + fluid_synth_pitch_bend(synth, ch, 8192); + fluid_synth_all_notes_off(synth, ch); } void qmpMidiOutFluid::reset(uint8_t ch) { - if(!~ch){fluid_synth_system_reset(synth);return;} - this->panic(ch); - for(int i=0;i<128;++i) - fluid_synth_cc(synth,ch,i,0); - if(ch==9) - fluid_synth_cc(synth,ch,0,127); - else - fluid_synth_cc(synth,ch,0,0); - fluid_synth_cc(synth,ch,7,100); - fluid_synth_cc(synth,ch,8,64); - fluid_synth_cc(synth,ch,10,64); - fluid_synth_cc(synth,ch,11,127); - fluid_synth_pitch_wheel_sens(synth,ch,2); -} -void qmpMidiOutFluid::onMapped(uint8_t,int) -{ -} -void qmpMidiOutFluid::onUnmapped(uint8_t,int) -{ -} -std::vector> qmpMidiOutFluid::getBankList() -{ - return bnk; -} -std::vector> qmpMidiOutFluid::getPresets(uint16_t bank) -{ - std::vector> ret; - if(pst.find(bank)==pst.end())return ret; - for(uint8_t i=0;i<128;++i) - { - if(pst[bank][i].length()) - ret.emplace_back(i,pst[bank][i]); - } - return ret; -} -std::string qmpMidiOutFluid::getPresetName(uint16_t bank,uint8_t preset) -{ - if(pst.find(bank)==pst.end())return ""; - //should consult fluidsynth instead? (4 bank mapping methods) - return pst[bank][preset]; -} -bool qmpMidiOutFluid::getChannelPreset(int ch,uint16_t *bank,uint8_t *preset,std::string &presetname) -{ - if(!synth)return false; - fluid_preset_t* chpreset=fluid_synth_get_channel_preset(synth,ch); - if(!chpreset) - { - *bank=*preset=-1; - presetname="---"; - return true; - } - *bank=fluid_preset_get_banknum(chpreset); - *preset=fluid_preset_get_num(chpreset); - presetname=fluid_preset_get_name(chpreset); - return true; -} -uint8_t qmpMidiOutFluid::getInitialCCValue(uint8_t cc,uint8_t) -{ - switch(cc) - { - case 11:return 127; - case 7:return 100; - case 8:case 10:case 71:case 72: - case 73:case 74:case 75:case 76: - case 77:case 78:return 64; - case 129:return 2; - default:return 0; - } -} -void qmpMidiOutFluid::setOptStr(const char *opt,const char *val) -{ - fluid_settings_setstr(settings,opt,val); -} -void qmpMidiOutFluid::setOptInt(const char *opt,int val) -{ - fluid_settings_setint(settings,opt,val); -} -void qmpMidiOutFluid::setOptNum(const char *opt,double val) -{ - fluid_settings_setnum(settings,opt,val); + if (!~ch) + { + fluid_synth_system_reset(synth); + return; + } + this->panic(ch); + + for (int i = 0; i < 128; ++i) + fluid_synth_cc(synth, ch, i, 0); + + if (ch == 9) + fluid_synth_cc(synth, ch, 0, 127); + else + fluid_synth_cc(synth, ch, 0, 0); + + fluid_synth_cc(synth, ch, 7, 100); + fluid_synth_cc(synth, ch, 8, 64); + fluid_synth_cc(synth, ch, 10, 64); + fluid_synth_cc(synth, ch, 11, 127); + fluid_synth_pitch_wheel_sens(synth, ch, 2); +} +void qmpMidiOutFluid::onMapped(uint8_t, int) +{ +} +void qmpMidiOutFluid::onUnmapped(uint8_t, int) +{ +} +std::vector> qmpMidiOutFluid::getBankList() +{ + return bnk; +} +std::vector> qmpMidiOutFluid::getPresets(uint16_t bank) +{ + std::vector> ret; + if (pst.find(bank) == pst.end()) + return ret; + for (uint8_t i = 0; i < 128; ++i) + { + if (pst[bank][i].length()) + ret.emplace_back(i, pst[bank][i]); + } + return ret; +} +std::string qmpMidiOutFluid::getPresetName(uint16_t bank, uint8_t preset) +{ + if (pst.find(bank) == pst.end()) + return ""; + //should consult fluidsynth instead? (4 bank mapping methods) + return pst[bank][preset]; +} +bool qmpMidiOutFluid::getChannelPreset(int ch, uint16_t *bank, uint8_t *preset, std::string &presetname) +{ + if (!synth) + return false; + fluid_preset_t *chpreset = fluid_synth_get_channel_preset(synth, ch); + if (!chpreset) + { + *bank = *preset = -1; + presetname = "---"; + return true; + } + *bank = fluid_preset_get_banknum(chpreset); + *preset = fluid_preset_get_num(chpreset); + presetname = fluid_preset_get_name(chpreset); + return true; +} +uint8_t qmpMidiOutFluid::getInitialCCValue(uint8_t cc, uint8_t) +{ + switch (cc) + { + case 11: + return 127; + case 7: + return 100; + case 8: + case 10: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + return 64; + case 129: + return 2; + default: + return 0; + } +} +void qmpMidiOutFluid::setOptStr(const char *opt, const char *val) +{ + fluid_settings_setstr(settings, opt, val); +} +void qmpMidiOutFluid::setOptInt(const char *opt, int val) +{ + fluid_settings_setint(settings, opt, val); +} +void qmpMidiOutFluid::setOptNum(const char *opt, double val) +{ + fluid_settings_setnum(settings, opt, val); } void qmpMidiOutFluid::loadSFont(const char *path) { - if(synth)fluid_synth_sfload(synth,path,1); - update_preset_list(); + if (synth) + fluid_synth_sfload(synth, path, 1); + update_preset_list(); } int qmpMidiOutFluid::getSFCount() { - return synth?fluid_synth_sfcount(synth):0; + return synth ? fluid_synth_sfcount(synth) : 0; } void qmpMidiOutFluid::update_preset_list() { - bnk.clear();pst.clear(); - for(int i=getSFCount()-1;i>=0;--i) - { - fluid_sfont_t* psf=fluid_synth_get_sfont(synth,i); - fluid_preset_t* preset; - fluid_sfont_iteration_start(psf); - while(preset=fluid_sfont_iteration_next(psf)) - { - uint16_t b=fluid_preset_get_banknum(preset); - uint8_t p=fluid_preset_get_num(preset); - if(bnk.empty()||bnk.back().first!=b) - bnk.emplace_back(b,""); - if(pst[b].empty())pst[b].resize(128); - pst[b][p]=std::string(fluid_preset_get_name(preset)); - if(!pst[b][p].length())pst[b][p]=" "; - } - } - std::sort(bnk.begin(),bnk.end()); - bnk.erase(std::unique(bnk.begin(),bnk.end()),bnk.end()); + bnk.clear(); + pst.clear(); + for (int i = getSFCount() - 1; i >= 0; --i) + { + fluid_sfont_t *psf = fluid_synth_get_sfont(synth, i); + fluid_preset_t *preset; + fluid_sfont_iteration_start(psf); + while ((preset = fluid_sfont_iteration_next(psf))) + { + uint16_t b = fluid_preset_get_banknum(preset); + uint8_t p = fluid_preset_get_num(preset); + if (bnk.empty() || bnk.back().first != b) + bnk.emplace_back(b, ""); + if (pst[b].empty()) + pst[b].resize(128); + pst[b][p] = std::string(fluid_preset_get_name(preset)); + if (!pst[b][p].length()) + pst[b][p] = " "; + } + } + std::sort(bnk.begin(), bnk.end()); + bnk.erase(std::unique(bnk.begin(), bnk.end()), bnk.end()); } int qmpMidiOutFluid::getPolyphone() { - return synth?fluid_synth_get_active_voice_count(synth):0; + return synth ? fluid_synth_get_active_voice_count(synth) : 0; } int qmpMidiOutFluid::getMaxPolyphone() { - return synth?fluid_synth_get_polyphony(synth):0; + return synth ? fluid_synth_get_polyphony(synth) : 0; } void qmpMidiOutFluid::setGain(double gain) { - if(settings)fluid_settings_setnum(settings,"synth.gain",gain); -} -void qmpMidiOutFluid::getChannelInfo(int ch,int *b,int *p,char *s) -{ - if(!synth)return; - fluid_preset_t* chpreset=fluid_synth_get_channel_preset(synth,ch); - if(!chpreset) - { - *b=*p=-1; - strcpy(s,"---"); - return; - } - *b=fluid_preset_get_banknum(chpreset); - *p=fluid_preset_get_num(chpreset); - strncpy(s,fluid_preset_get_name(chpreset),256); -} -void qmpMidiOutFluid::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 qmpMidiOutFluid::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 qmpMidiOutFluid::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(synth); - *d=fluid_synth_get_chorus_depth(synth); - *type=fluid_synth_get_chorus_type(synth); -} -void qmpMidiOutFluid::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); + if (settings) + fluid_settings_setnum(settings, "synth.gain", gain); +} +void qmpMidiOutFluid::getChannelInfo(int ch, int *b, int *p, char *s) +{ + if (!synth) + return; + fluid_preset_t *chpreset = fluid_synth_get_channel_preset(synth, ch); + if (!chpreset) + { + *b = *p = -1; + strcpy(s, "---"); + return; + } + *b = fluid_preset_get_banknum(chpreset); + *p = fluid_preset_get_num(chpreset); + strncpy(s, fluid_preset_get_name(chpreset), 256); +} +void qmpMidiOutFluid::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 qmpMidiOutFluid::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 qmpMidiOutFluid::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(synth); + *d = fluid_synth_get_chorus_depth(synth); + *type = fluid_synth_get_chorus_type(synth); +} +void qmpMidiOutFluid::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); } -qmpFileRendererFluid::qmpFileRendererFluid(const char *_fn,const char *_ofn) +qmpFileRendererFluid::qmpFileRendererFluid(const char *_fn, const char *_ofn) { - settings=new_fluid_settings(); - fluid_settings_setstr(settings,"audio.file.name",_ofn); - fn=std::string(_fn); - finished=false; + settings = new_fluid_settings(); + fluid_settings_setstr(settings, "audio.file.name", _ofn); + fn = std::string(_fn); + finished = false; } qmpFileRendererFluid::~qmpFileRendererFluid() { - if(player||synth||settings)renderDeinit(); + if (player || synth || settings) + renderDeinit(); } void qmpFileRendererFluid::renderInit() { - synth=new_fluid_synth(settings); - player=new_fluid_player(synth); - fluid_player_add(player,fn.c_str()); + synth = new_fluid_synth(settings); + player = new_fluid_player(synth); + fluid_player_add(player, fn.c_str()); } void qmpFileRendererFluid::renderDeinit() { - delete_fluid_player(player); - delete_fluid_synth(synth); - delete_fluid_settings(settings); - player=nullptr;synth=nullptr;settings=nullptr; + delete_fluid_player(player); + delete_fluid_synth(synth); + delete_fluid_settings(settings); + player = nullptr; + synth = nullptr; + settings = nullptr; } void qmpFileRendererFluid::renderWorker() { - 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=true; + 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 = true; } -void qmpFileRendererFluid::setOptStr(const char *opt,const char *val) +void qmpFileRendererFluid::setOptStr(const char *opt, const char *val) { - fluid_settings_setstr(settings,opt,val); + fluid_settings_setstr(settings, opt, val); } -void qmpFileRendererFluid::setOptInt(const char *opt,int val) +void qmpFileRendererFluid::setOptInt(const char *opt, int val) { - fluid_settings_setint(settings,opt,val); + fluid_settings_setint(settings, opt, val); } -void qmpFileRendererFluid::setOptNum(const char *opt,double val) +void qmpFileRendererFluid::setOptNum(const char *opt, double val) { - fluid_settings_setnum(settings,opt,val); + fluid_settings_setnum(settings, opt, val); } void qmpFileRendererFluid::loadSFont(const char *path) { - if(synth)fluid_synth_sfload(synth,path,1); + if (synth) + fluid_synth_sfload(synth, path, 1); } bool qmpFileRendererFluid::isFinished() { - return finished; + return finished; } void qmpFileRendererFluid::setGain(double gain) { - if(settings)fluid_settings_setnum(settings,"synth.gain",gain); + if (settings) + fluid_settings_setnum(settings, "synth.gain", gain); } -void qmpFileRendererFluid::setReverbPara(int e,double r,double d,double w,double l) +void qmpFileRendererFluid::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); + if (!synth) + return; + fluid_synth_set_reverb_on(synth, e); + fluid_synth_set_reverb(synth, r, d, w, l); } -void qmpFileRendererFluid::setChorusPara(int e,int fb,double l,double r,double d,int type) +void qmpFileRendererFluid::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); + if (!synth) + return; + fluid_synth_set_chorus_on(synth, e); + fluid_synth_set_chorus(synth, fb, l, r, d, type); } diff --git a/core/qmpmidioutfluid.hpp b/core/qmpmidioutfluid.hpp index 63963ee..acefb68 100644 --- a/core/qmpmidioutfluid.hpp +++ b/core/qmpmidioutfluid.hpp @@ -8,85 +8,85 @@ #include class IFluidSettings { - public: - virtual void setOptStr(const char* opt,const char* val)=0; - virtual void setOptInt(const char* opt,int val)=0; - virtual void setOptNum(const char* opt,double val)=0; - virtual void loadSFont(const char* path)=0; - virtual void setGain(double gain)=0; - virtual void setReverbPara(int e,double r,double d,double w,double l)=0; - virtual void setChorusPara(int e,int fb,double l,double r,double d,int type)=0; +public: + virtual void setOptStr(const char *opt, const char *val) = 0; + virtual void setOptInt(const char *opt, int val) = 0; + virtual void setOptNum(const char *opt, double val) = 0; + virtual void loadSFont(const char *path) = 0; + virtual void setGain(double gain) = 0; + virtual void setReverbPara(int e, double r, double d, double w, double l) = 0; + virtual void setChorusPara(int e, int fb, double l, double r, double d, int type) = 0; }; -class qmpMidiOutFluid:public qmpMidiOutDevice,public IFluidSettings +class qmpMidiOutFluid: public qmpMidiOutDevice, public IFluidSettings { - private: - fluid_settings_t* settings; - fluid_synth_t* synth; - fluid_audio_driver_t* adriver; - std::vector> bnk; - std::unordered_map> pst; - qmpPluginAPI* coreapi; - std::vector drivers; - int default_driver=-1; - void update_preset_list(); - public: - qmpMidiOutFluid(); - ~qmpMidiOutFluid(); - void registerOptions(qmpPluginAPI *coreapi); - void deviceInit(); - void deviceDeinit(); - void deviceDeinit(bool freshsettings); - void basicMessage(uint8_t type,uint8_t p1,uint8_t p2); - void extendedMessage(uint32_t length,const char *data); - void rpnMessage(uint8_t ch,uint16_t type,uint16_t val); - void nrpnMessage(uint8_t ch,uint16_t type,uint16_t val); - void panic(uint8_t ch); - void reset(uint8_t ch); - void onMapped(uint8_t ch,int refcnt); - void onUnmapped(uint8_t ch,int refcnt); - std::vector> getBankList(); - std::vector> getPresets(uint16_t bank); - std::string getPresetName(uint16_t bank,uint8_t preset); - bool getChannelPreset(int ch,uint16_t *bank,uint8_t *preset,std::string &presetname); - uint8_t getInitialCCValue(uint8_t cc,uint8_t ch); - //FluidSynth specific stuff - void setOptStr(const char* opt,const char* val); - void setOptInt(const char* opt,int val); - void setOptNum(const char* opt,double val); - void loadSFont(const char* path); - int getSFCount(); +private: + fluid_settings_t *settings; + fluid_synth_t *synth; + fluid_audio_driver_t *adriver; + std::vector> bnk; + std::unordered_map> pst; + qmpPluginAPI *coreapi; + std::vector drivers; + int default_driver = -1; + void update_preset_list(); +public: + qmpMidiOutFluid(); + ~qmpMidiOutFluid(); + void registerOptions(qmpPluginAPI *coreapi); + void deviceInit(); + void deviceDeinit(); + void deviceDeinit(bool freshsettings); + void basicMessage(uint8_t type, uint8_t p1, uint8_t p2); + void extendedMessage(uint32_t length, const char *data); + void rpnMessage(uint8_t ch, uint16_t type, uint16_t val); + void nrpnMessage(uint8_t ch, uint16_t type, uint16_t val); + void panic(uint8_t ch); + void reset(uint8_t ch); + void onMapped(uint8_t ch, int refcnt); + void onUnmapped(uint8_t ch, int refcnt); + std::vector> getBankList(); + std::vector> getPresets(uint16_t bank); + std::string getPresetName(uint16_t bank, uint8_t preset); + bool getChannelPreset(int ch, uint16_t *bank, uint8_t *preset, std::string &presetname); + uint8_t getInitialCCValue(uint8_t cc, uint8_t ch); + //FluidSynth specific stuff + void setOptStr(const char *opt, const char *val); + void setOptInt(const char *opt, int val); + void setOptNum(const char *opt, double val); + void loadSFont(const char *path); + int getSFCount(); - int getPolyphone(); - int getMaxPolyphone(); - void setGain(double gain); - void getChannelInfo(int ch,int *b,int *p,char *s); - 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); + int getPolyphone(); + int getMaxPolyphone(); + void setGain(double gain); + void getChannelInfo(int ch, int *b, int *p, char *s); + 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); }; -class qmpFileRendererFluid:public IFluidSettings +class qmpFileRendererFluid: public IFluidSettings { - private: - fluid_settings_t* settings; - fluid_synth_t* synth; - fluid_player_t* player; - bool finished; - std::string fn; - public: - qmpFileRendererFluid(const char* _fn,const char* _ofn); - ~qmpFileRendererFluid(); - void renderInit(); - void renderDeinit(); - void renderWorker(); - void setOptStr(const char* opt,const char* val); - void setOptInt(const char* opt,int val); - void setOptNum(const char* opt,double val); - void loadSFont(const char *path); - bool isFinished(); - void setGain(double gain); - void setReverbPara(int e,double r,double d,double w,double l); - void setChorusPara(int e,int fb,double l,double r,double d,int type); +private: + fluid_settings_t *settings; + fluid_synth_t *synth; + fluid_player_t *player; + bool finished; + std::string fn; +public: + qmpFileRendererFluid(const char *_fn, const char *_ofn); + ~qmpFileRendererFluid(); + void renderInit(); + void renderDeinit(); + void renderWorker(); + void setOptStr(const char *opt, const char *val); + void setOptInt(const char *opt, int val); + void setOptNum(const char *opt, double val); + void loadSFont(const char *path); + bool isFinished(); + void setGain(double gain); + void setReverbPara(int e, double r, double d, double w, double l); + void setChorusPara(int e, int fb, double l, double r, double d, int type); }; #endif // QMPMIDIOUTFLUID_H diff --git a/core/qmpmidioutrtmidi.cpp b/core/qmpmidioutrtmidi.cpp index 2f7125e..04594e4 100644 --- a/core/qmpmidioutrtmidi.cpp +++ b/core/qmpmidioutrtmidi.cpp @@ -7,353 +7,405 @@ #include "RtMidi.h" #include "qmpmidioutrtmidi.hpp" -void split(std::string s,char c,std::deque& v) +void split(std::string s, char c, std::deque &v) { - v.clear(); - for(size_t anch=0;;) - { - std::string sec; - if(s.find(c,anch)==std::string::npos) - sec=s.substr(anch); - else sec=s.substr(anch,s.find(c,anch)-anch); - if(!sec.empty())v.push_back(sec); - if(s.find(c,anch)==std::string::npos)break; - anch=s.find(c,anch)+1; - } + v.clear(); + for (size_t anch = 0;;) + { + std::string sec; + if (s.find(c, anch) == std::string::npos) + sec = s.substr(anch); + else + sec = s.substr(anch, s.find(c, anch) - anch); + if (!sec.empty()) + v.push_back(sec); + if (s.find(c, anch) == std::string::npos) + break; + anch = s.find(c, anch) + 1; + } } -qmpDeviceInitializer* qmpDeviceInitializer::parse(const char* path) +qmpDeviceInitializer *qmpDeviceInitializer::parse(const char *path) { - qmpDeviceInitializer *ret=new qmpDeviceInitializer(); - ret->initseq.eventList.clear(); - memset(ret->sinitv,0xFF,sizeof(ret->sinitv)); + qmpDeviceInitializer *ret = new qmpDeviceInitializer(); + ret->initseq.eventList.clear(); + memset(ret->sinitv, 0xFF, sizeof(ret->sinitv)); - bool st_inmapping=false; - char buf[1024]; - int ln=0; - int cmsb=-1,clsb=-1; + bool st_inmapping = false; + char buf[1024]; + int ln = 0; + int cmsb = -1, clsb = -1; #define err(e) {delete ret;return fprintf(stderr,"line %d: %s",ln,e),nullptr;} - FILE* f=fopen(path,"r"); - if(!f)err("file not found") + FILE *f = fopen(path, "r"); + if (!f) + err("file not found") - //writing such a bad parser makes me want my money for - //the credits from "compiler principles" back... - auto h2d=[](char c)->char{return 'F'>=c&&c>='A'?c-'A'+10:'9'>=c&&c>='0'?c-'0':-1;}; - auto hh2d=[](const char *c)->int - { - if(!c||!*c||strlen(c)>2)return -1; - int x=-1,r; - r=sscanf(c,"%x",&x); - (x<0||x>0xff)&&(x=-1); - return r==1?x:-1; - }; - while(fgets(buf,1024,f)) - { - ++ln; - if(buf[0]=='#')continue; - std::string b(buf); - while(b.length()&&b.back()=='\n')b.pop_back(); - std::deque tok; - split(b,' ',tok); - if(!tok.size())continue; - if(tok.front()=="MAP") - { - if(st_inmapping) - err("invalid command") - st_inmapping=true; - } - else if(tok.front()=="ENDMAP") - { - if(!st_inmapping) - err("invalid command") - st_inmapping=false; - } - else if(tok.front()=="X") - { - if(st_inmapping) - err("invalid command") - tok.pop_front(); - std::string sysx; - for(auto&i:tok)sysx+=i; - SEvent ev; - ev.type=0xF0; - ev.str=""; - for(auto i=sysx.begin();i!=sysx.end();++i) - { - char hn=h2d((char)toupper(*i)); - if(hn<0)err("invalid sysex") - if(++i==sysx.end())err("invalid sysex") - char ln=h2d((char)toupper(*i)); - ev.str.push_back((char)(hn<<4|ln)); - } - ret->initseq.appendEvent(ev); - } - else if(tok.front()=="C") - { - if(st_inmapping) - err("invalid command") - if(tok.size()!=4)err("invalid control") - int ch=hh2d(tok[1].c_str()); - int cc=hh2d(tok[2].c_str()); - int cv=hh2d(tok[3].c_str()); - if(!~cc||!~cv)err("invalid control parameters") - if(ch==0xff) - { - for(int i=0;i<16;++i) - { - SEvent e; - e.type=(uint8_t)(0xB0|i); - e.p1=(uint8_t)cc; - e.p2=(uint8_t)cv; - ret->initseq.appendEvent(e); - } - } - else if(ch>=0&&ch<0x10) - { - SEvent e; - e.type=(uint8_t)(0xB0|ch); - e.p1=(uint8_t)cc; - e.p2=(uint8_t)cv; - ret->initseq.appendEvent(e); - } - else err("invalid channel") - } - else if(tok.front()=="IV") - { - if(st_inmapping) - err("invalid command") - int ci=0; - tok.pop_front(); - for(auto&tk:tok) - { - int v=0,rep=1; - if(tk.find(',')!=std::string::npos) - sscanf(tk.c_str(),"%x,%d",&v,&rep); - else sscanf(tk.c_str(),"%x",&v); - if(v>0xff||v<0)err("invalid init vector value") - for(int i=0;i=130)err("invalid init vector") - ret->initv[ci++]=(uint8_t)v; - } - } - } - else if(tok.front()=="SIV") - { - if(st_inmapping) - err("invalid command") - int ch=hh2d(tok[1].c_str()); - int cc=hh2d(tok[2].c_str()); - int cv=hh2d(tok[3].c_str()); - ret->sinitv[ch][cc]=uint8_t(cv); - } - else if(st_inmapping) - { - if(b.front()=='['&&b.back()==']') - { - b=b.substr(1,b.length()-2); - split(b,':',tok); - if(tok.size()<3)err("invalid bank") - cmsb=strtol(tok[0].c_str(),nullptr,10); - clsb=strtol(tok[1].c_str(),nullptr,10); - ret->banks[cmsb<<7|clsb]=BankStore{{},tok[2]}; - } - else if(b.find('=')!=std::string::npos) - { - if(!~cmsb||!~clsb)err("inst outside a bank") - split(b,'=',tok); - if(tok.size()<2)err("invalid inst") - int p=strtol(tok[0].c_str(),nullptr,10); - ret->banks[cmsb<<7|clsb].presets[p]=tok[1]; - } - else err("invalid mapping line") - } - } + //writing such a bad parser makes me want my money for + //the credits from "compiler principles" back... + auto h2d = [](char c)->char + { + return 'F' >= c && c >= 'A' ? + c - 'A' + 10 : + '9' >= c && c >= '0' ? c - '0' : -1; + }; + auto hh2d = [](const char *c)->int + { + if (!c || !*c || strlen(c) > 2) + return -1; + int x = -1, r; + r = sscanf(c, "%x", &x); + (x < 0 || x > 0xff) &&(x = -1); + return r == 1 ? x : -1; + }; + while (fgets(buf, 1024, f)) + { + ++ln; + if (buf[0] == '#') + continue; + std::string b(buf); + while (b.length() && b.back() == '\n') + b.pop_back(); + std::deque tok; + split(b, ' ', tok); + if (!tok.size()) + continue; + if (tok.front() == "MAP") + { + if (st_inmapping) + err("invalid command") + st_inmapping = true; + } + else if (tok.front() == "ENDMAP") + { + if (!st_inmapping) + err("invalid command") + st_inmapping = false; + } + else if (tok.front() == "X") + { + if (st_inmapping) + err("invalid command") + tok.pop_front(); + std::string sysx; + for (auto &i : tok) + sysx += i; + SEvent ev; + ev.type = 0xF0; + ev.str = ""; + for (auto i = sysx.begin(); i != sysx.end(); ++i) + { + char hn = h2d((char)toupper(*i)); + if (hn < 0) + err("invalid sysex") + if (++i == sysx.end()) + err("invalid sysex") + char ln = h2d((char)toupper(*i)); + ev.str.push_back((char)(hn << 4 | ln)); + } + ret->initseq.appendEvent(ev); + } + else if (tok.front() == "C") + { + if (st_inmapping) + err("invalid command") + if (tok.size() != 4) + err("invalid control") + int ch = hh2d(tok[1].c_str()); + int cc = hh2d(tok[2].c_str()); + int cv = hh2d(tok[3].c_str()); + if (!~cc || !~cv) + err("invalid control parameters") + if (ch == 0xff) + { + for (int i = 0; i < 16; ++i) + { + SEvent e; + e.type = (uint8_t)(0xB0 | i); + e.p1 = (uint8_t)cc; + e.p2 = (uint8_t)cv; + ret->initseq.appendEvent(e); + } + } + else if (ch >= 0 && ch < 0x10) + { + SEvent e; + e.type = (uint8_t)(0xB0 | ch); + e.p1 = (uint8_t)cc; + e.p2 = (uint8_t)cv; + ret->initseq.appendEvent(e); + } + else err("invalid channel") + } + else if (tok.front() == "IV") + { + if (st_inmapping) + err("invalid command") + int ci = 0; + tok.pop_front(); + for (auto &tk : tok) + { + int v = 0, rep = 1; + if (tk.find(',') != std::string::npos) + sscanf(tk.c_str(), "%x,%d", &v, &rep); + else sscanf(tk.c_str(), "%x", &v); + if (v > 0xff || v < 0) + err("invalid init vector value") + for (int i = 0; i < rep; ++i) + { + if (ci >= 130) + err("invalid init vector") + ret->initv[ci++] = (uint8_t)v; + } + } + } + else if (tok.front() == "SIV") + { + if (st_inmapping) + err("invalid command") + int ch = hh2d(tok[1].c_str()); + int cc = hh2d(tok[2].c_str()); + int cv = hh2d(tok[3].c_str()); + ret->sinitv[ch][cc] = uint8_t(cv); + } + else if (st_inmapping) + { + if (b.front() == '[' && b.back() == ']') + { + b = b.substr(1, b.length() - 2); + split(b, ':', tok); + if (tok.size() < 3) + err("invalid bank") + cmsb = strtol(tok[0].c_str(), nullptr, 10); + clsb = strtol(tok[1].c_str(), nullptr, 10); + ret->banks[cmsb << 7 | clsb] = BankStore{{}, tok[2]}; + } + else if (b.find('=') != std::string::npos) + { + if (!~cmsb || !~clsb) + err("inst outside a bank") + split(b, '=', tok); + if (tok.size() < 2) + err("invalid inst") + int p = strtol(tok[0].c_str(), nullptr, 10); + ret->banks[cmsb << 7 | clsb].presets[p] = tok[1]; + } + else err("invalid mapping line") + } + } #undef err - fclose(f); - return ret; + fclose(f); + return ret; } qmpMidiOutRtMidi::qmpMidiOutRtMidi(unsigned _portid) { - portid=_portid; - outport=nullptr; - devinit=nullptr; + portid = _portid; + outport = nullptr; + devinit = nullptr; } qmpMidiOutRtMidi::~qmpMidiOutRtMidi() { - if(!outport)return; - if(devinit){delete devinit;devinit=nullptr;} - if(outport->isPortOpen())outport->closePort(); - delete outport;outport=nullptr; + if (!outport) + return; + if (devinit) + { + delete devinit; + devinit = nullptr; + } + if (outport->isPortOpen()) + outport->closePort(); + delete outport; + outport = nullptr; } void qmpMidiOutRtMidi::deviceInit() { - try - { - outport=new RtMidiOut(); - reset(0xFF); - } - catch(RtMidiError &e) - { - fprintf(stderr,"Cannot create RtMidi Output instance: %s\n",e.what()); - outport=nullptr; - } + try + { + outport = new RtMidiOut(); + reset(0xFF); + } + catch (RtMidiError &e) + { + fprintf(stderr, "Cannot create RtMidi Output instance: %s\n", e.what()); + outport = nullptr; + } } void qmpMidiOutRtMidi::deviceDeinit() { - if(!outport||!outport->isPortOpen())return; - outport->closePort(); + if (!outport || !outport->isPortOpen()) + return; + outport->closePort(); } -void qmpMidiOutRtMidi::basicMessage(uint8_t type,uint8_t p1,uint8_t p2) +void qmpMidiOutRtMidi::basicMessage(uint8_t type, uint8_t p1, uint8_t p2) { - if(!outport||!outport->isPortOpen())return; - std::vectormsg; - msg.push_back(type); - msg.push_back(p1); - if(((type&0xF0)!=0xC0)&&((type&0xF0)!=0xD0)) - msg.push_back(p2); - try - { - outport->sendMessage(&msg); - } - catch(RtMidiError &e) - { - fprintf(stderr,"Failed to send midi message: %s\n",e.what()); - } + if (!outport || !outport->isPortOpen()) + return; + std::vectormsg; + msg.push_back(type); + msg.push_back(p1); + if (((type & 0xF0) != 0xC0) && ((type & 0xF0) != 0xD0)) + msg.push_back(p2); + try + { + outport->sendMessage(&msg); + } + catch (RtMidiError &e) + { + fprintf(stderr, "Failed to send midi message: %s\n", e.what()); + } } -void qmpMidiOutRtMidi::extendedMessage(uint32_t length,const char *data) +void qmpMidiOutRtMidi::extendedMessage(uint32_t length, const char *data) { - if(!outport||!outport->isPortOpen())return; - std::vectormsg(data,data+length); - try - { - outport->sendMessage(&msg); - } - catch(RtMidiError &e) - { - fprintf(stderr,"Failed to send midi message: %s\n",e.what()); - } + if (!outport || !outport->isPortOpen()) + return; + std::vectormsg(data, data + length); + try + { + outport->sendMessage(&msg); + } + catch (RtMidiError &e) + { + fprintf(stderr, "Failed to send midi message: %s\n", e.what()); + } } -void qmpMidiOutRtMidi::rpnMessage(uint8_t ch,uint16_t type,uint16_t val) +void qmpMidiOutRtMidi::rpnMessage(uint8_t ch, uint16_t type, uint16_t val) { - basicMessage(0xB0|ch,0x64,type&0x7F); - basicMessage(0xB0|ch,0x65,type>>7); - basicMessage(0xB0|ch,0x06,val>>7); - basicMessage(0xB0|ch,0x26,val&0x7F); + basicMessage(0xB0 | ch, 0x64, type & 0x7F); + basicMessage(0xB0 | ch, 0x65, type >> 7); + basicMessage(0xB0 | ch, 0x06, val >> 7); + basicMessage(0xB0 | ch, 0x26, val & 0x7F); } -void qmpMidiOutRtMidi::nrpnMessage(uint8_t ch,uint16_t type,uint16_t val) +void qmpMidiOutRtMidi::nrpnMessage(uint8_t ch, uint16_t type, uint16_t val) { - basicMessage(0xB0|ch,0x62,type&0x7F); - basicMessage(0xB0|ch,0x63,type>>7); - basicMessage(0xB0|ch,0x06,val>>7); - basicMessage(0xB0|ch,0x26,val&0x7F); + basicMessage(0xB0 | ch, 0x62, type & 0x7F); + basicMessage(0xB0 | ch, 0x63, type >> 7); + basicMessage(0xB0 | ch, 0x06, val >> 7); + basicMessage(0xB0 | ch, 0x26, val & 0x7F); } void qmpMidiOutRtMidi::panic(uint8_t ch) { - //maybe all notes off is more close to panic? - basicMessage(0xE0|ch,0x0,0x40); - basicMessage(0xB0|ch,123,0); + //maybe all notes off is more close to panic? + basicMessage(0xE0 | ch, 0x0, 0x40); + basicMessage(0xB0 | ch, 123, 0); } void qmpMidiOutRtMidi::reset(uint8_t ch) { - if(ch==0xFF) - { - if(devinit) - for(auto&msg:devinit->initseq.eventList) - { - if((msg.type&0xF0)==0xF0) - extendedMessage(msg.str.length(),msg.str.data()); - else - basicMessage(msg.type,msg.p1,msg.p2); - } - } - else - { - basicMessage(0xB0|ch,121,0); - basicMessage(0xB0|ch,123,0); - } + if (ch == 0xFF) + { + if (devinit) + for (auto &msg : devinit->initseq.eventList) + { + if ((msg.type & 0xF0) == 0xF0) + extendedMessage(msg.str.length(), msg.str.data()); + else + basicMessage(msg.type, msg.p1, msg.p2); + } + } + else + { + basicMessage(0xB0 | ch, 121, 0); + basicMessage(0xB0 | ch, 123, 0); + } } -void qmpMidiOutRtMidi::onMapped(uint8_t,int) +void qmpMidiOutRtMidi::onMapped(uint8_t, int) { - if(!outport)deviceInit(); - if(outport->isPortOpen())return; - try - { - outport->openPort(portid); - } - catch(RtMidiError &e) - { - fprintf(stderr,"Device initialization failure: %s\n",e.what()); - } + if (!outport) + deviceInit(); + if (outport->isPortOpen()) + return; + try + { + outport->openPort(portid); + } + catch (RtMidiError &e) + { + fprintf(stderr, "Device initialization failure: %s\n", e.what()); + } } -void qmpMidiOutRtMidi::onUnmapped(uint8_t ch,int refcnt) +void qmpMidiOutRtMidi::onUnmapped(uint8_t ch, int refcnt) { - panic(ch); - if(!refcnt&&outport)outport->closePort(); + panic(ch); + if (!refcnt && outport) + outport->closePort(); } -std::vector> qmpMidiOutRtMidi::getBankList() +std::vector> qmpMidiOutRtMidi::getBankList() { - if(!devinit)return{}; - std::vector> ret; - for(auto&i:devinit->banks) - ret.push_back({i.first,i.second.bankname}); - std::sort(ret.begin(),ret.end(),[](std::pair&a,std::pair&b){return a.first> ret; + for (auto &i : devinit->banks) + ret.push_back({i.first, i.second.bankname}); + std::sort(ret.begin(), ret.end(), [](std::pair &a, std::pair &b) + { + return a.first < b.first; + }); + return ret; } -std::vector> qmpMidiOutRtMidi::getPresets(uint16_t bank) +std::vector> qmpMidiOutRtMidi::getPresets(uint16_t bank) { - if(!devinit)return{}; - if(devinit->banks.find(bank)==devinit->banks.end())return{}; - std::vector> ret; - for(auto&i:devinit->banks[bank].presets) - ret.push_back({i.first,i.second}); - std::sort(ret.begin(),ret.end(),[](std::pair&a,std::pair&b){return a.firstbanks.find(bank) == devinit->banks.end()) + return{}; + std::vector> ret; + for (auto &i : devinit->banks[bank].presets) + ret.push_back({i.first, i.second}); + std::sort(ret.begin(), ret.end(), [](std::pair &a, std::pair &b) + { + return a.first < b.first; + }); + return ret; } -std::string qmpMidiOutRtMidi::getPresetName(uint16_t bank,uint8_t preset) +std::string qmpMidiOutRtMidi::getPresetName(uint16_t bank, uint8_t preset) { - if(!devinit)return""; - if(devinit->banks.find(bank)!=devinit->banks.end()) - if(devinit->banks[bank].presets.find(preset)!=devinit->banks[bank].presets.end()) - return devinit->banks[bank].presets[preset]; - return""; + if (!devinit) + return std::string(); + if (devinit->banks.find(bank) != devinit->banks.end()) + if (devinit->banks[bank].presets.find(preset) != devinit->banks[bank].presets.end()) + return devinit->banks[bank].presets[preset]; + return std::string(); } -bool qmpMidiOutRtMidi::getChannelPreset(int ch,uint16_t *bank,uint8_t *preset,std::string &presetname) +bool qmpMidiOutRtMidi::getChannelPreset(int ch, uint16_t *bank, uint8_t *preset, std::string &presetname) { - //This ain't a state-tracking device - return false; + //This ain't a state-tracking device + return false; } -uint8_t qmpMidiOutRtMidi::getInitialCCValue(uint8_t cc,uint8_t ch) +uint8_t qmpMidiOutRtMidi::getInitialCCValue(uint8_t cc, uint8_t ch) { - if(!devinit||cc>=130)return 0;//Nope! - if(ch<16&&devinit->sinitv[ch][cc]!=0xFF) - return devinit->sinitv[ch][cc]; - return devinit->initv[cc]; + if (!devinit || cc >= 130) + return 0; + if (ch < 16 && devinit->sinitv[ch][cc] != 0xFF) + return devinit->sinitv[ch][cc]; + return devinit->initv[cc]; } -void qmpMidiOutRtMidi::setInitializerFile(const char* path) +void qmpMidiOutRtMidi::setInitializerFile(const char *path) { - if(devinit)delete devinit; - devinit=qmpDeviceInitializer::parse(path); - reset(0xFF); + if (devinit) + delete devinit; + devinit = qmpDeviceInitializer::parse(path); + reset(0xFF); } -std::vector> qmpRtMidiManager::devices; -std::vector> qmpRtMidiManager::getDevices() +std::vector> qmpRtMidiManager::devices; +std::vector> qmpRtMidiManager::getDevices() { - if(devices.size())return devices; - RtMidiOut *dummy; - try{dummy=new RtMidiOut();} - catch(RtMidiError &e) - { - fprintf(stderr,"Failed to initialize the dummy device: %s\n",e.what()); - return{}; - } - for(unsigned i=0;igetPortCount();++i) - devices.push_back(std::make_pair(new qmpMidiOutRtMidi(i),dummy->getPortName(i))); - delete dummy; - return devices; + if (devices.size()) + return devices; + RtMidiOut *dummy; + try + { + dummy = new RtMidiOut(); + } + catch (RtMidiError &e) + { + fprintf(stderr, "Failed to initialize the dummy device: %s\n", e.what()); + return{}; + } + for (unsigned i = 0; i < dummy->getPortCount(); ++i) + devices.push_back(std::make_pair(new qmpMidiOutRtMidi(i), dummy->getPortName(i))); + delete dummy; + return devices; } diff --git a/core/qmpmidioutrtmidi.hpp b/core/qmpmidioutrtmidi.hpp index 45662ba..8276cce 100644 --- a/core/qmpmidioutrtmidi.hpp +++ b/core/qmpmidioutrtmidi.hpp @@ -6,53 +6,53 @@ #include "../include/qmpcorepublic.hpp" struct qmpDeviceInitializer { - CMidiTrack initseq; - struct BankStore - { - std::unordered_map presets; - std::string bankname; - }; - std::unordered_map banks; - uint8_t initv[130]; - uint8_t sinitv[16][130]; + CMidiTrack initseq; + struct BankStore + { + std::unordered_map presets; + std::string bankname; + }; + std::unordered_map banks; + uint8_t initv[130]; + uint8_t sinitv[16][130]; - static qmpDeviceInitializer* parse(const char* path); + static qmpDeviceInitializer *parse(const char *path); }; class RtMidiOut; -class qmpMidiOutRtMidi:public qmpMidiOutDevice +class qmpMidiOutRtMidi : public qmpMidiOutDevice { private: - unsigned portid; - RtMidiOut* outport; - qmpDeviceInitializer* devinit; + unsigned portid; + RtMidiOut *outport; + qmpDeviceInitializer *devinit; public: - qmpMidiOutRtMidi(unsigned _portid); - ~qmpMidiOutRtMidi(); - void deviceInit(); - void deviceDeinit(); - void basicMessage(uint8_t type,uint8_t p1,uint8_t p2); - void extendedMessage(uint32_t length,const char *data); - void rpnMessage(uint8_t ch,uint16_t type,uint16_t val); - void nrpnMessage(uint8_t ch,uint16_t type,uint16_t val); - void panic(uint8_t ch); - void reset(uint8_t ch); - void onMapped(uint8_t ch,int refcnt); - void onUnmapped(uint8_t ch,int refcnt); - std::vector> getBankList(); - std::vector> getPresets(uint16_t bank); - std::string getPresetName(uint16_t bank,uint8_t preset); - bool getChannelPreset(int ch,uint16_t *bank,uint8_t *preset,std::string &presetname); - uint8_t getInitialCCValue(uint8_t cc,uint8_t ch); + qmpMidiOutRtMidi(unsigned _portid); + ~qmpMidiOutRtMidi(); + void deviceInit(); + void deviceDeinit(); + void basicMessage(uint8_t type, uint8_t p1, uint8_t p2); + void extendedMessage(uint32_t length, const char *data); + void rpnMessage(uint8_t ch, uint16_t type, uint16_t val); + void nrpnMessage(uint8_t ch, uint16_t type, uint16_t val); + void panic(uint8_t ch); + void reset(uint8_t ch); + void onMapped(uint8_t ch, int refcnt); + void onUnmapped(uint8_t ch, int refcnt); + std::vector> getBankList(); + std::vector> getPresets(uint16_t bank); + std::string getPresetName(uint16_t bank, uint8_t preset); + bool getChannelPreset(int ch, uint16_t *bank, uint8_t *preset, std::string &presetname); + uint8_t getInitialCCValue(uint8_t cc, uint8_t ch); - void setInitializerFile(const char* path); + void setInitializerFile(const char *path); }; class qmpRtMidiManager { - private: - static std::vector> devices; - public: - static std::vector> getDevices(); +private: + static std::vector> devices; +public: + static std::vector> getDevices(); }; #endif // QMPMIDIMAPPERS_H diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp index a354d6e..c987e01 100644 --- a/core/qmpmidiplay.cpp +++ b/core/qmpmidiplay.cpp @@ -9,609 +9,778 @@ #include uint64_t pf; #endif -CMidiPlayer* CMidiPlayer::ref=nullptr; +CMidiPlayer *CMidiPlayer::ref = nullptr; bool CMidiPlayer::processEvent(const SEvent *e) { - SEvent fe(*e); - fe.flags&=0xfe; - if(tceptr>=eorder.size()-1||getEvent(tceptr+1)->time>e->time) - fe.flags|=0x01; - for(int i=0;i<16;++i)if(eventHandlerCB[i]) - eventHandlerCB[i]->callBack((void*)&fe,eventHandlerCBuserdata[i]); - for(auto i=event_handlers.begin();i!=event_handlers.end();++i) - if(!std::get<2>(i->second)) - std::get<0>(i->second)((void*)e,std::get<1>(i->second)); - uint8_t ch=e->type&0x0F; - if((e->type&0xF0)<0xF0) - levtt[ch]=std::chrono::system_clock::now(); - switch(e->type&0xF0) - { - case 0x80://Note off - return true; - case 0x90://Note on - if((mute>>ch)&1&&e->p2)return false;//muted - if(solo&&!((solo>>ch)&1)&&e->p2)return false;//excluded by solo flags - return true; - case 0xB0://CC - if(e->p1==100)rpnid[ch]=e->p2; - if(e->p1==6)rpnval[ch]=e->p2; - if(~rpnid[ch]&&~rpnval[ch]) - { - if(rpnid[ch]==0) - { - mididev[mappedoutput[ch]].dev->rpnMessage(ch,0,rpnval[ch]<<7); - pbr[ch]=rpnval[ch]; - } - rpnid[ch]=rpnval[ch]=-1; - } - chstatus[ch][e->p1]=e->p2; - return true; - case 0xC0://PC - chstatus[ch][128]=e->p1; - return true; - case 0xE0://PW - pbv[ch]=(e->p1|(e->p2<<7))&0x3FFF; - return true; - case 0xF0://Meta/SysEx - if((e->type&0x0F)==0x0F) - { - switch(e->p1) - { - case 0x51: - { - ctempo=0; + SEvent fe(*e); + fe.flags &= 0xfe; + if (tceptr >= eorder.size() - 1 || getEvent(tceptr + 1)->time > e->time) + fe.flags |= 0x01; + for (int i = 0; i < 16; ++i) + if (eventHandlerCB[i]) + eventHandlerCB[i]->callBack((void *)&fe, eventHandlerCBuserdata[i]); + for (auto i = event_handlers.begin(); i != event_handlers.end(); ++i) + if (!std::get<2>(i->second)) + std::get<0>(i->second)((void *)e, std::get<1>(i->second)); + uint8_t ch = e->type & 0x0F; + if ((e->type & 0xF0) < 0xF0) + levtt[ch] = std::chrono::system_clock::now(); + switch (e->type & 0xF0) + { + case 0x80://Note off + return true; + case 0x90://Note on + if ((mute >> ch) & 1 && e->p2) + return false; //muted + if (solo && !((solo >> ch) & 1) && e->p2) + return false; //excluded by solo flags + return true; + case 0xB0://CC + if (e->p1 == 100) + rpnid[ch] = e->p2; + if (e->p1 == 6) + rpnval[ch] = e->p2; + if (~rpnid[ch] && ~rpnval[ch]) + { + if (rpnid[ch] == 0) + { + mididev[mappedoutput[ch]].dev->rpnMessage(ch, 0, rpnval[ch] << 7); + pbr[ch] = rpnval[ch]; + } + rpnid[ch] = rpnval[ch] = -1; + } + chstatus[ch][e->p1] = e->p2; + return true; + case 0xC0://PC + chstatus[ch][128] = e->p1; + return true; + case 0xE0://PW + pbv[ch] = (e->p1 | (e->p2 << 7)) & 0x3FFF; + return true; + case 0xF0://Meta/SysEx + if ((e->type & 0x0F) == 0x0F) + { + switch (e->p1) + { + case 0x51: + { + ctempo = 0; #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)||defined(_WIN32) - std::string x(e->str); - std::reverse(x.begin(),x.end()); - memcpy(&ctempo,x.data(),3); + std::string x(e->str); + std::reverse(x.begin(), x.end()); + memcpy(&ctempo, x.data(), 3); #else - memcpy(&ctempo,e->str.data(),3); - ctempo>>=8; + memcpy(&ctempo, e->str.data(), 3); + ctempo >>= 8; #endif - dpt=ctempo*1000./divs; - ttime=std::chrono::high_resolution_clock::now(); - ttick=getTick(); - } - break; - case 0x58: - ctsn=(uint32_t)e->str[0]; - ctsd=1<<((uint32_t)e->str[1]); - break; - case 0x59: - if(e->str.length()==2) - cks=(e->str[0]<<8u)|e->str[1]; - 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)&&sendSysEx) - for(auto& i:mididev) - if(i.refcnt) - i.dev->extendedMessage(uint32_t(e->str.length()),e->str.c_str()); - return false; - } - return false; + dpt = ctempo * 1000. / divs; + ttime = std::chrono::high_resolution_clock::now(); + ttick = getTick(); + } + break; + case 0x58: + ctsn = (uint32_t)e->str[0]; + ctsd = 1 << ((uint32_t)e->str[1]); + break; + case 0x59: + if (e->str.length() == 2) + cks = (e->str[0] << 8u) | e->str[1]; + 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) && sendSysEx) + for (auto &i : mididev) + if (i.refcnt) + i.dev->extendedMessage(uint32_t(e->str.length()), e->str.c_str()); + return false; + } + return false; } void CMidiPlayer::processEventStub(const SEvent *e) { - switch(e->type&0xF0) - { - case 0xB0://CC - if(e->p1==100)rpnid[e->type&0x0F]=e->p2; - if(e->p1==6)rpnval[e->type&0x0F]=e->p2; - if(~rpnid[e->type&0x0F]&&~rpnval[e->type&0x0F]) - { - if(rpnid[e->type&0x0F]==0)ccc[e->type&0x0F][134]=rpnval[e->type&0x0F]; - rpnval[e->type&0x0F]=-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|(e->p2<<7))&0x3FFF; - break; - case 0xF0://Meta/SysEx - if((e->type&0x0F)==0x0F) - { - switch(e->p1) - { - case 0x51: - { - ctempo=0; + switch (e->type & 0xF0) + { + case 0xB0://CC + if (e->p1 == 100) + rpnid[e->type & 0x0F] = e->p2; + if (e->p1 == 6) + rpnval[e->type & 0x0F] = e->p2; + if (~rpnid[e->type & 0x0F] && ~rpnval[e->type & 0x0F]) + { + if (rpnid[e->type & 0x0F] == 0) + ccc[e->type & 0x0F][134] = rpnval[e->type & 0x0F]; + rpnval[e->type & 0x0F] = -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 | (e->p2 << 7)) & 0x3FFF; + break; + case 0xF0://Meta/SysEx + if ((e->type & 0x0F) == 0x0F) + { + switch (e->p1) + { + case 0x51: + { + ctempo = 0; #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)||defined(_WIN32) - std::string x(e->str); - std::reverse(x.begin(),x.end()); - memcpy(&ctempo,x.data(),3); + std::string x(e->str); + std::reverse(x.begin(), x.end()); + memcpy(&ctempo, x.data(), 3); #else - memcpy(&ctempo,e->str.data(),3); - ctempo>>=8; + memcpy(&ctempo, e->str.data(), 3); + ctempo >>= 8; #endif - dpt=ctempo*1000./divs; - ccc[0][131]=ctempo; - } - break; - case 0x58: - ccc[0][132]=(e->str[0]<<24u)|(e->str[1]<<16u); - break; - case 0x59: - if(e->str.length()>=2) - ccc[0][133]=(e->str[0]<<8u)|e->str[1]; - break; - } - } - break; - } + dpt = ctempo * 1000. / divs; + ccc[0][131] = ctempo; + } + break; + case 0x58: + ccc[0][132] = (e->str[0] << 24u) | (e->str[1] << 16u); + break; + case 0x59: + if (e->str.length() >= 2) + ccc[0][133] = (e->str[0] << 8u) | e->str[1]; + 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 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 -SEvent* CMidiPlayer::getEvent(uint32_t id) +SEvent *CMidiPlayer::getEvent(uint32_t id) { - size_t t=eorder[id].first,e=eorder[id].second; - return &midiFile->tracks[t].eventList[e]; + size_t t = eorder[id].first, e = eorder[id].second; + return &midiFile->tracks[t].eventList[e]; } void CMidiPlayer::prePlayInit() { - playerPanic(true); - for(size_t i=0;ireset(0xFF); + playerPanic(true); + for (size_t i = 0; i < mididev.size(); ++i) + if (mididev[i].refcnt) + mididev[i].dev->reset(0xFF); } void CMidiPlayer::playEvents() { - static uint32_t _lrtick;_lrtick=0; - ttick=getEvent(0)->time;ttime=std::chrono::high_resolution_clock::now(); - for(ct=getEvent(0)->time;tceptrtime; - ttick=getTick(); - ttime=high_resolution_clock::now(); - continue; - } - high_resolution_clock::time_point b=high_resolution_clock::now(); - if(getTick()-_lrtick>divs){ - _lrtick=getTick(); - //double _dt=(getTick()-ttick)*dpt-duration_cast((b-ttime)).count(); - //<0: slower than expected, >0: faster than expected - //fprintf(stderr,"@ tick %u, dtime %.6fus",getTick(),_dt/1000.); - } - for(;!tcstop&&midiReaders&&tceptrtime;++tceptr) - { - if(processEvent(getEvent(tceptr))) - { - SEvent* e=getEvent(tceptr); - mididev[mappedoutput[e->type&0x0F]].dev->basicMessage(e->type,e->p1,e->p2); - } - for(auto i=event_handlers.begin();i!=event_handlers.end();++i) - if(std::get<2>(i->second)) - std::get<0>(i->second)((void*)getEvent(tceptr),std::get<1>(i->second)); - } - if(tcstop||!midiReaders||tceptr>=ecnt)break; - high_resolution_clock::time_point a=high_resolution_clock::now(); - auto sendtime=a-b; - if(sendtime.count()<(getEvent(tceptr)->time-ct)*dpt) - { - double ns_sleep=(getEvent(tceptr)->time-ct)*dpt-sendtime.count(); - double correction=(getTick()-ttick)*dpt-(b-ttime).count(); - if(correction>0)correction=0; + static uint32_t _lrtick; + _lrtick = 0; + ttick = getEvent(0)->time; + ttime = std::chrono::high_resolution_clock::now(); + for (ct = getEvent(0)->time; tceptr < ecnt;) + { + using namespace std::chrono; + while (tcpaused) + std::this_thread::sleep_for(milliseconds(100)); + if (resumed) + { + resumed = false; + ct = getEvent(tceptr)->time; + ttick = getTick(); + ttime = high_resolution_clock::now(); + continue; + } + high_resolution_clock::time_point b = high_resolution_clock::now(); + if (getTick() - _lrtick > divs) + { + _lrtick = getTick(); + //double _dt=(getTick()-ttick)*dpt-duration_cast((b-ttime)).count(); + //<0: slower than expected, >0: faster than expected + //fprintf(stderr,"@ tick %u, dtime %.6fus",getTick(),_dt/1000.); + } + for (; !tcstop && midiReaders && tceptr < ecnt && ct == getEvent(tceptr)->time; ++tceptr) + { + if (processEvent(getEvent(tceptr))) + { + SEvent *e = getEvent(tceptr); + mididev[mappedoutput[e->type & 0x0F]].dev->basicMessage(e->type, e->p1, e->p2); + } + for (auto i = event_handlers.begin(); i != event_handlers.end(); ++i) + if (std::get<2>(i->second)) + std::get<0>(i->second)((void *)getEvent(tceptr), std::get<1>(i->second)); + } + if (tcstop || !midiReaders || tceptr >= ecnt) + break; + high_resolution_clock::time_point a = high_resolution_clock::now(); + auto sendtime = a - b; + if (sendtime.count() < (getEvent(tceptr)->time - ct)*dpt) + { + double ns_sleep = (getEvent(tceptr)->time - ct) * dpt - sendtime.count(); + double correction = (getTick() - ttick) * dpt - (b - ttime).count(); + if (correction > 0) + correction = 0; #ifdef _WIN32 - w32usleep((uint64_t)(((getEvent(tceptr)->time-ct)*dpt-sendtime.count())/1000)); + w32usleep((uint64_t)(((getEvent(tceptr)->time - ct)*dpt - sendtime.count()) / 1000)); #else - std::this_thread::sleep_for(std::chrono::nanoseconds((uint64_t)(ns_sleep+correction))); + std::this_thread::sleep_for(std::chrono::nanoseconds((uint64_t)(ns_sleep + correction))); #endif - } - if(tcstop||!midiReaders)break; - ct=getEvent(tceptr)->time; - } - if(tceptr>=ecnt) - finished=1; + } + if (tcstop || !midiReaders) + break; + ct = getEvent(tceptr)->time; + } + if (tceptr >= ecnt) + finished = 1; } void CMidiPlayer::fileTimer1Pass() { - ftime=.0;ctempo=0x7A120;dpt=ctempo*1000./divs; - for(uint32_t eptr=0,ct=getEvent(0)->time;eptrtime) - processEventStub(getEvent(eptr++)); - if(eptr>=ecnt)break; - ftime+=(getEvent(eptr)->time-ct)*dpt/1e9; - ct=getEvent(eptr)->time; - } + ftime = .0; + ctempo = 0x7A120; + dpt = ctempo * 1000. / divs; + for (uint32_t eptr = 0, ct = getEvent(0)->time; eptr < ecnt;) + { + while (eptr < ecnt && ct == getEvent(eptr)->time) + processEventStub(getEvent(eptr++)); + if (eptr >= ecnt) + break; + ftime += (getEvent(eptr)->time - ct) * dpt / 1e9; + ct = 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));memset(rpnid,0xFF,sizeof(rpnid));memset(rpnval,0xFF,sizeof(rpnval)); - for(int i=0;i<16;++i) - { - memset(ccc[i],0xFF,128*sizeof(uint32_t)); - ccc[i][131]=ctempo;ccc[i][132]=0x04021808; - ccc[i][133]=0;ccc[i][134]=2; - } - 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=getEvent(0)->time;eptrtime) - processEventStub(getEvent(eptr++)); - if(eptr>=ecnt)break; - ctime+=(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=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++]=ecnt; - } + 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)); + memset(rpnid, 0xFF, sizeof(rpnid)); + memset(rpnval, 0xFF, sizeof(rpnval)); + for (int i = 0; i < 16; ++i) + { + memset(ccc[i], 0xFF, 128 * sizeof(uint32_t)); + ccc[i][131] = ctempo; + ccc[i][132] = 0x04021808; + ccc[i][133] = 0; + ccc[i][134] = 2; + } + 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 = getEvent(0)->time; eptr < ecnt;) + { + while (eptr < ecnt && ct == getEvent(eptr)->time) + processEventStub(getEvent(eptr++)); + if (eptr >= ecnt) + break; + ctime += (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 = 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++] = ecnt; + } } CMidiPlayer::CMidiPlayer() { - midiReaders=new CMidiFileReaderCollection(); - resumed=false;midiFile=nullptr; - waitvoice=true; - event_handlers_id=event_read_handlers_id=file_read_finish_hooks_id=0; - memset(eventHandlerCB,0,sizeof(eventHandlerCB)); - memset(eventHandlerCBuserdata,0,sizeof(eventHandlerCBuserdata)); - memset(eventReaderCB,0,sizeof(eventReaderCB)); - memset(eventReaderCBuserdata,0,sizeof(eventReaderCBuserdata)); - memset(fileReadFinishCB,0,sizeof(fileReadFinishCB)); - memset(fileReadFinishCBuserdata,0,sizeof(fileReadFinishCBuserdata)); - memset(mappedoutput,0xff,sizeof(mappedoutput)); - memset(chstatus,0,sizeof(chstatus)); - for(int i=0;i<16;++i) - memset(chstatus[i],0xff,128*sizeof(uint8_t)); + midiReaders = new CMidiFileReaderCollection(); + resumed = false; + midiFile = nullptr; + waitvoice = true; + event_handlers_id = event_read_handlers_id = file_read_finish_hooks_id = 0; + memset(eventHandlerCB, 0, sizeof(eventHandlerCB)); + memset(eventHandlerCBuserdata, 0, sizeof(eventHandlerCBuserdata)); + memset(eventReaderCB, 0, sizeof(eventReaderCB)); + memset(eventReaderCBuserdata, 0, sizeof(eventReaderCBuserdata)); + memset(fileReadFinishCB, 0, sizeof(fileReadFinishCB)); + memset(fileReadFinishCBuserdata, 0, sizeof(fileReadFinishCBuserdata)); + memset(mappedoutput, 0xff, sizeof(mappedoutput)); + memset(chstatus, 0, sizeof(chstatus)); + for (int i = 0; i < 16; ++i) + memset(chstatus[i], 0xff, 128 * sizeof(uint8_t)); #ifdef _WIN32 - QueryPerformanceFrequency((LARGE_INTEGER*)&pf); + QueryPerformanceFrequency((LARGE_INTEGER *)&pf); #endif - ref=this; + ref = this; } CMidiPlayer::~CMidiPlayer() { - if(midiFile)delete midiFile;delete midiReaders; + if (midiFile) + delete midiFile; + delete midiReaders; } void CMidiPlayer::playerPanic(bool reset) { - for(auto& i:mididev) - if(i.refcnt) - { - for(uint8_t j=0;j<16;++j) - reset?i.dev->reset(j):i.dev->panic(j); - } -} -bool CMidiPlayer::playerLoadFile(const char* fn) -{ - notes=0;if(midiFile)delete midiFile; - midiFile=midiReaders->readFile(fn); - if(!midiFile||!midiFile->valid)return false; - divs=midiFile->divs;maxtk=ecnt=0; - for(CMidiTrack& i:midiFile->tracks) - { - ecnt+=i.eventList.size(); - if(i.eventList.size()) - maxtk=std::max(maxtk,i.eventList.back().time); - } - for(int i=0;i<16;++i)if(fileReadFinishCB[i]) - fileReadFinishCB[i]->callBack(nullptr,fileReadFinishCBuserdata[i]); - for(auto i=file_read_finish_hooks.begin();i!=file_read_finish_hooks.end();++i) - i->second.first(nullptr,i->second.second); - eorder.clear(); - for(size_t i=0;itracks.size();++i) - for(size_t j=0;jtracks[i].eventList.size();++j) - eorder.push_back(std::make_pair(i,j)); - std::sort(eorder.begin(),eorder.end(), - [this](std::pair &a,std::pair &b)->bool{ - return midiFile->tracks[a.first].eventList[a.second]< - midiFile->tracks[b.first].eventList[b.second]; - } - ); - fileTimer1Pass(); - fileTimer2Pass(); - return true; + for (auto &i : mididev) + if (i.refcnt) + { + for (uint8_t j = 0; j < 16; ++j) + reset ? i.dev->reset(j) : i.dev->panic(j); + } +} +bool CMidiPlayer::playerLoadFile(const char *fn) +{ + notes = 0; + if (midiFile) + delete midiFile; + midiFile = midiReaders->readFile(fn); + if (!midiFile || !midiFile->valid) + return false; + divs = midiFile->divs; + maxtk = ecnt = 0; + for (CMidiTrack &i : midiFile->tracks) + { + ecnt += i.eventList.size(); + if (i.eventList.size()) + maxtk = std::max(maxtk, i.eventList.back().time); + } + for (int i = 0; i < 16; ++i) + if (fileReadFinishCB[i]) + fileReadFinishCB[i]->callBack(nullptr, fileReadFinishCBuserdata[i]); + for (auto i = file_read_finish_hooks.begin(); i != file_read_finish_hooks.end(); ++i) + i->second.first(nullptr, i->second.second); + eorder.clear(); + for (size_t i = 0; i < midiFile->tracks.size(); ++i) + for (size_t j = 0; j < midiFile->tracks[i].eventList.size(); ++j) + eorder.push_back(std::make_pair(i, j)); + std::sort(eorder.begin(), eorder.end(), + [this](std::pair &a, std::pair &b)->bool + { + return midiFile->tracks[a.first].eventList[a.second] < + midiFile->tracks[b.first].eventList[b.second]; + }); + fileTimer1Pass(); + fileTimer2Pass(); + return true; } void CMidiPlayer::playerInit() { - ctempo=0x7A120;ctsn=4;ctsd=4;cks=0;dpt=ctempo*1000./divs; - tceptr=0;tcstop=false;tcpaused=0;finished=0;mute=solo=0; - for(int i=0;i<16;++i)pbr[i]=2,pbv[i]=8192; - sendSysEx=true;memset(rpnid,0xFF,sizeof(rpnid));memset(rpnval,0xFF,sizeof(rpnval)); - memset(chstatus,0,sizeof(chstatus)); - for(int i=0;i<16;++i) - memset(chstatus[i],0xff,128*sizeof(uint8_t)); + ctempo = 0x7A120; + ctsn = 4; + ctsd = 4; + cks = 0; + dpt = ctempo * 1000. / divs; + tceptr = 0; + tcstop = false; + tcpaused = 0; + finished = 0; + mute = solo = 0; + for (int i = 0; i < 16; ++i) + pbr[i] = 2, pbv[i] = 8192; + sendSysEx = true; + memset(rpnid, 0xFF, sizeof(rpnid)); + memset(rpnval, 0xFF, sizeof(rpnval)); + memset(chstatus, 0, sizeof(chstatus)); + for (int i = 0; i < 16; ++i) + memset(chstatus[i], 0xff, 128 * sizeof(uint8_t)); } void CMidiPlayer::playerDeinit() { - tceptr=0;tcstop=true;tcpaused=0; - delete midiFile;midiFile=nullptr; + tceptr = 0; + tcstop = true; + tcpaused = 0; + delete midiFile; + midiFile = nullptr; } void CMidiPlayer::playerThread() { - prePlayInit(); - playEvents(); + prePlayInit(); + playEvents(); } -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==ecnt)tcstop=true;else tceptr=ep; - for(int i=0;i<16;++i) - { - qmpMidiOutDevice* dest=mididev[mappedoutput[i]].dev; - for(int j=0;j<120;++j) - { - if(~ccstamps[st][i][j]) - { - dest->basicMessage(0xB0|i,j,ccstamps[st][i][j]); - chstatus[i][j]=ccstamps[st][i][j]; - } - } - dest->basicMessage(0xC0|i,ccstamps[st][i][128],0); - chstatus[i][128]=ccstamps[st][i][128]; - dest->rpnMessage(i,0,ccstamps[st][i][134]<<7); - pbr[i]=ccstamps[st][i][134]; - ctempo=ccstamps[st][0][131];dpt=ctempo*1000./divs; - 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;} -int CMidiPlayer::getCurrentKeySignature(){return cks;} -uint32_t CMidiPlayer::getFileNoteCount(){return notes;} -uint32_t CMidiPlayer::getFileStandard(){return midiFile?midiFile->std:0;} -const char* CMidiPlayer::getTitle(){return midiFile?midiFile->title:"";} -const char* CMidiPlayer::getCopyright(){return midiFile?midiFile->copyright:"";} -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;} -double CMidiPlayer::getPitchBend(int ch){return((int)pbv[ch]-8192)/8192.*pbr[ch];} -double CMidiPlayer::getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr) -{ - if(pb)*pb=this->pbv[ch]; - if(pbr)*pbr=this->pbr[ch]; -} -uint32_t CMidiPlayer::getTCpaused(){return tcpaused;} -void CMidiPlayer::setTCpaused(uint32_t ps){tcpaused=ps;} -uint32_t CMidiPlayer::isFinished(){return finished;} -bool CMidiPlayer::stopFlag(){return tcstop;} -void CMidiPlayer::setResumed(){resumed=true;} +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 == ecnt) + tcstop = true; + else tceptr = ep; + for (int i = 0; i < 16; ++i) + { + qmpMidiOutDevice *dest = mididev[mappedoutput[i]].dev; + for (int j = 0; j < 120; ++j) + { + if (~ccstamps[st][i][j]) + { + dest->basicMessage(0xB0 | i, j, ccstamps[st][i][j]); + chstatus[i][j] = ccstamps[st][i][j]; + } + } + dest->basicMessage(0xC0 | i, ccstamps[st][i][128], 0); + chstatus[i][128] = ccstamps[st][i][128]; + dest->rpnMessage(i, 0, ccstamps[st][i][134] << 7); + pbr[i] = ccstamps[st][i][134]; + ctempo = ccstamps[st][0][131]; + dpt = ctempo * 1000. / divs; + 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; +} +int CMidiPlayer::getCurrentKeySignature() +{ + return cks; +} +uint32_t CMidiPlayer::getFileNoteCount() +{ + return notes; +} +uint32_t CMidiPlayer::getFileStandard() +{ + return midiFile ? midiFile->std : 0; +} +const char *CMidiPlayer::getTitle() +{ + return midiFile ? midiFile->title : ""; +} +const char *CMidiPlayer::getCopyright() +{ + return midiFile ? midiFile->copyright : ""; +} +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; +} +double CMidiPlayer::getPitchBend(int ch) +{ + return ((int)pbv[ch] - 8192) / 8192.*pbr[ch]; +} +double CMidiPlayer::getPitchBendRaw(int ch, uint32_t *pb, uint32_t *pbr) +{ + if (pb) + *pb = this->pbv[ch]; + if (pbr) + *pbr = this->pbr[ch]; +} +uint32_t CMidiPlayer::getTCpaused() +{ + return tcpaused; +} +void CMidiPlayer::setTCpaused(uint32_t ps) +{ + tcpaused = ps; +} +uint32_t CMidiPlayer::isFinished() +{ + return finished; +} +bool CMidiPlayer::stopFlag() +{ + return tcstop; +} +void CMidiPlayer::setResumed() +{ + resumed = true; +} -void CMidiPlayer::setChannelPreset(int ch,int b,int p) +void CMidiPlayer::setChannelPreset(int ch, int b, int p) { - chstatus[ch][128]=p; - chstatus[ch][0]=b>>7;chstatus[ch][32]=b&0x7F; - qmpMidiOutDevice* d=mididev[mappedoutput[ch]].dev; - d->basicMessage(0xB0|ch,0x00,b>>7); - d->basicMessage(0xB0|ch,0x20,b&0x7F); - d->basicMessage(0xC0|ch,p,0); + chstatus[ch][128] = p; + chstatus[ch][0] = b >> 7; + chstatus[ch][32] = b & 0x7F; + qmpMidiOutDevice *d = mididev[mappedoutput[ch]].dev; + d->basicMessage(0xB0 | ch, 0x00, b >> 7); + d->basicMessage(0xB0 | ch, 0x20, b & 0x7F); + d->basicMessage(0xC0 | ch, p, 0); } void CMidiPlayer::dumpFile() { - if(!midiFile)return; - for(CMidiTrack &i:midiFile->tracks) - for(SEvent &j:i.eventList) - if(j.str.length()) - printf("type %x #%d @%d p1 %d p2 %d str %s\n",j.type, - j.iid,j.time,j.p1,j.p2,j.str.c_str()); - else - printf("type %x #%d @%d p1 %d p2 %d\n",j.type,j.iid, - j.time,j.p1,j.p2); + if (!midiFile) + return; + for (CMidiTrack &i : midiFile->tracks) + for (SEvent &j : i.eventList) + if (j.str.length()) + printf("type %x #%d @%d p1 %d p2 %d str %s\n", j.type, + j.iid, j.time, j.p1, j.p2, j.str.c_str()); + else + printf("type %x #%d @%d p1 %d p2 %d\n", j.type, j.iid, + j.time, j.p1, j.p2); } //16MSB..LSB1 void CMidiPlayer::setBit(uint16_t &n, uint16_t bn, uint16_t b) -{n^=(((~b)+1)^n)&(1<>ch)&1)||(solo&&!((solo>>ch)&1));} -uint16_t CMidiPlayer::getCC(int ch,int id) { - if(chstatus[ch][id]==0xff) - return getChannelOutputDevice(ch)->getInitialCCValue(uint8_t(id),uint8_t(ch)); - return chstatus[ch][id]; + return ((mute >> ch) & 1) || (solo && !((solo >> ch) & 1)); +} +uint16_t CMidiPlayer::getCC(int ch, int id) +{ + if (chstatus[ch][id] == 0xff) + return getChannelOutputDevice(ch)->getInitialCCValue(uint8_t(id), uint8_t(ch)); + return chstatus[ch][id]; } -void CMidiPlayer::setCC(int ch,int id,int val) +void CMidiPlayer::setCC(int ch, int id, int val) { - chstatus[ch][id]=val; - mididev[mappedoutput[ch]].dev->basicMessage(0xB0|ch,id,val); + chstatus[ch][id] = val; + mididev[mappedoutput[ch]].dev->basicMessage(0xB0 | ch, id, val); } -void CMidiPlayer::registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name) +void CMidiPlayer::registerMidiOutDevice(qmpMidiOutDevice *dev, std::string name) { - SMidiDev d; - d.dev=dev;d.name=name; - d.refcnt=0; - mididev.push_back(d); + SMidiDev d; + d.dev = dev; + d.name = name; + d.refcnt = 0; + mididev.push_back(d); } void CMidiPlayer::unregisterMidiOutDevice(std::string name) { - for(auto i=mididev.begin();i!=mididev.end();++i) - if(i->name==name) - { - i->dev->deviceDeinit(); - mididev.erase(i); - break; - } + for (auto i = mididev.begin(); i != mididev.end(); ++i) + if (i->name == name) + { + i->dev->deviceDeinit(); + mididev.erase(i); + break; + } } std::vector CMidiPlayer::getMidiOutDevices() { - std::vector ret; - for(auto &i:mididev) - ret.push_back(i.name); - return ret; + std::vector ret; + for (auto &i : mididev) + ret.push_back(i.name); + return ret; } int CMidiPlayer::getChannelOutput(int ch) { - return mappedoutput[ch]; -} -qmpMidiOutDevice* CMidiPlayer::getChannelOutputDevice(int ch) -{ - return mididev[mappedoutput[ch]].dev; -} -void CMidiPlayer::setChannelOutput(int ch,int outid) -{ - int origoutput=mappedoutput[ch]; - if(origoutput==outid) - return; - SMidiDev& dnew=mididev[outid]; - dnew.dev->onMapped(ch,++dnew.refcnt); - for(int i=0;i<124;++i) - { - if(i!=6&&i!=38&&i!=100&&i!=101)//avoid sending RPN/NRPN - { - unsigned st=chstatus[ch][i]; - if(!~st) - st=dnew.dev->getInitialCCValue(i,ch); - dnew.dev->basicMessage(0xB0|ch,i,chstatus[ch][i]); - } - } - dnew.dev->basicMessage(0xC0|ch,~chstatus[ch][128]?chstatus[ch][128]:dnew.dev->getInitialCCValue(128,ch),0); - mappedoutput[ch]=outid; - if(~origoutput) - { - SMidiDev& dold=mididev[origoutput]; - dold.dev->onUnmapped(ch,--dold.refcnt); - } -} -const std::chrono::system_clock::time_point* CMidiPlayer::getLastEventTS() -{return levtt;} -int CMidiPlayer::setEventHandlerCB(ICallBack *cb,void *userdata) -{ - for(int i=0;i<16;++i) - { - if(eventHandlerCB[i]==cb)return i; - if(eventHandlerCB[i]==nullptr) - { - eventHandlerCB[i]=cb;eventHandlerCBuserdata[i]=userdata; - return i; - } - } - return -1; + return mappedoutput[ch]; +} +qmpMidiOutDevice *CMidiPlayer::getChannelOutputDevice(int ch) +{ + return mididev[mappedoutput[ch]].dev; +} +void CMidiPlayer::setChannelOutput(int ch, int outid) +{ + int origoutput = mappedoutput[ch]; + if (origoutput == outid) + return; + SMidiDev &dnew = mididev[outid]; + dnew.dev->onMapped(ch, ++dnew.refcnt); + for (int i = 0; i < 124; ++i) + { + if (i != 6 && i != 38 && i != 100 && i != 101) //avoid sending RPN/NRPN + { + unsigned st = chstatus[ch][i]; + if (!~st) + st = dnew.dev->getInitialCCValue(i, ch); + dnew.dev->basicMessage(0xB0 | ch, i, chstatus[ch][i]); + } + } + dnew.dev->basicMessage(0xC0 | ch, ~chstatus[ch][128] ? chstatus[ch][128] : dnew.dev->getInitialCCValue(128, ch), 0); + mappedoutput[ch] = outid; + if (~origoutput) + { + SMidiDev &dold = mididev[origoutput]; + dold.dev->onUnmapped(ch, --dold.refcnt); + } +} +const std::chrono::system_clock::time_point *CMidiPlayer::getLastEventTS() +{ + return levtt; +} +int CMidiPlayer::setEventHandlerCB(ICallBack *cb, void *userdata) +{ + for (int i = 0; i < 16; ++i) + { + if (eventHandlerCB[i] == cb) + return i; + if (eventHandlerCB[i] == nullptr) + { + eventHandlerCB[i] = cb; + eventHandlerCBuserdata[i] = userdata; + return i; + } + } + return -1; } void CMidiPlayer::unsetEventHandlerCB(int id) -{eventHandlerCB[id]=nullptr;eventHandlerCBuserdata[id]=nullptr;} -int CMidiPlayer::setEventReaderCB(ICallBack *cb,void *userdata) -{ - for(int i=0;i<16;++i) - { - if(eventReaderCB[i]==cb)return i; - if(eventReaderCB[i]==nullptr) - { - eventReaderCB[i]=cb;eventReaderCBuserdata[i]=userdata; - return i; - } - } - return -1; +{ + eventHandlerCB[id] = nullptr; + eventHandlerCBuserdata[id] = nullptr; +} +int CMidiPlayer::setEventReaderCB(ICallBack *cb, void *userdata) +{ + for (int i = 0; i < 16; ++i) + { + if (eventReaderCB[i] == cb) + return i; + if (eventReaderCB[i] == nullptr) + { + eventReaderCB[i] = cb; + eventReaderCBuserdata[i] = userdata; + return i; + } + } + return -1; } void CMidiPlayer::unsetEventReaderCB(int id) -{eventReaderCB[id]=nullptr;eventReaderCBuserdata[id]=nullptr;} -int CMidiPlayer::setFileReadFinishedCB(ICallBack *cb,void *userdata) -{ - for(int i=0;i<16;++i) - { - if(fileReadFinishCB[i]==cb)return i; - if(fileReadFinishCB[i]==nullptr) - { - fileReadFinishCB[i]=cb;fileReadFinishCBuserdata[i]=userdata; - return i; - } - } - return -1; +{ + eventReaderCB[id] = nullptr; + eventReaderCBuserdata[id] = nullptr; +} +int CMidiPlayer::setFileReadFinishedCB(ICallBack *cb, void *userdata) +{ + for (int i = 0; i < 16; ++i) + { + if (fileReadFinishCB[i] == cb) + return i; + if (fileReadFinishCB[i] == nullptr) + { + fileReadFinishCB[i] = cb; + fileReadFinishCBuserdata[i] = userdata; + return i; + } + } + return -1; } void CMidiPlayer::unsetFileReadFinishedCB(int id) -{fileReadFinishCB[id]=nullptr;fileReadFinishCBuserdata[id]=nullptr;} -int CMidiPlayer::registerEventHandler(callback_t cb,void *userdata,bool post) { - int ret; - event_handlers[ret=event_handlers_id++]=std::make_tuple(cb,userdata,post); - return ret; + fileReadFinishCB[id] = nullptr; + fileReadFinishCBuserdata[id] = nullptr; +} +int CMidiPlayer::registerEventHandler(callback_t cb, void *userdata, bool post) +{ + int ret; + event_handlers[ret = event_handlers_id++] = std::make_tuple(cb, userdata, post); + return ret; } void CMidiPlayer::unregisterEventHandler(int id) { - event_handlers.find(id)!=event_handlers.end()&&event_handlers.erase(id); + event_handlers.find(id) != event_handlers.end() &&event_handlers.erase(id); } -int CMidiPlayer::registerEventReadHandler(callback_t cb,void *userdata) +int CMidiPlayer::registerEventReadHandler(callback_t cb, void *userdata) { - int ret; - event_read_handlers[ret=event_read_handlers_id++]=std::make_pair(cb,userdata); - return ret; + int ret; + event_read_handlers[ret = event_read_handlers_id++] = std::make_pair(cb, userdata); + return ret; } void CMidiPlayer::unregisterEventReadHandler(int id) { - event_read_handlers.find(id)!=event_read_handlers.end()&&event_read_handlers.erase(id); + event_read_handlers.find(id) != event_read_handlers.end() &&event_read_handlers.erase(id); } -int CMidiPlayer::registerFileReadFinishHook(callback_t cb,void *userdata) +int CMidiPlayer::registerFileReadFinishHook(callback_t cb, void *userdata) { - int ret; - file_read_finish_hooks[ret=file_read_finish_hooks_id++]=std::make_pair(cb,userdata); - return ret; + int ret; + file_read_finish_hooks[ret = file_read_finish_hooks_id++] = std::make_pair(cb, userdata); + return ret; } void CMidiPlayer::unregisterFileReadFinishHook(int id) { - file_read_finish_hooks.find(id)!=file_read_finish_hooks.end()&&file_read_finish_hooks.erase(id); + file_read_finish_hooks.find(id) != file_read_finish_hooks.end() &&file_read_finish_hooks.erase(id); +} +void CMidiPlayer::registerReader(qmpFileReader *reader, std::string name) +{ + midiReaders->registerReader(reader, name); } -void CMidiPlayer::registerReader(qmpFileReader *reader,std::string name) -{midiReaders->registerReader(reader,name);} void CMidiPlayer::unregisterReader(std::string name) -{midiReaders->unregisterReader(name);} +{ + midiReaders->unregisterReader(name); +} void CMidiPlayer::callEventReaderCB(SEvent d) { - if((d.type&0xF0)==0x90)++notes; - for(int i=0;i<16;++i)if(eventReaderCB[i]) - eventReaderCB[i]->callBack(&d,eventReaderCBuserdata[i]); - for(auto i=event_read_handlers.begin();i!=event_read_handlers.end();++i) - i->second.first(&d,i->second.second); + if ((d.type & 0xF0) == 0x90) + ++notes; + for (int i = 0; i < 16; ++i) + if (eventReaderCB[i]) + eventReaderCB[i]->callBack(&d, eventReaderCBuserdata[i]); + for (auto i = event_read_handlers.begin(); i != event_read_handlers.end(); ++i) + i->second.first(&d, i->second.second); } void CMidiPlayer::discardCurrentEvent() { - if(midiReaders->getCurrentReader()) - midiReaders->getCurrentReader()->discardCurrentEvent(); + if (midiReaders->getCurrentReader()) + midiReaders->getCurrentReader()->discardCurrentEvent(); } void CMidiPlayer::commitEventChange(SEvent d) { - if(midiReaders->getCurrentReader()) - midiReaders->getCurrentReader()->commitEventChange(d); + if (midiReaders->getCurrentReader()) + midiReaders->getCurrentReader()->commitEventChange(d); } -CMidiPlayer* CMidiPlayer::getInstance(){return ref;} +CMidiPlayer *CMidiPlayer::getInstance() +{ + return ref; +} diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp index 471c1db..b6a40d9 100644 --- a/core/qmpmidiplay.hpp +++ b/core/qmpmidiplay.hpp @@ -11,169 +11,170 @@ #define QMP_MAIN #include "qmpcorepublic.hpp" class CMidiPlayer; -class CSMFReader:public qmpFileReader +class CSMFReader : public qmpFileReader { - private: - CMidiFile* ret; - CMidiTrack* curTrack; - uint32_t fmt,trk; - FILE *f; - bool eventdiscarded; - uint32_t byteread,curt,curid; +private: + CMidiFile *ret; + CMidiTrack *curTrack; + uint32_t fmt, trk; + FILE *f; + bool eventdiscarded; + uint32_t byteread, curt, curid; - void error(int fatal,const char* format,...); - uint8_t read_u8(); - uint16_t read_u16(); - uint32_t read_u32(); - uint32_t read_varlen(); - int read_event(); - void read_track(); - void read_header(); - uint32_t read_chunk(int is_header); - public: - CSMFReader(); - ~CSMFReader(); - CMidiFile* readFile(const char* fn); - void discardCurrentEvent(); - void commitEventChange(SEvent d); + void error(int fatal, const char *format, ...); + uint8_t read_u8(); + uint16_t read_u16(); + uint32_t read_u32(); + uint32_t read_varlen(); + int read_event(); + void read_track(); + void read_header(); + uint32_t read_chunk(int is_header); +public: + CSMFReader(); + ~CSMFReader(); + CMidiFile *readFile(const char *fn); + void discardCurrentEvent(); + void commitEventChange(SEvent d); }; -class CMidiFileReaderCollection{ - private: - std::vector> readers; - qmpFileReader* currentReader; - public: - CMidiFileReaderCollection(); - ~CMidiFileReaderCollection(); - void registerReader(qmpFileReader* reader,std::string name); - void unregisterReader(std::string name); - CMidiFile* readFile(const char* fn); - qmpFileReader* getCurrentReader(); +class CMidiFileReaderCollection +{ +private: + std::vector> readers; + qmpFileReader *currentReader; +public: + CMidiFileReaderCollection(); + ~CMidiFileReaderCollection(); + void registerReader(qmpFileReader *reader, std::string name); + void unregisterReader(std::string name); + CMidiFile *readFile(const char *fn); + qmpFileReader *getCurrentReader(); }; class CMidiPlayer { - friend class CMidiFileReaderCollection; - private: - CMidiFileReaderCollection *midiReaders; - CMidiFile* midiFile; - std::vector> eorder; - uint32_t stamps[101],notes,ecnt,maxtk; - 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]; - uint16_t mute,solo; - double ftime; - bool sendSysEx,waitvoice; - uint8_t chstatus[16][130];//0..127: cc 128: pc - uint32_t ctempo,ctsn,ctsd,divs,cks; - double dpt;//time per tick - //raw tempo, timesig num., timesig den., division, keysig - //thread control - uint32_t tceptr,tcpaused,ct; - bool tcstop; - uint32_t finished,resumed; - uint32_t pbr[16],pbv[16]; - //playback correction - uint32_t ttick; - std::chrono::high_resolution_clock::time_point ttime; - std::chrono::system_clock::time_point levtt[16]; - struct SMidiDev - { - std::string name; - qmpMidiOutDevice* dev; - int refcnt; - }; - std::vector mididev; - int mappedoutput[16]; - ICallBack* eventHandlerCB[16]; - ICallBack* eventReaderCB[16]; - ICallBack* fileReadFinishCB[16]; - void* eventHandlerCBuserdata[16]; - void* eventReaderCBuserdata[16]; - void* fileReadFinishCBuserdata[16]; - std::unordered_map> event_handlers; - std::unordered_map> event_read_handlers; - std::unordered_map> file_read_finish_hooks; - int event_handlers_id,event_read_handlers_id,file_read_finish_hooks_id; - static CMidiPlayer* ref; + friend class CMidiFileReaderCollection; +private: + CMidiFileReaderCollection *midiReaders; + CMidiFile *midiFile; + std::vector> eorder; + uint32_t stamps[101], notes, ecnt, maxtk; + 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]; + uint16_t mute, solo; + double ftime; + bool sendSysEx, waitvoice; + uint8_t chstatus[16][130];//0..127: cc 128: pc + uint32_t ctempo, ctsn, ctsd, divs, cks; + double dpt;//time per tick + //raw tempo, timesig num., timesig den., division, keysig + //thread control + uint32_t tceptr, tcpaused, ct; + bool tcstop; + uint32_t finished, resumed; + uint32_t pbr[16], pbv[16]; + //playback correction + uint32_t ttick; + std::chrono::high_resolution_clock::time_point ttime; + std::chrono::system_clock::time_point levtt[16]; + struct SMidiDev + { + std::string name; + qmpMidiOutDevice *dev; + int refcnt; + }; + std::vector mididev; + int mappedoutput[16]; + ICallBack *eventHandlerCB[16]; + ICallBack *eventReaderCB[16]; + ICallBack *fileReadFinishCB[16]; + void *eventHandlerCBuserdata[16]; + void *eventReaderCBuserdata[16]; + void *fileReadFinishCBuserdata[16]; + std::unordered_map> event_handlers; + std::unordered_map> event_read_handlers; + std::unordered_map> file_read_finish_hooks; + int event_handlers_id, event_read_handlers_id, file_read_finish_hooks_id; + static CMidiPlayer *ref; - SEvent *getEvent(uint32_t id); - void dumpFile(); - void setBit(uint16_t &n,uint16_t bn,uint16_t b); - bool processEvent(const SEvent *e); - void processEventStub(const SEvent *e); - void prePlayInit(); - void playEvents(); - void fileTimer1Pass(); - void fileTimer2Pass(); - public: - CMidiPlayer(); - ~CMidiPlayer(); - bool playerLoadFile(const char* fn); - void playerInit(); - void playerDeinit(); - void playerThread(); - void playerPanic(bool reset=false); + SEvent *getEvent(uint32_t id); + void dumpFile(); + void setBit(uint16_t &n, uint16_t bn, uint16_t b); + bool processEvent(const SEvent *e); + void processEventStub(const SEvent *e); + void prePlayInit(); + void playEvents(); + void fileTimer1Pass(); + void fileTimer2Pass(); +public: + CMidiPlayer(); + ~CMidiPlayer(); + bool playerLoadFile(const char *fn); + void playerInit(); + void playerDeinit(); + void playerThread(); + void playerPanic(bool reset = false); - //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(); - bool stopFlag(); - void setResumed(); + //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(); + bool stopFlag(); + void setResumed(); - double getFtime(); - void getCurrentTimeSignature(int *n,int *d); - int getCurrentKeySignature(); - uint32_t getFileNoteCount(); - uint32_t getFileStandard(); - double getTempo(); - uint32_t getTick(); - uint32_t getRawTempo(); - uint32_t getDivision(); - uint32_t getMaxTick(); - double getPitchBend(int ch); - double getPitchBendRaw(int ch,uint32_t *pb,uint32_t *pbr); - const char* getTitle(); - const char* getCopyright(); + double getFtime(); + void getCurrentTimeSignature(int *n, int *d); + int getCurrentKeySignature(); + uint32_t getFileNoteCount(); + uint32_t getFileStandard(); + double getTempo(); + uint32_t getTick(); + uint32_t getRawTempo(); + uint32_t getDivision(); + uint32_t getMaxTick(); + double getPitchBend(int ch); + double getPitchBendRaw(int ch, uint32_t *pb, uint32_t *pbr); + const char *getTitle(); + const char *getCopyright(); - void sendSysX(bool send); + void sendSysX(bool send); - void setChannelPreset(int ch,int b,int p); - void setMute(int ch,bool m); - void setSolo(int ch,bool s); - bool getChannelMask(int ch); - uint16_t getCC(int ch,int id); - void setCC(int ch,int id,int val); + void setChannelPreset(int ch, int b, int p); + void setMute(int ch, bool m); + void setSolo(int ch, bool s); + bool getChannelMask(int ch); + uint16_t getCC(int ch, int id); + void setCC(int ch, int id, int val); - void registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name); - void unregisterMidiOutDevice(std::string name); - std::vector getMidiOutDevices(); - int getChannelOutput(int ch); - qmpMidiOutDevice* getChannelOutputDevice(int ch); - void setChannelOutput(int ch,int outid); - const std::chrono::system_clock::time_point* getLastEventTS(); - int setEventHandlerCB(ICallBack *cb,void *userdata); - void unsetEventHandlerCB(int id); - int setEventReaderCB(ICallBack *cb,void *userdata); - void unsetEventReaderCB(int id); - int setFileReadFinishedCB(ICallBack *cb,void *userdata); - void unsetFileReadFinishedCB(int id); - int registerEventHandler(callback_t cb,void *userdata,bool post); - void unregisterEventHandler(int id); - int registerEventReadHandler(callback_t cb,void *userdata); - void unregisterEventReadHandler(int id); - int registerFileReadFinishHook(callback_t cb,void *userdata); - void unregisterFileReadFinishHook(int id); - void registerReader(qmpFileReader* reader,std::string name); - void unregisterReader(std::string name); - void callEventReaderCB(SEvent d); + void registerMidiOutDevice(qmpMidiOutDevice *dev, std::string name); + void unregisterMidiOutDevice(std::string name); + std::vector getMidiOutDevices(); + int getChannelOutput(int ch); + qmpMidiOutDevice *getChannelOutputDevice(int ch); + void setChannelOutput(int ch, int outid); + const std::chrono::system_clock::time_point *getLastEventTS(); + int setEventHandlerCB(ICallBack *cb, void *userdata); + void unsetEventHandlerCB(int id); + int setEventReaderCB(ICallBack *cb, void *userdata); + void unsetEventReaderCB(int id); + int setFileReadFinishedCB(ICallBack *cb, void *userdata); + void unsetFileReadFinishedCB(int id); + int registerEventHandler(callback_t cb, void *userdata, bool post); + void unregisterEventHandler(int id); + int registerEventReadHandler(callback_t cb, void *userdata); + void unregisterEventReadHandler(int id); + int registerFileReadFinishHook(callback_t cb, void *userdata); + void unregisterFileReadFinishHook(int id); + void registerReader(qmpFileReader *reader, std::string name); + void unregisterReader(std::string name); + void callEventReaderCB(SEvent d); - void discardCurrentEvent(); - void commitEventChange(SEvent d); + void discardCurrentEvent(); + void commitEventChange(SEvent d); - static CMidiPlayer* getInstance(); + static CMidiPlayer *getInstance(); }; #endif diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp index c7d11f2..b3d4efe 100644 --- a/core/qmpmidiread.cpp +++ b/core/qmpmidiread.cpp @@ -7,248 +7,315 @@ #include #include #include +#include #include "qmpmidiplay.hpp" -static const char* GM1SysX="\xF0\x7E\x7F\x09\x01\xF7"; -static const char* GM2SysX="\xF0\x7E\x7F\x09\x03\xF7"; -static const char* GSSysEx="\xF0\x41\x10\x42\x12\x40\x00\x7F\x00\x41\xF7"; -static const char* XGSysEx="\xF0\x43\x10\x4C\x00\x00\x7E\x00\xF7"; +static const char *GM1SysX = "\xF0\x7E\x7F\x09\x01\xF7"; +static const char *GM2SysX = "\xF0\x7E\x7F\x09\x03\xF7"; +static const char *GSSysEx = "\xF0\x41\x10\x42\x12\x40\x00\x7F\x00\x41\xF7"; +static const char *XGSysEx = "\xF0\x43\x10\x4C\x00\x00\x7E\x00\xF7"; #define assert(x) if(!(x))this->error(false,"assertion failure @ qmpmidiread.cpp:%d",__LINE__) -void CSMFReader::error(int fatal,const char* format,...) +void CSMFReader::error(int fatal, const char *format, ...) { - va_list ap;char buf[1024],bufr[1024]; - va_start(ap,format);vsnprintf(buf,1024,format,ap);va_end(ap); - snprintf(bufr,1024,"%s at %#lx",buf,ftell(f)); - if(fatal)throw std::runtime_error(bufr); - else fprintf(stderr,"CSMFReader W: %s.\n",bufr); + va_list ap; + char buf[1024], bufr[1024]; + va_start(ap, format); + vsnprintf(buf, 1024, format, ap); + va_end(ap); + snprintf(bufr, 1024, "%s at %#lx", buf, ftell(f)); + if (fatal) + throw std::runtime_error(bufr); + else fprintf(stderr, "CSMFReader W: %s.\n", bufr); } uint8_t CSMFReader::read_u8() { - uint8_t ret=0; - int t=fgetc(f); - if(!~t)error(1,"Unexpected EOF"); - ret=(uint8_t)t; - return ret; + uint8_t ret = 0; + int t = fgetc(f); + if (!~t) + error(1, "Unexpected EOF"); + ret = (uint8_t)t; + return ret; } uint16_t CSMFReader::read_u16() { - uint16_t ret=0; - size_t sz=fread(&ret,2,1,f); - if(sz<1)error(1,"Unexpected EOF"); + uint16_t ret = 0; + size_t sz = fread(&ret, 2, 1, f); + if (sz < 1) + error(1, "Unexpected EOF"); #if defined(_MSC_VER)&&defined(_WIN32) - ret=_byteswap_ushort(ret); + ret = _byteswap_ushort(ret); #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - ret=__builtin_bswap16(ret); + ret = __builtin_bswap16(ret); #endif - return ret; + return ret; } uint32_t CSMFReader::read_u32() { - uint32_t ret=0; - size_t sz=fread(&ret,4,1,f); - if(sz<1)error(1,"Unexpected EOF"); + uint32_t ret = 0; + size_t sz = fread(&ret, 4, 1, f); + if (sz < 1) + error(1, "Unexpected EOF"); #if defined(_MSC_VER)&&defined(_WIN32) - ret=_byteswap_ulong(ret); + ret = _byteswap_ulong(ret); #elif __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - ret=__builtin_bswap32(ret); + ret = __builtin_bswap32(ret); #endif - return ret; + return ret; } uint32_t CSMFReader::read_varlen() { - uint32_t ret=0,c=0; - int t; - do - { - t=fgetc(f); - if(!~t)error(1,"Unexpected EOF"); - if(++c>4)error(1,"Variable length type overflow"); - ret<<=7;ret|=(t&0x7F); - }while(t&0x80); - return ret; + uint32_t ret = 0, c = 0; + int t; + do + { + t = fgetc(f); + if (!~t) + error(1, "Unexpected EOF"); + if (++c > 4) + error(1, "Variable length type overflow"); + ret <<= 7; + ret |= (t & 0x7F); + } while (t & 0x80); + return ret; } int CSMFReader::read_event()//returns 0 if End of Track encountered { - uint32_t delta=read_varlen();curt+=delta; - uint8_t type=read_u8();uint32_t p1,p2; - static uint8_t lasttype;eventdiscarded=false; - if(!(type&0x80)){fseek(f,-1,SEEK_CUR);type=lasttype;} - switch(type&0xF0) - { - case 0x80://Note Off - case 0x90://Note On - case 0xA0://Note Aftertouch - case 0xB0://Controller Change - case 0xE0://Pitch wheel - p1=read_u8();p2=read_u8(); - curTrack->appendEvent(SEvent(curid,curt,type,p1,p2)); - break; - case 0xC0://Patch Change - case 0xD0://Channel Aftertouch - p1=read_u8(); - curTrack->appendEvent(SEvent(curid,curt,type,p1,0)); - break; - case 0xF0: - if((type&0x0F)==0x0F)//Meta Event - { - uint8_t metatype=read_u8(); - uint32_t len=read_varlen();char* str=nullptr; - if(len<=1024&&len>0)str=new char[len+8]; - if(str)fread(str,1,len,f);else fseek(f,len,SEEK_CUR); - std::string sstr; - if(str){str[len]='\0';sstr=std::string(str,len);} - switch(metatype) - { - case 0x00://Sequence Number - assert(len==2||len==0); - break; - case 0x20://Channel Prefix - assert(len==1); - break; - case 0x2F://End of Track - assert(len==0); - return 0; - case 0x51://Set Tempo - assert(len==3); - curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,sstr)); - break; - case 0x54://SMTPE offset, not handled. - assert(len==5); - break; - case 0x58://Time signature - assert(len==4); - curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,sstr)); - break; - case 0x59://Key signature - assert(len==2); - curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,sstr)); - break; - case 0x01:case 0x02:case 0x03: - case 0x04:case 0x05:case 0x06: - case 0x07:case 0x7F:default://text-like meta - { - curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,sstr)); - if(str&&metatype==0x03&&!ret->title) - { - ret->title=new char[len+8]; - strcpy(ret->title,str); - } - if(str&&metatype==0x02&&!ret->copyright) - { - ret->copyright=new char[len+8]; - strcpy(ret->copyright,str); - } - } - } - if(str)delete[] str; - } - else if((type&0x0F)==0x00||(type&0x0F)==0x07)//SysEx - { - uint32_t len=read_varlen();char* str=nullptr; - str=new char[len+8]; - if((type&0x0F)==0x00) - { - str[0]=char(0xF0);++len; - size_t sz=fread(str+1,1,len-1,f); - if(szappendEvent(SEvent(curid,curt,type,0,0,std::string(str,len))); - 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,"Unknown event type %#x",type); - break; - default: - error(0,"Unknown event type %#x",type); - } - lasttype=type;++curid; - if(curTrack->eventList.size()) - { - SEvent& le=curTrack->eventList.back(); - CMidiPlayer::getInstance()->callEventReaderCB(le); - } - return 1; + uint32_t delta = read_varlen(); + curt += delta; + uint8_t type = read_u8(); + uint32_t p1, p2; + static uint8_t lasttype; + eventdiscarded = false; + if (!(type & 0x80)) + { + fseek(f, -1, SEEK_CUR); + type = lasttype; + } + switch (type & 0xF0) + { + case 0x80://Note Off + case 0x90://Note On + case 0xA0://Note Aftertouch + case 0xB0://Controller Change + case 0xE0://Pitch wheel + p1 = read_u8(); + p2 = read_u8(); + curTrack->appendEvent(SEvent(curid, curt, type, p1, p2)); + break; + case 0xC0://Patch Change + case 0xD0://Channel Aftertouch + p1 = read_u8(); + curTrack->appendEvent(SEvent(curid, curt, type, p1, 0)); + break; + case 0xF0: + if ((type & 0x0F) == 0x0F) //Meta Event + { + uint8_t metatype = read_u8(); + uint32_t len = read_varlen(); + char *str = nullptr; + if (len <= 1024 && len > 0) + str = new char[len + 8]; + if (str) + fread(str, 1, len, f); + else + fseek(f, len, SEEK_CUR); + std::string sstr; + if (str) + { + str[len] = '\0'; + sstr = std::string(str, len); + } + switch (metatype) + { + case 0x00://Sequence Number + assert(len == 2 || len == 0); + break; + case 0x20://Channel Prefix + assert(len == 1); + break; + case 0x2F://End of Track + assert(len == 0); + return 0; + case 0x51://Set Tempo + assert(len == 3); + curTrack->appendEvent(SEvent(curid, curt, type, metatype, 0, sstr)); + break; + case 0x54://SMTPE offset, not handled. + assert(len == 5); + break; + case 0x58://Time signature + assert(len == 4); + curTrack->appendEvent(SEvent(curid, curt, type, metatype, 0, sstr)); + break; + case 0x59://Key signature + assert(len == 2); + curTrack->appendEvent(SEvent(curid, curt, type, metatype, 0, sstr)); + break; + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x7F: + default://text-like meta + { + curTrack->appendEvent(SEvent(curid, curt, type, metatype, 0, sstr)); + if (str && metatype == 0x03 && !ret->title) + { + ret->title = new char[len + 8]; + strcpy(ret->title, str); + } + if (str && metatype == 0x02 && !ret->copyright) + { + ret->copyright = new char[len + 8]; + strcpy(ret->copyright, str); + } + } + } + if (str) + delete[] str; + } + else if ((type & 0x0F) == 0x00 || (type & 0x0F) == 0x07) //SysEx + { + uint32_t len = read_varlen(); + char *str = nullptr; + str = new char[len + 8]; + if ((type & 0x0F) == 0x00) + { + str[0] = char(0xF0); + ++len; + size_t sz = fread(str + 1, 1, len - 1, f); + if (sz < len - 1) + error(1, "Unexpected EOF"); + } + else + { + size_t sz = fread(str, 1, len, f); + if (sz < len) + error(1, "Unexpected EOF"); + } + curTrack->appendEvent(SEvent(curid, curt, type, 0, 0, std::string(str, len))); + 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, "Unknown event type %#x", type); + break; + default: + error(0, "Unknown event type %#x", type); + } + lasttype = type; + ++curid; + if (curTrack->eventList.size()) + { + SEvent &le = curTrack->eventList.back(); + CMidiPlayer::getInstance()->callEventReaderCB(le); + } + return 1; } void CSMFReader::read_track() { - ret->tracks.push_back(CMidiTrack()); - curTrack=&ret->tracks.back(); - uint32_t chnklen=read_u32();byteread=ftell(f);curt=0;curid=0; - while(read_event()); - byteread=ftell(f)-byteread; - if(bytereadchnklen) - error(1,"Read past end of track"); + ret->tracks.push_back(CMidiTrack()); + curTrack = &ret->tracks.back(); + uint32_t chnklen = read_u32(); + byteread = ftell(f); + curt = 0; + curid = 0; + while (read_event()); + byteread = ftell(f) - byteread; + if (byteread < chnklen) + { + error(0, "Extra bytes after EOT event"); + for (; byteread < chnklen; ++byteread) + fgetc(f); + } + if (byteread > chnklen) + error(1, "Read past end of track"); } void CSMFReader::read_header() { - uint32_t chnklen=read_u32();byteread=ftell(f); - if(chnklen<6)error(1,"Header chunk too short"); - if(chnklen>6)error(0,"Header chunk length longer than expected. Ignoring extra bytes"); - fmt=read_u16();trk=read_u16();ret->divs=read_u16(); - if(ret->divs&0x8000)error(1,"SMTPE format is not supported"); - for(byteread=ftell(f)-byteread;byteread 6) + error(0, "Header chunk length longer than expected. Ignoring extra bytes"); + fmt = read_u16(); + trk = read_u16(); + ret->divs = read_u16(); + if (ret->divs & 0x8000) + error(1, "SMTPE format is not supported"); + for (byteread = ftell(f) - byteread; byteread < chnklen; ++byteread) + fgetc(f); } uint32_t CSMFReader::read_chunk(int is_header) { - char hdr[6]; - fread(hdr,1,4,f); - if(feof(f))error(1,"Unexpected EOF"); - if(is_header) - { - if(!strncmp(hdr,"RIFF",4)) - { - fseek(f,4,SEEK_CUR); - fread(hdr,1,4,f); - if(strncmp(hdr,"RMID",4)){error(1,"Wrong file type in RIFF container");} - fseek(f,8,SEEK_CUR); - fread(hdr,1,4,f); - } - if(strncmp(hdr,"MThd",4)){error(1,"Wrong MIDI header.");} - else return read_header(),0; - } - else - if(strncmp(hdr,"MTrk",4)) - { - error(0,"Wrong track chunk header. Ignoring the entire chunk."); - uint32_t chnklen=read_u32();fseek(f,chnklen,SEEK_CUR);return 0; - } - else return read_track(),1; - return 0; + char hdr[6]; + fread(hdr, 1, 4, f); + if (feof(f)) + error(1, "Unexpected EOF"); + if (is_header) + { + if (!strncmp(hdr, "RIFF", 4)) + { + fseek(f, 4, SEEK_CUR); + fread(hdr, 1, 4, f); + if (strncmp(hdr, "RMID", 4)) + error(1, "Wrong file type in RIFF container"); + fseek(f, 8, SEEK_CUR); + fread(hdr, 1, 4, f); + } + if (strncmp(hdr, "MThd", 4)) + error(1, "Wrong MIDI header."); + else return read_header(), 0; + } + else if (strncmp(hdr, "MTrk", 4)) + { + error(0, "Wrong track chunk header. Ignoring the entire chunk."); + uint32_t chnklen = read_u32(); + fseek(f, chnklen, SEEK_CUR); + return 0; + } + else + return read_track(), 1; + return 0; } CSMFReader::CSMFReader() { - f=nullptr; + f = nullptr; } -CMidiFile* CSMFReader::readFile(const char* fn) +CMidiFile *CSMFReader::readFile(const char *fn) { - ret=new CMidiFile; - ret->title=ret->copyright=nullptr;ret->std=0;ret->valid=1; - try - { - if(!(f=fopen(fn,"rb"))) - throw std::runtime_error("Can't open file"); - read_chunk(1); - for(uint32_t i=0;ivalid=0;if(f)fclose(f);f=nullptr; - } - return ret; + ret = new CMidiFile; + ret->title = ret->copyright = nullptr; + ret->std = 0; + ret->valid = 1; + try + { + if (!(f = fopen(fn, "rb"))) + throw std::runtime_error("Can't open file"); + read_chunk(1); + for (uint32_t i = 0; i < trk; i += read_chunk(0)); + fclose(f); + f = nullptr; + } + catch (std::runtime_error &e) + { + fprintf(stderr, "CSMFReader E: %s is not a supported file. Cause: %s.\n", fn, e.what()); + ret->valid = 0; + if (f) + fclose(f); + f = nullptr; + } + return ret; } CSMFReader::~CSMFReader() { @@ -256,54 +323,65 @@ CSMFReader::~CSMFReader() void CSMFReader::discardCurrentEvent() { - if(eventdiscarded)return;eventdiscarded=true; - curTrack->eventList.pop_back(); + if (eventdiscarded) + return; + eventdiscarded = true; + curTrack->eventList.pop_back(); } void CSMFReader::commitEventChange(SEvent d) { - curTrack->eventList.back().time=d.time; - curTrack->eventList.back().type=d.type; - curTrack->eventList.back().p1=d.p1; - curTrack->eventList.back().p2=d.p2; + curTrack->eventList.back().time = d.time; + curTrack->eventList.back().type = d.type; + curTrack->eventList.back().p1 = d.p1; + curTrack->eventList.back().p2 = d.p2; } CMidiFileReaderCollection::CMidiFileReaderCollection() { - readers.clear();currentReader=nullptr; - registerReader(new CSMFReader(),"Default SMF Reader"); + readers.clear(); + currentReader = nullptr; + registerReader(new CSMFReader(), "Default SMF Reader"); } CMidiFileReaderCollection::~CMidiFileReaderCollection() { - delete readers[0].first; + delete readers[0].first; } -void CMidiFileReaderCollection::registerReader(qmpFileReader* reader,std::string name) +void CMidiFileReaderCollection::registerReader(qmpFileReader *reader, std::string name) { - for(unsigned i=0;isecond==name) - { - readers.erase(i); - return; - } + for (auto i = readers.begin(); i != readers.end(); ++i) + if (i->second == name) + { + readers.erase(i); + return; + } } -CMidiFile* CMidiFileReaderCollection::readFile(const char* fn) +CMidiFile *CMidiFileReaderCollection::readFile(const char *fn) { - CMidiFile *file=nullptr; - for(unsigned i=0;inotes=0; - CMidiFile* t=readers[i].first->readFile(fn); - if(t->valid){file=t;break;} - else delete t; - } - currentReader=nullptr; - return file; + CMidiFile *file = nullptr; + 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 + delete t; + } + currentReader = nullptr; + return file; +} +qmpFileReader *CMidiFileReaderCollection::getCurrentReader() +{ + return currentReader; } -qmpFileReader* CMidiFileReaderCollection::getCurrentReader() -{return currentReader;} -- cgit v1.2.3