

/******************************************************************
*                 Music in the Key of C                           *
*                                                                 *
*          Written by Mark Lewis Baldwin - Nov 1986               *
*                 Beacon Technical Services                       *
*                 7711 S Crutice Way #E                           *
*                 Littleton, CO 80120                             *    
*                                                                 *
*                (C) 1986 Mark Lewis Baldwin                      *
*                The enclosed material is NOT public domain       *
*                but may be distribute freely so long as the      *
*                copyright remains intact                         *
*                This program is distributed as shareware         *
*                                                                 *
*    The following routines allow the playing of music with       *
*    seperatly defined instruments in C (Megamax)                 *           
******************************************************************/

#include <osbind.h>
#include <stdio.h>

/*------------------Some general definitions --------------------*/

#define MIN(x,y) ((x)<(y)?(x):(y)) 
#define MAX(x,y) ((x)>(y)?(x):(y)) 
#define TRUE 1 
#define FALSE 0 

/*------------------------ MUSIC ------------------------------*/

/* Define Notes for Music */

#define B6  100,       /*  Each note is referenced by the note, then */
#define AS6 101,       /*  the octave with an S included for sharp   */ 
#define A6  102,       /*  i.e. GS5 is G sharp in the fifth octave   */
#define GS6 103,       /*  Flats and additional octaves can easily   */
#define G6  104,       /*  be added to the table.                    */
#define FS6 105,
#define F6  106,
#define E6  107,
#define DS6 108,
#define D6  109,
#define CS6 110,
#define C6  111,

#define B5  112,
#define AS5 113,
#define A5  114,
#define GS5 115,
#define G5  116,
#define FS5 117,
#define F5  118,
#define E5  119,
#define DS5 120,
#define D5  121,
#define CS5 122,
#define C5  123,

#define B4  124,
#define AS4 125,
#define A4  126,
#define GS4 127,
#define G4  128,
#define FS4 129,
#define F4  130,
#define E4  131,
#define DS4 132,
#define D4  133,
#define CS4 134,
#define C4  135,

#define B3  136,
#define AS3 137,
#define A3  138,
#define GS3 139,
#define G3  140,
#define FS3 141,
#define F3  142,
#define E3  143,
#define DS3 144,
#define D3  145,
#define CS3 146,
#define C3  147,

#define B2  148,
#define AS2 149,
#define A2  150,
#define GS2 151,
#define G2  152,
#define FS2 153,
#define F2  154,
#define E2  155,
#define DS2 156,
#define D2  157,
#define CS2 158,
#define C2  159,

/* The below tables reference the above defined notes less 100.
   note_f is the lower part of the two byte word while note_c is
   the upper.  */
   
int note_f[] = {
      63,  67,  71,  75,  79,  84,  89,  95, 100, 106, 112, 119,        
     127, 134, 142, 150, 159, 169, 179, 190, 201, 213, 225, 239,
     253,  12,  28,  45,  63,  82, 102, 123, 146, 170, 195, 222,
     250,  24,  56,  90, 126, 164, 204, 246,  36,  83, 134, 188,
     244,  49, 112, 180,   9,  71, 152, 237,  71, 167,  12, 119  } ;
int note_c[] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
     1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 
     3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7  } ;

/* Define note sizes for music */
     
#define WH  200,         /* Note sizes are defined as WH, HF, QU, EI, SX,*/
#define WHD 201,         /* and TH for WHole, HaLf, QUarter, EIgth,      */
#define HF  203,         /* SiXteenth and THirtysecond respectively      */
#define HFD 204,         /* The D and T placed at the end represent      */
#define HFT 205,         /* Dotted and Tripleted note size               */
#define QU  206,
#define QUD 207,
#define QUT 208,
#define EI  209,
#define EID 210,
#define EIT 211, 
#define SX  212,
#define SXD 213,
#define SXT 214,
#define TH  215,
#define THD 216,
#define THT 217,

/*  The table below represents the reciprocals of the above note sizes
    less 200 */
    
