#
#	ASCORE.AWK
#
#	Creates a Csound score from an "ASCORE" script
#   Provides a front-end for MIDI scoring with Mscore.
#
#
#  -----------------------------------------------------------------------
#  Copyright 2004--2009  Alfred Steffens Jr.
#  -----------------------------------------------------------------------
#  -----------------------------------------------------------------------
#    Copying Permission:
#
#    Ascore is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation.
#
#    Ascore is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Ascore (see file called "COPYING"); if not, write to the Free
#    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
#    02111-1307  USA
#  ------------------------------------------------------------------------
#
#	REVISION HISTORY:
#
#   ver. 4.300 06/27/09 A. Steffems Jr.  CTRLRAMP feature.
#   ver. 4.220 06/18/09 A. Steffems Jr.  BEGIN statement now required.
#	ver. 4.210 06/17/09 Alfred Steffens Jr.  BANKDEF definitions.
#	ver. 4.205 01/21/09 Alfred Steffens Jr.  Allow MIDI_MASTER mode to pass
#        channels designated as "ignore" channels.
#	ver. 4.204 01/02/09 Alfred Steffens Jr., MIDI_MUTE mode.
#	ver. 4.203 01/01/09 Alfred Steffens Jr., MIDI_MASTER mode.
#	ver. 4.202 11/25/08	Alfred Steffens Jr., more INLINE midi work. The
#       is_a_var_reference() function must find $ at index 1.
#	ver. 4.201 11/23/08	Alfred Steffens Jr.
#       Added syntax for INLINE midi CONTROL parameters
#
#-------------------------------------------------------------------------


BEGIN {

	tracks = 0
	tempo = 100
	scorelineno = 0
	scoretime = 0
	sectcount = 0
	varcount = 0
	reverbcount = 0
	skip_score = 0
	skip_track[1] = 0
	have_skiptrack = 0
	use_random = 0
	random_range = 0
	is_a_mixscore_line = 0	# mod 3.6
	is_a_mark_line = 0
	srand()
	not_defined = 0
    ifdef_count = 0
    ifdef_varcount = 0
    file_line_count = 0
    fatal_on_redef = 1
    max_instr_to_channel = 0
    midi_master_flag = 0
    midi_master_ctrlid[0] = 0
    midi_master_ctrlval[0] = 0
    midi_mute_flag = 0
    midi_master_ignore_count = 0
    maxctrlramp = 0
    rctrlnum = 1
    rctrlstart = 2
    rctrlend = 3
    rctrlduration = 4
    rctrldurincr = 5
    rctrlchannel = 6
    rctrlsteps = 7
    rctrlsteptime = 8
    rctrlstepvalue = 9
    rctrlcurrval = 10
    rctrlmore = 11
    rctrltimep = 12

	#debug_child = 1	# DEBUGGER


	print "ASCORE ver. 4.220 (090618), copyright 2009, Alfred Steffens Jr."
	print "GNU General Public License, see the file called COPYING"
	#
	# defaults
	#
	evaluate_durations(tempo)
}

#
#	ignore comments
#
$1 ~ /#/ {
    file_line_count++
	next
}

#
#	ignore blank lines
#
NF == 0 {
    file_line_count++
	next
}


#
#   BEGIN startment must come first
#
$1 ~ /BEGIN/ {
    print ";" > "hold.sco"
    print "; ASCORE generated Csound score" >> "hold.sco"
    print ";" >> "hold.sco"
    next
}
#   BEGIN


#
#	section statement (not used)
#
$1 ~ /SECTION/ {
	#
	#	A section definition
	#

	#
	#	must have at least 9 fields
	#
	#	1 = SECTION
	#	2 = name
	#	3 = awk file for riff or "sect"
	#	4 = variable name to hold instr #
	#	5 = variable name to hold start time
	#	6 = variable name to hold tempo
	#	7 = variable name to hold key
	#	8 = variable name to hold amplitude
	#	9 = variable name to hold pan
	#

	sectcount += 1
	sect_cmd[sectcount] = sprintf("echo junk | awk -f %s instr=$%s start=$%s\
			 tempo=$%s key=$%s amp=$%s pan=$%s >> hold.sco", \
			$3, $4, $5, $6, $7, $8, $9)
	sect_name[sectcount] = $2
	sect_file[sectcount] = $3
	sect_instr_name[sectcount] = $4
	sect_start_name[sectcount] = $5
	sect_tempo_name[sectcount] = $6
	sect_key_name[sectcount] = $7
	sect_amp_name[sectcount] = $8
	sect_pan_name[sectcount] = $9

	#print sect_name[sectcount]
	#print sect_cmd[sectcount]

    file_line_count++
	next

} # SECTION




#
#	tracks statement
#
$1 ~ /TRACKS/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: TRACKS statement syntax: need \"TRACKS <number>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	tracks = $2
	print "TRACKS = " tracks
	next

} # TRACKS


#
#	tempo statement
#
$1 ~ /TEMPO/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: TEMPO statement syntax: need \"TEMPO <number>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	tempo = $2
	evaluate_durations(tempo)

	if (NF >= 3 && index($3,"passthr")==1) {
		#
		#	pass-through command (mod 3.6, 2/17/2007)
		#	TEMPO will be placed in the score as it occurs here in the
		#	score section (since TEMPO statements can be placed anywhere
		#	in the file, those will just be extraneous)
		#	This will appear in the output as
		#		;TEMPO <new tempo> <score time>
		#	which the subsequent parsers will use to pass to midi.
		#
		print ";TEMPO", scoretime, tempo >> "hold.sco"
	}


	next

} #  TEMPO



#
#	MIDI control -- "fade" or "ramp" the control parameter automatically
#
$1 ~ /CTRLRAMP/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

    #
    #   MIDI_MASTER or MIDI_MUTE is handled like CONTROL:
    #   We substitute CONTROL and ignore the CTRLRAMP
    #

    #
    #   MIDI_MASTER mode?
    #   If yes, then we intercept any relevant midi commands and substitute
    #   so that the master channel only is heard and its control values
    #   remain flat (for recording purposes).
    #
    if (midi_master_flag)
    {
        #
        #   Does the channel match?
        #
        if ($6 == midi_master_channel)
        {
            #
            #   Yes, this is the channel to me mastered.
            #

            #
            #   Volume message?
            #
            if ($2 == "VOLUME" || $2 == 7)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midivol = find_midi_master_volume()
                if (midivol >= 0)
                {
                    print ";CONTROL", scoretime, $2, midivol, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   ModWheel message?
            #
            if (index($2,"MOD") == 1 || $2 == 1)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(1)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Pan message?
            #
            if ($2 == "PAN" || $2 == 10)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(10)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Effect1  message?
            #
            if ($2 == "YM_EF1" || $2 == 91)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(91)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Effect3  message?
            #
            if ($2 == "YM_EF3" || $2 == 93)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(93)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }

            #
            #   Any other CONTROL message given as a numeric ID?
            #
            if ($2 >= 0 && $2 <= 127)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl($2)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
        }
        else
        {
            #
            #   No, one of the channels that should be removed from the score
            #

            #
            #   We trurn off the volume on all channels besides the MIDI_MASTER
            #   except if it is a MIDI_MASTER "ignore" channel.  We can allow
            #   one or more channels to escape the block.
            #
            if (is_midi_master_ignore($4) == 0)
            {
                #
                #   Volume message?
                #
                if ($2 == "VOLUME" || $2 == 7)
                {
                    #
                    #   Set this volume to 0
                    #
                    print ";CONTROL", scoretime, $2, "0", $4 >> "hold.sco"
                    next
                }
            }
        }
    }
    # if -- MIDI_MASTER

    #
    #   MIDI_MUTE mode?
    #   The opposite of MIDI_MASTER--just mute selected channels are let
    #   the other channels pass through.
    #
    if (midi_mute_flag)
    {
        #
        #   Does the channel match?
        #
        if (find_midi_mute_channel($6))
        {
            #
            #   Yes, this is a channel to me muted.
            #

            #
            #   Volume message?
            #
            if ($2 == "VOLUME" || $2 == 7)
            {
                #
                #   Set this volume to 0
                #
                print ";CONTROL", scoretime, $2, "0", $4 >> "hold.sco"
                next
            }

        }
    }
    # if -- MIDI_MUTE


    #
    #   Valid CTRLRAMP
    #
    #   CTRLRAMP  ctrlnum   v1,v2   duration   dur_incr  midi_channel
    #   ex:
    #       CTRLRAMP  7  80,50  12*  0.6*  2
    #
    maxctrlramp++
    #
    #   control number
    #
    ctrlramp[maxctrlramp,rctrlnum] = $2
    #
    #   control value range
    #
    tmperr = split($3, tmpary, ",")
    if (tmperr != 2)
    {
        print "ERROR: syntax"  | "cat 1>&2"
        print "(", $0, ")"  | "cat 1>&2"
        next
    }
    tmp_v1 = int(tmpary[1])
    tmp_v2 = int(tmpary[2])
    ctrlramp[maxctrlramp,rctrlstart] = tmp_v1
    ctrlramp[maxctrlramp,rctrlend] = tmp_v2
    #
    #   total duration (string)
    #
    ctrlramp[maxctrlramp,rctrlduration] = $4
    #
    #   duration incr (string)
    #
    ctrlramp[maxctrlramp,rctrldurincr] = $5
    #
    #   midi channel
    #
    ctrlramp[maxctrlramp,rctrlchannel] = $6
    #
    #   calculated values
    #
    tmp_steps = ramp_steps($4,$5)
    ctrlramp[maxctrlramp,rctrlsteps] = tmp_steps
    ctrlramp[maxctrlramp,rctrlsteptime] = ramp_steptime($5)
    ctrlramp[maxctrlramp,rctrlstepvalue] = ramp_stepvalue(tmp_v1,tmp_v2,tmp_steps)
    ctrlramp[maxctrlramp,rctrlcurrval] = tmp_v1     # current state
    ctrlramp[maxctrlramp,rctrlmore] = tmp_steps     # steps remaining
    ctrlramp[maxctrlramp,rctrltimep] = 0

    #
    #   Set the value immediately to the starting value
    #
    print ";CONTROL", scoretime, $2, tmp_v1, $6 >> "hold.sco"

    next
}
#   CTRLRAMP


#
#	MIDI control change parameter
#
$1 ~ /CONTROL/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 4) {
		print "ERROR: CONTROL statement syntax: need \"CONTROL <type> <value> <channel>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}


    #
    #   MIDI_MASTER mode?
    #   If yes, then we intercept any relevant midi commands and substitute
    #   so that the master channel only is heard and its control values
    #   remain flat (for recording purposes).
    #
    if (midi_master_flag)
    {
        #
        #   Does the channel match?
        #
        if ($4 == midi_master_channel)
        {
            #
            #   Yes, this is the channel to me mastered.
            #

            #
            #   Volume message?
            #
            if ($2 == "VOLUME" || $2 == 7)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midivol = find_midi_master_volume()
                if (midivol >= 0)
                {
                    print ";CONTROL", scoretime, $2, midivol, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   ModWheel message?
            #
            if (index($2,"MOD") == 1 || $2 == 1)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(1)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Pan message?
            #
            if ($2 == "PAN" || $2 == 10)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(10)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Effect1  message?
            #
            if ($2 == "YM_EF1" || $2 == 91)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(91)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
            #
            #   Effect3  message?
            #
            if ($2 == "YM_EF3" || $2 == 93)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl(93)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }

            #
            #   Any other CONTROL message given as a numeric ID?
            #
            if ($2 >= 0 && $2 <= 127)
            {
                #
                #   Set this volume to the user-specified (flat) level
                #
                midictrl = find_midi_master_ctrl($2)
                if (midictrl >= 0)
                {
                    print ";CONTROL", scoretime, $2, midictrl, $4 >> "hold.sco"
                    next
                }
            }
        }
        else
        {
            #
            #   No, one of the channels that should be removed from the score
            #

            #
            #   We trurn off the volume on all channels besides the MIDI_MASTER
            #   except if it is a MIDI_MASTER "ignore" channel.  We can allow
            #   one or more channels to escape the block.
            #
            if (is_midi_master_ignore($4) == 0)
            {
                #
                #   Volume message?
                #
                if ($2 == "VOLUME" || $2 == 7)
                {
                    #
                    #   Set this volume to 0
                    #
                    print ";CONTROL", scoretime, $2, "0", $4 >> "hold.sco"
                    next
                }
            }
        }
    }
    # if -- MIDI_MASTER

    #
    #   MIDI_MUTE mode?
    #   The opposite of MIDI_MASTER--just mute selected channels are let
    #   the other channels pass through.
    #
    if (midi_mute_flag)
    {
        #
        #   Does the channel match?
        #
        if (find_midi_mute_channel($4))
        {
            #
            #   Yes, this is a channel to me muted.
            #

            #
            #   Volume message?
            #
            if ($2 == "VOLUME" || $2 == 7)
            {
                #
                #   Set this volume to 0
                #
                print ";CONTROL", scoretime, $2, "0", $4 >> "hold.sco"
                next
            }

        }
    }
    # if -- MIDI_MUTE



	#
	#	$2 = <type>, which must be MODWHEEL, PORTATIME, PORTASWITCH, VOLUME,
    #           BANK_MSB, BANK_LSB, PAN, YM_EF1, YM_EF3, or a decimal number
	#	$3 = <value>, 0-127
	#	$4 = <channel>, 0-15
	#
    print ";CONTROL", scoretime, $2, $3, $4 >> "hold.sco"

	next

} #  CONTROL



#
#	MIDI program change parameter
#
$1 ~ /PROGRAM/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 3) {
		print "ERROR: PROGRAM statement syntax: need \"PROGRAM <value> <channel>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	$2 = <value>, 0-127
	#	$3 = <channel>, 0-15
	#
	print ";PROGRAM", scoretime, $2, $3 >> "hold.sco"


	next

} #  PROGRAM



#
#	MIDI pitch bend parameter
#
$1 ~ /PITCH/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 4) {
		print "ERROR: PITCH statement syntax: need \"PITCH  <value> <channel>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	$2 = <value>, -8191 to +8191
	#	$3 = <channel>, 0-15
	#
	#
	print ";PITCH", scoretime $2, $3 >> "hold.sco"


	next

} #  PITCH


#
#	MIDI system exclusive
#
$1 ~ /SYSEX/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: SYSEX statement syntax: need \"SYSEX <value>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	The Ascore file must contain:
    #
    #       SYSEX <ch> d0 d1 d2 ... dn
    #
    #       where the data bytes, d0 d1, ..., must be 2-digit hexidecimal
    #       in upper case.
	#
    #   We export to the Csound score the following line:
    #       ;SYSEX <scoretime> <ch> D0 D1 ... Dn
    #

    #   set the score time
    sysexline = sprintf(";SYSEX %s", scoretime)
    #   set the midi channel
    sysexline = sprintf("%s %s", sysexline, $2)
    #   add the data bytes
    for (sj = 3; sj <= NF; sj++)
        sysexline = sprintf("%s %s", sysexline, $sj)

    print sysexline >> "hold.sco"

	next

} #  SYSEX


#
#   BANKDEF
#
$1 ~ /BANKDEF/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

    #
    #   BANKDEF doesn't care about score time
    #
    bankdef_line = sprintf(";%s", $0)
	print bankdef_line >> "hold.sco"

    next

} # BANKDEF


