]> icculus.org git repositories - btb/d2x.git/blob - arch/win32/hmpfile.c
added UDP support for win32
[btb/d2x.git] / arch / win32 / hmpfile.c
1 /* This is HMP file playing code by Arne de Bruijn */
2 #include <stdio.h>
3 #include <limits.h>
4 #include <stdlib.h>
5 #include "hmpfile.h"
6
7 #if 0
8 #define CFILE FILE
9 #define cfopen fopen
10 #define cfseek fseek
11 #define cfread fread
12 #define cfclose fclose
13 #else
14 #include "cfile.h"
15 #endif
16
17 extern void PumpMessages(void);
18
19 hmp_file *hmp_open(const char *filename) {
20         int i;
21         char buf[256];
22         long data;
23         CFILE *fp;
24         hmp_file *hmp;
25         int num_tracks;
26         unsigned char *p;
27
28         if (!(fp = cfopen((char *)filename, "rb")))
29                 return NULL;
30
31         hmp = malloc(sizeof(hmp_file));
32         if (!hmp) {
33                 cfclose(fp);
34                 return NULL;
35         }
36
37         memset(hmp, 0, sizeof(*hmp));
38
39         if ((cfread(buf, 1, 8, fp) != 8) || (memcmp(buf, "HMIMIDIP", 8)))
40                 goto err;
41
42         if (cfseek(fp, 0x30, SEEK_SET))
43                 goto err;
44
45         if (cfread(&num_tracks, 4, 1, fp) != 1)
46                 goto err;
47
48         if ((num_tracks < 1) || (num_tracks > HMP_TRACKS))
49                 goto err;
50
51         hmp->num_trks = num_tracks;
52     hmp->tempo = 120;
53
54         if (cfseek(fp, 0x308, SEEK_SET))
55                 goto err;
56
57     for (i = 0; i < num_tracks; i++) {
58                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(&data, 4, 1, fp) != 1))
59                         goto err;
60
61                 data -= 12;
62
63 #if 0
64                 if (i == 0)  /* track 0: reserve length for tempo */
65                     data += sizeof(hmp_tempo);
66 #endif
67
68                 hmp->trks[i].len = data;
69
70                 if (!(p = hmp->trks[i].data = malloc(data)))
71                         goto err;
72
73 #if 0
74                 if (i == 0) { /* track 0: add tempo */
75                         memcpy(p, hmp_tempo, sizeof(hmp_tempo));
76                         p += sizeof(hmp_tempo);
77                         data -= sizeof(hmp_tempo);
78                 }
79 #endif
80                                              /* finally, read track data */
81                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(p, data, 1, fp) != 1))
82             goto err;
83    }
84    cfclose(fp);
85    return hmp;
86
87 err:
88    cfclose(fp);
89    hmp_close(hmp);
90    return NULL;
91 }
92
93 void hmp_stop(hmp_file *hmp) {
94         MIDIHDR *mhdr;
95         if (!hmp->stop) {
96                 hmp->stop = 1;
97                 PumpMessages();
98                 midiStreamStop(hmp->hmidi);
99                 while (hmp->bufs_in_mm)
100                  {
101                         PumpMessages();
102                         Sleep(0);
103                  }
104         }
105         while ((mhdr = hmp->evbuf)) {
106                 midiOutUnprepareHeader((HMIDIOUT)hmp->hmidi, mhdr, sizeof(MIDIHDR));
107                 hmp->evbuf = mhdr->lpNext;
108                 free(mhdr);
109         }
110         
111         if (hmp->hmidi) {
112                 midiStreamClose(hmp->hmidi);
113                 hmp->hmidi = NULL;
114         }
115 }
116
117 void hmp_close(hmp_file *hmp) {
118         int i;
119
120         hmp_stop(hmp);
121         for (i = 0; i < hmp->num_trks; i++)
122                 if (hmp->trks[i].data)
123                         free(hmp->trks[i].data);
124         free(hmp);
125 }
126
127 /*
128  * read a HMI type variabele length number
129  */
130 static int get_var_num_hmi(unsigned char *data, int datalen, unsigned long *value) {
131         unsigned char *p;
132         unsigned long v = 0;
133         int shift = 0;
134
135         p = data;
136         while ((datalen > 0) && !(*p & 0x80)) {
137                 v += *(p++) << shift;
138                 shift += 7;
139                 datalen --;
140     }
141         if (!datalen)
142                 return 0;
143     v += (*(p++) & 0x7f) << shift;
144         if (value) *value = v;
145     return p - data;
146 }
147
148 /*
149  * read a MIDI type variabele length number
150  */
151 static int get_var_num(unsigned char *data, int datalen,
152  unsigned long *value) {
153         unsigned char *orgdata = data;
154         unsigned long v = 0;
155
156         while ((datalen > 0) && (*data & 0x80))
157                 v = (v << 7) + (*(data++) & 0x7f);
158         if (!datalen)
159                 return 0;
160     v = (v << 7) + *(data++);
161     if (value) *value = v;
162     return data - orgdata;
163 }
164
165 static int get_event(hmp_file *hmp, event *ev) {
166     static int cmdlen[7]={3,3,3,3,2,2,3};
167         unsigned long got;
168         unsigned long mindelta, delta;
169         int i, ev_num;
170         hmp_track *trk, *fndtrk;
171
172         mindelta = INT_MAX;
173         fndtrk = NULL;
174         for (trk = hmp->trks, i = hmp->num_trks; (i--) > 0; trk++) {
175                 if (!trk->left)
176                         continue;
177                 if (!(got = get_var_num_hmi(trk->cur, trk->left, &delta)))
178                         return HMP_INVALID_FILE;
179                 if (trk->left > got + 2 && *(trk->cur + got) == 0xff
180                         && *(trk->cur + got + 1) == 0x2f) {/* end of track */
181                         trk->left = 0;
182                         continue;
183                 }
184         delta += trk->cur_time - hmp->cur_time;
185                 if (delta < mindelta) {
186                         mindelta = delta;
187                         fndtrk = trk;
188                 }
189         }
190         if (!(trk = fndtrk))
191                         return HMP_EOF;
192
193         got = get_var_num_hmi(trk->cur, trk->left, &delta);
194
195         trk->cur_time += delta;
196         ev->delta = trk->cur_time - hmp->cur_time;
197         hmp->cur_time = trk->cur_time;
198
199         if ((trk->left -= got) < 3)
200                         return HMP_INVALID_FILE;
201         trk->cur += got;
202         /*memset(ev, 0, sizeof(*ev));*/ev->datalen = 0;
203         ev->msg[0] = ev_num = *(trk->cur++);
204         trk->left--;
205         if (ev_num < 0x80)
206             return HMP_INVALID_FILE; /* invalid command */
207         if (ev_num < 0xf0) {
208                 ev->msg[1] = *(trk->cur++);
209                 trk->left--;
210                 if (cmdlen[((ev_num) >> 4) - 8] == 3) {
211                         ev->msg[2] = *(trk->cur++);
212                         trk->left--;
213                 }
214         } else if (ev_num == 0xff) {
215                 ev->msg[1] = *(trk->cur++);
216                 trk->left--;
217                 if (!(got = get_var_num(ev->data = trk->cur, 
218                         trk->left, (unsigned long *)&ev->datalen)))
219                         return HMP_INVALID_FILE;
220             trk->cur += ev->datalen;
221                 if (trk->left <= ev->datalen)
222                         return HMP_INVALID_FILE;
223                 trk->left -= ev->datalen;
224         } else /* sysex -> error */
225             return HMP_INVALID_FILE;
226         return 0;
227 }
228
229 static int fill_buffer(hmp_file *hmp) {
230         MIDIHDR *mhdr = hmp->evbuf;
231         unsigned int *p = (unsigned int *)(mhdr->lpData + mhdr->dwBytesRecorded);
232         unsigned int *pend = (unsigned int *)(mhdr->lpData + mhdr->dwBufferLength);
233         unsigned int i;
234         event ev;
235
236         while (p + 4 <= pend) {
237                 if (hmp->pending_size) {
238                         i = (p - pend) * 4;
239                         if (i > hmp->pending_size)
240                                 i = hmp->pending_size;
241                         *(p++) = hmp->pending_event | i;
242                         *(p++) = 0;
243                         memcpy((unsigned char *)p, hmp->pending, i);
244                         hmp->pending_size -= i;
245                         p += (i + 3) / 4;
246                 } else {
247                         if ((i = get_event(hmp, &ev))) {
248                                 mhdr->dwBytesRecorded = ((unsigned char *)p) - ((unsigned char *)mhdr->lpData);
249                                 return i;
250                         }
251                         if (ev.datalen) {
252                                 hmp->pending_size = ev.datalen;
253                                 hmp->pending = ev.data;
254                                 hmp->pending_event = ev.msg[0] << 24;
255                         } else {
256                                 *(p++) = ev.delta;
257                                 *(p++) = 0;
258                                 *(p++) = (((DWORD)MEVT_SHORTMSG) << 24) |
259                                         ((DWORD)ev.msg[0]) |
260                                         (((DWORD)ev.msg[1]) << 8) |
261                                         (((DWORD)ev.msg[2]) << 16);
262                         }
263                 }
264         }
265         mhdr->dwBytesRecorded = ((unsigned char *)p) - ((unsigned char *)mhdr->lpData);
266         return 0;
267 }
268
269 static int setup_buffers(hmp_file *hmp) {
270         int i;
271         MIDIHDR *buf, *lastbuf;
272
273         lastbuf = NULL;
274         for (i = 0; i < HMP_BUFFERS; i++) {
275                 if (!(buf = malloc(HMP_BUFSIZE + sizeof(MIDIHDR))))
276                         return HMP_OUT_OF_MEM;
277                 memset(buf, 0, sizeof(MIDIHDR));
278                 buf->lpData = (unsigned char *)buf + sizeof(MIDIHDR);
279                 buf->dwBufferLength = HMP_BUFSIZE;
280                 buf->dwUser = (DWORD)hmp;
281                 buf->lpNext = lastbuf;
282                 lastbuf = buf;
283         }
284         hmp->evbuf = lastbuf;
285         return 0;
286 }
287
288 static void reset_tracks(struct hmp_file *hmp) {
289         int i;
290
291         for (i = 0; i < hmp->num_trks; i++) {
292                 hmp->trks[i].cur = hmp->trks[i].data;
293                 hmp->trks[i].left = hmp->trks[i].len;
294         }
295 }
296
297 static void _stdcall midi_callback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) {
298         MIDIHDR *mhdr;
299         hmp_file *hmp;
300         int rc;
301
302         if (uMsg != MOM_DONE)
303                 return;
304         
305         mhdr = ((MIDIHDR *)dw1);
306         mhdr->dwBytesRecorded = 0;
307         hmp = (hmp_file *)(mhdr->dwUser);
308         mhdr->lpNext = hmp->evbuf;
309         hmp->evbuf = mhdr;
310         hmp->bufs_in_mm--;
311
312         if (!hmp->stop) {
313                 while (fill_buffer(hmp) == HMP_EOF)
314                         reset_tracks(hmp);
315                 if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf, 
316                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
317                         /* ??? */
318                 } else {
319                         hmp->evbuf = hmp->evbuf->lpNext;
320                         hmp->bufs_in_mm++;
321                 }
322         }
323
324 }
325
326 static void setup_tempo(hmp_file *hmp, unsigned long tempo) {
327         MIDIHDR *mhdr = hmp->evbuf;
328         unsigned int *p = (unsigned int *)(mhdr->lpData + mhdr->dwBytesRecorded);
329         *(p++) = 0;
330         *(p++) = 0;
331         *(p++) = (((DWORD)MEVT_TEMPO)<<24) | tempo;
332         mhdr->dwBytesRecorded += 12;
333 }
334
335 int hmp_play(hmp_file *hmp) {
336         int rc;
337         MIDIPROPTIMEDIV mptd;
338 #if 0
339         unsigned int    numdevs;
340         int i=0;
341
342         numdevs=midiOutGetNumDevs();
343         hmp->devid=-1;
344         do
345         {
346          MIDIOUTCAPS devcaps;
347          midiOutGetDevCaps(i,&devcaps,sizeof(MIDIOUTCAPS));
348          if ((devcaps.wTechnology==MOD_FMSYNTH) || (devcaps.wTechnology==MOD_SYNTH))
349              hmp->devid=i;
350          i++;
351         } while ((i<(int)numdevs) && (hmp->devid==-1));
352 #else
353         hmp->devid = MIDI_MAPPER;
354 #endif
355
356         if ((rc = setup_buffers(hmp)))
357                 return rc;
358         if ((midiStreamOpen(&hmp->hmidi, &hmp->devid,1,(DWORD)midi_callback,
359          0, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) {
360                 hmp->hmidi = NULL;
361                 return HMP_MM_ERR;
362         }
363         mptd.cbStruct  = sizeof(mptd);
364         mptd.dwTimeDiv = hmp->tempo;
365         if ((midiStreamProperty(hmp->hmidi,
366          (LPBYTE)&mptd,
367          MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR) {
368                 /* FIXME: cleanup... */
369                 return HMP_MM_ERR;
370         }
371
372         reset_tracks(hmp);
373         setup_tempo(hmp, 0x0f4240);
374
375         hmp->stop = 0;
376         while (hmp->evbuf) {
377                 if ((rc = fill_buffer(hmp))) {
378                         if (rc == HMP_EOF) {
379                                 reset_tracks(hmp);
380                                 continue;
381                         } else
382                                 return rc;
383                 }
384 #if 0
385                 {  FILE *f = fopen("dump","wb"); fwrite(hmp->evbuf->lpData, 
386  hmp->evbuf->dwBytesRecorded,1,f); fclose(f); exit(1);}
387 #endif
388                 if ((rc = midiOutPrepareHeader((HMIDIOUT)hmp->hmidi, hmp->evbuf, 
389                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
390                         /* FIXME: cleanup... */
391                         return HMP_MM_ERR;
392                 }
393                 if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf, 
394                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
395                         /* FIXME: cleanup... */
396                         return HMP_MM_ERR;
397                 }
398                 hmp->evbuf = hmp->evbuf->lpNext;
399                 hmp->bufs_in_mm++;
400         }
401         midiStreamRestart(hmp->hmidi);
402         return 0;
403 }
404