#
#	mscore.awk
#
#  -----------------------------------------------------------------------
#  Copyright 2007  Alfred Steffens Jr.
#  -----------------------------------------------------------------------
#  -----------------------------------------------------------------------
#    Copying Permission:
#
#    mscore 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.
#
#    mscore 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 mscore (see file called "COPYING"); if not, write to the Free
#    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
#    02111-1307  USA
#  ------------------------------------------------------------------------
#
#   Mscore release 1.1
#
#	ver. 1.01	9/22/2007		A. Steffens Jr.   Bug fix--added sequencer
#                               specific codes and smpte offset codes.
#	ver. 1.0	2/08/2005		A. Steffens Jr.
#
#





BEGIN {

	version_no = "1.01 (package 1.1)"
	#
	#	header flags
	#
	header_done=0
	have_midi_format=0
	have_no_tracks=0
	have_division=0

	midi_format=1		# default
	no_tracks=0
	division=0
	tempo_bpm=0
	have_tempo=0
	currtime=0			# midi ticks
	lasttime=0

	note_event_index=0
	lineno=0
}
#	---------------   BEGIN ---------------------------------------------




$1 ~ /#(.)*/ {
	lineno++
	next
}


$1 ~ /MIDI_FORMAT/ {

	if (header_done==0 && have_midi_format==0) {
		#
		#	ignore any format statement after the header is written
		#
		if ($2 == "single_track") {
			midi_format = 0
		}
		else if ($2 == "multi_track") {
			midi_format = 1
		}
		else if ($2 == "multi_song") {
			midi_format = 2
			print "ERROR: mscore does not support multi-song format"
			exit
		}
		have_midi_format=1

		#
		#	have enough to write the header?
		#
		if (have_midi_format && have_no_tracks && have_division) {
			#
			#	write the header
			#
			write_header()
			header_done=1
		} # if ---

	}
	lineno++
	next
}



$1 ~ /TRACKS/ {
	if (header_done==0 && have_no_tracks==0) {
		no_tracks = $2
		have_no_tracks=1

		#
		#	have enough to write the header?
		#
		if (have_midi_format && have_no_tracks && have_division) {
			#
			#	write the header
			#
			write_header()
			header_done=1
		} # if ---
	}
	lineno++
	next
}



$1 ~ /TICS_PER_BEAT/ {
	if (header_done==0 && have_division==0) {
		division = $2
		have_division=1

		#
		#	have enough to write the header?
		#
		if (have_midi_format && have_no_tracks && have_division) {
			#
			#	write the header
			#
			write_header()
			header_done=1
		} # if ---
	}
	lineno++
	next
}