#
#	set statement
#
$1 ~ /set/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 3) {
		print "ERROR: set statement syntax: need \"set <var_name> <number>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}
	fields_in_set_statement = NF


	#
	#	check if there is already a variable with that name
	#
	var_already_exists = 0
	for (i=1; i<varcount; i++) {
		if ($2 == var_name[i]) {
			var_already_exists = 1
			varindex = i
		}
	}

	if (var_already_exists) {
		# use the index found above
        if (fatal_on_redef)
        {
		    print "ERROR: variable \"" $2 "\" defined again at line", file_line_count | "cat 1>&2" 
            exit 1
        }
        else
        {
		    print "WARNING: variable \"" $2 "\" defined again at line", file_line_count | "cat 1>&2" 
        }
	}
	else {
		#
		# new variable, increment count
		#
		varcount += 1
		varindex = varcount
	}

	var_name[varindex] = $2

	#
	#	since this is not a function (but may be redefined
	#	later as one)
	#
	var_op[varindex] = "null"
	var_operand[varindex] = "null"
	var_reference[varindex] = "null"


	#
	#	possibilities for value:
	#	1.	a number  (NF == 3, arg3 is a number)
	#	2.	another variable  (NF==3, arg3 starts with "$")
	#	3.	a list  (NF==3, arg3 is a "{" )
	#
	if ($3 == "{") {
		#
		#	read lines until a closing "}" is found
		#
		var_size[varindex] = 0
		getline
		while ($1 != "}") {

			if (NF > 1) {
				print "ERROR: set statement syntax: 1 field allowed in list, line", file_line_count | "cat 1>&2" 
				exit 1
			}

			if (is_an_alias($1)) {
				var_value[varindex] = sprintf("%s %s", var_value[varindex], insert_alias($1))
				var_size[varindex] += 1
			}
			else if (is_a_var_reference($1)) {
				j = get_var_index_from_reference($1)

				if (is_an_operation(var_name[j])) {
					newvalue = perform_operation(var_reference[j], var_op[j], var_operand[j])
					var_value[varindex] = sprintf("%s %s", \
						var_value[varindex], newvalue)
					var_size[varindex] += 1
					#print "set a VAR REFERENCE field " $3 " value= (" var_value[varindex] ")"
				}
				else {
					var_value[varindex] = sprintf("%s %s", \
						var_value[varindex], var_value[j])
					var_size[varindex] += var_size[j]
					#print "set a VAR REFERENCE field " $1
					#print "value= (" var_value[varcount] ")"
				} # else -- is not an operation
			}
			else {
				var_value[varindex] = sprintf("%s %s", var_value[varindex], $1)
				var_size[varindex] += 1
			}

			getline
		} # while

	}
	else {
		#
		# 	a single field or list specified on one line
		#
		# copy the string starting at field #3
		#
		#-----------------------------------------------
		#	the following code can give the wrong answer
		#	which is why it is not used
		#	ind = index($0, $3)
		#	var_value[varcount] = substr($0, ind)
		#-----------------------------------------------

		#
		#	first field (number 3)
		#
		if (is_an_alias($3)) {
			var_value[varindex] = insert_alias($3)
			var_size[varindex] += 1
		}
		else if (is_a_var_reference($3)) {

			i = get_var_index_from_reference($3)
			if (is_an_operation(var_name[i])) {
				#
				#	if this is an "operation" ---> from eval
				#
				var_value[varindex] = perform_operation(var_reference[i], var_op[i], var_operand[i])
				var_size[varindex] += 1
				#print "set a VAR REFERENCE field " $3 " value= (" var_value[varindex] ")"
			}
			else {
				#
				#	else just a normal variable value
				#
				var_value[varindex] = var_value[i]
				var_size[varindex] += var_size[i]
				#print "var_size= " var_size[varindex]
				#print "set a VAR REFERENCE field " $3
				#print "value= (" var_value[varcount] ")"
			}
		}
		else {
			var_value[varindex] = $3
			var_size[varindex] += 1
		}

		#
		#	fields 4 through the last
		#
		for (i=4; i<=NF; i++) {

			if (is_an_alias($i)) {
				var_value[varindex] = sprintf("%s %s", \
					var_value[varindex], insert_alias($i))
				var_size[varindex] += 1
			}
			else if (is_a_var_reference($i)) {
				j = get_var_index_from_reference($i)

				if (is_an_operation(var_name[j])) {
					newvalue = perform_operation(var_reference[j], var_op[j], var_operand[j])
					var_value[varindex] = sprintf("%s %s", \
						var_value[varindex], newvalue)
					var_size[varindex] += 1
					#print "set a VAR REFERENCE field " $3 " value= (" var_value[varindex] ")"
				}
				else {
					var_value[varindex] = sprintf("%s %s", \
						var_value[varindex], var_value[j])
					var_size[varindex] += var_size[j]
					#print "var_size= " var_size[varindex]
					#print "set a VAR REFERENCE field " $i
					#print "value= (" var_value[varcount] ")"
				} # else -- not an operation -- just a value
			}
			else {
				var_value[varindex] = sprintf("%s %s", \
					var_value[varindex], $i)
				var_size[varindex] += 1
			}

		} # for -- i

	} # else -- a single line

	#print "set: found " var_size[varindex] " fields"
	#print "set:  (" var_value[varindex] ") from (" $0 ") at index " ind

#	if (fields_in_set_statement == 3) {
#		a1 = Sc_EvalVar(var_name[varindex])
#		print "var " var_name[varindex] " evaluates to " a1
#		if (is_a_numeric_var(var_value[varindex])) {
#			print var_value[varindex] " is a number"
#		}
#	}
	next

} #  set



#
#	seteval statement
#
$1 ~ /eval/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	#
	#	eval should be used like the following examples:
	#
	#	eval newvar $oldvar * 3.4
	#	eval newvar $oldvar / 3.4
	#	eval newvar $oldvar + 3.4
	#	eval newvar $oldvar - 3.4
	#
	#	eval newvar $oldvar <op> <number>
	#
	#	or
	#
	#	eval newvar $oldvar1 <op> $oldvar2
	#

	if (NF < 5) {
		print "ERROR: seteval statement syntax: need \"seteval <var_name> <$var> op <number>\" line", file_line_count  | "cat 1>&2"
		exit 1
	}
	fields_in_set_statement = NF

	#
	#	check if there is already a variable with that name
	#
	var_already_exists = 0
	for (i=1; i<varcount; i++) {
		if ($2 == var_name[i]) {
			var_already_exists = 1
			varindex = i
		}
	}

	if (var_already_exists) {
		# use the index found above
	}
	else {
		#
		# new variable, increment count
		#
		varcount += 1
		varindex = varcount
		var_name[varindex] = $2
	}


	#
	#	the 3rd argument should be the reference to another
	#	variable:  $var1
	#
	if (is_a_var_reference($3)) {
		#
		#	we don't need the index yet, because we wont evaluate
		#	the function now
		#
		i = get_var_index_from_reference($3)
		var_reference[varindex] = $3
		var_value[varindex] = "null"
		var_size[varindex] = 1
	}
	else {
		print "ERROR: seteval syntax: 1st argument must be a" \
			" reference ($ prefix) line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	for now we just store the operation
	#
	var_op[varindex] = $4
	var_operand[varindex] = $5

	#
	#	that's all for now--evaluation takes place at Sc_EvalVar
	#

	next

} # eval



$1 ~ /child/ {

#
#		child  newname  oldname  column_to_mod  operator  arg1  arg2 ....
#

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 6) {
		print "ERROR: child statement syntax: not enough arguments for line (" $0 ") line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	create the "argstring" to be passed to Mod_ScVar
	#

	#
	#	allow comments at the end of the "child" line
	#
	comchd_start = 0
	for (i=1; i<=NF; i++) {
		if (index($i, "#") == 1) {
			#	found a comment on the child line
			comchd_start = i
			break
		}
	}

	if (comchd_start) {
		#
		#	comments were found at the end
		#
		modscvar_argstring = sprintf("%s", $4)
		for (i=5; i<=(comchd_start-1); i++) {
			modscvar_argstring = sprintf("%s %s", modscvar_argstring, $i)
		}
	}
	else {
		#
		#	no comments
		#
		modscvar_argstring = sprintf("%s", $4)
		for (i=5; i<=NF; i++) {
			modscvar_argstring = sprintf("%s %s", modscvar_argstring, $i)
		}
	}


	if (debug_child) {
	print "DEBUGGER: child: argstring= (" modscvar_argstring ")"
	}

	#
	#	arguments:
	#
	#		newname,  oldname,  argstring
	#
	#	where "argstring" is $4 $5 .....$NF
	#
	Mod_ScVar($2, $3, modscvar_argstring)

	#
	#	now the above call to Mod_ScVar will have added a new variable to the
	#	table with the name of "newname"
	#

	if (debug_child) {
	#	DEBUGGER : find the new variable
	i = get_var_index_from_name($2)
	print "DEBUGGER: child: new variable has index (" i ")"
	}


	next
} #  child


#
#	alias	definition
#
$1 ~ /alias/ {
	#
	#	usage:
	#
	#	alias <alias_name> {
	#		<input_1> <output_1>
	#		<input_2> <output_2>
	#		<input_3> <output_3>
	#			.		.
	#			.		.
	#		<input_N> <output_N>
	#
	#	example definition:
	#
	#	alias noteinstr1 {
	#		7.05	20
	#		7.06	20
	#		7.07	21
	#		7.08	21
	#	}
	#
	#	example in the score:
	#
	#	~noteinstr1(7.05)
	#	will be substituted with 20
	#	~noteinstr1(7.06)
	#	will be substituted with 20
	#	~noteinstr1(7.07)
	#	will be substituted with 21
	#	~noteinstr1(7.08)
	#	will be substituted with 21
	#
	#
	#	code:
	#	alias_list[name, input] = output
	#

    file_line_count++
	curr_alias_name = $2
	if ($3 != "{") {
		print "ERROR: alias statement syntax: curly bracket not found, line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	#	read the following lines until a closing brace is found
	#
	getline
	while ($1 != "}") {

		if (NF < 2) {
			print "ERROR: alias statement syntax: at least 2 fields needed on a line", file_line_count | "cat 1>&2" 
			exit 1
		}

		alias_list[curr_alias_name, $1] = $2

		getline
	} # while

	next
} # alias



$1 ~ /_DEFINE_/ {

    file_line_count++
    #
    #   Find a variable by the name of the first token
    #
    ifdef_varindex = 0
    if (ifdef_varcount > 0)
    {
        for (i=1; i<=ifdef_varcount; i++) {
            if ($2 == ifdef_var_name[i]) {
                ifdef_varindex = i
            }
        }
    }

    #
    #   Does the _DEFINE_ exist yet?
    #
    if (ifdef_varindex == 0)
    {
        #
        #   It does not exist yet--add it
        #
        ifdef_varcount++
        ifdef_var_name[ifdef_varcount] = $2
    }

    next
}
# _DEFINE_



#
#   A "Pound Define" for skipping over code if a certain condition
#   is met
#   (unlike C code which is actually skipped after a FALSE define, a FALSE
#   here merely sets not_defined=1,  so nested IFDEF lines are still read,
#   therefore we keep track of any nesting with the "ifdef_count")
#
$1 ~ /_IFDEF_/ {

    file_line_count++
    ifdef_count++

    #
    #   Ignore empty argument
    #
    if (NF < 2) {
        next
    }


    #
    #   Find a variable by the name of the first token
    #
	ifdef_varindex = 0
	for (i=1; i<=ifdef_varcount; i++) {
		if ($2 == ifdef_var_name[i]) {
			ifdef_varindex = i
		}
	}

    #
    #   If the variable has not been defined then the following code
    #   is not valid
    #
    if (ifdef_varindex == 0)
    {
        not_defined = 1
        next
    }

    next
}
#   IFDEF


#
#   Alternative to the preceding _IFDEF_ statement
#
$1 ~ /_ELSE_/ {

    file_line_count++
    #
    #   If this is an _ELSE_ nested inside code that is already turned off,
    #   then ignore it
    #
    if ((not_defined && ifdef_count > 1) || ifdef_count == 0)
    {
        next
    }

    #
    #   Valid _ELSE_
    #   Toggle the value
    #
    not_defined = 1 - not_defined
   
    next 
}
# _ELSE_



#
#   Terminate an _IFDEF_ condition
#
$1 ~ /_ENDIF_/ {

    file_line_count++
    #
    #   Ignore a misplaced ENDIF
    #
    if (ifdef_count == 0)
    {
        not_defined = 0
        next
    }

    #
    #   Decrement the number of ifdef entries
    #
    ifdef_count--
    if (ifdef_count < 0)
    {
        ifdef_count = 0
    }

    #
    #   When we reach the bottom, turn back on
    #
    if (ifdef_count == 0)
    {
        #
        #   Outside of all IFDEF / ENDIF pairs the code must be valid
        #
        not_defined = 0
    }

    next
}
# ENDIF




#
#	Header file
#
$1 ~ /HEADER/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: HEADER statement syntax: need \"HEADER <filename>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	cmd = sprintf("cat %s >> hold.sco", $2)
	system(cmd)
	next

} #  HEADER



#
#	Include file
#
$1 ~ /INCLUDE/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: INCLUDE statement syntax: need \"INCLUDE <filename>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	cmd = sprintf("cat %s >> hold.sco", $2)
	system(cmd)
	next

} #  INCLUDE




#
#	End
#
$1 ~ /END/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	# mod. 3.6
	if (index($1, "END") == 1) {
		#
		#	only signal the END if "END" is at the beginning of $1
		#
		print ";----------------------------------------------------------------" >> "hold.sco"
		print ";	End of Score" >> "hold.sco"
		print "e" >> "hold.sco"
		print ";----------------------------------------------------------------" >> "hold.sco"
		print "" >> "hold.sco"
		next
	}


} #   END



#
#	REVERB macros
#
$1 ~ /REVERB/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	#
	#	must have at least 4 fields
	#
	#	1 = REVERB
	#	2 = name
	#	3 = awkfile
	#	4 = first instrument number
	#
	if (NF < 4) {
		print "ERROR: REVERB statement syntax: need \"REVERB <var_name> <awkfile> <instr1> <instr2> ...\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	reverbcount += 1
	reverb_name[reverbcount] = $2
	reverb_file[reverbcount] = $3
	#
	#	allow comments at the end of a REVERB line
	#
	comrvb_start = 0
	for (i=1; i<=NF; i++) {
		if (index($i, "#") == 1) {
			#	found a comment on the REVERB line
			comrvb_start = i
			break
		}
	}
	if (comrvb_start) {
		#
		#	comments were found at the end of the REVERB line
		#	starting at field number comrvb_start
		#
		reverb_numinstr[reverbcount] = (comrvb_start-1) - 3
	}
	else {
		#
		#	no comments
		#
		reverb_numinstr[reverbcount] = NF - 3
	}
	numi = reverb_numinstr[reverbcount]
	#
	#	all the instrument numbers form a string
	#
	if (comrvb_start) {
		#
		#	get the remainder of the line -- but leave off the comment
		#
		cindex = index($0, "#")
		reverb_instr[reverbcount] = substr($0, match($0, /\ [1-9][0-9]*/)+1, (cindex-1))
	}
	else {
		#
		#	get the remainder of the line
		#
		reverb_instr[reverbcount] = substr($0, match($0, /\ [1-9][0-9]*/)+1)
	}
	#print "reverb instr string: " reverb_instr[reverbcount]

	next

} #		REVERB


#
#	turn Reverb On
#
$1 ~ /RVB_ON/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 3) {
		print "ERROR: RVB_ON statement syntax: need \"RVB_ON <name> <amp>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	# find the index corresponding to the Reverb name
	#
	found_reverb=0
	for (i=1; i<=reverbcount; i++) {

		if ($2 == reverb_name[i]) {
			reverb_index = i
			found_reverb=1
			break;
		} # if

	} # for i

	if (found_reverb) {
		reverb_start[reverb_index] = scoretime
		reverb_amp[reverb_index] = $3
	}

	next
} #  RVB_ON


#
#	turn Reverb Off
#
$1 ~ /RVB_OFF/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}

	if (NF < 2) {
		print "ERROR: RVB_OFF statement syntax: need \"RVB_OFF <name>\" line", file_line_count | "cat 1>&2" 
		exit 1
	}

	#
	# find the index corresponding to the Reverb name
	#
	found_reverb=0
	for (i=1; i<=reverbcount; i++) {

		if ($2 == reverb_name[i]) {
			reverb_index = i
			found_reverb=1
			break;
		} # if

	} # for i

	if (found_reverb) {

		reverb_end[reverb_index] = scoretime
		rvb_duration = reverb_end[reverb_index] - reverb_start[reverb_index]

		#
		#	get individual instr numbers
		#
		split(reverb_instr[reverb_index], rvb_ary)
		for (i=1; i<=reverb_numinstr[reverb_index]; i++) {

			instr = rvb_ary[i]
			cmd = sprintf("echo junk | awk -f %s instr=%d start=%g duration=%g amp=%g >> hold.sco", \
					reverb_file[reverb_index], 
					instr, reverb_start[reverb_index], 
					rvb_duration, reverb_amp[reverb_index])

			#print "reverb awk cmd = (" cmd ")"
			system(cmd)
		} # for i
	}

	next
} #  RVB_OFF


#
#	Use SKIPSCORE AND STARTSCORE together
#
$1 ~ /SKIPSCORE/ {

	if (not_defined) {
		next
	}
	#
	#	signal to skip all following score lines
	#	until a STARTSCORE statement is found
	#

	skip_score = 1
	next

} #  SKIPSCORE

#
#	Use SKIPSCORE AND STARTSCORE together
#
$1 ~ /STARTSCORE/ {

	if (not_defined) {
		next
	}
	#
	#	turn score lines back on
	#

	skip_score = 0
	next

} #  SKIPSCORE

#
#	SKIPTRACK
#	usage:
#		SKIPTRACK 2 3 6
#	will cause tracks 2, 3, and 6 to be skipped until a
#	SKIPTRACK with no arguments is found, which resets the skip
#	to "don't skip"
#
$1 ~ /SKIPTRACK/ {

	if (skip_score || not_defined) {
		next
	}
	#
	#	expect a white-space separated list of tracks to skip
	#

	#
	#	first, clear any previous SKIPTRACK statement
	#
	for (j=1; j<=tracks; j++) {
		if ((j) in skip_track) {
			delete skip_track[j]
		}
	}
	skip_track[1] = 0
	have_skiptrack = 0

	#
	#	set the tracks to skip
	#
	j = 1
	for (i=2; i<=NF; i++) {

		itracknumber = $i
		if (is_a_number(itracknumber)) {

			skip_track[j] = itracknumber
			j++

			have_skiptrack = 1
		} # if -- is_a_number
	} # for -- i

	next

} #  SKIPTRACK



#
#	RANDOMIZE
#	usage:
#		RANDOMIZE 0.X
#	Adds a random offset (uniform prob dist=white noise) to
#	every start time after this statement.  Using a RANDOMIZE
#	statement with no argument turns it off.  The argument
#	should be a fraction between 0 and 1 which is the percent
#	of a quarter.
#
#	example:
#		RANDOMIZE 0.21
#	means that at most +- 21 percent of a quarter note will be
#	added (subtracted) from the start time.  The seed is given at
#	the start of the score.
#
$1 ~ /RANDOMIZE/ {

    file_line_count++
	if (skip_score || not_defined) {
		next
	}
	#
	#	reset any previous RANDOMIZE statement
	#
	use_random = 0
	random_range = 0

	#
	#	look for the range argument
	#
	if (NF >= 2) {
		if (is_a_number($2)) {
			random_range = $2
			use_random = 1
		}
	}

	next

} # RANDOMIZE