int dur_array[] = { 3, 2, 2, 6, 4, 9, 12, 8, 18, 24, 16, 36, 
                     48, 32, 72, 96, 64, 144 } ;
                                 
/* Define other notations for the music score */

#define REST -1,           /* allows sound rest of current duration size */
#define C_REST -1
#define END  -2,           /* use at end of all song files */
#define C_END -2 
#define P    -3,           /* P is used to execute the next step */
#define C_P  -3 
#define VU   -4,           /* Increase volume by one magnitude   */
#define C_VU -4 
#define VD   -5,           /* Decrease volume by one magnitude   */
#define C_VD -5 
#define VN   -6,           /* Return volume to normal            */
#define C_VN  -6 
#define SPEED -7,          /* Change or initialize song speed, to be    */
#define C_SPEED -7         /* followed by a speed recprical value and a , */
#define DO   -8,           /* Repeat followin group n times         */
#define C_DO  -8
#define ENDDO -9,          /* End do loop                           */
#define C_ENDDO -9

/* Define the Instruments */

#define N_INST 3            /* number of instruments in the tables */
#define ORGAN 0,            /* ORGAN will be the first instrument  */
#define HORN  1,
#define SNARE 2,

/*  The instrument voice is broken down into 4 phases, being attack, decay,
    sustain and release.  A value between 0 and 15 is used for the time and
    initial magnitude of each phase.  There is one exception, a time value
    of 16 for the sustain phase will sustain the note for the length of the
    note whatever size the current note is.  Tone and Noise assign the
    tone and noise flags for each instrument.  
*/
    
int inst_tim[N_INST][4] = { { 4, 8, 16, 9 },
                            { 4, 15, 16, 8 },
                            { 6, 6, 6, 6 }  } ;
int inst_mag[N_INST][4] = { { 3, 13, 9, 7 },
                            { 6, 11, 10, 6 },
                            { 13, 11, 7, 4 } } ;
int inst_tone[N_INST]  = { TRUE, TRUE, TRUE } ; 
int inst_noise[N_INST] = { FALSE, FALSE, TRUE } ;

/* Score for the music */

/* The Interstel Theme is Copyright Interstel Corp, 1986 */

int interstel[] = { SPEED 120,
     ORGAN EID C4 P SX C4 P EI C4 P C4 P HF F4 P EID C4 A3 P SX C4 A3 P
     EI C4 A3 P C4 A3 P HF F4 C4 A3 P EID C4 A3 P SX C4 A3 P EI C4 A3 P
     C4 A3 P QU F4 C4 A3 P SX C3 A2 F2 P F3 C3 A2 P EI A3 F3 C3 P
     QU C4 A3 F3 P C4 A3 F3 P SX C4 A2 F2 P EID C4 A2 F2 P  
     EI C4 A2 F2 P AS3 AS2 F2 P EID A3 A2 F2 P G3 G2 D2 P 
     SX F3 EI G2 D2 P SX G3 P HF F3 A2 F2 P END } ;

/* Thus Spake Zarathustra by Richard Strauss              */

int spake[] = { SPEED 90,
     HORN HF C2 P G2 P C3 P QUD G3 P EI G3 C4 E4 P HF G3 C4 DS4 P
     SNARE EI E2 P C2 P E2 P C2 P E2 P C2 P E2 P C2 P  
     HORN HF C2 P G2 P C3 P QUD G3 P EI C4 DS4 G4 P HF E4 G4 C5 P
     SNARE EI DO 4, E2 P C2 P ENDDO
     HORN HF C2 P G2 P C3 P QUD G3 P VU EI C4 E4 G4 P 
     WH VU C4 F4 A4 P EI C4 F4 A4 P D4 G4 B4 P HF F4 A4 C5 P 
     QU F4 B4 D5 P EI G3 C4 E4 P A3 D4 F4 P HF C4 E4 G4 P
     EI VU G3 C4 E4 P A3 D4 F4 P WH C4 E4 G4 P
     HF VU C4 E4 A4 P D4 G4 B4 P WH E4 G4 C5 P
     VN E4 G4 C5 P END } ;
               
