-/* 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 <stdio.h>
-#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <malloc.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <math.h>
-#include <dos.h>
-
-#include "src/lib/doslib/vga.h"
-#include "src/lib/doslib/dos.h"
-#include "src/lib/doslib/8254.h" /* 8254 timer */
-#include "src/lib/doslib/8259.h"
-#include "src/lib/doslib/vgagui.h"
-#include "src/lib/doslib/vgatty.h"
-#include "src/lib/doslib/adlib.h"
-
-/* one per OPL channel */
-struct midi_note {
- unsigned char note_number;
- unsigned char note_velocity;
- unsigned char note_track; /* from what MIDI track */
- unsigned char note_channel; /* from what MIDI channel */
- unsigned int busy:1; /* if occupied */
-};
-
-struct midi_channel {
- unsigned char program;
-};
-
-struct midi_track {
- /* track data, raw */
- unsigned char* raw; /* raw data base */
- unsigned char* fence; /* raw data end (last byte + 1) */
- unsigned char* read; /* raw data read ptr */
- /* state */
- unsigned long us_per_quarter_note; /* Microseconds per quarter note (def 120 BPM) */
- unsigned long us_tick_cnt_mtpq; /* Microseconds advanced (up to 10000 us or one unit at 100Hz) x ticks per quarter note */
- unsigned long wait;
- unsigned char last_status; /* MIDI last status byte */
- unsigned int eof:1; /* we hit the end of the track */
-};
-
-#define MIDI_MAX_CHANNELS 16
-#define MIDI_MAX_TRACKS 64
-
-struct midi_note midi_notes[ADLIB_FM_VOICES];
-struct midi_channel midi_ch[MIDI_MAX_CHANNELS];
-struct midi_track midi_trk[MIDI_MAX_TRACKS];
-static unsigned int midi_trk_count=0;
-static volatile unsigned char midi_playing=0;
-
-/* 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" */
-
-static void (interrupt *old_irq0)();
-static volatile unsigned long irq0_ticks=0;
-static volatile unsigned int irq0_cnt=0,irq0_add=0,irq0_max=0;
-
-#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;
-}
-
-int main(int argc,char **argv) {
- unsigned long ptick;
- int i,c;
-
- printf("ADLIB FM test program\n");
- if (argc < 2) {
- printf("You must specify a MIDI file to play\n");
- return 1;
- }
-
- if (!probe_vga()) {
- printf("Cannot init VGA\n");
- return 1;
- }
- if (!init_adlib()) {
- printf("Cannot init library\n");
- return 1;
- }
- if (!probe_8254()) { /* we need the timer to keep time with the music */
- printf("8254 timer not found\n");
- return 1;
- }
-
- for (i=0;i < MIDI_MAX_TRACKS;i++) {
- midi_trk[i].raw = NULL;
- midi_trk[i].read = NULL;
- midi_trk[i].fence = NULL;
- }
-
- if (load_midi_file(argv[1]) == 0) {
- printf("Failed to load MIDI\n");
- return 1;
- }
-
- write_8254_system_timer(T8254_REF_CLOCK_HZ / 100); /* tick faster at 100Hz please */
- irq0_cnt = 0;
- irq0_add = 182;
- irq0_max = 1000; /* about 18.2Hz */
- old_irq0 = _dos_getvect(8);/*IRQ0*/
- _dos_setvect(8,irq0);
-
- adlib_shut_up();
- midi_reset_channels();
- midi_reset_tracks();
- _cli();
- irq0_ticks = ptick = 0;
- _sti();
- midi_playing = 1;
-
- while (1) {
- unsigned long adv;
-
- _cli();
- adv = irq0_ticks - ptick;
- if (adv >= 100UL) adv = 100UL;
- ptick = irq0_ticks;
- _sti();
-
- while (adv != 0) {
- midi_tick();
- adv--;
- }
-
- if (kbhit()) {
- c = getch();
- if (c == 0) c = getch() << 8;
-
- if (c == 27) {
- break;
- }
- }
- }
-
- midi_playing = 0;
- adlib_shut_up();
- shutdown_adlib();
- _dos_setvect(8,old_irq0);
- write_8254_system_timer(0); /* back to normal 18.2Hz */
-
- for (i=0;i < MIDI_MAX_TRACKS;i++) {
- if (midi_trk[i].raw) {
-#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))
- _dos_freemem(FP_SEG(midi_trk[i].raw)); /* NTS: Because we allocated with _dos_allocmem */
-#else
- free(midi_trk[i].raw);
-#endif
- midi_trk[i].raw = NULL;
- }
- midi_trk[i].fence = NULL;
- midi_trk[i].read = NULL;
- }
-
- 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 <stdio.h>\r
+#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include <malloc.h>\r
+#include <ctype.h>\r
+#include <fcntl.h>\r
+#include <math.h>\r
+#include <dos.h>\r
+\r
+#include "src/lib/doslib/vga.h"\r
+#include "src/lib/doslib/dos.h"\r
+#include "src/lib/doslib/8254.h" /* 8254 timer */\r
+#include "src/lib/doslib/8259.h"\r
+#include "src/lib/doslib/vgagui.h"\r
+#include "src/lib/doslib/vgatty.h"\r
+#include "src/lib/doslib/adlib.h"\r
+\r
+/* one per OPL channel */\r
+struct midi_note {\r
+ unsigned char note_number;\r
+ unsigned char note_velocity;\r
+ unsigned char note_track; /* from what MIDI track */\r
+ unsigned char note_channel; /* from what MIDI channel */\r
+ unsigned int busy:1; /* if occupied */\r
+};\r
+\r
+struct midi_channel {\r
+ unsigned char program;\r
+};\r
+\r
+struct midi_track {\r
+ /* track data, raw */\r
+ unsigned char* raw; /* raw data base */\r
+ unsigned char* fence; /* raw data end (last byte + 1) */\r
+ unsigned char* read; /* raw data read ptr */\r
+ /* state */\r
+ unsigned long us_per_quarter_note; /* Microseconds per quarter note (def 120 BPM) */\r
+ unsigned long us_tick_cnt_mtpq; /* Microseconds advanced (up to 10000 us or one unit at 100Hz) x ticks per quarter note */\r
+ unsigned long wait;\r
+ unsigned char last_status; /* MIDI last status byte */\r
+ unsigned int eof:1; /* we hit the end of the track */\r
+};\r
+\r
+#define MIDI_MAX_CHANNELS 16\r
+#define MIDI_MAX_TRACKS 64\r
+\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
+static unsigned int midi_trk_count=0;\r
+static volatile unsigned char midi_playing=0;\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
+static void (interrupt *old_irq0)();\r
+static volatile unsigned long irq0_ticks=0;\r
+static volatile unsigned int irq0_cnt=0,irq0_add=0,irq0_max=0;\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
+// fprintf(stderr,"Type 0x7F len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\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
+// fprintf(stderr,"Type 0x%02x len=%lu %p/%p/%p\n",c,d,t->raw,t->read,t->fence);\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
+// fprintf(stderr,"Sysex len=%lu %p/%p/%p\n",len,t->raw,t->read,t->fence);\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
+\r
+int main(int argc,char **argv) {\r
+ unsigned long ptick;\r
+ int i,c;\r
+\r
+ printf("ADLIB FM test program\n");\r
+ if (argc < 2) {\r
+ printf("You must specify a MIDI file to play\n");\r
+ return 1;\r
+ }\r
+\r
+ if (!probe_vga()) {\r
+ printf("Cannot init VGA\n");\r
+ return 1;\r
+ }\r
+ if (!init_adlib()) {\r
+ printf("Cannot init library\n");\r
+ return 1;\r
+ }\r
+ if (!probe_8254()) { /* we need the timer to keep time with the music */\r
+ printf("8254 timer not found\n");\r
+ return 1;\r
+ }\r
+\r
+ for (i=0;i < MIDI_MAX_TRACKS;i++) {\r
+ midi_trk[i].raw = NULL;\r
+ midi_trk[i].read = NULL;\r
+ midi_trk[i].fence = NULL;\r
+ }\r
+\r
+ if (load_midi_file(argv[1]) == 0) {\r
+ printf("Failed to load MIDI\n");\r
+ return 1;\r
+ }\r
+\r
+ write_8254_system_timer(T8254_REF_CLOCK_HZ / 100); /* tick faster at 100Hz please */\r
+ irq0_cnt = 0;\r
+ irq0_add = 182;\r
+ irq0_max = 1000; /* about 18.2Hz */\r
+ old_irq0 = _dos_getvect(8);/*IRQ0*/\r
+ _dos_setvect(8,irq0);\r
+\r
+ adlib_shut_up();\r
+ midi_reset_channels();\r
+ midi_reset_tracks();\r
+ _cli();\r
+ irq0_ticks = ptick = 0;\r
+ _sti();\r
+ midi_playing = 1;\r
+\r
+ while (1) {\r
+ unsigned long adv;\r
+\r
+ _cli();\r
+ adv = irq0_ticks - ptick;\r
+ if (adv >= 100UL) adv = 100UL;\r
+ ptick = irq0_ticks;\r
+ _sti();\r
+\r
+ while (adv != 0) {\r
+ midi_tick();\r
+ adv--;\r
+ }\r
+\r
+ if (kbhit()) {\r
+ c = getch();\r
+ if (c == 0) c = getch() << 8;\r
+\r
+ if (c == 27) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ midi_playing = 0;\r
+ adlib_shut_up();\r
+ shutdown_adlib();\r
+ _dos_setvect(8,old_irq0);\r
+ write_8254_system_timer(0); /* back to normal 18.2Hz */\r
+\r
+ for (i=0;i < MIDI_MAX_TRACKS;i++) {\r
+ if (midi_trk[i].raw) {\r
+#if TARGET_MSDOS == 16 && (defined(__LARGE__) || defined(__COMPACT__))\r
+ _dos_freemem(FP_SEG(midi_trk[i].raw)); /* NTS: Because we allocated with _dos_allocmem */\r
+#else\r
+ free(midi_trk[i].raw);\r
+#endif\r
+ midi_trk[i].raw = NULL;\r
+ }\r
+ midi_trk[i].fence = NULL;\r
+ midi_trk[i].read = NULL;\r
+ }\r
+\r
+ return 0;\r
+}\r