music21简明指南
music21
是一个由MIT开发的功能强大的计算音乐学分析Python库。相比于pretty_midi
库只能处理MIDI文件,music21
可以处理包括MusicXML,MIDI,abc等多种格式的音乐文件,并可以从零开始构建音乐文件或对音乐进行分析。
在搜索引擎中搜索music21,大概率会被引导到这个网址:https://web.mit.edu/music21/。我在初次接触music21
时看到这个网页感觉版面杂乱,不知何从下手。但是,进入右侧的Get Started with music21,并一步步查看教程,这时结构就清晰了许多,并且一步一步由浅入深阅读就可掌握music21
的使用方法了。
music21
官方教程其实介绍相当详尽易懂,教程中由浅入深介绍了music21
的各个模块和使用方式。即使你对Python不甚了解,直接阅读官方教程也不会特别吃力,因为教程中有针对Python特性以及使用方法的介绍。
唯一的缺点是官方教程过于冗长,共有58章,每章也很长。如果需要快速上手,生成一份音乐文件,那一步步阅读教程所耗费的时间就过长了。因此,本文介绍了一些简单功能,足以使读者在短时间内使用music21
生成需要的音乐文件,并令读者概略了解music21
的使用方式。本文中大部分内容是从music21
官方教程中摘录而来,并假设读者对Python有简单的了解。
首先,导入music21
包,在这里我们使用官方教程中的习惯对music21包进行全部加载,但使用时建议通过import music21
进行导入以避免污染命名空间。music21
可以很方便的使用pip install music21
安装。
from music21 import *
创建音符
音符是音乐中的最基本单位,music21
库的风格是强类型的。基本来说,构成乐谱的每个音乐元素都有相应的类,并有其成员、方法。所有元素的类型都应是明确的。音符在music21
中为music.note.Note
类型,可以直接使用note.Note()
初始化该类的实例(note.Note()
中无参数则返回一个C4音高的四分音符)。
f = note.Note("F5#") #创建一个音高为F5#的音符
f.name #音符的音名
'F#'
f.step #音符的音级(不包含变化音及八度信息的音名,这里成为音级严格来说并不准确)
'F'
f.pitch.pitchClassString #音级(以C为0级的音程数)
'6'
f.octave #八度
5
f.pitch #音符的音高,返回一个音高对象
<music21.pitch.Pitch F#5>
f.duration #音符的时值,返回一个时值对象
<music21.duration.Duration 1.0>
f.duration.type #音符的时值的类型
'quarter'
在音符创建后,可修改其中的属性
f.octave=6
f.pitch
<music21.pitch.Pitch F#6>
f = note.Note("F5#") #可以在note.Note()中指定音高、八度及变化音来初始化音符对象
f.pitch.accidental=pitch.Accidental(-1) #pitch.accidental是变化音的类型
f
<music21.note.Note F->
f.duration.dots+=1 #改变符点数量
f.duration
<music21.duration.Duration 1.5>
f.duration.quarterLength=2 #直接改变时值,以四分音符计
f.duration
<music21.duration.Duration 2.0>
f.articulations = [articulations.Staccato()]
和弦
除了音符之外,乐谱中的另一个重要元素为和弦。和弦在music21
中也有其单独的类型music21.chord.Chord
。
cMajor = chord.Chord(["E3","C4","G4"]) #初始化和弦
print(cMajor)
<music21.chord.Chord E3 C4 G4>
cMajor = chord.Chord() #也可以初始化一个空白类并一个个添加
cMajor.add(note.Note("E3"))
cMajor.add(note.Note("C4"))
cMajor.add(note.Note("G4"))
print(cMajor)
<music21.chord.Chord E3 C4 G4>
# 与Note类似,和弦也可以修改时值:
cMajor.duration.quarterLength=2
Stream
Stream是在music21中类似list的基本单位,它可以储存任意music21对象及其组合。
创建一个stream:
stream1 = stream.Stream()
stream1.append(f)
n3 = note.Note('D#5')
stream1.repeatAppend(n3, 4)
print(len(stream1))
5
当需要显示Stream以及整个乐谱等复杂结构的内容时,需要使用.show()
方法。.show()
方法不加参数是打开默认的乐谱编辑器,但是其设置较为麻烦,请读者自行配置。本文中皆使用.show('text')
进行文字显示。
stream1.show('text')
{0.0} <music21.note.Note F->
{2.0} <music21.note.Note D#>
{3.0} <music21.note.Note D#>
{4.0} <music21.note.Note D#>
{5.0} <music21.note.Note D#>
当然也可以当迭代器使用:
for thisNote in stream1:
print(thisNote.step)
F
D
D
D
D
也支持如列表的查找和删除:
note3Index = stream1.index(f)
stream1.pop(note3Index)
<music21.note.Note F->
stream1[2] == n3
True
stream1.show('text')
{2.0} <music21.note.Note D#>
{3.0} <music21.note.Note D#>
{4.0} <music21.note.Note D#>
{5.0} <music21.note.Note D#>
添加元素或添加Stream
stream可以嵌套使用,形成层级关系。
biggerStream = stream.Stream()
note2 = note.Note("D#5")
biggerStream.insert(0, note2)
biggerStream.append(stream1)
biggerStream.show('text') # 在文字显示的缩进上可以看出层级的关系
{0.0} <music21.note.Note D#>
{1.0} <music21.stream.Stream 0x1cd59e005f8>
{2.0} <music21.note.Note D#>
{3.0} <music21.note.Note D#>
{4.0} <music21.note.Note D#>
{5.0} <music21.note.Note D#>
小节 (Measure):
m1=stream.Measure(number=0)
m1.repeatAppend(f, 4)
m2=stream.Measure(number=1)
m2.repeatAppend(n3, 2)
stream2=stream.Stream()
stream2.append(m1)
stream2.append(m2)
ts=meter.TimeSignature()
ts.denominator=4
ts.numerator=int(m1.duration.quarterLength)
m1.insert(0,ts)
m1.show('text')
{0.0} <music21.meter.TimeSignature 8/4>
{0.0} <music21.note.Note F->
{2.0} <music21.note.Note F->
{4.0} <music21.note.Note F->
{6.0} <music21.note.Note F->
查找:
可以查找所有指定的类,getElementsByClass
返回迭代器:
for thisNote in stream1.getElementsByClass(note.Note):
print(thisNote, thisNote.offset)
<music21.note.Note D#> 2.0
<music21.note.Note D#> 3.0
<music21.note.Note D#> 4.0
<music21.note.Note D#> 5.0
stream1.append(note.Rest())
for thisNote in stream1.getElementsByClass(["Note", "Rest"]):
print(thisNote, thisNote.offset)
<music21.note.Note D#> 2.0
<music21.note.Note D#> 3.0
<music21.note.Note D#> 4.0
<music21.note.Note D#> 5.0
<music21.note.Rest rest> 6.0
也可以列出所有音高(不管时值)
stream1.pitches
[<music21.pitch.Pitch D#5>,
<music21.pitch.Pitch D#5>,
<music21.pitch.Pitch D#5>,
<music21.pitch.Pitch D#5>]
有些子方法与使用getElementsByClass
查找的用途一直,例如.notes
相当于.getElementsByClass(["Note", "Chord"])
,.notesAndRests
相当于.getElementsByClass(['Note', 'Chord', 'Rest'])
。
分析最大音高与最小音高的差:
stream1.analyze('ambitus')
<music21.interval.Interval P1>
生成乐谱
创建一个乐谱对象:
score = stream.Score()
添加声部:
part = stream.Part()
part.partName = 'Example Part'
将之前生成的Stream添加到声部中:
part.append(stream2)
将声部添加到乐谱中:
score.insert(0, part)
添加题目、作者等元数据:
score.insert(0, metadata.Metadata())
score.metadata.title = 'example title'
score.metadata.composer = 'example composer'
写入文件:
score.write('xml',fp=r'example.xml')