#
#	line by line section
#
{
	#	---------------------------------------------
	#	NO "next" STATEMENTS ALLOWED IN THIS SECTION
	#	---------------------------------------------


	#
	#	ignore blank lines
	#
	if (NF == 0) {
		lineno++
		next
	}


	#
	#	this time duration
	#	is the duration UNTIL THE NEXT EVENT--not the duration of the note
	#	("currtime" is the sum of all the previous score durations)
	#

	ticks = duration_score_to_ticks($1)
	# print "DEBUGGER: " $1 " ==> " ticks " ticks"

	#
	#	convert the duration in ticks into a variable length hex number
	#
	# vartimestr = make_variable_length_hex(ticks)



	#
	#	argument $2 will determine the type of event
	#

	if ($2 == "MIDI") {

		note_event_index += 1
		track_index = substr($3, 3)
		trk_eventtype[track_index, note_event_index] = 1		# MIDI
		trk_channel[track_index, note_event_index] = substr($4, 3)
		trk_currtime[track_index, note_event_index] = currtime
		trk_midicommand[track_index, note_event_index] = substr($5, 1, 2)
		trk_midibyte2[track_index, note_event_index] = substr($5, 3)
		if (NF >= 6) {
			trk_midibyte3[track_index, note_event_index] = substr($6,3)
		}
		trk_duration[track_index, note_event_index] = 0
		#
		#	delta time since previous event
		#
		trk_vtime[track_index, note_event_index] = ticks

	}
	else if ($2 == "META") {

		note_event_index += 1
		track_index = substr($3, 3)
		trk_eventtype[track_index, note_event_index] = 2		# META
		trk_channel[track_index, note_event_index] = substr($4, 3)
		trk_currtime[track_index, note_event_index] = currtime
		#
		#	delta time since previous event
		#
		trk_vtime[track_index, note_event_index] = ticks
		#
		#	tempo?
		#
		if ($4 == "TEMPO") {
			tempo_bpm = $5
			trk_metatype[track_index, note_event_index] = "51"
			trk_metavalue[track_index, note_event_index] = $5
		}
		else if ($4 == "SEQNUM") {
			trk_metatype[track_index, note_event_index] = "00"
			trk_metavalue[track_index, note_event_index] = $5
		}
		else if ($4 == "TEXT") {
			trk_metatype[track_index, note_event_index] = "01"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "COPYRIGHT") {
			trk_metatype[track_index, note_event_index] = "02"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "TRACKNAME") {
			trk_metatype[track_index, note_event_index] = "03"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "INSTRNAME") {
			trk_metatype[track_index, note_event_index] = "04"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "LYRICS") {
			trk_metatype[track_index, note_event_index] = "05"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "MARKER") {
			trk_metatype[track_index, note_event_index] = "06"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "CUE") {
			trk_metatype[track_index, note_event_index] = "07"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "CHANNEL") {
			trk_metatype[track_index, note_event_index] = "20"
			trk_metavalue[track_index, note_event_index] = $5
		}
		else if ($4 == "MIDIPORT") {
			trk_metatype[track_index, note_event_index] = "21"
			trk_metavalue[track_index, note_event_index] = $5
		}
		else if ($4 == "TIME_SIGNATURE") {
			trk_metatype[track_index, note_event_index] = "58"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "KEY_SIGNATURE") {
			trk_metatype[track_index, note_event_index] = "59"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "SMPTE_OFFSET") {
			trk_metatype[track_index, note_event_index] = "54"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else if ($4 == "SEQUENCER_SPECIFIC") {
			trk_metatype[track_index, note_event_index] = "7f"
			trk_metavalue[track_index,note_event_index] = $5
			for (i=6; i<=NF; i++) {
				trk_metavalue[track_index,note_event_index] = sprintf("%s %s", trk_metavalue[track_index,note_event_index], $i)
			}
		}
		else {
		}
	}
	else if ($2 == "SYSEX") {
		track_index = substr($3, 3)
		note_event_index += 1
		trk_eventtype[track_index, note_event_index] = 3		# SYSEX
	}
	else if ($2 == "SYSCM") {
		track_index = substr($3, 3)
		note_event_index += 1
		trk_eventtype[track_index, note_event_index] = 4		# SYSCM
	}
	else if ($2 == "SYSRT") {
		track_index = substr($3, 3)
		note_event_index += 1
		trk_eventtype[track_index, note_event_index] = 5		# SYSRT
	}
	else {
		#
		#	must be a note (MIDI) event --  (for example,  @2{67__2q}97 )
		#
		if (NF != (no_tracks+1)) {
			print "ERROR: format error: "
			print
			print " tracks (" no_tracks ") not equal fields (" NF ") at line " lineno
			for (i=1; i<=NF; i++) {
				print i ": " $i
			}
			exit
		}

		note_event_index += 1
		for (track_index=1; track_index<=no_tracks; track_index++) {

			#
			#	start parsing notes at argument $2
			#
			argnum = track_index+1

			#
			#	skip empty tracks
			#
			if ($argnum == "|") {
				continue
			}


			#
			#	separate the note expression on each side of the "__" double
			#	underscore
			#
			ary1[1] = substr($argnum, 1, index($argnum,"_")-1)
			ary1[2] = substr($argnum, index($argnum, "_")+2)

			#
			#	split the "@2{120" side
			#
			err = split(ary1[1], ary2, "{")
			if (err != 2) {
				print "ERROR: splitting note format at \"{\""
				exit
			}

			#
			#	split the "3q}97" side
			#
			err = split(ary1[2], ary3, "}")
			if (err != 2) {
				print "ERROR: splitting note format at \"}\""
				exit
			}

			trk_eventtype[track_index, note_event_index] = 1		# MIDI
			trk_midicommand[track_index, note_event_index] = "NOTEON"

			#
			#	parse the channel from the @n syntax
			#	( "substr" removes the "@" character)
			#
			trk_channel[track_index, note_event_index] = substr(ary2[1], 2)

			#
			#	midi note number
			#
			trk_notenumber[track_index, note_event_index] = ary2[2]

			#
			#	duration of note will be in [!*qho] format
			#	duration must be saved in midi ticks
			#
			trk_duration[track_index, note_event_index] = duration_score_to_ticks(ary3[1])
			#
			#	attack of the note
			#
			trk_attack[track_index, note_event_index] = ary3[2]

			#
			#	the absolute time
			#
			trk_currtime[track_index, note_event_index] = currtime

			#
			#	delta time until next event
			#
			trk_vtime[track_index, note_event_index] = ticks


		} # for -- track_index




	} # else -- a note event

	#
	#	update
	#
	currtime += ticks
	lineno++
}
#
#	---------------------------------------------------------------------
#	end  --  default
#	---------------------------------------------------------------------
#




END {

	#
	#	write out the tracks
	#

	#
	#	first assemble each track into an array of bytes
	#
	for (i=1; i<=no_tracks; i++) {

		#
		#	reset the track string for this track
		#	the track_string holds all the bytes in the track,
		#	in ascii-hex format in an array until all the bytes can
		#	be counted
		#
		for (k in track_string) {
			delete track_string[k]
		}
		index_track_string = 1

		#
		#	start the track_string by storing the ID bytes
		#	into "track_string"
		#
		store_track_head()


		#
		#	loop through the events
		#
		in_index = 0		# fifo input indices
		out_index = 1		# fifo output indices
		for (j=1; j<=note_event_index; j++) {

			#
			#	if this track has something stored at this time slot
			#
			if ( (i,j) in trk_channel) {

				#
				#	save index into the trk_currtime array
				#
				next_j = j	# unless a note off comes first
				currtime = trk_currtime[i,j]
				done_reading_fifo = 0
				#
				#	check if any note off event in the FIFO should come first
				#
				while (done_reading_fifo==0) {

					for (k=out_index; k<=in_index; k++) {
		
						if ( k in fifo_currtime ) {
	
							if (fifo_currtime[k] <= currtime) {
								#
								#	a note off event comes first
								#
								next_j = fifo_eventindex[k]

								#	----------------------------
								#	convert this event into HEX
								#	and append it to the "track_string" array
								#	----------------------------
								vtime = fifo_currtime[k] - lasttime
								store_note_off(vtime, trk_channel[i,next_j], trk_notenumber[i,next_j], 64)
								last_eventtype = 1		# MIDI
								last_midibyte = make_note_off_byte(trk_channel[i,next_j])
								lasttime = fifo_currtime[k]

								#
								#	shift the FIFO
								#
								delete fifo_currtime[k]
								delete fifo_eventindex[k]
								out_index = k+1
							} # if
	
						} # if k in fifo_currtime
	
					} # for k
					if (k > in_index) {
						#
						#	we exhausted the fifo
						#
						done_reading_fifo = 1	# break out
					} # if

				} # while


				#
				#	by now any note off events in the fifo have been handled
				#	jth event
				#

				if (trk_eventtype[i, j] == 1) {
					#
					#	MIDI event
					#
					if (trk_midicommand[i, j] == "NOTEON") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						if (trk_duration[i,j] > 0) {

							vtime = currtime - lasttime
							store_note_on(vtime, trk_channel[i,j], trk_notenumber[i,j], trk_attack[i,j])
							last_eventtype = 1		# MIDI
							last_midibyte = make_note_on_byte(trk_channel[i,j])
							lasttime = currtime
	
	
							#
							#	push its corresponding note off event onto the FIFO
							#
							in_index++
							fifo_currtime[in_index] = currtime + trk_duration[i,j]
							fifo_eventindex[in_index] = j
							#
							#	keep the fifo in increasing order of time
							#
							if (out_index <= in_index) {
								sort_fifo(out_index, in_index)
							}

						} # if -- duration greater than 0


					} # if -- MIDI NOTE
					else if (trk_midicommand[i, j] == "KP") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						vtime = currtime - lasttime
						store_key_pressure(vtime, trk_channel[i,j], trk_midibyte2[i,j], trk_midibyte3[i,j])
						last_eventtype = 1		# MIDI
						last_midibyte = make_key_pressure_byte(trk_channel[i,j])
						lasttime = currtime

					} # else if -- MIDI key pressure
					else if (trk_midicommand[i, j] == "CT") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						vtime = currtime - lasttime
						store_control_change(vtime, trk_channel[i,j], trk_midibyte2[i,j], trk_midibyte3[i,j])
						last_eventtype = 1		# MIDI
						last_midibyte = make_control_change_byte(trk_channel[i,j])
						lasttime = currtime

					} # else if -- MIDI controller change
					else if (trk_midicommand[i, j] == "PG") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						vtime = currtime - lasttime
						store_program_change(vtime, trk_channel[i,j], trk_midibyte2[i,j])
						last_eventtype = 1		# MIDI
						last_midibyte = make_program_change_byte(trk_channel[i,j])
						lasttime = currtime

					} # else if -- MIDI program change
					else if (trk_midicommand[i, j] == "CP") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						vtime = currtime - lasttime
						store_channel_pressure(vtime, trk_channel[i,j], trk_midibyte2[i,j])
						last_eventtype = 1		# MIDI
						last_midibyte = make_channel_pressure_byte(trk_channel[i,j])
						lasttime = currtime

					} # else if -- MIDI channel pressure
					else if (trk_midicommand[i, j] == "PB") {
						#
						#	------------------------------------------
						#	convert this event into HEX
						#	and append it to the "track_string" array
						#	------------------------------------------
						#
						vtime = currtime - lasttime
						store_pitch_bend(vtime, trk_channel[i,j], trk_midibyte2[i,j], trk_midibyte3[i,j])
						last_eventtype = 1		# MIDI
						last_midibyte = make_pitch_bend_byte(trk_channel[i,j])
						lasttime = currtime
					} # else if -- MIDI pitch bend


				} # if -- eventtype  MIDI
				else if (trk_eventtype[i, j] == 2) {
					#
					#	META event
					#
					vtime = currtime - lasttime
					store_meta_event(vtime, i, j)
					lasttime = currtime

				} # else if -- META event


			} # if -- an event in this track


			# lasttime = currtime
		} # for -- j over events

		#
		#	after the last scored event is handled, the fifo must still
		#	be emptied	of note off events
		#
		for (k=out_index; k<=in_index; k++) {
		
			if ( k in fifo_currtime ) {
	
				#
				#	a note off event comes first
				#
				next_j = fifo_eventindex[k]

				#	----------------------------
				#	convert this event into HEX
				#	and append it to the "track_string" array
				#	----------------------------
				vtime = fifo_currtime[k] - lasttime
				store_note_off(vtime, trk_channel[i,next_j], trk_notenumber[i,next_j], 64)
				last_eventtype = 1		# MIDI
				last_midibyte = make_note_off_byte(trk_channel[i,next_j])
				lasttime = fifo_currtime[k]

				#
				#	shift the FIFO
				#
				delete fifo_currtime[k]
				delete fifo_eventindex[k]
				out_index = k+1
	
			} # if k in fifo_currtime
	
		} # for k

		#
		#	end of track marker
		#
		track_string[index_track_string] = "00"
		index_track_string++
		track_string[index_track_string] = "ff"
		index_track_string++
		track_string[index_track_string] = "2f"
		index_track_string++
		track_string[index_track_string] = "00"
		index_track_string++



		#
		#	done writing data into the track buffer
		#
		track_length = index_track_string - 1
		#
		#	convert the decimal length into 4 ascii hex bytes
		#
		mtracklen = track_length - 8
		tmplenstr = decimal_to_8bit_hex_list(mtracklen)
		err = split(tmplenstr, ary, ",")
		if (err > 4) {
			print "ERROR: track length (track " i ") > 4 bytes"
			exit
		}
		incup = 4 - err
		for (k=1; k<=err; k++) {
			track_string[k+4+incup] = ary[k]
		}
		#	------  done with track length ---------





		lasttime = 0	# reset for the next track



		#
		#	write out the data for this track
		#
		for (k=1; k<=track_length; k++) {

			print track_string[k]

		} # for k

	} # for -- i over tracks

}
#	----------------------- END ---------------------------------------




