我正在尝试编写一个用于获得音阶音符的程序。这就是我现在所做的,但它看起来非常复杂!我错过了什么?它应该是那样的吗?
notes = [ "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" ]
major = [2,2,1,2,2,2] # semitone steps
root = "f"
root_i= notes.index(root)
index = [root_i+i for i in [sum(major[:y]) for y in range(len(major)+1)]]
scale = [notes[i] if i < len(notes) else notes[i-len(notes)] for i in index]
我只需要在root_i
中的每个“步骤”增加major
,并在我到达notes
结束时重新开始...
感谢。
答案 0 :(得分:2)
如果您关心如何正确拼写音符,您将需要更复杂的方法。例如,当您真正想要的是E#作为主音时,您的F#主音阶将显示[F#,G#,A#,B,C#,D#,F]。同样,如果你关心拼写,你也需要实施单位。如果您关心除了主要音阶(自然和谐音,Lydian等)之外的全音阶音阶,您还需要将音符间距与所需的音阶间距分离。你想要的是更复杂的东西:
def getScale(root='C', mode='major')
noteNames = ['C','D','E','F','G','A','B']
noteSpacing = [2,2,1,2,2,2,1]
if mode == 'natural minor':
scaleSpacing = [2,1,2,2,1,2,2]
elif mode == 'harmonic minor':
scaleSpacing = [2,1,2,2,1,3,1]
else:
scaleSpacing = [2,2,1,2,2,2,1]
startingIndex = noteNames.index(root[0])
baseSemitoneOffset = root.count('#') - root.count('b')
currentSemitones = 0
correctSemitones = 0
scale = [root]
for noteDegree in range(1, 7):
currentIndex = (startingIndex + noteDegree) % len(noteNames)
currentSemitones += scaleSpacing[(noteDegree -1) % len(noteNames)]
correctSemitones += noteSpacing[(currentIndex - 1) % len(noteNames)]
currentSemitonesWithOffset = currentSemitones + baseSemitoneOffset
thisNoteStep = noteNames[currentIndex]
if currentSemitonesWithOffset < correctSemitones:
thisNoteName = thisNoteStep + 'b' * (correctSemitones - currentSemitonesWithOffset)
elif currentSemitonesWithOffset > correctSemitones:
thisNoteName = thisNoteStep + '#' * (currentSemitonesWithOffset - correctSemitones)
else:
thisNoteName = thisNoteStep
#print thisNoteName, currentSemitonesWithOffset, currentSemitones, correctSemitones
scale.append(thisNoteName)
return scale
这些值会返回您期望的值
print getScale('C')
print getScale('Ab')
print getScale('F#')
['C', 'D', 'E', 'F', 'G', 'A', 'B']
['Ab', 'Bb', 'C', 'Db', 'Eb', 'F', 'G']
['F#', 'G#', 'A#', 'B', 'C#', 'D#', 'E#']
适用于更加模糊的尺度:
print getScale('C', mode='harmonic minor')
print getScale('Ab', mode='natural minor')
print getScale('Fb', mode='major')
['C', 'D', 'Eb', 'F', 'G', 'Ab', 'B']
['Ab', 'Bb', 'Cb', 'Db', 'Eb', 'Fb', 'Gb']
['Fb', 'Gb', 'Ab', 'Bbb', 'Cb', 'Db', 'Eb']
有一个真正的假设,即音乐理论比在计算机中实现的图形或音频要容易得多......但它确实如此,但并不容易。 Python程序员可能会对Pedro Kroger的书Music for Geeks and Nerds感兴趣;或者如果你想进入更深层次的音乐理论问题(旋律小音阶,不同的升序和降序;非八度音阶重复音阶等),你可以(无耻地插上我自己的作品)看music21 Python toolkit,特别是music21.scale模块。
答案 1 :(得分:1)
scale = [notes[i] if i < len(notes) else notes[i-len(notes)] for i in index]
可以写成
scale = [notes[i % len(notes)] for i in index]
可以使用itertools
:
import itertools as it
notes = [ "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" ]
major = [2,2,1,2,2,2] # semitone steps
root = "f"
note_iter = it.dropwhile(root.__ne__, it.cycle(notes))
scale = [list(it.islice(note_iter, m))[0] for m in major]
或“one”-liner:
scale = [n for i, n in it.izip(it.chain.from_iterable(xrange(m) for m in major),
it.dropwhile(root.__ne__, it.cycle(notes)))
if i == 0]
答案 2 :(得分:1)
这样做的一种方法是使用deque
,但基于list
的方法并没有什么问题。我只是倾向于通过把它放在自己的函数中来使它变得更加明显......
from collections import deque
notes = [ "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" ]
def get_scale(seq, start):
d = deque(seq)
d.rotate(-seq.index(start))
yield d[0]
for idx in [2, 2, 1, 2, 2, 2]:
d.rotate(-idx) # always bring element to index 0
yield d[0]
print list(get_scale(notes, 'c'))
然后,您可以预先计算该批次:
>>> scales = {k:list(get_scale(notes, k)) for k in notes}
>>> scales
{'a': ['a', 'b', 'c#', 'd', 'e', 'f#', 'g#'], 'c': ['c', 'd', 'e', 'f', 'g', 'a', 'b'], 'b': ['b', 'c#', 'd#', 'e', 'f#', 'g#', 'a#'], 'e': ['e', 'f#', 'g#', 'a', 'b', 'c#', 'd#'], 'd': ['d', 'e', 'f#', 'g', 'a', 'b', 'c#'], 'g': ['g', 'a', 'b', 'c', 'd', 'e', 'f#'], 'f': ['f', 'g', 'a', 'a#', 'c', 'd', 'e'], 'c#': ['c#', 'd#', 'f', 'f#', 'g#', 'a#', 'c'], 'd#': ['d#', 'f', 'g', 'g#', 'a#', 'c', 'd'], 'f#': ['f#', 'g#', 'a#', 'b', 'c#', 'd#', 'f'], 'g#': ['g#', 'a#', 'c', 'c#', 'd#', 'f', 'g'], 'a#': ['a#', 'c', 'd', 'd#', 'f', 'g', 'a']}
>>> scales['d']
['d', 'e', 'f#', 'g', 'a', 'b', 'c#']
>>> scales['c']
['c', 'd', 'e', 'f', 'g', 'a', 'b']
答案 3 :(得分:1)
最简单的?
scale = [notes[(y+root_i)%len(notes)] for y in [0,2,4,5,7,9,11]]
甚至
scale = [notes[(y+notes.index(root))%len(notes)] for y in [0,2,4,5,7,9,11]]
您不需要root_i或索引