#include "main.h" #include "seq.h" #include #include "resource.h" // {76B6A32D-99A9-4d51-B1F5-DAD3F47FE75D} static const GUID midiout_guid = { 0x76b6a32d, 0x99a9, 0x4d51, { 0xb1, 0xf5, 0xda, 0xd3, 0xf4, 0x7f, 0xe7, 0x5d } }; // {7F00BC9C-AEA3-472a-BBB9-D74ABD4FCA58} static const GUID midiout_driver_guid = { 0x7f00bc9c, 0xaea3, 0x472a, { 0xbb, 0xb9, 0xd7, 0x4a, 0xbd, 0x4f, 0xca, 0x58 } }; static MMRESULT midiOutOpen_wrap(HMIDIOUT * hMo,int id) { try { return midiOutOpen(hMo,(UINT)id,0,0,CALLBACK_NULL); } catch(...) { return -1; } } class MIDI_device_midiout : public MIDI_device { private: GUID guid; UINT id; DWORD flags; UINT type; virtual player_base * create(); virtual GUID get_guid() {return guid;} virtual bool is_default() {return id==(UINT)(-1);} virtual bool volctrl_happy() {return (type==7) && (flags & MIDICAPS_VOLUME);} public: MIDI_device_midiout(UINT p_id,DWORD p_flags,UINT p_type,const wchar_t * p_name,const wchar_t * p_info) { id=p_id; guid = midiout_guid; *(DWORD*)&guid+=id; flags = p_flags; type = p_type; set_name(p_name); set_info(p_info); } inline DWORD get_flags() {return flags;} inline DWORD get_id() {return id;} }; static void midiout_sysex(HMIDIOUT hMo,BYTE* p,UINT len) { MIDIHDR h; ZeroMemory(&h,sizeof(h)); h.dwBytesRecorded=h.dwBufferLength=len; h.lpData=(char*)p; if (FAILED(midiOutPrepareHeader(hMo,&h,sizeof(h)))) { #ifdef USE_LOG log_write("unable to send sysex"); #endif return; } if (SUCCEEDED(midiOutLongMsg(hMo,&h,sizeof(h)))) { while(!(h.dwFlags&MHDR_DONE)) MIDI_callback::Idle(); } midiOutUnprepareHeader(hMo,&h,sizeof(h)); //log_write("sysex sent OK"); } void midiout_sysex(HMIDIOUT hMo,BYTE* p,UINT len); static void sysex_startup_midiout(UINT m_id) { if (need_sysex_start()) { // MessageBox(GetActiveWindow(),"blah",0,0); HMIDIOUT hMo; MMRESULT r=midiOutOpen_wrap(&hMo,m_id); if (!r) { sysex_startup((SYSEXFUNC)midiout_sysex,hMo); midiOutClose(hMo); } } } class midiout_volctrl { private: HMIDIOUT hMo; int vol,pan; void _setvol(); static UINT map_vol(UINT volume,UINT scale); public: void volctrl_init(HMIDIOUT,MIDI_device_midiout*); int volctrl_setvol(int); int volctrl_setpan(int); }; class player_midiout : public seq_base, private midiout_volctrl { public: virtual ~player_midiout(); virtual int setvol(int i) {return volctrl_setvol(i);}; virtual int setpan(int i) {return volctrl_setpan(i);}; player_midiout(MIDI_device_midiout * p_dev) { dev=p_dev; hMo=0; } int play(); private: MIDI_device_midiout * dev; HMIDIOUT hMo; virtual void seq_shortmsg(DWORD msg) {midiOutShortMsg(hMo,msg);} virtual void seq_sysex(BYTE* ptr,UINT len) {midiout_sysex(hMo,ptr,len);} virtual int seq_play_start(); virtual void seq_play_stop(); }; int player_midiout::seq_play_start() { return 1; } void player_midiout::seq_play_stop() { if (hMo) { #ifdef USE_LOG log_write("midiOutClose"); #endif DWORD r=midiOutClose(hMo); if (r==MIDIERR_STILLPLAYING) { log_write("still playing (?), calling midiOutReset"); midiOutReset(hMo); r=midiOutClose(hMo); } #ifdef USE_LOG if (r) log_write("warning: unable to close midiOut"); else log_write("midiOut closed OK"); #endif } hMo=0; } int player_midiout::play() { DWORD r=midiOutOpen_wrap(&hMo,dev->get_id()); if (r) { if (r!=-1) MIDI_core::MM_error(r); return 0; } volctrl_init(hMo,dev); if (!seq_cmd_start(0)) { midiOutClose(hMo); hMo=0; return 0; } return 1; } player_midiout::~player_midiout() { #ifdef USE_LOG log_write("shutting down midiOut"); #endif seq_cmd_stop(); } class MIDI_driver_midiout : MIDI_driver { virtual void do_init() { MIDIOUTCAPSW caps; UINT n_mo_dev=midiOutGetNumDevs()+1; UINT n; for(n=0;nhMo; MIDIHDR h; ZeroMemory(&h,sizeof(h)); h.dwUser=1; DWORD l=12+len; if (l&3) l=(l+4)&~3; DWORD* ev=(DWORD*)alloca(l); ev[0]=ev[1]=0; ev[2]=MEVT_F_LONG|len; memcpy(ev+3,p,len); h.dwBytesRecorded=h.dwBufferLength=l; h.lpData=(char*)ev; if (FAILED(midiOutPrepareHeader((HMIDIOUT)hMo,&h,sizeof(h)))) return; if (FAILED(midiStreamOut(hMo,&h,sizeof(h)))) { midiOutUnprepareHeader((HMIDIOUT)hMo,&h,sizeof(h)); return; } pl->in_mm++; do_messages(pl->wnd,(bool*)&h.dwUser); log_write("sysex sent OK"); }*/ DWORD player_midistream::pos4time(DWORD t) { DWORD r=0; while(r1) { c_loop--; cp=pos4time(ct=loop_start); continue; } else { got_eof=1; if (cfg_eof_delay) { renderbuf_wait(hdr,cfg_eof_delay); } else break; } } if (midiOutPrepareHeader((HMIDIOUT)hMo,hdr,sizeof(MIDIHDR))) { got_eof=1; break; } if (midiStreamOut(hMo,hdr,sizeof(MIDIHDR))) { got_eof=1; break; } cur_buf=(cur_buf+1)&BUF_MASK; in_mm++; n_free--; if (!got_eof) ct=cp ? events[cp-1].tm : 0; } } UINT player_midistream::renderbuf(MIDIHDR* buf,DWORD ts,UINT start,UINT end) { UINT n=start; UINT p=0; DWORD* pEv=(DWORD*)buf->lpData; UINT c_t=ts; while(nevents[events[n].ev&0x7FFFFFFF]; if (p+(se->len>>2)>=BUF_MAX) { p-=2; break; } pEv[p++]=MEVT_F_LONG|se->len; DWORD d=se->len>>2; if (se->len&3) { pEv[p+(d++)]=0; } memcpy(pEv+p,smap->data+se->ofs,se->len); p+=d; } else { pEv[p++]=events[n].ev; } n++; } if (p==0) return -1; buf->dwBufferLength=p<<2; buf->dwBytesRecorded=p<<2; buf->dwFlags=0; return n; } void player_midistream::renderbuf_wait(MIDIHDR* buf,UINT len) { UINT p=0; DWORD* pEv=(DWORD*)buf->lpData; pEv[p++]=len<<3; pEv[p++]=0; pEv[p++]=MEVT_NOP<<24; buf->dwBufferLength=p<<2; buf->dwBytesRecorded=p<<2; buf->dwFlags=0; } UINT player_midistream::renderbuf_seek(UINT start,UINT end) { BYTE ins_tab[16] = {0}; memset(ins_tab,-1,sizeof(ins_tab)); BYTE ctrl_tab[16][128] = {0}; memset(ctrl_tab,-1,sizeof(ctrl_tab)); UINT n=start; DWORD* pEv=(DWORD*)seekbuf.h.lpData; while(n>8)&0x7F]=(BYTE)(ec>>16); else if (cd==0xC0) ins_tab[ch]=(BYTE)(ec>>8); n++; } UINT c; UINT p=0; for(c=0;c<16;c++) { for(n=0;n<128;n++) { if (!(ctrl_tab[c][n]&0x80)) { pEv[p++]=0; pEv[p++]=0; pEv[p++]=0xB0|c|(n<<8)|(ctrl_tab[c][n]<<16); } if (p>=BUF_MAX) goto q; } if (!(ins_tab[c]&0x80)) { pEv[p++]=0; pEv[p++]=0; pEv[p++]=0xC0|c|(ins_tab[c]<<8); } if (p>=BUF_MAX) goto q; } q: if (p==0) return -1; seekbuf.h.dwBufferLength=p<<2; seekbuf.h.dwBytesRecorded=p<<2; seekbuf.h.dwFlags=0; return n; } void player_midistream::buf_done(MIDIHDR* h) { in_mm--; midiOutUnprepareHeader((HMIDIOUT)hMo,h,sizeof(MIDIHDR)); if (h->dwUser) { h->dwUser=0; return; } if (h==&seekbuf.h) return; n_free++; if (quitting) return; if (!in_mm && got_eof) MIDI_core::Eof(); else if (!got_eof) { do_bufs(); } } LRESULT WINAPI player_midistream::midiOutProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) { if (msg==MM_MOM_DONE) { player_midistream* p=(player_midistream*)GetWindowLong(wnd,0); if (p) p->buf_done((MIDIHDR*)lp); } else if (msg==WM_SEEK) { player_midistream* p=(player_midistream*)GetWindowLong(wnd,0); if (p) p->settime(lp); } return DefWindowProc(wnd,msg,wp,lp); } player_midistream::player_midistream(MIDI_device_midiout * p_dev) { dev=p_dev; m_id=0; c_loop=0; smap=0; hMo=0; n_events=0; events=0; tm_ofs=0;p_time=0; loop_start=0; total_len=0; memset(&hdrs,0,sizeof(hdrs)); memset(&seekbuf,0,sizeof(seekbuf)); cur_buf=0; in_mm=0;n_free=0; ct=0;cp=0; got_eof=0; paused=0; quitting=0; seek_to=0; wnd=0; trd_id=0; static ATOM cb_class; if (!cb_class) cb_class=do_callback_class(midiOutProc); wnd=create_callback_wnd(cb_class,this); c_loop=cfg_loop_infinite ? -1 : cfg_loop_count; } player_base* MIDI_device_midiout::create() { if (cfg_playback_mode) { player_midiout *p=new player_midiout(this); if (p) { if (!p->play()) {delete p;p=0;} } return p; } else { player_midistream *p=new player_midistream(this); if (p) { if (!p->play()) {delete p;p=0;} } return p; } } //extern bool cfg_alt_sysex; int player_midistream::play() { trd_id=GetCurrentThreadId(); UINT n; for(n=0;nget_id()); } #ifdef USE_LOG log_write("starting midiOut / streamed"); #endif { UINT id=dev->get_id(); DWORD err=midiStreamOpen(&hMo,&id,1,(DWORD)wnd,0,CALLBACK_WINDOW); if (err) { MIDI_core::MM_error(err); return 0; } MIDIPROPTIMEDIV td; td.cbStruct=sizeof(td); td.dwTimeDiv=1*8;//tix / q err=midiStreamProperty(hMo,(BYTE*)&td,MIDIPROP_SET|MIDIPROP_TIMEDIV); if (err) { midiStreamClose(hMo); MIDI_core::MM_error(err); return 0; } MIDIPROPTEMPO tempo; tempo.cbStruct=sizeof(tempo); tempo.dwTempo=1000;//ns / q err=midiStreamProperty(hMo,(BYTE*)&tempo,MIDIPROP_SET|MIDIPROP_TEMPO); if (err) { midiStreamClose(hMo); MIDI_core::MM_error(err); return 0; } } events=do_table(MIDI_core::getFile(),8,&n_events,&loop_start,0); if (!events) { midiStreamClose(hMo); hMo=0; return 0; } total_len=events[n_events-1].tm>>3; if (!cfg_nosysex && MIDI_core::getFile()->smap && MIDI_core::getFile()->smap->pos) { smap=MIDI_core::getFile()->smap; } else smap=0; paused=0; volctrl_init((HMIDIOUT)hMo,dev); midiStreamPause(hMo); //sysex_startup((SYSEXFUNC)midiout_sysex,hMo); seek_to=-1; tm_ofs=GET_TIME; cur_buf=0; in_mm=0; n_free=N_BUFS; ct=cp=0; do_bufs(); #ifdef USE_LOG log_write("started OK"); #endif midiStreamRestart(hMo); return 1; } player_midistream::~player_midistream() { // bool alt_sysex=cfg_alt_sysex; #ifdef USE_LOG log_write("shutting down midistream"); #endif if (hMo) { //ASSERT(trd_id!=GetCurrentThreadId()); quitting=1; #ifdef USE_LOG log_write("midiStreamStop"); #endif midiStreamStop(hMo); // do_messages(wnd,(bool*)&in_mm); if (n_free!=N_BUFS) { UINT n; for(n=0;ntotal_len) { UINT _ls=loop_start>>3; ret=(ret-_ls)%(total_len-_ls)+_ls; } } return ret; } int player_midistream::settime(int tm) { if (!paused) { if (trd_id==GetCurrentThreadId()) { seek_to=tm<<3; got_eof=0; tm_ofs=GET_TIME-tm; midiStreamStop(hMo); midiStreamPause(hMo); quitting=1; do_messages(wnd,(bool*)&in_mm); quitting=0; do_bufs(); midiStreamRestart(hMo); } else PostMessage(wnd,WM_SEEK,0,tm); } else { p_time=tm; } return 1; } void player_midistream::pause() { p_time=GET_TIME-tm_ofs; paused=1; midiStreamPause(hMo); } void player_midistream::unpause() { tm_ofs=GET_TIME-p_time; paused=0; if (seek_to!=-1) { midiStreamStop(hMo); midiStreamPause(hMo); if (trd_id==GetCurrentThreadId()) { quitting=1; do_messages(wnd,(bool*)&in_mm); quitting=0; do_bufs(); } } midiStreamRestart(hMo); } UINT midiout_volctrl::map_vol(UINT volume,UINT scale) { double _vol=volume>0 ? 20*log10((double)volume/(double)scale) : -60.0;//in negative db _vol=_vol/60.0+1; if (_vol<0) _vol=0; return (UINT)(_vol*(double)scale); } void midiout_volctrl::_setvol() { DWORD _vol=257*vol; DWORD vol1=_vol,vol2=_vol; if (pan!=666) { if (pan<0) { vol2=(vol2*(128+pan))>>7; } else if (pan>0) { vol1=(vol1*(128-pan))>>7; } } if (cfg_logvol) { vol1=map_vol(vol1,0xFFFF); vol2=map_vol(vol2,0xFFFF); } midiOutSetVolume((HMIDIOUT)hMo,(vol2<<16)|vol1); } void midiout_volctrl::volctrl_init(HMIDIOUT _hMo,MIDI_device_midiout * dev) { hMo=_hMo; pan=(dev->get_flags()&MIDICAPS_LRVOLUME) ? MIDI_core::player_getPan() : 666; vol=(dev->get_flags()&MIDICAPS_VOLUME) ? MIDI_core::player_getVol() : 666; _setvol(); } int midiout_volctrl::volctrl_setvol(int _vol) { if (vol!=666) { vol=_vol; _setvol(); return 1; } else return 0; } int midiout_volctrl::volctrl_setpan(int _pan) { if (pan!=666) { pan=_pan; _setvol(); return 1; } else return 0; }