aboutsummaryrefslogblamecommitdiff
path: root/core/qmpmidiplay.cpp
blob: c24b0c9b8b9e9094700bc8a49b969fb6e16393a9 (plain) (tree)
1
2
3
4
5
6
7
8
9
                 
                  
                    


                       
                          
             
                
                    
            
      
                                      
                                               
 



                                                                     
                                                  
                                                                                  
                                                                      

                                                                                
                                

                                                           


                                    
                            
                                   

                                                                                             
                            
                              


                                                      
                         
                                                
                                 


                                                                                                      
                                 
                                                        
                         

                                                  
                              

                                                
                              

                                                          





                                                  










                                                                                

                                                                                                
                                         

                                                  

                                                                              

                                                  

                                                                              



                                                                      
                                                                         


                                              


                                                                                 
                                                                                                                 
                             
         
                     


                                                   


                              


                                                                       
                         

                                                                                                      
                         








                                                       
                                                                         






                                                  










                                                                                
                                                                   
                                         
                                              
                                                  
                                                                                              

                                                  

                                                                                      
                                              



                                 
 


                          
                           
                           

                                                     

                                                                                 

                                                             
                                     
                         

      
                                          



                                                      


                               

                                            
                                    
 

                              

                                                                                
                                              

                                                                                           

                                                                                 





                                                                                                         
                                                                                            
                 




                                                                                                           



                                                                                                               
                                                            

                                                                                 


                                                                   
                    
                                                                    


                                                                                         
             
                                                                                                       
     
                                                                                                               
      
                 
                                              
                                          
         
                                                                                                                              



                                  
                                                      
                                                            
         




                                                           



                                  
                                                                          
                                                                            

                                                                                                      
         
                                                         
                                                          
                                            
         
                                                   
                                            
                                                            
         



                                                           

                                         
                                                                   

                                                            
                                       
                 
                                        
         

                    
                                                           
                                                    
                                 
         
 

                          
 
                                                    
                                                                           

                                                                   
                                                                             



                                                                        

                                                                            

                                                    
                                            


                                                       
                 
 

                           



                                              
                                      

                                                       
 
                                         
 

                            
         

                                                      
         
 
                                                
 

                                            
                                                    



                                           
                                      

                                                              
                                                    


                                                                                      









                                                                                      

                         
                    


                              
                                                                 
                                                            
                                                  
                                                                                           
                                            

                             
                                                             
         



                                     
                                         


                                
                      

                     
 
                                                      

                                                          

                                                    
                     
                                            

                             
                                                                    
                                      
                 





                                                                                         
                 
                                                                           

                                                                  
                                                                       
                                                                       
                                                              
                                            
                                                                  

                                                                                        

         
                                             
                                                                          
                                                      
                                                       


                                                                                
                                                        
                                           
                                                   
                                                 
                                                 
                                                                                  



                                                        
                                                      
 

                                                      
                            




                                                          
 











                                                                     

                                                              
                          
                                        
                        
                                        
                        

                                                

                                     
                                  
                                                                                              
                                


                                              
                             
                                                                    
 



                                                                               
 



                              
 
                                                           
 






                                                        
 
                                                         
 



                                     
 



                                         



                                                             
                                                    

                                        

                                             
                              




                                                               
         

                                                       
         
 

                                                                          
                                                                



                                                  
                                              







                                                                                
                                                                
                                                               



                                                 
                                             







                                                                              
                                                              
                                                                    



                                                    
                                                







                                                                                    
                                                                    
                                                                             
 
                
                                                                                  
                   






                                                                                


                                                                                      






                                                                                               


                                                                                            




                                                                                                        
                                                                        


                                                    
                                             



                                                                

                                                                                





                                                               
                                             





                                                              
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <thread>
#include <fluidsynth.h>
#include "qmpmidiplay.hpp"
#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
uint64_t pf;
#endif
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)
				{
					internalFluid->rpnMessage(ch,0,rpnval[ch]<<7);
					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);
#else
						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;
}
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];
				rpnid[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);
#else
						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;
	}
}
#ifdef _WIN32
void w32usleep(uint64_t t)
{
	uint64_t st=0,ct=0;
	timeBeginPeriod(1);
	QueryPerformanceCounter((LARGE_INTEGER*)&st);
	do{
		if(t>10000+(ct-st)*1000000/pf)Sleep((t-(ct-st)*1000000/pf)/2000);
		else if(t>5000+(ct-st)*1000000/pf)Sleep(1);
		else std::this_thread::yield();
		QueryPerformanceCounter((LARGE_INTEGER*)&ct);
	}while((ct-st)*1000000<t*pf);
	timeEndPeriod(1);
}
#endif
SEvent* CMidiPlayer::getEvent(uint32_t id)
{
	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;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;tceptr<ecnt;)
	{
		while(tcpaused)std::this_thread::sleep_for(std::chrono::milliseconds(100));
		using namespace std::chrono;
		high_resolution_clock::time_point b=high_resolution_clock::now();
		if(getTick()-_lrtick>divs){
			_lrtick=getTick();
			//double _dt=(getTick()-ttick)*dpt-duration_cast<nanoseconds>((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(resumed){resumed=false;
		ttick=getTick();ttime=high_resolution_clock::now();
		}
		else
		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();
#ifdef _WIN32
			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)));
