python循环索引



我正在尝试编写一个程序来获取音阶的音符。这就是我现在所做的,但它似乎复杂得离谱!!我错过了什么?应该是那样吗?

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的末尾时重新启动。。。

谢谢。

一种方法是使用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']

如果你关心音符的正确拼写,你需要一种更复杂的方法。例如,当你真正想要的是E#作为主音时,你的F#主音阶会读成[F#,G#,A#,B,C#,D#,F]。同样,如果你关心拼写,你也需要实现flats。如果除了大音阶(自然和和声小音阶、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的《Geeks and Nerds的音乐》一书感兴趣;或者,如果你想深入研究音乐理论问题(旋律小音阶,升降不同;非八度重复音阶等),你可以(为我自己的作品无耻地插上插头)看看music21Python工具包,尤其是music21.scale模块。

最简单的?

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或索引

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]

或者一个"一"行:

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]

相关内容

  • 没有找到相关文章

最新更新