#	---------------------------------------------------------------------
#	---------------------------------------------------------------------
#	---------------------------------------------------------------------
#	Functions
#	---------------------------------------------------------------------
#	---------------------------------------------------------------------
#	---------------------------------------------------------------------





function write_header(tmpstr)
{

	#
	#	the "MThd" id string
	#
	print "4d"
	print "54"
	print "68"
	print "64"

	#
	#	header length (always 6)
	#
	print "00"
	print "00"
	print "00"
	print "06"


	#
	#	format
	#
	tmpstr = sprintf("0%d", midi_format)
	print "00"
	print tmpstr


	#
	#	tracks
	#
	tmpstr = byte_for_bits_8_15(no_tracks)
	print tmpstr
	tmpstr = byte_for_bits_0_7(no_tracks)
	print tmpstr

	#
	#	division
	#
	tmpstr = byte_for_bits_8_15(division)
	print tmpstr
	tmpstr = byte_for_bits_0_7(division)
	print tmpstr


}
#	------------------------- write_header -----------------------------



function store_track_head()
{
	#
	#	store the 4 track ID bytes to the tracks's hex string
	#
	track_string[1] = "4d"
	track_string[2] = "54"
	track_string[3] = "72"
	track_string[4] = "6b"

	track_string[5] = "00"
	track_string[6] = "00"
	track_string[7] = "00"
	track_string[8] = "00"
	index_track_string = 9	# incremented past the ID and the length
}
#	----------------------- store_track_head ----------------------------



