home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / playadlb / playrtn.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  7.6 KB  |  337 lines

  1. /*
  2.     PLAY.C
  3.  
  4.     29-jun-88, Marc Savary, Ad Lib Inc.
  5.  
  6.     Demo file to play the AdLib MIDI music file ( *.MUS) with the *.SND
  7.     timbre bank file.
  8.     This simple driver use the PC's timer-0 for timing. (see TIMER.ASM )
  9.  
  10.     See "convert.c" for *.MUS file structure.
  11.  
  12.     This file and the file BANK.C (timbre bank manager) MUST BE compiled
  13.     WITHOUT stack overflow check. (-v option of Lattice C Compiler,
  14.     -Gs for Microsoft)
  15.  
  16.     If compiling with Lattice, 'LATTICE' must be defined for this file
  17.     and BANK.C.
  18.  
  19.     With Microsoft, use the following ('MICROSOFT' must be defined for
  20.     this file and BANK.C):
  21.       masm timer.asm
  22.       cl -DMICROSOFT -AS -J -Zi -Zp1 -Ox -Gs -c play.c bank.c adlib.c
  23.       link play.obj adlib.obj bank.obj timer.obj
  24.  
  25. */
  26. #define MICROSOFT 1
  27. #include <stdio.h>
  28. #include <fcntl.h>
  29.  
  30. #include "convert.h"
  31. #include "bank.h"
  32.  
  33. #ifdef MICROSOFT
  34. #define UNSIGNED_CHAR unsigned char
  35. #endif
  36. #ifdef LATTICE
  37. #define UNSIGNED_CHAR char
  38. #endif
  39.  
  40. BankPtr    bank;                    /* pointer to instrument bank */
  41. char *        musPtr;                    /* pointer to first data byte of melody */
  42. struct        MusHeader * headPtr;    /* pointer to header of .MUS file */
  43. char        musRunning;                /* != 0 if music is playing */
  44. UNSIGNED_CHAR status;                                 /* running status byte */
  45. char        volume[ NR_VOICES];        /* actual volume of all voices */
  46.  
  47. long        tickCount;                /* time counter, for information only */
  48. unsigned    delay;                    /* length of last delay */
  49.  
  50. #include  <malloc.h>
  51. #include  <string.h>
  52. #define  rlsmem(x,y)    free(x)
  53. #define  getmem(x)      malloc(x)
  54. #define  setmem(x,y,z)  memset(x,z,y)
  55. #define  movmem(x,y,z)  memmove(y,x,z)
  56. #define  max(x,y)       ((x > y) ? x:y)
  57.  
  58. /*
  59.     Simple demonstration.
  60.     Syntax:     play <musfile.mus> <bankfile.snd>
  61. */
  62.  
  63. /*
  64.     Wait until the end of melody ( musRunning == 0)
  65. */
  66. WaitEndMelo()
  67.     {
  68.     static unsigned measure = 0, beat = 0;
  69.     unsigned m, b, c, i;
  70.  
  71.     while( musRunning)
  72.       {
  73.         if( kbhit()) 
  74.          {
  75.             c = getch();
  76.             if( c == 0x1b)    StopMelo();
  77.             }
  78.         }
  79.     }    /* WaitEndMelo() */
  80.  
  81.  
  82. /*
  83.     Initialize the driver.
  84. */
  85. InitSnd()
  86.     {
  87.     /* initalize the low-level sound-driver: */
  88.     if( !SoundColdInit( 0x388)) 
  89.       {
  90.         exit( 1);
  91.         }
  92.     /* install the clock driver: */
  93.     clk_install();
  94.     }
  95.  
  96.  
  97. /*
  98.     Uninstall the clock driver ...
  99. */
  100. Terminate()
  101.     {
  102.     clk_uninstall();
  103.     }
  104.  
  105.  
  106. /*
  107.     Start playing a melody. Set some global pointers for the interrupt
  108.     routine. Set the tempo, sound mode & pitch bend range.
  109.     Reset volume of each voice. Start the clock driver with the first
  110.     delay ( >= 1)
  111. */
  112. StartMelo( header, data, len, timBank)
  113.     struct MusHeader * header;    /* pointer to header struc. of music file */
  114.     char * data;            /* pointer to music data */
  115.     unsigned len;            /* size of data */
  116.     BankPtr timBank;        /* bank of timbres */
  117.     {
  118.     int i;
  119.  
  120.     musPtr = data;
  121.     headPtr = header;
  122.     bank = timBank;
  123.     tickCount = 0;
  124.     for( i = 0; i < NR_VOICES; i++)
  125.         volume[ i] = 0;
  126.  
  127.     SetMode( header->soundMode);
  128.     SetPitchRange( header->pitchBRange);
  129.     SetTempo( header->basicTempo, header->tickBeat);
  130.  
  131.     StartTimeOut( 0);
  132.     delay = *musPtr++;
  133.     musRunning = 1;
  134.     /* NEVER START COUNT-DOWN WITH 0, since 0 means MAXIMUM delay!!! */
  135.     StartTimeOut( max( delay, 1));
  136.     }   /* StartMelo() */
  137.  
  138.  
  139. /*
  140.     Stop playing the melody. Send note-off to all voices and reset
  141.     the clock frequency to nominal ( 18.2 Hz).
  142. */
  143. StopMelo()
  144.     {
  145.     int i;
  146.  
  147.     musRunning = 0;
  148.     for( i = 0; i < NR_VOICES; i++)
  149.         NoteOff( i);
  150.     SetTempo( 0, 1);
  151.     }    /* StopMelo() */
  152.  
  153.  
  154. /*
  155.     Change the tempo.
  156.  
  157.     Reload the timer-0 with the proper divider for generating
  158.     the appropriate frequency.
  159.  
  160.     If tempo is zero, reprogram the counter for 18.2 Hz.
  161. */
  162. SetTempo( tempo, tickBeat)
  163.     unsigned tempo;                /* beats per minute */
  164.     unsigned tickBeat;            /* ticks per beat */
  165.     {
  166.     long t1;
  167.     unsigned freq;
  168.     unsigned low, high, flags, count;
  169.  
  170.     t1 = tickBeat * (long)tempo;
  171.     freq = t1 /60;                /* interrupt rate needed */
  172.  
  173.     if( !freq)
  174.         count = 0;
  175.     else {
  176.         /* make sure that frequency is >= 19 Hz, since counter min. output
  177.             frequency is 18.2 Hz: */
  178.         freq = freq < 19 ? 19 : freq;
  179.         /* compute counter divider: */
  180.         count = (1193180 /(long)freq);
  181.         }
  182.     /* and set the counter: */
  183.     SetClkRate( count);
  184.     }   /* SetTempo() */
  185.  
  186.  
  187.  
  188. /*
  189.     Interrupt routine. Called by low-level clock driver when
  190.     the delay count has expired.
  191.  
  192.     'musPtr' always points to an OVERFLOW BYTE or to the first byte AFTER
  193.     the timing byte.
  194.  
  195.     When this routine is called, the active SS ( stack segment) is not
  196.     the original of the application, so take care. 
  197.     This routine, and all others called by, must be compiled
  198.     without stack-overflow checking, since the SS has changed!!!
  199.     
  200.     Return to caller the number of clock ticks to wait for before
  201.     the next call.
  202. */
  203. unsigned TimeOut()
  204.     {
  205.     unsigned pitch, tempo, haut, vol;
  206.     unsigned char newStatus;
  207.     int timbreDef[TIMBRE_DEF_LEN], timbre;
  208.     int comm, id, integer, frac, voice = 1;
  209.  
  210.     if( ! musRunning)
  211.         /* Music has not started or has been stopped, so wait the minimum delay ... */
  212.         return 1;
  213.  
  214.     tickCount += delay;
  215.     do {
  216.         newStatus = *musPtr;
  217.         if( newStatus == OVERFLOW_BYTE) {
  218.             /* timing overflow ... */
  219.             musPtr++;
  220.             delay = OVERFLOW_BYTE;
  221.             break;
  222.             }
  223.  
  224.         else if( newStatus == STOP_BYTE) {
  225.             StopMelo();
  226.             return 0;       /* maximum delay ... */
  227.             }
  228.         else if( newStatus == SYSTEM_XOR_BYTE) {
  229.             /*
  230.             non-standard... this is a tempo multiplier:
  231.             data format: <F0> <7F> <00> <integer> <frac> <F7>
  232.             tempo = basicTempo * integerPart + basicTempo * fractionPart/128
  233.             */
  234.             musPtr++;
  235.             id = *musPtr++;
  236.             comm = *musPtr++;
  237.             if( id != ADLIB_CTRL_BYTE || comm != TEMPO_CTRL_BYTE) {
  238.                 /* unknown format ... skip all the XOR message */
  239.                 musPtr -= 2;
  240.                 while( *musPtr++ != EOX_BYTE)
  241.                     ;
  242.                 }
  243.             else {
  244.                 integer = *musPtr++;
  245.                 frac = *musPtr++;
  246.                 tempo = headPtr->basicTempo;
  247.                 tempo = tempo * integer + (unsigned)(((long)tempo * frac) >> 7);
  248.                 SetTempo( tempo, headPtr->tickBeat);
  249.                 musPtr++;       /* skip EOX_BYTE */
  250.                 }
  251.             delay = *musPtr++;
  252.             }
  253.         else {
  254.             if( newStatus >= 0x80) {
  255.                 musPtr++;
  256.                 status = newStatus;
  257.                 }
  258.             voice = (int) (status & 0x0f);
  259.  
  260.             switch( status & 0xf0) {
  261.                 case NOTE_ON_BYTE:
  262.                     haut = *musPtr++;
  263.                     vol = *musPtr++;
  264.                     if( ! vol) {
  265.                         NoteOff( voice);
  266.                         }
  267.                     else {
  268.                         if( vol != volume[ voice]) {
  269.                             SetVoiceVolume( voice, vol);
  270.                             volume[ voice] = vol;
  271.                             }
  272.                         NoteOn( voice, haut);
  273.                         }
  274.                     break;
  275.  
  276.                 case NOTE_OFF_BYTE:
  277.                     musPtr += 2;
  278.                     NoteOff( voice);
  279.                     break;
  280.  
  281.                 case AFTER_TOUCH_BYTE:
  282.                     SetVoiceVolume( voice, *musPtr++);
  283.                     break;
  284.  
  285.                 case PROG_CHANGE_BYTE:
  286.                     timbre = *musPtr++;
  287.                     if( GetTimbre( "", &timbre, timbreDef, bank)) {
  288.                         SetVoiceTimbre( voice, timbreDef);
  289.                         }
  290.                     break;
  291.  
  292.                 case PITCH_BEND_BYTE:
  293.                     pitch = *musPtr++;
  294.                     pitch += (unsigned)(*musPtr++) << 7;
  295.                     SetVoicePitch( voice, pitch);
  296.                     break;
  297.  
  298.                 case CONTROL_CHANGE_BYTE:
  299.                     /* not implemented ... */
  300.                     musPtr += 2;
  301.                     break;
  302.  
  303.                 case CHANNEL_PRESSURE_BYTE:
  304.                     /* not implemented ... */
  305.                     musPtr++;
  306.                     break;
  307.  
  308.                 default:
  309.                     SkipToTiming();
  310.                     break;
  311.                 }
  312.  
  313.             delay = *musPtr++;
  314.             }
  315.         } while( delay == 0);
  316.  
  317.     if( delay == OVERFLOW_BYTE) {
  318.         delay = OVERFLOW;
  319.         if( *musPtr != OVERFLOW_BYTE)
  320.             delay += *musPtr++;
  321.         }
  322.     return delay;
  323. }
  324.  
  325.  
  326. /*
  327.     A bad status byte ( or unimplemented MIDI command) has been encontered.
  328.     Skip bytes until next timing byte followed by status byte.
  329. */
  330. static SkipToTiming()
  331.     {
  332.     while( *musPtr < 0x80)
  333.         musPtr++;
  334.     if( *musPtr != OVERFLOW_BYTE)
  335.         musPtr--;
  336.     }    /* SkipToTiming() */
  337.