/*  constants for controlling the absolute music speed */
#define SPEED_CONST 25000 
#define MUSIC_DELAY 6

int  s_a_pointer ;              /* pointer to the music chip data array */
char s_array[ 100 ] ;           /* array containing data for sound chip */
int  v_count[ 3 ] ;             /* current count of note for voice i    */
int  v_dur[ 3 ] ;               /* duration of note for voice i         */
int  v_inst[ 3 ] ;              /* instrument assigned to voice i       */
int  v_tim2[ 3 ] ;              /* calculated lenght of sustain for v i */
int  vol_mod     ;              /* volume modifier ( 0 is normal )      */
int byte7 ;                     /* word contianing current tone & noise */

int i_tim0[2], i_tim1[2], i_tim2[2], i_tim3[2],  /* precalulated timing */
    i_mag0[2], i_mag1[2], i_mag2[2], i_mag3[2],
    i_tim01[2],i_tim013[2],i_mag10[2],i_mag21[2],
    i_mag32[2] ;
    
int music_flag ;                 /* FALSE to stop music                  */


/***********************************************************************
*                             wait( delay )                            *
*    delay the program for delay/200 of a second and return            *
***********************************************************************/

long *ptr;                       /* pointer for wait routines          */
                                  
gettime()                 /* get 200HZ clock current value */
     {
     *ptr = *(long *)0x4ba;    /* address of 200 HZ counter */
     }

wait( delay )

int delay ;                   /* time to wait delay/200 seconds      */

     {
     long t_cur;             /* current time                 */
     long t_fin ;            /* finish time                  */
     
     ptr = &t_cur ;
     Supexec(gettime);       /* go into super mode to get clock value */
     t_fin = t_cur + delay ; /* time to finish wait   */
     while ( t_cur < t_fin ) /* wait for time passage */
          Supexec(gettime) ; /* update t_cur          */
     }

/*************************************************************************
*                       sound_build( reg, value )                        *
*    build the sound array such that 'value' will be put in sound chip   *
*    register 'reg'.                                                     *
*************************************************************************/

sound_build( reg, value )

int reg,             /*  register to store value in */
    value ;          /*  value to be stored         */

     {
     s_array[ s_a_pointer++ ] = reg ;     /* place reg and value in */
     s_array[ s_a_pointer++ ] = value ;   /* the sound array        */
     }

/*************************************************************************
*                       sound_go( delay )                                *
*     update the sound chip with the values in s_array and wait 'delay'  *
*************************************************************************/

sound_go( delay )    

     {
     sound_build( 255, 0 ) ;       /* close sound string */
     Dosound( s_array ) ;          /* xbios function which inserts data */
                                   /* into the sound chip               */  
     s_a_pointer = 0 ;             /* reset sound pointer */
     wait( delay ) ;               /* wait delay/200 seconds */
     }

/********************************************************************
*                   clear_io_buffer()                               *
*     clear the keyboard buffer of any waiting keys                 *
********************************************************************/

clear_io_buffer()               
     {
     while (Cconis())          /*  If there is anything in the buffer */
          Crawcin() ;          /*  ...remove it                       */
     }

/*********************************************************************
*                  wait_cycle()                                      *
*     wait MUSIC_DELAY time and check for keyboard hit to quit       *
*     playing the music                                              *
*********************************************************************/

wait_cycle()

     {
     wait( MUSIC_DELAY ) ;
     if (Cconis())                 /* see if key was hit to stop music */
          {
          clear_io_buffer() ;      /* clear the buffer */
          music_flag = FALSE ;     /* stop music       */
          }
     }


/********************************************************************
*                  calc_s_volume( voice )                           *
*     calculate and return the current volume for 'voice'           *
********************************************************************/         

int calc_s_volume( voice )

