用 200 行 Python 代码掌握基本音乐理论
Python中文社区
共 30977字,需浏览 62分钟
·
2021-04-29 21:19
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'],
]
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'],
]
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
或主要的第三音阶相对应的音符。M3
的索引为4。即'M3' in intervals[4] == True
。colour('D')[4]
是音符['E ##','F#','Gb']
的列表。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#
。M3
)是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')
unicode
字符的更好输出:>>> 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'],
]
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,
}
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 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']
,则会得到以下内容:更多阅读
特别推荐
点击下方阅读原文加入社区会员
评论