function make_note_on_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "90"
	}
	else if (chn == 2) {
		retstr = "91"
	}
	else if (chn == 3) {
		retstr = "92"
	}
	else if (chn == 4) {
		retstr = "93"
	}
	else if (chn == 5) {
		retstr = "94"
	}
	else if (chn == 6) {
		retstr = "95"
	}
	else if (chn == 7) {
		retstr = "96"
	}
	else if (chn == 8) {
		retstr = "97"
	}
	else if (chn == 9) {
		retstr = "98"
	}
	else if (chn == 10) {
		retstr = "99"
	}
	else if (chn == 11) {
		retstr = "9a"
	}
	else if (chn == 12) {
		retstr = "9b"
	}
	else if (chn == 13) {
		retstr = "9c"
	}
	else if (chn == 14) {
		retstr = "9d"
	}
	else if (chn == 15) {
		retstr = "9e"
	}
	else if (chn == 16) {
		retstr = "9f"
	}
	else {
		print "ERROR: make_note_on_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_note_on_byte ----------------------------



function make_note_off_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "80"
	}
	else if (chn == 2) {
		retstr = "81"
	}
	else if (chn == 3) {
		retstr = "82"
	}
	else if (chn == 4) {
		retstr = "83"
	}
	else if (chn == 5) {
		retstr = "84"
	}
	else if (chn == 6) {
		retstr = "85"
	}
	else if (chn == 7) {
		retstr = "86"
	}
	else if (chn == 8) {
		retstr = "87"
	}
	else if (chn == 9) {
		retstr = "88"
	}
	else if (chn == 10) {
		retstr = "89"
	}
	else if (chn == 11) {
		retstr = "8a"
	}
	else if (chn == 12) {
		retstr = "8b"
	}
	else if (chn == 13) {
		retstr = "8c"
	}
	else if (chn == 14) {
		retstr = "8d"
	}
	else if (chn == 15) {
		retstr = "8e"
	}
	else if (chn == 16) {
		retstr = "8f"
	}
	else {
		print "ERROR: make_note_off_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_note_off_byte ----------------------------



function make_key_pressure_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "a0"
	}
	else if (chn == 2) {
		retstr = "a1"
	}
	else if (chn == 3) {
		retstr = "a2"
	}
	else if (chn == 4) {
		retstr = "a3"
	}
	else if (chn == 5) {
		retstr = "a4"
	}
	else if (chn == 6) {
		retstr = "a5"
	}
	else if (chn == 7) {
		retstr = "a6"
	}
	else if (chn == 8) {
		retstr = "a7"
	}
	else if (chn == 9) {
		retstr = "a8"
	}
	else if (chn == 10) {
		retstr = "a9"
	}
	else if (chn == 11) {
		retstr = "aa"
	}
	else if (chn == 12) {
		retstr = "ab"
	}
	else if (chn == 13) {
		retstr = "ac"
	}
	else if (chn == 14) {
		retstr = "ad"
	}
	else if (chn == 15) {
		retstr = "ae"
	}
	else if (chn == 16) {
		retstr = "af"
	}
	else {
		print "ERROR: make_key_pressure_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_key_pressure_byte ----------------------------



function make_control_change_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "b0"
	}
	else if (chn == 2) {
		retstr = "b1"
	}
	else if (chn == 3) {
		retstr = "b2"
	}
	else if (chn == 4) {
		retstr = "b3"
	}
	else if (chn == 5) {
		retstr = "b4"
	}
	else if (chn == 6) {
		retstr = "b5"
	}
	else if (chn == 7) {
		retstr = "b6"
	}
	else if (chn == 8) {
		retstr = "b7"
	}
	else if (chn == 9) {
		retstr = "b8"
	}
	else if (chn == 10) {
		retstr = "b9"
	}
	else if (chn == 11) {
		retstr = "ba"
	}
	else if (chn == 12) {
		retstr = "bb"
	}
	else if (chn == 13) {
		retstr = "bc"
	}
	else if (chn == 14) {
		retstr = "bd"
	}
	else if (chn == 15) {
		retstr = "be"
	}
	else if (chn == 16) {
		retstr = "bf"
	}
	else {
		print "ERROR: make_control_change_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_control_change_byte ----------------------------



function make_program_change_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "c0"
	}
	else if (chn == 2) {
		retstr = "c1"
	}
	else if (chn == 3) {
		retstr = "c2"
	}
	else if (chn == 4) {
		retstr = "c3"
	}
	else if (chn == 5) {
		retstr = "c4"
	}
	else if (chn == 6) {
		retstr = "c5"
	}
	else if (chn == 7) {
		retstr = "c6"
	}
	else if (chn == 8) {
		retstr = "c7"
	}
	else if (chn == 9) {
		retstr = "c8"
	}
	else if (chn == 10) {
		retstr = "c9"
	}
	else if (chn == 11) {
		retstr = "ca"
	}
	else if (chn == 12) {
		retstr = "cb"
	}
	else if (chn == 13) {
		retstr = "cc"
	}
	else if (chn == 14) {
		retstr = "cd"
	}
	else if (chn == 15) {
		retstr = "ce"
	}
	else if (chn == 16) {
		retstr = "cf"
	}
	else {
		print "ERROR: make_program_change_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_program_change_byte ----------------------------



function make_channel_pressure_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "d0"
	}
	else if (chn == 2) {
		retstr = "d1"
	}
	else if (chn == 3) {
		retstr = "d2"
	}
	else if (chn == 4) {
		retstr = "d3"
	}
	else if (chn == 5) {
		retstr = "d4"
	}
	else if (chn == 6) {
		retstr = "d5"
	}
	else if (chn == 7) {
		retstr = "d6"
	}
	else if (chn == 8) {
		retstr = "d7"
	}
	else if (chn == 9) {
		retstr = "d8"
	}
	else if (chn == 10) {
		retstr = "d9"
	}
	else if (chn == 11) {
		retstr = "da"
	}
	else if (chn == 12) {
		retstr = "db"
	}
	else if (chn == 13) {
		retstr = "dc"
	}
	else if (chn == 14) {
		retstr = "dd"
	}
	else if (chn == 15) {
		retstr = "de"
	}
	else if (chn == 16) {
		retstr = "df"
	}
	else {
		print "ERROR: make_channel_pressure_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_channel_pressure_byte ----------------------------



function make_pitch_bend_byte(chn,   retstr)
{
	#
	#	build the command byte
	#
	if (chn == 1) {
		retstr = "e0"
	}
	else if (chn == 2) {
		retstr = "e1"
	}
	else if (chn == 3) {
		retstr = "e2"
	}
	else if (chn == 4) {
		retstr = "e3"
	}
	else if (chn == 5) {
		retstr = "e4"
	}
	else if (chn == 6) {
		retstr = "e5"
	}
	else if (chn == 7) {
		retstr = "e6"
	}
	else if (chn == 8) {
		retstr = "e7"
	}
	else if (chn == 9) {
		retstr = "e8"
	}
	else if (chn == 10) {
		retstr = "e9"
	}
	else if (chn == 11) {
		retstr = "ea"
	}
	else if (chn == 12) {
		retstr = "eb"
	}
	else if (chn == 13) {
		retstr = "ec"
	}
	else if (chn == 14) {
		retstr = "ed"
	}
	else if (chn == 15) {
		retstr = "ee"
	}
	else if (chn == 16) {
		retstr = "ef"
	}
	else {
		print "ERROR: make_pitch_bend_byte: unknown channel (" chn ")"
	}

	return retstr
}
#	---------------------- make_pitch_bend_byte ----------------------------




function store_note_on(timeticks, chn, notenum, noteattack,    err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_note_on: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}


	#
	#	build the command byte
	#
	track_string[index_track_string] = make_note_on_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(notenum)
	index_track_string++
	track_string[index_track_string] = byte_for_bits_0_7(noteattack)
	index_track_string++

}
#	----------------------- store_note_on -------------------------------



function store_note_off(timeticks, chn, notenum, noteattack,    err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_note_off: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}


	#
	#	build the command byte
	#
	track_string[index_track_string] = make_note_off_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(notenum)
	index_track_string++
	track_string[index_track_string] = byte_for_bits_0_7(noteattack)
	index_track_string++

}
#	----------------------- store_note_off -------------------------------



function store_key_pressure(timeticks, chn, notenum, notepressure,    err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_key_pressure: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}

	#
	#	build the command byte
	#
	track_string[index_track_string] = make_key_pressure_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(notenum)
	index_track_string++
	track_string[index_track_string] = byte_for_bits_0_7(notepressure)
	index_track_string++

}
#	----------------------- store_key_pressure -------------------------------



function store_control_change(timeticks, chn, controlnum, value,    err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_control_change: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}

	#
	#	build the command byte
	#
	track_string[index_track_string] = make_control_change_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(controlnum)
	index_track_string++
	track_string[index_track_string] = byte_for_bits_0_7(value)
	index_track_string++

}
#	----------------------- store_control_change ----------------------------



function store_program_change(timeticks, chn, programnum,     err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_program_change: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}

	#
	#	build the command byte
	#
	track_string[index_track_string] = make_program_change_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(programnum)
	index_track_string++

}
#	----------------------- store_program_change ----------------------------



function store_channel_pressure(timeticks, chn, channelpressure,     err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return value is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_channel_pressure: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}

	#
	#	build the command byte
	#
	track_string[index_track_string] = make_channel_pressure_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(channelpressure)
	index_track_string++

}
#	----------------------- store_channel_pressure ----------------------------


function store_pitch_bend(timeticks, chn, byte1, byte2,    err,i,tmpstr,tmpary)
{
	#
	#	store the sequents of ascii-hex bytes into the "track_string"
	#

	#	"chn" is the channel number and runs from 1 to 16

	#
	#	get the vtime as a variable-length number
	#	the return byte2 is a string in which the hex bytes are separated
	#	by commas
	#
	if (timeticks == 0) {
		track_string[index_track_string] = "00"
		index_track_string++
	}
	else {
		tmpstr = make_variable_length_hex(timeticks)
		err = split(tmpstr, tmpary, ",")
		if (err <= 0) {
			print "ERROR: store_pitch_bend: NULL vtime string for timeticks " timeticks
			exit
		}
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
	}

	#
	#	build the command byte
	#
	track_string[index_track_string] = make_pitch_bend_byte(chn)
	index_track_string++


	track_string[index_track_string] = byte_for_bits_0_7(byte1)
	index_track_string++
	track_string[index_track_string] = byte_for_bits_0_7(byte2)
	index_track_string++

}
#	----------------------- store_pitch_bend ----------------------------