int voice ;                       /* voice to calculate volume for */

     {
     int count;                /* current voice count */
     int inst;                 /* current instrument  */
     int vol ;                 /* calculated volume   */

     count = v_count[ voice ] ;
     if ( count >= v_dur[ voice ] )  /* is count past note duration? */
          vol = 0 ;
     else
          {
          inst = v_inst[ voice ] ;  
                 
          /* find phase of instrument */

          /* is the note in the attack phase?  */
          if ( count < i_tim0[ inst ] )    
               vol = ( count * i_mag10[ inst ] ) / i_tim0[ inst ]
                     + i_mag0[ inst ] + vol_mod ;
          /* is the note in the decay phase?  */           
          else if ( count < i_tim01[ inst ] ) 
               vol = (( count - i_tim0[ inst ]) * i_mag21[ inst ] )
                      / i_tim1[ inst ] + i_mag1[ inst ] + vol_mod  ;
          /* is the note in the sustain phase? */              
          else if ( count < i_tim01[ inst ] + v_tim2[ voice ] ) 
               vol = (( count - i_tim01[ inst ] ) * i_mag32[ inst ])
                      / v_tim2[ voice ] + i_mag2[ inst ] + vol_mod ;
          /* or the release phase? */              
          else if ( count < i_tim013[ inst ] + v_tim2[ voice ] ) 
               vol = (( -count + i_tim01[ inst ] + v_tim2[ voice ]) *
               i_mag2[ inst ] ) / i_tim3[ inst ] + i_mag3[ inst ] + vol_mod ;
          else
               vol = 0 ;         /* after end of note */
          } 
     return ( MIN( MAX( 0, vol), 15 ) ) ; /* keep value in range */
     } 

/************************************************************************
*             setbit(&b, bit)     clearbit(&b, bit)                     *
*     set and clear bit number 'bit' in word 'b'                        *
************************************************************************/

setbit( b , bit ) 

int *b ;
int  bit ;

     {
     *b |= (1 << bit)  ;
     } 

clearbit( b, bit )

int *b ;
int  bit ;

     { 
     *b &= ( 0xFFFF - ( 1 << bit ))  ;
     }

/***********************************************************************
*                          clear_sound()                               *
*     clear the sound chip by zeroing all registers                    *
***********************************************************************/

clear_sound() 

     {
     int reg ;                   /* register  */

     s_a_pointer = 0 ;           /* reset pointer for sound string */
     for (reg = 0; reg <= 13; reg++ )  /* zero out all sound registers */
          sound_build( reg, 0 ) ;
     sound_go( 0 ) ;             /* place them in the sound chip */      
     }

/***********************************************************************
*                        init_music()                                  *
*     initialize sound precalculations before playing music            *
***********************************************************************/

init_music()

     {
     int i ;           /* instrument   */

/* this precalculates instrument values so we aren't delayed by doing
   them during the music.  It probably isn't necessary                */
     
     for (i = 0 ; i < N_INST ; i++ )
          {
          i_tim0[ i ] = inst_tim[ i ][ 0 ] ;
          i_tim1[ i ] = inst_tim[ i ][ 1 ] ;
          i_tim2[ i ] = inst_tim[ i ][ 2 ] ;
          i_tim3[ i ] = inst_tim[ i ][ 3 ] ;
          i_mag0[ i ] = inst_mag[ i ][ 0 ] ;
          i_mag1[ i ] = inst_mag[ i ][ 1 ] ;
          i_mag2[ i ] = inst_mag[ i ][ 2 ] ;
          i_mag3[ i ] = inst_mag[ i ][ 3 ] ;
          i_tim01[ i ] = inst_tim[ i ][ 0 ] + inst_tim[ i ][ 1 ] ;
          i_tim013[ i ] = i_tim01[ i ] + inst_tim[ i ][ 3 ] ;
          i_mag10[ i ] = inst_mag[ i ][ 1 ] - inst_mag[ i ][ 0 ] ;
          i_mag21[ i ] = inst_mag[ i ][ 2 ] - inst_mag[ i ][ 1 ] ;
          i_mag32[ i ] = inst_mag[ i ][ 3 ] - inst_mag[ i ][ 2 ] ;
          } 
     clear_sound() ;        /* make sure the sound registers are cleared */
     } 

