use strict; use warnings; my %Tone =( A => 0, B => 2, C => 3, D => 5, E => 7, F => 8, G=>10); my @Notes; foreach my $natural ('A'..'G') { my $i = $Tone{$natural}; my $iFlat = $i ? $i-1 : 11; $Tone{$natural.'#'} = $i+1; $Tone{$natural.'b'} = $iFlat; $Notes[$iFlat][0]=$natural.'b'; $Notes[$i][0] = $natural; $Notes[$i][1] = $natural; $Notes[$i+1][1]=$natural.'#'; } #print "Tone: ". Dumper(\%Tone); #print "Notes: ". Dumper(\@Notes); sub transpose { my ($key, $aChord, $toKey) = @_; my @aTransposed; my $bMinor = $key =~ /m$/ ? 1 : 0; # <== $key= $key =~ /^(.*)m$/ ? $1 : $key; # <== $toKey= $toKey =~ /^(.*)m$/ ? $1 : $toKey; # <== my $iShift = $Tone{$toKey} - $Tone{$key}; my $iIndex = $bMinor # <== ? ($toKey =~ /^(?:C|D|F|G|.b)$/ ? 0 : 1) # minor key : ($toKey =~ /^(?:F|.b)$/ ? 0 : 1); # F uses flats in major push @aTransposed, $Notes[($Tone{$_}+$iShift) % 12][$iIndex] for @$aChord; return \@aTransposed; } # demo local $"='-'; print "Major modes\n"; for ('A'..'G','Eb','Bb', 'D', 'E') { print "C-E-G => $_ : @{transpose('C',[qw(C E G)],$_)}\n"; } print "Minor modes\n"; for ('A'..'G','Eb','Bb', 'D', 'E') { print "C-Eb-G => $_ : @{transpose('Cm',[qw(C Eb G)],qq{${_}m})}\n"; } # outputs Major modes C-E-G => A : A-C#-E C-E-G => B : B-D#-F# C-E-G => C : C-E-G C-E-G => D : D-F#-A C-E-G => E : E-G#-B C-E-G => F : F-A-C C-E-G => G : G-B-D C-E-G => Eb : Eb-G-Bb C-E-G => Bb : Bb-D-F C-E-G => D : D-F#-A C-E-G => E : E-G#-B Minor modes C-Eb-G => A : A-C-E C-Eb-G => B : B-D-F# C-Eb-G => C : C-Eb-G C-Eb-G => D : D-F-A C-Eb-G => E : E-G-B C-Eb-G => F : F-Ab-C C-Eb-G => G : G-Bb-D C-Eb-G => Eb : Eb-Gb-Bb C-Eb-G => Bb : Bb-Db-F C-Eb-G => D : D-F-A C-Eb-G => E : E-G-B