如何使用 JavaScript 转置音乐和弦
How do I transpose music chords using JavaScript?
我想知道如何创建一个 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';
像这样一点怎么样:
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"(。
而且,很明显,我的函数只理解锐音,不理解平音,并且不理解键,因此,例如,当它真正应该是"C#/E#"时,transposeChord("C/E", 1)
会给出"C#/F"。
只是为了扩展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>
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上放了一个演示。
编辑:问题出在负模量上。 只要负数不超过长度,上述方法就可以工作(例如,您不能向下转置 100 步(。
用对象定义键:
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( 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#"
所有这些解决方案都缺乏这样一个事实,即在转调音符后,需要根据键或和弦将其转换为尖音或平音。
因此,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));
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:上午7
我需要对吉他/尤克里里歌谱中的和弦线进行转置,并想出了以下函数,如果空间允许,可以保持间距,并重复使用 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/#。
- jQuery中是否内置了任何字符串格式化函数
- 主干集合重置和解析
- 如何覆盖锚点元素's href目标,并在我点击转到目标javascript时删除其他错误
- 无法将picadate日期选择器重置为指向当前月份
- 转置 JSON 数据
- 转置数组时收到“回调函数不支持此操作”错误
- 如何在 AppsScript 中拼合和转置数组
- 当按下后退键转到其他页面时,滚动条位置不会重置
- 如何转置/旋转多维数组
- 将列数据转置并复制到每一行
- 删除布局和用转置行绑定
- 如何使用 JavaScript 转置音乐和弦
- 我如何动态地填充一个转置HTML表与JQuery
- 当转置到PHP时,脚本不按预期工作
- 如何使用D3轴命令正确转置X轴和Y轴
- JS:将带有键的2D对象转换为转置的csv
- Javascript中的方形矩阵转置
- 使用undercore.js快速矩阵转置
- Asp.当通过浏览器后退按钮转到后退页面时,网络标签文本会重置
- 当跳转到下一页时,倒计时不应该被重置