]> 4ch.mooo.com Git - 16.git/blobdiff - src/lib/midi.c
16_pm worked on~
[16.git] / src / lib / midi.c
index 760906c87681b9c38a28b379a066f7ca6be47c97..7e6ac1c5ac07776ba505f195d72d742b0b9ab3be 100755 (executable)
-/* midi.c
- *
- * Adlib OPL2/OPL3 FM synthesizer chipset test program.
- * Play MIDI file using the OPLx synthesizer (well, poorly anyway)
- * (C) 2010-2012 Jonathan Campbell.
- * Hackipedia DOS library.
- *
- * This code is licensed under the LGPL.
- * <insert LGPL legal text here>
- *
- * Compiles for intended target environments:
- *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
- */
-
-#include "src/lib/midi.h"
-
-static unsigned int            midi_trk_count=0;
-struct midi_note               midi_notes[ADLIB_FM_VOICES];
-struct midi_channel            midi_ch[MIDI_MAX_CHANNELS];
-struct midi_track              midi_trk[MIDI_MAX_TRACKS];
-
-/* MIDI params. Nobody ever said it was a straightforward standard!
- * NTS: These are for reading reference. Internally we convert everything to 100Hz time base. */
-static unsigned int ticks_per_quarter_note=0;  /* "Ticks per beat" */
-
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-static inline unsigned long farptr2phys(unsigned char far *p) { /* take 16:16 pointer convert to physical memory address */
-       return ((unsigned long)FP_SEG(p) << 4UL) + ((unsigned long)FP_OFF(p));
-}
-#endif
-
-static inline unsigned char midi_trk_read(struct midi_track *t) {
-       unsigned char c;
-
-       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */
-       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {
-               t->eof = 1;
-               return 0xFF;
-       }
-
-       c = *(t->read);
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-       if (FP_OFF(t->read) >= 0xF) /* 16:16 far pointer aware (NTS: Programs reassigning this pointer MUST normalize the FAR pointer) */
-               t->read = MK_FP(FP_SEG(t->read)+0x1,0);
-       else
-               t->read++;
-#else
-       t->read++;
-#endif
-       return c;
-}
-
-void midi_trk_end(struct midi_track *t) {
-       t->wait = ~0UL;
-       t->read = t->fence;
-}
-
-void midi_trk_skip(struct midi_track *t,unsigned long len) {
-       unsigned long rem;
-
-       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */
-       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)
-               return;
-
-       if (len > 0xFFF0UL) {
-               midi_trk_end(t);
-               return;
-       }
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-       {
-               unsigned long tt;
-
-               tt = farptr2phys(t->read);
-               rem = farptr2phys(t->fence) - tt;
-               if (rem > len) rem = len;
-               tt += rem;
-               t->read = MK_FP(tt>>4,tt&0xF);
-       }
-#else
-       rem = (unsigned long)(t->fence - t->read);
-       if (len > rem) len = rem;
-       t->read += len;
-#endif
-}
-
-static const uint32_t midikeys_freqs[0x80] = {
-       0x00082d01,     /* key 0 = 8.17579891564371Hz */
-       0x0008a976,     /* key 1 = 8.66195721802725Hz */
-       0x00092d51,     /* key 2 = 9.17702399741899Hz */
-       0x0009b904,     /* key 3 = 9.72271824131503Hz */
-       0x000a4d05,     /* key 4 = 10.3008611535272Hz */
-       0x000ae9d3,     /* key 5 = 10.9133822322814Hz */
-       0x000b8ff4,     /* key 6 = 11.5623257097386Hz */
-       0x000c3ff6,     /* key 7 = 12.2498573744297Hz */
-       0x000cfa70,     /* key 8 = 12.9782717993733Hz */
-       0x000dc000,     /* key 9 = 13.75Hz */
-       0x000e914f,     /* key 10 = 14.5676175474403Hz */
-       0x000f6f11,     /* key 11 = 15.4338531642539Hz */
-       0x00105a02,     /* key 12 = 16.3515978312874Hz */
-       0x001152ec,     /* key 13 = 17.3239144360545Hz */
-       0x00125aa2,     /* key 14 = 18.354047994838Hz */
-       0x00137208,     /* key 15 = 19.4454364826301Hz */
-       0x00149a0a,     /* key 16 = 20.6017223070544Hz */
-       0x0015d3a6,     /* key 17 = 21.8267644645627Hz */
-       0x00171fe9,     /* key 18 = 23.1246514194771Hz */
-       0x00187fed,     /* key 19 = 24.4997147488593Hz */
-       0x0019f4e0,     /* key 20 = 25.9565435987466Hz */
-       0x001b8000,     /* key 21 = 27.5Hz */
-       0x001d229e,     /* key 22 = 29.1352350948806Hz */
-       0x001ede22,     /* key 23 = 30.8677063285078Hz */
-       0x0020b404,     /* key 24 = 32.7031956625748Hz */
-       0x0022a5d8,     /* key 25 = 34.647828872109Hz */
-       0x0024b545,     /* key 26 = 36.7080959896759Hz */
-       0x0026e410,     /* key 27 = 38.8908729652601Hz */
-       0x00293414,     /* key 28 = 41.2034446141087Hz */
-       0x002ba74d,     /* key 29 = 43.6535289291255Hz */
-       0x002e3fd2,     /* key 30 = 46.2493028389543Hz */
-       0x0030ffda,     /* key 31 = 48.9994294977187Hz */
-       0x0033e9c0,     /* key 32 = 51.9130871974931Hz */
-       0x00370000,     /* key 33 = 55Hz */
-       0x003a453d,     /* key 34 = 58.2704701897612Hz */
-       0x003dbc44,     /* key 35 = 61.7354126570155Hz */
-       0x00416809,     /* key 36 = 65.4063913251497Hz */
-       0x00454bb0,     /* key 37 = 69.295657744218Hz */
-       0x00496a8b,     /* key 38 = 73.4161919793519Hz */
-       0x004dc820,     /* key 39 = 77.7817459305202Hz */
-       0x00526829,     /* key 40 = 82.4068892282175Hz */
-       0x00574e9b,     /* key 41 = 87.307057858251Hz */
-       0x005c7fa4,     /* key 42 = 92.4986056779086Hz */
-       0x0061ffb5,     /* key 43 = 97.9988589954373Hz */
-       0x0067d380,     /* key 44 = 103.826174394986Hz */
-       0x006e0000,     /* key 45 = 110Hz */
-       0x00748a7b,     /* key 46 = 116.540940379522Hz */
-       0x007b7888,     /* key 47 = 123.470825314031Hz */
-       0x0082d012,     /* key 48 = 130.812782650299Hz */
-       0x008a9760,     /* key 49 = 138.591315488436Hz */
-       0x0092d517,     /* key 50 = 146.832383958704Hz */
-       0x009b9041,     /* key 51 = 155.56349186104Hz */
-       0x00a4d053,     /* key 52 = 164.813778456435Hz */
-       0x00ae9d36,     /* key 53 = 174.614115716502Hz */
-       0x00b8ff49,     /* key 54 = 184.997211355817Hz */
-       0x00c3ff6a,     /* key 55 = 195.997717990875Hz */
-       0x00cfa700,     /* key 56 = 207.652348789973Hz */
-       0x00dc0000,     /* key 57 = 220Hz */
-       0x00e914f6,     /* key 58 = 233.081880759045Hz */
-       0x00f6f110,     /* key 59 = 246.941650628062Hz */
-       0x0105a025,     /* key 60 = 261.625565300599Hz */
-       0x01152ec0,     /* key 61 = 277.182630976872Hz */
-       0x0125aa2e,     /* key 62 = 293.664767917408Hz */
-       0x01372082,     /* key 63 = 311.126983722081Hz */
-       0x0149a0a7,     /* key 64 = 329.62755691287Hz */
-       0x015d3a6d,     /* key 65 = 349.228231433004Hz */
-       0x0171fe92,     /* key 66 = 369.994422711634Hz */
-       0x0187fed4,     /* key 67 = 391.995435981749Hz */
-       0x019f4e00,     /* key 68 = 415.304697579945Hz */
-       0x01b80000,     /* key 69 = 440Hz */
-       0x01d229ec,     /* key 70 = 466.16376151809Hz */
-       0x01ede220,     /* key 71 = 493.883301256124Hz */
-       0x020b404a,     /* key 72 = 523.251130601197Hz */
-       0x022a5d81,     /* key 73 = 554.365261953744Hz */
-       0x024b545c,     /* key 74 = 587.329535834815Hz */
-       0x026e4104,     /* key 75 = 622.253967444162Hz */
-       0x0293414f,     /* key 76 = 659.25511382574Hz */
-       0x02ba74da,     /* key 77 = 698.456462866008Hz */
-       0x02e3fd24,     /* key 78 = 739.988845423269Hz */
-       0x030ffda9,     /* key 79 = 783.990871963499Hz */
-       0x033e9c01,     /* key 80 = 830.60939515989Hz */
-       0x03700000,     /* key 81 = 880Hz */
-       0x03a453d8,     /* key 82 = 932.32752303618Hz */
-       0x03dbc440,     /* key 83 = 987.766602512248Hz */
-       0x04168094,     /* key 84 = 1046.50226120239Hz */
-       0x0454bb03,     /* key 85 = 1108.73052390749Hz */
-       0x0496a8b8,     /* key 86 = 1174.65907166963Hz */
-       0x04dc8208,     /* key 87 = 1244.50793488832Hz */
-       0x0526829e,     /* key 88 = 1318.51022765148Hz */
-       0x0574e9b5,     /* key 89 = 1396.91292573202Hz */
-       0x05c7fa49,     /* key 90 = 1479.97769084654Hz */
-       0x061ffb53,     /* key 91 = 1567.981743927Hz */
-       0x067d3802,     /* key 92 = 1661.21879031978Hz */
-       0x06e00000,     /* key 93 = 1760Hz */
-       0x0748a7b1,     /* key 94 = 1864.65504607236Hz */
-       0x07b78880,     /* key 95 = 1975.5332050245Hz */
-       0x082d0128,     /* key 96 = 2093.00452240479Hz */
-       0x08a97607,     /* key 97 = 2217.46104781498Hz */
-       0x092d5171,     /* key 98 = 2349.31814333926Hz */
-       0x09b90410,     /* key 99 = 2489.01586977665Hz */
-       0x0a4d053c,     /* key 100 = 2637.02045530296Hz */
-       0x0ae9d36b,     /* key 101 = 2793.82585146403Hz */
-       0x0b8ff493,     /* key 102 = 2959.95538169308Hz */
-       0x0c3ff6a7,     /* key 103 = 3135.96348785399Hz */
-       0x0cfa7005,     /* key 104 = 3322.43758063956Hz */
-       0x0dc00000,     /* key 105 = 3520Hz */
-       0x0e914f62,     /* key 106 = 3729.31009214472Hz */
-       0x0f6f1100,     /* key 107 = 3951.06641004899Hz */
-       0x105a0250,     /* key 108 = 4186.00904480958Hz */
-       0x1152ec0e,     /* key 109 = 4434.92209562995Hz */
-       0x125aa2e3,     /* key 110 = 4698.63628667852Hz */
-       0x13720820,     /* key 111 = 4978.03173955329Hz */
-       0x149a0a79,     /* key 112 = 5274.04091060592Hz */
-       0x15d3a6d6,     /* key 113 = 5587.65170292806Hz */
-       0x171fe927,     /* key 114 = 5919.91076338615Hz */
-       0x187fed4e,     /* key 115 = 6271.92697570799Hz */
-       0x19f4e00a,     /* key 116 = 6644.87516127912Hz */
-       0x1b800000,     /* key 117 = 7040Hz */
-       0x1d229ec4,     /* key 118 = 7458.62018428944Hz */
-       0x1ede2200,     /* key 119 = 7902.13282009799Hz */
-       0x20b404a1,     /* key 120 = 8372.01808961916Hz */
-       0x22a5d81c,     /* key 121 = 8869.84419125991Hz */
-       0x24b545c7,     /* key 122 = 9397.27257335704Hz */
-       0x26e41040,     /* key 123 = 9956.06347910659Hz */
-       0x293414f2,     /* key 124 = 10548.0818212118Hz */
-       0x2ba74dac,     /* key 125 = 11175.3034058561Hz */
-       0x2e3fd24f,     /* key 126 = 11839.8215267723Hz */
-       0x30ffda9c      /* key 127 = 12543.853951416Hz */
-};
-
-static uint32_t midi_note_freq(struct midi_channel *ch,unsigned char key) {
-       return midikeys_freqs[key&0x7F];
-}
-
-static struct midi_note *get_fm_note(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char do_alloc) {
-       unsigned int tch = (unsigned int)(t - midi_trk); /* pointer math */
-       unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */
-       unsigned int i,freen=~0;
-
-       for (i=0;i < ADLIB_FM_VOICES;i++) {
-               if (midi_notes[i].busy) {
-                       if (midi_notes[i].note_channel == ach && midi_notes[i].note_track == tch && midi_notes[i].note_number == key)
-                               return &midi_notes[i];
-               }
-               else {
-                       if (freen == ~0) freen = i;
-               }
-       }
-
-       if (do_alloc && freen != ~0) return &midi_notes[freen];
-       return NULL;
-}
-
-static void drop_fm_note(struct midi_channel *ch,unsigned char key) {
-       unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */
-       unsigned int i;
-
-       for (i=0;i < ADLIB_FM_VOICES;i++) {
-               if (midi_notes[i].busy && midi_notes[i].note_channel == ach) {
-                       midi_notes[i].busy = 0;
-                       break;
-               }
-       }
-}
-
-static inline void on_key_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {
-       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);
-       uint32_t freq = midi_note_freq(ch,key);
-       unsigned int ach;
-
-       if (note == NULL) return;
-
-       note->busy = 1;
-       note->note_number = key;
-       note->note_velocity = vel;
-       note->note_track = (unsigned int)(t - midi_trk);
-       note->note_channel = (unsigned int)(ch - midi_ch);
-       ach = (unsigned int)(note - midi_notes); /* which FM channel? */
-       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);
-       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */
-       adlib_fm[ach].mod.sustain_level = vel >> 3;
-       adlib_fm[ach].mod.key_on = 1;
-       adlib_update_groupA0(ach,&adlib_fm[ach]);
-}
-
-static inline void on_key_on(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {
-       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/1);
-       uint32_t freq = midi_note_freq(ch,key);
-       unsigned int ach;
-
-       /* HACK: Ignore percussion */
-       if ((ch->program >= 8 && ch->program <= 15)/*Chromatic percussion*/ ||
-               (ch->program >= 112 && ch->program <= 119)/*Percussive*/ ||
-               ch == &midi_ch[9]/*MIDI channel 10 (DAMN YOU 1-BASED COUNTING)*/)
-               return;
-
-       if (note == NULL) {
-               /* then we'll have to knock one off to make room */
-               drop_fm_note(ch,key);
-               note = get_fm_note(t,ch,key,1);
-               if (note == NULL) return;
-       }
-
-       note->busy = 1;
-       note->note_number = key;
-       note->note_velocity = vel;
-       note->note_track = (unsigned int)(t - midi_trk);
-       note->note_channel = (unsigned int)(ch - midi_ch);
-       ach = (unsigned int)(note - midi_notes); /* which FM channel? */
-       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);
-       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */
-       adlib_fm[ach].mod.sustain_level = vel >> 3;
-       adlib_fm[ach].mod.key_on = 1;
-       adlib_update_groupA0(ach,&adlib_fm[ach]);
-}
-
-static inline void on_key_off(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {
-       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);
-       uint32_t freq = midi_note_freq(ch,key);
-       unsigned int ach;
-
-       if (note == NULL) return;
-
-       note->busy = 0;
-       ach = (unsigned int)(note - midi_notes); /* which FM channel? */
-       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);
-       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */
-       adlib_fm[ach].mod.sustain_level = vel >> 3;
-       adlib_fm[ach].mod.key_on = 0;
-       adlib_update_groupA0(ach,&adlib_fm[ach]);
-}
-
-static inline void on_control_change(struct midi_track *t,struct midi_channel *ch,unsigned char num,unsigned char val) {
-}
-
-static inline void on_program_change(struct midi_track *t,struct midi_channel *ch,unsigned char inst) {
-       ch->program = inst;
-}
-
-static inline void on_channel_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char velocity) {
-}
-
-static inline void on_pitch_bend(struct midi_track *t,struct midi_channel *ch,int bend/*-8192 to 8192*/) {
-}
-
-unsigned long midi_trk_read_delta(struct midi_track *t) {
-       unsigned long tc = 0;
-       unsigned char c = 0,b;
-
-       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */
-       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)
-               return tc;
-
-       while (c < 4) {
-               b = midi_trk_read(t);
-               tc = (tc << 7UL) + (unsigned long)(b&0x7F);
-               if (!(b&0x80)) break;
-               c++;
-       }
-
-       return tc;
-}
-
-void midi_tick_track(unsigned int i) {
-       struct midi_track *t = midi_trk + i;
-       struct midi_channel *ch;
-       unsigned char b,c,d;
-       int cnt=0;
-
-       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */
-       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {
-               t->eof = 1;
-               return;
-       }
-
-       t->us_tick_cnt_mtpq += 10000UL * (unsigned long)ticks_per_quarter_note;
-       while (t->us_tick_cnt_mtpq >= t->us_per_quarter_note) {
-               t->us_tick_cnt_mtpq -= t->us_per_quarter_note;
-               cnt++;
-
-               while (t->wait == 0) {
-                       if ((unsigned long)t->read >= (unsigned long)t->fence) {
-                               t->eof = 1;
-                               break;
-                       }
-
-                       /* read pointer should be pointing at MIDI event bytes, just after the time delay */
-                       b = midi_trk_read(t);
-                       if (b&0x80) {
-                               if (b < 0xF8) {
-                                       if (b >= 0xF0)
-                                               t->last_status = 0;
-                                       else
-                                               t->last_status = b;
-                               }
-                               if (b != 0x00 && ((b&0xF8) != 0xF0))
-                                       c = midi_trk_read(t);
-                       }
-                       else {
-                               /* blegh. last status */
-                               c = b;
-                               b = t->last_status;
-                       }
-                       switch (b>>4) {
-                               case 0x8: { /* note off */
-                                       d = midi_trk_read(t);
-                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */
-                                       on_key_off(t,ch,c,d);
-                                       } break;
-                               case 0x9: { /* note on */
-                                       d = midi_trk_read(t);
-                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */
-                                       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? */
-                                       else on_key_off(t,ch,c,d);
-                                       } break;
-                               case 0xA: { /* polyphonic aftertouch */
-                                       d = midi_trk_read(t);
-                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */
-                                       on_key_aftertouch(t,ch,c,d);
-                                       } break;
-                               case 0xB: { /* control change */
-                                       d = midi_trk_read(t);
-                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */
-                                       on_control_change(t,ch,c,d);
-                                       } break;
-                               case 0xC: { /* program change */
-                                       on_program_change(t,ch,c); /* c=instrument d=not used */
-                                       } break;
-                               case 0xD: { /* channel aftertouch */
-                                       on_channel_aftertouch(t,ch,c); /* c=velocity d=not used */
-                                       } break;
-                               case 0xE: { /* pitch bend */
-                                       d = midi_trk_read(t);
-                                       on_pitch_bend(t,ch,((c&0x7F)|((d&0x7F)<<7))-8192); /* c=LSB d=MSB */
-                                       } break;
-                               case 0xF: { /* event */
-                                       if (b == 0xFF) {
-                                               if (c == 0x7F) { /* c=type d=len */
-                                                       unsigned long len = midi_trk_read_delta(t);
-//====
-                                                       fprintf(stderr,"Type 0x7F len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);
-//====
-                                                       if (len < 512UL) {
-                                                               /* unknown */
-                                                               midi_trk_skip(t,len);
-                                                       }
-                                                       else {
-                                                               midi_trk_end(t);
-                                                       }
-                                               }
-                                               else if (c < 0x7F) {
-                                                       d = midi_trk_read(t);
-
-                                                       if (c == 0x51 && d >= 3) {
-                                                               d -= 3;
-                                                               t->us_per_quarter_note = ((unsigned long)midi_trk_read(t)<<16UL)+
-                                                                       ((unsigned long)midi_trk_read(t)<<8UL)+
-                                                                       ((unsigned long)midi_trk_read(t)<<0UL);
-
-                                                               if (1/*TODO: If format 0 or format 1*/) {
-                                                                       /* Ugh. Unless format 2, the tempo applies to all tracks */
-                                                                       int j;
-
-                                                                       for (j=0;j < midi_trk_count;j++) {
-                                                                               if (j != i) midi_trk[j].us_per_quarter_note =
-                                                                                       t->us_per_quarter_note;
-                                                                       }
-                                                               }
-                                                       }
-                                                       else {
-//====
-                                                               fprintf(stderr,"Type 0x%02x len=%lu %p/%p/%p\n",c,d,t->raw,t->read,t->fence);
-//====
-                                                       }
-
-                                                       midi_trk_skip(t,d);
-                                               }
-                                               else {
-                                                       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);
-                                               }
-                                       }
-                                       else {
-                                               unsigned long len = midi_trk_read_delta(t);
-//====
-                                               fprintf(stderr,"Sysex len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);
-//====
-                                               midi_trk_skip(t,len);
-                                       }
-                                       } break;
-                               default:
-                                       if (b != 0x00) {
-                                               fprintf(stderr,"t=%u Unknown MIDI message 0x%02x at %p/%p/%p\n",i,b,t->raw,t->read,t->fence);
-                                               midi_trk_end(t);
-                                       }
-                                       break;
-                       };
-
-                       /* and then read the next event */
-                       t->wait = midi_trk_read_delta(t);
-               }
-               if (t->wait != 0) {
-                       t->wait--;
-               }
-       }
-}
-
-//void adlib_shut_up();
-void midi_reset_tracks();
-void midi_reset_channels();
-
-void midi_tick() {
-       if (midi_playing) {
-               unsigned int i;
-               int eof=0;
-
-               for (i=0;i < midi_trk_count;i++) {
-                       midi_tick_track(i);
-                       eof += midi_trk[i].eof?1:0;
-               }
-
-               if (eof >= midi_trk_count) {
-                       adlib_shut_up();
-                       midi_reset_tracks();
-                       midi_reset_channels();
-               }
-       }
-}
-
-/* WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models */
-void interrupt irq0() {
-//     midi_tick();
-       irq0_ticks++;
-       if ((irq0_cnt += irq0_add) >= irq0_max) {
-               irq0_cnt -= irq0_max;
-               old_irq0();
-       }
-       else {
-               p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
-       }
-}
-
-void adlib_shut_up() {
-       int i;
-
-       memset(adlib_fm,0,sizeof(adlib_fm));
-       memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));
-       for (i=0;i < adlib_fm_voices;i++) {
-               struct adlib_fm_operator *f;
-               f = &adlib_fm[i].mod;
-               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
-               f = &adlib_fm[i].car;
-               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
-       }
-
-       for (i=0;i < adlib_fm_voices;i++) {
-               struct adlib_fm_operator *f;
-
-               midi_notes[i].busy = 0;
-               midi_notes[i].note_channel = 0;
-
-               f = &adlib_fm[i].mod;
-               f->mod_multiple = 1;
-               f->total_level = 63 - 16;
-               f->attack_rate = 15;
-               f->decay_rate = 4;
-               f->sustain_level = 0;
-               f->release_rate = 8;
-               f->f_number = 400;
-               f->sustain = 1;
-               f->octave = 4;
-               f->key_on = 0;
-
-               f = &adlib_fm[i].car;
-               f->mod_multiple = 1;
-               f->total_level = 63 - 16;
-               f->attack_rate = 15;
-               f->decay_rate = 4;
-               f->sustain_level = 0;
-               f->release_rate = 8;
-               f->f_number = 0;
-               f->sustain = 1;
-               f->octave = 0;
-               f->key_on = 0;
-       }
-
-       adlib_apply_all();
-}
-
-void midi_reset_track(unsigned int i) {
-       struct midi_track *t;
-
-       if (i >= MIDI_MAX_TRACKS) return;
-       t = &midi_trk[i];
-       t->eof = 0;
-       t->last_status = 0;
-       t->us_tick_cnt_mtpq = 0;
-       t->us_per_quarter_note = (60000000UL / 120UL); /* 120BPM */
-       t->read = midi_trk[i].raw;
-       t->wait = midi_trk_read_delta(t); /* and then the read pointer will point at the MIDI event when wait counts down */
-}
-
-void midi_reset_tracks() {
-       int i;
-
-       for (i=0;i < midi_trk_count;i++)
-               midi_reset_track(i);
-}
-
-void midi_reset_channels() {
-       int i;
-
-       for (i=0;i < MIDI_MAX_CHANNELS;i++) {
-               midi_ch[i].program = 0;
-       }
-}
-
-int load_midi_file(const char *path) {
-       unsigned char tmp[256];
-       unsigned int tracks=0;
-       unsigned int tracki=0;
-       int fd;
-
-       fd = open(path,O_RDONLY|O_BINARY);
-       if (fd < 0) {
-               printf("Failed to load file %s\n",path);
-               return 0;
-       }
-
-       ticks_per_quarter_note = 0;
-       while (read(fd,tmp,8) == 8) {
-               uint32_t sz;
-
-               sz =    ((uint32_t)tmp[4] << (uint32_t)24) |
-                       ((uint32_t)tmp[5] << (uint32_t)16) |
-                       ((uint32_t)tmp[6] << (uint32_t)8) |
-                       ((uint32_t)tmp[7] << (uint32_t)0);
-               if (!memcmp(tmp,"MThd",4)) {
-                       unsigned short t,tdiv;
-
-                       if (sz < 6 || sz > 255) {
-                               fprintf(stderr,"Invalid MThd size %lu\n",(unsigned long)sz);
-                               goto err;
-                       }
-                       if (read(fd,tmp,(int)sz) != (int)sz) {
-                               fprintf(stderr,"MThd read error\n");
-                               goto err;
-                       }
-
-                       /* byte 0-1 = format type (0,1 or 2) */
-                       /* byte 2-3 = number of tracks */
-                       /* byte 4-5 = time divison */
-                       t = tmp[1] | (tmp[0] << 8);
-                       if (t > 1) {
-                               fprintf(stderr,"MThd type %u not supported\n",t);
-                               goto err; /* we only take type 0 or 1, don't support 2 */
-                       }
-                       tracks = tmp[3] | (tmp[2] << 8);
-                       if (tracks > MIDI_MAX_TRACKS) {
-                               fprintf(stderr,"MThd too many (%u) tracks\n",tracks);
-                               goto err;
-                       }
-                       tdiv = tmp[5] | (tmp[4] << 8);
-                       if (tdiv & 0x8000) {
-                               fprintf(stderr,"MThd SMPTE time division not supported\n");
-                               goto err; /* we do not support the SMPTE form */
-                       }
-                       if (tdiv == 0) {
-                               fprintf(stderr,"MThd time division == 0\n");
-                               goto err;
-                       }
-                       ticks_per_quarter_note = tdiv;
-               }
-               else if (!memcmp(tmp,"MTrk",4)) {
-                       if (sz == 0UL) continue;
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-                       if (sz > (640UL << 10UL)) goto err; /* 640KB */
-#elif TARGET_MSDOS == 32
-                       if (sz > (1UL << 20UL)) goto err; /* 1MB */
-#else
-                       if (sz > (60UL << 10UL)) goto err; /* 60KB */
-#endif
-                       if (tracki >= MIDI_MAX_TRACKS) goto err;
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-                       {
-                               unsigned segv;
-
-                               /* NTS: _fmalloc() is still limited to 64KB sizes */
-                               if (_dos_allocmem((unsigned)((sz+15UL)>>4UL),&segv) != 0) goto err;
-                               midi_trk[tracki].raw = MK_FP(segv,0);
-                       }
-#else
-                       midi_trk[tracki].raw = malloc(sz);
-#endif
-                       if (midi_trk[tracki].raw == NULL) goto err;
-                       midi_trk[tracki].read = midi_trk[tracki].raw;
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
-                       {
-                               unsigned char far *p = midi_trk[tracki].raw;
-                               unsigned long rem = (unsigned long)sz;
-                               unsigned long cando;
-                               unsigned read;
-
-                               while (rem != 0UL) {
-                                       read = 0;
-
-                                       cando = 0x10000UL - (unsigned long)FP_OFF(p);
-                                       if (cando > rem) cando = rem;
-                                       if (cando > 0xFFFFUL) cando = 0xFFFFUL; /* we're limited to 64KB-1 of reading */
-
-                                       if (_dos_read(fd,p,(unsigned)cando,&read) != 0) goto err;
-                                       if (read != (unsigned)cando) goto err;
-
-                                       rem -= cando;
-                                       if ((((unsigned long)FP_OFF(p))+cando) == 0x10000UL)
-                                               p = MK_FP(FP_SEG(p)+0x1000,0);
-                                       else
-                                               p += (unsigned)cando;
-                               }
-
-                               cando = farptr2phys(p);
-                               midi_trk[tracki].fence = MK_FP(cando>>4,cando&0xF);
-                       }
-#else
-                       midi_trk[tracki].fence = midi_trk[tracki].raw + (unsigned)sz;
-                       if (read(fd,midi_trk[tracki].raw,(unsigned)sz) != (int)sz) goto err;
-#endif
-                       tracki++;
-               }
-               else {
-                       fprintf(stderr,"Unknown MIDI chunk %c%c%c%c\n",tmp[0],tmp[1],tmp[2],tmp[3]);
-                       goto err;
-               }
-       }
-       if (tracki == 0 || ticks_per_quarter_note == 0) goto err;
-       midi_trk_count = tracki;
-
-       fprintf(stderr,"Ticks per quarter note: %u\n",ticks_per_quarter_note);
-
-       close(fd);
-       return 1;
-err:
-       close(fd);
-       return 0;
-}
+/* midi.c\r
+ *\r
+ * Adlib OPL2/OPL3 FM synthesizer chipset test program.\r
+ * Play MIDI file using the OPLx synthesizer (well, poorly anyway)\r
+ * (C) 2010-2012 Jonathan Campbell.\r
+ * Hackipedia DOS library.\r
+ *\r
+ * This code is licensed under the LGPL.\r
+ * <insert LGPL legal text here>\r
+ *\r
+ * Compiles for intended target environments:\r
+ *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]\r
+ */\r
+\r
+#include "src/lib/midi.h"\r
+\r
+static unsigned int            midi_trk_count=0;\r
+struct midi_note               midi_notes[ADLIB_FM_VOICES];\r
+struct midi_channel            midi_ch[MIDI_MAX_CHANNELS];\r
+struct midi_track              midi_trk[MIDI_MAX_TRACKS];\r
+\r
+/* MIDI params. Nobody ever said it was a straightforward standard!\r
+ * NTS: These are for reading reference. Internally we convert everything to 100Hz time base. */\r
+static unsigned int ticks_per_quarter_note=0;  /* "Ticks per beat" */\r
+\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+static inline unsigned long farptr2phys(unsigned char far *p) { /* take 16:16 pointer convert to physical memory address */\r
+       return ((unsigned long)FP_SEG(p) << 4UL) + ((unsigned long)FP_OFF(p));\r
+}\r
+#endif\r
+\r
+static inline unsigned char midi_trk_read(struct midi_track *t) {\r
+       unsigned char c;\r
+\r
+       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
+       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {\r
+               t->eof = 1;\r
+               return 0xFF;\r
+       }\r
+\r
+       c = *(t->read);\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+       if (FP_OFF(t->read) >= 0xF) /* 16:16 far pointer aware (NTS: Programs reassigning this pointer MUST normalize the FAR pointer) */\r
+               t->read = MK_FP(FP_SEG(t->read)+0x1,0);\r
+       else\r
+               t->read++;\r
+#else\r
+       t->read++;\r
+#endif\r
+       return c;\r
+}\r
+\r
+void midi_trk_end(struct midi_track *t) {\r
+       t->wait = ~0UL;\r
+       t->read = t->fence;\r
+}\r
+\r
+void midi_trk_skip(struct midi_track *t,unsigned long len) {\r
+       unsigned long rem;\r
+\r
+       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
+       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)\r
+               return;\r
+\r
+       if (len > 0xFFF0UL) {\r
+               midi_trk_end(t);\r
+               return;\r
+       }\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+       {\r
+               unsigned long tt;\r
+\r
+               tt = farptr2phys(t->read);\r
+               rem = farptr2phys(t->fence) - tt;\r
+               if (rem > len) rem = len;\r
+               tt += rem;\r
+               t->read = MK_FP(tt>>4,tt&0xF);\r
+       }\r
+#else\r
+       rem = (unsigned long)(t->fence - t->read);\r
+       if (len > rem) len = rem;\r
+       t->read += len;\r
+#endif\r
+}\r
+\r
+static const uint32_t midikeys_freqs[0x80] = {\r
+       0x00082d01,     /* key 0 = 8.17579891564371Hz */\r
+       0x0008a976,     /* key 1 = 8.66195721802725Hz */\r
+       0x00092d51,     /* key 2 = 9.17702399741899Hz */\r
+       0x0009b904,     /* key 3 = 9.72271824131503Hz */\r
+       0x000a4d05,     /* key 4 = 10.3008611535272Hz */\r
+       0x000ae9d3,     /* key 5 = 10.9133822322814Hz */\r
+       0x000b8ff4,     /* key 6 = 11.5623257097386Hz */\r
+       0x000c3ff6,     /* key 7 = 12.2498573744297Hz */\r
+       0x000cfa70,     /* key 8 = 12.9782717993733Hz */\r
+       0x000dc000,     /* key 9 = 13.75Hz */\r
+       0x000e914f,     /* key 10 = 14.5676175474403Hz */\r
+       0x000f6f11,     /* key 11 = 15.4338531642539Hz */\r
+       0x00105a02,     /* key 12 = 16.3515978312874Hz */\r
+       0x001152ec,     /* key 13 = 17.3239144360545Hz */\r
+       0x00125aa2,     /* key 14 = 18.354047994838Hz */\r
+       0x00137208,     /* key 15 = 19.4454364826301Hz */\r
+       0x00149a0a,     /* key 16 = 20.6017223070544Hz */\r
+       0x0015d3a6,     /* key 17 = 21.8267644645627Hz */\r
+       0x00171fe9,     /* key 18 = 23.1246514194771Hz */\r
+       0x00187fed,     /* key 19 = 24.4997147488593Hz */\r
+       0x0019f4e0,     /* key 20 = 25.9565435987466Hz */\r
+       0x001b8000,     /* key 21 = 27.5Hz */\r
+       0x001d229e,     /* key 22 = 29.1352350948806Hz */\r
+       0x001ede22,     /* key 23 = 30.8677063285078Hz */\r
+       0x0020b404,     /* key 24 = 32.7031956625748Hz */\r
+       0x0022a5d8,     /* key 25 = 34.647828872109Hz */\r
+       0x0024b545,     /* key 26 = 36.7080959896759Hz */\r
+       0x0026e410,     /* key 27 = 38.8908729652601Hz */\r
+       0x00293414,     /* key 28 = 41.2034446141087Hz */\r
+       0x002ba74d,     /* key 29 = 43.6535289291255Hz */\r
+       0x002e3fd2,     /* key 30 = 46.2493028389543Hz */\r
+       0x0030ffda,     /* key 31 = 48.9994294977187Hz */\r
+       0x0033e9c0,     /* key 32 = 51.9130871974931Hz */\r
+       0x00370000,     /* key 33 = 55Hz */\r
+       0x003a453d,     /* key 34 = 58.2704701897612Hz */\r
+       0x003dbc44,     /* key 35 = 61.7354126570155Hz */\r
+       0x00416809,     /* key 36 = 65.4063913251497Hz */\r
+       0x00454bb0,     /* key 37 = 69.295657744218Hz */\r
+       0x00496a8b,     /* key 38 = 73.4161919793519Hz */\r
+       0x004dc820,     /* key 39 = 77.7817459305202Hz */\r
+       0x00526829,     /* key 40 = 82.4068892282175Hz */\r
+       0x00574e9b,     /* key 41 = 87.307057858251Hz */\r
+       0x005c7fa4,     /* key 42 = 92.4986056779086Hz */\r
+       0x0061ffb5,     /* key 43 = 97.9988589954373Hz */\r
+       0x0067d380,     /* key 44 = 103.826174394986Hz */\r
+       0x006e0000,     /* key 45 = 110Hz */\r
+       0x00748a7b,     /* key 46 = 116.540940379522Hz */\r
+       0x007b7888,     /* key 47 = 123.470825314031Hz */\r
+       0x0082d012,     /* key 48 = 130.812782650299Hz */\r
+       0x008a9760,     /* key 49 = 138.591315488436Hz */\r
+       0x0092d517,     /* key 50 = 146.832383958704Hz */\r
+       0x009b9041,     /* key 51 = 155.56349186104Hz */\r
+       0x00a4d053,     /* key 52 = 164.813778456435Hz */\r
+       0x00ae9d36,     /* key 53 = 174.614115716502Hz */\r
+       0x00b8ff49,     /* key 54 = 184.997211355817Hz */\r
+       0x00c3ff6a,     /* key 55 = 195.997717990875Hz */\r
+       0x00cfa700,     /* key 56 = 207.652348789973Hz */\r
+       0x00dc0000,     /* key 57 = 220Hz */\r
+       0x00e914f6,     /* key 58 = 233.081880759045Hz */\r
+       0x00f6f110,     /* key 59 = 246.941650628062Hz */\r
+       0x0105a025,     /* key 60 = 261.625565300599Hz */\r
+       0x01152ec0,     /* key 61 = 277.182630976872Hz */\r
+       0x0125aa2e,     /* key 62 = 293.664767917408Hz */\r
+       0x01372082,     /* key 63 = 311.126983722081Hz */\r
+       0x0149a0a7,     /* key 64 = 329.62755691287Hz */\r
+       0x015d3a6d,     /* key 65 = 349.228231433004Hz */\r
+       0x0171fe92,     /* key 66 = 369.994422711634Hz */\r
+       0x0187fed4,     /* key 67 = 391.995435981749Hz */\r
+       0x019f4e00,     /* key 68 = 415.304697579945Hz */\r
+       0x01b80000,     /* key 69 = 440Hz */\r
+       0x01d229ec,     /* key 70 = 466.16376151809Hz */\r
+       0x01ede220,     /* key 71 = 493.883301256124Hz */\r
+       0x020b404a,     /* key 72 = 523.251130601197Hz */\r
+       0x022a5d81,     /* key 73 = 554.365261953744Hz */\r
+       0x024b545c,     /* key 74 = 587.329535834815Hz */\r
+       0x026e4104,     /* key 75 = 622.253967444162Hz */\r
+       0x0293414f,     /* key 76 = 659.25511382574Hz */\r
+       0x02ba74da,     /* key 77 = 698.456462866008Hz */\r
+       0x02e3fd24,     /* key 78 = 739.988845423269Hz */\r
+       0x030ffda9,     /* key 79 = 783.990871963499Hz */\r
+       0x033e9c01,     /* key 80 = 830.60939515989Hz */\r
+       0x03700000,     /* key 81 = 880Hz */\r
+       0x03a453d8,     /* key 82 = 932.32752303618Hz */\r
+       0x03dbc440,     /* key 83 = 987.766602512248Hz */\r
+       0x04168094,     /* key 84 = 1046.50226120239Hz */\r
+       0x0454bb03,     /* key 85 = 1108.73052390749Hz */\r
+       0x0496a8b8,     /* key 86 = 1174.65907166963Hz */\r
+       0x04dc8208,     /* key 87 = 1244.50793488832Hz */\r
+       0x0526829e,     /* key 88 = 1318.51022765148Hz */\r
+       0x0574e9b5,     /* key 89 = 1396.91292573202Hz */\r
+       0x05c7fa49,     /* key 90 = 1479.97769084654Hz */\r
+       0x061ffb53,     /* key 91 = 1567.981743927Hz */\r
+       0x067d3802,     /* key 92 = 1661.21879031978Hz */\r
+       0x06e00000,     /* key 93 = 1760Hz */\r
+       0x0748a7b1,     /* key 94 = 1864.65504607236Hz */\r
+       0x07b78880,     /* key 95 = 1975.5332050245Hz */\r
+       0x082d0128,     /* key 96 = 2093.00452240479Hz */\r
+       0x08a97607,     /* key 97 = 2217.46104781498Hz */\r
+       0x092d5171,     /* key 98 = 2349.31814333926Hz */\r
+       0x09b90410,     /* key 99 = 2489.01586977665Hz */\r
+       0x0a4d053c,     /* key 100 = 2637.02045530296Hz */\r
+       0x0ae9d36b,     /* key 101 = 2793.82585146403Hz */\r
+       0x0b8ff493,     /* key 102 = 2959.95538169308Hz */\r
+       0x0c3ff6a7,     /* key 103 = 3135.96348785399Hz */\r
+       0x0cfa7005,     /* key 104 = 3322.43758063956Hz */\r
+       0x0dc00000,     /* key 105 = 3520Hz */\r
+       0x0e914f62,     /* key 106 = 3729.31009214472Hz */\r
+       0x0f6f1100,     /* key 107 = 3951.06641004899Hz */\r
+       0x105a0250,     /* key 108 = 4186.00904480958Hz */\r
+       0x1152ec0e,     /* key 109 = 4434.92209562995Hz */\r
+       0x125aa2e3,     /* key 110 = 4698.63628667852Hz */\r
+       0x13720820,     /* key 111 = 4978.03173955329Hz */\r
+       0x149a0a79,     /* key 112 = 5274.04091060592Hz */\r
+       0x15d3a6d6,     /* key 113 = 5587.65170292806Hz */\r
+       0x171fe927,     /* key 114 = 5919.91076338615Hz */\r
+       0x187fed4e,     /* key 115 = 6271.92697570799Hz */\r
+       0x19f4e00a,     /* key 116 = 6644.87516127912Hz */\r
+       0x1b800000,     /* key 117 = 7040Hz */\r
+       0x1d229ec4,     /* key 118 = 7458.62018428944Hz */\r
+       0x1ede2200,     /* key 119 = 7902.13282009799Hz */\r
+       0x20b404a1,     /* key 120 = 8372.01808961916Hz */\r
+       0x22a5d81c,     /* key 121 = 8869.84419125991Hz */\r
+       0x24b545c7,     /* key 122 = 9397.27257335704Hz */\r
+       0x26e41040,     /* key 123 = 9956.06347910659Hz */\r
+       0x293414f2,     /* key 124 = 10548.0818212118Hz */\r
+       0x2ba74dac,     /* key 125 = 11175.3034058561Hz */\r
+       0x2e3fd24f,     /* key 126 = 11839.8215267723Hz */\r
+       0x30ffda9c      /* key 127 = 12543.853951416Hz */\r
+};\r
+\r
+static uint32_t midi_note_freq(struct midi_channel *ch,unsigned char key) {\r
+       return midikeys_freqs[key&0x7F];\r
+}\r
+\r
+static struct midi_note *get_fm_note(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char do_alloc) {\r
+       unsigned int tch = (unsigned int)(t - midi_trk); /* pointer math */\r
+       unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */\r
+       unsigned int i,freen=~0;\r
+\r
+       for (i=0;i < ADLIB_FM_VOICES;i++) {\r
+               if (midi_notes[i].busy) {\r
+                       if (midi_notes[i].note_channel == ach && midi_notes[i].note_track == tch && midi_notes[i].note_number == key)\r
+                               return &midi_notes[i];\r
+               }\r
+               else {\r
+                       if (freen == ~0) freen = i;\r
+               }\r
+       }\r
+\r
+       if (do_alloc && freen != ~0) return &midi_notes[freen];\r
+       return NULL;\r
+}\r
+\r
+static void drop_fm_note(struct midi_channel *ch,unsigned char key) {\r
+       unsigned int ach = (unsigned int)(ch - midi_ch); /* pointer math */\r
+       unsigned int i;\r
+\r
+       for (i=0;i < ADLIB_FM_VOICES;i++) {\r
+               if (midi_notes[i].busy && midi_notes[i].note_channel == ach) {\r
+                       midi_notes[i].busy = 0;\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+static inline void on_key_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
+       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);\r
+       uint32_t freq = midi_note_freq(ch,key);\r
+       unsigned int ach;\r
+\r
+       if (note == NULL) return;\r
+\r
+       note->busy = 1;\r
+       note->note_number = key;\r
+       note->note_velocity = vel;\r
+       note->note_track = (unsigned int)(t - midi_trk);\r
+       note->note_channel = (unsigned int)(ch - midi_ch);\r
+       ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
+       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
+       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
+       adlib_fm[ach].mod.sustain_level = vel >> 3;\r
+       adlib_fm[ach].mod.key_on = 1;\r
+       adlib_update_groupA0(ach,&adlib_fm[ach]);\r
+}\r
+\r
+static inline void on_key_on(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
+       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/1);\r
+       uint32_t freq = midi_note_freq(ch,key);\r
+       unsigned int ach;\r
+\r
+       /* HACK: Ignore percussion */\r
+       if ((ch->program >= 8 && ch->program <= 15)/*Chromatic percussion*/ ||\r
+               (ch->program >= 112 && ch->program <= 119)/*Percussive*/ ||\r
+               ch == &midi_ch[9]/*MIDI channel 10 (DAMN YOU 1-BASED COUNTING)*/)\r
+               return;\r
+\r
+       if (note == NULL) {\r
+               /* then we'll have to knock one off to make room */\r
+               drop_fm_note(ch,key);\r
+               note = get_fm_note(t,ch,key,1);\r
+               if (note == NULL) return;\r
+       }\r
+\r
+       note->busy = 1;\r
+       note->note_number = key;\r
+       note->note_velocity = vel;\r
+       note->note_track = (unsigned int)(t - midi_trk);\r
+       note->note_channel = (unsigned int)(ch - midi_ch);\r
+       ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
+       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
+       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
+       adlib_fm[ach].mod.sustain_level = vel >> 3;\r
+       adlib_fm[ach].mod.key_on = 1;\r
+       adlib_update_groupA0(ach,&adlib_fm[ach]);\r
+}\r
+\r
+static inline void on_key_off(struct midi_track *t,struct midi_channel *ch,unsigned char key,unsigned char vel) {\r
+       struct midi_note *note = get_fm_note(t,ch,key,/*do_alloc*/0);\r
+       uint32_t freq = midi_note_freq(ch,key);\r
+       unsigned int ach;\r
+\r
+       if (note == NULL) return;\r
+\r
+       note->busy = 0;\r
+       ach = (unsigned int)(note - midi_notes); /* which FM channel? */\r
+       adlib_freq_to_fm_op(&adlib_fm[ach].mod,(double)freq / 65536);\r
+       adlib_fm[ach].mod.attack_rate = vel >> 3; /* 0-127 to 0-15 */\r
+       adlib_fm[ach].mod.sustain_level = vel >> 3;\r
+       adlib_fm[ach].mod.key_on = 0;\r
+       adlib_update_groupA0(ach,&adlib_fm[ach]);\r
+}\r
+\r
+static inline void on_control_change(struct midi_track *t,struct midi_channel *ch,unsigned char num,unsigned char val) {\r
+}\r
+\r
+static inline void on_program_change(struct midi_track *t,struct midi_channel *ch,unsigned char inst) {\r
+       ch->program = inst;\r
+}\r
+\r
+static inline void on_channel_aftertouch(struct midi_track *t,struct midi_channel *ch,unsigned char velocity) {\r
+}\r
+\r
+static inline void on_pitch_bend(struct midi_track *t,struct midi_channel *ch,int bend/*-8192 to 8192*/) {\r
+}\r
+\r
+unsigned long midi_trk_read_delta(struct midi_track *t) {\r
+       unsigned long tc = 0;\r
+       unsigned char c = 0,b;\r
+\r
+       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
+       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence)\r
+               return tc;\r
+\r
+       while (c < 4) {\r
+               b = midi_trk_read(t);\r
+               tc = (tc << 7UL) + (unsigned long)(b&0x7F);\r
+               if (!(b&0x80)) break;\r
+               c++;\r
+       }\r
+\r
+       return tc;\r
+}\r
+\r
+void midi_tick_track(unsigned int i) {\r
+       struct midi_track *t = midi_trk + i;\r
+       struct midi_channel *ch;\r
+       unsigned char b,c,d;\r
+       int cnt=0;\r
+\r
+       /* NTS: 16-bit large/compact builds MUST compare pointers as unsigned long to compare FAR pointers correctly! */\r
+       if (t->read == NULL || (unsigned long)t->read >= (unsigned long)t->fence) {\r
+               t->eof = 1;\r
+               return;\r
+       }\r
+\r
+       t->us_tick_cnt_mtpq += 10000UL * (unsigned long)ticks_per_quarter_note;\r
+       while (t->us_tick_cnt_mtpq >= t->us_per_quarter_note) {\r
+               t->us_tick_cnt_mtpq -= t->us_per_quarter_note;\r
+               cnt++;\r
+\r
+               while (t->wait == 0) {\r
+                       if ((unsigned long)t->read >= (unsigned long)t->fence) {\r
+                               t->eof = 1;\r
+                               break;\r
+                       }\r
+\r
+                       /* read pointer should be pointing at MIDI event bytes, just after the time delay */\r
+                       b = midi_trk_read(t);\r
+                       if (b&0x80) {\r
+                               if (b < 0xF8) {\r
+                                       if (b >= 0xF0)\r
+                                               t->last_status = 0;\r
+                                       else\r
+                                               t->last_status = b;\r
+                               }\r
+                               if (b != 0x00 && ((b&0xF8) != 0xF0))\r
+                                       c = midi_trk_read(t);\r
+                       }\r
+                       else {\r
+                               /* blegh. last status */\r
+                               c = b;\r
+                               b = t->last_status;\r
+                       }\r
+                       switch (b>>4) {\r
+                               case 0x8: { /* note off */\r
+                                       d = midi_trk_read(t);\r
+                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
+                                       on_key_off(t,ch,c,d);\r
+                                       } break;\r
+                               case 0x9: { /* note on */\r
+                                       d = midi_trk_read(t);\r
+                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
+                                       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
+                                       else on_key_off(t,ch,c,d);\r
+                                       } break;\r
+                               case 0xA: { /* polyphonic aftertouch */\r
+                                       d = midi_trk_read(t);\r
+                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
+                                       on_key_aftertouch(t,ch,c,d);\r
+                                       } break;\r
+                               case 0xB: { /* control change */\r
+                                       d = midi_trk_read(t);\r
+                                       ch = midi_ch + (b&0xF); /* c=key d=velocity */\r
+                                       on_control_change(t,ch,c,d);\r
+                                       } break;\r
+                               case 0xC: { /* program change */\r
+                                       on_program_change(t,ch,c); /* c=instrument d=not used */\r
+                                       } break;\r
+                               case 0xD: { /* channel aftertouch */\r
+                                       on_channel_aftertouch(t,ch,c); /* c=velocity d=not used */\r
+                                       } break;\r
+                               case 0xE: { /* pitch bend */\r
+                                       d = midi_trk_read(t);\r
+                                       on_pitch_bend(t,ch,((c&0x7F)|((d&0x7F)<<7))-8192); /* c=LSB d=MSB */\r
+                                       } break;\r
+                               case 0xF: { /* event */\r
+                                       if (b == 0xFF) {\r
+                                               if (c == 0x7F) { /* c=type d=len */\r
+                                                       unsigned long len = midi_trk_read_delta(t);\r
+//====\r
+                                                       fprintf(stderr,"Type 0x7F len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\r
+//====\r
+                                                       if (len < 512UL) {\r
+                                                               /* unknown */\r
+                                                               midi_trk_skip(t,len);\r
+                                                       }\r
+                                                       else {\r
+                                                               midi_trk_end(t);\r
+                                                       }\r
+                                               }\r
+                                               else if (c < 0x7F) {\r
+                                                       d = midi_trk_read(t);\r
+\r
+                                                       if (c == 0x51 && d >= 3) {\r
+                                                               d -= 3;\r
+                                                               t->us_per_quarter_note = ((unsigned long)midi_trk_read(t)<<16UL)+\r
+                                                                       ((unsigned long)midi_trk_read(t)<<8UL)+\r
+                                                                       ((unsigned long)midi_trk_read(t)<<0UL);\r
+\r
+                                                               if (1/*TODO: If format 0 or format 1*/) {\r
+                                                                       /* Ugh. Unless format 2, the tempo applies to all tracks */\r
+                                                                       int j;\r
+\r
+                                                                       for (j=0;j < midi_trk_count;j++) {\r
+                                                                               if (j != i) midi_trk[j].us_per_quarter_note =\r
+                                                                                       t->us_per_quarter_note;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       else {\r
+//====\r
+                                                               fprintf(stderr,"Type 0x%02x len=%lu %p/%p/%p\n",c,d,t->raw,t->read,t->fence);\r
+//====\r
+                                                       }\r
+\r
+                                                       midi_trk_skip(t,d);\r
+                                               }\r
+                                               else {\r
+                                                       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
+                                               }\r
+                                       }\r
+                                       else {\r
+                                               unsigned long len = midi_trk_read_delta(t);\r
+//====\r
+                                               fprintf(stderr,"Sysex len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\r
+//====\r
+                                               midi_trk_skip(t,len);\r
+                                       }\r
+                                       } break;\r
+                               default:\r
+                                       if (b != 0x00) {\r
+                                               fprintf(stderr,"t=%u Unknown MIDI message 0x%02x at %p/%p/%p\n",i,b,t->raw,t->read,t->fence);\r
+                                               midi_trk_end(t);\r
+                                       }\r
+                                       break;\r
+                       };\r
+\r
+                       /* and then read the next event */\r
+                       t->wait = midi_trk_read_delta(t);\r
+               }\r
+               if (t->wait != 0) {\r
+                       t->wait--;\r
+               }\r
+       }\r
+}\r
+\r
+//void adlib_shut_up();\r
+void midi_reset_tracks();\r
+void midi_reset_channels();\r
+\r
+void midi_tick() {\r
+       if (midi_playing) {\r
+               unsigned int i;\r
+               int eof=0;\r
+\r
+               for (i=0;i < midi_trk_count;i++) {\r
+                       midi_tick_track(i);\r
+                       eof += midi_trk[i].eof?1:0;\r
+               }\r
+\r
+               if (eof >= midi_trk_count) {\r
+                       adlib_shut_up();\r
+                       midi_reset_tracks();\r
+                       midi_reset_channels();\r
+               }\r
+       }\r
+}\r
+\r
+/* WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models */\r
+void interrupt irq0() {\r
+//     midi_tick();\r
+       irq0_ticks++;\r
+       if ((irq0_cnt += irq0_add) >= irq0_max) {\r
+               irq0_cnt -= irq0_max;\r
+               old_irq0();\r
+       }\r
+       else {\r
+               p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);\r
+       }\r
+}\r
+\r
+void adlib_shut_up() {\r
+       int i;\r
+\r
+       memset(adlib_fm,0,sizeof(adlib_fm));\r
+       memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));\r
+       for (i=0;i < adlib_fm_voices;i++) {\r
+               struct adlib_fm_operator *f;\r
+               f = &adlib_fm[i].mod;\r
+               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
+               f = &adlib_fm[i].car;\r
+               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
+       }\r
+\r
+       for (i=0;i < adlib_fm_voices;i++) {\r
+               struct adlib_fm_operator *f;\r
+\r
+               midi_notes[i].busy = 0;\r
+               midi_notes[i].note_channel = 0;\r
+\r
+               f = &adlib_fm[i].mod;\r
+               f->mod_multiple = 1;\r
+               f->total_level = 63 - 16;\r
+               f->attack_rate = 15;\r
+               f->decay_rate = 4;\r
+               f->sustain_level = 0;\r
+               f->release_rate = 8;\r
+               f->f_number = 400;\r
+               f->sustain = 1;\r
+               f->octave = 4;\r
+               f->key_on = 0;\r
+\r
+               f = &adlib_fm[i].car;\r
+               f->mod_multiple = 1;\r
+               f->total_level = 63 - 16;\r
+               f->attack_rate = 15;\r
+               f->decay_rate = 4;\r
+               f->sustain_level = 0;\r
+               f->release_rate = 8;\r
+               f->f_number = 0;\r
+               f->sustain = 1;\r
+               f->octave = 0;\r
+               f->key_on = 0;\r
+       }\r
+\r
+       adlib_apply_all();\r
+}\r
+\r
+void midi_reset_track(unsigned int i) {\r
+       struct midi_track *t;\r
+\r
+       if (i >= MIDI_MAX_TRACKS) return;\r
+       t = &midi_trk[i];\r
+       t->eof = 0;\r
+       t->last_status = 0;\r
+       t->us_tick_cnt_mtpq = 0;\r
+       t->us_per_quarter_note = (60000000UL / 120UL); /* 120BPM */\r
+       t->read = midi_trk[i].raw;\r
+       t->wait = midi_trk_read_delta(t); /* and then the read pointer will point at the MIDI event when wait counts down */\r
+}\r
+\r
+void midi_reset_tracks() {\r
+       int i;\r
+\r
+       for (i=0;i < midi_trk_count;i++)\r
+               midi_reset_track(i);\r
+}\r
+\r
+void midi_reset_channels() {\r
+       int i;\r
+\r
+       for (i=0;i < MIDI_MAX_CHANNELS;i++) {\r
+               midi_ch[i].program = 0;\r
+       }\r
+}\r
+\r
+int load_midi_file(const char *path) {\r
+       unsigned char tmp[256];\r
+       unsigned int tracks=0;\r
+       unsigned int tracki=0;\r
+       int fd;\r
+\r
+       fd = open(path,O_RDONLY|O_BINARY);\r
+       if (fd < 0) {\r
+               printf("Failed to load file %s\n",path);\r
+               return 0;\r
+       }\r
+\r
+       ticks_per_quarter_note = 0;\r
+       while (read(fd,tmp,8) == 8) {\r
+               uint32_t sz;\r
+\r
+               sz =    ((uint32_t)tmp[4] << (uint32_t)24) |\r
+                       ((uint32_t)tmp[5] << (uint32_t)16) |\r
+                       ((uint32_t)tmp[6] << (uint32_t)8) |\r
+                       ((uint32_t)tmp[7] << (uint32_t)0);\r
+               if (!memcmp(tmp,"MThd",4)) {\r
+                       unsigned short t,tdiv;\r
+\r
+                       if (sz < 6 || sz > 255) {\r
+                               fprintf(stderr,"Invalid MThd size %lu\n",(unsigned long)sz);\r
+                               goto err;\r
+                       }\r
+                       if (read(fd,tmp,(int)sz) != (int)sz) {\r
+                               fprintf(stderr,"MThd read error\n");\r
+                               goto err;\r
+                       }\r
+\r
+                       /* byte 0-1 = format type (0,1 or 2) */\r
+                       /* byte 2-3 = number of tracks */\r
+                       /* byte 4-5 = time divison */\r
+                       t = tmp[1] | (tmp[0] << 8);\r
+                       if (t > 1) {\r
+                               fprintf(stderr,"MThd type %u not supported\n",t);\r
+                               goto err; /* we only take type 0 or 1, don't support 2 */\r
+                       }\r
+                       tracks = tmp[3] | (tmp[2] << 8);\r
+                       if (tracks > MIDI_MAX_TRACKS) {\r
+                               fprintf(stderr,"MThd too many (%u) tracks\n",tracks);\r
+                               goto err;\r
+                       }\r
+                       tdiv = tmp[5] | (tmp[4] << 8);\r
+                       if (tdiv & 0x8000) {\r
+                               fprintf(stderr,"MThd SMPTE time division not supported\n");\r
+                               goto err; /* we do not support the SMPTE form */\r
+                       }\r
+                       if (tdiv == 0) {\r
+                               fprintf(stderr,"MThd time division == 0\n");\r
+                               goto err;\r
+                       }\r
+                       ticks_per_quarter_note = tdiv;\r
+               }\r
+               else if (!memcmp(tmp,"MTrk",4)) {\r
+                       if (sz == 0UL) continue;\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+                       if (sz > (640UL << 10UL)) goto err; /* 640KB */\r
+#elif TARGET_MSDOS == 32\r
+                       if (sz > (1UL << 20UL)) goto err; /* 1MB */\r
+#else\r
+                       if (sz > (60UL << 10UL)) goto err; /* 60KB */\r
+#endif\r
+                       if (tracki >= MIDI_MAX_TRACKS) goto err;\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+                       {\r
+                               unsigned segv;\r
+\r
+                               /* NTS: _fmalloc() is still limited to 64KB sizes */\r
+                               if (_dos_allocmem((unsigned)((sz+15UL)>>4UL),&segv) != 0) goto err;\r
+                               midi_trk[tracki].raw = MK_FP(segv,0);\r
+                       }\r
+#else\r
+                       midi_trk[tracki].raw = malloc(sz);\r
+#endif\r
+                       if (midi_trk[tracki].raw == NULL) goto err;\r
+                       midi_trk[tracki].read = midi_trk[tracki].raw;\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+                       {\r
+                               unsigned char far *p = midi_trk[tracki].raw;\r
+                               unsigned long rem = (unsigned long)sz;\r
+                               unsigned long cando;\r
+                               unsigned read;\r
+\r
+                               while (rem != 0UL) {\r
+                                       read = 0;\r
+\r
+                                       cando = 0x10000UL - (unsigned long)FP_OFF(p);\r
+                                       if (cando > rem) cando = rem;\r
+                                       if (cando > 0xFFFFUL) cando = 0xFFFFUL; /* we're limited to 64KB-1 of reading */\r
+\r
+                                       if (_dos_read(fd,p,(unsigned)cando,&read) != 0) goto err;\r
+                                       if (read != (unsigned)cando) goto err;\r
+\r
+                                       rem -= cando;\r
+                                       if ((((unsigned long)FP_OFF(p))+cando) == 0x10000UL)\r
+                                               p = MK_FP(FP_SEG(p)+0x1000,0);\r
+                                       else\r
+                                               p += (unsigned)cando;\r
+                               }\r
+\r
+                               cando = farptr2phys(p);\r
+                               midi_trk[tracki].fence = MK_FP(cando>>4,cando&0xF);\r
+                       }\r
+#else\r
+                       midi_trk[tracki].fence = midi_trk[tracki].raw + (unsigned)sz;\r
+                       if (read(fd,midi_trk[tracki].raw,(unsigned)sz) != (int)sz) goto err;\r
+#endif\r
+                       tracki++;\r
+               }\r
+               else {\r
+                       fprintf(stderr,"Unknown MIDI chunk %c%c%c%c\n",tmp[0],tmp[1],tmp[2],tmp[3]);\r
+                       goto err;\r
+               }\r
+       }\r
+       if (tracki == 0 || ticks_per_quarter_note == 0) goto err;\r
+       midi_trk_count = tracki;\r
+\r
+       fprintf(stderr,"Ticks per quarter note: %u\n",ticks_per_quarter_note);\r
+\r
+       close(fd);\r
+       return 1;\r
+err:\r
+       close(fd);\r
+       return 0;\r
+}\r