home *** CD-ROM | disk | FTP | other *** search
/ Action Ware 12: Heretic & Hexen / actionware12.iso / acware12 / sounds / mus_play / musplay.c < prev    next >
C/C++ Source or Header  |  1994-10-30  |  21KB  |  909 lines

  1. /*
  2.  *    Name:        MUS File Player
  3.  *    Version:    1.50
  4.  *    Author:        Vladimir Arnost (QA-Software)
  5.  *    Last revision:    Oct-30-1994
  6.  *    Compiler:    Borland C++ 3.1
  7.  *
  8.  */
  9.  
  10. /*
  11.  * Revision History:
  12.  *
  13.  *    Aug-8-1994    V1.00    V.Arnost
  14.  *        Written from scratch
  15.  *    Aug-9-1994    V1.10    V.Arnost
  16.  *        Some minor changes to improve sound quality. Tried to add
  17.  *        stereo sound capabilities, but failed to -- my SB Pro refuses
  18.  *        to switch to stereo mode.
  19.  *    Aug-13-1994    V1.20    V.Arnost
  20.  *        Stereo sound fixed. Now works also with Sound Blaster Pro II
  21.  *        (chip OPL3 -- gives 18 "stereo" (ahem) channels).
  22.  *        Changed code to handle properly notes without volume.
  23.  *        (Uses previous volume on given channel.)
  24.  *        Added cyclic channel usage to avoid annoying clicking noise.
  25.  *    Aug-17-1994    V1.30    V.Arnost
  26.  *        Completely rewritten time synchronization. Now the player runs
  27.  *        on IRQ 8 (RTC Clock - 1024 Hz).
  28.  *    Aug-28-1994    V1.40    V.Arnost
  29.  *        Added Adlib and SB Pro II detection.
  30.  *        Fixed bug that caused high part of 32-bit registers (EAX,EBX...)
  31.  *        to be corrupted.
  32.  *    Oct-30-1994    V1.50    V.Arnost
  33.  *        Tidied up the source code
  34.  *        Added C key - invoke COMMAND.COM
  35.  *        Added RTC timer interrupt flag check (0000:04A0)
  36.  *        Added BLASTER environment variable parsing
  37.  *        FIRST PUBLIC RELEASE
  38.  */
  39.  
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <malloc.h>
  43. #include <string.h>
  44. #include <mem.h>
  45. #include <bios.h>
  46. #include <dos.h>
  47. #include <conio.h>
  48. #include "adlib.h"
  49.  
  50. #define VERSION       "1.50"
  51. #define COPYRIGHT  "MUS File Player  Version "VERSION"  (c) 1994 QA-Software"
  52.  
  53. /* Global type declarations */
  54.  
  55. #ifdef __386__
  56.  typedef unsigned long DWORD;
  57. #else
  58.  typedef unsigned char BYTE;
  59.  typedef unsigned int  WORD;
  60.  typedef unsigned long DWORD;
  61. #endif
  62.  
  63. #if __BORLANDC__ >= 0x300    // BC 3.1: in you want to compile in 386 mode,
  64. #define FASTCALL _fastcall   // undef this #define
  65. #else
  66. #define FASTCALL
  67. #endif
  68.  
  69. //#define DEBUG            // turn on debugging messages
  70. //#define NOIRQ            // turn on non-interrupt-driven player
  71.  
  72. #define CHANNELS 16        // total channels 0..CHANNELS-1
  73. #define PERCUSSION 15        // percussion channel
  74.  
  75. #define INSTRSIZE 36        // instrument size (in bytes)
  76.  
  77. struct MUSheader {
  78.     char    ID[4];        // identifier "MUS" 0x1A
  79.     WORD    scoreLen;
  80.     WORD    scoreStart;
  81.     WORD    channels;
  82.     WORD    dummy1;
  83.     WORD    instrCnt;
  84.     WORD    dummy2;
  85. //    WORD    instruments[];
  86. } header;
  87.  
  88. #ifdef DEBUG
  89.   #include "musinstr.c"            // instrument names (for debugging)
  90. #endif
  91.  
  92.  
  93. WORD    SBProPort = SBPROPORT;
  94. WORD    channelInstr[CHANNELS];        // instrument #
  95. BYTE    channelVolume[CHANNELS];    // volume
  96. BYTE    channelLastVolume[CHANNELS];    // last volume
  97. signed char channelPan[CHANNELS];    // pan, 0=normal
  98. signed char channelPitch[CHANNELS];    // pitch wheel, 0=normal
  99. DWORD    Adlibtime[OUTPUTCHANNELS];    // Adlib channel start time
  100. WORD    Adlibchannel[OUTPUTCHANNELS];    // Adlib channel & note #
  101. BYTE    Adlibnote[OUTPUTCHANNELS];    // Adlib channel note
  102. BYTE   *Adlibinstr[OUTPUTCHANNELS];    // Adlib channel instrument address
  103. BYTE    Adlibvolume[OUTPUTCHANNELS];    // Adlib channel volume
  104. signed char Adlibfinetune[OUTPUTCHANNELS];// Adlib 2nd channel pitch difference
  105. #ifdef NOIRQ
  106. DWORD    speed = 6832;
  107. #endif
  108.  
  109. void interrupt (*oldint70h)(void);
  110. WORD far *timerstack = NULL;
  111. WORD far *timerstackend = NULL;
  112. volatile WORD far *timersavestack = NULL;
  113. volatile DWORD    MUStime;
  114. volatile BYTE  *MUSdata;
  115. volatile DWORD    MUSticks;
  116. volatile WORD    playingAtOnce = 0;
  117. volatile WORD    playingPeak = 0;
  118. volatile WORD    playingChannels = 0;
  119.  
  120. BYTE   *score;
  121. BYTE   *instruments;
  122.  
  123. /* Command-line parameters */
  124. char   *musname = NULL;
  125. char   *instrname = "GENMIDI.OP2";
  126. int    help = 0;
  127. int    singlevoice = 0;
  128. int    stereo = 1;
  129. char   *execCmd = NULL;
  130. int    loopForever = 0;
  131.  
  132. int readMUS(FILE *f)
  133. {
  134.     if (fread(&header, sizeof(BYTE), sizeof header, f) != sizeof header)
  135.     {
  136.     puts("Unexpected end of file.");
  137.     return -1;
  138.     }
  139.  
  140.     if (header.ID[0] != 'M' ||
  141.     header.ID[1] != 'U' ||
  142.     header.ID[2] != 'S' ||
  143.     header.ID[3] != 0x1A)
  144.     {
  145.     puts("Bad file.");
  146.     return -1;
  147.     }
  148.  
  149.     fseek(f, header.scoreStart, SEEK_SET);
  150.  
  151.     if ( (score = malloc(header.scoreLen)) == NULL)
  152.     {
  153.     puts("Not enough memory.");
  154.     return -1;
  155.     }
  156.  
  157.     if (fread(score, sizeof(BYTE), header.scoreLen, f) != header.scoreLen)
  158.     {
  159.     puts("Unexpected end of file.");
  160.     return -1;
  161.     }
  162.  
  163.     return 0;
  164. }
  165.  
  166. int readINS(FILE *f)
  167. {
  168.     char hdr[8];
  169.     static char masterhdr[8] = {'#','O','P','L','_','I','I','#'};
  170.  
  171.     if (fread(&hdr, sizeof(BYTE), sizeof hdr, f) != sizeof hdr)
  172.     {
  173.     puts("Unexpected end of instrument file.");
  174.     return -1;
  175.     }
  176.     if (memcmp(hdr, masterhdr, sizeof hdr))
  177.     {
  178.     puts("Bad instrument file.");
  179.     return -1;
  180.     }
  181.     if ( (instruments = calloc(175, INSTRSIZE)) == NULL)
  182.     {
  183.     puts("Not enough memory.");
  184.     return -1;
  185.     }
  186.     if (fread(instruments, INSTRSIZE, 175, f) != 175)
  187.     {
  188.     puts("Unexpected end of instrument file.");
  189.     return -1;
  190.     }
  191.     return 0;
  192. }
  193.  
  194. static WORD freqtable[] = {
  195.     172, 183, 194, 205, 217, 230, 244, 258, 274, 290, 307, 326,
  196.     345, 365, 387, 410, 435, 460, 488, 517, 547, 580, 615, 651,
  197.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  198.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  199.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  200.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  201.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  202.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  203.     690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651,
  204.     690, 731, 774, 820, 869, 921, 975, 1023, 1023, 1023, 1023, 1023,
  205.     1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023};
  206. static char octavetable[] = {
  207.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  208.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  209.     0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
  210.     1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
  211.     2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
  212.     3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
  213.     4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
  214.     5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
  215.     6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7,
  216.     7, 7, 7, 7, 7, 7, 7, -1, -1, -1, -1, -1,
  217.     -1, -1, -1, -1, -1, -1, -1, -1};
  218.  
  219. void WriteFrequency(BYTE Adlibchannel, WORD note, int pitch, BYTE keyOn)
  220. {
  221.     WORD freq = freqtable[note];
  222.     WORD octave = octavetable[note];
  223.  
  224.     if (pitch > 0)
  225.     {
  226.     int freq2;
  227.     int octave2 = octavetable[note+1] - octave;
  228.  
  229. #ifdef DEBUG
  230.     printf("DEBUG: pitch: N: %d  P: %d\n", note, pitch);
  231. #endif
  232.  
  233.     if (octave2)
  234.     {
  235.         octave++;
  236.         freq >>= 1;
  237.     }
  238.     freq2 = freqtable[note+1] - freq;
  239.     freq += (freq2 * pitch) / 64;
  240.     } else
  241.     if (pitch < 0)
  242.     {
  243.     int freq2;
  244.     int octave2 = octave - octavetable[note-1];
  245.  
  246. #ifdef DEBUG
  247.     printf("DEBUG: pitch: N: %d  P: %d\n", note, pitch);
  248. #endif
  249.  
  250.     if (octave2)
  251.     {
  252.         octave--;
  253.         freq <<= 1;
  254.     }
  255.     freq2 = freq - freqtable[note-1];
  256.     freq -= (freq2 * -pitch) / 64;
  257.     }
  258.     WriteFreq(Adlibchannel, freq, octave, keyOn);
  259. }
  260.  
  261. int OccupyChannel(WORD i, WORD channel, BYTE note, int volume, BYTE *instr,
  262.           WORD flag)
  263. {
  264.     playingChannels++;
  265.     Adlibchannel[i] = (channel << 8) | note | (flag << 15);
  266.     Adlibtime[i] = MUStime;
  267.     if (volume == -1)
  268.     volume = channelLastVolume[channel];
  269.     else
  270.     channelLastVolume[channel] = volume;
  271.     volume = channelVolume[channel] * (Adlibvolume[i] = volume) / 127;
  272.     if (instr[0] & 1)
  273.     note = instr[3];
  274.     else if (channel == PERCUSSION)
  275.     note = 60;            // C-5
  276.     if (flag && (instr[0] & 4))
  277.     Adlibfinetune[i] = instr[2] - 0x80;
  278.     else
  279.     Adlibfinetune[i] = 0;
  280.     if (flag)
  281.     instr += 16+4;
  282.     else
  283.     instr += 4;
  284.     WriteInstrument(i, Adlibinstr[i] = instr);
  285.     WritePan(i, instr, channelPan[channel]);
  286.     WriteVolume(i, instr, volume);
  287.     Adlibnote[i] = note += instr[14] + 12;
  288.     WriteFrequency(i, note, Adlibfinetune[i]+channelPitch[channel], 1);
  289.     return i;
  290. }
  291.  
  292. int ReleaseChannel(WORD i, WORD killed)
  293. {
  294.     WORD channel = (Adlibchannel[i] >> 8) & 0x7F;
  295. #ifdef DEBUG
  296.     printf("\nDEBUG: Release  Ch: %d  Adl: %d  %04X\n", channel, i, Adlibchannel[i]);
  297. #endif
  298.     playingChannels--;
  299.     WriteFrequency(i, Adlibnote[i], Adlibfinetune[i]+channelPitch[channel], 0);
  300.     Adlibchannel[i] = 0xFFFF;
  301.     if (killed)
  302.     {
  303.     WriteChannel(0x80, i, 0x0F, 0x0F);    // release rate - fastest
  304.     WriteChannel(0x40, i, 0x3F, 0x3F);    // no volume
  305.     }
  306.     return i;
  307. }
  308.  
  309. int FindFreeChannel(WORD flag)
  310. {
  311.     static WORD last = 0xFFFF;
  312.     WORD i;
  313.     WORD latest = 0xFFFF;
  314.     DWORD latesttime = MUStime;
  315.  
  316.     for(i = 0; i < AdlibChannels; i++)
  317.     {
  318.     if (++last == AdlibChannels)
  319.         last = 0;
  320.     if (Adlibchannel[last] == 0xFFFF)
  321.         return last;
  322.     }
  323.  
  324.     if (flag & 1)
  325.     return -1;
  326.  
  327.     for(i = 0; i < AdlibChannels; i++)
  328.     {
  329.     if ((Adlibchannel[i] & 0x8000))
  330.     {
  331. #ifdef DEBUG
  332.         printf("\nDEBUG: Kill 2nd %04X\n", Adlibchannel[i]);
  333. #endif
  334.         ReleaseChannel(i, -1);
  335.         return i;
  336.     } else
  337.         if (Adlibtime[i] < latesttime)
  338.         {
  339.         latesttime = Adlibtime[i];
  340.         latest = i;
  341.         }
  342.     }
  343.  
  344.     if ( !(flag & 2) && latest != 0xFFFF)
  345.     {
  346. #ifdef DEBUG
  347.     printf("DEBUG: Kill %04X !!!\n", Adlibchannel[latest]);
  348. #endif
  349.     ReleaseChannel(latest, -1);
  350.     return latest;
  351.     }
  352.  
  353. #ifdef DEBUG
  354.     printf("DEBUG: Full!!!\n");
  355. #endif
  356.     return -1;
  357. }
  358.  
  359. // code 1: play note
  360. void playNote(WORD channel, BYTE note, int volume)
  361. {
  362.     int i; // orignote = note;
  363.     BYTE *instr, instrnumber;
  364.  
  365.     if (channel == PERCUSSION)
  366.     {
  367.     if (note < 35 || note > 81)
  368.         return;            // wrong percussion number
  369.     instrnumber = (128-35) + note;
  370.     } else
  371.     instrnumber = channelInstr[channel];
  372.     instr = &instruments[instrnumber*INSTRSIZE];
  373. #ifdef DEBUG
  374.     cprintf("\rDEBUG: play: Ch: %d  N: %d  V: %d (%d)  I: %d (%s)  Fi: %d\r\n",
  375.     channel, note, volume, channelVolume[channel], instrnumber, instrumentName[instrnumber],
  376.     (instr[0] & 4) ? (instr[2] - 0x80) : 0);
  377. #endif
  378.  
  379.     if ( (i = FindFreeChannel((channel == PERCUSSION) ? 2 : 0)) != -1)
  380.     {
  381.     OccupyChannel(i, channel, note, volume, instr, 0);
  382.     if (!singlevoice && instr[0] == 4)
  383.     {
  384.         if ( (i = FindFreeChannel((channel == PERCUSSION) ? 3 : 1)) != -1)
  385.         OccupyChannel(i, channel, note, volume, instr, 1);
  386.     }
  387.     }
  388. }
  389.  
  390. // code 0: release note
  391. void releaseNote(WORD channel, BYTE note)
  392. {
  393.     WORD i;
  394.  
  395. #ifdef DEBUG
  396.     printf("DEBUG: release: Ch: %d  N: %d\n", channel, note);
  397. #endif
  398.     channel = (channel << 8) | note;
  399.     for(i = 0; i < AdlibChannels; i++)
  400.     if ((Adlibchannel[i] & 0x7FFF) == channel)
  401.     {
  402.         ReleaseChannel(i, 0);
  403. //        return;
  404.     }
  405. }
  406.  
  407. // code 2: change pitch wheel (bender)
  408. void pitchWheel(WORD channel, int pitch)
  409. {
  410.     WORD i;
  411.  
  412. #ifdef DEBUG
  413.     printf("DEBUG: pitch: Ch: %d  P: %d\n", channel, pitch);
  414. #endif
  415.     channelPitch[channel] = pitch;
  416.     for(i = 0; i < AdlibChannels; i++)
  417.     if (((Adlibchannel[i] >> 8) & 0x7F) == channel)
  418.     {
  419.         Adlibtime[i] = MUStime;
  420.         WriteFrequency(i, Adlibnote[i], Adlibfinetune[i]+pitch, 1);
  421.     }
  422. }
  423.  
  424. // code 4: change control
  425. void changeControl(WORD channel, BYTE controller, int value)
  426. {
  427.     WORD i;
  428. #ifdef DEBUG
  429.     printf("DEBUG: ctrl: Ch: %d  C: %d  V: %d\n", channel, controller, value);
  430. #endif
  431.  
  432.     switch (controller) {
  433.     case 0:    // change instrument
  434.         channelInstr[channel] = value;
  435.         break;
  436.     case 3:    // change volume
  437.         channelVolume[channel] = value;
  438.         for(i = 0; i < AdlibChannels; i++)
  439.         if (((Adlibchannel[i] >> 8) & 0x7F) == channel)
  440.         {
  441.             Adlibtime[i] = MUStime;
  442.             WriteVolume(i, Adlibinstr[i], value * Adlibvolume[i] / 127);
  443.         }
  444.         break;
  445.     case 4:    // change pan (balance)
  446.         channelPan[channel] = value -= 64;
  447.         for(i = 0; i < AdlibChannels; i++)
  448.         if (((Adlibchannel[i] >> 8) & 0x7F) == channel)
  449.         {
  450.             Adlibtime[i] = MUStime;
  451.             WritePan(i, Adlibinstr[i], value);
  452.         }
  453.         break;
  454.     }
  455. }
  456.  
  457. BYTE *playTick(BYTE *data)
  458. {
  459.     for(;;) {
  460.     BYTE command = (*data >> 4) & 7;
  461.     BYTE channel = *data & 0x0F;
  462.     BYTE last = *data & 0x80;
  463.     data++;
  464.  
  465.     switch (command) {
  466.         case 0:    // release note
  467.         playingAtOnce--;
  468.         releaseNote(channel, *data++);
  469.         break;
  470.         case 1: {    // play note
  471.         BYTE note = *data++;
  472.         playingAtOnce++;
  473.         if (playingAtOnce > playingPeak)
  474.             playingPeak = playingAtOnce;
  475.         if (note & 0x80)    // note with volume
  476.             playNote(channel, note & 0x7F, *data++);
  477.         else
  478.             playNote(channel, note, -1);
  479.         } break;
  480.         case 2:    // pitch wheel
  481.         pitchWheel(channel, *data++ - 0x80);
  482.         break;
  483.         case 4:    // change control
  484.         changeControl(channel, data[0], data[1]);
  485.         data += 2;
  486.         break;
  487.         case 6:    // end
  488.         return NULL;
  489.         case 5:    // ???
  490.         case 7:    // ???
  491.         break;
  492.         case 3:    // set tempo ??? -- ignore it
  493.         data++;
  494.         break;
  495.     }
  496.     if (last)
  497.         break;
  498.     }
  499.     return data;
  500. }
  501.  
  502. BYTE *delayTicks(BYTE *data, DWORD *delaytime)
  503. {
  504.     DWORD time = 0;
  505.  
  506.     do {
  507.     time <<= 7;
  508.     time += *data & 0x7F;
  509.     } while (*data++ & 0x80);
  510.  
  511.     *delaytime = time;
  512.     return data;
  513. }
  514.  
  515. #ifdef NOIRQ
  516. void playMUS(void)
  517. {
  518.     BYTE *data = score;
  519.     DWORD ticks = 0, playtime = 0;
  520.     WORD i;
  521.     volatile BYTE flag = 0;
  522.  
  523.     if (stereo)
  524.     InitAdlib(SBProPort, 1);
  525.     else
  526.     InitAdlib(ADLIBPORT, 0);
  527.  
  528.     setmem(Adlibchannel, sizeof Adlibchannel, 0xFF);
  529.     for (i = 0; i < CHANNELS; i++)
  530.     {
  531.     channelVolume[i] = 127;     // default volume 127 (full volume)
  532.     channelLastVolume[i] = 100;
  533.     }
  534.     MUStime = 0;
  535.  
  536.     for(;;) {
  537.     DWORD temp;
  538.     /* Bios INT 15h, Fn 8300h -- Start timer */
  539.     flag = 0;        // byte to be set when the interval elapses
  540.     asm {
  541.         push    ds
  542.         push    bp
  543.         mov    dx,WORD PTR speed[0]    // CX:DX - interval (in microseconds)
  544.         mov    cx,WORD PTR speed[2]
  545.         push    ss
  546.         pop    es
  547.         lea    bx,flag            // ES:BX - flag address
  548.         mov    ax,8300h        // start timer
  549.         int    15h
  550.         pop    bp
  551.         pop    ds
  552.     }
  553.     playingPeak = playingAtOnce;
  554.     if (!ticks)
  555.     {
  556. #ifdef DEBUG
  557. //        printf("DEBUG: data = %p\n", data);
  558. #endif
  559.         data = playTick(data);
  560.         if (data == NULL)
  561.         break;
  562.         data = delayTicks(data, &ticks);
  563. #ifdef DEBUG
  564.         puts("------------------------------");
  565. #endif
  566.     }
  567.     ticks--;
  568.     MUStime++;
  569.     // show playing time (not too accurate, though)
  570.     playtime += speed;
  571. //    temp = playtime / 1000;
  572.     temp = (MUStime*1000)/128;
  573.     cprintf("\rPlaying ... %2ld:%02ld:%03ld  %2d (%2d)", temp / 60000,
  574.         (temp / 1000) % 60, temp % 1000, playingAtOnce, playingChannels);
  575.     if (bioskey(1) == 0x011B)    // Esc - exit to DOS
  576.         break;
  577.     if ( !(bioskey(2) & 3) )    // Shift Key - full speed ahead!
  578.         while (!flag);        // synchronization
  579.     }
  580.     asm {
  581.     mov    ax,8301h        // cancel timer
  582.     int    15h
  583.     }
  584.     printf("\n");
  585.  
  586.     DeinitAdlib();
  587. }
  588.  
  589. #else  // NOIRQ
  590.  
  591. void playMusic(void)
  592. {
  593.     BYTE keyflags = *(BYTE far *)MK_FP(0x0040, 0x0018);
  594.  
  595.     playingPeak = playingAtOnce;
  596.     if (!MUSdata) return;
  597.     if (!MUSticks || keyflags & 4)
  598.     {
  599.     MUSdata = playTick((BYTE *)MUSdata);
  600.     if (MUSdata == NULL)
  601.     {
  602.         if (loopForever)
  603.         MUSdata = score;
  604.         return;
  605.     }
  606.     MUSdata = delayTicks((BYTE *)MUSdata, &(DWORD)MUSticks);
  607.     MUSticks = (MUSticks * 75) / 10;
  608. //    MUSticks *= 8;
  609.     MUStime += MUSticks;
  610.     }
  611.     MUSticks--;
  612. //    MUStime++;
  613. }
  614.  
  615. void FASTCALL CMOSwrite(BYTE reg, BYTE value)
  616. {
  617.     asm {
  618.     mov    al,reg
  619.     out    70h,al
  620.     jmp    delay1
  621.     }
  622. delay1:    asm jmp    delay2
  623. delay2: asm {
  624.     mov    al,value
  625.     out    71h,al
  626.     }
  627. }
  628.  
  629. int FASTCALL CMOSread(BYTE reg)
  630. {
  631.     asm {
  632.     mov    al,reg
  633.     out    70h,al
  634.     jmp    delay1
  635.     }
  636. delay1:    asm jmp    delay2
  637. delay2: asm {
  638.     in    al,71h
  639.     xor    ah,ah
  640.     }
  641.     return _AX;
  642. }
  643.  
  644. void interrupt newint70h_handler(void)
  645. {
  646.     static WORD count = 0;
  647.  
  648.     if ( ! (CMOSread(0x0C) & 0x40) )
  649.     {
  650.     (*oldint70h)();
  651.     return;
  652.     }
  653.  
  654.     if (!count)
  655.     {
  656.     count++;
  657.     asm {
  658.         mov    WORD PTR timersavestack[2],ss
  659.         mov    WORD PTR timersavestack[0],sp
  660.         mov    ss,WORD PTR timerstackend[2]
  661.         mov    sp,WORD PTR timerstackend[0]
  662.         db 66h; pusha    // *386* pushad
  663.     }
  664.     playMusic();
  665.     asm {
  666.         db 66h; popa    // *386* popad
  667.         nop            // workaround against 386 POPAD bug
  668.         mov    ss,WORD PTR timersavestack[2]
  669.         mov    sp,WORD PTR timersavestack[0]
  670.     }
  671.     count--;
  672.     }
  673.  
  674.     asm {
  675.     mov    al,20h            // end-of-interrupt
  676.     out    0A0h,al
  677.     jmp    delay1
  678.     }
  679. delay1:    asm out    20h,al
  680. }
  681.  
  682. int SetupTimer(void)
  683. {
  684.     if ( *(BYTE far *)MK_FP(0x0000, 0x04A0) )        // is the timer busy?
  685.         return -1;
  686.     if ( (timerstack = calloc(0x100, sizeof(WORD))) == NULL)
  687.     return -1;
  688.     *(BYTE far *)MK_FP(0x0000, 0x04A0) = 1;    // mark the timer as busy
  689.     timerstackend = &timerstack[0x100];
  690.     oldint70h = getvect(0x70);
  691.     setvect(0x70, newint70h_handler);
  692.     CMOSwrite(0x0B, CMOSread(0x0B) | 0x40);    // enable periodic interrupt
  693.     asm {
  694.     in    al,0A1h        // enable IRQ 8
  695.     and    al,0FEh
  696.     out    0A1h,al
  697.     }
  698.     return 0;
  699. }
  700.  
  701. int ShutdownTimer(void)
  702. {
  703.     CMOSwrite(0x0B, CMOSread(0x0B) & ~0x40);    // disable periodic interrupt
  704.     asm {
  705.     in    al,0A1h        // disable IRQ 8
  706.     or    al,1
  707.     out    0A1h,al
  708.     }
  709.     setvect(0x70, oldint70h);
  710.     free(timerstack);
  711.     *(BYTE far *)MK_FP(0x0000, 0x04A0) = 0;    // mark the timer as unused
  712.     return 0;
  713. }
  714.  
  715. void playMUS(void)
  716. {
  717.     DWORD lasttime;
  718.     WORD i;
  719.  
  720.     if (stereo)
  721.     InitAdlib(SBProPort, 1);
  722.     else
  723.     InitAdlib(ADLIBPORT, 0);
  724.  
  725.     setmem(Adlibchannel, sizeof Adlibchannel, 0xFF);
  726.     for (i = 0; i < CHANNELS; i++)
  727.     {
  728.     channelVolume[i] = 127;     // default volume 127 (full volume)
  729.     channelLastVolume[i] = 100;
  730.     }
  731.  
  732.     MUSdata = score;
  733.     MUSticks = 0;
  734.     MUStime = 0;
  735.     lasttime = 0xFFFFFFFF;
  736.  
  737.     if (SetupTimer())
  738.     {
  739.     printf("FATAL ERROR: Cannot initialize 1024 Hz timer. Aborting.\n");
  740.     return;
  741.     }
  742.  
  743.     if (execCmd)
  744.     system(execCmd);
  745.  
  746.     for(;;) {
  747.     if (!MUSdata)        // no more music
  748.         break;
  749.     if (bioskey(1))
  750.     {
  751.         WORD key = bioskey(0);
  752.         if (key == 0x011B)        // Esc - exit to DOS
  753.         break;
  754.         else if ((BYTE)key == 'c' || (BYTE)key == 'C')
  755.         system("");        // C - run COMMAND.COM
  756.     }
  757.     if (lasttime != MUStime)
  758.     {
  759.         DWORD playtime = ((lasttime = MUStime)*1000)/1024;
  760.         cprintf("\rPlaying ... %2ld:%02ld:%03ld  %2d (%2d)", playtime / 60000,
  761.         (playtime / 1000) % 60, playtime % 1000, playingAtOnce, playingChannels);
  762.     }
  763.     }
  764.     ShutdownTimer();
  765.     cprintf("\r\n");
  766.  
  767.     DeinitAdlib();
  768. }
  769. #endif // NOIRQ
  770.  
  771. int detectHardware(void)
  772. {
  773.     DetectBlaster(&SBProPort, NULL, NULL, NULL);
  774.     if (!DetectAdlib(ADLIBPORT))
  775.     return -1;
  776.     return DetectSBProII(SBProPort);
  777. }
  778.  
  779. int dispatchOption(char *option)
  780. {
  781.     switch (*option) {
  782.     case '?':
  783.     case 'H':        // help
  784.         help = 1;
  785.         break;
  786.     case 'B':        // instrument bank
  787.         instrname = option+1;
  788.         break;
  789. #ifdef NOIRQ
  790.     case 'S':        // speed
  791.         speed = strtoul(option+1, NULL, 0);
  792.         break;
  793. #endif
  794.     case '1':        // single voice
  795.         singlevoice = 1;
  796.         break;
  797.     case 'M':        // mono mode
  798.         stereo = 0;
  799.         break;
  800.     case 'C':        // command
  801.         execCmd = option+1;
  802.         break;
  803.     case 'L':        // loop
  804.         loopForever = 1;
  805.         break;
  806.     default:
  807.         return -1;
  808.     }
  809.     return 0;
  810. }
  811.  
  812. main(int argc, char *argv[])
  813. {
  814.     FILE *f;
  815.  
  816.     puts(COPYRIGHT);
  817.     while (argv++, --argc)
  818.     {
  819.     strupr(*argv);
  820.     if (**argv == '/' || **argv == '-')
  821.     {
  822.         if (dispatchOption(*argv+1))
  823.         {
  824.         printf("Invalid option %s\n", *argv);
  825.         return 2;
  826.         }
  827.     } else {
  828.         if (!musname)
  829.         musname = *argv;
  830.         else {
  831.         printf("Too many filenames.\n");
  832.         return 3;
  833.         }
  834.     }
  835.     }
  836.  
  837.     if (!musname || help)
  838.     {
  839.     puts("Syntax:\n"
  840.          "  MUSPLAY filename.MUS [/B[path\]GENMIDI.OP2] [/1] [/M] [/Ccmd] [/L]\n"
  841.          "Options:\n"
  842.          "    /Bbank    set different instrument bank\n"
  843. #ifdef NOIRQ
  844.          "    /Sspeed   change duration of one time-tick (default 6832 microseconds)\n"
  845. #endif
  846.          "    /1        don't use double-voice instruments\n"
  847.          "    /M        play in mono -- thru Adlib instead of SoundBlaster Pro II\n"
  848.          "    /Ccmd     exec DOS command cmd during replay\n"
  849.          "    /L        play continuously (loop forever)\n");
  850.     return 1;
  851.     }
  852.  
  853.     switch (detectHardware()) {
  854.     case -1:
  855.         puts("Adlib not detected. Exiting.");
  856.         return 4;
  857.     case 0:
  858.         stereo = 0;
  859.         puts("Adlib detected (9 channels mono)");
  860.         break;
  861.     case 1:
  862.         puts("Sound Blaster Pro II detected (18 channels stereo)");
  863.         break;
  864.     }
  865.  
  866.     if ( (f = fopen(musname, "rb")) == NULL)
  867.     {
  868.     printf("Can't open file %s\n", musname);
  869.     return 5;
  870.     } else
  871.     printf("Reading file %s ...\n", musname);
  872.  
  873.     if (readMUS(f))
  874.     {
  875.     fclose(f);
  876.     return 6;
  877.     }
  878.     fclose(f);
  879.  
  880. #ifdef DEBUG
  881.     printf("DEBUG: coreleft = %lu\n", (long)coreleft());
  882. #endif
  883.  
  884.     if ( (f = fopen(instrname, "rb")) == NULL)
  885.     {
  886.     printf("Can't open file %s\n", instrname);
  887.     return 7;
  888.     } else
  889.     printf("Reading file %s ...\n", instrname);
  890.  
  891.     if (readINS(f))
  892.     {
  893.     fclose(f);
  894.     return 8;
  895.     }
  896.     fclose(f);
  897.  
  898. #ifdef DEBUG
  899.     printf("DEBUG: coreleft = %lu\n", (long)coreleft());
  900. #endif
  901.  
  902.     playMUS();
  903.  
  904.     free(score);
  905.     free(instruments);
  906.  
  907.     return 0;
  908. }
  909.