}
}
+### Chords ###
+
+package VS::Chord;
+
+# Internal representation of chords: <base-tone>:<type> (so C="0:", C#="1:", Dmi="2:mi" etc.)
+# but usually they are accompanied by position info after a second colon
+
+our (%t2n, @n2t);
+
+BEGIN {
+%t2n = (
+ "C" => 0,
+ "C#" => 1,
+ "Db" => 1,
+ "D" => 2,
+ "D#" => 3,
+ "Eb" => 3,
+ "E" => 4,
+ "Fb" => 4,
+ "E#" => 5,
+ "F" => 5,
+ "F#" => 6,
+ "Gb" => 6,
+ "G" => 7,
+ "G#" => 8,
+ "Ab" => 8,
+ "A" => 9,
+ "Bb" => 9,
+ "A#" => 10,
+ "Hb" => 10,
+ "B" => 10,
+ "H" => 11,
+ "B#" => 11,
+ "Cb" => 11,
+ "H#" => 0
+);
+@n2t = ( "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "B", "H" );
+}
+
+sub parse_line($) {
+ my $r = shift @_;
+ my @l = ();
+ my $pos = 0;
+ while (my ($spaces,$chord,$rest) = $r =~ /(\s*)(\S+)(.*)/) {
+ $pos += length $spaces;
+ if (my ($tone,$sh,$mod) = ($chord =~ /^([CDEFGABH](#|b|))(.*)$/)) {
+ my $k = $t2n{$tone};
+ push @l, "$k:$mod:$pos"
+ } else {
+ push @l, "0:?$chord:$pos";
+ }
+ $pos += length $chord;
+ $r = $rest;
+ }
+ return \@l;
+}
+
+sub synthesize_line($$) {
+ my ($l,$xpos) = @_;
+ my $pp = 0;
+ my $result = "";
+ for (my $i=0; $i<@$l; $i++) {
+ my ($tone,$mod,$pos) = split(/:/, $l->[$i]);
+ $tone = ($tone + $xpos) % 12;
+ my $chord = $n2t[$tone] . "$mod ";
+ if ($pp < $pos) {
+ $result .= " " x ($pos - $pp);
+ $pp = $pos;
+ }
+ $result .= $chord;
+ $pp += length $chord;
+ }
+ return $result;
+}
+
### Window Objects ###
package VS::Window;
$w->{"file"} = "";
$w->{"attrs"} = {};
$w->{"lines"} = ["", "", " The Virtual Songbook 0.0\n", " (c) 2003 Martin Mares <mj\@ucw.cz>"];
- $w->{"linetype"} = [0,0,0,0];
+ $w->{"chords"} = [0,0,0,0];
$w->{"n"} = 4;
$w->{"top"} = 0;
+ $w->{"chords_analysed"} = 0;
+ $w->{"current_xpose"} = 0;
+ $w->{"requested_xpose"} = 0;
return bless $w;
}
if ($w->{"file"} ne $f) {
$w->{"file"} = $f;
$w->{"xfile"} = $x;
+ $w->{"current_xpose"} = 0;
+ $w->{"chords_analysed"} = 0;
$f =~ s@^./@@;
$x =~ s@^./@@;
if (open X, $f) {
}
}
my @lines = ();
- my @types = ();
+ my @chords = ();
while (<X>) {
chomp;
- if (s/^!//) { push @types, 1; } else { push @types, 0; }
+ if (s/^!//) { push @chords, 1; } else { push @chords, 0; }
push @lines, $_;
}
close X;
$w->{"attrs"} = \%attrs;
$w->{"lines"} = \@lines;
- $w->{"linetypes"} = \@types;
+ $w->{"chords"} = \@chords;
+ $w->{"chordtable"} = [];
$w->{"top"} = 0;
$w->{"n"} = scalar @lines;
$w->redraw;
}
}
+sub transpose($) {
+ my $w = shift @_;
+ if (!$w->{"chords_analysed"}) {
+ for (my $i=0; $i<$w->{"n"}; $i++) {
+ if ($w->{"chords"}->[$i]) {
+ $w->{"chordtable"}->[$i] = VS::Chord::parse_line($w->{"lines"}->[$i]);
+ }
+ }
+ $w->{"chords_analysed"} = 1;
+ }
+ for (my $i=0; $i<$w->{"n"}; $i++) {
+ if ($w->{"chords"}->[$i]) {
+ $w->{"lines"}->[$i] = VS::Chord::synthesize_line($w->{"chordtable"}->[$i], $w->{"requested_xpose"});
+ }
+ }
+ $w->{"current_xpose"} = $w->{"requested_xpose"};
+}
+
sub redraw_line($$) {
my ($w,$i) = @_;
my $win = $w->{"win"};
sub redraw($) {
my $w = shift @_;
+ $w->transpose if $w->{"current_xpose"} != $w->{"requested_xpose"};
my $win = $w->{"win"};
my $top = $w->{"top"};
my $cnt = $w->{"n"} - $w->{"top"};
elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
elsif ($key eq KEY_END) { $w->go(1000000000); }
+ elsif ($key eq "+" || $key eq "=") { $w->{"requested_xpose"} = ($w->{"requested_xpose"}+1)%12; $status_window->redraw; $w->redraw; }
+ elsif ($key eq "-") { $w->{"requested_xpose"} = ($w->{"requested_xpose"}+11)%12; $status_window->redraw; $w->redraw; }
+ elsif ($key eq "0") { $w->{"requested_xpose"} = 0; $status_window->redraw; $w->redraw; }
else { $status_window->tell("Unknown key <$key>"); }
}
$win->bkgdset($attr_status);
$win->addstr(0, 0, $w->{"msg"});
$win->clrtoeol;
+ my $aux = "";
+ $aux = "T=" . $main_window->{"requested_xpose"} if ($main_window->{"requested_xpose"});
+ $win->addstr(0, $w->{"w"}-length $aux, $aux) if $aux ne "";
$win->refresh;
}