python循环索引

时间:2012-12-05 12:30:29

标签: python loops indexing music-notation

我正在尝试编写一个用于获得音阶音符的程序。这就是我现在所做的,但它看起来非常复杂!我错过了什么?它应该是那样的吗?

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结束时重新开始...

感谢。

4 个答案:

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