function store_meta_event(timeticks, trackn, eventn,   i,err,tmpstr,tmpary,tmpary2,mtype,mlen)
{


	#
	#	get the vtime as a variable-length number
	#	the return byte2 is a string in which the hex bytes are separated
	#	by commas
	#
	tmpstr = make_variable_length_hex(timeticks)
	err = split(tmpstr, tmpary, ",")
	for (i=1; i<=err; i++) {
		track_string[index_track_string] = tmpary[i]
		index_track_string++
	}

	track_string[index_track_string] = "ff"
	index_track_string++

	mtype = trk_metatype[trackn, eventn]
	if (mtype == "00") {
		#
		#	seqnum
		#
		track_string[index_track_string] = "00"		# meta type
		index_track_string++

		if ( (trackn,eventn) in trk_metavalue ) {
			track_string[index_track_string] = "01"		# vlength
			index_track_string++
	
			track_string[index_track_string] = trk_metavalue[trackn,eventn]
			index_track_string++
		} # if -- in array
		else {
			track_string[index_track_string] = "00"		# vlength
			index_track_string++
		}

	}
	else if (mtype == "01") {
		#
		#	text
		#
		track_string[index_track_string] = "01"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}



	} # else if
	else if (mtype == "02") {
		#
		#	copyright
		#
		track_string[index_track_string] = "02"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "03") {
		#
		#	trackname
		#
		track_string[index_track_string] = "03"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "04") {
		#
		#	instrname
		#
		track_string[index_track_string] = "04"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "05") {
		#
		#	lyrics
		#
		track_string[index_track_string] = "05"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "06") {
		#
		#	marker
		#
		track_string[index_track_string] = "06"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "07") {
		#
		#	cue
		#
		track_string[index_track_string] = "07"		# meta type
		index_track_string++

		#
		#	check for empty string
		#
		tmpascii = string_to_ascii_list(trk_metavalue[trackn,eventn])
		if (tmpascii == "") {
			#	vlength
			mlen = 0
			track_string[index_track_string] = "00"
			index_track_string++
		}
		else {
			#	vlength
			mlen = length(trk_metavalue[trackn,eventn])
			tmpstr = make_variable_length_hex(mlen)
			err = split(tmpstr, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
			#
			#	separate the string into hex bytes
			#
			err = split(tmpascii, tmpary, ",")
			for (i=1; i<=err; i++) {
				track_string[index_track_string] = tmpary[i]
				index_track_string++
			}
		}

	} # else if
	else if (mtype == "20") {
		#
		#	channel
		#
		track_string[index_track_string] = "20"		# meta type
		index_track_string++

		track_string[index_track_string] = "01"		# vlength
		index_track_string++

		tmpascii = byte_for_bits_0_7(trk_metavalue[trackn,eventn])
		track_string[index_track_string] = tmpascii
		index_track_string++
	}
	else if (mtype == "21") {
		#
		#	midi port
		#
		track_string[index_track_string] = "21"		# meta type
		index_track_string++

		track_string[index_track_string] = "01"		# vlength
		index_track_string++

		tmpascii = byte_for_bits_0_7(trk_metavalue[trackn,eventn])
		track_string[index_track_string] = tmpascii
		index_track_string++
	}
	else if (mtype == "51") {
		#
		#	tempo
		#
		track_string[index_track_string] = "51"		# meta type
		index_track_string++

		#
		#	we don't know the length yet
		#	---- first convert to raw tempo
		#
		#
		if (trk_metavalue[trackn,eventn]) {
			tempo_raw = 60000000 / trk_metavalue[trackn,eventn]
		}
		else {
			tempo_raw = 0
		}

		#
		#	convert to a list of 8-bit hex bytes
		#
		tmpstr = decimal_to_8bit_hex_list(tempo_raw)
		err = split(tmpstr, tmpary2, ",")
		mlen = err		#	the length in bytes
		#
		#	make a vlength number	-- now that we know the length
		#
		tmpstr = make_variable_length_hex(mlen)
		err = split(tmpstr, tmpary, ",")
		for (i=1; i<=err; i++) {
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}
		#
		#	add the data bytes
		#
		for (i=1; i<=mlen; i++) {
			track_string[index_track_string] = tmpary2[i]
			index_track_string++
		}

	} # else if -- 51 (TEMPO)
	else if (mtype == "58") {
		#
		#	time signature
		#
		track_string[index_track_string] = "58"		# meta type
		index_track_string++

		#	vlength
		mlen = 4
		tmpstr = make_variable_length_hex(mlen)
		track_string[index_track_string] = tmpstr
		index_track_string++

		#
		#	separate the string into hex bytes
		#
		tmpstr = trk_metavalue[trackn,eventn]
		err = split(tmpstr, tmpary, " ")
		if (err != 4) {
			print "ERROR: time signature (" tmpstr ") doesn't have 4 bytes (" err ")"
			exit
		}
		for (i=1; i<=err; i++) {
			if (length(tmpary[i]) != 2) {
				print "ERROR: bad hex format (" tmpstr ") in time signature--must be two-digit"
				exit
			}
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}

	} # else if
	else if (mtype == "59") {
		#
		#	key signature
		#
		track_string[index_track_string] = "59"		# meta type
		index_track_string++

		#	vlength
		mlen = 2
		tmpstr = make_variable_length_hex(mlen)
		track_string[index_track_string] = tmpstr
		index_track_string++

		#
		#	separate the string into hex bytes
		#
		tmpstr = trk_metavalue[trackn,eventn]
		err = split(tmpstr, tmpary, " ")
		if (err != 2) {
			print "ERROR: key signature (" tmpstr ") doesn't have 2 bytes (" err ")"
			exit
		}
		for (i=1; i<=err; i++) {
			if (length(tmpary[i]) != 2) {
				print "ERROR: bad hex format (" tmpstr ") in key signature--must be two-digit"
				exit
			}
			track_string[index_track_string] = tmpary[i]
			index_track_string++
		}

	} # else if

}
#	---------------------- store_meta_event -----------------------------



