11

我想知道如何创建一个用于转置音乐和弦的 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

我想这应该是全部,除非我忘记了什么。

所以基本上,假设你有一个名为的 javascript 变量chord和 transpose value 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';
4

8 回答 8

12

像这样的小东西怎么样:

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”。

于 2011-10-29T04:22:41.257 回答
3

只是为了扩展 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>

于 2017-08-31T11:01:25.867 回答
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.

我在jsFiddle上放了一个demo 。

编辑:问题出在负模数上。只要负数不超过长度(例如,您不能向下移调 100 步),上述方法就可以工作。

于 2011-10-29T03:40:17.453 回答
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;
于 2011-10-29T03:56:18.637 回答
1

好的,所以我现在已经考虑过这个问题,并且我有一个实用的答案。这是标准的西方音阶音调(对不起北欧人)。

要真正转调和弦,您需要 3 条信息:1) CHORD 名称,2) OLDKEY,和 3) NEWKEY。用一个 AMOUNT 来调制并不总是足够的(从 E 调的 UP-2 是 F# 还是 Gb?)。

基本上,在将 CHORD 映射到 NEWKEY 时,您需要保留两个距离——CHORD 和 OLDKEY 之间的音高距离,以及 CHORD 和 OLDKEY 之间的“字母”距离。

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

肯定有更好、更“像计算机一样思考”的方式来做到这一点,但这需要

transposeChord("Am7/G", "C", "A#")

并返回

"F*m7/E#"
于 2017-02-08T20:26:49.840 回答
1

所有这些解决方案都缺少这样一个事实,即在移调音符后,需要根据键或和弦将其转换为升音或降音。

所以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));
于 2018-06-05T13:59:31.737 回答
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("");
}

和弦 = C/B,数量 = 1:C#/C 或和弦 = Gm7,数量 2:Am7

于 2013-09-25T02:57:12.327 回答
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/#。

于 2021-07-30T20:31:39.927 回答