/*************************************************************************
*                        new_voice()                                     *
*     select one of the sound chip voices (either 0, 1 or 2) in which    *
*     the next note will be played                                       *
*************************************************************************/

int new_voice()  
                   
     {
     int  v_status ;          /* status of current voice */
     int  voice;              /* voice to be looking at  */
     int  new_voice ;         /* trial free voice */
     new_voice = 0 ;          /*  assume voice 0 */

     v_status = v_count[ 0 ] - v_dur[ 0 ] ; /* status of voice 0 */
     for (voice = 1 ; voice <= 2 ; voice++ ) /* check the other voices */
          {
          /* see if the next voice has a later status */
          if ( v_count[ voice ] - v_dur[ voice ] > v_status )
              {  
              v_status = v_count[ voice ] - v_dur[ voice ] ;
              new_voice = voice ;
              } 
          }
     return ( new_voice ) ; 
     } 

/***********************************************************************
*                inst_sound( note[],inst[],dur[],cycle )               *
*     input up to three new notes into the song and play for           *
*     'cycle' lenght of time before returning                          *
***********************************************************************/ 

inst_sound( note, inst, dur,  cycle )
                                
int note[] ;            /* new notes to be played */
int inst[] ;            /* instruments associated with notes */
int dur[]  ;            /* note duration associated with notes */
int cycle ;             /* time cycles to play before returning */

     {
     int v ;            /* current voice to put note in */
     int i ;            /* counter for 1 to 3 notes     */
     int n ;            /* current note being setup */
     int sp ;

     sp = 0 ;
     for (i = 0 ; i <= 2 ; i++ )                  /* Loop on note array */
          {
          n = note[ i ] ;
          if ( (n >= 0) && (n < 60) )              /* If leagal note?    */ 
               {
               v = new_voice() ;                  /* Find a new voice   */
               sound_build( v * 2, note_f[ n ] ) ;/* Build the note     */
               sound_build( v * 2+1, note_c[ n ] ) ;
               v_count[ v ] = 0 ;                 /* Reset voice counter*/
               v_inst[ v ] = inst[ i ] ;          /* Assign instrument  */
               v_dur[ v ] = dur[ i ] ;           /* Assign note duration*/
               if ( i_tim2[ inst[ i ] ] > 15 )   /* Test sustained voice*/
                   v_tim2[ v ] = v_dur[ v ] - i_tim013[ inst[ i ] ] ;
               else
                   v_tim2[ v ] = i_tim2[ inst[ i ] ] ;
               if ( inst_tone[ inst[ i ] ] )      /* Set tone and noise */
                    clearbit( &byte7, v ) ;
               else
                   setbit( &byte7, v ) ;
               if ( inst_noise[ inst[ i ] ] ) 
                   clearbit( &byte7, v+3 ) ;
               else
                   setbit( &byte7, v+3 ) ;                   
               } 
          }      
     sound_build( 7, byte7 ) ;       /* instert noise/tone byte */
          
/*     Play notes until time to calculate new ones  */
    
     for ( i=1 ; i<= cycle ; i++ )      /* play number of cycles */
          {       
          for ( v=0 ; v <= 2 ; v++ )    /* now calculate each voice */
               {
               sound_build( 8+v, calc_s_vol( v ) ) ;
               v_count[ v ] += 1 ;
               } 
          sound_go( 0 ) ;               /* now play the music */
          wait_cycle() ;                /* and wait           */
          } 
     } 

/***********************************************************************
*                     play_music( song[] )                             *
*     play the song contained in the array 'song'                      *
***********************************************************************/

play_music( song )        

