While trying to brush up on my jazz improvisation, i had the great idea of making a program that would quiz me on my major scale modes! (similar to a game a friend and i would play) Improvements are encouraged greatly. Originally i planned to get it to quiz on Major and Melodic minor modes, but the program proved more complicated than i thought. Take a look:
#!/usr/bin/perl use strict; use Benchmark; my @modes = qw(Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locria +n); my @notes = qw(C D E F G A B); my @prefs = qw(# b # b # b b # b # b #); my @accidentals = ("#", "b", " "); my @scales; $scales[0] = [qw(0 2 4 5 7 9 11)]; $scales[1] = [qw(1 3 5 6 8 10 0)]; $scales[2] = [qw(2 4 6 7 9 11 1)]; $scales[3] = [qw(3 5 7 8 10 0 2)]; $scales[4] = [qw(4 6 8 9 11 1 3)]; $scales[5] = [qw(5 7 9 10 0 2 4)]; $scales[6] = [qw(6 8 10 11 1 3 5)]; $scales[7] = [qw(7 9 11 0 2 4 6)]; $scales[8] = [qw(8 10 0 1 3 5 7)]; $scales[9] = [qw(9 11 1 2 4 6 8)]; $scales[10] = [qw(10 0 2 3 5 7 9)]; $scales[11] = [qw(11 1 3 4 6 8 10)]; #how many notes ahead are each mode my %modevalues = qw(Ionian 0 Dorian 2 Phrygian 4 Lydian 5 Mixolydian 7 + Aeolian 9 Locrian 11); #get random scale (note and mode) my $mode = int(rand($#modes+1)); my $accidental = int(rand($#accidentals+1)); my $note = int(rand($#notes+1)); #find out correct number scale from random scale my $numb = let2numb($notes[$note] . $accidentals[$accidental]); #conve +rt music note to corresponding number my $scale = $numb - $modevalues{$modes[$mode]}; #find +out which original scale the mode came from $scale += 12 if ($scale < 0); #corre +ct negative offset my @reference = @{$scales[$scale]}; #get t +he correct original scale my @take = splice(@reference,0,$mode); #conve +rt the original scale to the right mode @reference = (@reference, @take); #put t +he correct scale all together #print out what scale we want print $notes[$note], $accidentals[$accidental], " ", $modes[$mode], ": +\n"; #get response, convert to number music scale, and time it my $t0 = new Benchmark; my $cool = <STDIN>; my $t1 = new Benchmark; my ($measure) = timestr(timediff($t1, $t0)) =~ /^\s?(\d+ wallclock sec +s).*/; my @response = split(/\s/, $cool); foreach (@response) { $_ = let2numb($_); } #get correct scale in letter form my $correct; foreach (@reference) { $correct .= numb2let($_,$prefs[$scale])." "; } #check if correct then exit if (join(' ', @reference) eq join(' ', @response)) { print "You're Right! $measure\n"; } else { print "Sorry $measure\nThe correct scale was: $correct\n"; } exit; #function to convert a regular form music note into an number sub let2numb() { my ($let, $acc) = split(//, lc(shift @_)); my %cnotes = ("c", "0", "d", "2", "e", "4", "f", "5", "g", "7", "a +", "9", "b", "11"); my $change; if ($acc eq "b") { $change = -1; } elsif ($acc eq "#") { $change = 1; } else { $change = 0; } my $return = $cnotes{$let} + $change; if ($return > 11) { $return -= 12; } elsif ($return < 0) { $return += 12; } return $return; } #function to convert number form of a music scale note into basic lett +er scale, takes sharp or flat preference. sub numb2let() { my $number = shift; my $pref = shift; my @cnotes = qw(C h D h E F h G h A h B); if ($cnotes[$number] eq "h") { return ($pref eq "#") ? $cnotes[$number-1].$pref : $cnotes[$nu +mber+1].$pref; } else { return $cnotes[$number]; } }

Replies are listed 'Best First'.
Re: Practice your major scale modes
by benn (Vicar) on Mar 06, 2003 at 02:53 UTC
    Great idea - thanks for a fun exercise :) In the spirit of TMTOWTDI, you might consider some of the techniques used below to achieve the same end. It's a little more 'Perlish', and demonstrates a few things - in particular, use of single hashes to replace whole 'lookup' subroutines.
    #!/usr/bin/perl use strict; use Benchmark; my @white_keys = (0,2,4,5,7,9,11); my @modes = qw(Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locria +n); my $note = int(rand(12)); my $mode = int(rand(7)); my $pref = qw(# b # b # b b # b # b #)[$note]; my %note2num = qw(C 0 C# 1 Db 1 D 2 D# 3 Eb 3 E 4 F 5 F# 6 Gb 6 G 7 G# + 8 Ab 8 A 9 A# 10 Bb 10 B 11); my %num2note = (($pref eq 'b') ? qw(0 C 1 Db 2 D 3 Eb 4 E 5 F 6 Gb 7 G 8 Ab 9 A 10 Bb 11 B) : qw(0 C 1 C# 2 D 3 D# 4 E 5 F 6 F# 7 G 8 G# 9 A 10 A# 11 B)); #rotate white_keys round to right mode foreach (0..$mode-1) {push @white_keys, shift @white_keys} my @scale = map {($_+$note-$white_keys[0])%12} @white_keys; print $num2note{$note}, " ", $modes[$mode], ":+\n"; my $t0 = new Benchmark; my $cool = <STDIN>; my $t1 = new Benchmark; my ($measure) = timestr(timediff($t1, $t0)) =~ /^\s?(\d+ wallclock sec ++s).*/; my $response = join(" ", map {$note2num{$_}} split(/\s/, $cool)); if ($response eq join (" ", @scale)) { print "You're Right! $measure\n"; } else { print "Sorry $measure\nThe correct scale was: ",join (",",map {$nu +m2note{$_}} @scale),"\n"; } exit;
    Of course, the musicologists will notice that the enharmonic equivalents need sorting out - strictly speaking, for instance, Bb Phrygian should be Bb,Cb etc., attempting to maintain the 'always change the letter' directive...I leave this as a further exercise :)

    Good luck with the impro - all the best people are jazzers. :)