OSDN Git Service

fixed an issue of bakapi.exe while in pan mode you cannot escape until you press...
[proj16/16.git] / 16 / othersrc / midi.c
1 /* midi.c\r
2  *\r
3  * Adlib OPL2/OPL3 FM synthesizer chipset test program.\r
4  * Play MIDI file using the OPLx synthesizer (well, poorly anyway)\r
5  * (C) 2010-2012 Jonathan Campbell.\r
6  * Hackipedia DOS library.\r
7  *\r
8  * This code is licensed under the LGPL.\r
9  * <insert LGPL legal text here>\r
10  *\r
11  * Compiles for intended target environments:\r
12  *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]\r
13  */\r
14  \r
15 #include <stdio.h>\r
16 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */\r
17 #include <stdlib.h>\r
18 #include <string.h>\r
19 #include <unistd.h>\r
20 #include <malloc.h>\r
21 #include <ctype.h>\r
22 #include <fcntl.h>\r
23 #include <math.h>\r
24 #include <dos.h>\r
25 \r
26 #include "src/lib/doslib/vga.h"\r
27 #include "src/lib/doslib/dos.h"\r
28 #include "src/lib/doslib/8254.h"                /* 8254 timer */\r
29 #include "src/lib/doslib/8259.h"\r
30 #include "src/lib/doslib/vgagui.h"\r
31 #include "src/lib/doslib/vgatty.h"\r
32 #include "src/lib/doslib/adlib.h"\r
33 \r
34 /* one per OPL channel */\r
35 struct midi_note {\r
36         unsigned char           note_number;\r
37         unsigned char           note_velocity;\r
38         unsigned char           note_track;     /* from what MIDI track */\r
39         unsigned char           note_channel;   /* from what MIDI channel */\r
40         unsigned int            busy:1;         /* if occupied */\r
41 };\r
42 \r
43 struct midi_channel {\r
44         unsigned char           program;\r
45 };\r
46 \r
47 struct midi_track {\r
48         /* track data, raw */\r
49         unsigned char*          raw;            /* raw data base */\r
50         unsigned char*          fence;          /* raw data end (last byte + 1) */\r
51         unsigned char*          read;           /* raw data read ptr */\r
52         /* state */\r
53         unsigned long           us_per_quarter_note; /* Microseconds per quarter note (def 120 BPM) */\r
54         unsigned long           us_tick_cnt_mtpq; /* Microseconds advanced (up to 10000 us or one unit at 100Hz) x ticks per quarter note */\r
55         unsigned long           wait;\r
56         unsigned char           last_status;    /* MIDI last status byte */\r
57         unsigned int            eof:1;          /* we hit the end of the track */\r
58 };\r
59 \r
60 #define MIDI_MAX_CHANNELS       16\r
61 #define MIDI_MAX_TRACKS         64\r
62 \r
63 struct midi_note                midi_notes[ADLIB_FM_VOICES];\r
64 struct midi_channel             midi_ch[MIDI_MAX_CHANNELS];\r
65 struct midi_track               midi_trk[MIDI_MAX_TRACKS];\r
66 static unsigned int             midi_trk_count=0;\r
67 static volatile unsigned char   midi_playing=0;\r
68 \r
69 /* MIDI params. Nobody ever said it was a straightforward standard!\r
70  * NTS: These are for reading reference. Internally we convert everything to 100Hz time base. */\r
71 static unsigned int ticks_per_quarter_note=0;   /* "Ticks per beat" */\r
72 \r
73 static void (interrupt *old_irq0)();\r
74 static volatile unsigned long irq0_ticks=0;\r
75 static volatile unsigned int irq0_cnt=0,irq0_add=0,irq0_max=0;\r
76 \r
77 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
78 static inline unsigned long farptr2phys(unsigned char far *p) { /* take 16:16 pointer convert to physical memory address */\r
79         return ((unsigned long)FP_SEG(p) << 4UL) + ((unsigned long)FP_OFF(p));\r
80 }\r
81 #endif\r
82 \r
83 static inline unsigned char midi_trk_read(struct midi_track *t) {\r
84         unsigned char c;\r
85 \r
86         /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
87         if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {\r
88                 t->eof = 1;\r
89                 return 0xFF;\r
90         }\r
91 \r
92         c = *(t->read);\r
93 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
94         if (FP_OFF(t->read) >= 0xF) /* 16:16 far pointer aware (NTS: Programs reassigning this pointer MUST normalize the FAR pointer) */\r
95                 t->read = MK_FP(FP_SEG(t->read)+0x1,0);\r
96         else\r
97                 t->read++;\r
98 #else\r
99         t->read++;\r
100 #endif\r
101         return c;\r
102 }\r
103 \r
104 void midi_trk_end(struct midi_track *t) {\r
105         t->wait = ~0UL;\r
106         t->read = t->fence;\r
107 }\r
108 \r
109 void midi_trk_skip(struct midi_track *t,unsigned long len) {\r
110         unsigned long rem;\r
111 \r
112         /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
113         if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)\r
114                 return;\r
115 \r
116         if (len > 0xFFF0UL) {\r
117                 midi_trk_end(t);\r
118                 return;\r
119         }\r
120 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
121         {\r
122                 unsigned long tt;\r
123 \r
124                 tt = farptr2phys(t->read);\r
125                 rem = farptr2phys(t->fence) - tt;\r
126                 if (rem > len) rem = len;\r
127                 tt += rem;\r
128                 t->read = MK_FP(tt>>4,tt&0xF);\r
129         }\r
130 #else\r
131         rem = (unsigned long)(t->fence - t->read);\r
132         if (len > rem) len = rem;\r
133         t->read += len;\r
134 #endif\r
135 }\r
136 \r
137 static const uint32_t midikeys_freqs[0x80] = {\r
138         0x00082d01,     /* key 0 = 8.17579891564371Hz */\r
139         0x0008a976,     /* key 1 = 8.66195721802725Hz */\r
140         0x00092d51,     /* key 2 = 9.17702399741899Hz */\r
141         0x0009b904,     /* key 3 = 9.72271824131503Hz */\r
142         0x000a4d05,     /* key 4 = 10.3008611535272Hz */\r
143         0x000ae9d3,     /* key 5 = 10.9133822322814Hz */\r
144         0x000b8ff4,     /* key 6 = 11.5623257097386Hz */\r
145         0x000c3ff6,     /* key 7 = 12.2498573744297Hz */\r
146         0x000cfa70,     /* key 8 = 12.9782717993733Hz */\r
147         0x000dc000,     /* key 9 = 13.75Hz */\r
148         0x000e914f,     /* key 10 = 14.5676175474403Hz */\r
149         0x000f6f11,     /* key 11 = 15.4338531642539Hz */\r
150         0x00105a02,     /* key 12 = 16.3515978312874Hz */\r
151         0x001152ec,     /* key 13 = 17.3239144360545Hz */\r
152         0x00125aa2,     /* key 14 = 18.354047994838Hz */\r
153         0x00137208,     /* key 15 = 19.4454364826301Hz */\r
154         0x00149a0a,     /* key 16 = 20.6017223070544Hz */\r
155         0x0015d3a6,     /* key 17 = 21.8267644645627Hz */\r
156         0x00171fe9,     /* key 18 = 23.1246514194771Hz */\r
157         0x00187fed,     /* key 19 = 24.4997147488593Hz */\r
158         0x0019f4e0,     /* key 20 = 25.9565435987466Hz */\r
159         0x001b8000,     /* key 21 = 27.5Hz */\r
160         0x001d229e,     /* key 22 = 29.1352350948806Hz */\r
161         0x001ede22,     /* key 23 = 30.8677063285078Hz */\r
162         0x0020b404,     /* key 24 = 32.7031956625748Hz */\r
163         0x0022a5d8,     /* key 25 = 34.647828872109Hz */\r
164         0x0024b545,     /* key 26 = 36.7080959896759Hz */\r
165         0x0026e410,     /* key 27 = 38.8908729652601Hz */\r
166         0x00293414,     /* key 28 = 41.2034446141087Hz */\r
167         0x002ba74d,     /* key 29 = 43.6535289291255Hz */\r
168         0x002e3fd2,     /* key 30 = 46.2493028389543Hz */\r
169         0x0030ffda,     /* key 31 = 48.9994294977187Hz */\r
170         0x0033e9c0,     /* key 32 = 51.9130871974931Hz */\r
171         0x00370000,     /* key 33 = 55Hz */\r
172         0x003a453d,     /* key 34 = 58.2704701897612Hz */\r
173         0x003dbc44,     /* key 35 = 61.7354126570155Hz */\r
174         0x00416809,     /* key 36 = 65.4063913251497Hz */\r
175         0x00454bb0,     /* key 37 = 69.295657744218Hz */\r
176         0x00496a8b,     /* key 38 = 73.4161919793519Hz */\r
177         0x004dc820,     /* key 39 = 77.7817459305202Hz */\r
178         0x00526829,     /* key 40 = 82.4068892282175Hz */\r
179         0x00574e9b,     /* key 41 = 87.307057858251Hz */\r
180         0x005c7fa4,     /* key 42 = 92.4986056779086Hz */\r
181         0x0061ffb5,     /* key 43 = 97.9988589954373Hz */\r
182         0x0067d380,     /* key 44 = 103.826174394986Hz */\r
183         0x006e0000,     /* key 45 = 110Hz */\r
184         0x00748a7b,     /* key 46 = 116.540940379522Hz */\r
185         0x007b7888,     /* key 47 = 123.470825314031Hz */\r
186         0x0082d012,     /* key 48 = 130.812782650299Hz */\r
187         0x008a9760,     /* key 49 = 138.591315488436Hz */\r
188         0x0092d517,     /* key 50 = 146.832383958704Hz */\r
189         0x009b9041,     /* key 51 = 155.56349186104Hz */\r
190         0x00a4d053,     /* key 52 = 164.813778456435Hz */\r
191         0x00ae9d36,     /* key 53 = 174.614115716502Hz */\r
192         0x00b8ff49,     /* key 54 = 184.997211355817Hz */\r
193         0x00c3ff6a,     /* key 55 = 195.997717990875Hz */\r
194         0x00cfa700,     /* key 56 = 207.652348789973Hz */\r
195         0x00dc0000,     /* key 57 = 220Hz */\r
196         0x00e914f6,     /* key 58 = 233.081880759045Hz */\r
197         0x00f6f110,     /* key 59 = 246.941650628062Hz */\r
198         0x0105a025,     /* key 60 = 261.625565300599Hz */\r
199         0x01152ec0,     /* key 61 = 277.182630976872Hz */\r
200         0x0125aa2e,     /* key 62 = 293.664767917408Hz */\r
201         0x01372082,     /* key 63 = 311.126983722081Hz */\r
202         0x0149a0a7,     /* key 64 = 329.62755691287Hz */\r
203         0x015d3a6d,     /* key 65 = 349.228231433004Hz */\r
204         0x0171fe92,     /* key 66 = 369.994422711634Hz */\r
205         0x0187fed4,     /* key 67 = 391.995435981749Hz */\r
206         0x019f4e00,     /* key 68 = 415.304697579945Hz */\r
207         0x01b80000,     /* key 69 = 440Hz */\r
208         0x01d229ec,     /* key 70 = 466.16376151809Hz */\r
209         0x01ede220,     /* key 71 = 493.883301256124Hz */\r
210         0x020b404a,     /* key 72 = 523.251130601197Hz */\r
211         0x022a5d81,     /* key 73 = 554.365261953744Hz */\r
212         0x024b545c,     /* key 74 = 587.329535834815Hz */\r
213         0x026e4104,     /* key 75 = 622.253967444162Hz */\r
214         0x0293414f,     /* key 76 = 659.25511382574Hz */\r
215         0x02ba74da,     /* key 77 = 698.456462866008Hz */\r
216         0x02e3fd24,     /* key 78 = 739.988845423269Hz */\r
217         0x030ffda9,     /* key 79 = 783.990871963499Hz */\r
218         0x033e9c01,     /* key 80 = 830.60939515989Hz */\r
219         0x03700000,     /* key 81 = 880Hz */\r
220         0x03a453d8,     /* key 82 = 932.32752303618Hz */\r
221         0x03dbc440,     /* key 83 = 987.766602512248Hz */\r
222         0x04168094,     /* key 84 = 1046.50226120239Hz */\r
223         0x0454bb03,     /* key 85 = 1108.73052390749Hz */\r
224         0x0496a8b8,     /* key 86 = 1174.65907166963Hz */\r
225         0x04dc8208,     /* key 87 = 1244.50793488832Hz */\r
226         0x0526829e,     /* key 88 = 1318.51022765148Hz */\r
227         0x0574e9b5,     /* key 89 = 1396.91292573202Hz */\r
228         0x05c7fa49,     /* key 90 = 1479.97769084654Hz */\r
229         0x061ffb53,     /* key 91 = 1567.981743927Hz */\r
230         0x067d3802,     /* key 92 = 1661.21879031978Hz */\r
231         0x06e00000,     /* key 93 = 1760Hz */\r
232         0x0748a7b1,     /* key 94 = 1864.65504607236Hz */\r
233         0x07b78880,     /* key 95 = 1975.5332050245Hz */\r
234         0x082d0128,     /* key 96 = 2093.00452240479Hz */\r
235         0x08a97607,     /* key 97 = 2217.46104781498Hz */\r
236         0x092d5171,     /* key 98 = 2349.31814333926Hz */\r
237         0x09b90410,     /* key 99 = 2489.01586977665Hz */\r
238         0x0a4d053c,     /* key 100 = 2637.02045530296Hz */\r
239         0x0ae9d36b,     /* key 101 = 2793.82585146403Hz */\r
240         0x0b8ff493,     /* key 102 = 2959.95538169308Hz */\r
241         0x0c3ff6a7,     /* key 103 = 3135.96348785399Hz */\r
242         0x0cfa7005,     /* key 104 = 3322.43758063956Hz */\r
243         0x0dc00000,     /* key 105 = 3520Hz */\r
244         0x0e914f62,     /* key 106 = 3729.31009214472Hz */\r
245         0x0f6f1100,     /* key 107 = 3951.06641004899Hz */\r
246         0x105a0250,     /* key 108 = 4186.00904480958Hz */\r
247         0x1152ec0e,     /* key 109 = 4434.92209562995Hz */\r
248         0x125aa2e3,     /* key 110 = 4698.63628667852Hz */\r
249         0x13720820,     /* key 111 = 4978.03173955329Hz */\r
250         0x149a0a79,     /* key 112 = 5274.04091060592Hz */\r
251         0x15d3a6d6,     /* key 113 = 5587.65170292806Hz */\r
252         0x171fe927,     /* key 114 = 5919.91076338615Hz */\r
253         0x187fed4e,     /* key 115 = 6271.92697570799Hz */\r
254         0x19f4e00a,     /* key 116 = 6644.87516127912Hz */\r
255         0x1b800000,     /* key 117 = 7040Hz */\r
256         0x1d229ec4,     /* key 118 = 7458.62018428944Hz */\r
257         0x1ede2200,     /* key 119 = 7902.13282009799Hz */\r
258         0x20b404a1,     /* key 120 = 8372.01808961916Hz */\r
259         0x22a5d81c,     /* key 121 = 8869.84419125991Hz */\r
260         0x24b545c7,     /* key 122 = 9397.27257335704Hz */\r
261         0x26e41040,     /* key 123 = 9956.06347910659Hz */\r
262         0x293414f2,     /* key 124 = 10548.0818212118Hz */\r
263         0x2ba74dac,     /* key 125 = 11175.3034058561Hz */\r
264         0x2e3fd24f,     /* key 126 = 11839.8215267723Hz */\r
265         0x30ffda9c      /* key 127 = 12543.853951416Hz */\r
266 };\r
267 \r
268 static uint32_t midi_note_freq(struct midi_channel *ch,unsigned char key) {\r
269         return midikeys_freqs[key&0x7F];\r
270 }\r
271 \r
272 static struct midi_note *get_fm_note(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char do_alloc) {\r
273         unsigned int tch = (unsigned int)(t - midi_trk); /* pointer math */\r
274         unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */\r
275         unsigned int i,freen=~0;\r
276 \r
277         for (i=0;i < ADLIB_FM_VOICES;i++) {\r
278                 if (midi_notes[i].busy) {\r
279                         if (midi_notes[i].note_channel == ach && midi_notes[i].note_track == tch && midi_notes[i].note_number == key)\r
280                                 return &midi_notes[i];\r
281                 }\r
282                 else {\r
283                         if (freen == ~0) freen = i;\r
284                 }\r
285         }\r
286 \r
287         if (do_alloc && freen != ~0) return &midi_notes[freen];\r
288         return NULL;\r
289 }\r
290 \r
291 static void drop_fm_note(struct midi_channel *ch,unsigned char key) {\r
292         unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */\r
293         unsigned int i;\r
294 \r
295         for (i=0;i < ADLIB_FM_VOICES;i++) {\r
296                 if (midi_notes[i].busy && midi_notes[i].note_channel == ach) {\r
297                         midi_notes[i].busy = 0;\r
298                         break;\r
299                 }\r
300         }\r
301 }\r
302 \r
303 static inline void on_key_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
304         struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);\r
305         uint32_t freq = midi_note_freq(ch,key);\r
306         unsigned int ach;\r
307 \r
308         if (note == NULL) return;\r
309 \r
310         note->busy = 1;\r
311         note->note_number = key;\r
312         note->note_velocity = vel;\r
313         note->note_track = (unsigned int)(t - midi_trk);\r
314         note->note_channel = (unsigned int)(ch - midi_ch);\r
315         ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
316         adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
317         adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
318         adlib_fm[ach].mod.sustain_level = vel >> 3;\r
319         adlib_fm[ach].mod.key_on = 1;\r
320         adlib_update_groupA0(ach,&adlib_fm[ach]);\r
321 }\r
322 \r
323 static inline void on_key_on(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
324         struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/1);\r
325         uint32_t freq = midi_note_freq(ch,key);\r
326         unsigned int ach;\r
327 \r
328         /* HACK: Ignore percussion */\r
329         if ((ch->program >= 8 && ch->program <= 15)/*Chromatic percussion*/ ||\r
330                 (ch->program >= 112 && ch->program <= 119)/*Percussive*/ ||\r
331                 ch == &midi_ch[9]/*MIDI channel 10 (DAMN YOU 1-BASED COUNTING)*/)\r
332                 return;\r
333 \r
334         if (note == NULL) {\r
335                 /* then we'll have to knock one off to make room */\r
336                 drop_fm_note(ch,key);\r
337                 note = get_fm_note(t,ch,key,1);\r
338                 if (note == NULL) return;\r
339         }\r
340 \r
341         note->busy = 1;\r
342         note->note_number = key;\r
343         note->note_velocity = vel;\r
344         note->note_track = (unsigned int)(t - midi_trk);\r
345         note->note_channel = (unsigned int)(ch - midi_ch);\r
346         ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
347         adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
348         adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
349         adlib_fm[ach].mod.sustain_level = vel >> 3;\r
350         adlib_fm[ach].mod.key_on = 1;\r
351         adlib_update_groupA0(ach,&adlib_fm[ach]);\r
352 }\r
353 \r
354 static inline void on_key_off(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
355         struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);\r
356         uint32_t freq = midi_note_freq(ch,key);\r
357         unsigned int ach;\r
358 \r
359         if (note == NULL) return;\r
360 \r
361         note->busy = 0;\r
362         ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
363         adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
364         adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
365         adlib_fm[ach].mod.sustain_level = vel >> 3;\r
366         adlib_fm[ach].mod.key_on = 0;\r
367         adlib_update_groupA0(ach,&adlib_fm[ach]);\r
368 }\r
369 \r
370 static inline void on_control_change(struct midi_track *t,struct midi_channel *ch,unsigned char num,unsigned char val) {\r
371 }\r
372 \r
373 static inline void on_program_change(struct midi_track *t,struct midi_channel *ch,unsigned char inst) {\r
374         ch->program = inst;\r
375 }\r
376 \r
377 static inline void on_channel_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char velocity) {\r
378 }\r
379 \r
380 static inline void on_pitch_bend(struct midi_track *t,struct midi_channel *ch,int bend/*-8192 to 8192*/) {\r
381 }\r
382 \r
383 unsigned long midi_trk_read_delta(struct midi_track *t) {\r
384         unsigned long tc = 0;\r
385         unsigned char c = 0,b;\r
386 \r
387         /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
388         if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)\r
389                 return tc;\r
390 \r
391         while (c < 4) {\r
392                 b = midi_trk_read(t);\r
393                 tc = (tc << 7UL) + (unsigned long)(b&0x7F);\r
394                 if (!(b&0x80)) break;\r
395                 c++;\r
396         }\r
397 \r
398         return tc;\r
399 }\r
400 \r
401 void midi_tick_track(unsigned int i) {\r
402         struct midi_track *t = midi_trk + i;\r
403         struct midi_channel *ch;\r
404         unsigned char b,c,d;\r
405         int cnt=0;\r
406 \r
407         /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
408         if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {\r
409                 t->eof = 1;\r
410                 return;\r
411         }\r
412 \r
413         t->us_tick_cnt_mtpq += 10000UL * (unsigned long)ticks_per_quarter_note;\r
414         while (t->us_tick_cnt_mtpq >= t->us_per_quarter_note) {\r
415                 t->us_tick_cnt_mtpq -= t->us_per_quarter_note;\r
416                 cnt++;\r
417 \r
418                 while (t->wait == 0) {\r
419                         if ((unsigned long)t->read >= (unsigned long)t->fence) {\r
420                                 t->eof = 1;\r
421                                 break;\r
422                         }\r
423 \r
424                         /* read pointer should be pointing at MIDI event bytes, just after the time delay */\r
425                         b = midi_trk_read(t);\r
426                         if (b&0x80) {\r
427                                 if (b < 0xF8) {\r
428                                         if (b >= 0xF0)\r
429                                                 t->last_status = 0;\r
430                                         else\r
431                                                 t->last_status = b;\r
432                                 }\r
433                                 if (b != 0x00 && ((b&0xF8) != 0xF0))\r
434                                         c = midi_trk_read(t);\r
435                         }\r
436                         else {\r
437                                 /* blegh. last status */\r
438                                 c = b;\r
439                                 b = t->last_status;\r
440                         }\r
441                         switch (b>>4) {\r
442                                 case 0x8: { /* note off */\r
443                                         d = midi_trk_read(t);\r
444                                         ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
445                                         on_key_off(t,ch,c,d);\r
446                                         } break;\r
447                                 case 0x9: { /* note on */\r
448                                         d = midi_trk_read(t);\r
449                                         ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
450                                         if (d != 0) on_key_on(t,ch,c,d); /* "A Note On with a velocity of 0 is actually a note off" Bleh, really? */\r
451                                         else on_key_off(t,ch,c,d);\r
452                                         } break;\r
453                                 case 0xA: { /* polyphonic aftertouch */\r
454                                         d = midi_trk_read(t);\r
455                                         ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
456                                         on_key_aftertouch(t,ch,c,d);\r
457                                         } break;\r
458                                 case 0xB: { /* control change */\r
459                                         d = midi_trk_read(t);\r
460                                         ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
461                                         on_control_change(t,ch,c,d);\r
462                                         } break;\r
463                                 case 0xC: { /* program change */\r
464                                         on_program_change(t,ch,c); /* c=instrument d=not used */\r
465                                         } break;\r
466                                 case 0xD: { /* channel aftertouch */\r
467                                         on_channel_aftertouch(t,ch,c); /* c=velocity d=not used */\r
468                                         } break;\r
469                                 case 0xE: { /* pitch bend */\r
470                                         d = midi_trk_read(t);\r
471                                         on_pitch_bend(t,ch,((c&0x7F)|((d&0x7F)<<7))-8192); /* c=LSB d=MSB */\r
472                                         } break;\r
473                                 case 0xF: { /* event */\r
474                                         if (b == 0xFF) {\r
475                                                 if (c == 0x7F) { /* c=type d=len */\r
476                                                         unsigned long len = midi_trk_read_delta(t);\r
477 //                                                      fprintf(stderr,"Type 0x7F len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\r
478                                                         if (len < 512UL) {\r
479                                                                 /* unknown */\r
480                                                                 midi_trk_skip(t,len);\r
481                                                         }\r
482                                                         else {\r
483                                                                 midi_trk_end(t);\r
484                                                         }\r
485                                                 }\r
486                                                 else if (c < 0x7F) {\r
487                                                         d = midi_trk_read(t);\r
488 \r
489                                                         if (c == 0x51 && d >= 3) {\r
490                                                                 d -= 3;\r
491                                                                 t->us_per_quarter_note = ((unsigned long)midi_trk_read(t)<<16UL)+\r
492                                                                         ((unsigned long)midi_trk_read(t)<<8UL)+\r
493                                                                         ((unsigned long)midi_trk_read(t)<<0UL);\r
494 \r
495                                                                 if (1/*TODO: If format 0 or format 1*/) {\r
496                                                                         /* Ugh. Unless format 2, the tempo applies to all tracks */\r
497                                                                         int j;\r
498 \r
499                                                                         for (j=0;j < midi_trk_count;j++) {\r
500                                                                                 if (j != i) midi_trk[j].us_per_quarter_note =\r
501                                                                                         t->us_per_quarter_note;\r
502                                                                         }\r
503                                                                 }\r
504                                                         }\r
505                                                         else {\r
506 //                                                              fprintf(stderr,"Type 0x%02x len=%lu %p/%p/%p\n",c,d,t->raw,t->read,t->fence);\r
507                                                         }\r
508 \r
509                                                         midi_trk_skip(t,d);\r
510                                                 }\r
511                                                 else {\r
512                                                         fprintf(stderr,"t=%u Unknown MIDI f message 0x%02x 0x%02x %p/%p/%p\n",i,b,c,t->raw,t->read,t->fence);\r
513                                                 }\r
514                                         }\r
515                                         else {\r
516                                                 unsigned long len = midi_trk_read_delta(t);\r
517 //                                              fprintf(stderr,"Sysex len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\r
518                                                 midi_trk_skip(t,len);\r
519                                         }\r
520                                         } break;\r
521                                 default:\r
522                                         if (b != 0x00) {\r
523                                                 fprintf(stderr,"t=%u Unknown MIDI message 0x%02x at %p/%p/%p\n",i,b,t->raw,t->read,t->fence);\r
524                                                 midi_trk_end(t);\r
525                                         }\r
526                                         break;\r
527                         };\r
528 \r
529                         /* and then read the next event */\r
530                         t->wait = midi_trk_read_delta(t);\r
531                 }\r
532                 if (t->wait != 0) {\r
533                         t->wait--;\r
534                 }\r
535         }\r
536 }\r
537 \r
538 void adlib_shut_up();\r
539 void midi_reset_tracks();\r
540 void midi_reset_channels();\r
541 \r
542 void midi_tick() {\r
543         if (midi_playing) {\r
544                 unsigned int i;\r
545                 int eof=0;\r
546 \r
547                 for (i=0;i < midi_trk_count;i++) {\r
548                         midi_tick_track(i);\r
549                         eof += midi_trk[i].eof?1:0;\r
550                 }\r
551 \r
552                 if (eof >= midi_trk_count) {\r
553                         adlib_shut_up();\r
554                         midi_reset_tracks();\r
555                         midi_reset_channels();\r
556                 }\r
557         }\r
558 }\r
559 \r
560 /* WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models */\r
561 void interrupt irq0() {\r
562 //      midi_tick();\r
563         irq0_ticks++;\r
564         if ((irq0_cnt += irq0_add) >= irq0_max) {\r
565                 irq0_cnt -= irq0_max;\r
566                 old_irq0();\r
567         }\r
568         else {\r
569                 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);\r
570         }\r
571 }\r
572 \r
573 void adlib_shut_up() {\r
574         int i;\r
575 \r
576         memset(adlib_fm,0,sizeof(adlib_fm));\r
577         memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));\r
578         for (i=0;i < adlib_fm_voices;i++) {\r
579                 struct adlib_fm_operator *f;\r
580                 f = &adlib_fm[i].mod;\r
581                 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
582                 f = &adlib_fm[i].car;\r
583                 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
584         }\r
585 \r
586         for (i=0;i < adlib_fm_voices;i++) {\r
587                 struct adlib_fm_operator *f;\r
588 \r
589                 midi_notes[i].busy = 0;\r
590                 midi_notes[i].note_channel = 0;\r
591 \r
592                 f = &adlib_fm[i].mod;\r
593                 f->mod_multiple = 1;\r
594                 f->total_level = 63 - 16;\r
595                 f->attack_rate = 15;\r
596                 f->decay_rate = 4;\r
597                 f->sustain_level = 0;\r
598                 f->release_rate = 8;\r
599                 f->f_number = 400;\r
600                 f->sustain = 1;\r
601                 f->octave = 4;\r
602                 f->key_on = 0;\r
603 \r
604                 f = &adlib_fm[i].car;\r
605                 f->mod_multiple = 1;\r
606                 f->total_level = 63 - 16;\r
607                 f->attack_rate = 15;\r
608                 f->decay_rate = 4;\r
609                 f->sustain_level = 0;\r
610                 f->release_rate = 8;\r
611                 f->f_number = 0;\r
612                 f->sustain = 1;\r
613                 f->octave = 0;\r
614                 f->key_on = 0;\r
615         }\r
616 \r
617         adlib_apply_all();\r
618 }\r
619 \r
620 void midi_reset_track(unsigned int i) {\r
621         struct midi_track *t;\r
622 \r
623         if (i >= MIDI_MAX_TRACKS) return;\r
624         t = &midi_trk[i];\r
625         t->eof = 0;\r
626         t->last_status = 0;\r
627         t->us_tick_cnt_mtpq = 0;\r
628         t->us_per_quarter_note = (60000000UL / 120UL); /* 120BPM */\r
629         t->read = midi_trk[i].raw;\r
630         t->wait = midi_trk_read_delta(t); /* and then the read pointer will point at the MIDI event when wait counts down */\r
631 }\r
632 \r
633 void midi_reset_tracks() {\r
634         int i;\r
635 \r
636         for (i=0;i < midi_trk_count;i++)\r
637                 midi_reset_track(i);\r
638 }\r
639 \r
640 void midi_reset_channels() {\r
641         int i;\r
642 \r
643         for (i=0;i < MIDI_MAX_CHANNELS;i++) {\r
644                 midi_ch[i].program = 0;\r
645         }\r
646 }\r
647 \r
648 int load_midi_file(const char *path) {\r
649         unsigned char tmp[256];\r
650         unsigned int tracks=0;\r
651         unsigned int tracki=0;\r
652         int fd;\r
653 \r
654         fd = open(path,O_RDONLY|O_BINARY);\r
655         if (fd < 0) {\r
656                 printf("Failed to load file %s\n",path);\r
657                 return 0;\r
658         }\r
659 \r
660         ticks_per_quarter_note = 0;\r
661         while (read(fd,tmp,8) == 8) {\r
662                 uint32_t sz;\r
663 \r
664                 sz =    ((uint32_t)tmp[4] << (uint32_t)24) |\r
665                         ((uint32_t)tmp[5] << (uint32_t)16) |\r
666                         ((uint32_t)tmp[6] << (uint32_t)8) |\r
667                         ((uint32_t)tmp[7] << (uint32_t)0);\r
668                 if (!memcmp(tmp,"MThd",4)) {\r
669                         unsigned short t,tdiv;\r
670 \r
671                         if (sz < 6 || sz > 255) {\r
672                                 fprintf(stderr,"Invalid MThd size %lu\n",(unsigned long)sz);\r
673                                 goto err;\r
674                         }\r
675                         if (read(fd,tmp,(int)sz) != (int)sz) {\r
676                                 fprintf(stderr,"MThd read error\n");\r
677                                 goto err;\r
678                         }\r
679 \r
680                         /* byte 0-1 = format type (0,1 or 2) */\r
681                         /* byte 2-3 = number of tracks */\r
682                         /* byte 4-5 = time divison */\r
683                         t = tmp[1] | (tmp[0] << 8);\r
684                         if (t > 1) {\r
685                                 fprintf(stderr,"MThd type %u not supported\n",t);\r
686                                 goto err; /* we only take type 0 or 1, don't support 2 */\r
687                         }\r
688                         tracks = tmp[3] | (tmp[2] << 8);\r
689                         if (tracks > MIDI_MAX_TRACKS) {\r
690                                 fprintf(stderr,"MThd too many (%u) tracks\n",tracks);\r
691                                 goto err;\r
692                         }\r
693                         tdiv = tmp[5] | (tmp[4] << 8);\r
694                         if (tdiv & 0x8000) {\r
695                                 fprintf(stderr,"MThd SMPTE time division not supported\n");\r
696                                 goto err; /* we do not support the SMPTE form */\r
697                         }\r
698                         if (tdiv == 0) {\r
699                                 fprintf(stderr,"MThd time division == 0\n");\r
700                                 goto err;\r
701                         }\r
702                         ticks_per_quarter_note = tdiv;\r
703                 }\r
704                 else if (!memcmp(tmp,"MTrk",4)) {\r
705                         if (sz == 0UL) continue;\r
706 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
707                         if (sz > (640UL << 10UL)) goto err; /* 640KB */\r
708 #elif TARGET_MSDOS == 32\r
709                         if (sz > (1UL << 20UL)) goto err; /* 1MB */\r
710 #else\r
711                         if (sz > (60UL << 10UL)) goto err; /* 60KB */\r
712 #endif\r
713                         if (tracki >= MIDI_MAX_TRACKS) goto err;\r
714 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
715                         {\r
716                                 unsigned segv;\r
717 \r
718                                 /* NTS: _fmalloc() is still limited to 64KB sizes */\r
719                                 if (_dos_allocmem((unsigned)((sz+15UL)>>4UL),&segv) != 0) goto err;\r
720                                 midi_trk[tracki].raw = MK_FP(segv,0);\r
721                         }\r
722 #else\r
723                         midi_trk[tracki].raw = malloc(sz);\r
724 #endif\r
725                         if (midi_trk[tracki].raw == NULL) goto err;\r
726                         midi_trk[tracki].read = midi_trk[tracki].raw;\r
727 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
728                         {\r
729                                 unsigned char far *p = midi_trk[tracki].raw;\r
730                                 unsigned long rem = (unsigned long)sz;\r
731                                 unsigned long cando;\r
732                                 unsigned read;\r
733 \r
734                                 while (rem != 0UL) {\r
735                                         read = 0;\r
736 \r
737                                         cando = 0x10000UL - (unsigned long)FP_OFF(p);\r
738                                         if (cando > rem) cando = rem;\r
739                                         if (cando > 0xFFFFUL) cando = 0xFFFFUL; /* we're limited to 64KB-1 of reading */\r
740 \r
741                                         if (_dos_read(fd,p,(unsigned)cando,&read) != 0) goto err;\r
742                                         if (read != (unsigned)cando) goto err;\r
743 \r
744                                         rem -= cando;\r
745                                         if ((((unsigned long)FP_OFF(p))+cando) == 0x10000UL)\r
746                                                 p = MK_FP(FP_SEG(p)+0x1000,0);\r
747                                         else\r
748                                                 p += (unsigned)cando;\r
749                                 }\r
750 \r
751                                 cando = farptr2phys(p);\r
752                                 midi_trk[tracki].fence = MK_FP(cando>>4,cando&0xF);\r
753                         }\r
754 #else\r
755                         midi_trk[tracki].fence = midi_trk[tracki].raw + (unsigned)sz;\r
756                         if (read(fd,midi_trk[tracki].raw,(unsigned)sz) != (int)sz) goto err;\r
757 #endif\r
758                         tracki++;\r
759                 }\r
760                 else {\r
761                         fprintf(stderr,"Unknown MIDI chunk %c%c%c%c\n",tmp[0],tmp[1],tmp[2],tmp[3]);\r
762                         goto err;\r
763                 }\r
764         }\r
765         if (tracki == 0 || ticks_per_quarter_note == 0) goto err;\r
766         midi_trk_count = tracki;\r
767 \r
768         fprintf(stderr,"Ticks per quarter note: %u\n",ticks_per_quarter_note);\r
769 \r
770         close(fd);\r
771         return 1;\r
772 err:\r
773         close(fd);\r
774         return 0;\r
775 }\r
776 \r
777 int main(int argc,char **argv) {\r
778         unsigned long ptick;\r
779         int i,c;\r
780 \r
781         printf("ADLIB FM test program\n");\r
782         if (argc < 2) {\r
783                 printf("You must specify a MIDI file to play\n");\r
784                 return 1;\r
785         }\r
786 \r
787         if (!probe_vga()) {\r
788                 printf("Cannot init VGA\n");\r
789                 return 1;\r
790         }\r
791         if (!init_adlib()) {\r
792                 printf("Cannot init library\n");\r
793                 return 1;\r
794         }\r
795         if (!probe_8254()) { /* we need the timer to keep time with the music */\r
796                 printf("8254 timer not found\n");\r
797                 return 1;\r
798         }\r
799 \r
800         for (i=0;i < MIDI_MAX_TRACKS;i++) {\r
801                 midi_trk[i].raw = NULL;\r
802                 midi_trk[i].read = NULL;\r
803                 midi_trk[i].fence = NULL;\r
804         }\r
805 \r
806         if (load_midi_file(argv[1]) == 0) {\r
807                 printf("Failed to load MIDI\n");\r
808                 return 1;\r
809         }\r
810 \r
811         write_8254_system_timer(T8254_REF_CLOCK_HZ / 100); /* tick faster at 100Hz please */\r
812         irq0_cnt = 0;\r
813         irq0_add = 182;\r
814         irq0_max = 1000; /* about 18.2Hz */\r
815         old_irq0 = _dos_getvect(8);/*IRQ0*/\r
816         _dos_setvect(8,irq0);\r
817 \r
818         adlib_shut_up();\r
819         midi_reset_channels();\r
820         midi_reset_tracks();\r
821         _cli();\r
822         irq0_ticks = ptick = 0;\r
823         _sti();\r
824         midi_playing = 1;\r
825 \r
826         while (1) {\r
827                 unsigned long adv;\r
828 \r
829                 _cli();\r
830                 adv = irq0_ticks - ptick;\r
831                 if (adv >= 100UL) adv = 100UL;\r
832                 ptick = irq0_ticks;\r
833                 _sti();\r
834 \r
835                 while (adv != 0) {\r
836                         midi_tick();\r
837                         adv--;\r
838                 }\r
839 \r
840                 if (kbhit()) {\r
841                         c = getch();\r
842                         if (c == 0) c = getch() << 8;\r
843 \r
844                         if (c == 27) {\r
845                                 break;\r
846                         }\r
847                 }\r
848         }\r
849 \r
850         midi_playing = 0;\r
851         adlib_shut_up();\r
852         shutdown_adlib();\r
853         _dos_setvect(8,old_irq0);\r
854         write_8254_system_timer(0); /* back to normal 18.2Hz */\r
855 \r
856         for (i=0;i < MIDI_MAX_TRACKS;i++) {\r
857                 if (midi_trk[i].raw) {\r
858 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
859                         _dos_freemem(FP_SEG(midi_trk[i].raw)); /* NTS: Because we allocated with _dos_allocmem */\r
860 #else\r
861                         free(midi_trk[i].raw);\r
862 #endif\r
863                         midi_trk[i].raw = NULL;\r
864                 }\r
865                 midi_trk[i].fence = NULL;\r
866                 midi_trk[i].read = NULL;\r
867         }\r
868 \r
869         return 0;\r
870 }\r