int song[] ;                           /*  array containing the music */

     {
     int note[3]               ;       /* Notes to be played */
     int inst[3]               ;       /* Instriments associated w note */
     int dur[3]                ;       /* Duration of each note */
     int n_cnt   = 0           ;       /* note counter */
     int cycle   = 1000        ;       /* Time to next note set to play */
     int c_inst  = 0           ;       /* Current instrument */
     int c_dur                 ;       /* Current note duration */
     int c_cyc                 ;       /* Current cycle time    */
     int c_speed               ;       /* Current song speed    */
     int s_pointer = 0         ;       /* Song file pointer     */
     int i                     ;
     int nextw                 ;       /* next data word        */
     int do_addr = 0           ;       /* pointer to return on looping */
     int do_count = 0          ;       /* loop counter  */
          
 /*     initalize various data   */
 
     byte7 = 63 ;
     for ( i=0; i <= 2 ; i++ )
          {
          v_count[ i ] = 1000 ;
          v_dur[ i ] = 0 ;
          note[ i ] = -1000 ;
          dur[ i ] = 0 ;
          inst[ i ] = 0 ;
          note[ i ] = 0 ;
          } 
     vol_mod = 0 ;
     c_dur = 0 ;
     c_speed = SPEED_CONST / 100 ;
     nextw = song[ s_pointer++ ] ;
     music_flag = TRUE ;
     c_cyc = c_speed / dur_array[ c_dur ] ;

/*      Play data array until key is hit or end of data   */

     while ( (nextw != C_END) && music_flag )
          {

/*    Play the current set of notes */

          if ( nextw == C_P )
               {
               if (cycle < 1000)   /* see if any new notes have been added */
                    {
                    inst_sound( note, inst, dur, cycle ) ;  /* play it */
                    for ( i=0; i<=2; i++ )      /* reset the notes */
                         note[ i ] = -10000 ;
                    n_cnt = 0 ;
                    cycle = 1000 ;
                    }
               }

/*    Change the song speed   */

          else if ( nextw == C_SPEED )
               {
               c_speed = SPEED_CONST / song[ s_pointer++ ] ;
               c_cyc   = c_speed / dur_array[ c_dur ] ;
               }

/*    Insert a rest           */

          else if ( nextw == C_REST )
               cycle = MIN( cycle, c_cyc ) ;

/*    Change the current note size (time)  */

          else if ( (nextw >= 200) && (nextw < 240) )
               {
               c_dur = nextw - 200 ;
               c_cyc = c_speed / dur_array[ c_dur ] ;
               }
          
/*    enter a note grabing the current timing and instrument   */

          else if ( (nextw >= 100) && (nextw < 200 ))
               {
               inst[ n_cnt ] = c_inst ;
               dur[ n_cnt ] = c_cyc  ;
               cycle = MIN( cycle, c_cyc ) ;
               note[ n_cnt ] = nextw - 100 ;
               n_cnt = MIN( 2, n_cnt++ ) ;
               }

/*     increase the volume        */

           else if ( nextw == C_VU )
               vol_mod++ ;
               
/*     decrease the volume        */

           else if ( nextw == C_VD )
               vol_mod-- ;
               
/*     return the volume to normal   */

           else if ( nextw == C_VN )
               vol_mod = 0 ; 

/*     start of do loop               */

           else if ( nextw == C_DO )
                {
                do_count = song[ s_pointer++ ] - 1 ;
                do_addr    = s_pointer ;        
                }

/*     end of do loop                  */

           else if ( (nextw == C_ENDDO) && (do_count > 0 ))
                {
                do_count-- ;
                s_pointer = do_addr ;
                }          

/*    Change instrument   */

           else if ( ( nextw >= 0 ) && ( nextw < N_INST ) )
                c_inst = nextw ;
           nextw = song[ s_pointer++] ;
           } 
      clear_sound() ;
      } 

/******************************************************************/

main()             /*   lets go play a song or two  */

     {
     init_music() ;
     printf("The Interstel Theme . . .\n\n") ;
     play_music( interstel ) ;
     wait( 500 ) ;
     printf("Thus Spake Zarathustra by Richard Strauss . . .\n\n") ;
     play_music( spake ) ;
     }