function decimal_nibble_to_hex(decimal_nibble,   retstr)
{
	#
	#	the return value is a hex digit as a character string
	#

	if (decimal_nibble > 15) {
		print "ERROR: decimal_nibble_to_hex: value greater than a nibble (" decimal_nibble ")"
		exit
	}

	if (decimal_nibble==0) {
		retstr = sprintf("0");
	}
	else if (decimal_nibble==1) {
		retstr = sprintf("1");
	}
	else if (decimal_nibble==2) {
		retstr = sprintf("2");
	}
	else if (decimal_nibble==3) {
		retstr = sprintf("3");
	}
	else if (decimal_nibble==4) {
		retstr = sprintf("4");
	}
	else if (decimal_nibble==5) {
		retstr = sprintf("5");
	}
	else if (decimal_nibble==6) {
		retstr = sprintf("6");
	}
	else if (decimal_nibble==7) {
		retstr = sprintf("7");
	}
	else if (decimal_nibble==8) {
		retstr = sprintf("8");
	}
	else if (decimal_nibble==9) {
		retstr = sprintf("9");
	}
	else if (decimal_nibble==10) {
		retstr = sprintf("a");
	}
	else if (decimal_nibble==11) {
		retstr = sprintf("b");
	}
	else if (decimal_nibble==12) {
		retstr = sprintf("c");
	}
	else if (decimal_nibble==13) {
		retstr = sprintf("d");
	}
	else if (decimal_nibble==14) {
		retstr = sprintf("e");
	}
	else if (decimal_nibble==15) {
		retstr = sprintf("f");
	}
	return retstr
}
#	---------------------- decimal_nibble_to_hex -----------------------



function decimal_3nibble_to_hex_cleared(decimal_nibble,   retstr)
{
	#
	#	the return value is a hex digit as a character string
	#
	#	Used for creating 7-bit hex bytes. A "3nibble" is a 3-bit
	#	nibble.  Therefore, this function should not accept any
	#	value greater than 7.  "cleared" means that the value of
	#	the MSB in the return niblle will be zero.
	#

	if (decimal_nibble > 7) {
		print "ERROR: decimal_3nibble_to_hex_cleared: value greater than a 3nibble (" decimal_nibble ")"
		exit
	}

	if (decimal_nibble==0) {
		retstr = sprintf("0");
	}
	else if (decimal_nibble==1) {
		retstr = sprintf("1");
	}
	else if (decimal_nibble==2) {
		retstr = sprintf("2");
	}
	else if (decimal_nibble==3) {
		retstr = sprintf("3");
	}
	else if (decimal_nibble==4) {
		retstr = sprintf("4");
	}
	else if (decimal_nibble==5) {
		retstr = sprintf("5");
	}
	else if (decimal_nibble==6) {
		retstr = sprintf("6");
	}
	else if (decimal_nibble==7) {
		retstr = sprintf("7");
	}
	return retstr
}
#	---------------------- decimal_3nibble_to_hex_cleared --------------



function decimal_3nibble_to_hex_set(decimal_nibble,   retstr)
{
	#
	#	the return value is a hex digit as a character string
	#
	#	Used for creating 7-bit hex bytes. A "3nibble" is a 3-bit
	#	nibble.  Therefore, this function should not accept any
	#	value greater than 7.  "set" means that the value of
	#	the MSB in the return niblle will be one.
	#

	if (decimal_nibble > 7) {
		print "ERROR: decimal_3nibble_to_hex_set: value greater than a 3nibble"
		exit
	}

	if (decimal_nibble==0) {
		retstr = sprintf("8");
	}
	else if (decimal_nibble==1) {
		retstr = sprintf("9");
	}
	else if (decimal_nibble==2) {
		retstr = sprintf("a");
	}
	else if (decimal_nibble==3) {
		retstr = sprintf("b");
	}
	else if (decimal_nibble==4) {
		retstr = sprintf("c");
	}
	else if (decimal_nibble==5) {
		retstr = sprintf("d");
	}
	else if (decimal_nibble==6) {
		retstr = sprintf("e");
	}
	else if (decimal_nibble==7) {
		retstr = sprintf("f");
	}
	return retstr
}
#	---------------------- decimal_3nibble_to_hex_set --------------




function byte_for_bits_0_7(decimal_number,   retstr,tmpnumber,high_nibble,low_nibble,high_word,low_byte,hxh,hxl)
{
	#
	#	retreive the lowest-order byte from the decimal
	#	number and return it as a hex-format, 2-character string
	#
	#	assumes the number is positive
	#


	tmpnumber = decimal_number

	if (decimal_number > 255) {
			high_word = int(tmpnumber / 256)
			low_byte = tmpnumber - (high_word * 256)
			high_nibble = int(low_byte / 16)
			low_nibble = low_byte - (high_nibble * 16)

	}
	else {
		#
		#	the number is < 255
		#
		if (decimal_number >= 0) {
			#
			#	the number has only 1 byte--convert to hex
			#
			high_nibble = int(tmpnumber / 16)
			low_nibble = tmpnumber - (high_nibble * 16)

		}
		else {
			print "ERROR: byte_for_bits_0_7: negative number not allowed"
			exit
		}

	} # else -- LT 255

	# high_nibble = round(high_nibble)
	# low_nibble = round(low_nibble)
	high_nibble = int(high_nibble)
	low_nibble = int(low_nibble)

	hxh = decimal_nibble_to_hex(high_nibble)
	hxl = decimal_nibble_to_hex(low_nibble)
	# print "DEBUGGER: byte_for_bits_0_7: hi=(" hxh ") for (" high_nibble "), lo=(" hxl ") for (" low_nibble ")"
	retstr = sprintf("%s%s", hxh,hxl)
	return retstr
}
#	---------------------- byte_for_bits_0_7 ----------------------------



function byte_for_bits_8_15(decimal_number,   retstr,tmpnumber,high_nibble,low_nibble,high_word,low16,hi8,hxh,hxl)
{
	#
	#	retreive the 2nd lowest-order byte from the decimal
	#	number and return it as a hex-format, 2-character string
	#
	#	assumes the number is positive
	#


	tmpnumber = decimal_number

	if (decimal_number < 0) {
		print "ERROR: byte_for_bits_8_15: negative number not allowed"
		exit
	}

	high_word = int(tmpnumber / 65536)
	low16 = tmpnumber - (high_word * 65536)
	hi8 = int(low16 / 256)
	high_nibble = int(hi8 / 16)
	low_nibble = hi8 - (high_nibble * 16)


	hxh = decimal_nibble_to_hex(high_nibble)
	hxl = decimal_nibble_to_hex(low_nibble)
	retstr = sprintf("%s%s", hxh,hxl)
	return retstr
}
#	---------------------- byte_for_bits_8_15 ----------------------------



