#!/usr/bin/perl -w
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
#NAME
#  sw2abc - Convert Songwright music notation to ABC notation
#
#SYNOPSIS
#  sw2abc [file]..
#
#REQUIRES
#
#DESCRIPTION
#  This is a conventional unix "filter" program that reads Songwright music
#  notation and writes ABC to stdout.
#
#OPTIONS
#
#EXAMPLES
#
#FILES
#
#BUGS
#
#SEE ALSO
#
#AUTHOR
#  John Chambers <jc@trillian.mit.edu>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #

$| = 1;
$exitstat = 0;
($P = $0) =~ s".*/"";
$V = $ENV{"V_$P"} || $ENV{"D_$P"} || 2;	# Verbose level.

%Note = (
	'<'	=>	'C,',
	'='	=>	'D,',
	'>'	=>	'E,',
	'?'	=>	'F,',
	'@'	=>	'G,',
	'A'	=>	'A,',
	'B'	=>	'B,',
	'C'	=>	'C',
	'D'	=>	'D',
	'E'	=>	'E',
	'F'	=>	'F',
	'G'	=>	'G',
	'a'	=>	'A',
	'b'	=>	'B',
	'c'	=>	'c',
	'd'	=>	'd',
	'e'	=>	'e',
	'f'	=>	'f',
	'g'	=>	'g',
	'h'	=>	'a',
	'i'	=>	'b',
	'j'	=>	"c'",
	'k'	=>	"d'",
	'l'	=>	"e'",
	'm'	=>	"f'",
	'n'	=>	"g'",
	'o'	=>	"a'",
	'p'	=>	"b'",
	'r'	=>	'z',
	'R'	=>	'z',
	'x'	=>	'y',
	'X'	=>	'y',
);
%Acc = (
	' '	=>	'',		# normal
	'-'	=>	'',		# normal
	'#'	=>	'^',	# sharp
	'$'	=>	'^^',	# double sharp
	'&'	=>	'_',	# flat
	'*'	=>	'__',	# double flat
	'%'	=>	'=',	# natural
);
%Clef = (
	'M' => 'treble',	# treble clef
	'm' => 'bass',		# bass clef
	'+' => 'treble',	# treble clef with bars joined to clef below
	'-' => 'bass',		# bass clef not joined below
);

$index = 0;

for $line (<>) {
	$lines ++;
	$line =~ s/[\r\s]+$//;
	print "%== $line\n" if $V>1;
	if (($type,$sep,$space,$data) = ($line =~ /^([A-Z])([-+])(\s*)(.*)/)) {	# Parse SW line
		$cols = 3 + length($3);	# Column counter
		if ($type eq "N") {		# Name of tune
			$title = $data;
			$inhdr = 1;			# We're now reading SW headers
			print "% title=\"$title\"\n" if $V>2;
			++$index;
			print "\n";
			print "X: $index\n";
			print "T: $title\n";
			$composer = $author = $tempo = $speed = $keysig =
			$timesig  = $notes  = $Hline = $Mline = $Lline  = '';
		} elsif ($type eq "C") {	# Composer
			$composer = $data;
			print "% composer=\"$composer\"\n" if $V>3;
			print "C: $composer\n" if $composer;
		} elsif ($type eq "A") {	# Author
			$author = $data;
			print "% author=\"$author\"\n" if $V>3;
			print "C: $author\n" if $author;
		} elsif ($type eq "T") {	# Tempo
			$tempo = $data;
			print "% tempo=\"$tempo\"\n" if $V>3;
			print "P: $tempo\n" if $tempo;
		} elsif ($type eq "S") {	# Speed
			$speed = $data;
			print "% speed=\"$speed\"\n" if $V>3;
			print "Q: $speed\n" if $speed;
		} elsif ($type eq "K") {	# Key
			$keysig = $data;
			print "% keysig=\"$keysig\"\n" if $V>3;
		} elsif ($type eq "B") {	# Time signature
			$timesig = $data;
			print "% timesig=\"$timesig\" data=\"$data\"\n" if $V>3;
			print "M: $timesig\n";
			print "L: 1/16\n";
		} elsif ($type eq "F") {	# Footnote
			$note = $data;
			print "% note=\"$note\"\n" if $V>3;
			print "N: $note\n" if $note;
		# # # # # # # # # # # # # # # # #
		} elsif ($type eq "H") {	# Header (above music)
			$Hline = $data;
		} elsif ($type eq "M" || $type eq 'm' || $type eq '+' || $type eq '-') {
			$Mline = $data;
			$Mtype = $type;
			$Mflag = $sep;			# The [-+] in col 2 is significant
		} elsif ($type eq "L") {	# Lyrics (or chords)
			$Lline = $data;
			&musicline($Mtype,$Mflag,$Hline,$Mline,$Lline);
		}
	} else {
		print "%-- Line not recognized.\n" if $V>1;
	}
}

sub musicline {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Here we attempt to parse a line of SW music.  The main complexity  is  that #
# either  of  the  adjacent lines (linH,linL) may contain chords, but the lower (linL) #
# line may contain lyrics.  Also, more linL lines may follow with more lyrics.   #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
	local($type,$flag,$linH,$linM,$linL) = @_;
	local($h,$m,$l);	# Current char from each line.
	local($chH,$chL);	# Count of chords in H and L lines
	local($a,$clef,$l,$n,$note);
	local($state) = '';	# Current parse state
	print "% - - - - - - - - - - - - - - -\n" if $V>2;
	print "% 1M \"$linM\"\n" if $V>2;
	if ($linM =~ s/^(\d+)//) {	# Clef info
		$clef = $Clef{$type};
		$l = length($1);
		$cols += $l;			# Count the columns
		$bars = int($1);
		print "% Clef '$clef' bars $bars.\n" if $V>2;
		substr($linH,0,$l,'');	# Shift the H line
		substr($linL,0,$l,'');	# Shift the L line
	}
	print "% 2M \"$linM\"\n" if $V>2;
	if ($linM =~ s/^([W][-+]\d+)//) {	# Width info
		print "% Width '$1' ignored.\n" if $V>4;
		$cols += length($1);		# Count the columns
	}
	print "% 3M \"$linM\"\n" if $V>2;
	while ($m = substr($linM,0,1,'')) {	# Next char of middle line
		print "% H$m \"$linH\"\n" if $V>2;
		print "% M$m \"$linM\"\n" if $V>2;
		print "% L$m \"$linL\"\n" if $V>2;
		$h = substr($linH,0,1,'');		# Next char of higher line
		$l = substr($linL,0,1,'');		# Next char of lower line
		++ $cols;					# Count the columns
		if ($state eq '') {			# Not in note
			if ($n = $Note{$m}) {	# Is it a SW note?
				print "% NOTE: $m => $n\n" if $V>2;
				$state = 'n';		# Got a note
			} elsif ($m eq ' '){	# Ignore spaces
				;
			} else {
				print "% Char '$m' not recognized in state '$state'\n" if $V>1;
			}
		} elsif ($m eq ' '){	# Ignore spaces
			;
		} else {
			print %## Unrecognized state '$state' in line $lines char $cols.\n" if $V>1;
		}
	}
	
}

print "$P: Exit with status $exitstat.\n" if $V>1;
exit $exitstat;

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