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
8 * This code is licensed under the LGPL.
\r
9 * <insert LGPL legal text here>
\r
11 * Compiles for intended target environments:
\r
12 * - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
\r
16 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
\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
34 /* one per OPL channel */
\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
43 struct midi_channel {
\r
44 unsigned char program;
\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
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
56 unsigned char last_status; /* MIDI last status byte */
\r
57 unsigned int eof:1; /* we hit the end of the track */
\r
60 #define MIDI_MAX_CHANNELS 16
\r
61 #define MIDI_MAX_TRACKS 64
\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
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
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
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
83 static inline unsigned char midi_trk_read(struct midi_track *t) {
\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
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
104 void midi_trk_end(struct midi_track *t) {
\r
106 t->read = t->fence;
\r
109 void midi_trk_skip(struct midi_track *t,unsigned long len) {
\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
116 if (len > 0xFFF0UL) {
\r
120 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
\r
124 tt = farptr2phys(t->read);
\r
125 rem = farptr2phys(t->fence) - tt;
\r
126 if (rem > len) rem = len;
\r
128 t->read = MK_FP(tt>>4,tt&0xF);
\r
131 rem = (unsigned long)(t->fence - t->read);
\r
132 if (len > rem) len = rem;
\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
268 static uint32_t midi_note_freq(struct midi_channel *ch,unsigned char key) {
\r
269 return midikeys_freqs[key&0x7F];
\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
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
283 if (freen == ~0) freen = i;
\r
287 if (do_alloc && freen != ~0) return &midi_notes[freen];
\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
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
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
308 if (note == NULL) return;
\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
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
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
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
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
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
359 if (note == NULL) return;
\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
370 static inline void on_control_change(struct midi_track *t,struct midi_channel *ch,unsigned char num,unsigned char val) {
\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
377 static inline void on_channel_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char velocity) {
\r
380 static inline void on_pitch_bend(struct midi_track *t,struct midi_channel *ch,int bend/*-8192 to 8192*/) {
\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
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
392 b = midi_trk_read(t);
\r
393 tc = (tc << 7UL) + (unsigned long)(b&0x7F);
\r
394 if (!(b&0x80)) break;
\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
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
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
418 while (t->wait == 0) {
\r
419 if ((unsigned long)t->read >= (unsigned long)t->fence) {
\r
424 /* read pointer should be pointing at MIDI event bytes, just after the time delay */
\r
425 b = midi_trk_read(t);
\r
429 t->last_status = 0;
\r
431 t->last_status = b;
\r
433 if (b != 0x00 && ((b&0xF8) != 0xF0))
\r
434 c = midi_trk_read(t);
\r
437 /* blegh. last status */
\r
439 b = t->last_status;
\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
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
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
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
463 case 0xC: { /* program change */
\r
464 on_program_change(t,ch,c); /* c=instrument d=not used */
\r
466 case 0xD: { /* channel aftertouch */
\r
467 on_channel_aftertouch(t,ch,c); /* c=velocity d=not used */
\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
473 case 0xF: { /* event */
\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
480 midi_trk_skip(t,len);
\r
486 else if (c < 0x7F) {
\r
487 d = midi_trk_read(t);
\r
489 if (c == 0x51 && 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
495 if (1/*TODO: If format 0 or format 1*/) {
\r
496 /* Ugh. Unless format 2, the tempo applies to all tracks */
\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
506 // fprintf(stderr,"Type 0x%02x len=%lu %p/%p/%p\n",c,d,t->raw,t->read,t->fence);
\r
509 midi_trk_skip(t,d);
\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
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
523 fprintf(stderr,"t=%u Unknown MIDI message 0x%02x at %p/%p/%p\n",i,b,t->raw,t->read,t->fence);
\r
529 /* and then read the next event */
\r
530 t->wait = midi_trk_read_delta(t);
\r
532 if (t->wait != 0) {
\r
538 void adlib_shut_up();
\r
539 void midi_reset_tracks();
\r
540 void midi_reset_channels();
\r
543 if (midi_playing) {
\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
552 if (eof >= midi_trk_count) {
\r
554 midi_reset_tracks();
\r
555 midi_reset_channels();
\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
564 if ((irq0_cnt += irq0_add) >= irq0_max) {
\r
565 irq0_cnt -= irq0_max;
\r
569 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
\r
573 void adlib_shut_up() {
\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
586 for (i=0;i < adlib_fm_voices;i++) {
\r
587 struct adlib_fm_operator *f;
\r
589 midi_notes[i].busy = 0;
\r
590 midi_notes[i].note_channel = 0;
\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
597 f->sustain_level = 0;
\r
598 f->release_rate = 8;
\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
609 f->sustain_level = 0;
\r
610 f->release_rate = 8;
\r
620 void midi_reset_track(unsigned int i) {
\r
621 struct midi_track *t;
\r
623 if (i >= MIDI_MAX_TRACKS) return;
\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
633 void midi_reset_tracks() {
\r
636 for (i=0;i < midi_trk_count;i++)
\r
637 midi_reset_track(i);
\r
640 void midi_reset_channels() {
\r
643 for (i=0;i < MIDI_MAX_CHANNELS;i++) {
\r
644 midi_ch[i].program = 0;
\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
654 fd = open(path,O_RDONLY|O_BINARY);
\r
656 printf("Failed to load file %s\n",path);
\r
660 ticks_per_quarter_note = 0;
\r
661 while (read(fd,tmp,8) == 8) {
\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
671 if (sz < 6 || sz > 255) {
\r
672 fprintf(stderr,"Invalid MThd size %lu\n",(unsigned long)sz);
\r
675 if (read(fd,tmp,(int)sz) != (int)sz) {
\r
676 fprintf(stderr,"MThd read error\n");
\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
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
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
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
699 fprintf(stderr,"MThd time division == 0\n");
\r
702 ticks_per_quarter_note = tdiv;
\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
711 if (sz > (60UL << 10UL)) goto err; /* 60KB */
\r
713 if (tracki >= MIDI_MAX_TRACKS) goto err;
\r
714 #if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
\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
723 midi_trk[tracki].raw = malloc(sz);
\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
729 unsigned char far *p = midi_trk[tracki].raw;
\r
730 unsigned long rem = (unsigned long)sz;
\r
731 unsigned long cando;
\r
734 while (rem != 0UL) {
\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
741 if (_dos_read(fd,p,(unsigned)cando,&read) != 0) goto err;
\r
742 if (read != (unsigned)cando) goto err;
\r
745 if ((((unsigned long)FP_OFF(p))+cando) == 0x10000UL)
\r
746 p = MK_FP(FP_SEG(p)+0x1000,0);
\r
748 p += (unsigned)cando;
\r
751 cando = farptr2phys(p);
\r
752 midi_trk[tracki].fence = MK_FP(cando>>4,cando&0xF);
\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
761 fprintf(stderr,"Unknown MIDI chunk %c%c%c%c\n",tmp[0],tmp[1],tmp[2],tmp[3]);
\r
765 if (tracki == 0 || ticks_per_quarter_note == 0) goto err;
\r
766 midi_trk_count = tracki;
\r
768 fprintf(stderr,"Ticks per quarter note: %u\n",ticks_per_quarter_note);
\r
777 int main(int argc,char **argv) {
\r
778 unsigned long ptick;
\r
781 printf("ADLIB FM test program\n");
\r
783 printf("You must specify a MIDI file to play\n");
\r
787 if (!probe_vga()) {
\r
788 printf("Cannot init VGA\n");
\r
791 if (!init_adlib()) {
\r
792 printf("Cannot init library\n");
\r
795 if (!probe_8254()) { /* we need the timer to keep time with the music */
\r
796 printf("8254 timer not found\n");
\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
806 if (load_midi_file(argv[1]) == 0) {
\r
807 printf("Failed to load MIDI\n");
\r
811 write_8254_system_timer(T8254_REF_CLOCK_HZ / 100); /* tick faster at 100Hz please */
\r
814 irq0_max = 1000; /* about 18.2Hz */
\r
815 old_irq0 = _dos_getvect(8);/*IRQ0*/
\r
816 _dos_setvect(8,irq0);
\r
819 midi_reset_channels();
\r
820 midi_reset_tracks();
\r
822 irq0_ticks = ptick = 0;
\r
830 adv = irq0_ticks - ptick;
\r
831 if (adv >= 100UL) adv = 100UL;
\r
832 ptick = irq0_ticks;
\r
842 if (c == 0) c = getch() << 8;
\r
853 _dos_setvect(8,old_irq0);
\r
854 write_8254_system_timer(0); /* back to normal 18.2Hz */
\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
861 free(midi_trk[i].raw);
\r
863 midi_trk[i].raw = NULL;
\r
865 midi_trk[i].fence = NULL;
\r
866 midi_trk[i].read = NULL;
\r