Music-Chord-Namer
view release on metacpan or search on metacpan
lib/Music/Chord/Namer.pm view on Meta::CPAN
elsif($_[0]) { # or as a string
@notes = split(/\s+/, $_[0]); # deal with it!
}
else {
return; # no notes??
}
my @notenumbers = (); # store the corresponding numbers here
foreach my $note(@notes){
die "Bad note \"$note\"!" unless defined $notevalues{$note};
my $notenumber = $notevalues{$note};
# make sure that it's a higher number than that of the note that preceeded it...
if(defined $notenumbers[$#notenumbers]){
while($notenumber < $notenumbers[$#notenumbers]){ $notenumber += 12; }
}
# add it to the list
push @notenumbers, $notenumber;
}
# Naming
# We need to make some decisions about what to call it a chord...
# Lets assume we know no better and we're going to try every possible chord
# and see which name is the shortest!
# Lets go through every probable root note first... one of the two bass notes must
# be the 1, m3, 3, 5 or m7 of the chord. No cheating!
# We can then work out the names of these 10 chords...
# 1) The bass note is 1
# 2) The bass note is m3
# 3) The bass note is 3
# 4) The bass note is 5
# 5) The bass note is m7
# 6) The bass note is separate, the next note is 1
# 7) The bass note is separate, the next note is m3
# 8) The bass note is separate, the next note is 3
# 9) The bass note is separate, the next note is 5
# 10) The bass note is separate, the next note is m7
# notes set to bass note being a certain chord member
my @inversions = ();
# name, notes, split, comment
# the name depends on what we're saying the bass note is... it could be the root, minor or major 3rd
# 5th or minor 7th.
push @inversions,
{name => $value2note[($notevalues{$notes[0]}) % 12], notes => [map { $_ - $notenumbers[0] } @notenumbers], split => '', comment => 'bass 1'},
{name => $value2note[($notevalues{$notes[0]} - 3) % 12], notes => [map { $_ - $notenumbers[0] + 3 } @notenumbers], split => $notes[0], comment => 'bass m3'},
{name => $value2note[($notevalues{$notes[0]} - 4) % 12], notes => [map { $_ - $notenumbers[0] + 4 } @notenumbers], split => $notes[0], comment => 'bass 3'},
{name => $value2note[($notevalues{$notes[0]} + 5) % 12], notes => [map { $_ - $notenumbers[0] - 5 } @notenumbers], split => $notes[0], comment => 'bass 5'},
{name => $value2note[($notevalues{$notes[0]} + 2) % 12], notes => [map { $_ - $notenumbers[0] - 2 } @notenumbers], split => $notes[0], comment => 'bass m7'};
shift(@notenumbers); # get rid of bass note, incase it's a split!
# ... and do it all again!
push @inversions,
{name => $value2note[($notevalues{$notes[0]}) % 12], notes => [map { $_ - $notenumbers[0] } @notenumbers], split => $notes[0], comment => 'split 1'},
{name => $value2note[($notevalues{$notes[0]} - 3) % 12], notes => [map { $_ - $notenumbers[0] + 3 } @notenumbers], split => $notes[0], comment => 'split m3'},
{name => $value2note[($notevalues{$notes[0]} - 4) % 12], notes => [map { $_ - $notenumbers[0] + 4 } @notenumbers], split => $notes[0], comment => 'split 3'},
{name => $value2note[($notevalues{$notes[0]} + 5) % 12], notes => [map { $_ - $notenumbers[0] - 5 } @notenumbers], split => $notes[0], comment => 'split 5'},
{name => $value2note[($notevalues{$notes[0]} + 2) % 12], notes => [map { $_ - $notenumbers[0] - 2 } @notenumbers], split => $notes[0], comment => 'split m7'};
# ok, here's how it works:
# There are these notes:
# 0 1 2 3 4 5 6 7 8 9 10 11
# 1 b2 2 m3 3 4 b5 5 a5 6 m7 7
# 12 13 14 15 16 17 18 19 20 21 22 23
# 8 b9 9 m10 10 11 b12 12 b13 13 m14 14
# these are the names of the notes we could have in the chord
my @valuenames = qw(
1 b2 2 m3 3 4 b5 5 a5 6 m7 7
8 b9 9 m10 10 11 b12 12 b13 13 m14 14);
# Chord folding
# We'll fold our chord into this structure... whichever note is the root can get
# set as 0. Any note below it can have 12 added to it until it's above 0. Any
# note above 23 can have 12 taken from it until it is 23 or less.
# fold each of our inversions of the chord!
foreach my $hash(@inversions){
my $array = $hash->{notes} ;
for(my $i = 0; $i< @$array; $i++){
while($array->[$i] > 23){ $array->[$i] -= 12; } # anything over 23, drop it an octave
while($array->[$i] < 0){ $array->[$i] += 12; } # anything under 0, raise it an octave
}
}
# we'll put the chord names in here:
my @NAMES = ();
# now we need to turn them into hashes!!! We'll do all the rest for each hash
foreach my $hash(@inversions){
# skip it if the name is the same as the split... this could happen in the "next" inversions... there's
# no point to it because it will already have been covered by "bass 1"
next if $hash->{'split'} && $notevalues{$hash->{'split'}} == $notevalues{$hash->{name}};
# the notes...
my $array = $hash->{notes} ;
%NOTES = (); # global, setting it up before calling isset, etc
$NAME = $hash->{name}; # global
for(my $i = 0; $i< @$array; $i++){
$NOTES{$array->[$i]} = 1; # set up the existence of the notes in the hash
}
# Duplicate notes
# If any note from 0-11 is set then the corresponding note from 12-23 can be
# un-set.
foreach (0..11){ # remove notes from upper octave that are already in lower one!
isset($_) and unset($_+12)
}
# Shifting 1, 3, 5, 7
# If none of the 1sts, 3rds, 5ths or 7ths are set in the lower octave then any
# corresponding notes in the upper octave can be shifted down.
( run in 1.555 second using v1.01-cache-2.11-cpan-d7f47b0818f )