function make_variable_length_hex(decimal_number,   retstr,i,q7,tmpnum,rednum,low,hi,tary,hxstr)
{
	#
	#	returns a string
	#	in the format "xx,xx,xx,xx,..."
	#	where each "xx" is a hex number where only the lower 7 bits are used
	#

	#
	#	1st--separate into 7-bit partitions
	#	We start with the lowest and work up
	#	to the highest byte
	#

	if (decimal_number == 0) {
		return "00"
	}

	q7 = 0			#	array index
	tmpnum = decimal_number
	while (tmpnum > 0) {
		#
		#	divide the number by 128 until it is zero
		#
		rednum = int(tmpnum / 128)
		if (rednum) {
			#
			#	the current fragment has 7 or more bits in it
			#
			low = tmpnum - (rednum * 128)

		} # if tmpnum
		else {
			#
			#	the current fragment has fewer than 7 bits
			#

			low = tmpnum

		} # else -- result is 0

		q7 += 1
		tary[q7] = low			# not hex yet
		tmpnum = rednum
	} # while tmpnum

	#
	#	the highest power of 128 in the number is now given by
	#	"q7" now gives the length of the array
	#

	#
	#	convert to hex
	#
	for (i=q7; i>=1; i--) {


		#
		#	create a hex representation of the byte
		#
		tmpnum = tary[i]
		rednum = int(tmpnum / 16)
		low = tmpnum - (rednum * 16)
		hxstr = decimal_nibble_to_hex(low)

		if (i > 1) {
			#
			#	all but the lowest (last in big endian format)
			#	byte have their MSB set
			#
			hi = decimal_3nibble_to_hex_set(rednum)
			hxstr = sprintf("%s%s", hi, hxstr)
			if (i == q7) {
				retstr = sprintf("%s", hxstr)
			}
			else {
				retstr = sprintf("%s,%s", retstr,hxstr)
			}
		}
		else {

			hi = decimal_3nibble_to_hex_cleared(rednum)
			hxstr = sprintf("%s%s", hi, hxstr)

			if (i == q7) {
				retstr = sprintf("%s", hxstr)
			}
			else {
				retstr = sprintf("%s,%s", retstr,hxstr)
			}

		} # else


	} # for -- i


	return retstr
}
#	--------------------- make_variable_length_hex ----------------------



function duration_gain_and_note(durstr,   afront,err,tfront,aradd)
{
	#
	#	the gain and note part of the duration
	#
	afront = durstr
	if (index(afront, "!")) {
		if (afront == "!") {
			tfront = division * 0.25  # midi ticks
		}
		else {
			err = split(afront, aradd, "!")		# split out the gain in aradd[1]
			tfront = aradd[1] * division * 0.25  # midi ticks
		}
	}
	else if (index(afront, "*")) {
		if (afront == "*") {
			tfront = division * 0.5  # midi ticks
		}
		else {
			err = split(afront, aradd, "*")		# split out the gain in aradd[1]
			tfront = aradd[1] * division * 0.5  # midi ticks
		}
	}
	else if (index(afront, "q")) {
		if (afront == "q") {
			tfront = division * 1.0  # midi ticks
		}
		else {
			err = split(afront, aradd, "q")		# split out the gain in aradd[1]
			tfront = aradd[1] * division * 1.0  # midi ticks
		}
	}
	else if (index(afront, "h")) {
		if (afront == "h") {
			tfront = division * 2.0  # midi ticks
		}
		else {
			err = split(afront, aradd, "h")		# split out the gain in aradd[1]
			tfront = aradd[1] * division * 2.0  # midi ticks
		}
	}
	else if (index(afront, "o")) {
		if (afront == "o") {
			tfront = division * 4.0  # midi ticks
		}
		else {
			err = split(afront, aradd, "o")		# split out the gain in aradd[1]
			tfront = aradd[1] * division * 4.0  # midi ticks
		}
	}
	else {
		#
		#	just an absolute time in seconds
		#
		if (aradd[1] == 0) {
			return 0
		}

		if (tempo_bpm) {
			tfront = aradd[1] * division * tempo_bpm / 60  # midi ticks
		}
		else {
			print "ERROR: no TEMPO defined yet to interpret time stamp"
			exit
		}

	} # else

	return tfront
}
#	----------------- duration_gain_and_note ----------------------------



function duration_score_to_ticks(duration_string,   err,aradd,afront,aend,tfront,tend,tres,as1)
{
	#
	#	converts a duration string from the score file
	#	(i.e.,  [!*qho] format)
	#	into an integer in midi ticks
	#

	#
	#	this should be usable both for the score duration (far LHS of score)
	#	as well as the note score (in a @n{nnn__xx!*qho}aaa format)
	#

	#
	#	standard format:
	#		xx.xxx  DSYM  +-  [xx.xxx DSYM]
	#
	#	think of this line the equation of a straight line in algebra:
	#		y = ax+b
	#		xx.xxx  DSYM  +-  [xx.xxx DSYM]
	#			"a"  "x"  +       "b"
	#
	#	where xx.xxx is a floating point number that multiplies
	#	the duration symbol, DSYM, which is any of !, *, q, h, or o.
	#	o = whole note
	#	h = half note
	#	q = quarter note
	#	* = eighth note
	#	! = sixteenth note
	#
	#	+- is either a "+" sign or a "-" sign
	#	[xx.xxx DSYM]  is an additive offset that can be specified in
	#	note duration format
	#



	aend = ""

	#
	#	try to split the string at a "+" sign
	#
	if (index(duration_string, "+") || index(duration_string, "-")) {
		#
		#	there is an additive duration
		#
		err = split(duration_string, aradd, "+")
		if (err >= 2) {
			#
			#	there was indeed a "+" sign
			#
			afront = aradd[1]
			aend = aradd[2]
	
		} # if -- a "+" sign
		else {
			#
			#	must be a "-" sign
			#
			err = split(duration_string, aradd, "-")
			if (err >= 2) {
				afront = aradd[1]
				aend = aradd[2]
			}
			else {
				afront = ""
				aend = ""
			}
	
		} # else -- no "+" sign

	} # if -- "+" or "-"
	else {
		afront = duration_string
	} # -- no additive part


	#
	#	convert the "front" string ( xxx! or xxx* or xxxq, etc)
	#
	tfront = 0
	if (afront != "") {

		#
		#

		tfront = duration_gain_and_note(afront)

	} # if -- afront


	tend = 0
	if (aend != "") {
		#
		#	convert the "end" string ( [xxx!] or [xxx*] or [xxxq], etc)
		#

		#
		#	strip away the "[ ]" from the additive term
		#
		as1 = substr(aend, index(aend,"[")+1, index(aend,"]")-index(aend,"[")-1)
		tend = duration_gain_and_note(as1)

	} # if -- aend

	tres = round(tfront+tend)
	return (tres)

}
#	-------------------- duration_score_to_ticks ------------------------



