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 <windows.h>
29	    #include <windowsx.h>
30	    #include <mmsystem.h>
31	    #include <stdio.h>
32	    #include <stdlib.h>
33	    #include <limits.h>
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->NewPos<song->Size)
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)&currentsong->MidiStreamHdr))
168	            BlockOut(currentsong);
169	          break;
170	        case MOM_POSITIONCB:
171	          if ((currentsong->MusicLoaded) && ((DWORD)dwParam1 == (DWORD)&currentsong->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	 <!     midiStreamStop(hMidiStream);
254	 <!     midiStreamClose(hMidiStream);
	 !>      /* 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 */