-
-
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 (节奏类型)
- Interval (音程类型)
- chord_type (和弦种类类型)
初始化一个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大七和弦表示出来是这样:
chord(notes=[C5, E5, G5, B5], interval=[0, 0, 0, 0], start_time=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,
default_duration=1 / 4,
default_interval=0,
default_volume=100)
-
notes: 音符列表,为一个记载着这个和弦(或者曲子)所有音符的列表
-
duration: 和弦的每个音符各自的音符长度,默认值为None,如果为None则按照音符本身的长度,如果为一个整数,小数或者列表则对音符的长度进行调整
-
interval: 每两个连续音符之间的间隔,单位为小节,为一个记载着音符间隔的列表(如果在初始化时是整数或小数,则设定为全部的间隔都为此整数或小数),默认值为None,如果没有特别指定间隔,则默认全部的音符间隔为0。请注意,这里的音符间隔的定义是从当前音符的开头到下一个音符的开头之间的间隔。
-
rootpitch: 如果传入的音符列表的元素不是音符类型,而是表示音符的字符串,则会尝试用to_note函数转化为音符类型,如果音符字符串没有八度数,只有音名的情况下,会使用rootpitch来当做音符的八度数,默认值为4
-
other_messages: 用来记录其他的MIDI信息的列表,默认值为空列表
-
start_time: 和弦类型的开始时间,可以理解为从开头到第一个音符开始之间的休止符,单位为小节,默认值为0
-
default_duration: 每个音符的默认长度
-
default_interval: 每个音符的默认间隔
-
default_volume: 每个音符的默认音量
这个类可以表示一个特定的音阶。使用这个类可以快速按照音的间隔来构建调式,比如大调的音的排列是全全半全全全半(全代表全音,半代表半音),那么如果想构建一个C大调音阶,就可以写
scale('C5', interval=[2, 2, 1, 2, 2, 2, 1], mode='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: [M2, M2, m2, M2, M2, M2, m2]
scale notes: [C5, D5, E5, F5, G5, A5, B5, C6]
比如
scale('C5', 'minor')
得到以C5为根音的C小调音阶,
[scale]
scale name: C5 minor scale
scale intervals: [M2, m2, M2, M2, m2, M2, M2]
scale notes: [C5, D5, Eb5, F5, G5, Ab5, Bb5, 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),速度类型的实例必须放在和弦类型的tempos
列表中,设定开始改变的时间,单位为小节。速度类型可以作用于和弦类型,乐曲类型和音轨类型,并且在写入MIDI文件后也会作为MIDI事件存储在MIDI文件中。
在速度类型的基本语法
章节有关于速度类型的详细介绍。
弯音类型可以用来实时地改变某一个片段的音符的音高,可以很细微地变化音高。速度类型的实例必须放在和弦类型的pitch_bends
列表中,设定开始改变的时间,单位为小节。弯音类型可以作用于和弦类型,乐曲类型和音轨类型,并且在写入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
chord(notes=[C4, D4, E4], interval=[0, 1/2, 0], start_time=0)
C1 = C('Cmaj7')
C2 = C('Dmaj7')
C3 = C1 | rest(1/2) | C2
# 或者使用数字
# C3 = C1 | 1/2 | C2
>>> C3
chord(notes=[C4, E4, G4, B4, D4, F#4, A4, C#5], interval=[0, 0, 0, 3/4, 0, 0, 0, 0], start_time=0)
这是一个数据结构,代表乐理中的节拍,在一段节奏中使用。
一个节拍实例保存一个单一的节拍,有一个持续时间和一个附点数(如果它是一个附点节拍的话)。
你可以用一个节拍实例的列表组成一个完整的节奏。
beat(duration=1/4,
dotted=None)
- duration: 节拍的持续时间,以小节为单位(不算附点数)
- dotted: 节拍的附点数,如果它是None,则该节拍不是一个附点节拍
你可以使用节拍实例的get_duration
方法,通过应用附点数来获得节拍的实际持续时间,这个方法也适用于rest_symbol和continue_symbol。
这是一个数据结构,代表乐理中的休止符,可以和节拍类型一起使用,形成一个节奏。
rest_symbol实例采取与beat实例相同的参数。
在实践中,你可以将rest
实例与rest_symbol
实例互换使用,但建议使用rest_symbol
实例来组成节奏,因为有一个continue_symbol
类型与它一起工作,它们的名字最好更标准一些。
rest_symbol(duration=1/4,
dotted=None)
这是一个数据结构,它扩展了前一个音符的持续时间和间隔时间,可以和节拍类型一起使用,形成一个节奏。
continue_symbol实例的参数与beat实例相同。
continue_symbol(duration=1/4,
dotted=None)
这是一个表示节奏的数据结构,相当于一个节拍,休止符和延续符的列表。
一个节奏实例可以应用于一个和弦实例,以改变和弦实例的音符间隔和音符长度。
你可以把这个数据结构看成是鼓点类型的更高层次的抽象。
在节奏类型的基本语法
章节有关于节奏类型的详细介绍。
这是一个表示音程的数据结构,例如乐理中的大三度、全五度。
这些音程存储在database
模块中,每个音程都有多种读取方式,例如缩写,如 P5, M3, m7
(完全五度、大三度、小七度),或全称,如 perfect_fifth, major_third, minor_seventh
(完全五度、大三度、小七度)。
你可以为音符或者和弦加上或者减去音程,这将使音符或和弦的按照给定的音程移动,例如
note1 = N('C4')
>>> note1 + database.m3
Eb4
>> note1 - database.m3
A3
chord1 = C('Cmaj7', pitch=4)
>>> chord1 + database.m3
chord(notes=[Eb4, G4, Bb4, D5], interval=[0, 0, 0, 0], start_time=0)
>>> chord1 - database.m3
chord(notes=[A3, C#4, E4, G#4], interval=[0, 0, 0, 0], start_time=0)
Interval(number, quality, name=None, direction=1)
- number: 度数,应为 1 到 17 之间的整数
- quality: 音程类型,应为['P'、'M'、'm'、'd'、'A'、'dd'、'AA']`之一或者其中之一的叠加
- 名称: 音程的名称
- 方向: 音程的方向,设置为 1 表示正方向,-1 表示负方向
你可以像这样初始化一个音程实例:
interval1 = database.Interval(number=3, quality='M')
>>> interval1
M3
这是一个数据结构,用于详细表示和弦是如何产生的,包括根音、和弦类型、转位、省略、交替、和弦声部、复合和弦等。该数据结构目前主要用于算法模块中的和弦分析功能,用来存储一组音符如何按所需顺序转化为和弦的信息和过程。
root: str = None
chord_type: str = None
chord_speciality: str = 'root position'
inversion: int = None
omit: list = None
altered: list = None
non_chord_bass_note: str = None
voicing: list = None
type: str = 'chord'
note_name: str = None
interval_name: str = None
polychords: list = None
order: list = None
-
root: 和弦的根音
-
chord_type: 和弦的类型,如
maj7
,maj9
-
chord_speciality: 和弦的特殊性,可以是
root position
,inverted chord
,altered chord
,chord voicings
,polychord
中的一种 -
inversion: 和弦的转位数,如果它是一个转位和弦的话
-
omit: 和弦省略的音符
-
altered: 和弦的变化音
-
non_chord_bass_note: 和弦的非和弦低音
-
voicing: 和弦的声部排列
-
type: 区分单音,音程和和弦的类型
-
note_name: 音符名称,如果类型为单音
-
interval_name: 音程名称,如果类型为音程
-
polychords: 和弦种类实例的列表,如果是复合和弦
-
order: 表示和弦派生顺序的整数列表,整数的含义如下:
0: 省略某些音符 1: 改变某些音符 2: 转位 3: 和弦的声部排列 4: 添加一个非和弦低音
使用和弦种类实例的to_text
方法可以获得完整的和弦名称,to_chord
方法可以获得按照记录的信息推算出来的和弦,get_root_position
方法可以获得原位和弦名称。
- 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