本文作者是一位多年自学成才的吉他手,但对西方乐理一无所知,因此决定编写一些代码来搞懂它。本文用了大约200行Python代码来帮助我们理解西方音乐理论的基础知识。我们将首先查看西方音乐理论中的音符,使用它们来导出给定键中的半音阶,然后将其与音程公式结合起来以导出常见的音阶和和弦。最后,我们将研究模式,这些模式是从通用音阶衍生出来的整个音阶集合,可以用来唤起比主要音阶和次要音阶所提供的悲喜二分法更微妙的情绪和气氛。西方音乐的音乐字母由字母A到G组成,它们代表不同的音高。alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
但是,这些音符的频率分布不均。为了使音高之间的间距更均匀,我们有以下十二个音符:notes_basic = [
['A'],
['A#', 'Bb'],
['B'],
['C'],
['C#', 'Db'],
['D'],
['D#', 'Eb'],
['E'],
['F'],
['F#', 'Gb'],
['G'],
['G#', 'Ab'],
]
这里有四点要注意:首先,每个音符相距半步或半音,其次,它的表示方式是通过可选的尾随符号(称为偶然符号)来表示半步升高(尖锐, ♯)或将基调降低半个音阶(平坦,♭),第三,上述音符只是循环并重新开始,但是音阶较高。最后,您会注意到,其中一些音符由包含多个名称的列表表示:这些是谐音等效词,这是一种奇特的说法,即同一音符可以具有不同的“拼写”。因此,例如,音符在A上方半步是A♯,但也可以认为是B下方半步的音符,因此可以称为B♭。由于历史原因,音符 B/C 和 E/F 之间没有尖锐或平坦的部分。我们需要这些等效词的重要原因是,当我们开始推导通用音阶(大,小和模式)时,连续音符必须以连续字母开头。具有不同字母的谐音等效词使我们能够正确得出这些音阶。实际上,对于某些键,上述谐音音符是不够的。为了满足“连续字母的不同字母规则”,我们最终不得不使用双尖锐和双扁平方式来提高或降低音符整步。这些音阶通常具有不需要这些双重偶然性的等效词,但是为了完整起见,我们可以通过重写我们的注释来包括所有可能的谐音等效词,如下所示:notes = [
['B#', 'C', 'Dbb'],
['B##', 'C#', 'Db'],
['C##', 'D', 'Ebb'],
['D#', 'Eb', 'Fbb'],
['D##', 'E', 'Fb'],
['E#', 'F', 'Gbb'],
['E##', 'F#', 'Gb'],
['F##', 'G', 'Abb'],
['G#', 'Ab'],
['G##', 'A', 'Bbb'],
['A#', 'Bb', 'Cbb'],
['A##', 'B', 'Cb'],
]
半音阶是最简单的音阶,它仅由给定音调(音阶中的主要音符,也称为音调)的八度之间的所有(十二个)半音组成。我们可以很容易地为任何给定的键生成一个半音阶:(i)在我们的笔记列表中找到该音符的索引,(ii)向左旋转音符列表多次。让我们编写一个简单的函数来在此列表中查找特定的音符:def find_note_index(scale, search_note):
''' Given a scale, find the index of a particular note '''
for index, note in enumerate(scale):
# Deal with situations where we have a list of enharmonic
# equivalents, as well as just a single note as and str.
if type(note) == list:
if search_note in note:
return index
elif type(note) == str:
if search_note == note:
return index
find_note_index()
函数将一系列音符(scale
)和要搜索的音符(search_note
)作为参数,并通过简单的线性搜索返回索引。我们在循环中处理两种情况:(i)提供的音阶由单个音符组成(例如上面的字母列表),或(ii)由音阶等效音列表组成(例如上面的note
或notes_basic
列表)。下面是该函数对于这两种情况的示例:>>> find_note_index(notes, 'A') # notes is a list of lists
9
>>> find_note_index(alphabet, 'A') # alphabet is a list of notes
0
现在,我们可以编写一个将给定scale
旋转n
步的函数:def rotate(scale, n):
''' Left-rotate a scale by n positions. '''
return scale[n:] + scale[:n]
我们在位置n
处切割scale
列表,并交换这两半。这是将alphabet
列表旋转三个位置(将音符D放在前面)的示例:>>> alphabet
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> rotate(alphabet, 3)
['D', 'E', 'F', 'G', 'A', 'B', 'C']
现在,我们终于可以编写我们的colour()
函数了,该函数通过旋转notes
数组为给定的键生成一个半音音阶:def chromatic(key):
''' Generate a chromatic scale in a given key. '''
# Figure out how much to rotate the notes list by and return
# the rotated version.
num_rotations = find_note_index(notes, key)
return rotate(notes, num_rotations)
上面的colour()
函数在注释列表中找到所提供键的索引(使用我们的find_note_index()
函数),然后将其旋转该量以使其移到最前面(使用我们的rotate()
函数)。这是生成D
半音音阶的示例:>>> import pprint
>>> pprint.pprint(chromatic('D'))
[['C##', 'D', 'Ebb'],
['D#', 'Eb', 'Fbb'],
['D##', 'E', 'Fb'],
['E#', 'F', 'Gbb'],
['E##', 'F#', 'Gb'],
['F##', 'G', 'Abb'],
['G#', 'Ab'],
['G##', 'A', 'Bbb'],
['A#', 'Bb', 'Cbb'],
['A##', 'B', 'Cb'],
['B#', 'C', 'Dbb'],
['B##', 'C#', 'Db']]
对于半音音阶,通常在上升时使用锐利度,而在下降时使用平坦度。但是,就目前而言,我们将谐音等值保持不变。我们将看到如何选择正确的音节以供以后使用。因此,可以基于半音阶音符与根音的相对距离来命名。以下是每个音节的标准名称,其顺序与音节列表中的索引相同:intervals = [
['P1', 'd2'], # Perfect unison Diminished second
['m2', 'A1'], # Minor second Augmented unison
['M2', 'd3'], # Major second Diminished third
['m3', 'A2'], # Minor third Augmented second
['M3', 'd4'], # Major third Diminished fourth
['P4', 'A3'], # Perfect fourth Augmented third
['d5', 'A4'], # Diminished fifth Augmented fourth
['P5', 'd6'], # Perfect fifth Diminished sixth
['m6', 'A5'], # Minor sixth Augmented fifth
['M6', 'd7'], # Major sixth Diminished seventh
['m7', 'A6'], # Minor seventh Augmented sixth
['M7', 'd8'], # Major seventh Diminished octave
['P8', 'A7'], # Perfect octave Augmented seventh
]
同样,同一音符可以具有不同的音程名称。例如,根音可以被认为是完美的统一音色或减弱的第二音符。给定键中的半音音阶和上述数组中的间隔,我们可以指出要使用的确切音符(并从一组谐音等效项中过滤掉)。让我们看一下执行此操作的基本方法。举例来说,让我们看一下如何从D
色阶中找到与M3
或主要的第三音阶相对应的音符。1、从区间数组中,我们可以看到找到M3
的索引为4。即'M3' in intervals[4] == True
。2、现在,我们在D半音音阶(以其长度为模)中查看相同的索引。我们发现
colour('D')[4]
是音符['E ##','F#','Gb']
的列表。3、M3
中的数字(即3)表示我们需要使用的字母,其中1表示根字母。因此,例如,对于D
的键,1 = D,2 = E,3 = F,4 = G,5 = A,6 = B,7 = C,8 = D
…等等。因此,我们需要在包含字母F
的音节列表(['E ##','F#','Gb']
)中寻找一个音节。这就是音节F#
。我们可以编写一个相对简单的函数,以编程方式为我们应用此逻辑,并为我们提供一个字典,将给定键中的所有音程名称映射到正确的音符名称:def make_intervals_standard(key):
# Our labeled set of notes mapping interval names to notes
labels = {}
# Step 1: Generate a chromatic scale in our desired key
chromatic_scale = chromatic(key)
# The alphabets starting at provided key's alphabet
alphabet_key = rotate(alphabet, find_note_index(alphabet, key[0]))
# Iterate through all intervals (list of lists)
for index, interval_list in enumerate(intervals):
# Step 2: Find the notes to search through based on degree
notes_to_search = chromatic_scale[index % len(chromatic_scale)]
for interval_name in interval_list:
# Get the interval degree
degree = int(interval_name[1]) - 1 # e.g. M3 --> 3, m7 --> 7
# Get the alphabet to look for
alphabet_to_search = alphabet_key[degree % len(alphabet_key)]
try:
note = [x for x in notes_to_search if x[0] == alphabet_to_search][0]
except:
note = notes_to_search[0]
labels[interval_name] = note
return labels
>>> import pprint
>>> pprint.pprint(make_intervals_standard('C'), sort_dicts=False)
{'P1': 'C',
'd2': 'Dbb',
'm2': 'Db',
'A1': 'C#',
'M2': 'D',
'd3': 'Ebb',
'm3': 'Eb',
'A2': 'D#',
'M3': 'E',
'd4': 'Fb',
'P4': 'F',
'A3': 'E#',
'd5': 'Gb',
'A4': 'F#',
'P5': 'G',
'd6': 'Abb',
'm6': 'Ab',
'A5': 'G#',
'M6': 'A',
'd7': 'Bbb',
'm7': 'Bb',
'A6': 'A#',
'M7': 'B',
'd8': 'Cb',
'P8': 'C',
'A7': 'B#'}
现在,我们可以使用间隔名称指定公式或音节组,并能够将它们映射到我们想要的任何键:def make_formula(formula, labeled):
'''
Given a comma-separated interval formula, and a set of labeled
notes in a key, return the notes of the formula.
'''
return [labeled[x] for x in formula.split(',')]
formula = 'P1,M2,M3,P4,P5,M6,M7,P8'
我们可以使用它轻松地为不同的键生成主音阶,如下所示:>>> for key in alphabet:
>>> print(key, make_formula(formula, make_intervals_standard(key)))
C ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C']
D ['D', 'E', 'F#', 'G', 'A', 'B', 'C#', 'D']
E ['E', 'F#', 'G#', 'A', 'B', 'C#', 'D#', 'E']
F ['F', 'G', 'A', 'Bb', 'C', 'D', 'E', 'F']
G ['G', 'A', 'B', 'C', 'D', 'E', 'F#', 'G']
A ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#', 'A']
B ['B', 'C#', 'D#', 'E', 'F#', 'G#', 'A#', 'B']
def dump(scale, separator=' '):
'''
Pretty-print the notes of a scale. Replaces b and # characters
for unicode flat and sharp symbols.
'''
return separator.join(['{:<3s}'.format(x) for x in scale]) \
.replace('b', '\u266d') \
.replace('#', '\u266f')
>>> for key in alphabet:
>>> scale = make_formula(formula, make_intervals_standard(key))
>>> print('{}: {}'.format(key, dump(scale)))
C: C D E F G A B C
D: D E F♯ G A B C♯ D
E: E F♯ G♯ A B C♯ D♯ E
F: F G A B♭ C D E F
G: G A B C D E F♯ G
A: A B C♯ D E F♯ G♯ A
B: B C♯ D♯ E F♯ G♯ A♯ B
公式命名的另一种方法是基于主要标准的音节。弹奏乐器时这会更容易,因为如果您熟悉其主要音阶,则可以在给定的琴键中获得音阶和和弦。intervals_major = [
[ '1', 'bb2'],
['b2', '#1'],
[ '2', 'bb3', '9'],
['b3', '#2'],
[ '3', 'b4'],
[ '4', '#3', '11'],
['b5', '#4', '#11'],
[ '5', 'bb6'],
['b6', '#5'],
[ '6', 'bb7', '13'],
['b7', '#6'],
[ '7', 'b8'],
[ '8', '#7'],
]
我还添加了用于更复杂的和弦(第9、11和13)的常用音程。这些本质上是围绕模八进行包装的。因此,例如,第9位只是第2位,但高了八度。我们还可以修改我们的
make_intervals()
函数以使用此函数:def make_intervals(key, interval_type='standard'):
...
for index, interval_list in enumerate(intervals):
...
intervs = intervals if interval_type == 'standard' else intervals_major
for interval_name in intervs:
# Get the interval degree
if interval_type == 'standard':
degree = int(interval_name[1]) - 1 # e.g. M3 --> 3, m7 --> 7
elif interval_type == 'major':
degree = int(re.sub('[b#]', '', interval_name)) - 1
...
return labels
上面,我们刚刚向make_intervals()
函数添加了一个新参数(interval_type
),并在内部循环中以不同的方式计算degree
度数。如果将interval_type
指定为'major
',则只需删除所有b
和#
字符,然后再转换为整数以获取度数即可。formulas = {
# Scale formulas
'scales': {
# Major scale, its modes, and minor scale
'major': '1,2,3,4,5,6,7',
'minor': '1,2,b3,4,5,b6,b7',
# Melodic minor and its modes
'melodic_minor': '1,2,b3,4,5,6,7',
# Harmonic minor and its modes
'harmonic_minor': '1,2,b3,4,5,b6,7',
# Blues scales
'major_blues': '1,2,b3,3,5,6',
'minor_blues': '1,b3,4,b5,5,b7',
# Penatatonic scales
'pentatonic_major': '1,2,3,5,6',
'pentatonic_minor': '1,b3,4,5,b7',
'pentatonic_blues': '1,b3,4,b5,5,b7',
},
'chords': {
# Major
'major': '1,3,5',
'major_6': '1,3,5,6',
'major_6_9': '1,3,5,6,9',
'major_7': '1,3,5,7',
'major_9': '1,3,5,7,9',
'major_13': '1,3,5,7,9,11,13',
'major_7_#11': '1,3,5,7,#11',
# Minor
'minor': '1,b3,5',
'minor_6': '1,b3,5,6',
'minor_6_9': '1,b3,5,6,9',
'minor_7'
: '1,b3,5,b7',
'minor_9': '1,b3,5,b7,9',
'minor_11': '1,b3,5,b7,9,11',
'minor_7_b5': '1,b3,b5,b7',
# Dominant
'dominant_7': '1,3,5,b7',
'dominant_9': '1,3,5,b7,9',
'dominant_11': '1,3,5,b7,9,11',
'dominant_13': '1,3,5,b7,9,11,13',
'dominant_7_#11': '1,3,5,b7,#11',
# Diminished
'diminished': '1,b3,b5',
'diminished_7': '1,b3,b5,bb7',
'diminished_7_half': '1,b3,b5,b7',
# Augmented
'augmented': '1,3,#5',
# Suspended
'sus2': '1,2,5',
'sus4': '1,4,5',
'7sus2': '1,2,5,b7',
'7sus4': '1,4,5,b7',
},
}
这是在C键中生成所有这些音阶和和弦时的输出:
intervs = make_intervals('C', 'major')
for ftype in formulas:
print(ftype)
for name, formula in formulas[ftype].items():
v = make_formula(formula, intervs)
print('\t{}: {}'.format(name, dump(v)))
scales
major: C D E F G A B
minor: C D E♭ F G A♭ B♭
melodic_minor: C D E♭ F G A B
harmonic_minor: C D E♭ F G A♭ B
major_blues: C D E♭ E G A
minor_blues: C E♭ F G♭ G B♭
pentatonic_major: C D E G A
pentatonic_minor: C E♭ F G B♭
pentatonic_blues: C E♭ F G♭ G B♭
chords
major: C E G
major_6: C E G A
major_6_9: C E G A D
major_7: C E G B
major_9: C E G B D
major_13: C E G B D F A
major_7_#11: C E G B F♯
minor: C E♭ G
minor_6: C E♭ G A
minor_6_9: C E♭ G A D
minor_7: C E♭ G B♭
minor_9: C E♭ G B♭ D
minor_11: C E♭ G B♭ D F
minor_7_b5: C E♭ G♭ B♭
dominant_7: C E G B♭
dominant_9: C E G B♭ D
dominant_11: C E G B♭ D F
dominant_13: C E G B♭ D F A
dominant_7_#11: C E G B♭ F♯
diminished: C E♭ G♭
diminished_7: C E♭ G♭ B♭♭
diminished_7_half: C E♭ G♭ B♭
augmented: C E G♯
sus2: C D G
sus4: C F G
7sus2: C D G B♭
7sus4: C F G B♭
mode = rotate
需要注意的是,由于旋转后的根音会发生变化,因此所得到的旋转比例或模式处于不同的键中。对于每个键,主要有七个主要音阶模式,具体取决于所应用的左旋次数,每个模式都有一个特定的名称:major_mode_rotations = {
'Ionian': 0,
'Dorian': 1,
'Phrygian': 2,
'Lydian': 3,
'Mixolydian': 4,
'Aeolian': 5,
'Locrian': 6,
}
使用此方法,我们现在可以为任何给定键生成主要比例的模式。这是C大调的一个例子:intervs = make_intervals('C', 'major')
c_major_scale = make_formula(formulas['scales']['major'], intervs)
for m in major_mode_rotations:
v = mode(c_major_scale, major_mode_rotations[m])
print('{} {}: {}'.format(dump([v[0]]), m, dump(v)))
C Ionian: C D E F G A B
D Dorian: D E F G A B C
E Phrygian: E F G A B C D
F Lydian: F G A B C D E
G Mixolydian: G A B C D E F
A Aeolian: A B C D E F G
B Locrian: B C D E F G A
上面,我们正在研究从给定比例导出的模式。但是,实际上我们关心的是给定键的模式。因此,给定C的键,我们想知道C Ionian
,C Dorian
,C Mixolydian
等。另一种表达方式是,例如“ C Mixolidian”
与“the Mixolydian of C”
不同。前者是指根音为C的混合音阶,后者是指C大音阶的混合音阶(即上方的G混合音阶)。keys = [
'B#', 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'Fb', 'E#', 'F',
'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B', 'Cb',
]
modes = {}
for key in keys:
intervs = make_intervals(key, 'major')
c_major_scale = make_formula(formulas['scales']['major'], intervs)
for m in major_mode_rotations:
v = mode(c_major_scale, major_mode_rotations[m])
if v[0] not in modes:
modes[v[0]] = {}
modes[v[0]][m] = v
上面,我们循环了每个键,并建立了一个字典,其中包含我们遇到每个键时所使用的模式(通过检查模式的第一个音符)。现在,例如,如果我们打印出模式['C']
,则会得到以下内容:因此,我们研究了西方音乐理论中的基本音符。如何从这些音节中得出音阶。如何利用间隔名称从谐音等效项中选择正确的音符。然后,我们研究了如何使用间隔公式(使用标准音程名称和相对于大音阶的音程)来生成各种音阶和和弦。最后,我们看到模式只是音阶的旋转,对于给定的键可以用两种方式查看:通过旋转给定键的音阶(将在另一个键中)得出的模式,以及从模式中得出的模式。从某些键开始,这样第一个音符就是我们想要的键。
点击下方阅读原文加入社区会员