#
#
#	CS2MTX   (mod. 3.6)
#
#	This is a pass-through for subsequent scripts that convert a
#	csound score to MIDI.  These don't affect csound at all.
#	Any arguments after the "CS2MTX" keyword are parsed by pre_cs2mtx.awk
#	which re-formats then as parameters to cs2mtx.awk.  These statements will
#	assign MIDI parameters to the csound instrument:
#
#	MIDI channel:
#
#		CS2MTX midi_channel <number> instrument <number>  (more than one instr)
#
#	each time a statement like this occurs, more instrument numbers are
#	assigned to this midi channel.
#
#	Pitch shift:
#
#		CS2MTX midi_channel <number> nshift <+-half_tones>
#
#	MIDI Program (patch):
#
#		CS2MTX midi_channel <number> program <0--127>
#
#	where program number is a general midi patch number.
#
#	MIDI channel volume:
#
#		CS2MTX midi_channel <number> volume <0--127>
#
#	where program number is a general midi patch number.
#
#
$1 ~ /CS2MTX/ {

    file_line_count++
	if (not_defined) {
		next
	}

    #
    #   find the instrument and remember it
    #
    if (index($4, "instrument") == 1)
    {
        if (NF < 5)
        {
            print "ERROR: CS2MTX instrument line at ", file_line_count,\
                  ", no number given" | "cat 1>&2" 
            exit 1
        }
        max_instr_to_channel++
        instr_to_channel[$5] = $3
    }


    #
    #   MIDI_MASTER mode?
    #   If yes, then we intercept any relevant midi commands and substitute
    #   so that the master channel only is heard and its control values
    #   remain flat (for recording purposes).
    #   Since this is a CS2MTX line, the only relevant parameter here is
    #   the volume.
    #
    if (midi_master_flag)
    {
        #
        #   Does the channel match?
        #
        if ($3 == midi_master_channel)
        {
            #
            #   Yes, this is the channel to me mastered.
            #

            #
            #   Channel volume, hold flat
            #
            if (index($4, "volume") == 1)
            {
                midivol = find_midi_master_volume()
                if (midivol >= 0)
                {
                    #
                    #   set volume to user-specified value
                    #
                    tmpstr = sprintf("CS2MTX midi_channel %s volume %s", $3, midivol)
                    #
                    #   Print this instead of passing it thry
                    #
                    print ";" tmpstr >> "hold.sco"
                    next
                }
            }
        }
        else
        {
            #
            #   No, one of the channels to be ignored
            #
            if (is_midi_master_ignore($3) == 0)
            {
                if (index($4, "volume") == 1)
                {
                    #
                    #   set volume to 0
                    #
                    tmpstr = sprintf("CS2MTX midi_channel %s volume 0", $3)
                    #
                    #   Print this instead of passing it thry
                    #
                    print ";" tmpstr >> "hold.sco"
                    next
                }
            }
        }
    }
    # if -- MIDI_MASTER


    #
    #   MIDI_MUTE mode?
    #
    if (midi_mute_flag)
    {
        #
        #   Does the channel match?
        #
        if (find_midi_mute_channel($3))
        {
            #
            #   Yes, this is the channel to me muted.
            #
            if (index($4, "volume") == 1)
            {
                #
                #   set volume to 0
                #
                tmpstr = sprintf("CS2MTX midi_channel %s volume 0", $3)
                #
                #   Print this instead of passing it thry
                #
	            print ";" tmpstr >> "hold.sco"
	            next
            }

        }
    }
    # if -- MIDI_MUTE



	#
	#	just pass them through
	#
	print ";" $0 >> "hold.sco"
	next

} #  CS2MTX


#
#   MIDI_MASTER
#
#   Use this for selecting one midi at a time channel for recording.
#   This option mutes all other midi channels (volume to 0) and sets
#   all CONTROL midi commands to a pre-determined flat level.
#
#   The purpose of this option is to not tamper with the total number
#   of midi messages while allowing the one selected channel to play
#   flatly.  If the number of midi messages changes, the sequencer queue
#   may speed up slightly making the final mix unsynchronized.
#
#   MIDI_MASTER ch=<ch> ctrl###=<num>  ... ctrl###=<num> [ignore=<ch> ...]
#
#       where
#           ch      =       midi channel to keep
#           ctrl### =       control message to hold flat:
#                           "###" is the control ID (0--127), for example,
#                           1=modulation, 7=volume, 10=pan, 91=reverb_send.
#                           The "###" ID number must be 3 digits.
#                           "<num>" is the value to set every time this ID
#                           is found.
#           ignore  =       a midi channel to ignore, that is, allow it to
#                           pass through and be heard unaltered.
#
#   There can be no whitespace separating the "=" synbols.
#
#   This option was designed to occur only once in the score.  That means that
#   there can be only one of
#           midi_master_flag
#           midi_master_ch_count
#           midi_master_ctrl_count
#           midi_master_ctrlid[]
#           midi_master_ctrlval[]
#
$1 ~ /MIDI_MASTER/ {

    file_line_count++
	if (not_defined) {
		next
	}

    midi_master_ctrl_count = 0
    midi_master_ch_count = 0
    for (i=2; i <= NF; i++)
    {
        mmerr = split($i, mmarray, "=")
        if (mmerr != 2)
        {
		    print "ERROR: MIDI_MASTER: syntax, field", $i, " line", file_line_count | "cat 1>&2" 
		    exit 1
        }

        if (mmarray[1] == "ignore")
        {
            midi_master_ignore_count++
            midi_master_ignore[midi_master_ignore_count] = int(mmarray[2])
            continue
        }

        if (mmarray[1] == "ch")
        {
            midi_master_channel = mmarray[2]  # valid if 1--128
            midi_master_ch_count = 1
            continue
        }

        if (index(mmarray[1], "ctrl") == 1)
        {
            ctrlid = substr(mmarray[1], 5)
            ctrlval = mmarray[2]
            if (ctrlid < 0 || ctrlid > 127)
            {
		        print "ERROR: MIDI_MASTER: control id range must be 0--127, line", file_line_count | "cat 1>&2" 
		        exit 1
            }
            if (ctrlval < 0 || ctrlval > 127)
            {
		        print "ERROR: MIDI_MASTER: control value range must be 0--127, line", file_line_count | "cat 1>&2" 
		        exit 1
            }
            midi_master_ctrl_count++
            midi_master_ctrlid[midi_master_ctrl_count] = ctrlid
            midi_master_ctrlval[midi_master_ctrl_count] = ctrlval
            continue
        }
    }


    if (midi_master_ch_count == 0)
    {
		print "ERROR: MIDI_MASTER: no midi channel, line", file_line_count | "cat 1>&2" 
		exit 1
    }
    if (midi_master_ctrl_count == 0)
    {
		print "ERROR: MIDI_MASTER: no control parameters, line", file_line_count | "cat 1>&2" 
		exit 1
    }

    #
    #   Inform the remainder of the score that we are in MIDI_MASTER mode.
    #
    midi_master_flag = 1
	next
} #  MIDI_MASTER



#
#   MIDI_MUTE
#
#   The complimentary operation to MIDI_MASTER.  Mute one or more MIDI
#   channels, leaving the rest to pass through.
#
$1 ~ /MIDI_MUTE/ {

    file_line_count++
	if (not_defined) {
		next
	}

    midi_mute_ctrl_count = 0
    midi_mute_ch_count = 0
    for (i=2; i <= NF; i++)
    {
        mmerr = split($i, mmarray, "=")
        if (mmerr != 2)
        {
		    print "ERROR: MIDI_MUTE: syntax, field", $i, " line", file_line_count | "cat 1>&2" 
		    exit 1
        }

        if (mmarray[1] == "ch")
        {
            midi_mute_channel[i-1] = int(mmarray[2])  # valid if 1--128
            midi_mute_ch_count++
            continue
        }
    }

    if (midi_mute_ch_count == 0)
    {
		print "ERROR: MIDI_MUTE: no midi channel, line", file_line_count | "cat 1>&2" 
		exit 1
    }

    #
    #   Inform the remainder of the score that we are in MIDI_MASTER mode.
    #
    midi_mute_flag = 1
	next
} #  MIDI_MUTE




#	/////////////////////////////////////////////////////////////////
#	score line
#	Default line: a score line
#	No comments allowed on this line
#	/////////////////////////////////////////////////////////////////
#
{

    file_line_count++
	scorelineno += 1

	#
	#	if the "skip_score" field is set, this is like commenting-out
	#	the following score lines
	#
	if (skip_score || not_defined) {
		next
	}

	#
	#	print PASS-THRU comments right here
	#	mod. 3.6
	#
	if (($1 == "c") || (index($1, "c")==1)) {

		if ($1 == "c") {
			#
			#	white-space follows the "c"
			#
			cmt = sprintf("; ")
			for (j=2; j<=NF; j++) {
	
				cmt = sprintf("%s %s", cmt, $j)
	
			} # for j
			print cmt >> "hold.sco"
		}
		else {
			#
			#	the "c" is the first letter of something else
			#
			split($1,arytmp,"c")
			cmt = sprintf(";%s", arytmp[2])
			for (j=2; j<=NF; j++) {
	
				cmt = sprintf("%s %s", cmt, $j)
	
			} # for j
			print cmt >> "hold.sco"
		}

		next
	}



	if (NF != (tracks+1)) {

		print "ERROR: score line " scorelineno ": number of columns NOT EQUAL value of TRACKS, line", file_line_count | "cat 1>&2" 
		print "line: (" $0 ")"
		exit 1

	} # -- if NF


	print "timing: " $1
	#
	#	read the time field
	#	if the field is a number, then the start time taks this value
	#
	if (is_a_number($1)) {
		if ($1 > 0) {
			print "score time set " $1
			scoretime = $1
		}
	}
	else {
		print "score time  " scoretime
	}



	#
	#	loop through the track fields, see if any are section names
	#
	for (i=2; i<=NF; i++) {

		#
		#	July 4, 2005 -- SKIPTRACK
		#
		if (have_skiptrack) {
			#
			#	if the "SKIPTRACK" keyword is found
			#	we must skip that track in the score section
			#
			if (match_skiptrack(i-1)) {
				#
				#	ignore this field (column) and continue to
				#	the next column
				#
				continue
			}
		} # if -- skip_track


		found_sect = 0
		for (j=1; j<=sectcount; j++) {
			#
			#	sect name ?
			#
			if ($i == sect_name[j]) {
				found_sect = 1
				sect_num = j
				break;
			} # if -- matched a section name

		} #  for j

		#
		#	if we found a section name, 
		#	then look for its variables
		#
		if (found_sect) {

			print "sect : " sect_name[sect_num] " : line " scorelineno " : field " i

			#
			#	find instr name
			#
			found_instr=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_instr_name[sect_num]) {
					instr = var_value[k]
					found_instr=1
				} # match instr name

			} # for k

			if (found_instr==0) {
				print "ERROR: no instr variable: scoreline " scorelineno ", sect " sect_name[sect_num], "at line", file_line_count | "cat 1>&2" 
				exit 1
			}


			#
			#	find start name
			#
			found_start=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_start_name[sect_num]) {
					start = var_value[k]
					found_start=1
				} # match start name

			} # for k

			if (found_start==0) {
				#
				#	use the current score time
				#
				start = scoretime
			}

			#
			#	find tempo name
			#
			found_tempo=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_tempo_name[sect_num]) {
					ltempo = var_value[k]
					found_tempo=1
				} # match tempo name

			} # for k

			if (found_tempo==0) {
				#
				# just use the global tempo
				#
				ltempo = tempo
			}


			#
			#	find key name
			#
			found_key=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_key_name[sect_num]) {
					key = var_value[k]
					found_key=1
				} # match key name

			} # for k

			if (found_key==0) {
				print "ERROR: no key variable: scoreline " scorelineno ", sect " sect_name[sect_num], "at line", file_line_count | "cat 1>&2" 
				exit 1
			}

			#
			#	find amp name
			#
			found_amp=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_amp_name[sect_num]) {
					lamp = var_value[k]
					found_amp=1
				} # match amp name

			} # for k

			if (found_amp==0) {
				print "ERROR: no amplitude variable: scoreline " scorelineno ", sect " sect_name[sect_num], "at line", file_line_count | "cat 1>&2" 
				exit 1
			}


			#
			#	find pan name
			#
			found_pan=0
			for (k=1; k<=varcount; k++) {

				if (var_name[k] == sect_pan_name[sect_num]) {
					pan = var_value[k]
					found_pan=1
				} # match pan name

			} # for k

			if (found_pan==0) {
				pan = 0.5
			}

			sect_cmd[sect_num] = sprintf("echo junk | awk -f %s instr=%d start=%g tempo=%g key=%.2f amp=%g pan=%g >> hold.sco", \
					sect_file[sect_num], instr, start, ltempo, key, lamp, pan)

			#print "found command (" sect_cmd[sect_num] ")"
			system(sect_cmd[sect_num])

			continue

		} # if -- found_sect


		if (is_in_varlist($i)) {
			vret = Sc_EvalVar($i)
			#print "DEBUGGER: " $i " into Sc_EvalVar returned:"
			#print_string_matrix(vret)
			#
			#	output the variable expansion to the csound score
			#
			string_matrix_to_csound(vret)
		}


	} # for -- fields on track line


    #
    #   CTRLRAMP control messages
    #   We exhaust all the control ramp messages during this time slot,
    #   where "time slot" means until the next score line.  (This is
    #   more work than needed because the output file will be sorted
    #   by start time anyway. A.S.)
    #
    #   The TimeP is the transpired time in this time slot.  The ramp_incp
    #   function allows each ramp to watch the last time it printed with
    #   its own timep variable.
    #
    if (ramp_doramp())
    {
		inc_scoretime = get_duration($1)
        TimeP = 0
        while (TimeP < inc_scoretime)
        {
            #   get smallest time step
            TimeLeast = ramp_getleast()
            if (TimeLeast <= 0)
                break
            TimeP += TimeLeast
            #   tell each ramp a time incr is coming
            ramp_incp(TimeLeast)
            #   print intermediate CONTROL messages
            ramp_dumpramps(scoretime+TimeP)
        }
    }


	#
	# read the time field (last thing to do)
	#
	if (is_a_number($1)) {
	}
	else {
		scoretime += get_duration($1)
		print "time inc to " scoretime
	}

	# clean up
	is_a_mixscore_line = 0
	is_a_mark_line = 0

} #  score line



END {


}


#	----------------------------------------------------------------------
#	----------------------------------------------------------------------
#	----------------------------------------------------------------------
#	----------------------------------------------------------------------
#	----------------------------------------------------------------------
#	----------------------------------------------------------------------

function find_midi_master_volume(ii) {

    if (midi_master_flag)
    {
        for (ii=1; ii <= midi_master_ctrl_count; ii++)
        {
            if (int(midi_master_ctrlid[ii]) == 7)
            {
                return midi_master_ctrlval[ii]
            }
        }
    }
    return -1
}
#--------------------- find_midi_master_volume ----------------------------


function find_midi_master_ctrl(ctrlid     ,i) {

    if (midi_master_flag)
    {
        for (i=1; i <= midi_master_ctrl_count; i++)
        {
            if (int(midi_master_ctrlid[i]) == int(ctrlid))
            {
                return midi_master_ctrlval[i]
            }
        }
    }
    return -1
}
#--------------------- find_midi_master_ctrl ------------------------------


function is_midi_master_ignore(ch     ,i) {

    if (midi_master_flag)
    {
        for (i=1; i <= midi_master_ignore_count; i++)
        {
            if (int(midi_master_ignore[i]) == int(ch))
            {
                #
                #   Success, the "ch" is in the ignore list
                #
                return 1
            }
        }
    }
    return 0
}
#--------------------- find_midi_master_ctrl ------------------------------



function debug_midi_master(i,msgstr) {

    print "midi_master_ctrl_count=", midi_master_ctrl_count
    for (i=1; i <= midi_master_ctrl_count; i++)
    {
        msgstr = sprintf("midi_master_ctrlid[%d]=%d", i, midi_master_ctrlid[i])
        print msgstr
        msgstr = sprintf("midi_master_ctrlval[%d]=%d", i, midi_master_ctrlval[i])
        print msgstr
    }
}


function find_midi_mute_channel(ch    ,ii) {

    if (midi_mute_flag)
    {
        for (ii=1; ii <= midi_mute_ch_count; ii++)
        {
            if (int(midi_mute_channel[ii]) == int(ch))
            {
                return 1
            }
        }
    }
    return 0
}
#--------------------- find_midi_master_volume ----------------------------



function evaluate_durations(tempo) {
#
#	produces global values
#

	qdur = (1/tempo) * 60	# quarter note 	( "q" in script )
	print "q = " qdur
	edur = qdur/2			# eighth note	( "*" in script )
	print "* = " edur
	sdur = edur/2			# sixteenth		( "!" in script )
	print "! = " sdur
	
	hdur = qdur * 2			# half note		( "h" in script )
	print "h = " hdur
	wdur = qdur * 4			# whole note	( "o" in script )
	print "o = " wdur
}
#--------------------- evaluate_durations -------------------------------



function evaluate_qdur(ltempo,   dur) {
#
#	produces local value
#

	dur = (1/ltempo) * 60	# quarter note 	( "q" in script )
	return dur
}
#--------------------- evaluate_qdur -------------------------------



function evaluate_hdur(ltempo,   dur) {
#
#	produces local value
#

	dur = (1/ltempo) * 60	# quarter note 	( "q" in script )
	dur = dur*2				# half note		( "h" in script )
	return dur
}
#--------------------- evaluate_hdur -------------------------------



function evaluate_wdur(ltempo,   dur) {
#
#	produces local value
#

	dur = (1/ltempo) * 60	# quarter note 	( "q" in script )
	dur = dur*4				# whole note	( "o" in script )
	return dur
}
#--------------------- evaluate_wdur -------------------------------



