-
-
Notifications
You must be signed in to change notification settings - Fork 129
Musicpy的数据结构
musicpy这门语言可以让你用非常简洁的语法,来表达一段音乐的音符,节奏等等信息,并且可以简单地输出成MIDI文件的格式。这个库里面涉及到非常多的乐理知识,所以个人推荐至少要先了解一部分乐理再来使用会比较上手。相对地,如果你是一个对乐理比较了解的人,那么看完我写的教程之后你应该很快就上手了。
特别提醒:在musicpy中,除了个别的乐理函数,乐理类型本身的索引都是从0开始的,比如和弦类型,音阶类型,乐曲类型等等。
在musicpy里面,最基本的数据结构是note (音符), chord (和弦)和scale (音阶)。这几个类型是构成音乐代码的基础。musicpy里面的乐理功能非常多,先从几个最基本的开始介绍吧。
首先在这里需要先声明,musicpy里所有的音符长度,音符间隔的单位都是4/4拍的小节,可以是整数,小数或者分数。速度的单位是BPM,也就是1分钟可以演奏多少拍。在4/4拍里,1个小节就是4拍,1拍就是1/4个小节,也就是每分钟播放的小节数是BPM/4,1个小节的时间长度秒数是240/BPM。musicpy里所有的小节都当做4/4拍来处理。
在这个章节,我们介绍几个musicpy最重要以及最常用的数据结构。
- note (音符类型)
- chord (和弦类型)
- scale (音阶类型)
- piece (乐曲类型)
- track (音轨类型)
- drum (鼓点类型)
- tempo (速度类型)
- pitch_bend (弯音类型)
- pan (声相类型)
- volume (音量类型)
- rest (休止符类型)
- beat (节拍类型)
- rest_symbol (休止符类型)
- continue_symbol (延续符类型)
- rhythm (节奏类型)
初始化一个note的实例只需要给一个音名(比如C, Eb, A#,也可以是小写)和一个八度数(一个整数),现在你就可以使用这个音符去做音乐里的任何事情了。你还可以设定音符的duration(音符长度)和volume(音符的音量大小)。音符长度默认为0.25(1/4个小节),音量默认为100。
比如:
a = note('A', 5)
这样就是把音符A5赋值给了a,表示出来是这样:
A5
表示的是音名和八度数,这两个共同决定了音高。
使用note
构建音符类型时,八度数可以省略,默认值为4。
note(name,
num=4,
duration=0.25,
volume=100,
channel=None)
-
name: 音名 (C, D, E, G#, Gb, ...), 为一个表示音名的字符串
-
num: 八度数,为一个整数,和音名一起确定一个音的音高
-
duration: 音符的长度,单位为小节,比如duration = 1 表示音符长度为1个小节,默认值为0.25
-
volume: 音符的力度,对应的是MIDI文件里的音符的力度,范围从0到127,默认值为100
-
channel: 音符的MIDI通道编号,在写入MIDI文件时如果channel不为None,则写入对应的通道
-
degree: 音符的音高数,以C0为12,每一个八度有十二个音,比如C1的音高数就是24,D1的音高数就是26,C5的音高数就是72,以此类推,每一个音符在构建的时候都会自动计算自己的音名和八度数所对应的音高数并且存储起来,之后的用处非常多。(这个音高数是对应MIDI文件的通用标准里的音符的MIDI数字)
由于音高数这一基本属性,因此音符类本身是等值为纯数字的,也就是完全可以作为纯数字使用,和弦类型是音符类的集合,也说明和弦类型本身等值为一个全部都是数字的集合,也可以作为向量,甚至矩阵来看待(比如多个和弦的连接走向就可以看作多个向量的拼接,因此也就有了行列数,也就是矩阵的形式)。也因此在这门语言的数据结构设计中,音符类,和弦类型,音阶类都是可以进行数学运算的,比如线性代数领域的运算,离散数学领域的运算等等。也可以在这门语言的数据结构的基础上建立一整套乐理逻辑的算法,结合纯数学逻辑来进行多方面的音乐分析研究。现代音乐领域的很多实验性质的音乐,比如序列主义,偶然音乐,后现代主义音乐(比如极简主义音乐),理论上全部都可以在这门语言的纯数字化的数据结构的基础上进行严格的创作。即使不提实验性质的音乐,这门语言也可以写任何的古典音乐,爵士音乐,流行音乐。
这个应该是最重要的类了。在musicpy里,和弦类被定义为“一组音符的集合”,这个定义或许比乐理里面的和弦定义更为广义化,因为按照这个定义,一首完整的乐曲也可以完全装进和弦类型里面,在这个库里也确实可以。
你可以使用和弦类型表示一个和弦,也可以表示一段旋律,或者两者的结合。
初始化一个和弦实例,只需要给一个音符的列表即可。还可以设定duration(所有音符的长度设置),interval(音符之间的间隔,用列表表示)。这里比较人性化的一个地方是,你在给定音符列表时无需先用note类初始化,而只需要直接写音符的名字(字符串)就行了。
比如:
Cmaj7 = chord(['C5', 'E5', 'G5', 'B5'])
这样就写出了一个C大七和弦了。
(和弦类型的其他的构建方法还有很多种,可以写得比这里更加简洁,具体请看基础语法部分)
我们可以用play函数播放这个和弦
play(Cmaj7)
这个C大七和弦表示出来是这样:
[C5, E5, G5, B5] with interval [0, 0, 0, 0]
如果你想要在musicpy中创造一段旋律,逻辑和创建一个和弦是一样的,这个举个例子
melody = chord('C5, C5, G5, G5, A5, A5, G5, F5, F5, E5, E5, D5, D5, C5',
duration=[1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/4, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/4],
interval=[1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/4, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/4])
你可以播放这段旋律,比如以80BPM的速度
play(melody, bpm=80)
chord(notes,
duration=None,
interval=None,
rootpitch=4,
other_messages=[],
start_time=None)
-
notes: 音符列表,为一个记载着这个和弦(或者曲子)所有音符的列表
-
duration: 和弦的每个音符各自的音符长度,默认值为None,如果为None则按照音符本身的长度,如果为一个整数,小数或者列表则对音符的长度进行调整
-
interval: 每两个连续音符之间的间隔,单位为小节,为一个记载着音符间隔的列表(如果在初始化时是整数或小数,则设定为全部的间隔都为此整数或小数),默认值为None,如果没有特别指定间隔,则默认全部的音符间隔为0。
请注意,这里的音符间隔的定义是从当前音符的开头到下一个音符的开头之间的间隔。 -
rootpitch: 如果传入的音符列表的元素不是音符类型,而是表示音符的字符串,则会尝试用to_note函数转化为音符类型,如果音符字符串没有八度数,只有音名的情况下,会使用rootpitch来当做音符的八度数,默认值为4
-
other_messages: 用来记录其他的MIDI信息的列表,默认值为空列表
-
start_time: 和弦类型的开始时间,可以理解为从开头到第一个音符开始之间的休止符,单位为小节,默认值为0
请注意,当和弦类型作为音轨放在乐曲类型的音轨列表当中的时候,start_time
这个属性会被忽略,每个音轨的和弦类型的开始时间以乐曲类型的start_times
列表为准,除了需要用到和弦类型的合并(比如乐曲类型自身合并为一个和弦类型)或者单独从音轨列表播放和弦类型的时候start_time
还是会起到作用。这里建议放在乐曲类型中作为音轨的和弦类型的start_time
都设置为0,以避免和弦类型和乐曲类型的开始时间可能发生的冲突。
这个类可以表示一个特定的音阶。使用这个类可以快速按照音的间隔来构建调式,比如大调的音的排列是全全半全全全半(全代表全音,半代表半音),那么如果想构建一个C大调音阶,就可以写
scale('C5', interval=[2, 2, 1, 2, 2, 2, 1], name='major')
这样就得到了以C5为根音的C大调音阶,表示出来是这样:
[scale]
scale name: C5 major scale
scale intervals: [2, 2, 1, 2, 2, 2, 1]
scale notes: [C5, D5, E5, F5, G5, A5, B5, C6]
当然,对于大部分知名的调式来说,只需要输入调式的名称就行了。比如
scale('C5', 'major')
就可以得到以C5为根音的C大调音阶,
[scale]
scale name: C5 major scale
scale intervals: [2, 2, 1, 2, 2, 2, 1]
scale notes: [C5, D5, E5, F5, G5, A5, B5, C6]
比如
scale('C5', 'minor')
得到以C5为根音的C小调音阶,
[scale]
scale name: C5 minor scale
scale intervals: [2, 1, 2, 2, 1, 2, 2]
scale notes: [C5, D5, D#5, F5, G5, G#5, A#5, C6]
等等。 在database.py里面的scaleTypes是所有musicpy自带的调式,用户也可以自己定制调式。
scale(start=None,
mode=None,
interval=None,
notes=None)
-
start: 音阶的主音(起始音) ,为一个音符类型
-
mode: 音阶的名字,比如major, minor, dorian, lydian等等
-
interval: 音阶内的音程关系,1表示半音,2表示全音,3表示增二度,以此类推,为一个列表的形式,比如大调音阶的interval就是[2,2,1,2,2,2,1]
-
notes: 音符的列表,可以参考和弦类型的音符列表,一个音阶本身是一组确定的音符,因此notes就是一个音阶实例里的所有音符,也可以是一个和弦实例
音阶类的内置方法中,有着丰富的乐理逻辑函数,比如和声功能函数,主和弦,属和弦,下属和弦,某一级的副属和弦,从音阶按照一定的步进来提取自然三和弦,自然七和弦,按照五度圈进行顺时针或者逆时针方向一定步数的转调,关系调,平行调,负面和声(镜像和声),按照级数提取和弦走向,按照指定调式进行转调,从某一级的音得到衍生调式, 得到标准化的音名标记(每个调里的音名的升降记号)等等。关于具体的细节,我在基础语法和如何使用的部分会详细讲解。
当我们需要写有多个音轨以及每个音轨有自己的乐器的曲子时,可以使用乐曲类型,一个乐曲类型的实例可以存储任意多个音轨,每个音轨可以有自己的乐器,也可以通过MIDI信息随时改变乐器。
在乐曲类型的基础语法
章节有关于乐曲类型的详细介绍。
音轨类型是用来存储乐曲类型中单个音轨的信息的数据结构,虽然乐曲类型本身并不是由音轨类型构成的,但是音轨类型可以从乐曲类型中提取出来,也可以添加到乐曲类型中,音轨类型本身也可以直接播放和写入MIDI文件。
在音轨类型的基础语法
章节有关于音轨类型的详细介绍。
鼓点类型是一种特殊的数据结构,专门用来表示鼓点,也就是打击乐节奏,鼓点类型本质上与和弦类型很相似,其内部也是存储音符类型,在写入MIDI文件时可以对应到鼓的轨道的不同类型的鼓点上。鼓点类型最大的特点是在构建时可以使用一种独特的语法来写鼓点。这种语法比较简洁,不用考虑不同音高的音符对应到的是MIDI文件中的哪个类型的鼓点,而且支持一定程度上的批处理的操作。
在鼓点类型的基础语法
章节有关于鼓点类型的详细介绍。
速度类型可以用来实时地改变当前乐曲的速度(BPM),速度类型的实例可以当做音符类型插入到和弦类型中,可以在任意的两个音符类型之间改变当前乐曲的速度,也可以自己设定开始改变的时间,单位为小节,这样可以设定更加精确的速度变化的开始时间。速度类型可以作用于和弦类型,乐曲类型和音轨类型,并且在写入MIDI文件后也会作为MIDI事件存储在MIDI文件中。
在速度类型的基础语法
章节有关于速度类型的详细介绍。
弯音类型可以用来实时地改变某一个片段的音符的音高,可以很细微地变化音高。与速度类型一样,弯音类型也可以当做音符类型插入到和弦类型中,或者自己设定开始变化音高的时间,单位为小节。弯音类型可以作用于和弦类型,乐曲类型和音轨类型,并且在写入MIDI文件后也会作为MIDI事件存储在MIDI文件中。
在弯音类型的基础语法
章节有关于弯音类型的详细介绍。
声相类型是专门用来存储左右声道混音位置的类型,在乐曲类型中使用声相类型可以设置每一个音轨的声相。
在声相类型的基础语法
章节有关于声相类型的详细介绍。
音量类型是专门用来存储音轨的整体音量大小的类型,在乐曲类型中使用音量类型可以设置每一个音轨的整体音量大小。
在音量类型的基础语法
章节有关于音量类型的详细介绍。
目前在musicpy中休止符的定义是一种数据结构,可以在和弦类型构建与合并时传入,但是不会作为一个音符存在于和弦类型的音符列表中,因为按照和弦类型的定义,相邻的音符之间的间隔已经由和弦类型的音符间隔的列表定义了,因此休止符目前在和弦类型中存在的必要不是很大。当你在构建和弦类型时传入了休止符,休止符会体现在音符间隔的列表中,但是休止符本身不会添加到和弦类型的音符列表中。目前和弦类型的音符列表只可以包含3种数据结构:音符类型,速度类型,弯音类型。
rest(duration=1/4, dotted=None)
- duration: 休止符的长度,单位为小节
- dotted: 休止符的附点个数,默认为None,也就是不包含附点
# 在音符D与E之间添加1个2分休止符
C1 = chord(['C', 'D', rest(1/2), 'E'])
>>> C1
[C4, D4, E4] with interval [0, 0.5, 0]
C1 = C('Cmaj7')
C2 = C('Dmaj7')
C3 = C1 | rest(1/2) | C2
# 或者使用数字
# C3 = C1 | 1/2 | C2
>>> C3
[C4, E4, G4, B4, D4, F#4, A4, C#5] with interval [0, 0, 0, 0.75, 0, 0, 0, 0]
这是一个数据结构,代表音乐理论中的节拍,在一段节奏中使用。
一个节拍实例保存一个单一的节拍,有一个持续时间和一个附点数(如果它是一个附点节拍的话)。
你可以用一个节拍实例的列表组成一个完整的节奏。
beat(duration=1/4,
dotted=None)
- duration: 节拍的持续时间,以小节为单位(没有带点的数字)
- dotted:节拍的附点数,如果它是None,则该节拍不是一个附点节拍。
你可以使用节拍实例的get_duration
方法,通过应用附点数来获得节拍的实际持续时间。
这是一个数据结构,代表乐理中的休止符,可以和节拍类型一起使用,形成一个节奏。
rest_symbol实例采取与beat实例相同的参数。
在实践中,你可以将rest
实例与rest_symbol
实例互换使用,但建议使用rest_symbol
实例来组成节奏,因为有一个continue_symbol
类型与它一起工作,它们的名字最好更标准一些。
这是一个数据结构,它扩展了前一个音符的持续时间和间隔时间,可以和节拍类型一起使用,形成一个节奏。
continue_symbol实例的参数与beat实例相同。
这是一个表示节奏的数据结构,相当于一个节拍,休止符和延续符的列表。
一个节奏实例可以应用于一个和弦实例,以改变和弦实例的音符间隔和音符长度。
你可以把这个数据结构看成是鼓点类型的更高层次的抽象。
你可以在初始化一个节奏实例时设置一个固定的总长度,然后你所设置的节拍的持续时间将根据节拍的长度和数量来平均分配。
你还可以为节奏设置时间符号,如4/4, 3/4, 5/4。
为了简化,有一个节奏模式的语法,它与鼓点模式的语法有些类似,可以帮助你更快地制作一个节奏实例。
你可以将2个节奏实例相加,得到一个新的组合节奏实例,或者将一个节奏实例乘以一个整数,得到一个新的重复节奏实例。
rhythm(beat_list,
total_bar_length=None,
unit=None,
time_signature=None,
Separator=' ')
- beat_list: 一个节拍,休止符和延续符实例的列表,或一个符合节奏模式语法的字符串
- total_bar_length: 节奏的总长度(以小节为单位),如果设置,各拍子的持续时间将被平均分配
- 单位:如果设置,将所有节拍的持续时间设置为该值
- time_signature: 节奏的拍号, 一个[分子, 分母]形式的列表, 如果没有设置, 节奏的拍号将是4/4
- 分隔符: 如果
beat_list
是一个字符串,节拍的分隔字符
用b
代表一个拍子, 0
代表休止符, -
代表延续符。
如果你想设置节拍的持续时间,在节拍符号后使用:
,然后在它后面写上持续时间,持续时间支持与鼓点语法相同的语法。
你可以在节拍符号后加上.
,使节拍变成附点,点可以是一个或多个,作为节拍的附点数。
默认情况下,节拍符号是由空格隔开的。
你可以使用和弦实例的apply_rhythm
方法来应用一个节奏实例,它将根据应用的节奏实例来设置和弦实例的音符间隔和持续时间。如果节奏实例的拍子数与和弦实例的音符数不一致,将选择它们中的最小值作为应用节奏的音符数。这个方法返回一个新的和弦实例。
下面是一个创建节奏实例并将节奏应用到旋律中的例子。
rhythm1 = rhythm('b b b - b b b - b b b - b - b -', 2)
chord1 = chord('C5, D5, E5, E5, F5, G5, E5, D5, C5, A5, G5').apply_rhythm(rhythm1)
>>> print(chord1)
[C5, D5, E5, E5, F5, G5, E5, D5, C5, A5, G5] with interval [0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.25, 0.25]
- Basic syntax of note type
- Basic syntax of chord type
- Basic syntax of scale type
- Basic syntax of piece type
- Basic syntax of track type
- Basic syntax of tempo type
- Basic syntax of pitch_bend type
- Basic syntax of pan type
- Basic syntax of volume type
- Basic syntax of drum type
- Basic syntax of rhythm type
- Musicpy composition code examples Part 1
- Musicpy composition code examples Part 2
- Musicpy composition code examples Part 3
- Introduction of musicpy algorithms module
- The algorithm to split the main melody and chords from a piece of music
- The algorithm to determine the chord type of any group of notes according to the logic of music theory
- The algorithm to analyze the chord progressions of a piece of music
- The algorithm to analyze tonality and modulations in a piece of music