#endif
		}
		if(tcstop||!midiReaders)break;
		ct=getEvent(tceptr)->time;
	}
	while(!tcstop&&(waitvoice&&internalFluid->getPolyphone()>0))std::this_thread::sleep_for(std::chrono::milliseconds(2));
	finished=1;
}
void CMidiPlayer::fileTimer1Pass()
{
	ftime=.0;ctempo=0x7A120;dpt=ctempo*1000./divs;
	for(uint32_t eptr=0,ct=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;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;internalFluid=new qmpMidiOutFluid();
	registerMidiOutDevice(internalFluid,"Internal FluidSynth");
	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,0,sizeof(mappedoutput));
	mididev[0].refcnt=16;
	memset(chstatus,0,sizeof(chstatus));
#ifdef _WIN32
	QueryPerformanceFrequency((LARGE_INTEGER*)&pf);
#endif
	ref=this;
}
CMidiPlayer::~CMidiPlayer()
{
	if(internalFluid)
	{
		internalFluid->deviceDeinit();
		delete internalFluid;
		internalFluid=nullptr;
	}
	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;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<size_t,size_t> &a,std::pair<size_t,size_t> &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=0;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=1;tcpaused=0;
	delete midiFile;midiFile=nullptr;
}
void CMidiPlayer::playerThread()
{
	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=1;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])
			{
				internalFluid->basicMessage(0xB0|i,j,ccstamps[st][i][j]);
				dest->basicMessage(0xB0|i,j,ccstamps[st][i][j]);
				chstatus[i][j]=ccstamps[st][i][j];
			}
		}
		internalFluid->basicMessage(0xC0|i,ccstamps[st][i][128],0);
		dest->basicMessage(0xC0|i,ccstamps[st][i][128],0);
		chstatus[i][128]=ccstamps[st][i][128];
		//fluid_synth_pitch_bend(synth,i,ccstamps[st][i][130]);
		internalFluid->rpnMessage(i,0,ccstamps[st][i][134]<<7);
		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];}
uint32_t CMidiPlayer::getTCpaused(){return tcpaused;}
void CMidiPlayer::setTCpaused(uint32_t ps){tcpaused=ps;}
uint32_t CMidiPlayer::isFinished(){return finished;}
void CMidiPlayer::setResumed(){resumed=true;}
void CMidiPlayer::setWaitVoice(bool wv){waitvoice=wv;}

void CMidiPlayer::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);
}
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);
}
//16MSB..LSB1
void CMidiPlayer::setBit(uint16_t &n, uint16_t bn, uint16_t b)
{n^=(((~b)+1)^n)&(1<<bn);}
void CMidiPlayer::setMute(int ch,bool m)
{setBit(mute,ch,m?1:0);}
void CMidiPlayer::setSolo(int ch,bool s)
{setBit(solo,ch,s?1:0);}
bool CMidiPlayer::getChannelMask(int ch)
{return((mute>>ch)&1)||(solo&&!((solo>>ch)&1));}
int 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)
{
	chstatus[ch][id]=val;
	mididev[mappedoutput[ch]].dev->basicMessage(0xB0|ch,id,val);
}

qmpMidiOutFluid* CMidiPlayer::fluid(){return internalFluid;}

void CMidiPlayer::registerMidiOutDevice(qmpMidiOutDevice* dev,std::string name)
{
	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;
	}
}
std::vector<std::string> CMidiPlayer::getMidiOutDevices()
{
	std::vector<std::string> 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];
	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
	dnew.dev->basicMessage(0xB0|ch,i,chstatus[ch][i]);
	dnew.dev->basicMessage(0xC0|ch,chstatus[ch][128],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;
}
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;
}
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;
}
void CMidiPlayer::unregisterEventHandler(int id)
{
	event_handlers.find(id)!=event_handlers.end()&&event_handlers.erase(id);
}
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;
}
void CMidiPlayer::unregisterEventReadHandler(int id)
{
	event_read_handlers.find(id)!=event_read_handlers.end()&&event_read_handlers.erase(id);
}
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;
}
void CMidiPlayer::unregisterFileReadFinishHook(int 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::unregisterReader(std::string 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);
}
void CMidiPlayer::discardCurrentEvent()
{
	if(midiReaders->getCurrentReader())
	midiReaders->getCurrentReader()->discardCurrentEvent();
}
void CMidiPlayer::commitEventChange(SEvent d)
{
	if(midiReaders->getCurrentReader())
	midiReaders->getCurrentReader()->commitEventChange(d);
}

CMidiPlayer* CMidiPlayer::getInstance(){return ref;}