Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to transpose music chords with PHP?

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?

like image 922
lvk Avatar asked Jun 06 '16 09:06

lvk


People also ask

How do you transpose music chords?

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.

How do you transpose to F?

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.


2 Answers

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.

like image 59
Sharky Avatar answered Sep 22 '22 23:09

Sharky


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

like image 27
bobble bubble Avatar answered Sep 22 '22 23:09

bobble bubble