#!/usr/bin/perl
# $Id: SongInfo.pl,v 1.6 2007-05-20 17:44:27 steve Exp $
# SongInfo [options] infile...
#
) {
if (/^[ \t]*$/) { } # blank line
elsif (/^[ \t]*\%.*$/) { } # comment: ignore
# Variable-setting macros:
elsif (/\\begin\{song/) { begSong($_); } # \begin{song}{title}
elsif (/\\subtitle/) { $subtitle = getContent($_); }
elsif (/\\key/) { $key = getContent($_); }
elsif (/\\category/) { $category = getContent($_); }
elsif (/\\dedication/) { $dedication = getContent($_); }
elsif (/\\description/) { $description = getContent($_); }
elsif (/\\license/) { $license = getContent($_); }
elsif (/\\timing/) { $timing = getContent($_); }
elsif (/\\created/) { $created = getContent($_); }
elsif (/\\notice/) { $notice = getContent($_); }
elsif (/\\cvsid/) { $cvsid = getContent($_); }
elsif (/\\music/) { $music = getContent($_); }
elsif (/\\lyrics/) { $lyrics = getContent($_); }
elsif (/\\arranger/) { $arranger = getContent($_); }
elsif ($title) {
# everything's at the top, so we have it all now.
last;
}
}
close(STDIN);
# Set index_title (for sorting) from title
$index_title = "" . $title;
$index_title =~ s/^(An? |The )//;
# If lyricist specified but composer isn't, composer is the default
$music = $default_songwriter if ! $music && $lyrics;
# If lyricist isn't specified, it's the default
$lyrics = $default_songwriter unless $lyrics;
# look for a .wav file in ../Tracks/$shortname/
$trackddir = "$trackDir/$shortname";
$trackddir = "" unless -d $trackddir;
if ($track) {
# track data (wav file) specified on command line
$track_data = $track;
$track = "";
} elsif ($trackddir) {
# track data from ../tracks/$shortname
# if there's more than one, it takes the last (most recent) one
$track_data = `ls -tr $trackddir | grep .wav | tail -1`;
$track_data = $trackddir . "/" . trim($track_data);
} else {
$track_data = "";
}
}
sub printInfo {
$track_number = ($track_number)? $track_number + 1 : 1;
if ($format eq "cd") {
# One would think that there should be a subchannel, but that fails.
# === current cdrdao is probably screwed up somehow ===
print "TRACK AUDIO\n";
print "COPY\n";
print "CD_TEXT {\n";
print " LANGUAGE 0 {\n";
print " TITLE \"$title\"\n";
print " PERFORMER \"$performer\"\n";
print " SONGWRITER \"$lyrics\"\n";
print " COMPOSER \"$music\"\n" if $music;
print " ARRANGER \"$arranger\"\n" if $arranger;
print " }\n";
print "}\n";
# PREGAP used to work; it now causes an error:
# START 00:02:00 behind or at track end.
# The one in sarge still works, so copy that to /usr/local/bin.
print "PREGAP 0:2:0\n";
#print "SILENCE 0:2:0\n";
#print "START 0:2:0\n";
print "FILE \"$track_data\" 0\n";
if (! $track_data) {
$status = -1;
print STDERR "SongInfo: No track data for $shortname ($title)\n";
}
} elsif ($format eq "list.text") {
# the timing really needs to come off the track_data if present ===
if ($hex) {
print " " . sprintf("%02x", $track_number) . ": $title ($timing)";
} else {
print " " . sprintf("%2d", $track_number) . ": $title ($timing)";
}
if ($long) {
print "\n $description";
}
} elsif ($format eq "list.html" && $hex) {
print (" \n");
print (" | " .
sprintf("%2x", $track_number) .
" | \n");
print (" ");
if (-f "$songDir/$f.html") {
print "$title";
} else {
print "$title";
}
print " | \n";
print "
";
if ($long) {
print (" \n");
print (" | | \n");
print (" $description\n");
print (" | \n");
print ("
");
}
} elsif ($format eq "list.html") {
print (" ");
if (-f "$songDir/$f.html") {
print "$title";
} else {
print "$title";
}
print " ";
} elsif ($format eq "java") {
# Java uses hierarchical property names of the form a.b
# so we can use $shortname.property
} elsif ($format eq "ogg") {
# Output an oggenc argument list.
print "-a '$performer' ";
print "-t \"$title\" ";
print "-c 'songwriter=$lyrics' ";
print "-c 'composer=$music' " if $music;
print "-c 'arranger=$arranger' " if $arranger;
# === needs license and url
print "$track_data\n";
} elsif ($format eq "shell") {
# Shell is name='value' -- need single quotes to prevent expansion
print "shortname='$shortname'\n";
print "filename='$filename'\n";
print "title='$title'\n";
print "index_title='$index_title'\n";
print "subtitle='$subtitle'\n" if $subtitle;
# can't (easily) have multiline items in shell format
#print "dedication='$dedication'\n" if $dedication;
#print "description='$description'\n" if $description;
# === needs license and url
print "lyrics='$lyrics'\n";
print "music='$music'\n" if $music;
print "arranger='$arranger'\n" if $arranger;
print "timing='$timing'\n" if $timing;
print "category='$category'\n" if $category;
print "key='$key'\n" if $key;
print "created='$created'\n" if $created;
print "cvsid='$cvsid'\n" if $cvsid;
} else {
# Sort of a generic java/make format suitable for a only single song
print "shortname=$shortname\n";
print "filename=$filename\n";
print "title=$title\n";
print "index_title=$index_title\n";
print "subtitle=$subtitle\n" if $subtitle;
print "lyrics=$lyrics\n";
print "music=$music\n" if $music;
print "arranger=$arranger\n" if $arranger;
print "timing=$timing\n" if $timing;
print "category=$category\n" if $category;
print "key=$key\n" if $key;
print "created=$created\n" if $created;
print "cvsid=$cvsid\n" if $cvsid;
#$notice $license $dedication
}
}
sub printHeading {
if ($format eq "cd" && $title) {
print "CD_DA
CD_TEXT {
LANGUAGE_MAP {
0 : EN
}
LANGUAGE 0 {
TITLE \"$title\"
PERFORMER \"$default_performer\"
}
}\n\n";
} elsif ($format eq "tracklist" && $title) {
print "Track list for $title\n";
} elsif ($format eq "list.html" && $hex) {
print "\n";
} elsif ($format eq "list.html") {
print "\n";
}
}
sub printFooting {
if ($format eq "list.html" && $hex) {
print "
\n";
} elsif ($format eq "list.html") {
print "\n";
}
}
########################################################################
###
### Macro handlers:
###
### Each of the following routines handles a LaTeX macro.
###
### Separate verses.
sub sepVerse {
if ($vlines) { endVerse(); }
}
### Handle a blank line.
sub blankLine {
if ($vlines) { endVerse(); }
if ($plain) {
print "\n";
$plines = 0;
}
}
### Begin a song:
### Stash the title.
sub begSong {
my ($line) = @_; # input line
$line =~ s/^.*song\}//;
$title = getContent($line);
}
########################################################################
###
### Block conversion:
###
### Each of these routines converts the start or end of a
### delimited block of lines to output format.
###
sub doHeader {
if ($html) { htmlHeader(); }
else { textHeader(); }
$header ++;
}
sub center {
# === need to handle multiple lines ===
my ($text) = @_;
$text =~ s/^[ \t]*//;
$text =~ s/[ \t]*\n$//;
$text =~ s/\\copyright/Copyright/;
my $w = $WIDTH - length($text);
for ( ; $w > 0; $w -= 2) { $text = " " . $text; }
print "$text\n";
}
sub hcenter {
my ($h, $text) = @_;
$text =~ s/^[ \t]*//;
$text =~ s/\\copyright/\©/;
$text =~ s/\n/\
/g;
$text = "$text";
print "$text\n";
}
sub textHeader {
center "$title\n";
if ($subtitle) { center "$subtitle\n"; }
if ($notice) { center "$notice\n"; }
if ($license) { center "$license\n"; }
if ($dedication) { center "$dedication\n"; }
print "\n";
}
sub htmlHeader {
hcenter 1, $title;
if ($subtitle) { hcenter 2, $subtitle; }
if ($notice) { hcenter 3, $notice; }
if ($license) { hcenter 3, $license; }
if ($dedication) { hcenter 3, $dedication; }
print "\n";
}
sub footer {
}
########################################################################
###
### Line conversion:
###
### Each of these routines converts a single line of mixed chords
### and text.
###
### Process the current line:
### Does any necessary dispatching.
sub doLine {
# Put out the header, if this is the very first line.
if (! $header) { doHeader(); }
if ($plain) {
if ($plines == 0) {
if ($html) { print "\n"; }
else { print "\n"; }
}
$_ = deTeX($_);
s/\\newline/$NL/g;
s/\\\///g;
indentLine($_, $indent);
$plines ++;
} else {
if ($vlines == 0) { begVerse(); }
if ($tables) { print tableLine($_); }
else { print chordLine($_); }
$vlines ++;
}
}
### Put out a plain line, possibly indented.
sub indentLine {
my ($line, $indent) = @_;
$line =~ s/^[ \t]*//;
while ($indent--) { $line = " ".$line; }
print $line;
}
### Convert an ordinary line to chords + text
# === does not insert indent yet.
sub chordLine {
my ($line) = @_; # input line
my $cline = ""; # chord line
my $dline = ""; # dest. (text) line
my ($scol, $ccol, $dcol, $inchord, $inmacro) = ($indent, 0, 0, 0, 0);
my $c = ''; # current character
my $p = 0; # current position
$line = deTeX($line);
$line =~ s/^[ \t]*//;
$line =~ s/\\sus/sus/g;
$line =~ s/\\min/m/g;
for ($p = 0; $p < length($line); $p++) {
$c = substr($line, $p, 1);
if ($c eq "\n" || $c eq "\r") { break; }
if ($c eq '[') { $inchord ++; }
elsif ($c eq ']') { $inchord --; }
elsif ($c eq ' ') { if (!$inchord) { $scol ++; } }
elsif ($c eq "\t") {
if (!$inchord) { do {$scol ++; } while ($scol % 8); } }
else {
if ($inchord) {
while ($ccol < $scol) { $cline .= ' '; $ccol ++ }
$cline .= $c;
$ccol ++;
} else {
while ($dcol < $scol) { $dline .= ' '; $dcol ++ }
$dline .= $c;
$dcol ++;
$scol++;
}
}
}
# The result has a newline appended to it.
return (($cline eq "")? $dline : $cline . "\n" . $dline);
}
### Convert a line to a table
### When using tables, each line becomes a separate table.
### This, in turn, becomes a row in a table containing the verse.
sub tableLine {
}
### Convert a line to XML
sub xmlLine {
}
### Remove LaTeX constructs.
### This would be easier with a table.
sub deTeX {
my ($txt) = @_; # input line
while ($txt =~ /\%/) { # TeX comments eat the line break, too.
$txt =~ s/\%.*$//;
$txt .= ;
}
while ($txt =~ /\{\\em[ \t\n]/
|| $txt =~ /\{\\tt[ \t\n]/
|| $txt =~ /\{\\bf[ \t\n]/) {
# This will fail if there's a \bf and \em in one line in that order
if ($txt =~ /\{\\em[ \t\n]/) {
$txt =~ s/\{\\em[ \t\n]/$EM/;
while (! $txt =~ /\}/) { $txt .= ; }
$txt =~ s/\}/$_EM/;
}
if ($txt =~ /\{\\tt[ \t\n]/) {
$txt =~ s/\{\\tt[ \t\n]/$TT/;
while (! $txt =~ /\}/) { $txt .= ; }
$txt =~ s/\}/$_TT/;
}
if ($txt =~ /\{\\bf[ \t\n]/) {
$txt =~ s/\{\\bf[ \t\n]/$BF/;
while (! $txt =~ /\}/) { $txt .= ; }
$txt =~ s/\}/$_BF/;
}
}
if ($html) { $txt =~ s/\~/ /g; } else { $txt =~ s/\~/ /g; }
while ($txt =~ /\\link\{[^}]+\}\{[^}]+\}/s) {
if ($html) {
$txt =~ s/\\link\{([^}]+)\}\{([^}]+)\}/$2<\/a>/s;
} else {
$txt =~ s/\\link\{([^}]+)\}\{([^}]+)\}/$2/s;
}
}
$txt =~ s/\\&/$AMP/g;
$txt =~ s/\\;/$SP/g;
$txt =~ s/\\ /$SP/g;
$txt =~ s/\\ldots/.../g;
$txt =~ s/\\\\/$NL/g;
$txt =~ s/\\min/m/g;
$txt =~ s/\\capo/ capo/g;
return $txt
}
### getContent(line): get what's between macro braces.
sub getContent {
my ($line) = @_; # input line
# Throw away everything up to the "{"
$line =~ s/^[^{]*\{//;
$line = deTeX($line);
# Suck in more lines if we haven't seen the closing brace
while ($line !~ /\}/) { $line .= ; $line = deTeX($line); }
# Throw away everything after the "}"
$line =~ s/\}[^}]*$//;
$line =~ s/\n$//;
return $line;
}