通过和弦确定歌曲的关键

我怎样才能通过知道歌曲的和弦序列以编程方式find歌曲的关键?
我问过一些人如何确定一首歌曲的关键,他们都说他们是通过“耳朵”,“反复试验”,以及是否和弦来解决一首歌曲。对于一般的音乐家来说可能是好的,但作为一个程序员,真的不是我正在寻找的答案。

所以我开始寻找音乐相关的图书馆,看看是否有其他人为此写了一个algorithm。 但是,尽pipe我在GitHub上find了一个名为'tonal'的非常大的库: https : //danigb.github.io/tonal/api/index.html我找不到可以接受和弦的方法并返回键。

我select的语言将是JavaScript(NodeJs),但我不一定在寻找JavaScript的答案。 伪代码或可以毫不费力地翻译成代码的解释完全可以。

正如你们中有些人提到的那样,歌曲中的关键字可以改变。 我不确定是否可以足够可靠地检测到密钥更改。 所以,现在我们只是说,我正在寻找一个algorithm,对给定和弦序列的关键字进行很好的近似。

一首特定琴键中的和弦主要是琴键的音阶。 我想通过比较列出的和弦中主要的偶然事件和密钥的关键特征,你可以统计出一个很好的近似值(如果有足够的数据)。

请参阅https://en.wikipedia.org/wiki/Circle_of_fifths

当然,任何一把钥匙中的歌曲都可能会有不符合音阶的意外情况,所以这可能是一个统计上的近似值。 但是在几个酒吧里,如果加上偶然事​​件并过滤出所有经常出现的事件,那么您可以匹配一个密钥签名。

附录:正如Jonas w正确指出的那样,您可能能够得到签名,但是您不可能确定它是否是主要或次要关键。

您也可以使用每个“受支持”音阶的键保留一个结构,并使用与该音阶匹配的和弦作为值。

给出一个和弦进展,然后你可以开始根据你的结构做一个关键的清单。

通过多场比赛,你可以尝试做出有根据的猜测。 例如,将其他“权重”添加到与根音符相匹配的任何音阶。

给定一系列像这样的音调:

var tones = ["G","Fis","D"]; 

我们可以首先生成一组独特的音调:

 tones = [...new Set(tones)]; 

然后我们可以检查#和bs的出现:

 var sharps = ["C","G","D","A","E","H","Fis"][["Fis","Cis","Gis","Dis","Ais","Eis"].filter(tone=>tones.includes(tone)).length]; 

然后用bs做同样的事情,并得到结果:

 var key = sharps === "C" ? bs:sharps; 

但是,你仍然不知道它的主要次要的 ,许多组件不关心上层规则(和改变之间的关键)… … –

您可以使用螺旋arrays,Elaine Chew创build的三维音调模型,它有一个关键的检测algorithm。

Chuan,Ching-Hua和Elaine Chew。 “ 使用螺旋arraysCEGalgorithm的复调audio键发现 ”。 多媒体和博览会,2005年ICME 2005年IEEE国际会议。 IEEE,2005。

我最近的张力模型(在这里的.jar文件中 )也可以输出基于螺旋arrays的关键(除了张力测量之外)。 它可以采取一个musicXML文件或文本文件作为input,只需要在你的作品中的每个“时间窗口”的音高名称列表。

Herremans D.,Chew E .. 2016. 张力带:量化和可视化的音调张力 。 第二届音乐符号和performance技术国际会议(TENOR)。 2:8-18。

这是我想出来的。 现代的JS仍然是新的,所以对map()的混乱和糟糕的使用表示歉意。

我查看了音调库的内部,它有一个函数scales.detect(),但是它不需要每个音符都存在。 相反,我用它作为灵感,并将其演变为一个简单的音符列表,并在所有转换中将其作为所有可能音阶的一个子集进行检查。

 const _ = require('lodash'); const chord = require('tonal-chord'); const note = require('tonal-note'); const pcset = require('tonal-pcset'); const dictionary = require('tonal-dictionary'); const SCALES = require('tonal-scale/scales.json'); const dict = dictionary.dictionary(SCALES, function (str) { return str.split(' '); }); //dict is a dictionary of scales defined as intervals //notes is a string of tonal notes eg 'cd eb' //onlyMajorMinor if true restricts to the most common scales as the tonal dict has many rare ones function keyDetect(dict, notes, onlyMajorMinor) { //create an array of pairs of chromas (see tonal docs) and scale names var chromaArray = dict.keys(false).map(function(e) { return [pcset.chroma(dict.get(e)), e]; }); //filter only Major/Minor if requested if (onlyMajorMinor) { chromaArray = chromaArray.filter(function (e) { return e[1] === 'major' || e[1] === 'harmonic minor'; }); } //sets is an array of pitch classes transposed into every possibility with equivalent intervals var sets = pcset.modes(notes, false); //this block, for each scale, checks if any of 'sets' is a subset of any scale return chromaArray.reduce(function(acc, keyChroma) { sets.map(function(set, i) { if (pcset.isSubset(keyChroma[0], set)) { //the midi bit is a bit of a hack, i couldnt find how to turn an int from 0-11 into the repective note name. so i used the midi number where 60 is middle c //since the index corresponds to the transposition from 0-11 where c=0, it gives the tonic note of the key acc.push(note.pc(note.fromMidi(60+i)) + ' ' + keyChroma[1]); } }); return acc; }, []); } const p1 = [ chord.get('m','Bb'), chord.get('m', 'C'), chord.get('M', 'Eb') ]; const p2 = [ chord.get('M','F#'), chord.get('dim', 'B#'), chord.get('M', 'G#') ]; const p3 = [ chord.get('M','C'), chord.get('M','F') ]; const progressions = [ p1, p2, p3 ]; //turn the progression into a flat string of notes seperated by spaces const notes = progressions.map(function(e) { return _.chain(e).flatten().uniq().value(); }); const possibleKeys = notes.map(function(e) { return keyDetect(dict, e, true); }); console.log(possibleKeys); //[ [ 'Ab major' ], [ 'Db major' ], [ 'C major', 'F major' ] ] 

一些缺点:
– 不要给你想要的等音音符。 在p2中,更正确的答案是C#主要的,但是这可以通过以某种方式检查原始进程来解决。
– 不会处理出现在和弦中的“装饰”,这可能出现在stream行歌曲中,例如。 CMaj7 FMaj7 GMaj7而不是CF G.不知道这是多么普遍,不是我想的太多。

Interesting Posts