如何用PHP转换音乐和弦



我想知道如何在PHP中创建一个函数,用于转换一些音乐和弦。

我将尝试解释它在音乐理论中是如何运作的。我希望我没有忘记什么。如果有一些错误,请帮我改正。


1.简单和弦

简单的和弦几乎和字母表一样简单,它是这样的:

C、C#、D、D#、E、F、F#、G、G#、A、A#B

从B开始,它再次循环到C。因此,如果原始和弦是E,并且我们想转置+1,则得到的和弦是F。如果我们转置+4,得到的和弦是G#

2.扩展和弦

它们的工作原理几乎像简单的和弦,但包含了更多的字符,在转换时可以安全地忽略这些字符。例如:

Cmi,C#7,Dsus7,Emi,Fsus4,F#mi,G。。。

所以,和简单和弦一样,如果我们转置Dsus7+3=Fsus7

3.非根低音

当低音演奏不同于和弦根音的音调时,就会出现问题。这是由和弦后的斜线标记的,也需要转置。示例:

C/G、Dmi/A、F#sus7/A#

与示例1和2一样,一切都是一样的,但斜线后面的部分也需要转置,因此:

C/G+5=F/C

F#sus7/A#+1=Gsus7/B


因此,基本上,假设您有一个名为chord的PHP变量和转置值transpose。什么代码可以转换和弦?

示例:

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';

我在这里发现了一个关于StackOverflow的问题。他们讨论和弦进行的算法。


如何使用PHP通过增加或减少半音来转换音乐和弦?

快速解决方案:

<?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

代码还有很大的改进空间,我只是试着把它编码得易于理解。

这里是我对preg_replace_callback的正则表达式想法(使用匿名函数需要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);
}

在eval.in演示;(要测试正则表达式模式[A-G]#?,请参见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

好的,所以有一些事情需要处理。

首先,您希望能够在数组中循环。这很简单:使用模数运算符,在php中它是%

function transpose($chord, $increment) {
$map = array('A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#');
// Get the index of the given chord
$index = array_search($chord, $map);
if($index === false)
return false;
// Get the transposed index and chord
$transpose_index = ($index + $increment) % count($map);
if($transpose_index < 0)
$transpose_index += count($map);
return $map[$transpose_index];
}

其次,你希望能够去掉对你来说重要的实际和弦。您可以使用正则表达式(RegEx):

function transposeFull($chords, $increment) {
// This RegEx looks for one character (optionally followed by a sharp).
//     .#?
// This RegEx looks for an optional series of characters which are not /
//     [^/]*
// Put them together to get a RegEx that looks for an expanded chord
//     (.#?)([^/]*)
// Then, do it again, but add a / first, and make it optional.
//     (/(.#?)([^/]*))?
$regex = '%(.#?)([^/]*)(/(.#?)([^/]*))?%';
// Note that the () allow us to pull out the matches.
// $matches[0] is always the full thing.
// $matches[i] is the ith match 
//   (so $matches[3] is the whole optional second chord; which is not useful)
$matches = array();
preg_match($regex, $chords, $matches);
// Then, we get any parts that were matched and transpose them.
$chord1 = (count($matches) >= 2) ? transpose($matches[1], $increment) : false;
$expanded1 = (count($matches) >= 2) ? $matches[2] : '';
$chord2 = (count($matches) >= 5) ? transpose($matches[4], $increment) : false;
$expanded2 = (count($matches) >= 6) ? $matches[5] : '';
// Finally, put it back together.
$chords = '';
if($chord1 !== false)
$chords .= $chord1.$expanded1;
if($chord2 !== false)
$chords .= '/'.$chord2.$expanded2;
return $chords;
}

相关内容

  • 没有找到相关文章

最新更新