DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/midi_win32.h
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #ifndef WIN32_LEAN_AND_MEAN
00021 #define WIN32_LEAN_AND_MEAN
00022 #endif
00023 #include <windows.h>
00024 #include <mmsystem.h>
00025 #include <string>
00026 #include <sstream>
00027 
00028 #define WIN32_MIDI_PORT_PROTECT 1
00029 
00030 class MidiHandler_win32: public MidiHandler {
00031 private:
00032         HMIDIOUT m_out = NULL;
00033     MIDIHDR m_hdr = {};
00034         HANDLE m_event = NULL;
00035         bool isOpen;
00036 
00037 #if WIN32_MIDI_PORT_PROTECT
00038         HINSTANCE hMidiHelper = NULL;
00039         bool midi_dll = false;
00040         bool midi_dll_active = false;
00041 
00042         void MidiHelper_Reset()
00043         {
00044                 midi_dll = false;
00045                 midi_dll_active = false;
00046 
00047 
00048                 // force unloading of old app - releases VSC lock
00049                 hMidiHelper = LoadLibrary( "midi_helper.dll" );
00050                 if( hMidiHelper ) {
00051                         midi_dll = true;
00052                         while( FreeLibrary( hMidiHelper ) != 0 ) Sleep(1);
00053                 }
00054         }
00055 
00056         int MidiHelper_Start( DWORD devID )
00057         {
00058                 HMIDIOUT (*func_ptr)(DWORD);
00059 
00060                 hMidiHelper = LoadLibrary( "midi_helper.dll" );
00061                 if( !hMidiHelper ) return -1;
00062 
00063     func_ptr = (HMIDIOUT(*)(DWORD))GetProcAddress( hMidiHelper,"MIDIHelper_OpenMidiOut" );
00064                 if (!func_ptr ) return -1;
00065 
00066                 m_out = func_ptr( devID );
00067                 if( m_out == 0 ) return -1;
00068 
00069 
00070                 midi_dll_active = true;
00071                 return 0;
00072         }
00073 
00074         void MidiHelper_End()
00075         {
00076                 void (*func_ptr)(void);
00077 
00078 
00079     func_ptr = (void(*)(void))GetProcAddress( hMidiHelper,"MIDIHelper_CloseMidiOut" );
00080                 if (!func_ptr ) return;
00081 
00082                 func_ptr();
00083         }
00084 #endif
00085 
00086 public:
00087         MidiHandler_win32() : MidiHandler(),isOpen(false) {};
00088         const char * GetName(void) { return "win32";};
00089 
00090         bool Open(const char * conf) {
00091                 MIDIOUTCAPS mididev;
00092                 if (isOpen) return false;
00093 
00094 
00095 #if WIN32_MIDI_PORT_PROTECT
00096                 // VSC crash protection
00097                 MidiHelper_Reset();
00098 #endif
00099 
00100 
00101                 m_event = CreateEvent (NULL, true, true, NULL);
00102                 MMRESULT res = MMSYSERR_NOERROR;
00103                 if(conf && *conf) {
00104                         std::string strconf(conf);
00105                         std::istringstream configmidi(strconf);
00106                         unsigned int total = midiOutGetNumDevs();
00107                         unsigned int nummer = total;
00108                         configmidi >> nummer;
00109                         if (configmidi.fail() && total) {
00110                                 lowcase(strconf);
00111                                 for(unsigned int i = 0; i< total;i++) {
00112                                         midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS));
00113                                         std::string devname(mididev.szPname);
00114                                         lowcase(devname);
00115                                         if (devname.find(strconf) != std::string::npos) {
00116                                                 nummer = i;
00117                                                 break;
00118                                         }
00119                                 }
00120                         }
00121 
00122                         if (nummer < total) {
00123                                 midiOutGetDevCaps(nummer, &mididev, sizeof(MIDIOUTCAPS));
00124                                 LOG_MSG("MIDI:win32 selected %s",mididev.szPname);
00125 
00126 
00127 #if WIN32_MIDI_PORT_PROTECT
00128                                 if( midi_dll == false || strcmp( mididev.szPname, "Roland VSC" ) != 0 )
00129                                         res = midiOutOpen(&m_out, nummer, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
00130                                 else {
00131                                         // Roland VSC - crash protection
00132                                         res = MidiHelper_Start(nummer);
00133                                 }
00134 #else
00135                                 res = midiOutOpen(&m_out, nummer, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
00136 #endif
00137 
00138 
00139                                 if( res != MMSYSERR_NOERROR ) {
00140                                         if( strcmp( mididev.szPname, "Roland VSC" ) == 0 ) MessageBox( 0, "Roland VSC failed", "MIDI", MB_OK | MB_TOPMOST );
00141 
00142                                         if( nummer != 0 ) {
00143                                                 LOG_MSG("MIDI:win32 selected %s","default");
00144                                                 res = midiOutOpen(&m_out, MIDI_MAPPER, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
00145                                         }
00146                                 }
00147                         }
00148                 } else {
00149                         res = midiOutOpen(&m_out, MIDI_MAPPER, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
00150                 }
00151                 if (res != MMSYSERR_NOERROR) return false;
00152 
00153                 Reset();
00154 
00155                 isOpen=true;
00156                 return true;
00157         };
00158 
00159         void Close(void) {
00160                 if (!isOpen) return;
00161                 isOpen=false;
00162 
00163 
00164 #if WIN32_MIDI_PORT_PROTECT
00165                 if( midi_dll ) MidiHelper_End();
00166 #endif
00167 
00168                 // flush buffers, then shutdown
00169                 midiOutReset(m_out);
00170                 midiOutClose(m_out);
00171 
00172                 CloseHandle (m_event);
00173         };
00174 
00175         void PlayMsg(Bit8u * msg) {
00176                 midiOutShortMsg(m_out, *(Bit32u*)msg);
00177         };
00178 
00179         void PlaySysex(Bit8u * sysex,Bitu len) {
00180 #if WIN32_MIDI_PORT_PROTECT
00181                 if( midi_dll_active == false ) {
00182 #endif
00183                         if (WaitForSingleObject (m_event, 2000) == WAIT_TIMEOUT) {
00184                                 LOG(LOG_MISC,LOG_ERROR)("Can't send midi message");
00185                                 return;
00186                         }
00187 #if WIN32_MIDI_PORT_PROTECT
00188                 }
00189 #endif
00190 
00191                 midiOutUnprepareHeader (m_out, &m_hdr, sizeof (m_hdr));
00192 
00193                 m_hdr.lpData = (char *) sysex;
00194                 m_hdr.dwBufferLength = (DWORD)len;
00195                 m_hdr.dwBytesRecorded = (DWORD)len;
00196                 m_hdr.dwUser = 0;
00197 
00198                 MMRESULT result = midiOutPrepareHeader (m_out, &m_hdr, sizeof (m_hdr));
00199                 if (result != MMSYSERR_NOERROR) return;
00200                 ResetEvent (m_event);
00201                 result = midiOutLongMsg (m_out,&m_hdr,sizeof(m_hdr));
00202                 if (result != MMSYSERR_NOERROR) {
00203                         SetEvent (m_event);
00204                         return;
00205                 }
00206 
00207 #if WIN32_MIDI_PORT_PROTECT
00208                 if( midi_dll_active == true ) {
00209                         while( midiOutUnprepareHeader (m_out, &m_hdr, sizeof (m_hdr)) != 0 )
00210                                 Sleep(1);
00211                 }
00212 #endif
00213         }
00214         
00215         void ListAll(Program* base) {
00216 #if defined (WIN32)
00217                 unsigned int total = midiOutGetNumDevs();       
00218                 for(unsigned int i = 0;i < total;i++) {
00219                         MIDIOUTCAPS mididev;
00220                         midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS));
00221                         base->WriteOut("%2d\t \"%s\"\n",i,mididev.szPname);
00222                 }
00223 #endif
00224         }
00225 
00226         void Reset()
00227         {
00228                 Bit8u buf[64];
00229 
00230                 // flush buffers
00231                 midiOutReset(m_out);
00232 
00233 
00234                 // GM1 reset
00235                 buf[0] = 0xf0;
00236                 buf[1] = 0x7e;
00237                 buf[2] = 0x7f;
00238                 buf[3] = 0x09;
00239                 buf[4] = 0x01;
00240                 buf[5] = 0xf7;
00241                 PlaySysex( (Bit8u *) buf, 6 );
00242 
00243 
00244                 // GS1 reset
00245                 buf[0] = 0xf0;
00246                 buf[1] = 0x41;
00247                 buf[2] = 0x10;
00248                 buf[3] = 0x42;
00249                 buf[4] = 0x12;
00250                 buf[5] = 0x40;
00251                 buf[6] = 0x00;
00252                 buf[7] = 0x7f;
00253                 buf[8] = 0x00;
00254                 buf[9] = 0x41;
00255                 buf[10] = 0xf7;
00256                 PlaySysex( (Bit8u *) buf, 11 );
00257         }
00258 };
00259 
00260 MidiHandler_win32 Midi_win32;