function evaluate_edur(ltempo,   dur) {
#
#	produces local value
#

	dur = (1/ltempo) * 60	# quarter note 	( "q" in script )
	dur = dur/2				# eighth note	( "*" in script )
	return dur
}
#--------------------- evaluate_edur -------------------------------



function evaluate_sdur(ltempo,   dur) {
#
#	produces local value
#

	dur = (1/ltempo) * 60	# quarter note 		( "q" in script )
	dur = dur/4				# sixteenth note	( "!" in script )
	return dur
}
#--------------------- evaluate_sdur -------------------------------



function is_a_string(field,   newfield,nerr,ary) {
	# if (index(field, "\"")) {
	#	return 1
	# }

	nerr = split(field, ary, "\"")
	if (nerr >= 1) {
		if (ary[2] != "") {
			return 1
		}
	}
	return 0
}
# -------------------- is_a_string_var -----------------------------


function strip_quotes(field,   newfield,nerr,ary) {

	#
	#	if no quotes, just return the string
	#
	if (index(field, "\"") == 0) {
		return field
	}

	nerr = split(field, ary, "\"")
	if (nerr >= 1) {
		if (ary[2] != "") {
			return ary[2]
		}
	}
	return "null"
}
# --------------------- strip_quotes ------------------------------


function is_a_ta(field,   nerr,ary, merr,mary) {

	if (index(field, "t") == 1) {
		nerr = split(field, ary, "t")
		if (nerr >= 1) {
			if (index(ary[2], "a")) {
				merr = split(ary[2], mary, "a")
				if (merr == 2) {
					return 1
				}
			}
		}
	}
	return 0
}
# --------------------- is_a_ta -----------------------------------


function convert_ta(field,   nerr,ary, merr,mary) {

	if (index(field, "t") == 1) {
		nerr = split(field, ary, "t")
		if (nerr >= 1) {
			if (index(ary[2], "a")) {
				merr = split(ary[2], mary, "a")
				if (merr == 2) {
					return sprintf("%g,%g", mary[1], mary[2])
				}
			}
		}
	}
	return "null"
}
# ------------------- convert_ta ----------------------------------


function is_a_numeric_var(field) {
#
#	used on a "set" command
#
	if (match(field, /\$/)) {
		return 0
	}
	if (match(field, /[a-zA-Z]/)) {
		return 0
	}
	if (match(field, /[\&\*\!\@]/)) {
		return 0
	}
	if (match(field, /[0-9][0-9]*(\.)*[0-9]*/)) {
		return 1
	}
	return 0
}
# --------------------- is_a_numeric_var ---------------------------------



