I was wondering how would one create a function, in PHP, which is used for transposing some music chords.
I will try to explain how it works in music theory. I hope I don't forget something. If there are some mistakes, please help me to correct it.
1. The simple chords.
The simple chords are almost as simple as an alphabet and it goes like this:
C, C#, D, D#, E, F, F#, G, G#, A, A# B
From B it loops all over again to C. Therefore, If the original chord is E
and we want to transpose +1, the resulting chord is F
. If we transpose +4, the resulting chord is G#
.
2. Expanded chords.
They work almost like the simple chords, but contain a few more characters, which can safely be ignored when transposing. For example:
Cmi, C#7, Dsus7, Emi, Fsus4, F#mi, G ...
So again, as with the simple chords, if we transpose Dsus7
+ 3 = Fsus7
3. Non-root bass tone.
A problem arises when the bass plays a different tone than the chord root tone. This is marked by a slash after the chord and also needs to be transposed. Examples:
C/G, Dmi/A, F#sus7/A#
As with examples 1 and 2, everything is the same, but the part after the slash needs transpose too, therefore:
C/G
+ 5 = F/C
F#sus7/A#
+ 1 = Gsus7/B
So basically, imagine you have a PHP variable called chord
and the transpose value transpose
. What code would transpose the chord?
Examples:
var chord = 'F#sus7/C#';
var transpose = 3; // remember this value also may be negative, like "-4"
... code here ...
var result; // expected result = 'Asus7/E';
I have found an existed question on StackOverflow, at here. They talk about algorithm for chord-progressions.
How do I transpose music chords with PHP, by increasing or decreasing by semitones?
You can: Use a capo and utilize the same chord shapes in a new key. Use music theory to determine the chords of the new key using the Circle of Fifths. Use barre chord shapes to move the chords around and into the new key.
For example say that you want to transpose into the key of F major. Locate the C major line and place your finger on the original chord letter for the first chord (e.g. D min7), then trace down the column to the new key centre line F major and you should land on the new chord letter G. Do this for all the other chords.
A quick solution:
<?php
// produces the expected result
echo transpose("F#sus7/C#",3);
function transpose($chord,$transpose)
{
// the chords
$chords = array("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B");
$result = "";
// get root tone
$root_arr = explode("/",$chord);
$root = strtoupper($root_arr[0]);
// the chord is the first character and a # if there is one
$root = $root[0].((strpos($root, "#") !== false)?"#":"");
// get any extra info
$root_extra_info = str_replace("#","",substr($root_arr[0],1)); // assuming that extra info does not have any #
// find the index on chords array
$root_index = array_search($root,$chords);
// transpose the values and modulo by 12 so we always point to existing indexes in our array
$root_transpose_index = floor(($root_index + $transpose) % 12);
if ($root_transpose_index < 0)
{
$root_transpose_index += 12;
}
$result.= $chords[$root_transpose_index].$root_extra_info;
if(count($root_arr)>1)
{
// get the non root tone
$non_root = $root_arr[1];
// the chord is the first character and a # if there is one
$non_root = strtoupper($non_root[0]).((strpos($non_root, "#") !== false)?"#":"");
// get any extra info
$non_root_extra_info = str_replace("#","",substr($root_arr[1],1)); // assuming that extra info does not have any #
// find the index on chords array
$non_root_index = array_search($non_root,$chords);
// transpose the values and modulo by 12 so we always point to existing indexes in our array
$non_root_transpose_index = floor(($non_root_index + $transpose) % 12);
if ($non_root_transpose_index < 0)
{
$non_root_transpose_index += 12;
}
$result.= "/".$chords[$non_root_transpose_index].$non_root_extra_info;
}
return $result;
}
https://3v4l.org/Cd9Pg
lots of room for improvement in code, i just tried to code it to be easy to understand.
Here my regex idea with preg_replace_callback
(use of anonymous function requires PHP 5.3).
function transpose($str, $t=0)
{
// the chords
$chords = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];
// set transpose, return if none
$t = (int)$t % 12 + 12; if($t % 12 == 0) return $str;
// regex with callback
return preg_replace_callback('~[A-G]#?~', function($m) use (&$chords, &$t) {
return $chords[(array_search($m[0], $chords) + $t) % 12];
}, $str);
}
Demo at eval.in (for testing the regex pattern [A-G]#?
see regex101)
echo transpose("Cmi, C#7, Dsus7, Emi, Fsus4, F#mi, G C/G, Dmi/A, F#sus7/A#", -3);
Ami, A#7, Bsus7, C#mi, Dsus4, D#mi, E A/E, Bmi/F#, D#sus7/G
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With