From c4337307969449556e1c8841d763bed971c30f7c Mon Sep 17 00:00:00 2001 From: sparky4 Date: Wed, 15 Jul 2015 16:06:49 -0500 Subject: [PATCH] added some files~ modified: sountest.exe new file: src/midi.c modified: src/sountest.c new file: src/testsnd.c --- sountest.exe | Bin 68097 -> 68114 bytes src/midi.c | 870 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sountest.c | 1 + src/testsnd.c | 450 +++++++++++++++++++++++++ 4 files changed, 1321 insertions(+) create mode 100644 src/midi.c create mode 100644 src/testsnd.c diff --git a/sountest.exe b/sountest.exe index 74c09fffec4196d55cf981d3172fc2627b05f2f6..7429f02c02c7614a3ec93471cc68585872548641 100644 GIT binary patch delta 14677 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

(bIS8@TQ^? zvvV|RSQ;ki(_@0q&OtBcW~Zj)B4P*q;i6@0$tZm^+ZW_&a?(>X5$8s)TvS?>H^*gY zg2$$^J=vR4RDMa>+9X+@YGA5{%_04hMk6wcMqRSQDl`0&Hy1%?UJ8g}>*qjWXF4oa zx8KvR)ZH^jCw`>T*?kJTH`w7YJNopJ3#X>mb!;tYU*~VhvP%?}HX^o_>y0MVv6nQf z>)6xrnYwm;W{+!fL1yz36s6CRrO#wbP;%-RBJTX2whwxtCnEFq-z#YtGC$F^m%X{)>DkKx+zrZIvEepQ+biCjA?<#}SM<+kH1~=x zH;;aM#oLzVG-Z~lThGJ}O)a@|#g8ka=2zQtU(*g(-MEc(O^q|X?qSuZ zDLOHcwOyT!gvV*`>&#IF*S)wvy7GEkvrqAZX<^0B(_|&VVkfjrK>|L+Ucg7dHRH=n(5V-Y2HnDPDa1J>A^+Q zBR4&{#qFpiPX<5*guXRoZUCIpbr~lXp}1*b<0Zm34Z6Tw}>7+E-zOT zospH7$>xWz>AYJ(zU%Q5Z{6mH4caj|Sp|q{Q3|^$y>X>|+so$5mbf0Nn%I7QbdP%5>F(QZzJho9#^8wp4>WoYuS@7$7`rrBm;<;nve-_mp<0V65+jjAX%qcn)lG-no13K5M5n z8H~=##ldM3n`bwEqzr^^+cX+=zctUE**o56j|=Mij;*ar-b#`r+)g@O>x>^ipqtrK6kG1a7n^R1y;@hU zmR{HTSkz5#Dla3KZK4QYNA2squ>AC>cjQLVG4JrBA0ociRFd5T7tVurdf>_Rq+=i0b467%AADyJQpCC7Emu#{ z83&x$zzZLDgd%d0LnxYu@QQpKY;3q}*x(oa7*?YmTaC$9;7C&9gdgf}B^PmM;S5Xm z!ATTSKXqlP54OQcpy&o{aL^=toDhnLF^Uq2ob2%am79i_UY_JJY}lY>AE;qd=Yuy_ zB0qZNv7<>m&SP-44(9QAVM2}mYCDXb(cp!Ph=usa0I8GkjsLEY>QzMdFC=<3z5;=W zoc3e+qVgm_CPBz*rV^xAXhDdKnmw`fjKdxsMmQEBwXDm2p1q+2Xm_AW*OR4V@M_1t#g>**g2Uv{I50EaWF_^0d;gou!P%SRXgj}ZUd33@wX-x4caGcU)sV+EMgoTskb3A*U1W3Lm< z{h~A1Wn>%7?1G({?t^lO&Uamfc;QtiDL``+tVs)=DX|-}d*+B|?#O43uGcWw2w4KA z888R37PI6!f0*l%SQ#D+VPzH2rOzBg*wZv-G88#mNWxs|+nj{O={~wzmb7On=hH_h z@>i;O?#P~xpS#*uOaIL|>QH8ViDtr1OUn`Vrj-a4wD!59NgT!>j_SQJmRZU4&2uF- zzcw!%!-UN*d4Sm2sMl9g*2OI!oj*q6nN2JbUi6aFh+M-gY4!^x_YIx%!V!DcO$Zz4 z&j_c|hX^C6{BK7SH8yW}69rxgrH23Q$c4~&gePdBATRw}>BF{ogq#EKn!Um_x{uBm zq0y`c_*oxP{l4I(BlcxZ2n}iHmyT>lmh{pAJF?=JjwWnJhX0ZQ4y+4kCQ{Hza2+Mo zh2`ce#6Et*mTdW3M=aUCZ(X^sX#clLletq_9&J$WAfYaWZT*$d~;6v$!uO + * + * Compiles for intended target environments: + * - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] + */ + +#include +#include /* this is where Open Watcom hides the outp() etc. functions */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/sountest.c b/src/sountest.c index e8574425..8fd5b025 100644 --- a/src/sountest.c +++ b/src/sountest.c @@ -131,5 +131,6 @@ void main(int argc, char near *argv[]) adlib_update_groupA0(0,&adlib_fm[0]); } printf("!\n"); + shutdown_adlib(); IN_Shutdown(); } diff --git a/src/testsnd.c b/src/testsnd.c new file mode 100644 index 00000000..4c5f6534 --- /dev/null +++ b/src/testsnd.c @@ -0,0 +1,450 @@ +/* test.c + * + * Adlib OPL2/OPL3 FM synthesizer chipset test program. + * (C) 2010-2012 Jonathan Campbell. + * Hackipedia DOS library. + * + * This code is licensed under the LGPL. + * + * + * Compiles for intended target environments: + * - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] + * + * This test program uses a "text user interface" to allow you to play + * with the OPL2/OPL3 chipset and it's parameters. Some "instruments" + * are preset for you if you want to make noise faster. + */ + +#include +#include /* this is where Open Watcom hides the outp() etc. functions */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* 8254 timer */ +#include +#include +#include + +static unsigned int musical_scale[18] = { + 0x1B0, /* E */ + 0x1CA, /* F */ + 0x1E5, /* f# */ + 0x202, /* G */ + 0x220, /* G# */ + 0x241, /* A */ + 0x263, /* A# */ + 0x287, /* B */ + 0x2AE, /* C */ + + 0x2B0, /* E */ + 0x2CA, /* F */ + 0x2E5, /* f# */ + 0x302, /* G */ + 0x320, /* G# */ + 0x341, /* A */ + 0x363, /* A# */ + 0x387, /* B */ + 0x3AE, /* C */ +}; + +int main(int argc,char **argv) { + int i,loop,redraw,c,cc,selector=0,redrawln=0,hselect=0,selectsub=0; + VGA_ALPHA_PTR vga; + char tmp[128]; + + printf("ADLIB FM test program\n"); + + if (!probe_vga()) { + printf("Cannot init VGA\n"); + return 1; + } + if (!init_adlib()) { + printf("Cannot init library\n"); + return 1; + } + + int10_setmode(3); + + /* for VGA: free up space if needed by going to 80x50 */ + if (adlib_fm_voices > 9) + vga_bios_set_80x50_text(); + + 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; + + f = &adlib_fm[i].mod; + f->mod_multiple = 1; + f->total_level = 63 - 16; + f->attack_rate = 15; + f->decay_rate = 0; + f->sustain_level = 7; + f->release_rate = 7; + f->f_number = musical_scale[i%18]; + 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 = 0; + f->sustain_level = 7; + f->release_rate = 7; + f->f_number = 0; + f->octave = 0; + f->key_on = 0; + } + + adlib_apply_all(); + + vga_write_color(0x07); + vga_clear(); + + loop=1; + redraw=1; + while (loop) { + if (redraw || redrawln) { + if (redraw) { + for (vga=vga_alpha_ram,cc=0;cc < (80*vga_height);cc++) *vga++ = 0x1E00 | 177; + vga_moveto(0,0); + vga_write_color(0x1F); + sprintf(tmp,"Adlib FM, %u-voice %s. Use Z & X to adj F10=PRESET F1=QUIET ",adlib_fm_voices, + (adlib_flags & ADLIB_FM_OPL3) ? "OPL3" : + (adlib_flags & ADLIB_FM_DUAL_OPL2) ? "Dual OPL2" : "OPL2"); + vga_write(tmp); + if (adlib_flags & ADLIB_FM_OPL3) vga_write("F2=OPL3 off "); + } + + if (redrawln || redraw) { + struct adlib_reg_bd *bd = &adlib_reg_bd; + static const char *hsel_str[18] = { + "Amplitude modulatation", + "Vibrato", + "Sustain", + "Key scaling rate", + "Modulator frequency multiple", + "Level key scaling", + "Total level", + "Attack rate", + "Decay rate", + "Sustain level", + "Release rate", + "KEY ON", + "Octave", + "F-Number", + "Feedback", + "Connection (operator 1 -> operator 2)", + "Waveform", + "Channel mapping (OPL3)" + }; + + vga_write_color(0x1A); + + vga_moveto(0,2); + sprintf(tmp,"AM=%u VB=%u RYTHM=%u BAS=%u SNA=%u TOM=%u CYM=%u HI=%u\n", + bd->am_depth, + bd->vibrato_depth, + bd->rythm_enable, + bd->bass_drum_on, + bd->snare_drum_on, + bd->tom_tom_on, + bd->cymbal_on, + bd->hi_hat_on); + vga_write(tmp); + + vga_moveto(0,3); + vga_write(" "); + vga_moveto(0,3); + vga_write(hsel_str[hselect]); + + vga_moveto(0,4); + vga_write_color(hselect == 0 ? 0x70 : 0x1E); vga_write("AM "); + vga_write_color(hselect == 1 ? 0x70 : 0x1E); vga_write("VB "); + vga_write_color(hselect == 2 ? 0x70 : 0x1E); vga_write("SUST "); + vga_write_color(hselect == 3 ? 0x70 : 0x1E); vga_write("KSR "); + vga_write_color(hselect == 4 ? 0x70 : 0x1E); vga_write("MMUL "); + vga_write_color(hselect == 5 ? 0x70 : 0x1E); vga_write("LKS "); + vga_write_color(hselect == 6 ? 0x70 : 0x1E); vga_write("TL "); + vga_write_color(hselect == 7 ? 0x70 : 0x1E); vga_write("AR "); + vga_write_color(hselect == 8 ? 0x70 : 0x1E); vga_write("DR "); + vga_write_color(hselect == 9 ? 0x70 : 0x1E); vga_write("SL "); + vga_write_color(hselect == 10 ? 0x70 : 0x1E); vga_write("RR "); + vga_write_color(hselect == 11 ? 0x70 : 0x1E); vga_write("KEY "); + vga_write_color(hselect == 12 ? 0x70 : 0x1E); vga_write("OCT "); + vga_write_color(hselect == 13 ? 0x70 : 0x1E); vga_write("FNUM "); + vga_write_color(hselect == 14 ? 0x70 : 0x1E); vga_write("FEED "); + vga_write_color(hselect == 15 ? 0x70 : 0x1E); vga_write("CON "); + vga_write_color(hselect == 16 ? 0x70 : 0x1E); vga_write("WV "); + vga_write_color(hselect == 17 ? 0x70 : 0x1E); vga_write("ABCD "); + + for (i=0;i < adlib_fm_voices;i++) { + struct adlib_fm_operator *f; + double freq; + + f = &adlib_fm[i].mod; + vga_moveto(0,5+i*2); + freq = adlib_fm_op_to_freq(f); + vga_write_color(i == selector && selectsub == 0 ? 0x70 : 0x1E); + cc = sprintf(tmp,"%u %u %u %u %-2u %u %-2u %-2u %-2u %-2u %-2u %u %u %-4u %u %u %u %c%c%c%c %u %.1fHz ", + f->am, f->vibrato, f->sustain, f->key_scaling_rate, + f->mod_multiple, f->level_key_scale, f->total_level, f->attack_rate, + f->decay_rate, f->sustain_level, f->release_rate, f->key_on, + f->octave, f->f_number, f->feedback, f->connection, + f->waveform, f->ch_a?'*':'-', f->ch_b?'*':'-', f->ch_c?'*':'-', + f->ch_d?'*':'-', i+1, freq); + vga_write(tmp); + + f = &adlib_fm[i].car; + vga_moveto(0,5+i*2+1); + vga_write_color(i == selector && selectsub == 1 ? 0x70 : 0x1E); + cc = sprintf(tmp,"%u %u %u %u %-2u %u %-2u %-2u %-2u %-2u %-2u %u CAR ", + f->am, f->vibrato, f->sustain, f->key_scaling_rate, + f->mod_multiple, f->level_key_scale, f->total_level, f->attack_rate, + f->decay_rate, f->sustain_level, f->release_rate, f->waveform); + vga_write(tmp); + } + } + + redrawln = 0; + redraw = 0; + } + + if (kbhit()) { + c = getch(); + if (c == 0) c = getch() << 8; + + if (c == 27) { + loop = 0; + } + else if (c == 0x3B00) { /* F1 */ + for (i=0;i < adlib_fm_voices;i++) { + adlib_fm[i].mod.key_on = 0; + adlib_fm[i].car.key_on = 0; + adlib_update_groupA0(i,&adlib_fm[i]); + } + redrawln = 1; + } + else if (c == 0x3C00) { /* F2 */ + if (adlib_flags & ADLIB_FM_OPL3) { + shutdown_adlib_opl3(); + int10_setmode(3); + redraw = 1; + } + } + else if (c == 0x4400) { /* F10 */ + unsigned short op = adlib_voice_to_op[selector]; + + vga_write_color(0x07); + vga_clear(); + vga_moveto(0,0); + + vga_write("Choose an instrument to load into the channel:\n"); + vga_write(" 1. Violin 2. Piano 3. Harpsichord 4. Horn 5. Deep bass drum\n"); + vga_write(" 6. Small drum \n"); + vga_write_sync(); + + c = getch(); + + if (c == '1') + adlib_fm[selector] = + (adlib_flags & ADLIB_FM_OPL3 ? + adlib_fm_preset_violin_opl3 : adlib_fm_preset_violin_opl2); + else if (c == '2') + adlib_fm[selector] = adlib_fm_preset_piano; + else if (c == '3') + adlib_fm[selector] = adlib_fm_preset_harpsichord; + else if (c == '4') + adlib_fm[selector] = adlib_fm_preset_horn; + else if (c == '5') + adlib_fm[selector] = adlib_fm_preset_deep_bass_drum; + else if (c == '6') + adlib_fm[selector] = adlib_fm_preset_small_drum; + + adlib_update_groupA0(selector,&adlib_fm[selector]); + adlib_update_groupC0(selector,&adlib_fm[selector]); + adlib_update_operator(op,&adlib_fm[selector].mod); + adlib_update_operator(op+3,&adlib_fm[selector].car); + + redraw = 1; + } + else if (c == ' ') { + adlib_fm[selector].mod.key_on ^= 1; + adlib_update_groupA0(selector,&adlib_fm[selector]); + redrawln=1; + } + else if (c == 'a') { + if (hselect == 17) { + struct adlib_fm_operator *f = &adlib_fm[selector].mod; f->ch_a ^= 1; + adlib_update_groupC0(selector,&adlib_fm[selector]); + } + else { + adlib_reg_bd.am_depth ^= 1; + adlib_update_bd(&adlib_reg_bd); + } + redrawln = 1; + } + else if (c == 'v') { + adlib_reg_bd.vibrato_depth ^= 1; + adlib_update_bd(&adlib_reg_bd); + redrawln = 1; + } + else if (c == 'r') { + adlib_reg_bd.rythm_enable ^= 1; + adlib_update_bd(&adlib_reg_bd); + redrawln = 1; + } + else if (c == 'b') { + if (hselect == 17) { + struct adlib_fm_operator *f = &adlib_fm[selector].mod; f->ch_b ^= 1; + adlib_update_groupC0(selector,&adlib_fm[selector]); + } + else { + adlib_reg_bd.bass_drum_on ^= 1; + adlib_update_bd(&adlib_reg_bd); + } + redrawln = 1; + } + else if (c == 's') { + adlib_reg_bd.snare_drum_on ^= 1; + adlib_update_bd(&adlib_reg_bd); + redrawln = 1; + } + else if (c == 't') { + adlib_reg_bd.tom_tom_on ^= 1; + adlib_update_bd(&adlib_reg_bd); + redrawln = 1; + } + else if (c == 'c') { + if (hselect == 17) { + struct adlib_fm_operator *f = &adlib_fm[selector].mod; f->ch_c ^= 1; + adlib_update_groupC0(selector,&adlib_fm[selector]); + } + else { + adlib_reg_bd.cymbal_on ^= 1; + adlib_update_bd(&adlib_reg_bd); + } + redrawln = 1; + } + else if (c == 'd') { + if (hselect == 17) { + struct adlib_fm_operator *f = &adlib_fm[selector].mod; f->ch_d ^= 1; + adlib_update_groupC0(selector,&adlib_fm[selector]); + } + else { + } + redrawln = 1; + } + else if (c == 'h') { + adlib_reg_bd.hi_hat_on ^= 1; + adlib_update_bd(&adlib_reg_bd); + redrawln = 1; + } + else if (c == 'z' || c == 'Z' || c == 'x' || c == 'X') { + struct adlib_fm_operator *f; + int dec = tolower(c) == 'z'; + unsigned short operator; + + switch (hselect) { + case 11:selectsub = 0; + break; + } + + if (selectsub) f = &adlib_fm[selector].car; + else f = &adlib_fm[selector].mod; + operator = adlib_voice_to_op[selector] + (selectsub*3); + + switch (hselect) { + case 0: f->am ^= 1; adlib_update_group20(operator,f); break; + case 11: f->key_on ^= 1; adlib_update_groupA0(selector,&adlib_fm[selector]); break; + case 1: f->vibrato ^= 1; adlib_update_group20(operator,f); break; + case 2: f->sustain ^= 1; adlib_update_group20(operator,f); break; + case 15: f->connection ^= 1; adlib_update_group20(operator,f); break; + case 3: f->key_scaling_rate ^= 1; adlib_update_group20(operator,f); break; + + case 4: if (dec) f->mod_multiple--; else f->mod_multiple++; + adlib_update_group20(operator,f); break; + case 5: if (dec) f->level_key_scale--; else f->level_key_scale++; + adlib_update_group40(operator,f); break; + case 6: if (dec) f->total_level--; else f->total_level++; + adlib_update_group40(operator,f); break; + case 7: if (dec) f->attack_rate--; else f->attack_rate++; + adlib_update_group60(operator,f); break; + case 8: if (dec) f->decay_rate--; else f->decay_rate++; + adlib_update_group60(operator,f); break; + case 9: if (dec) f->sustain_level--; else f->sustain_level++; + adlib_update_group80(operator,f); break; + case 10: if (dec) f->release_rate--; else f->release_rate++; + adlib_update_group80(operator,f); break; + case 12: if (dec) f->octave--; else f->octave++; + adlib_update_groupA0(selector,&adlib_fm[selector]); break; + case 13: if (dec) f->f_number--; else f->f_number++; + adlib_update_groupA0(selector,&adlib_fm[selector]); break; + case 14: if (dec) f->feedback--; else f->feedback++; + adlib_update_groupC0(selector,&adlib_fm[selector]); break; + case 16: if (dec) f->waveform--; else f->waveform++; + adlib_update_groupE0(operator,f); break; + }; + + redrawln=1; + } + else if (c == 0x4800) { + if (selectsub && !(hselect >= 11 && hselect <= 15)) { + selectsub = 0; + redrawln = 1; + } + else if (selector > 0) { + selectsub = !(hselect >= 11 && hselect <= 15); + selector--; + redrawln = 1; + } + } + else if (c == 0x4B00) { + if (hselect > 0) { + hselect--; + redrawln=1; + } + } + else if (c == 0x4D00) { + if (hselect < 17) { + hselect++; + redrawln=1; + } + } + else if (c == 0x5000) { + if (selectsub == 0 && !(hselect >= 11 && hselect <= 15)) { + selectsub = 1; + redrawln = 1; + } + else if ((selector+1) < adlib_fm_voices) { + selectsub = 0; + selector++; + redrawln=1; + } + } + + } + } + + shutdown_adlib(); + int10_setmode(3); + + return 0; +} + -- 2.39.2