function is_a_relation_symbol(field) {
#
#	"field" is a value, not a name, similar to "is_a_numeric_var()"
#

	if (match(field, /\^./)) {
		return 0
	}
	else if (field == "c") {
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*o(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {
		#
		#	whole note
		#	decimal number times a duration
		#
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*h(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {
		#
		#	half note
		#	decimal number times a duration
		#
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*q(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {
		#
		#	quarter note
		#	decimal number times a duration
		#
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\*(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {
		#
		#	eighth note
		#	decimal number times a duration
		#
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\!(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {
		#
		#	sixteenth note
		#	decimal number times a duration
		#
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\&(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {  # scoretime offset by a number
		return 1
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\@(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {  # scoretime offset by a number
		return 1
	}
	else {
		return 0;
	}

}
#-------------------- is_a_relation_symbol -------------------------------



function is_a_relation_scoretime(field) {
#
#	"field" contains "&"
#

	if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\&(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {  # scoretime offset by a number
		return 1
	}
	else {
		return 0;
	}

}
#-------------------- is_a_relation_scoretime -------------------------------



function is_a_relation_ascoretime(field) {
#
#	"field" contains "@"
#

	if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\@(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {  # scoretime offset by a number
		return 1
	}
	else {
		return 0;
	}

}
#-------------------- is_a_relation_ascoretime -------------------------------




function is_a_var_reference(field)
{
#
#	used on a "set" command
#

	if (match(field, /\$[a-zA-Z][a-zA-Z0-9]*/)==1) {
		return 1
	}
	return 0
}
#---------------------- is_a_var_reference ------------------------------



function get_var_index_from_name(field,   i) {
#
#	gets the var name index from variable "name" that was given
#	by name (no "$")
#

	for (i=1; i<=varcount; i++) {

		if (field == var_name[i]) {

			return i
		}

	} # for -- i

	return 0
}
#------------------ get_var_index_from_name -----------------------


function get_var_index_from_reference(field,   i,name) {
#
#	gets the var name index from variable "name" that was given
#	by reference as "$name"
#

	#
	#	assumes that the first character is a "$"
	#
	name = substr(field, 2)

	return get_var_index_from_name(name)
}
#------------------ get_var_index_from_reference -----------------------


function get_var_value(field)
{

	return val_value[get_var_index_from_name(field)]
}
#-------------------- get_var_value -------------------------------------



function is_in_varlist(field,    i,sfield)
{
	#
	#	ignores a leading "^" sign
	#

	for (i=1; i<=varcount; i++) {

		if (match(field, /\^/)) {
			#
			# take off the leading "^"
			#
			sfield = substr(field,2)
		} # if -- match
		else {
			sfield = field
		}

		if (sfield == var_name[i]) {
			return 1
		}
	}
	return 0
}
#--------------------- is_in_varlist ------------------------------------



function is_a_number(field) {
#
#	used on the score line
#


	if (match(field, /[0-9]*q/)) {
		return 0
	}
	else if (match(field, /[0-9]*\*/)) {
		return 0
	}
	else if (match(field, /[0-9]*h/)) {
		return 0
	}
	else if (match(field, /[0-9]*o/)) {
		return 0
	}
	else if (match(field, /[0-9]*!/)) {
		return 0
	}
	else if (match(field, /[0-9][0-9]*(\.[0-9]*)*/)) {
		return 1
	}
	else {
		return 0
	}
}
#---------------------- is_a_number --------------------------------------



function is_valid_op(opfield) {

	if (opfield == "*" || opfield == "/" || opfield == "+" || opfield == "-") {
		return 1
	}
	return 0
}
#---------------------- is_valid_op --------------------------------------



function is_an_operation(name,   i) {

	i = get_var_index_from_name(name)
	if (var_value[i] == "null" && is_valid_op(var_op[i])) {
		return 1
	}
	return 0
}
#---------------------- is_an_operation ----------------------------------



function perform_operation(reference, opfield, operand,   i,j,val,object) {

	#
	#	get the index for the variable in reference
	#	"reference" should be a $ followed by a variable name
	#
	i = get_var_index_from_reference(reference)
	if (is_an_operation(var_name[i])) {
		#
		#	the value in the reference must itself be evaluated
		#
		val = perform_operation(var_reference[i],var_op[i],var_operand[i])
	}
	else if (is_a_var_reference(var_name[i])) {
		#
		#	but, if the name is itself a variable reference
		#
		j = get_var_index_from_reference(var_name[i])
		val = var_value[j]
	}
	else {
		#
		# default -- the variable stored a number
		#
		if (is_a_numeric_var(var_value[i])) {
			val = var_value[i]
		}
		else {
			print "ERROR: perform_operation: can't interpret value " var_value[i], "at line", file_line_count | "cat 1>&2" 
		}
	}


	#
	#	get the index of the value in the operand
	#
	if (is_a_numeric_var(operand)) {
		object = operand
	}
	else {
		#
		#	operand holds a variable name or reference
		#
		if (is_a_var_reference(operand)) {
			j = get_var_index_from_reference(operand)
		}
		else {
			print "ERROR: perform_operation: variable operand must be preceded by $ at line", file_line_count | "cat 1>&2" 
		}

		if (is_an_operation(var_name[j])) {
			#
			#	the value in the reference must itself be evaluated
			#
			object = perform_operation(var_reference[j],var_op[j],var_operand[j])
		}
		else if (is_a_var_reference(var_name[j])) {
			#
			#	but, if the name is itself a variable reference
			#
			j = get_var_index_from_reference(var_name[j])
			object = var_value[j]
		}
		else {
			#
			# default -- the variable stored a number
			#
			if (is_a_numeric_var(var_value[j])) {
				object = var_value[j]
			}
			else {
				print "ERROR: perform_operation: can't interpret value " var_value[j], "at line", file_line_count | "cat 1>&2" 
			}
		}


	} # else -- operand is a variable



	#
	#	perform the operation
	#
	if (opfield == "*") {
		return (val * object)
	}
	else if (opfield == "+") {
		return (val + object)
	}
	else if (opfield == "/") {
		return (val / object)
	}
	else if (opfield == "-") {
		return (val - object)
	}

	#
	#
	# if we drop down to here there is something wrong
	#
	print "ERROR: perform_operation: " reference " " opfield " " operand, "at line", file_line_count | "cat 1>&2" 
	return "null"
}
#---------------------- is_valid_op --------------------------------------



function get_duration_gain(field,  ind,ind2,len,num)
{
#
#	in the relation, "a&+b"
#	returns "a"
#

	if (field == "!") {
		return 1;
	}
	else if (field == "*") {
		return 1;
	}
	else if (field == "q") {
		return 1;
	}
	else if (field == "h") {
		return 1;
	}
	else if (field == "o") {
		return 1;
	}

	ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
	ind2 = match(field, /[\!\*qho]/)

	if (ind < ind2) {
		len = ind2 - ind
		num = substr(field, ind, len)
		return num
	}

	return 1
}
#---------------------- get_duration_gain -------------------------------



function get_duration_offset(field,   ind,ind2,sfd,off) {
#
#	in the relation, "a&+b"
#	returns "b"
#

	if (field == "!") {
		return 0;
	}
	else if (field == "*") {
		return 0;
	}
	else if (field == "q") {
		return 0;
	}
	else if (field == "h") {
		return 0;
	}
	else if (field == "o") {
		return 0;
	}


	ind = match(field, /\+/)
	if (ind) {
		sfd = substr(field, ind)
		ind2 = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
		off = substr(sfd,ind2)	# skip past the "!*qho"
		return off
	}

	ind = match(field, /\-/)
	if (ind) {
		sfd = substr(field, ind)
		ind2 = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
		off = substr(sfd,ind2)	# skip past the "!*qho"
		off *= -1
		return off
	}

	return 0
}
#---------------------- get_duration_offset -----------------------------



function get_duration(field,   ind,ind2,len,num,sfd,off) {


	ind = match(field, /\!/)
	if (ind) {
		num = get_duration_gain(field)
		off = get_duration_offset(field)
		return (num*sdur)+off
	}

	ind = match(field, /\*/)
	if (ind) {
		num = get_duration_gain(field)
		off = get_duration_offset(field)
		return (num*edur)+off
	}

	ind = match(field, /q/)
	if (ind) {
		num = get_duration_gain(field)
		off = get_duration_offset(field)
		return (num*qdur)+off
	}

	ind = match(field, /h/)
	if (ind) {
		num = get_duration_gain(field)
		off = get_duration_offset(field)
		return (num*hdur)+off
	}

	ind = match(field, /o/)
	if (ind) {
		num = get_duration_gain(field)
		off = get_duration_offset(field)
		return (num*wdur)+off
	}
	return -1
}
#---------------------- get_duration --------------------------------------




function get_duration_with_ltempo(field,ltempo,   ind,ind2,len,num,sfd,off,dur) {

	if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*q(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {

		dur = evaluate_qdur(ltempo)

		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		if (ind) {
			ind2 = match(field, /q/)
			len = ind2 - ind
			num = substr(field, ind, len)
			sfd = substr(field,ind2+1)
			ind = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
			if (ind) {
				off = substr(sfd, ind)
				return (num*dur)+off
			}
			return num*dur
		}

		return dur
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*\*(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {

		dur = evaluate_edur(ltempo)

		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		if (ind) {
			ind2 = match(field, /\*/)
			len = ind2 - ind
			num = substr(field, ind, len)
			sfd = substr(field,ind2+1)
			ind = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
			if (ind) {
				off = substr(sfd, ind)
				return (num*dur)+off
			}
			return num*dur
		}

		return dur
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*h(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {

		dur = evaluate_hdur(ltempo)

		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		if (ind) {
			ind2 = match(field, /h/)
			len = ind2 - ind
			num = substr(field, ind, len)
			sfd = substr(field,ind2+1)
			ind = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
			if (ind) {
				off = substr(sfd, ind)
				return (num*dur)+off
			}
			return num*dur
		}

		return dur
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*o(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {

		dur = evaluate_wdur(ltempo)

		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		if (ind) {
			ind2 = match(field, /o/)
			len = ind2 - ind
			num = substr(field, ind, len)
			sfd = substr(field,ind2+1)
			ind = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
			if (ind) {
				off = substr(sfd, ind)
				return (num*dur)+off
			}
			return num*dur
		}

		return dur
	}
	else if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*!(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {

		dur = evaluate_sdur(ltempo)

		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		if (ind) {
			ind2 = match(field, /!/)
			len = ind2 - ind
			num = substr(field, ind, len)
			sfd = substr(field,ind2+1)
			ind = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
			if (ind) {
				off = substr(sfd, ind)
				return (num*dur)+off
			}
			return num*dur
		}

		return dur
	}
	else {
		return -1	# error
	}
}
#---------------------- get_duration_with_ltempo ---------------------------------



function get_relation_gain(field,  ind,ind2,len,num)
{
#
#	in the relation, "a&+b"
#	returns "a"
#

	if (field == "&") {
		return 1;
	}
	if (field == "@") {
		return 1;
	}

	if (match(field, /([0-9\.]+)([&@])([+\-0-9\.\[\]!*qho])*/)) {
		ind = match(field, /[0-9][0-9]*(\.)*[0-9]*/)
		ind2 = match(field, /[\&\@]/)
	
		if (ind < ind2) {
			len = ind2 - ind
			num = substr(field, ind, len)
			return num
		}
		else {
			return 1
		}
	}
	else if (match(field, /([&@])([+\-0-9\.\[\]!*qho])*/)) {
		return 1;
	}

	return 1
}
#---------------------- get_relation_gain -------------------------------



function get_relation_offset(field,   ind,ind2,ind3,ind4,sfd,sfd2,len,off) {
#
#	in the relation, "a&+b"
#	returns "b"
#

	if (field == "&") {
		return 0;
	}
	if (field == "@") {
		return 0;
	}

	if (match(field, /([0-9\.]*)([&@])([+\-0-9\.\[\]!*qho])+/)) {

		ind = match(field, /[\+\-]/)
		sfd = substr(field, ind)
		if (ind) {
			#
			#	is offset a duration? ( for example, @+[7*] )
			#
			ind3 = match(sfd, /\[/)
			if (ind3) {
				ind3 = ind3+1
				ind4 = match(sfd,/\]/)
				len = ind4 - ind3
				sfd2 = substr(sfd,ind3,len)
				off = get_duration(sfd2)
			}
			else {
				#
				#	offset is just a number
				#
				ind2 = match(sfd, /[0-9][0-9]*(\.)*[0-9]*/)
				off = substr(sfd,ind2)	# skip past the "&"
			}
			return off
		}

	}
	else {
		return 0
	}

	return 0
}
#---------------------- get_relation_offset -----------------------------



function get_relation(field,currvalue,    gain,off,ret)
{
#
#	takes a "relation" expression such as 
#
#		&					(current value)
#		2.1&				(current value times a factor)
#		&+1.7				(current value plus an offset)
#		3&+20.2				(both)
#
#	and evaluates it like a function (similar to get_duration)
#	"currvalue" is a number to be substituted into "&", such
#	as a score time or a tempo
#
#	


	if (match(field,/([0-9][0-9]*(\.)*[0-9]*)*([\&\@])(\+[0-9][0-9]*(\.)*[0-9]*)*/)) {  # scoretime offset by a number

		gain = get_relation_gain(field)
		off = get_relation_offset(field)
		ret = (gain * currvalue + off)
		return ret
	}

	return "null"
}
# --------------------- get_relation --------------------------------------


function is_a_AND_relation(rel)
{
#
#	new with ver 2.9
#	if this is a relation of type "&" then it returns 1, else 0
#

	if (index(rel, "&")) {
		return 1
	}
	return 0
}
#-------------------- is_a_AND_relation -----------------------------------


function is_ANY_relation(rel)
{
#
#	new with ver 2.9
#	if this is a relation of type "&" or "@"  then it returns 1, else 0
#

	if (index(rel, "&")) {
		return 1
	}
	else if (index(rel, "@")) {
		return 1
	}
	return 0
}
#-------------------- is_ANY_relation -----------------------------------



function is_a_AT_relation(rel)
{
#
#	new with ver 2.9
#	if this is a relation of type "@" then it returns 1, else 0
#

	if (index(rel, "@")) {
		return 1
	}
	return 0
}
#-------------------- is_a_AT_relation -----------------------------------



function is_a_duration(rel,  i1,i2)
{
#
#	new with ver 2.9
#	if this is a relation of type "ohq*!" then it returns 1, else 0
#
#	because a duration symbol might occur in the offset of any kind
#	of relation, we must determine unambiguaously whether or not
#	this is a duration.
#

	#
	#	it can't be duration is any of these two symbols are present
	#
	if (index(rel, "&")) {
		return 0
	}
	if (index(rel, "@")) {
		return 0
	}

	i1 = match(rel, /ohq*!/)
	if (i1 == 0) {
		return 0
	}


	#
	#	check if there are any "[  ]" constructions
	#
	i2 = index(rel, "[")
	if (i2 > 0) {

		if (i1 < i2) {
			return 1
		}
		else {
			return 0
		}

	}
	else {

		return 1

	}

	return 0
}
#-------------------- is_a_duration -----------------------------------



function parse_relation_or_duration_string(rel,   i,j,len,srel,urel,sparse,result,a1,a2,a3,a4,a5,a6,a7)
{
#
#	takes any relation string or duration string of arbitrary format:
#	<a> & +- <b>
#	<a> @ +- <b>
#	<a> o +- <b>
#	<a> h +- <b>
#	<a> q +- <b>
#	<a> * +- <b>
#	<a> ! +- <b>
#
#	where <b> can be a number  or [ax+b] construct
#
#	The return value is a string with fields separated by ";" characters
#	(no whitespace)
#	gain ; relation_symbol ; additive_operator ; offset_gain ; 
#		offset_duration_symbol ; offset_additive_operator ; offset_offset
#
#	if any field is missing in the original string, then that placeholder
#	will have "null" (no quotes) in it.
#
#	example:		&
#	all placeholders will be null except for the 2nd, which will have "&"
#
#	example:		2.0@
#	place 1 will have "2.0", place 2 will have "@", all others null
#
#	example:		o+1.2
#	place 2 will have "o", place 7 (offset_offset) will have "1.2", all others
#	will be null
#
#	The calling function will then separate the fields by using 
#	split(result, ary, ";") on the result
#


	#
	#	first, eliminate ambiguity by finding any "[" or ']" which
	#	might contain a duration symbol
	#
	#	we will take care of handling fields a4,a5,a6,a7 first
	#
	i = index(rel, "[")
	if (i > 0) {
		#
		#	yes, there is a duration-bracket
		#

		#
		#	get the string between the "[" and ']"
		#
		j = index(rel, "]")
		if (j == 0) {
			print "ERROR: parse_relation_or_duration_string: incomplete duration-bracket in relation " rel, "at line", file_line_count | "cat 1>&2" 
			exit 1
		}
		else {
			#
			#	length of substring
			#
			len = j-i-1
			#
			#	get the sub string
			#
			srel = substr(rel, i+1, len)
			#
			#	call ourselves to parse this substring
			#
			sparse = parse_relation_or_duration_string(srel)
			#
			#	split this subparse
			#
			len = split(sparse, urel, ";")
			#
			# 	we are interested in 1,2,3, and 7
			#
			a4 = urel[1]
			a5 = urel[2]
			a6 = urel[3]
			a7 = urel[7]
		}

	}
	else {
		#
		#	no duration-bracket -- a4,a5,a6 are automatically null
		#
		a4 = "null"
		a5 = "null"
		a6 = "null"

		#
		#	now any offset is automatically a7
		#
		j = index(rel, "+")
		if (j > 0) {
			#
			#	get everything from the "+" to EOS
			#
			srel = substr(rel, j+1)
		}
		else {
			#
			#	minus?
			#
			j = index(rel, "-")
			if (j > 0) {
				#
				#	get everything from the "-" to EOS
				#
				srel = substr(rel, j+1)
			}
			else {
				srel = "null"
			}
		} # else -- minus

		a7 = srel

	} # else -- no bracket


	#
	#	now find a1,a2,a3
	#
	
	#
	#	if "i" was nonzero above, then it is the index of the "["
	#
	if (i > 0) {

		#
		#	move back from the "[" to the additive operator
		#	and this also becomes the length up the '["
		#
		i = i - 1
		srel = substr(rel, 1, i)

	} # if -- i
	else {
		#
		#	there was no bracket
		#
		srel = rel
	}

	#
	#	now srel has no ambiguity from the bracket
	#

	#	--------------
	#	test for "&"
	#	--------------
	#
	j = index(srel, "&")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "&"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "&"


	#	--------------
	#	test for "@"
	#	--------------
	#
	j = index(srel, "@")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "@"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "@"


	#	--------------
	#	test for "o"
	#	--------------
	#
	j = index(srel, "o")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "o"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "o"


	#	--------------
	#	test for "h"
	#	--------------
	#
	j = index(srel, "h")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "h"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "h"



	#	--------------
	#	test for "q"
	#	--------------
	#
	j = index(srel, "q")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "q"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "q"



	#	--------------
	#	test for "*"
	#	--------------
	#
	j = index(srel, "*")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "*"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "*"


	#	--------------
	#	test for "!"
	#	--------------
	#
	j = index(srel, "!")
	if (j) {

		#
		#	determine the gain
		#
		if (j == 1) {
			#
			#	there is no gain
			#
			a1 = "null"
		}
		else {
			#
			#	pull out the gain
			#
			a1 = substr(srel, 1, j-1)

		} # else -- has a gain

		#
		#	obvious what a2 is
		#
		a2 = "!"

		#
		#	additive operator, if any
		#
		len = length(srel)
		if (len > j) {
			a3 = substr(srel, j+1, 1)
		}
		else {
			a3 = "null"
		}


		result = sprintf("%s;%s;%s;%s;%s;%s;%s",a1,a2,a3,a4,a5,a6,a7)
		return result

	} # if -- has an "!"

	return "null"
}
#--------------------- parse_relation_or_duration_string ---------------



function collapse_relation_parse_to_string(a1, a2, a3, a4, a5, a6, a7,   result)
{
#
#	the reverse of parse_relation_or_duration_string()
#
#	this output string is of the form seen by the user
#

	if (debug_child) {
		print "DEBUGGER: collapse_relation_parse_to_string: input: a1= (" a1 "), a2= (" a2 "), a3= (" a3 "), a4= (" a4 "), a5= (" a5 "), a6= (" a6 "), a7= (" a7 ")"
	}

	#
	#	gain and first symbol
	#
	if (a1 == "null" || a1 == 1) {

		result = sprintf("%s", a2);

	}
	else {

		result = sprintf("%s%s", a1, a2);

	}


	#
	#	is there an offset?
	#
	if (a3 != "null") {

		#
		#	put in the additive operator
		#
		if ((a3 == "+") || (a3 == "-")) {
			result = sprintf("%s%s", result, a3);
		}
		else {
			print "ERROR: collapse_relation_parse_to_string: unsupported additive operator (" a3 ") in (" a1 a2 a3 a4 a5 a6 a7 ") at line", file_line_count | "cat 1>&2" 
			exit 1
		}

		#
		# should there be brackets?
		#
		if (a5 == "null") {
			#
			#	there is no duration operator in the offset
			#
			if (a7 == "null") {
				print "ERROR: collapse_relation_parse_to_string: additive operator (" a3 ") but no offset at line", file_line_count | "cat 1>&2" 
				exit 1
			}
			result = sprintf("%s%s", result, a7);
		}
		else {
			#
			#	a bracket is needed for the offset
			#
			result = sprintf("%s[", result)

			#
			#	the gain within the brackets
			#
			if (a4 == "null" || a4 == 1) {
				result = sprintf("%s%s", result, a5)
			}
			else {
				result = sprintf("%s%s%s", result, a4,a5)
			}

			#
			#	an offset within the brackets?
			#
			if (a6 != "null" && a7 != 0) {

				if (a6 == "+" || a6 == "-") {
					result = sprintf("%s%s%s]", result, a6,a7)
				}
				else {
					print "ERROR: collapse_relation_parse_to_string: unsupported additive operator (" a6 ") at line", file_line_count | "cat 1>&2" 
					exit 1
				}
			}


		} # else -- a bracket is needed

	}
	return result
}
#------------------ collapse_relation_parse_to_string ------------------



function multiply_relation_symbol(insymbol, multiplier,   len,a,sparse,a1,a2,a3,a4,a5,a6,a7,result)
{
#
#	Called by Multiply_ScVar
#
#	we expect that "multiplier" has been resolved into a literal by the
#	calling function
#
#	note:  if the original relation symbol had a construction with
#	[q], [1.2h+1.5], ... kind of offset, this symbolic level is lost
#	here and turned into a literal offset number
#

	sparse = parse_relation_or_duration_string(insymbol)
	if (sparse == "null") {
		print "ERROR: multiply_relation_symbol: (" insymbol ") not a relation at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	if (debug_child) {
	print "DEBUGGER: multiply_relation_symbol: parse'd string= (" sparse ")"
	}

	len = split(sparse, a, ";")
	if (len != 7) {
		print "ERROR: multiply_relation_symbol: not 7 parse fields at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	if (debug_child) {
	print "DEBUGGER: multiply_relation_symbol: split into (" len ") substrings"
	print "DEBUGGER: multiply_relation_symbol: a[1]= (" a[1] ")"
	print "DEBUGGER: multiply_relation_symbol: a[2]= (" a[2] ")"
	print "DEBUGGER: multiply_relation_symbol: a[3]= (" a[3] ")"
	print "DEBUGGER: multiply_relation_symbol: a[4]= (" a[4] ")"
	print "DEBUGGER: multiply_relation_symbol: a[5]= (" a[5] ")"
	print "DEBUGGER: multiply_relation_symbol: a[6]= (" a[6] ")"
	print "DEBUGGER: multiply_relation_symbol: a[7]= (" a[7] ")"
	}

	#
	#	a[1]	=	gain
	#	a[2]	=	symbol
	#	a[3]	=	additive operator
	#	a[4]	= 	offset_gain
	#	a[5]	=	offset_symbol
	#	a[6]	=	offset_additive_operator
	#	a[7]	=	offset_offset
	#

	#
	#	defaults:
	#
	a1 = "null"
	a2= "null"
	a3 = "null"
	a4 = "null"
	a5 = "null"
	a6 = "null"
	a7 = "null"

	#
	#	multiply numbers, then collapse back into a string
	#

	if (a[1] == "null") {
		a1 = multiplier
	}
	else {
		a1 = a[1] * multiplier
	}

	if (debug_child) {
	print "DEBUGGER: multiply_relation_symbol: a1= (" a1 ")"
	}


	a2 = a[2]
	a3 = a[3]

	if (a[5] != "null") {
		#
		#	there was a duration symbol in the offset
		#
		if (a[4] == "null") {
			a4 = multiplier
		}
		else {
			a4 = a[4] * multiplier
		}

		a5 = a[5]

		if (a[6] != "null") {
			if (a[7] != "null") {
				a7 = a[7] * multiplier
				a6 = a[6]
			}
		}

	}

	if (a[7] != "null") {
		a7 = a[7] * multiplier
	}


	result = collapse_relation_parse_to_string(a1,a2,a3,a4,a5,a6,a7)
	return result
}
#--------------------- multiply_relation_symbol ---------------------------



function add_to_relation_symbol(insymbol, multiplier,   len,a,sparse,a1,a2,a3,a4,a5,a6,a7,result)
{
#
#	Called by Add_ScVar
#
#	we expect that "multiplier" has been resolved into a literal by the
#	calling function
#
#	note:  if the original relation symbol had a construction with
#	[q], [1.2h+1.5], ... kind of offset, this symbolic level is lost
#	here and turned into a literal offset number
#

	sparse = parse_relation_or_duration_string(insymbol)
	if (sparse == "null") {
		print "ERROR: add_to_relation_symbol: (" insymbol ") not a relation at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	if (debug_child) {
	print "DEBUGGER: add_to_relation_symbol: parse'd string= (" sparse ")"
	}

	len = split(sparse, a, ";")
	if (len != 7) {
		print "ERROR: add_to_relation_symbol: not 7 parse fields at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	if (debug_child) {
	print "DEBUGGER: add_to_relation_symbol: split into (" len ") substrings"
	print "DEBUGGER: add_to_relation_symbol: a[1]= (" a[1] ")"
	print "DEBUGGER: add_to_relation_symbol: a[2]= (" a[2] ")"
	print "DEBUGGER: add_to_relation_symbol: a[3]= (" a[3] ")"
	print "DEBUGGER: add_to_relation_symbol: a[4]= (" a[4] ")"
	print "DEBUGGER: add_to_relation_symbol: a[5]= (" a[5] ")"
	print "DEBUGGER: add_to_relation_symbol: a[6]= (" a[6] ")"
	print "DEBUGGER: add_to_relation_symbol: a[7]= (" a[7] ")"
	}

	#
	#	a[1]	=	gain
	#	a[2]	=	symbol
	#	a[3]	=	additive operator
	#	a[4]	= 	offset_gain
	#	a[5]	=	offset_symbol
	#	a[6]	=	offset_additive_operator
	#	a[7]	=	offset_offset
	#

	#
	#	defaults:
	#
	a1 = a[1]
	a2 = a[2]
	a4 = a[4]
	a5 = a[5]


	if (a[5] != "null") {

		#
		#	if there are brackets, then we don't worry about
		#	a1,a2,a3
		#
		a3 = a[3]

		if (a[7] == "null") {
			a7 = multiplier
			a6 = "+"
		}
		else if (a[6] == "+") {
			a7 = a[7] + multiplier
			a6 = a[6]
		}
		else if (a[6] == "-") {
			a7 = multiplier - a[7]
			a6 = a[6]
		}


	}
	else {

		#
		#	if there are no brackets, then we don't worry about
		#	a4,a5,a6
		#
		a6 = a[6]

		if (a[7] == "null") {
			a7 = multiplier
			if (a[2] == "null") {
				a3 = "null"
			}
			else {
				a3 = "+"
			}
		}
		else if (a[3] == "+") {
			a7 = a[7] + multiplier
			a3 = a[3]
		}
		else if (a[3] == "-") {
			a7 = multiplier - a[7]
			a3 = a[3]
		}


	}



	result = collapse_relation_parse_to_string(a1,a2,a3,a4,a5,a6,a7)
	return result
}
#--------------------- add_to_relation_symbol ---------------------------



function get_string_matrix_rowscols(sm,   i,ary,cary,rows,cols,cl)
{

	rows = split(sm, ary, ";")
	cols = 1
	for (i in ary) {
		cl = split(ary[i], cary, " ")
		if (cl > cols) {
			cols = cl
		}
	}
	return sprintf("%d %d", rows,cols)
}
#----------------- get_string_matrix_rowscols ----------------------------



function get_element_from_string_matrix(sm, row, col,   as,am,bs,bm)
{

	#
	#	split into rows
	#
	as = split(sm, am, ";")
	if (row <= as) {

		bs = split(am[row], bm, " ")
		if (col <= bs) {
			return bm[col]
		}
	}

	return "null"
}
#---------------- get_element_from_string_matrix -------------------------



function transpose_string_matrix(sm,   i,j,el,mrc,maxrc,result)
{
	#
	#	get the dimensions
	#
	mrc = get_string_matrix_rowscols(sm)
	split(mrc,maxrc," ")

	#
	#	build the new matrix row-wise by copying 
	#	(r,c) from (c,r), (r,c+1) from (c+1,r), etc
	#
	for (i=1; i<=maxrc[2]; i++) {

		#
		#	loop down rows of a column
		#
		for (j=1; j<=maxrc[1]; j++) {

			el = get_element_from_string_matrix(sm, j, i)
			result = sprintf("%s %s", result, el)

		}

		if (i < maxrc[2]) {
			result = sprintf("%s ;", result)
		}

	} # for -- i

	return result
}
#-------------------- transpose_string_matrix ---------------------------



function print_string_matrix(sm,   i,as,ary)
{
	as = split(sm, ary, ";")

	for (i=1; i<=as; i++) {

		print ary[i]

	}
	return
}
#--------------------- print_string_matrix ------------------------------



function increment_name(oldname,   i,j,k,len,cvar,nvar,newname)
{

	cvar = oldname
	len = length(cvar)
	i = match(cvar, /(_m[0-9])+/)
	if (i) {

		#
		#	skip to the last "_"
		#
		k = i+2
		nvar = substr(cvar, k)
		j = match(nvar, /_m[0-9]+/)
		while (j) {

			k = j+2
			nvar = substr(nvar, k)
			j = match(nvar, /_m[0-9]+/)

		}

		#
		#	k should be the index of the last number
		#	nvar should be the number itself
		#
		cvar = substr(cvar, 1, k-3)		# stem
		nvar += 1
		newname = sprintf("%s_%d", cvar, nvar)


	}
	else {
		newname = sprintf("%s_m1", cvar)
	}

	#
	#	check if name already exists
	#
	k = get_var_index_from_name(newname)
	while (k > 0) {

		i = match(newname, /_m[0-9]+/)
		j = i+2
		nvar = substr(newname, j)
		nvar += 1
		cvar = substr(newname, 1, j-3)		# stem
		newname = sprintf("%s_m%d", cvar, nvar)
		k = get_var_index_from_name(newname)
	}


	return newname
}
#-------------------- increment_name ------------------------------------



function add_halftones_to_key(adder, oldkey,    mant,dec,oldtones,newtones,newmant,newdec,result)
{

	#
	#	decompose oldkey into halftones
	#
	mant = int(oldkey)
	dec = oldkey - mant
	dec = dec * 100



	oldtones = mant * 12
	oldtones += dec

	if (debug_child) {
		print "DEBUGGER:  old mant = " mant ", old dec = " dec ", oldtones = " oldtones
	}

	#
	#	adder is assumed to already be expressed in halftones
	#
	newtones = oldtones + adder


	newmant = int(newtones / 12)
	newdec = newtones - (newmant*12)

	if (debug_child) {
		print "DEBUGGER:  newtones = " newtones ", new mant = " newmant ", new dec = " newdec
	}

	result = newmant + (newdec/100)

	if (debug_child) {
		print "DEBUGGER: new key = " result
	}

	return result
}
#------------------ add_halftones_to_key----------------------------------



function Multiply_ScVar(oldcolumn, multiplier,    i,j,k,cvar,nvar,tvar,as,ary,nstr,result,mt,mz,var)
{
#
#	Called by Mod_ScVar.
#	Given the column of values to be modified, "oldcolumn", multiplies
#	the values by "multiplier" and returns a new column as a variable name.
#

	#
	#	at first, "oldcolumn" is just a variable name with a "^" on it
	#
	if (match(oldcolumn, /\^/)) {

		#
		# take off the leading "^"
		#
		cvar = substr(oldcolumn,2)

	} # if -- match
	else {
		cvar = oldcolumn
	}

	if (debug_child) {
	print "DEBUGGER: Multiply_ScVar: old column = (" cvar ")"
	}

	#
	#	create the new name (myvar --> myvar_1,  myvar_1 --> myvar_2, etc)
	#
	nvar = increment_name(cvar)

	if (debug_child) {
	print "DEBUGGER: Multiply_ScVar: new column name = (" nvar ")"
	}

	#
	#	resolve the multiplier if it is a variable
	#
	if (is_a_numeric_var(multiplier)) {
		mt = multiplier
	}
	else {
		k = get_var_index_from_name(multiplier)
		mt = var_value[k]
	}
	#	---------------------------------------
	#	now, the multiplier is a literal in "mt"
	#	---------------------------------------

	if (debug_child) {
	print "DEBUGGER: multiplier= (" mt ")"	
	}

	#
	#	spilt the column into its fields
	#
	k = get_var_index_from_name(cvar)
	as = split(var_value[k], ary, " ")

	if (debug_child) {
	#	DEBUGGER
	print "DEBUGGER: column has components: "
	for (i=1; i<=as; i++) {
		print "DEBUGGER: " ary[i]
	}
	}


	#
	#	multiply each field
	#
	for (i=1; i<=as; i++) {

		#
		#	resolve the field if it is a variable
		#
		if (is_a_numeric_var(ary[i])) {
			#
			#	do the multiplication
			#
			mz = mt * ary[i]

			if (debug_child) {
			print "DEBUGGER: multiplied numeric = (" mz ")"
			}
		}
		else if (is_a_relation_symbol(ary[i])) {
			#
			#	must handle this separately
			#	multiply the gain
			#	multiply the offset
			#	return a new relation symbol
			#
			mz = multiply_relation_symbol(ary[i], mt)

			if (debug_child) {
			print "DEBUGGER: multiplied relation = (" mz ")"
			}
		}
		else if (is_a_var_reference(ary[i])) {
			#
			#	find the variable after the "$" prefix
			#
			j = get_var_index_from_reference(ary[i])
			#
			#	do the multiplication with the referenced value
			#
			mz = mt * var_value[j]

			if (debug_child) {
			print "DEBUGGER: multiplied reference = (" mz ")"
			}

		}
		else if (is_an_operation(ary[i])) {
			#
			# if this was created by "eval"
			#
			#	we must create a new variable to hold this
			#	because it involve a set of references
			#	that must be accessed at matrix resolution time
			#
			#
			#	new name
			#
			tvar = increment_name(ary[i])

			#
			#	find this variable in the table
			#
			k = get_var_index_from_name(ary[i])


			#
			# new variable for this new "operation" variable
			#
			varcount += 1				# one more var in table
			j = varcount
			var_name[j] = tvar
			var_value[j] = var_value[k]
			var_size[j] = 1
			var_op[j] = var_op[k]
			var_operand[j] = var_operand[k] * mt
			var_reference[j] = var_reference[k]

			mz = tvar

			if (debug_child) {
			print "DEBUGGER: multiplied operation = (" mz ")"
			}

		}
		else {
			#
			# unsupported
			#
			print "ERROR: Multiply_ScVar: unsupported column element (" ary[i] ") at line", file_line_count | "cat 1>&2" 
			exit 1
		}


		#
		#	append this multiplied value to the list of values
		#
		nstr = sprintf("%s %s", nstr, mz)

	} # for -- i

	#
	# new variable for this new column
	#
	varcount += 1				# one more var in table
	j = varcount
	var_name[j] = nvar
	var_value[j] = nstr
	var_size[j] = as
	var_op[j] = "null"
	var_operand[j] = "null"
	var_reference[j] = "null"

	#
	#	attach the column operator to match the input variable
	#
	result = sprintf("^%s", var_name[j])

	if (debug_child) {
	print "DEBUGGER: Multiply_ScVar: returning new var (" result ")"
	}

	#
	#	done
	#
	return result
}
#------------------------ Multiply_ScVar --------------------------------



function Add_ScVar(oldcolumn, multiplier,    i,j,k,cvar,nvar,tvar,as,ary,nstr,result,mt,mz,var)
{
#
#	Called by Mod_ScVar.
#	Given the column of values to be modified, "oldcolumn", adds
#	the values by "multiplier" and returns a new column as a variable name.
#

	#
	#	at first, "oldcolumn" is just a variable name with a "^" on it
	#
	if (match(oldcolumn, /\^/)) {

		#
		# take off the leading "^"
		#
		cvar = substr(oldcolumn,2)

	} # if -- match
	else {
		cvar = oldcolumn
	}

	if (debug_child) {
	print "DEBUGGER: Add_ScVar: old column = (" cvar ")"
	}

	#
	#	create the new name (myvar --> myvar_1,  myvar_1 --> myvar_2, etc)
	#
	nvar = increment_name(cvar)

	if (debug_child) {
	print "DEBUGGER: Add_ScVar: new column name = (" nvar ")"
	}

	#
	#	resolve the multiplier if it is a variable
	#
	if (is_a_numeric_var(multiplier)) {
		mt = multiplier
	}
	else {
		k = get_var_index_from_name(multiplier)
		mt = var_value[k]
	}
	#	---------------------------------------
	#	now, the multiplier is a literal in "mt"
	#	---------------------------------------

	if (debug_child) {
	print "DEBUGGER: multiplier= (" mt ")"	
	}

	#
	#	spilt the column into its fields
	#
	k = get_var_index_from_name(cvar)
	as = split(var_value[k], ary, " ")

	if (debug_child) {
	#	DEBUGGER
	print "DEBUGGER: column has components: "
	for (i=1; i<=as; i++) {
		print "DEBUGGER: " ary[i]
	}
	}


	#
	#	add to each field
	#
	for (i=1; i<=as; i++) {

		#
		#	resolve the field if it is a variable
		#
		if (is_a_numeric_var(ary[i])) {
			#
			#	do the multiplication
			#
			mz = mt + ary[i]

			if (debug_child) {
			print "DEBUGGER: added to numeric = (" mz ")"
			}
		}
		else if (is_a_relation_symbol(ary[i])) {
			#
			#	must handle this separately
			#	multiply the gain
			#	multiply the offset
			#	return a new relation symbol
			#
			mz = add_to_relation_symbol(ary[i], mt)

			if (debug_child) {
			print "DEBUGGER: added to relation = (" mz ")"
			}
		}
		else if (is_a_var_reference(ary[i])) {
			#
			#	find the variable after the "$" prefix
			#
			j = get_var_index_from_reference(ary[i])
			#
			#	do the multiplication with the referenced value
			#
			mz = mt + var_value[j]

			if (debug_child) {
			print "DEBUGGER: added to reference = (" mz ")"
			}

		}
		else if (is_an_operation(ary[i])) {
			#
			# if this was created by "eval"
			#
			#	we must create a new variable to hold this
			#	because it involve a set of references
			#	that must be accessed at matrix resolution time
			#
			#
			#	new name
			#
			tvar = increment_name(ary[i])

			#
			#	find this variable in the table
			#
			k = get_var_index_from_name(ary[i])


			#
			# new variable for this new "operation" variable
			#
			varcount += 1				# one more var in table
			j = varcount
			var_name[j] = tvar
			var_value[j] = var_value[k]
			var_size[j] = 1
			var_op[j] = var_op[k]
			var_operand[j] = var_operand[k] + mt
			var_reference[j] = var_reference[k]

			mz = tvar

			if (debug_child) {
			print "DEBUGGER: added to operation = (" mz ")"
			}

		}
		else {
			#
			# unsupported
			#
			print "ERROR: Add_ScVar: unsupported column element (" ary[i] ") at line", file_line_count | "cat 1>&2" 
			exit 1
		}


		#
		#	append this multiplied value to the list of values
		#
		nstr = sprintf("%s %s", nstr, mz)

	} # for -- i

	#
	# new variable for this new column
	#
	varcount += 1				# one more var in table
	j = varcount
	var_name[j] = nvar
	var_value[j] = nstr
	var_size[j] = as
	var_op[j] = "null"
	var_operand[j] = "null"
	var_reference[j] = "null"

	#
	#	attach the column operator to match the input variable
	#
	result = sprintf("^%s", var_name[j])

	if (debug_child) {
	print "DEBUGGER: Add_ScVar: returning new var (" result ")"
	}

	#
	#	done
	#
	return result
}
#------------------------ Add_ScVar --------------------------------



function KeyShift_ScVar(oldcolumn, multiplier,    i,j,k,cvar,nvar,tvar,as,ary,nstr,result,mt,mz,var)
{
#
#	Called by Mod_ScVar.
#	Given the column of values to be modified, "oldcolumn", adds
#	the values by "multiplier" and returns a new column as a variable name.
#

	#
	#	at first, "oldcolumn" is just a variable name with a "^" on it
	#
	if (match(oldcolumn, /\^/)) {

		#
		# take off the leading "^"
		#
		cvar = substr(oldcolumn,2)

	} # if -- match
	else {
		cvar = oldcolumn
	}

	if (debug_child) {
	print "DEBUGGER: KeyShift_ScVar: old column = (" cvar ")"
	}

	#
	#	create the new name (myvar --> myvar_1,  myvar_1 --> myvar_2, etc)
	#
	nvar = increment_name(cvar)

	if (debug_child) {
	print "DEBUGGER: KeyShift_ScVar: new column name = (" nvar ")"
	}

	#
	#	resolve the multiplier if it is a variable
	#
	if (is_a_numeric_var(multiplier)) {
		mt = multiplier
	}
	else {
		k = get_var_index_from_name(multiplier)
		mt = var_value[k]
	}
	#	---------------------------------------
	#	now, the multiplier is a literal in "mt"
	#	---------------------------------------

	if (debug_child) {
	print "DEBUGGER: multiplier= (" mt ")"	
	}

	#
	#	spilt the column into its fields
	#
	k = get_var_index_from_name(cvar)
	as = split(var_value[k], ary, " ")

	if (debug_child) {
	#	DEBUGGER
	print "DEBUGGER: column has components: "
	for (i=1; i<=as; i++) {
		print "DEBUGGER: " ary[i]
	}
	}


	#
	#	add to each field
	#
	for (i=1; i<=as; i++) {

		#
		#	resolve the field if it is a variable
		#
		if (is_a_numeric_var(ary[i])) {
			#
			#	do the multiplication
			#
			mz = add_halftones_to_key(mt, ary[i])

			if (debug_child) {
			print "DEBUGGER: added to numeric = (" mz ")"
			}
		}
		else if (is_a_relation_symbol(ary[i])) {
			#
			#	must handle this separately
			#	multiply the gain
			#	multiply the offset
			#	return a new relation symbol
			#
			print "ERROR: Shift Key in child function doesn't take relation expressions (" ary[i] ") at line", file_line_count | "cat 1>&2" 
			exit 1
		}
		else if (is_a_var_reference(ary[i])) {
			#
			#	find the variable after the "$" prefix
			#
			j = get_var_index_from_reference(ary[i])
			#
			#	do the multiplication with the referenced value
			#
			mz = add_halftones_to_key(mt, var_value[j])

			if (debug_child) {
			print "DEBUGGER: added to reference = (" mz ")"
			}

		}
		else if (is_an_operation(ary[i])) {
			#
			# if this was created by "eval"
			#
			#	we must create a new variable to hold this
			#	because it involve a set of references
			#	that must be accessed at matrix resolution time
			#
			#
			#	new name
			#
			tvar = increment_name(ary[i])

			#
			#	find this variable in the table
			#
			k = get_var_index_from_name(ary[i])


			#
			# new variable for this new "operation" variable
			#
			varcount += 1				# one more var in table
			j = varcount
			var_name[j] = tvar
			var_value[j] = var_value[k]
			var_size[j] = 1
			var_op[j] = var_op[k]
			var_operand[j] = add_halftones_to_key(mt, var_operand[k])
			var_reference[j] = var_reference[k]

			mz = tvar

			if (debug_child) {
			print "DEBUGGER: added to operation = (" mz ")"
			}

		}
		else {
			#
			# unsupported
			#
			print "ERROR: KeyShift_ScVar: unsupported column element (" ary[i] ") at line", file_line_count | "cat 1>&2" 
			exit 1
		}


		#
		#	append this multiplied value to the list of values
		#
		nstr = sprintf("%s %s", nstr, mz)

	} # for -- i

	#
	# new variable for this new column
	#
	varcount += 1				# one more var in table
	j = varcount
	var_name[j] = nvar
	var_value[j] = nstr
	var_size[j] = as
	var_op[j] = "null"
	var_operand[j] = "null"
	var_reference[j] = "null"

	#
	#	attach the column operator to match the input variable
	#
	result = sprintf("^%s", var_name[j])

	if (debug_child) {
	print "DEBUGGER: KeyShift_ScVar: returning new var (" result ")"
	}

	#
	#	done
	#
	return result
}
#------------------------ KeyShift_ScVar --------------------------------



function Mod_ScVar(newname, oldname, argstring,   i,ind,nind,as,ary,argary,coln,op,newcol)
{
#
#	This is what modifies a pattern variable, by the script command
#
#		child  newname  oldname  column_to_mod  operator  arg1  arg2 ....
#
#	which only works on a variable that is ready for the score line, i.e.,
#	represents a csound matrix. Thus, we take an existing note pattern
#	and change one of its csound parameters (one column).  For example,
#	we might transpose its key by operating on that particular column.
#	We might change its tempo by operating on the first column, or by
#	operating on the fourth (csound's third) column.
#
#	"argstring" is taken from the script is everything from "column_to_mod"
#	to the right
#
#	Doesn't have to return anything: we already know the new name.  It will
#	now be listed in the symbol table
#

	if (debug_child) {
	print "DEBUGGER: Mod_ScVar: newname= (" newname "), oldname= (" oldname "), argstring= (" argstring ")"
	}

	#
	#	"oldname" is the name of the old pattern variable to be child'ed
	#	find it in the Variable list
	#
	i = get_var_index_from_name(oldname)
	if (var_size[i] <= 1) {
		print "ERROR: Mod_ScVar: variable " oldname " is not a pattern at line", file_line_count | "cat 1>&2" 
		exit 1
	}
	ind = i


	#
	#	get the arguments -- a partial length of the original argument string
	#	is given as "argstring"
	#
	as = split(argstring, argary, " ")
	coln = argary[1] 	# column number should be first
	op = argary[2]		#	operator (+,-,*,/,...other)

	if (debug_child) {
	print "DEBUGGER: Mod_ScVar: column= (" coln "), op= (" op"), multiplier= (" argary[3] ")"
	}


	#
	#	the "value" of the old pattern name should be a row of sub-variables
	#	each sub-variable should be a column (should have a ^ character prefixed)
	#	split into "columns"
	#
	as = split(var_value[ind], ary, " ")
	if (as != var_size[ind]) {
		print "ERROR: Mod_ScVar: num fields found for variable " sfield " not equal " var_size[ind], "at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	if (debug_child) {
	#	DEBUGGER
	print "DEBUGGER: old pattern= ("
	for (i=1; i<=as; i++) {
		print ary[i]
	}
	print ")"
	}


	#
	#	create the new variable to hold the new pattern
	#
	#
	#
	#	check if there is already a variable with that name
	#
	for (i=1; i<varcount; i++) {
		if (newname == var_name[i]) {
			print "ERROR: Mod_ScVar: new name " newname " at line", file_line_count, "is already assigned" | "cat 1>&2" 
			exit 1
		}
	}

	#
	# new variable, increment count
	#
	varcount += 1
	nind = varcount
	var_name[nind] = newname
	var_op[nind] = "null"
	var_operand[nind] = "null"
	var_reference[nind] = "null"

	#
	#	CAUTION: don't use varcount (or varindex) again to access this variable
	#	because it will be changed in the subfunctions called below
	#


	#
	#	Dup all the columns except the one to be modified
	#
	for (i=1; i<=as; i++) {

		if (i == coln) {

			if (op == "*") {
				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: mulyiply to column (" ary[i] ")"
				}

				newcol = Multiply_ScVar(ary[i], argary[3])

				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: received new multiplied column (" newcol ")"
				}

			}
			else if (op == "+") {
				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: add to column (" ary[i] ")"
				}

				newcol = Add_ScVar(ary[i], argary[3])

				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: received new added-to column (" newcol ")"
				}

			}
			else if (op == "k+") {
				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: key shift column (" ary[i] ")"
				}

				newcol = KeyShift_ScVar(ary[i], argary[3])

				if (debug_child) {
				print "DEBUGGER: Mod_ScVar: received new key shift column (" newcol ")"
				}

			}
			else {
				print "ERROR: unknown op (" op ") at line", file_line_count | "cat 1>&2" 
				exit 1
			}

			#
			#	the new column name returned is expected to already
			#	have an ^ prefix
			#
			var_value[nind] = sprintf("%s %s", var_value[nind], newcol)
		}
		else {
			#
			#	Dup it
			#
			var_value[nind] = sprintf("%s %s", var_value[nind], ary[i])
		}

		#
		# keep track of the size
		#
		var_size[nind] += 1

		if (debug_child) {
		print "DEBUGGER: Mod_ScVar: new var (" var_name[nind] ") has (" var_value[nind] ")"
		}

	} # for -- loop thru columns

}
#------------------------- Mod_ScVar ------------------------------------



function Eval_Variable(field,  i,j,k,sfield,ref,op,opd,refv,opdv,ret) {

#
#	On the first call...
#	"field" is the field name exactly as it is found
#	in the file.  No "$" is expected.
#
#	Recursive calls might expect a "$" or a number.
#
#	in the command "seteval name arg1 arg2 arg3":
#	arg1 can be a reference to a variable, e.g. $b1, or a number
#	arg2 must be an operation, e.g., +, -, *, /
#	arg3 can be a reference to a variable, e.g. $b1, or a number
#


	#
	#	remove any leading "$" or "^"
	#
	if (match(field, /\$/)) {
		sfield = substr(field,2)
	}
	else if (match(field, /\^/)) {
		sfield = substr(field,2)
	}
	else {
		sfield = field
	}



	if (is_in_varlist(sfield)) {

		i = get_var_index_from_name(sfield)
		ref = var_reference[i]
		op = var_op[i]
		opd = var_operand[i]

		#
		#	if the reference is itself an eval, then we must
		#	recursively call Eval_Variable on it
		#
		if (is_a_var_reference(ref)) {
			j = get_var_index_from_reference(ref)

			if (var_op[j] != "null") {
				#
				#	the reference itself is a seteval
				#
				refv = Eval_Variable(var_name[j])
			}
			else {
				#
				# then get the value
				#
				refv = var_value[j]
				
			}


		}
		else {
			#
			#	else, ref must be a number
			#
			refv = ref
		}



		#
		#	if the operand is itself an eval, then we must
		#	recursively call Eval_Variable on it
		#
		if (is_a_var_reference(opd)) {
			k = get_var_index_from_reference(opd)

			if (var_op[k] != "null") {
				#
				#	the operand itself is a seteval
				#
				opdv = Eval_Variable(var_name[k])
			}
			else {
				#
				# then get the value
				#
				opdv = var_value[k]
			}

		}
		else {
			#
			#	else, opd must be a number
			#
			opdv = opd
		}

		#
		#
		#	at this point both the ref and opd should have been
		#	resolved into numbers
		#
		if (refv == "null" ) {
			print "ERROR: Eval_Variable: refv resolved to null at line", file_line_count | "cat 1>&2" 
			exit 1
		}
		if (opdv == "null" ) {
			print "ERROR: Eval_Variable: opdv resolved to null at line", file_line_count | "cat 1>&2" 
			exit 1
		}


		#
		#	do the operation
		#
		if (op == "+") {
			ret = refv + opdv
		}
		else if (op == "-") {
			ret = refv - opdv
		}
		else if (op == "*") {
			ret = refv * opdv
		}
		else if (op == "/") {
			ret = refv / opdv
		}

		return ret


	}
	else {
		#
		# must be a number
		#
		return sfield
	}

}
#----------------------- Eval_Variable ---------------------------------



function is_an_alias(field,   ind,par1)
#
#	determine if the field in a "set" definition is an alias:
#	that is, is of the form  "~name(value)"
#
{

	ind = index(field, "~")
	if (ind != 1) {
		return 0	# not an alias call
	}

	#
	#	if we drop down to here, then the first character in the field
	#	was an "~", so this must be an alias call
	#
	ind = index(field, "(")
	if (ind < 3) {
		#
		#	there must be at least 1 character following the "~"
		#	that is, there must be at least one character in the alias
		#	name
		#
		print "ERROR: left paranthesis in alias call at field (" field ") at line", file_line_count | "cat 1>&2" 
		exit 1
	}

	par1 = ind	# index of the opening paranthesis

	ind = index(field, ")")
	if (ind < (par1+2)) {
		#
		#	there must be at least 1 character following the "("
		#	that is, there must be at least one character inside
		#	the paranthesis--a void argument is not allowed
		#
		print "ERROR: right paranthesis in alias call at field (" field ") at line", file_line_count | "cat 1>&2" 
		exit 1
	}
	
	return 1	# success
}
#	-------------------- is_an_alias -----------------------------------



function insert_alias(whole_alias,   ind,par1,par2,name,arginput)
#
#	Given an entire alias call, "~name(arg_input)", this function returns
#	the output value associated with arg_input
#
{

	if (is_an_alias(whole_alias) == 0) {
		print "ERROR (bug): insert_alias: argument is not an alias call at line", file_line_count | "cat 1>&2" 
		exit 1
	}


	#
	#	go directly to the parantheses
	#
	ind = index(whole_alias, "(")
	par1 = ind
	ind = index(whole_alias, ")")
	par2 = ind
	name = substr(whole_alias, 2, (par1-2))
	arginput = substr(whole_alias, (par1+1), (par2-par1-1))

	if ((name, arginput) in alias_list) {
		return alias_list[name, arginput]
	}
	else {
		print "ERROR: argument (" arginput ") not found in alias (" name ") at line", file_line_count | "cat 1>&2" 
		exit 1
	}
}
#	-------------------- insert_alias -----------------------------------



function match_skiptrack(track,   i)
{
	for (i=1; i<=tracks; i++) {
		if ((i) in skip_track) {
			if (track == skip_track[i]) {
				return 1
			}
		}
	}
	return 0
}
#	------------------- match_skiptrack ----------------------------------



function ramp_doramp(       k)
{
    if (maxctrlramp > 0)
    {
        for (k=1; k <= maxctrlramp; k++)
        {
            if (ctrlramp[k,rctrlmore] > 0)
            {
                #
                #   At least one is still active
                #
                return 1
            }
        }
    }
    return 0    # no
}
#	------------------- ramp_doramp ---------------------------------------



function ramp_getleast(       k,st,mst,mk)
{
    #
    #   Find the smallest time step
    #
    if (maxctrlramp > 0)
    {
        mst = 1000000
        mk = 0
        for (k=1; k <= maxctrlramp; k++)
        {
            if (ctrlramp[k,rctrlmore] > 0)
            {
                #
                #   At least one is still active
                #
                st = ctrlramp[k,rctrlsteptime]
                if (st < 0)
                    st = st * (-1)
                if (st < mst)
                {
                    mst = st
                    mk = k
                }
            }
        }
        if (mk > 0)
        {
            return mst
        }
    }
    return 0.0
}
#	------------------- ramp_getleast ---------------------------------------



function ramp_incp(timep       ,k)
{
    #
    #   Increment the "timep" value for each ramp
    #
    if (maxctrlramp > 0)
    {
        for (k=1; k <= maxctrlramp; k++)
        {
            if (ctrlramp[k,rctrlmore] > 0)
            {
                #
                #   At least one is still active
                #
                ctrlramp[k,rctrltimep] += timep
            }
        }
    }
}
#	------------------- ramp_incp ---------------------------------------



function ramp_dumpramps(curr_st     ,k,cv,cn,ch,vinc,ve)
{
    #
    #   print all ramps that have "triggered"
    #
    if (maxctrlramp > 0)
    {
        for (k=1; k <= maxctrlramp; k++)
        {
            if (ctrlramp[k,rctrlmore] > 0)
            {
                #
                #   At least one is still active
                #
                if (ctrlramp[k,rctrltimep] >= ctrlramp[k,rctrlsteptime])
                {
                    #
                    #   reset the "TimeP" pointer
                    #
                    ctrlramp[k,rctrltimep] = 0
                    ctrlramp[k,rctrlmore] = ctrlramp[k,rctrlmore] - 1
                    if (ctrlramp[k,rctrlmore] < 0)
                        ctrlramp[k,rctrlmore] = 0
                    #
                    #   Print the CONTROL statement
                    #
                    ve = ctrlramp[k,rctrlend]
                    cn = ctrlramp[k,rctrlnum]
                    vinc = ctrlramp[k,rctrlstepvalue]
                    ctrlramp[k,rctrlcurrval] = ctrlramp[k,rctrlcurrval] + vinc
                    cv = ctrlramp[k,rctrlcurrval]
                    #
                    #   check that value has not overshot
                    #
                    if (vinc > 0)
                    {
                        if (cv > ve)
                        {
                            ctrlramp[k,rctrlcurrval] = ve
                            cv = ve
                        }
                    }
                    else
                    {
                        if (cv < ve)
                        {
                            ctrlramp[k,rctrlcurrval] = ve
                            cv = ve
                        }
                    }
                    cv = int(cv)
                    ch = ctrlramp[k,rctrlchannel]
                    #
                    #   output
                    #
                    print ";CONTROL", curr_st, cn, cv, ch >> "hold.sco"
                }
            }
        }
    }
}
#	------------------- ramp_dumpramps --------------------------------------



function ramp_steps(total_dur, dur_incr   ,ts1,ts2,g1,g2,s1,s2,err,ary,rmd,steps)
{
    #
    #   CTRLRAMP
    #   Calculate the number of steps required to perform the ramp
    #

    #
    #   separate the total duration into gain and symbol
    #
    ts1 = parse_relation_or_duration_string(total_dur)
    ts2 = parse_relation_or_duration_string(dur_incr)

    err = split(ts1, ary, ";")
    g1 = ary[1]
    if (g1 == "null")
        g1 = 1
    s1 = ary[2]
    if (s1 == "null")
    {
        print "ERROR: ramp_steps: raw numeric for duration not allowed" | "cat 1>&2"
        exit 1
    }

    err = split(ts2, ary, ";")
    g2 = ary[1]
    if (g2 == "null")
        g2 = 1
    s2 = ary[2]
    if (s2 == "null")
    {
        print "ERROR: ramp_steps: raw numeric for duration increment not allowed" | "cat 1>&2"
        exit 1
    }


    steps = 1           # default
    err = 0
    if (s1 == "o")
    {
        if (s2 == "o")
            rmd = g1 / g2
        else if (s2 == "h")
            rmd = g1 / (g2/2)
        else if (s2 == "q")
            rmd = g1 / (g2/4)
        else if (s2 == "*")
            rmd = g1 / (g2/8)
        else if (s2 == "!")
            rmd = g1 / (g2/16)
        else
            err = 1

    }
    else if (s1 == "h")
    {
        if (s2 == "o")
            rmd = g1 / (g2*2)
        else if (s2 == "h")
            rmd = g1 / g2
        else if (s2 == "q")
            rmd = g1 / (g2/2)
        else if (s2 == "*")
            rmd = g1 / (g2/4)
        else if (s2 == "!")
            rmd = g1 / (g2/8)
        else
            err = 1
    }
    else if (s1 == "q")
    {
        if (s2 == "o")
            rmd = g1 / (g2*4)
        else if (s2 == "h")
            rmd = g1 / (g2*2)
        else if (s2 == "q")
            rmd = g1 / g2
        else if (s2 == "*")
            rmd = g1 / (g2/2)
        else if (s2 == "!")
            rmd = g1 / (g2/4)
        else
            err = 1
    }
    else if (s1 == "*")
    {
        if (s2 == "o")
            rmd = g1 / (g2*8)
        else if (s2 == "h")
            rmd = g1 / (g2*4)
        else if (s2 == "q")
            rmd = g1 / (g2*2)
        else if (s2 == "*")
            rmd = g1 / g2
        else if (s2 == "!")
            rmd = g1 / (g2/2)
        else
            err = 1
    }
    else if (s1 == "!")
    {
        if (s2 == "o")
            rmd = g1 / (g2*16)
        else if (s2 == "h")
            rmd = g1 / (g2*8)
        else if (s2 == "q")
            rmd = g1 / (g2*4)
        else if (s2 == "*")
            rmd = g1 / (g2*2)
        else if (s2 == "!")
            rmd = g1 / g2
        else
            err = 1
    }
    else
        err = 2

    if (err > 0)
    {
        if (err == 1)
        {
            print "ERROR: ramp_steps: unknown symbol", s2, "for duration increment" | "cat 1>&2"
            exit 1
        }
        else
        {
            print "ERROR: ramp_steps: unknown symbol", s1, "for duration" | "cat 1>&2"
            exit 1
        }
    }

    if (rmd <= 1)
        steps = 1
    else
    {
        steps = int(rmd)
        if ((rmd - steps) > 0)
            steps++
    }
    return steps
}
#	------------------- ramp_steps ------------------------------------------



function ramp_steptime(dur_incr   ,ts2,g2,s2,err,ary,rmd)
{
    #
    #   CTRLRAMP
    #   Calculate the (absolute) time for one step of the ramp
    #
    
    #
    #   separate the duration into gain and symbol
    #
    ts2 = parse_relation_or_duration_string(dur_incr)

    err = split(ts2, ary, ";")
    g2 = ary[1]
    if (g2 == "null")
        g2 = 1
    s2 = ary[2]
    if (s2 == "null")
    {
        print "ERROR: ramp_steptime: raw numeric for duration increment not allowed" | "cat 1>&2"
        exit 1
    }

    print "DEBUGGER: g2=", g2, "s2=", s2

    err = 0
    if (s2 == "o")
        rmd = g2 * wdur
    else if (s2 == "h")
        rmd = g2 * hdur
    else if (s2 == "q")
        rmd = g2 * qdur
    else if (s2 == "*")
        rmd = g2 * edur
    else if (s2 == "!")
        rmd = g2 * sdur
    else
        err = 1

    if (err > 0)
    {
        print "ERROR: ramp_steps: unknown symbol", s2, "for duration increment" | "cat 1>&2"
        exit 1
    }
    print "DEBUGGER: steptime= ", rmd
    return rmd
}
#	------------------- ramp_steptime ---------------------------------------



function ramp_stepvalue(val_start, val_end, steps)
{
    return (val_end - val_start) / steps
}
#	------------------- ramp_stepvalue --------------------------------------



function Sc_EvalVar(field,   i,iscol,sfield,islist,lresult,result) {
#
#	Eval Var in a score line
#	The return value must be a number or a matrix of numbers
#	A matrix of numbers is an array of strings
#
#	Assumes that "field" is a variable:  literal numbers are not passed
#	to this function
#
#	Since you can't return an array, the matrix returned will be
#	of the form, "a1 a2 a3... ; b1 b2 b3 ... ; ..."
#	----> "string-matrix" format
#

	# print "DEBUGGER Sc_EvalVar receives (" field ")"

    #
    #   immediately pass-thru a INLINE midi value (%nnn_vvv)
    #
    if (match(field, /%/)==1)
        return field

	#
	#	before we can find the name in the variable list
	#	we must check if there is a leading "^" that specifies
	#	COLUMN evaluation
	#
	if (match(field, /\^/)) {
		iscol = 1
	} # if -- match

	if (iscol) {
		#
		# take off the leading "^"
		#
		sfield = substr(field,2)
	}
	else {
		sfield = field
	}

	#print "Sc_EvalVar: sfield= " sfield

	#
	#	allows mixscore lines
	#
	if (is_a_string(sfield))
		return sfield
	if (is_a_ta(sfield))
		return sfield

	#
	#	find it in the Variable list
	#
	for (i=1; i<=varcount; i++) {

		if (var_name[i] == sfield) {

			if (var_size[i] > 1) {
				islist = 1
			}
			#print "DEBUGGER: Sc_EvalVar: sfield value=" var_value[i]

			if (islist) {

				if (iscol) {
					#print "Sc_EvalVar passes (" sfield ") to Sc_EvalCol"
					result = Sc_EvalCol(sfield)
					#print "DEBUGGER: Sc_EvalVar: ScEvalCol returns:"
					#print_string_matrix(result)
					return result
				}
				else {
					result = Sc_EvalRow(sfield)
					#print "DEBUGGER: Sc_EvalVar: ScEvalRow returns:"
					#print_string_matrix(result)
					return result
				}

			}
			else {
				#
				#	a single field
				#
				if (var_op[i] != "null") {
					#
					#	if this is a "function" variable defined with seteval
					#	we must return the ultimate value resolved by the
					#	nested operations (run Eval_Variable)
					#
					#print "DEBUGGER: seteval var " var_name[i]
					return Eval_Variable(var_name[i])
					
				}
                else if (match(var_value[i],/%.*/)==1) {
					return var_value[i]
                }
				else if (is_a_numeric_var(var_value[i])) {
					#print "DEBUGGER: Sc_EvalVar: numeric= " var_value[i]
					return var_value[i]
				}
				else if (is_a_relation_symbol(var_value[i])) {
					#print "DEBUGGER: Sc_EvalVar: relation= " var_value[i]
					return var_value[i]
				}
				else if (is_a_string(var_value[i])) {
					#print "DEBUGGER: Sc_EvalVar: relation= " var_value[i]
					return var_value[i]
				}
				else if (is_a_ta(var_value[i])) {
					#print "DEBUGGER: Sc_EvalVar: relation= " var_value[i]
					return var_value[i]
				}
				else {
					#
					#	recuresively call Sc_EvalVar until
					#	a number, or array of strings, is found
					#
					result = Sc_EvalVar(var_value[i])
					#print "DEBUGGER: return from recursive Sc_EvalVar: ", result
					lresult = result
					while (is_a_numeric_var(result)==0 && is_a_relation_symbol(result)==0 && is_a_string(result)==0 && is_a_ta(result)==0) {
						result = Sc_EvalVar(result)
						if (result == "null") {
							return lresult
						}
					}
					return result
				}

			} # else -- a single field

		}

	} # for -- i

	# print "DEBUGGER: Sc_EvalVar: ",  sfield, "not in list"

	result = sprintf("null")
	return result
}
#----------------------    Sc_EvalVar  -----------------------------------




function Sc_EvalRow(field,    i,j,k,ind,as,ary,mary,mrc,maxrc,maxrows,el,result) {
#
#	Evaluate a list of recursive ptrs as a row: build a matrix from the
#	top row downwards.
#
#	Since you can't return an array, the matrix returned will be
#	of the form, "a1 a2 a3... ; b1 b2 b3 ... ; ..."
#	----> "string-matrix" format
#

	#print "Sc_EvalRow receives (" field ")"

	#
	#	find the variable
	#
	for (i=1; i<=varcount; i++) {

		if (var_name[i] == field) {
			ind = i
		} # if -- field

	} # for -- i

	#

	#
	# parse the list
	#
	as = split(var_value[ind], ary, " ")
	if (as != var_size[ind]) {
		#print "ERROR: Sc_EvalRow: num fields in list not equal size" | "cat 1>&2" 
		exit 1
	}

	#
	#	if it is a comment line, just ignore it
	#
	if (ary[1] == "c") {
		#print "Sc_EvalRow: returning c line: (" var_value[ind] ")"
		return var_value[ind]
	}

	#
	#	loop through list
	#
	for (i=1; i<=as; i++) {
		#
		#	if it is a number or rel-symb, cat it to the
		#	string "matrix", else pass it to Sc_EvalVar
		#

		if (is_a_numeric_var(ary[i])) {
			mary[i] = ary[i]
			#print "Sc_EvalRow: numeric mary " mary[i]
		}
		else if (is_a_relation_symbol(ary[i])) {
			mary[i] = ary[i]
			#print "Sc_EvalRow: relation mary " mary[i]
		}
		else {
			#
			#	the variable in ary[i] will be expanded
			#	into a string-matrix of literals
			#
			#	for now save this string-matrix in this
			#	array
			#
			mary[i] = Sc_EvalVar(ary[i])
			#print "Sc_EvalRow: from Sc_EvalVar:"  mary[i], " from the arg (" ary[i] ")"

		} # else -- a variable

	} # for i in ary


	#
	#	get max dimensions
	#
	maxrows = 1
	for (i=1; i<=as; i++) {

		mrc = get_string_matrix_rowscols(mary[i])
		split(mrc, maxrc, " ")
		if (maxrc[1] > maxrows) {
			maxrows = maxrc[1]
		}

	}

	#
	#	loop down the rows building the final string-matrix
	#
	for (i=1; i<=maxrows; i++) {

		for (j=1; j<=as; j++) {

			mrc = get_string_matrix_rowscols(mary[j])
			split(mrc, maxrc, " ")
			#
			#	loop thru this submatrix columns
			#
			for (k=1; k<=maxrc[2]; k++) {
				#
				#	get the entry from this row,col
				#	if this row is beyond the max row of
				#	this submatrix, then repeat its last
				#	row entry
				#
				if (i <= maxrc[1]) {
					el = get_element_from_string_matrix(mary[j], i, k)
				}
				else {
					el = get_element_from_string_matrix(mary[j], maxrc[1], k)
				}
				#
				#	cat this element into this row of string
				#
				result = sprintf("%s %s", result, el)
			}

		} # for -- j in mary

		#
		#	next row--cat a ";" into the string
		#
		if (i < maxrows) {
			result = sprintf("%s ;", result)
		}

	} # for -- i


	return result
}
#------------------------  Sc_EvalRow  ----------------------------------




function Sc_EvalCol(field,    i,j,k,ind,as,ary,mary,mrc,maxrc,maxcols,el,result,ret) {
#
#	Evaluate a list of recursive ptrs as a column: build a matrix from the
#	left column towards the right
#
#	Since you can't return an array, the matrix returned will be
#	of the form, "a1 a2 a3... ; b1 b2 b3 ... ; ..."
#	----> "string-matrix" format
#

	#print "Sc_EvalCol receives (" field ")"

	#
	#	find the variable
	#
	for (i=1; i<=varcount; i++) {

		if (var_name[i] == field) {
			ind = i
		} # if -- field

	} # for -- i

	#

	#
	# parse the list
	#
	as = split(var_value[ind], ary, " ")
	if (as != var_size[ind]) {
		print "ERROR: Sc_EvalCol: num fields in list (" var_value[ind] ")  not equal size " var_size[ind], "at line", file_line_count | "cat 1>&2" 
		exit 1
	}


	#
	#	if it is a comment line, just ignore it
	#
	if (ary[1] == "c") {
		#print "Sc_EvalCol: returning c line: (" var_value[ind] ")"
		return var_value[ind]
	}

	#
	#	loop through list
	#
	for (i=1; i<=as; i++) {
		#
		#	if it is a number or rel-symb, cat it to the
		#	string "matrix", else pass it to Sc_EvalVar
		#

		if (is_a_numeric_var(ary[i])) {
			mary[i] = ary[i]
			#print "numeric mary " mary[i]
		}
		else if (is_a_relation_symbol(ary[i])) {
			mary[i] = ary[i]
			#print "relation mary " mary[i]
		}
		else {
			#
			#	the variable in ary[i] will be expanded
			#	into a string-matrix of literals
			#
			#	for now save this string-matrix in this
			#	array
			#
			mary[i] = Sc_EvalVar(ary[i])
			#print "in Sc_EvalCol: Sc_EvalVar returns " mary[i]

		} # else -- a variable

	} # for i in ary


	#
	#	get max dimensions
	#
	maxcols = 1
	for (i=1; i<=as; i++) {

		mrc = get_string_matrix_rowscols(mary[i])
		split(mrc, maxrc, " ")
		if (maxrc[2] > maxcols) {
			maxcols = maxrc[2]
		}

	}

	#
	#	loop across the columns building the final string-matrix
	#
	for (i=1; i<=maxcols; i++) {

		for (j=1; j<=as; j++) {

			mrc = get_string_matrix_rowscols(mary[j])
			split(mrc, maxrc, " ")
			#
			#	loop thru this submatrix rows
			#
			for (k=1; k<=maxrc[1]; k++) {
				#
				#	get the entry from this row,col
				#	if this col is beyond the max col of
				#	this submatrix, then repeat its last
				#	col entry
				#
				if (i <= maxrc[2]) {
					el = get_element_from_string_matrix(mary[j], k, i)
				}
				else {
					el = get_element_from_string_matrix(mary[j], k, maxrc[1])
				}
				#
				#	cat this element into this col of string
				#	(this is done just like Sc_EvalRow, above
				#	because we will simply transpose the matrix before
				#	returning it)
				#
				result = sprintf("%s %s", result, el)
			}

		} # for -- j in mary

		#
		#	next row--cat a ";" into the string
		#
		if (i < maxcols) {
			result = sprintf("%s ;", result)
		}

	} # for -- i

	#
	#	now transpose the matrix
	#
	ret = transpose_string_matrix(result)

	return ret
}
#------------------------  Sc_EvalCol  ----------------------------------



function string_matrix_to_csound(sm,   i,j,as,ary,cas,cary,cmt,ltempo,isltempo,\
         instr,stt,ltime,dur,outline,pseq,icn,ica,ics,icch,last_icw,mval)
{
#
#	after Sc_EvalVar crunches the variable on the score line
#	we convert it into csound score lines
#	"pseq" is a flag that say we're using the "parallel sequence"
#	start times that result with the "@" start time symbol
#

	ltime = scoretime
	print "global score time is " scoretime
	pseq = 0
    last_icw = ""
    ics = ""

	#
	#	split into rows
	#
	as = split(sm, ary, ";")
	for (i=1; i<=as; i++) {

		#
		#	split this row into columns
		#
		cas = split(ary[i], cary, " ")

		#---------------------------------------------
		#	column 1
		#---------------------------------------------
	
		#
		#	the first column is always a tempo statement
		#	and doesn't become a column in the csound score
		#

		#
		#	evaluate the first column as a tempo statement
		#
		if (is_a_numeric_var(cary[1])) {
			ltempo = cary[1]
			isltempo=1
		}
		else if (cary[1] == "&") {
			isltempo=0
		}
		else if (is_a_relation_symbol(cary[1])) {
			ltempo = get_relation(cary[1], tempo)
			isltempo=1
		}
		else {
			#
			#	special case of a FILE line
			#
			if (index(cary[1], "FILE")) {
				is_a_mixscore_line = 1
				isltempo=0
			}
			else if (index(cary[1], "MARK")) {
				is_a_mark_line = 1
				isltempo=0
			}
			else {
				print "ERROR: string_matrix_to_csound: 1st column of a"\
					" variable expansion is not a tempo at line", file_line_count | "cat 1>&2" 
				print "---> (" cary[1] ")" | "cat 1>&2" 
				print "in line (" ary[i] ")" | "cat 1>&2" 
				exit 1
			}
		}

		#
		#	bug fix
		#	removed   "outline = ltempo"  9/27/2005
		#
		outline = ""
		if (is_a_mixscore_line) {
			outline = sprintf(";FILE")
		}
		else if (is_a_mark_line) {
			outline = sprintf(";%s", strip_quotes(cary[1]))
			#
			#	print the line out to "hold.sco"
			#
			print outline >> "hold.sco"
			continue
		}

		#---------------------------------------------
		#	column 2	--  must be an instr index
		#---------------------------------------------
		#
		#	evaluate the second column as instrument statement
		#
		if (is_a_numeric_var(cary[2])) {
			instr = cary[2]
			
		}
		else {
			#
			#	ok in the special case of a FILE line
			#
			if (is_a_mixscore_line) {
				#
				#	the letter in fron t of the number will need to
				#	be accounted when the print out happens
				#
				# instr = cary[2]
				instr = strip_quotes(cary[2])
			}
			else {
				print "ERROR: string_matrix_to_csound: 2nd column of a"\
					" variable expansion is not legal for an instrument"\
					" index at line", file_line_count | "cat 1>&2" 
				print "---> (" cary[2] ")" | "cat 1>&2" 
				print "in line (" ary[i] ")" | "cat 1>&2" 
				exit 1
			}
		}

		if (is_a_mixscore_line) {
			#
			#	mixscore index
			#
			outline = sprintf("%s %s", outline, instr)
		}
		else {
			#
			#	csound instr id
			#
			outline = sprintf("%s i%g", outline, instr)
		}


		#---------------------------------------------
		#	column 3	--  must be a start time
		#---------------------------------------------
		#
		#	evaluate the third column as a start time
		#
		if (is_a_numeric_var(cary[3])) {
			stt = cary[3]
		}
		else if (is_a_relation_symbol(cary[3])) {
			if (is_a_relation_scoretime(cary[3])) {
				pseq = 0
				#
				# this is an "&" type start time
				#
				stt = get_relation(cary[3], ltime)
			}
			else if (is_a_relation_ascoretime(cary[3])) {
				pseq = 1
				#
				# this is an "@" type start time
				#
				stt = get_relation(cary[3], scoretime)
			}
		}
		else {
			print "ERROR: string_matrix_to_csound: 3rd column of a"\
				" variable expansion is not legal for a start time at line", file_line_count | "cat 1>&2" 
			print "---> (" cary[3] ")" | "cat 1>&2" 
			print "in line (" ary[i] ")" | "cat 1>&2" 
			exit 1
		}

		#
		#	(July 5, 2005)
		#	Before we print the start time to the output score line
		#	introduce a random offset if desired
		#
		if (use_random) {
			#
			#	random_range should be a fractional value between 0 and 1
			#	which is the percentage (+ or -) of a quater note, qdur
			#
			stt = stt + (qdur * random_range * 2 * (rand()-0.5))
		}

		#
		#	output the start time
		#
		outline = sprintf("%s %g", outline, stt)


		#---------------------------------------------
		#	column 4	--  must be a duration
		#---------------------------------------------
		#
		#	evaluate the fourth column as a duration
		#
		if (is_a_numeric_var(cary[4])) {
			dur = cary[4]
		}
		else if (is_a_relation_symbol(cary[4])) {
			if (isltempo) {
				#
				#	this line has a non-default tempo
				#
				if (pseq) {
					#
					# "parallel" start times with "@" symbols
					#
					dur = get_duration(cary[4])
				}
				else {
					#
					# "serial" start times with "&" symbols
					# the tempo is slowed down because the durations are
					# increased, and therefore, the start time delayed
					#
					dur = get_duration_with_ltempo(cary[4], ltempo)
				}
			}
			else {
				dur = get_duration(cary[4])
			}
		}
		else {
			print "ERROR: string_matrix_to_csound: 4th column of a"\
				" variable expansion is not legal for a duration at line", file_line_count | "cat 1>&2" 
			print "---> (" cary[4] ")" | "cat 1>&2" 
			print "in line (" ary[i] ")" | "cat 1>&2" 
			exit 1
		}

		outline = sprintf("%s %g", outline, dur)



		#
		#	now start reading from arbitrary assignments
		#	all variables should have been reduced to a number
		#	after the call to Sc_EvalVar
		#
		for (j=5; j<=cas; j++) {

            if (match(cary[j], /%[0-9]+_[0-9a-zA-Z$]+/)) {
                #
                #   This clause had to be moved before the is_a_numeric
                #   clause below because it was being caught there
                #

                #   -------------------------------------------------------
                #   inline midi control parameter of form
                #   %nnn_vvv, which means control nnn of value vvv, and
                #   nnn and vvv are assummed to be midi-type numbers in the
                #   range 0-127
                #   example:
                #       %7_40
                #   set the volume (control 7) to midi value 40 (31%)
                #       %10_64
                #   sets the stereo pan (control 10) to midi value 64 (center)
                #       %1_110
                #   sets the modulation wheel (control 1) to 110
                #   -------------------------------------------------------

                icn = split(cary[j], ica, /[%_]/)
                if (icn < 2)
                {
                    print "ERROR: invalid INLINE midi control"\
                        " parameter ", cary[j], "at line", file_line_count,\
                        ", fval=", ica[1] | "cat 1>&2" 
                    exit 1
                }

                #
                #   Is the "vvv" part a variable reference?
                #   Example:
                #       %7_$sval
                #   where sval is a variable defined with eval.
                #
                mval = 0
                mval = Eval_Variable(ica[3])

                #
                #   print it into the Csound row, the MIDI number vvv is normalized
                #   into a fractional decimal between 0 and 1
                #
				outline = sprintf("%s %g", outline, mval/127)
                mval = int(mval)

                #
                #   If this is not a repetition of the previous note's control
                #   value, put a new CONTROL statement into the score
                #
                if (cary[j] != last_icw)
                {
                    #
                    #   find the midi channel for this pattern
                    #   (cary[2] should be the instrument number--see above)
                    #   The global array called "instr_to_channel" must already have
                    #   an entry for this instrument number
                    #
                    icch = int(cary[2])
                    if ( icch in instr_to_channel )
                    {
                        icch_ch = instr_to_channel[icch]
                    }
                    else
                    {
                        print "ERROR: invalid INLINE midi control"\
                            " parameter ", cary[j], "at line", file_line_count\
                            ", there is such MIDI channel yet" | "cat 1>&2" 
                        exit 1
                    }

                    #
                    #   MIDI_MASTER mode?
                    #   Intercept "mval" if in the MIDI_MASTER mode.
                    #
                    if (midi_master_flag)
                    {
                        #
                        #   Does the channel match?
                        #
                        if (icch_ch == midi_master_channel)
                        {
                            #
                            #   Yes, this is the channel to me mastered.
                            #
                            if (int(ica[2]) == 7)
                            {
                                midivol = find_midi_master_volume()
                                if (midivol >= 0)
                                {
                                    mval = midivol
                                }
                            }
                            else
                            {
                                midictrl = find_midi_master_ctrl(ica[2])
                                if (midictrl >= 0)
                                {
                                    mval = midictrl
                                }
                            }
                        }
                        else
                        {
                            #
                            #   No, this midi channel to be ignored
                            #
                            if (is_midi_master_ignore(icch_ch) == 0)
                            {
                                if (int(ica[2]) == 7)
                                {
                                    #
                                    #   set the volume to 0
                                    #
                                    mval = 0
                                }
                            }
                        }
                    }

                    #
                    #   MIDI_MUTE mode?
                    #   Intercept "mval" if in the MIDI_MUTE mode.
                    #
                    if (midi_mute_flag)
                    {
                        #
                        #   Does the channel match?
                        #
                        if (find_midi_mute_channel(icch_ch))
                        {
                            #
                            #   Yes, this is a channel to be muted.
                            #
                            if (int(ica[2]) == 7)
                            {
                                #
                                #   set the volume to 0
                                #
                                mval = 0
                            }
                        }
                    }

                    #
                    #   create the "ics" string, which will be a CONTROL statement
                    #   in the Csound score
                    #
                    ics = sprintf(";CONTROL %s %s %s", stt, ica[2], mval)
                    ics = sprintf("%s %d", ics, icch_ch)
                }
                else
                {
                    ics = ""
                }
                last_icw = cary[j]
            }

			else if (is_a_numeric_var(cary[j])) {
                #
                #   Default, normal case
                #
				outline = sprintf("%s %g", outline, cary[j])
			}
			else {
				#
				#	special case of a FILE line
				#
				if (is_a_string(cary[j])) {
					outline = sprintf("%s %s", outline, strip_quotes(cary[j]))
				}
				else if (is_a_ta(cary[j])) {
					outline = sprintf("%s %s", outline, convert_ta(cary[j]))
				}
				else {
					print "ERROR: string_matrix_to_csound: column " j " of a"\
						" variable expansion is not legal at line", file_line_count,\
					    " (" cary[j] "), in line (" ary[i] ")" | "cat 1>&2" 
					exit 1
				}
			}


		} # for -- cols

		#
		#
		#	print the line out to "hold.sco"
		#
		print outline >> "hold.sco"

        #
        #   Special MIDI or META-MIDI statement due to an inline amp or pan
        #   control parameter?  This is after the score line for the last note.
        #
        if (length(ics) > 0)
        {
            print ics >> "hold.sco"
        }

		ltime += dur

	} # for -- rows

}
#--------------------- string_matrix_to_csound -------------------------




