aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2019-06-16 20:12:14 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2019-06-16 20:12:14 +0800
commit795043d6851a355d70aa2341e2edfd526c24d041 (patch)
tree203991e9a062934254bc9c6ca2053bdf4c096773 /core
parent7b03fd544837fbe0bc5a5373b60dfd5de50892e1 (diff)
downloadQMidiPlayer-795043d6851a355d70aa2341e2edfd526c24d041.tar.xz
Inital implementation of the device properties API.
Added new interfaces to qmpMidiOutDevice. Implemented the new interfaces for qmpMidiOutFluid. Initial infra for device initialization file parsing. Move to the new interfaces for getting list of presets. Use DevIL instead of CxImage. External output devices are broken now but that is for another commit.
Diffstat (limited to 'core')
-rw-r--r--core/qmpmidioutfluid.cpp76
-rw-r--r--core/qmpmidioutfluid.hpp10
-rw-r--r--core/qmpmidioutrtmidi.cpp151
-rw-r--r--core/qmpmidioutrtmidi.hpp19
-rw-r--r--core/qmpmidiplay.cpp19
-rw-r--r--core/qmpmidiplay.hpp2
-rw-r--r--core/qmpmidiread.cpp11
7 files changed, 256 insertions, 32 deletions
diff --git a/core/qmpmidioutfluid.cpp b/core/qmpmidioutfluid.cpp
index bb8f290..414cb3a 100644
--- a/core/qmpmidioutfluid.cpp
+++ b/core/qmpmidioutfluid.cpp
@@ -1,6 +1,6 @@
#include <cstdio>
#include <cstring>
-#include <map>
+#include <algorithm>
#include "qmpmidioutfluid.hpp"
qmpMidiOutFluid::qmpMidiOutFluid()
{
@@ -30,6 +30,7 @@ void qmpMidiOutFluid::deviceInit()
}
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)
@@ -38,6 +39,7 @@ void qmpMidiOutFluid::deviceDeinit(bool freshsettings)
delete_fluid_audio_driver(adriver);
delete_fluid_synth(synth);
synth=nullptr;adriver=nullptr;
+ bnk.clear();pst.clear();
if(freshsettings)
{
delete_fluid_settings(settings);
@@ -117,6 +119,54 @@ void qmpMidiOutFluid::onMapped(uint8_t,int)
void qmpMidiOutFluid::onUnmapped(uint8_t,int)
{
}
+std::vector<std::pair<uint16_t,std::string>> qmpMidiOutFluid::getBankList()
+{
+ return bnk;
+}
+std::vector<std::pair<uint8_t,std::string>> qmpMidiOutFluid::getPresets(int bank)
+{
+ std::vector<std::pair<uint8_t,std::string>> 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 "";
+ 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)
+{
+ 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);
@@ -132,29 +182,33 @@ void qmpMidiOutFluid::setOptNum(const char *opt,double val)
void qmpMidiOutFluid::loadSFont(const char *path)
{
if(synth)fluid_synth_sfload(synth,path,1);
+ update_preset_list();
}
int qmpMidiOutFluid::getSFCount()
{
return synth?fluid_synth_sfcount(synth):0;
}
-std::vector<std::pair<std::pair<int,int>,std::string>> qmpMidiOutFluid::listPresets()
+void qmpMidiOutFluid::update_preset_list()
{
- std::vector<std::pair<std::pair<int,int>,std::string>> ret;
- std::map<std::pair<int,int>,std::string> pmap;
+ 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))
- pmap[std::make_pair(
- fluid_preset_get_banknum(preset),
- fluid_preset_get_num(preset)
- )]=fluid_preset_get_name(preset);
+ {
+ 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]=" ";
+ }
}
- for(auto i=pmap.begin();i!=pmap.end();++i)
- ret.push_back(std::make_pair(i->first,i->second));
- return ret;
+ std::sort(bnk.begin(),bnk.end());
+ bnk.erase(std::unique(bnk.begin(),bnk.end()),bnk.end());
}
int qmpMidiOutFluid::getPolyphone()
{
diff --git a/core/qmpmidioutfluid.hpp b/core/qmpmidioutfluid.hpp
index 963f9df..72197e7 100644
--- a/core/qmpmidioutfluid.hpp
+++ b/core/qmpmidioutfluid.hpp
@@ -3,6 +3,7 @@
#include <string>
#include <utility>
#include <vector>
+#include <unordered_map>
#include "../include/qmpcorepublic.hpp"
#include <fluidsynth.h>
class IFluidSettings
@@ -22,6 +23,9 @@ class qmpMidiOutFluid:public qmpMidiOutDevice,public IFluidSettings
fluid_settings_t* settings;
fluid_synth_t* synth;
fluid_audio_driver_t* adriver;
+ std::vector<std::pair<uint16_t,std::string>> bnk;
+ std::unordered_map<uint16_t,std::vector<std::string>> pst;
+ void update_preset_list();
public:
qmpMidiOutFluid();
~qmpMidiOutFluid();
@@ -36,13 +40,17 @@ class qmpMidiOutFluid:public qmpMidiOutDevice,public IFluidSettings
void reset(uint8_t ch);
void onMapped(uint8_t ch,int refcnt);
void onUnmapped(uint8_t ch,int refcnt);
+ std::vector<std::pair<uint16_t,std::string>> getBankList();
+ std::vector<std::pair<uint8_t,std::string>> getPresets(int 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);
//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();
- std::vector<std::pair<std::pair<int,int>,std::string>> listPresets();
int getPolyphone();
int getMaxPolyphone();
diff --git a/core/qmpmidioutrtmidi.cpp b/core/qmpmidioutrtmidi.cpp
index 2d6d41f..0ff2c86 100644
--- a/core/qmpmidioutrtmidi.cpp
+++ b/core/qmpmidioutrtmidi.cpp
@@ -1,8 +1,144 @@
+#include <cctype>
#include <cstdio>
#include <cstring>
+#include <deque>
#include <vector>
#include RT_MIDI_H
#include "qmpmidioutrtmidi.hpp"
+
+void split(std::string s,char c,std::deque<std::string>& 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;
+ }
+}
+
+qmpDeviceInitializer* qmpDeviceInitializer::parse(const char* path)
+{
+ qmpDeviceInitializer *ret=new qmpDeviceInitializer();
+ ret->initseq.eventList.clear();
+ FILE* f=fopen(path, "r");
+ if(!f)return nullptr;
+
+ bool st_inmapping=false;
+ char buf[1024];
+ int ln=0;
+ int cmsb=-1,clsb=-1;
+
+ //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;
+ };
+#define err(e) {delete ret;return fprintf(stderr,"line %d: %s",ln,e),nullptr;}
+ 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<std::string> 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(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")
+ {
+ 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;
+ }
+ }
+ }
+ }
+#undef err
+
+ fclose(f);
+ return ret;
+}
+
qmpMidiOutRtMidi::qmpMidiOutRtMidi(unsigned _portid)
{
portid=_portid;
@@ -91,6 +227,21 @@ void qmpMidiOutRtMidi::onUnmapped(uint8_t ch,int refcnt)
panic(ch);
if(!refcnt&&outport)outport->closePort();
}
+std::vector<std::pair<uint16_t,std::string>> qmpMidiOutRtMidi::getBankList()
+{
+}
+std::vector<std::pair<uint8_t,std::string>> qmpMidiOutRtMidi::getPresets(int bank)
+{
+}
+std::string qmpMidiOutRtMidi::getPresetName(uint16_t bank,uint8_t preset)
+{
+}
+bool qmpMidiOutRtMidi::getChannelPreset(int ch,uint16_t *bank,uint8_t *preset,std::string &presetname)
+{
+}
+uint8_t qmpMidiOutRtMidi::getInitialCCValue(uint8_t cc)
+{
+}
RtMidiOut* qmpRtMidiManager::dummy=nullptr;
void qmpRtMidiManager::createDevices()
diff --git a/core/qmpmidioutrtmidi.hpp b/core/qmpmidioutrtmidi.hpp
index 48ee0cf..23adec6 100644
--- a/core/qmpmidioutrtmidi.hpp
+++ b/core/qmpmidioutrtmidi.hpp
@@ -1,9 +1,23 @@
#ifndef QMPMIDIMAPPERS_H
#define QMPMIDIMAPPERS_H
+#include <unordered_map>
#include <vector>
#define QMP_MAIN
#include "../include/qmpcorepublic.hpp"
#include RT_MIDI_H
+struct qmpDeviceInitializer
+{
+ CMidiTrack initseq;
+ struct BankStore
+ {
+ std::unordered_map<uint8_t,std::string> presets;
+ std::string bankname;
+ };
+ std::unordered_map<uint16_t,BankStore> banks;
+ uint8_t initv[130];
+
+ static qmpDeviceInitializer* parse(const char* path);
+};
class qmpMidiOutRtMidi:public qmpMidiOutDevice
{
private:
@@ -22,6 +36,11 @@ public:
void reset(uint8_t ch);
void onMapped(uint8_t ch,int refcnt);
void onUnmapped(uint8_t ch,int refcnt);
+ std::vector<std::pair<uint16_t,std::string>> getBankList();
+ std::vector<std::pair<uint8_t,std::string>> getPresets(int 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);
};
class qmpRtMidiManager
{
diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp
index 28ca9e7..bbf1c3a 100644
--- a/core/qmpmidiplay.cpp
+++ b/core/qmpmidiplay.cpp
@@ -241,7 +241,7 @@ void CMidiPlayer::fileTimer2Pass()
memset(ccc,0,sizeof(ccc));memset(rpnid,0xFF,sizeof(rpnid));memset(rpnval,0xFF,sizeof(rpnval));
for(int i=0;i<16;++i)
{
- ccc[i][7]=100;ccc[i][8]=64;ccc[i][10]=64;ccc[i][11]=127;
+ ccc[i][7]=100;ccc[i][8]=64;ccc[i][10]=64;
ccc[i][11]=127;ccc[i][71]=64;ccc[i][72]=64;
ccc[i][73]=64;ccc[i][74]=64;ccc[i][75]=64;
ccc[i][76]=64;ccc[i][77]=64;ccc[i][78]=64;
@@ -427,19 +427,6 @@ uint32_t CMidiPlayer::isFinished(){return finished;}
void CMidiPlayer::setResumed(){resumed=true;}
void CMidiPlayer::setWaitVoice(bool wv){waitvoice=wv;}
-void CMidiPlayer::getChannelPreset(int ch,int *b,int *p,char *name)
-{
- if(mappedoutput[ch])
- {
- *b=((int)chstatus[ch][0]<<7)|chstatus[ch][32];
- *p=chstatus[ch][128];
- strcpy(name,"");
- }
- else
- {
- internalFluid->getChannelInfo(ch,b,p,name);
- }
-}
void CMidiPlayer::setChannelPreset(int ch,int b,int p)
{
chstatus[ch][128]=p;
@@ -510,6 +497,10 @@ 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];
diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp
index f42c98b..da9e9ef 100644
--- a/core/qmpmidiplay.hpp
+++ b/core/qmpmidiplay.hpp
@@ -141,7 +141,6 @@ class CMidiPlayer
void sendSysX(bool send);
void setChannelPreset(int ch,int b,int p);
- void getChannelPreset(int ch,int *b,int *p,char *name);
void setMute(int ch,bool m);
void setSolo(int ch,bool s);
bool getChannelMask(int ch);
@@ -154,6 +153,7 @@ class CMidiPlayer
void unregisterMidiOutDevice(std::string name);
std::vector<std::string> getMidiOutDevices();
int getChannelOutput(int ch);
+ qmpMidiOutDevice* getChannelOutputDevice(int ch);
void setChannelOutput(int ch,int outid);
uint8_t* getChstates();
int setEventHandlerCB(ICallBack *cb,void *userdata);
diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp
index 7f5bdfc..48a149d 100644
--- a/core/qmpmidiread.cpp
+++ b/core/qmpmidiread.cpp
@@ -94,7 +94,8 @@ int CSMFReader::read_event()//returns 0 if End of Track encountered
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);
- if(str)str[len]='\0';
+ std::string sstr;
+ if(str){str[len]='\0';sstr=std::string(str,len);}
switch(metatype)
{
case 0x00://Sequence Number
@@ -108,24 +109,24 @@ int CSMFReader::read_event()//returns 0 if End of Track encountered
return 0;
case 0x51://Set Tempo
assert(len==3);
- curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,str));
+ 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,str));
+ 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,str));
+ 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,str));
+ curTrack->appendEvent(SEvent(curid,curt,type,metatype,0,sstr));
if(str&&metatype==0x03&&!ret->title)
{
ret->title=new char[len+8];