我想知道如何创建一个用于转置音乐和弦的javascript函数。
由于我不希望每个人都成为这里的音乐家,我会试着解释它在音乐理论中是如何运作的。我希望我不要忘记一些事情。如果是的话,请音乐家纠正我。
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
的javascript变量和转置值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';
答案 0 :(得分:9)
有点像这样的事情:
function transposeChord(chord, amount) {
var scale = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
return chord.replace(/[CDEFGAB]#?/g,
function(match) {
var i = (scale.indexOf(match) + amount) % scale.length;
return scale[ i < 0 ? i + scale.length : i ];
});
}
alert(transposeChord("Dm7/G", 2)); // gives "Em7/A"
alert(transposeChord("Fmaj9#11", -23)); // gives "F#maj9#11"
请注意,我投入了“F#maj9#11”示例,只是为了让您更多地考虑有关构成有效和弦名称的内容:您可能会发现一个不符合“#”尖锐符号一封信(在这种情况下,它属于“11”)。
显然,我的函数只能理解锐利,而不理解平面,并且不理解密钥,因此,例如,transposeChord("C/E", 1)
在真正应该是“C#/ E#”时会给出“C#/ F”。
答案 1 :(得分:2)
function transpose(chord, increment)
{
var cycle = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
var el = chord.charAt(0);
if(chord.length > 1 && chord.charAt(1) == '#')
{
el += "#";
}
var ind = cycle.indexOf(el);
var newInd = (ind + increment + cycle.length) % cycle.length;
var newChord = cycle[newInd];
return newChord + chord.substring(el.length);
}
我会让你弄清楚低音部分,因为它实际上只是调用该函数两次。
此外,您可以在功能之前为不支持indexOf
的旧浏览器添加代码here。
我在jsFiddle上添加了demo。
编辑:问题是负模数。只要负数不超过长度(例如,你不能向下移动100步),上述工作就会有效。
答案 2 :(得分:1)
使用对象定义键:
var keys = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"];
用正则表达式解析你的和弦:
var matches = /([A-G]#?)([^\/]*)(?:\/([A-G]#?))?/.exec(chord);
var key = matches[1];
var descriptor = matches[2];
var bass = matches[3];
做一些小数学来获得新密钥:
var newKey = keys[(keys.indexOf(key) + transpose) % keys.length];
var newBass = keys[(keys.indexOf(bass) + transpose) % keys.length];
将它们全部重新组合在一起:
var newChord = newKey + descriptor;
if (newBass) {
newChord += "/" + newBass;
}
return newChord;
答案 3 :(得分:1)
只是为了扩展nnnnnn的答案。我们可以使用他的代码并添加更多代码来实际使其与平面一起使用。
transposeChord("F#sus7/A#", 1)
> "Gsus7/B"
transposeChord("Bb", 1)
> "B"
... works like a charm
function transposeChord(chord, amount) {
var scale = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
var normalizeMap = {"Cb":"B", "Db":"C#", "Eb":"D#", "Fb":"E", "Gb":"F#", "Ab":"G#", "Bb":"A#", "E#":"F", "B#":"C"}
return chord.replace(/[CDEFGAB](b|#)?/g, function(match) {
var i = (scale.indexOf((normalizeMap[match] ? normalizeMap[match] : match)) + amount) % scale.length;
return scale[ i < 0 ? i + scale.length : i ];
})
}
<!-- Example Page -->
Chord: <input id="chord" type="text" value="C#" style="width:70px">
transposed by <input id="amount" type="number" value="0" style="width:30px">
= <input id="new-chord" type="text" style="width:70px">
<button onclick="document.getElementById('new-chord').value = transposeChord(document.getElementById('chord').value,parseInt(document.getElementById('amount').value))">Calculate</button>
答案 4 :(得分:0)
function transposechord(chord, amount){
var scale = ["C","Cb","C#","D","Db","D#","E","Eb","E#","F","Fb","F#","G","Gb","G#",
"A","Ab","A#","B","Bb","B#"];
var transp = ["Cb","C","C#","Bb","Cb","C","C","C#","D","Db","D","D#","C","Db","D",
"D","D#","E","Eb","E","F","D","Eb","E", "E","E#","F#", "E","F","F#",
"Eb","Fb","F","F","F#","G","Gb","G","G#","F","Gb","G", "G","G#","A",
"Ab","A","A#","G","Ab","A","A","A#","B","Bb","B","C","A","Bb","B",
"B","B#","C#"];
var subst = chord.match(/[^b#][#b]?/g);
for(var ax in subst){
if(scale.indexOf(subst[ax])!==-1){
if(amount>0){
for(ix=0;ix<amount;ix++){
var pos = scale.indexOf(subst[ax]);
var transpos = 3*pos-2+3;
subst[ax] = transp[transpos+1];
}
}
if(amount<0){
for(ix=0;ix>amount;ix--){
var pos = scale.indexOf(subst[ax]);
var transpos = 3*pos-2+3;
subst[ax] = transp[transpos-1];
}
}
}
}
chord=subst.join("");
}
chord = C / B,金额= 1: C#/ C 要么 和弦= Gm7,金额2: AM7
答案 5 :(得分:0)
好的,所以我现在已经考虑了这个问题,而且我有一个功能性答案。它采用标准的Western Scale音调(对不起北欧人)。
要真正转置和弦,您需要3条信息:1)CHORD名称,2)OLDKEY,以及3)NEWKEY。通过AMOUNT进行调制并不总是足够的(来自E的关键字是F#还是Gb?)。
基本上你需要保留两个距离 - CHORD和OLDKEY之间的距离,以及&#39;字母中的距离。在CHORD和OLDKEY之间 - 将CHORD映射到NEWKEY。
function transposeChord(chord, oldKey, newKey) {}
为了简化(?)这个,我已经预定了与该比例根相关的每个尺度和每个可能的音符(以及一些不可能的音符)。
var scales = {
// scale_form = [1-7, #1-7, b1-7, *1-7, bb1-7]
"CScale": ["C", "D", "E", "F", "G", "A", "B", "C#", "D#", "E#", "F#", "G#", "A#", "B#", "Cb", "Db", "Eb", "Fb", "Gb", "Ab", "Bb", "C*", "D*", "E*", "F*", "G*", "A*", "B*", "Cbb", "Dbb", "Ebb", "Fbb", "Gbb", "Abb", "Bbb"],
"GScale": ["G", "A", "B", "C", "D", "E", "F#", "G#", "A#", "B#", "C#", "D#", "E#", "F*", "Gb", "Ab", "Bb", "Cb", "Db", "Eb", "F", "G*", "A*", "B*", "C*", "D*", "E*", "F#*", "Gbb", "Abb", "Bbb", "Cbb", "Dbb", "Ebb", "Fb"],
"DScale": ["D", "E", "F#", "G", "A", "B", "C#", "D#", "E#", "F*", "G#", "A#", "B#", "C*", "Db", "Eb", "F", "Gb", "Ab", "Bb", "C", "D*", "E*", "F#*", "G*", "A*", "B*", "C#*", "Dbb", "Ebb", "Fb", "Gbb", "Abb", "Bbb", "Cb"],
"AScale": ["A", "B", "C#", "D", "E", "F#", "G#", "A#", "B#", "C*", "D#", "E#", "F*", "G*", "Ab", "Bb", "C", "Db", "Eb", "F", "G", "A*", "B*", "C#*", "D*", "E*", "F#*", "G#*", "Abb", "Bbb", "Cb", "Dbb", "Ebb", "Fb", "Gb"],
"EScale": ["E", "F#", "G#", "A", "B", "C#", "D#", "E#", "F*", "G*", "A#", "B#", "C*", "D*", "Eb", "F", "G", "Ab", "Bb", "C", "D", "E*", "F#*", "G#*", "A*", "B*", "C#*", "D#*", "Ebb", "Fb", "Gb", "Abb", "Bbb", "Cb", "Db"],
"BScale": ["B", "C#", "D#", "E", "F#", "G#", "A#", "B#", "C*", "D*", "E#", "F*", "G*", "A*", "Bb", "C", "D", "Eb", "F", "G", "A", "B*", "C#*", "D#*", "E*", "F#*", "G#*", "A#*", "Bbb", "Cb", "Db", "Ebb", "Fb", "Gb", "Ab"],
"F#Scale": ["F#", "G#", "A#", "B", "C#", "D#", "E#", "F*", "G*", "A*", "B#", "C*", "D*", "E*", "F", "G", "A", "Bb", "C", "D", "E", "F#*", "G#*", "A#*", "B*", "C#*", "D#*", "E#*", "Fb", "Gb", "Ab", "Bbb", "Cb", "Db", "Eb"],
"C#Scale": ["C#", "D#", "E#", "F#", "G#", "A#", "B#", "C*", "D*", "E*", "F*", "G*", "A*", "B*", "C", "D", "E", "F", "G", "A", "B", "C#*", "D#*", "E#*", "F#*", "G#*", "A#*", "B#*", "Cb", "Db", "Eb", "Fb", "Gb", "Ab", "Bb"],
"G#Scale": ["G#", "A#", "B#", "C#", "D#", "E#", "F*", "G*", "A*", "B*", "C*", "D*", "E*", "F#*", "G", "A", "B", "C", "D", "E", "F#", "G#*", "A#*", "B#*", "C#*", "D#*", "E#*", "F**", "Gb", "Ab", "Bb", "Cb", "Db", "Eb", "F"],
"D#Scale": ["D#", "E#", "F*", "G#", "A#", "B#", "C*", "D*", "E*", "F#*", "G*", "A*", "B*", "C#*", "D", "E", "F#", "G", "A", "B", "C#", "D#*", "E#*", "F**", "G#*", "A#*", "B#*", "C**", "Db", "Eb", "F", "Gb", "Ab", "Bb", "C"],
"A#Scale": ["A#", "B#", "C*", "D#", "E#", "F*", "G*", "A*", "B*", "C#*", "D*", "E*", "F#*", "G#*", "A", "B", "C#", "D", "E", "F#", "G#", "A#*", "B#*", "C**", "D#*", "E#*", "F**", "G**", "Ab", "Bb", "C", "D#", "Eb", "F", "G"],
// E#Scale:
// B#Scale:
"FScale": ["F", "G", "A", "Bb", "C", "D", "E", "F#", "G#", "A#", "B", "C#", "D#", "E#", "Fb", "Gb", "Ab", "Bbb", "Cb", "Db", "Eb", "F*", "G*", "A*", "B#", "C*", "D*", "E*", "Fbb", "Gbb", "Abb", "Bbbb", "Cbb", ,"Dbb", ,"Ebb"],
"BbScale": ["Bb", "C", "D", "Eb", "F", "G", "A", "B", "C#", "D#", "E", "F#", "G#", "A#", "Bbb", "Cb", "Db", "Ebb", "Fb", "Gb", "Ab", "B#", "C*", "D*", "E#", "F*", "G*", "A*", "Bbbb", "Cbb", "Dbb", "Ebbb", "Fbb", "Gbb", "Abb"],
"EbScale": ["Eb", "F", "G", "Ab", "Bb", "C", "D", "E", "F#", "G#", "A", "B", "C#", "D#", "Ebb", "Fb", "Gb", "Abb", "Bbb", "Cb", "Db", "E#", "F*", "G*", "A#", "B#", "C*", "D*", "Ebbb", "Fbb", "Gbb", "Abbb", "Bbbb", "Cbb", "Dbb"],
"AbScale": ["Ab", "Bb", "C", "Db", "Eb", "F", "G", "A", "B", "C#", "D", "E", "F#", "G#", "Abb", "Bbb", "Cb", "Dbb", "Ebb", "Fb", "Gb", "A#", "B#", "C*", "D#", "E#", "F*", "G*", "Abbb", "Bbbb", "Cbb", "Dbbb", "Ebbb", "Fbb", "Gbb"],
"DbScale": ["Db", "Eb", "F", "Gb", "Ab", "Bb", "C", "D", "E", "F#", "G", "A", "B", "C#", "Dbb", "Ebb", "Fb", "Gbb", "Abb", "Bbb", "Cb", "D#", "E#", "F*", "G#", "A#", "B#", "C*", "Dbbb", "Ebbb", "Fbb", "Gbbb", "Abbb", "Bbbb", "Cbb"],
"GbScale": ["Gb", "Ab", "Bb", "Cb", "Db", "Eb", "F", "G", "A", "B", "C", "D", "E", "F#", "Gbb", "Abb", "Bbb", "Cbb", "Dbb", "Ebb", "Fb", "G#", "A#", "B#", "C#", "D#", "E#", "F*", "Gbbb", "Abbb", "Bbbb", "Cbbb", "Dbbb", "Ebbb", "Fbb"]
// CbScale:
// FbScale:
// BbbFlatScale:
// ...
}
然后根据OLDKEY和NEWKEY分配比例尺:
var oldKeyScale = scales[key + "Scale"]
var newKeyScale = scales[newKey + "Scale"]
最后,一些正则表达式用NEWKEY标度中的相应位置找到并替换所有那些和弦根/平面/锐利/双平面等等。
var transposedChord
transposedChord = chord.replace(/(([CDEFGAB]#\*)|([CDEFGAB]#)|([CDEFGAB]b+)|([CDEFGAB]\**))/g, function(match) {
var i = oldKeyScale.indexOf(match)
return newKeyScale[i]
})
return transposedChord
这绝对是一台更好,更像“思考”的电脑&#39;这样做的方法,但这将采取
transposeChord("Am7/G", "C", "A#")
并返回
"F*m7/E#"
答案 6 :(得分:0)
所有这些解决方案都缺乏这样的事实:在转换音符后,需要根据琴键或和弦将其转换为锐利或平坦。
所以API必须是:
transpose(note, semitones, useSharps)
这是我的实施。它还处理多个#和b修饰符。
function transposeNote(note, semitones, useSharps) {
// parse root followed by modifiers (# and b)
const rx = /^([a-gA-G])([#b]*)$/;
const m = rx.exec(note);
if (!m) {
return null;
}
// convert note from 0 to 11 based off of A
let root;
switch (m[1].toUpperCase()) {
case "A":
root = 0;
break;
case "B":
root = 2;
break;
case "C":
root = 3;
break;
case "D":
root = 5;
break;
case "E":
root = 7;
break;
case "F":
root = 8;
break;
case "G":
root = 10;
break;
}
// modify root
let mods = m[2];
if (mods) {
for (var i = 0; i < mods.length; i++) {
if (mods.charAt(i) === "#") {
root++;
} else {
root--;
}
}
}
// transpose note
root = (root + semitones) % 12;
if (root < 0) {
root += 12
}
// convert back to a note
const sharps = [
"A",
"A#",
"B",
"C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#"
];
const flats = [
"A",
"Bb",
"B",
"C",
"Db",
"D",
"Eb",
"E",
"F",
"Gb",
"G",
"Ab"
];
const transposedNote = useSharps ? sharps[root] : flats[root];
return transposedNote;
}
function transposeChord(chord, semitones, useSharps) {
const rx = /^([a-gA-G][#b]*)\s*([^\/]*)(\/[a-gA-G][#b]*)?$/;
const m = rx.exec(chord);
if (!m) {
return null;
}
const root = transposeNote(m[1], semitones, useSharps);
const quality = m[2] || "";
let bass = m[3] || "";
if (bass.length > 0) {
bass = "/" + transposeNote(bass.substring(1), semitones, useSharps);
}
return root + quality + bass;
}
像这样使用
console.log(transposeChord("Cmin7/Eb", 3, false));
答案 7 :(得分:0)
我需要对吉他/尤克里里琴谱中的和弦线进行移调,并提出了以下功能,如果空间允许,可以保持间距,并重新使用 7/dim/sus4 类型信息:
function transposeChord( chord, amount ) {
const sharpnotes = ["A","A#","B","C","C#","D","D#","E","F","F#","G","G#"];
const flatnotes = ["A","Bb","B","C","Db","D","Eb","E","F","Gb","G","Ab"];
let rootChord = chord[0];
if(chord[1] === '#' || chord[1] == 'b') {
rootChord += chord[1];
}
amount = (amount % sharpnotes.length) || 1;
if(amount < 0) { amount += sharpnotes.length; }
for(let note=0; note < sharpnotes.length; ++note) {
if(rootChord === sharpnotes[note]) {
return( (sharpnotes[(note + amount) % sharpnotes.length]) + chord.substr(rootChord.length) );
}
if(rootChord === flatnotes[note]) {
return( (flatnotes[(note + amount) % flatnotes.length]) + chord.substr(rootChord.length) );
}
}
return ('???');
}
function transposeChordLine( line, amount ) {
amount = amount || 1;
let count = 0;
let newLine = '';
while(count < line.length) {
if(line[count] >= 'A' && line[count] <= 'G') {
let chord = line[count++];
while (count < line.length && line[count] !== ' ' && (line[count] < 'A' || line[count] > 'G')) {
chord += line[count++];
}
let newChord = transposeChord(chord, amount);
if(newChord.length < chord.length) { // pad if shorter
newChord += " ";
}
if(newChord.length > chord.length && count < line.length && (line[count] < 'A' || line[count] > 'G')) { // trim if there's space
count++;
}
newLine += newChord;
} else {
newLine += line[count++];
}
}
return(newLine);
}
所以(例如)
transposeChordLine(" C D7 Dm7 Gb7 ", 4)
输出
" E F#7 F#m7 Bb7 "
如果合适的话,在调换时,降号和升号被假定为保持 b/#。