I am writing a PHP application that represents the six strings of the guitar as a series of ranges. In this way, I can add, or subtract to a set of numbers to systematically change an archetypical structure I define.
The ranges are:
//E: 1-15, A: 26-40, D: 51-65, G: 76-90, b: 101-115, e: 126-140
I am having trouble with the code below.
The function
//the key of the key value pair represents its position on the guitar, and the value
//represents a theoretical function. If the inversion is "0", do nothing. If the
//inversion is "1", all notes that have the value of "r" should have their key
//incremented by 4 and their value changed to '3'.
//example: 1=>"r",28=>"5",52=>"7",77=>"3"
function invChange($pattern, $inversion) {
if ($inversion == 1) {
foreach ($pattern as $position => $function) {
if ($function == 'r' ) { $position += 4; $junction = '3'; }
if ($function == '3' ) { $position += 3; $junction = '5'; }
if ($function == '5' ) { $position += 4; $junction = '7'; }
if ($function == '7' ) { $position += 1; $junction = 'r'; }
$modScale[$position] = $junction;
}
}
if ($inversion == 2) {
foreach ($pattern as $position => $function) {
if ($function == 'r' ) { $position += 7; $junction = '5';}
if ($function == '3' ) { $position += 7; $junction = '7';}
if ($function == '5' ) { $position += 5; $junction = 'r';}
if ($function == '7' ) { $position += 5; $junction = '3';}
$modScale[$position] = $junction;
}
}
if ($inversion == 3) {
foreach ($pattern as $position => $function) {
if ($function == 'r' ) { $position += 11; $junction = '7';}
if ($function == '3' ) { $position += 8; $junction = 'r';}
if ($function == '5' ) { $position += 9; $junction = '3';}
if ($function == '7' ) { $position += 8; $junction = '5';}
$modScale[$position] = $junction;
}
}
return $modScale;
}
As you can see, this is quite repetitive. Just looking at this code makes me think there has got to be a better way. I think what I need is to use an array in an infinite linear fashion:
array("root" => 4, "third" => 3, "fifth" => 4, "seventh" => 1);
Now I need to take any of a predefined $note => $offset pair and jump them across this array by 1, 2, or 3 jumps. For instance, if it starts as a root, and it makes one jump, I need to add 4 to turn it into a "third", and change its value to 'third'. But if it starts as a root and makes two jumps, I need to add 4, add 3, and then change its value to "fifth".
Well.. you can do it with math. Even more, with restrictions you've provided, it can be done with, actually, one function. Quadratic function.
All you need to do - is somehow create a math function, which will take your inversion as an argument, and return a certain position by a given "function" (how it's called inside your PHP function). Since you have three values for "inversion", you can done this with quadratic function:
f(x) = ax2 + bx + c
here, you'll need to find coefficients a
, b
, and c
by given x
values ($inversion
for your case) and function values (values for "position" in each of your "inversion" if
blocks).
However, you have four different position switches for each of your "function" (that's inside foreach
block) - that's why, actually, you'll have to deal with four quadratic functions.
Now, about junctions. I'm not sure what it is in logical sense, but, certainly, the dependence is obvious: given the array of all possible junctions, value for certain "function" is just junction at position "current" + "inversion". So, if we have junctions array ['r', '3', '5', '7']
, then, if inversion is 1
and current is 3
, then result would be 5
. If inversion is 2
, then result would be 7
e t.c.
Here we go:
function invChangeN($pattern, $inversion)
{
$junc = ['r', '3', '5', '7'];
$pos = [
'r' => [ 1, 7, 14],
'3' => [-3, 5, 14],
'5' => [ 3, 5, 10],
'7' => [-1, 7, 10]
];
$inversion -= 2;
$modScale = [];
foreach ($pattern as $p => $f)
{
$p += ($pos[$f][0]*pow($inversion,2) + $pos[$f][1]*$inversion + $pos[$f][2])/2;
$j = $junc[(array_search($f, $junc)+$inversion+2)%count($junc)];
$modScale[$p] = $j;
}
return $modScale;
}
Little about, why are we doing $inversion -= 2
. It's just about - to transpose three points from 1
, 2
and 3
(original inversion values) to -1
, 0
and 1
- with that it's much easier to calculate our a
, b
and c
.
$pos
contains arrays of coefficients for quadratic function for each "function" (that is $f
inside loop). Due to resolving linear equations system, they all are "half-integers" (so, have a view W/2
, so denominator of fraction is 2
). That's why I've just multiplied them all to 2
and added division by 2 to position calculation.
Tests
I've done tests of your function and this new "math" analog - with successful results, which are, of course for inversion as 1
, 2
and 3
, like:
//derived from old function:
array(4) {
[8]=>
string(1) "5"
[33]=>
string(1) "r"
[57]=>
string(1) "3"
[84]=>
string(1) "7"
}
//derived from new function:
array(4) {
[8]=>
string(1) "5"
[33]=>
string(1) "r"
[57]=>
string(1) "3"
[84]=>
string(1) "7"
}
Results above are for pattern [1=>"r",28=>"5",52=>"7",77=>"3"]
and inversion 2