/****************************************************************** * 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 #include /*------------------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 ) ; }