function char_to_ascii_hex(strchar)
{

	if (strchar == " ") {
		return "20"
	}
	else if (strchar == "!") {
		return "21"
	}
	else if (strchar == "\"") {
		return "22"
	}
	else if (strchar == "#") {
		return "23"
	}
	else if (strchar == "$") {
		return "24"
	}
	else if (strchar == "%") {
		return "25"
	}
	else if (strchar == "&") {
		return "26"
	}
	else if (strchar == "'") {
		return "27"
	}
	else if (strchar == "(") {
		return "28"
	}
	else if (strchar == ")") {
		return "29"
	}
	else if (strchar == "*") {
		return "2a"
	}
	else if (strchar == "+") {
		return "2b"
	}
	else if (strchar == ",") {
		return "2c"
	}
	else if (strchar == "-") {
		return "2d"
	}
	else if (strchar == ".") {
		return "2e"
	}
	else if (strchar == "/") {
		return "2f"
	}
	else if (strchar == "0") {
		return "30"
	}
	else if (strchar == "1") {
		return "31"
	}
	else if (strchar == "2") {
		return "32"
	}
	else if (strchar == "3") {
		return "33"
	}
	else if (strchar == "4") {
		return "34"
	}
	else if (strchar == "5") {
		return "35"
	}
	else if (strchar == "6") {
		return "36"
	}
	else if (strchar == "7") {
		return "37"
	}
	else if (strchar == "8") {
		return "38"
	}
	else if (strchar == "9") {
		return "39"
	}
	else if (strchar == ":") {
		return "3a"
	}
	else if (strchar == ";") {
		return "3b"
	}
	else if (strchar == "<") {
		return "3c"
	}
	else if (strchar == "=") {
		return "3d"
	}
	else if (strchar == ">") {
		return "3e"
	}
	else if (strchar == "?") {
		return "3f"
	}
	else if (strchar == "@") {
		return "40"
	}
	else if (strchar == "A") {
		return "41"
	}
	else if (strchar == "B") {
		return "42"
	}
	else if (strchar == "C") {
		return "43"
	}
	else if (strchar == "D") {
		return "44"
	}
	else if (strchar == "E") {
		return "45"
	}
	else if (strchar == "F") {
		return "46"
	}
	else if (strchar == "G") {
		return "47"
	}
	else if (strchar == "H") {
		return "48"
	}
	else if (strchar == "I") {
		return "49"
	}
	else if (strchar == "J") {
		return "4a"
	}
	else if (strchar == "K") {
		return "4b"
	}
	else if (strchar == "L") {
		return "4c"
	}
	else if (strchar == "M") {
		return "4d"
	}
	else if (strchar == "N") {
		return "4e"
	}
	else if (strchar == "O") {
		return "4f"
	}
	else if (strchar == "P") {
		return "50"
	}
	else if (strchar == "Q") {
		return "51"
	}
	else if (strchar == "R") {
		return "52"
	}
	else if (strchar == "S") {
		return "53"
	}
	else if (strchar == "T") {
		return "54"
	}
	else if (strchar == "U") {
		return "55"
	}
	else if (strchar == "V") {
		return "56"
	}
	else if (strchar == "W") {
		return "57"
	}
	else if (strchar == "X") {
		return "58"
	}
	else if (strchar == "Y") {
		return "59"
	}
	else if (strchar == "Z") {
		return "5a"
	}
	else if (strchar == "[") {
		return "5b"
	}
	else if (strchar == "]") {
		return "5d"
	}
	else if (strchar == "^") {
		return "5e"
	}
	else if (strchar == "_") {
		return "5f"
	}
	else if (strchar == "`") {
		return "60"
	}
	else if (strchar == "a") {
		return "61"
	}
	else if (strchar == "b") {
		return "62"
	}
	else if (strchar == "c") {
		return "63"
	}
	else if (strchar == "d") {
		return "64"
	}
	else if (strchar == "e") {
		return "65"
	}
	else if (strchar == "f") {
		return "66"
	}
	else if (strchar == "g") {
		return "67"
	}
	else if (strchar == "h") {
		return "68"
	}
	else if (strchar == "i") {
		return "69"
	}
	else if (strchar == "j") {
		return "6a"
	}
	else if (strchar == "k") {
		return "6b"
	}
	else if (strchar == "l") {
		return "6c"
	}
	else if (strchar == "m") {
		return "6d"
	}
	else if (strchar == "n") {
		return "6e"
	}
	else if (strchar == "o") {
		return "6f"
	}
	else if (strchar == "p") {
		return "70"
	}
	else if (strchar == "q") {
		return "71"
	}
	else if (strchar == "r") {
		return "72"
	}
	else if (strchar == "s") {
		return "73"
	}
	else if (strchar == "t") {
		return "74"
	}
	else if (strchar == "u") {
		return "75"
	}
	else if (strchar == "v") {
		return "76"
	}
	else if (strchar == "w") {
		return "77"
	}
	else if (strchar == "x") {
		return "78"
	}
	else if (strchar == "y") {
		return "79"
	}
	else if (strchar == "z") {
		return "7a"
	}
	else if (strchar == "{") {
		return "7b"
	}
	else if (strchar == "|") {
		return "7c"
	}
	else if (strchar == "}") {
		return "7d"
	}
	else if (strchar == "~") {
		return "7e"
	}
	else {
		return "00"
	}

}
#	----------------------- char_to_ascii_hex ---------------------------




function string_to_ascii_list(strval,    i,chr,tmpstr,mlen,nonempty)
{
	#
	#	takes a character string
	#	and converts to a comma-separated list of ascii-hex characters
	#	in a string return value
	#	for example,
	#					"01,2b,34,21,...."
	#
	#

	if (strval == "") {
		return ""
	}

	mlen = length(strval)
	#
	#	test for an empty string
	#
	nonempty = 0
	for (i=1; i<=mlen; i++) {
		if (substr(strval,i,1) != " ") {
			nonempty=1
			break;
		}
	}
	if (nonempty==0) {
		return ""
	}

	tmpstr = char_to_ascii_hex(substr(strval,1,1))
	for (i=2; i<=mlen; i++) {
		chr = char_to_ascii_hex(substr(strval,i,1))
		tmpstr = sprintf("%s,%s", tmpstr, chr)
	}

	return tmpstr
}
#	-------------------- string_to_ascii_list ---------------------------



function decimal_to_8bit_hex_list(decimal_number,   i,tmpstr,tmpnum,pow256,tary,lowbyte,hibyte)
{
	#	takes a decimal number
	#	and converts to a comma-separated list of ascii-hex characters
	#	in a string return value
	#	for example,
	#					"01,2b,34,21,...."
	#

	tmpnum = decimal_number
	pow256 = 1
	while (tmpnum) {

		hibyte = int(tmpnum / 256)

		lowbyte = tmpnum - (hibyte * 256)
		tary[pow256] = lowbyte

		tmpnum = hibyte
		pow256 += 1
	} # while -- tmpnum


	tmpstr = byte_for_bits_0_7(tary[pow256-1])

	for (i=(pow256-2); i>=1; i--) {

		tmpstr = sprintf("%s,%s", tmpstr, byte_for_bits_0_7(tary[i]))

	} # for -- i


	return tmpstr
}
#	------------------ decimal_to_8bit_hex_list --------------------------


function round(decimal,   ival,fval,aval,rval)
{

	ival = int(decimal)
	fval = decimal - ival
	aval = 0
	if (fval > 0) {
		if (fval >= 0.5) {
			aval = 1
		}
	}
	rval = ival + aval
	return rval
}
#	------------------------- round --------------------------------------



function sort_fifo(start, end,    i,j,temp,temp2)
{

	for (i=(start+1); i<=end; ++i) {


		for (j=i; fifo_currtime[j-1] > fifo_currtime[j]; --j) {

			temp = fifo_currtime[j]
			temp2 = fifo_eventindex[j]

			fifo_currtime[j] = fifo_currtime[j-1]
			fifo_eventindex[j] = fifo_eventindex[j-1]

			fifo_currtime[j-1] = temp
			fifo_eventindex[j-1] = temp2

		} # for -- j


	} # for -- i

}
#	------------------------ sort_fifo -----------------------------------


