Comparing: C:\Documents and Settings\Owner\My Documents\native_midi_win32.c To: C:\Documents and Settings\Owner\My Documents\native_midi_win32_fix.c ==== 1 /* 2 native_midi: Hardware Midi support for the SDL_mixer library 3 Copyright (C) 2000,2001 Florian 'Proff' Schulze 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 Florian 'Proff' Schulze 20 florian.proff.schulze@gmx.net 21 */ 22 #include "SDL_config.h" 23 24 /* everything below is currently one very big bad hack ;) Proff */ 25 26 #if __WIN32__ 27 #define WIN32_LEAN_AND_MEAN 28 #include 29 #include 30 #include 31 #include 32 #include 33 #include 34 #include "native_midi.h" 35 #include "native_midi_common.h" 36 37 struct _NativeMidiSong { 38 int MusicLoaded; 39 int MusicPlaying; 40 MIDIHDR MidiStreamHdr; 41 MIDIEVENT *NewEvents; 42 Uint16 ppqn; 43 int Size; 44 int NewPos; 45 }; 46 47 static UINT MidiDevice=MIDI_MAPPER; 48 static HMIDISTRM hMidiStream; 49 static NativeMidiSong *currentsong; 50 ==== !> /* haleyjd 10/06/08: synchronization mechanism to protect MCI buffer */ !> static HANDLE hBufferReleaseEvent; !> enum !> { !> NM_CB_KILL, /* main thread wants callback to wait for buffer release */ !> NM_CB_DEAD, /* main thread is tired of waiting, or buffer is released */ !> NM_CB_DONTRUN, /* do nothing because it's not ready */ !> NM_CB_NORMAL, /* all systems go */ !> }; !> static int callbackStatus = NM_CB_DONTRUN; !> ==== 51 static int BlockOut(NativeMidiSong *song) 52 { 53 MMRESULT err; 54 int BlockSize; ==== 55 ==== 56 if ((song->MusicLoaded) && (song->NewEvents)) 57 { 58 // proff 12/8/98: Added for savety 59 midiOutUnprepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); 60 if (song->NewPos>=song->Size) 61 return 0; 62 BlockSize=(song->Size-song->NewPos); 63 if (BlockSize<=0) 64 return 0; 65 if (BlockSize>36000) 66 BlockSize=36000; 67 song->MidiStreamHdr.lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos); 68 song->NewPos+=BlockSize; 69 song->MidiStreamHdr.dwBufferLength=BlockSize; 70 song->MidiStreamHdr.dwBytesRecorded=BlockSize; 71 song->MidiStreamHdr.dwFlags=0; 72 err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); 73 if (err!=MMSYSERR_NOERROR) 74 return 0; 75 err=midiStreamOut(hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); 76 return 0; 77 } 78 return 1; 79 } 80 81 static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist) 82 { 83 int eventcount; 84 MIDIEvent *event; 85 MIDIEVENT *newevent; 86 87 eventcount=0; 88 event=evntlist; 89 while (event) 90 { 91 eventcount++; 92 event=event->next; 93 } 94 song->NewEvents=malloc(eventcount*3*sizeof(DWORD)); 95 if (!song->NewEvents) 96 return; 97 memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD))); 98 99 eventcount=0; 100 event=evntlist; 101 newevent=song->NewEvents; 102 while (event) 103 { 104 int status = (event->status&0xF0)>>4; 105 switch (status) 106 { 107 case MIDI_STATUS_NOTE_OFF: 108 case MIDI_STATUS_NOTE_ON: 109 case MIDI_STATUS_AFTERTOUCH: 110 case MIDI_STATUS_CONTROLLER: 111 case MIDI_STATUS_PROG_CHANGE: 112 case MIDI_STATUS_PRESSURE: 113 case MIDI_STATUS_PITCH_WHEEL: 114 newevent->dwDeltaTime=event->time; 115 newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24); 116 newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); 117 eventcount++; 118 break; 119 120 case MIDI_STATUS_SYSEX: 121 if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */ 122 { 123 int tempo = (event->extraData[0] << 16) | 124 (event->extraData[1] << 8) | 125 event->extraData[2]; 126 newevent->dwDeltaTime=event->time; 127 newevent->dwEvent=(MEVT_TEMPO<<24) | tempo; 128 newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); 129 eventcount++; 130 } 131 break; 132 } 133 134 event=event->next; 135 } 136 137 song->Size=eventcount*3*sizeof(DWORD); 138 139 { 140 int time; 141 int temptime; 142 143 song->NewPos=0; 144 time=0; 145 newevent=song->NewEvents; 146 while (song->NewPosSize) 147 { 148 temptime=newevent->dwDeltaTime; 149 newevent->dwDeltaTime-=time; 150 time=temptime; 151 if ((song->NewPos+12)>=song->Size) 152 newevent->dwEvent |= MEVT_F_CALLBACK; 153 newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); 154 song->NewPos+=12; 155 } 156 } 157 song->NewPos=0; 158 song->MusicLoaded=1; 159 } 160 161 void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD dwInstance, 162 DWORD dwParam1, DWORD dwParam2 ) 163 { 164 switch( uMsg ) 165 { 166 case MOM_DONE: ==== !> /* !> haleyjd 10/06/08: An MCI MOM_DONE message indicates that MCI is "done" !> with the stream buffer. In the event that callbackStatus == NM_CB_KILL, !> the main thread wants us to indicate we've reached this point on the !> next time this callback is executed. It is waiting for the release event !> to be posted. !> */ !> if (callbackStatus == NM_CB_KILL) !> { !> callbackStatus = NM_CB_DEAD; // It's dead, Jim. !> SetEvent(hBufferReleaseEvent); // signal the event !> return; !> } !> else if (callbackStatus != NM_CB_NORMAL) !> return; /* haleyjd 10/06/08: don't do anything when status is abnormal */ !> ==== 167 if ((currentsong->MusicLoaded) && ((DWORD)dwParam1 == (DWORD)¤tsong->MidiStreamHdr)) 168 BlockOut(currentsong); 169 break; 170 case MOM_POSITIONCB: 171 if ((currentsong->MusicLoaded) && ((DWORD)dwParam1 == (DWORD)¤tsong->MidiStreamHdr)) 172 currentsong->MusicPlaying=0; 173 break; 174 default: 175 break; 176 } 177 } 178 179 int native_midi_detect() 180 { 181 MMRESULT merr; 182 HMIDISTRM MidiStream; 183 184 merr=midiStreamOpen(&MidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION); 185 if (merr!=MMSYSERR_NOERROR) 186 MidiStream=0; 187 midiStreamClose(MidiStream); 188 if (!MidiStream) 189 return 0; 190 else 191 return 1; 192 } 193 194 NativeMidiSong *native_midi_loadsong(char *midifile) 195 { 196 NativeMidiSong *newsong; 197 MIDIEvent *evntlist = NULL; 198 SDL_RWops *rw; 199 200 newsong=malloc(sizeof(NativeMidiSong)); 201 if (!newsong) 202 return NULL; 203 memset(newsong,0,sizeof(NativeMidiSong)); 204 205 /* Attempt to load the midi file */ 206 rw = SDL_RWFromFile(midifile, "rb"); 207 if (rw) { 208 evntlist = CreateMIDIEventList(rw, &newsong->ppqn); 209 SDL_RWclose(rw); 210 if (!evntlist) 211 { 212 free(newsong); 213 return NULL; 214 } 215 } 216 217 MIDItoStream(newsong, evntlist); 218 219 FreeMIDIEventList(evntlist); 220 221 return newsong; 222 } 223 224 NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw) 225 { 226 NativeMidiSong *newsong; 227 MIDIEvent *evntlist = NULL; 228 229 newsong=malloc(sizeof(NativeMidiSong)); 230 if (!newsong) 231 return NULL; 232 memset(newsong,0,sizeof(NativeMidiSong)); 233 234 /* Attempt to load the midi file */ 235 evntlist = CreateMIDIEventList(rw, &newsong->ppqn); 236 if (!evntlist) 237 { 238 free(newsong); 239 return NULL; 240 } 241 242 MIDItoStream(newsong, evntlist); 243 244 FreeMIDIEventList(evntlist); 245 246 return newsong; 247 } 248 249 void native_midi_freesong(NativeMidiSong *song) 250 { 251 if (hMidiStream) 252 { ==== 253 /* haleyjd 10/06/08: we need a full call to stop here */ !> native_midi_stop(); ==== 255 } 256 if (song) 257 { 258 if (song->NewEvents) 259 free(song->NewEvents); 260 free(song); 261 } 262 } 263 264 void native_midi_start(NativeMidiSong *song) 265 { 266 MMRESULT merr; 267 MIDIPROPTIMEDIV mptd; 268 269 native_midi_stop(); ==== !> !> callbackStatus = NM_CB_DONTRUN; /* haleyjd: do nothing until it's all set up */ !> ==== 270 if (!hMidiStream) 271 { ==== !> /* haleyjd 10/06/08: create synchronization event */ !> hBufferReleaseEvent = CreateEvent(NULL, FALSE, FALSE, "SDL_mixer buffer release event"); !> if (!hBufferReleaseEvent) !> return; /* woops! we need this to go on. */ !> ==== 272 merr=midiStreamOpen(&hMidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION); 273 if (merr!=MMSYSERR_NOERROR) 274 { 275 hMidiStream=0; 276 return; 277 } 278 //midiStreamStop(hMidiStream); 279 currentsong=song; 280 currentsong->NewPos=0; 281 currentsong->MusicPlaying=1; 282 mptd.cbStruct=sizeof(MIDIPROPTIMEDIV); 283 mptd.dwTimeDiv=currentsong->ppqn; 284 merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV); 285 BlockOut(song); 286 merr=midiStreamRestart(hMidiStream); ==== !> !> callbackStatus = NM_CB_NORMAL; /* haleyjd: heavy troopa is ready to launch */ ==== 287 } 288 } 289 290 void native_midi_stop() 291 { 292 if (!hMidiStream) 293 return; ==== !> !> if (callbackStatus == NM_CB_NORMAL) !> { !> /* haleyjd 10/06/08: we want to kill the callback */ !> callbackStatus = NM_CB_KILL; !> !> /* !> haleyjd: wait for the callback to indicate that the buffer has been !> released by the MCI subsystem; otherwise we are going to cause mayhem. !> A timeout is required to prevent hangups while debugging; I choose a !> 2 second period for arbitrary reasons. !> */ !> if (WaitForSingleObject(hBufferReleaseEvent, 2000) == WAIT_TIMEOUT) !> { !> /* though remotely dangerous, we must go on */ !> callbackStatus = NM_CB_DEAD; !> } !> } !> ==== 294 midiStreamStop(hMidiStream); 295 midiStreamClose(hMidiStream); 296 currentsong=NULL; 297 hMidiStream = 0; ==== !> !> /* haleyjd 10/06/08: destroy event handle */ !> if (hBufferReleaseEvent) !> { !> CloseHandle(hBufferReleaseEvent); !> hBufferReleaseEvent = 0; !> callbackStatus = NM_CB_DONTRUN; /* back to initial state */ !> } ==== 298 } 299 300 int native_midi_active() 301 { 302 return currentsong->MusicPlaying; 303 } 304 305 void native_midi_setvolume(int volume) 306 { 307 int calcVolume; 308 if (volume > 128) 309 volume = 128; 310 if (volume < 0) 311 volume = 0; 312 calcVolume = (65535 * volume / 128); 313 314 midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume)); 315 } 316 317 char *native_midi_error() 318 { 319 return ""; 320 } 321 322 #endif /* Windows native MIDI support */