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 )