利用Python进行音乐编曲

利用Python进行音乐编曲

一、实现方法

1.Python的musicpy库(简单易行好操作)

Musicpy可以让你用非常简洁的语法来表达一段音乐的音符,和弦,旋律,节奏,力度等信息,可以通过乐理逻辑来生成曲子,并且进行高级的乐理操作。你可以很容易地把musicpy代码输出为MIDI文件的格式,也可以很简单地加载MIDI文件并且转换为musicpy的数据结构进行高级乐理的操作。musicpy的语法设计非常地简洁与灵活,因此musicpy的代码的可读性比较强,并且musicpy和python完全兼容,因此可以写python代码和musicpy进行互动。

2.一些基础的乐理知识

3.一个有趣的想法(bushi)

二、关于musicpy的说明

musicpy的内容十分丰富,我们首先要了解它的数据结构、基本语法,才能更加有效并且方便地使用它。

1.关于数据结构

在这里仅介绍几个常用的数据结构:

1)音符(note)

初始化一个note的实例只需要给一个音名(比如C, Eb, A#,也可以是小写)和一个八度数(一个整数),现在你就可以使用这 个音符去做音乐里的任何事情了。你还可以设定音符的duration(音符长度)和volume(音符的音量大小)。音符长度默认为0.25 (1/4个小节),音量默认为100。

note的格式如下:

note(name,
 num=4,
 duration=0.25,
 volume=100,
 channel=None)

·name:为一个表示音名的字符串(如C,D,E#等)

·num:八度数,和音名一起确定一个音高

·duration: 音符的长度,单位为小节,比如duration = 1 表示音符长度为1个小节,默认值为0.25

·volume: 音符的力度,对应的是MIDI文件里的音符的力度,范围从0到127,默认值为100

·channel: 音符的MIDI通道编号,在写入MIDI文件时如果channel不为None,则写入对应的通道

2)和弦(chord)

在musicpy里,和弦类被定义为“一组音符的集合”,这个定义或许比乐理里面的和弦定义更为广义化,因为按照这个定义,一首完整的乐曲也可以完全装进和弦类里面,在这个库里也确实可以。

初始化一个和弦实例,只需要给一个音符的列表即可。还可以设定duration(所有音符的长度设置),interval(音符之间的间隔,用列表表示)。在给定音符列表时无需先用note类初始化,而只需要直接写音符的名字(字符串)就行了。

chord的格式如下:

chord(notes,
 duration=None,
 interval=None,
 rootpitch=4,
 other_messages=[],
 start_time=None)

·notes: 音符列表,为一个记载着这个和弦(或者曲子)所有音符的列表

·duration: 和弦的每个音符各自的音符长度,默认值为None,如果为None则按照音符本身的长度,如果为一个整数,小数或者列表则对音符的长度进行调整

·interval: 每两个连续音符之间的间隔,单位为小节,为一个记载着音符间隔的列表(如果在初始化时是整数或小数,则设定为全部的间隔都为此整数或小数),默认值为None,如果没有特别指定间隔,则默认全部的音符间隔为0

·rootpitch: 如果传入的音符列表的元素不是音符类型,而是表示音符的字符串,则会尝试用toNote函数转化为音符类型,如果音符字符串没有八度数,只有音名的情况下,会使用rootpitch来当做音符的八度数,默认值为4

·other_messages: 用来记录其他的MIDI信息的列表,默认值为空列表

·start_time: 和弦类型的开始时间,可以理解为从开头到第一个音符开始之间的休止符,单位为小节,默认值为0

3)音阶(scale)

这个类可以表示一个特定的音阶,使用这个类可以快速按照音的间隔来构建调式。

scale的格式如下:

scale(start=None,
 mode=None,
 interval=None,
 name=None,
 notels=None,
 pitch=4)

·start: 音阶的主音(起始音) ,为一个音符类

·mode: 音阶的名字,比如major, minor, dorian, lydian等等

·interval: 音阶内的音程关系,1表示半音,2表示全音,3表示增二度,以此类推,为一个列表的形式

·name: 当直接输入interval而不输入mode构建音阶的时候,作为音阶的名字来使用

·notels: 音符的列表,可以参考和弦类的音符列表,一个音阶本身是一组确定的音符,因此notels就是一个音阶类里的所有音符

·pitch: 当start(音阶的主音)在构建音阶的时候只有音名,没有八度数,pitch就作为音阶的主音的八度数,默认值为4

2.关于基本语法

实现musicpy编曲,我们需要了解一些实现乐曲代码化的基本语法,主要介绍和弦和乐曲的重点部分。

1)和弦类型基本语法

Ⅰ.对一个和弦进行音符间隔和音符长度的设置

比如现在有一个和弦A,想要设置其音符间隔都为1个小节,音符长度都为1.5个小节,那么可以使用和弦类的内置方法set,set的第一个参数是音符长度duration,第二个参数是音符间隔interval

A.set(1.5, 1)

得到的是和弦A的音符长度全部设置为1.5,音符间隔全部设置为1的全新的和弦类型,请注意不是修改和弦A的内部属性,而是直接返回一个全新的和弦,这个和弦的内部属性为和弦A修改过后的值。如果对于每个音的音符长度或者每个音符间隔都有不同的设定,那么可以传一个列表进去,比如

A.set([0.5, 0.5, 1, 1], [1, 1, 2, 2])

返回的是一个音符长度分别为0.5, 0.5, 1, 1 (单位为小节),音符间隔分别为1, 1, 2, 2 (单位为小节)的和弦。

进阶写法:

A % (1.5, 1)

Ⅱ.根据和弦根音和和弦名获得一个和弦

介绍一个很方便的函数:getchord,这个函数可以让你得到你想要的类型的和弦,只需给定和弦的根音和和弦类型即可。由于这个函数的名字相对有点长,所以用chd这个名字(chord的缩写)也可以调用getchord。比如:

Am7 = chd('A', 'm7')

这样我们就创建了一个A的小七和弦。其表示为

[A5, C6, E6, G6] with interval [0, 0, 0, 0]

当一个和弦的interval全部为0的时候,这个和弦就是所有音符一起同时演奏。如果某个interval为0,表示的是两个音同时一起开始弹。

Ⅲ.输入音符名称,音符长度和音符间距构建和弦

比如我们想构建一个大七和弦,根音为C5,音符间距为每个音都相差一个小节,每个音符的长度都为两个小节,那么可以这么写:

chord(['C5','E5', 'G5', 'B5'], interval=1, duration=2)

如果音符间距和音符长度不是一样的,那么可以传一个列表进去,比如:

chord(['C5','E5', 'G5', 'B5'], interval=[0.5, 0.5, 0, 2], duration=[1, 2, 0.5, 1])

Ⅳ.和弦的表示

在musicpy里,一个和弦类型的表示为:

[音符1, 音符2, 音符3, ...] with interval [间隔1, 间隔2, 间隔3, ...]

比如A是一个C大七和弦,根音为C5,

A = chd('C5', 'maj7')

和弦A的表示为

[C5, E5, G5, B5] with interval [0, 0, 0, 0]

2)乐曲类型基本语法

Ⅰ.piece类型(乐曲类型),用来处理多个音轨以及每个音轨有自己的乐器的曲子

用piece函数构建一个乐曲类型:

A1 = C('Cmaj7') % (1, 1/8)
B1 = chord('A2') % (1,)
C1 = chord('C,D,E,F,G') % (1/8, 1/8)
D1 = chord('C5')

new_piece = piece([A1, B1, C1, D1],
 ['Acoustic Grand Piano', 'Electric Bass (finger)', 'Orchestral Harp', 'Synth Drum'],
 150,
 [0, 2, 2, 4],
 ['piano', 'bass', 'harp', 'drum'])

使用build函数,可以用另一种语法来构建乐曲类型,这种语法是把一个音轨的所有信息放在一个列表作为第1个轨道,下一个音轨的所有信息放在下一个列表作为第2个轨道, 以此类推。

build函数可以接收任意多个音轨的列表,曲速(BPM)默认值为120,指定曲速必须用关键字参数bpm,其他的乐曲类型的参数也可以通过关键字参数指定,比如可以写成:

new_piece = build([A1, 'Acoustic Grand Piano', 0, 1, 'piano'],
 [B1, 'Electric Bass (finger)', 2, 2, 'bass'],
 [C1, 'Orchestral Harp', 2, 3, 'harp'],
 [D1, 'Synth Drum', 4, 4, 'drum'],
 bpm=150)

除此之外,build函数也可以接收音轨类型作为参数构建乐曲类型。音轨类型和音轨信息的列表可以混合输入。

你也可以传入一个元素为音轨类型或者音轨信息的列表(可以混合)的列表,同样也可以构建乐曲类型。

# 使用多个音轨类型构建乐曲类型
new_piece = build(track(A1, instrument='Acoustic Grand Piano', start_time=0, channel=1, track_name='piano'),
 track(B1, instrument='Electric Bass (finger)', start_time=2, channel=2, track_name='bass'),
 track(C1, instrument='Orchestral Harp', start_time=2, channel=3, track_name='harp'),
 track(D1, instrument='Synth Drum', start_time=4, channel=4, track_name='drum'),
 bpm=150)

# 使用一个音轨类型的列表构建乐曲类型
new_piece = build([track(A1, instrument='Acoustic Grand Piano', start_time=0, channel=1, track_name='piano'),
 track(B1, instrument='Electric Bass (finger)', start_time=2, channel=2, track_name='bass'),
 track(C1, instrument='Orchestral Harp', start_time=2, channel=3, track_name='harp'),
 track(D1, instrument='Synth Drum', start_time=4, channel=4, track_name='drum')],
 bpm=150)

Ⅱ.piece类型的编辑操作一览
# 首先构建一个乐曲类型,A1, B1, C1, D1都是已经写好的和弦类型
a = piece(tracks=[A1, B1, C1, D1],
 instruments_list=[1, 35, 1, 49],
 bpm=150,
 start_times=[0, 8, 8, 16],
 channels=[0,1,9,2],
 track_names=['piano', 'electric bass', 'drums', 'strings'])

# 如果想要完全重复这个乐曲类型n遍,那么可以写
b = a | n

# 如果想要对这个乐曲类型的所有MIDI通道进行复制粘贴n遍的操作,那么可以写
b = a * n
b = a % n
# 分别对应不同的add模式,可以参考和弦类型的对应的符号化写法的运作逻辑

# 可以使用index值查看一个乐曲类型的某一个音轨的信息,以0作为第1个音轨
# 使用a[n]的语法可以得到第n个音轨类型
>>> a[0]
[track] 
BPM: 150
channel 0 piano | instrument: Acoustic Grand Piano | start time: 0 | [C4, E4, G4, B4] with interval [0, 0, 0, 0]

# 使用a(n)的语法可以得到一个列表,元素依次为第n个MIDI通道的和弦类型,乐器类型,BPM,开始时间,
# 通道编号,音轨名称,通道声相,通道音量
>>> a(0)
[[C4, E4, G4, B4] with interval [0, 0, 0, 0], 'Acoustic Grand Piano', 150, 0, 0, 'piano', [], []]

# 升高/降低整首曲子n个半音,同样也是与音符类型,和弦类型相同的语法,可以使用up/down函数或者+/-的进阶语法
b = a.up()
b = +a
b = a + 2
b = a.down()
b = -a
b = a - 2

# 有时你不想在升高或降低乐曲类型时改变鼓的音轨的音符,当使用乐曲类型的up/down功能时,你可以把参数`mode`设置为1来升高或降低整个乐曲,但鼓的音轨除外,通道号为9的音轨将被视为鼓的音轨。
b = a.up(mode=1)

# 可以往指定的位置添加实时的速度变化
a.add_tempo_change(bpm=100, start_time=None, ind=None, track_ind=None)
# bpm: 想要变化到的速度,单位为BPM
# start_time: 速度变化发生的时间,单位为小节,如果不设置则以ind为准
# ind: 速度变化信息插入的位置,需要和track_ind配合使用
# track_ind: 可以选择在第几个MIDI通道插入速度的信息,以0作为第1个MIDI通道,ind是选择的MIDI通道里放在第几个位置
# 如果ind和track_ind没有设置,那么就默认往第一个MIDI通道的最后添加速度变化的信息

# 可以往指定的位置添加实时的弯音(pitch bend可以模拟出音符的弯音,滑音,颤音等效果)
a.add_pitch_bend(value, start_time=0, channel='all', track=0, mode='cents', ind=None)
# value: 音符的音高变化的量
# start_time: 音符的音高变化发生的时间,单位为小节
# channel: 选择往第几个通道插入pitch bend信息,以0作为第1个MIDI通道,如果为'all'则往所有的MIDI通道插入相同的pitch bend信息
# track: MIDI轨道,一般情况下不用设置
# mode: 弯音信息的单位,之前有详细的说明
# ind: 弯音信息插入的位置,如果start_time有设置则以start_time的位置为准

# 查看乐曲类型个MIDI通道数量
>>> len(a)
4

# 添加新的音轨
a.append(new_track)
# new_track: 新添加的音轨,必须为音轨类型

# 可以使用build函数通过多个音轨类型构建乐曲类型
new_piece = build(track(A1, instrument=1, start_time=0, channel=0, track_name='piano'),
 track(B1, instrument=35, start_time=1, channel=1, track_name='electric bass'),
 track(C1, instrument=1, start_time=8, channel=9, track_name='drums'),
 track(D1, instrument=49, start_time=16, channel=2, track_name='strings'),
 bpm=150)
# 请注意,用build函数传入音轨类型来构建乐曲类型时,得到的乐曲类型的BPM只取决于build函数的bpm这个参数,
# 与传入的音轨类型自带的BPM无关

三、进行编曲

一段前奏

m = S('E major', 3)
r = m%(64516458, 1/2, 0.3/4, 5)
r = [i('omit7')^2 for i in r]
play(r*2, bpm=70, instrument=4)

一曲轻松的氛围音乐

m = C('CM9') @ [1,3,5,2.1,4.1,5.1] % (1, 1/8) | 1/4
n = C('CM9', 4) % (1, 0)
n |= n - 2
n %= 4
n.setvolume(50)
b = chord('C2, A#1') % ([1, 1], [1, 1]) * 4
s = ((~m) | (~m) -2) * 4
t = chord('B4[.16;.16], C5[.16;.16], D5[1;1], C5[.2;.2], E5[.2;.2], D5[1;1], C5[7/8;7/8]')
the_tune = P([s | s-4, b | b-4, n | n-4, t | t-4], [9, 34, 49, 'Violin'], 90,[0, 0, 0, 3+7/8])
play(the_tune)

实际演示效果见视频(intro & relaxing)

四、总结

通过不断的学习,我也可以在短时间内快速上手musicpy库,编写出比较简单的音乐曲子。在当下电脑普及,许多人若是想学习编曲而没有足够的硬件条件支持的话,仅仅通过一台电脑就能实现音乐梦想。相较于复杂的FL Studio,通过编程实现编曲既没有复杂的操作界面,又不需要去掌握各种乐器音色的分配模式,只要有想法,有努力,就能实现。这也是当代科技(编程)与艺术(音乐)的一种有机结合。

五、鸣谢

请大家多多关注musicpy库的作者:Rainbow_Dreamer

bilibili:Rainbow_Dreamer的个人空间哔哩哔哩bilibili

Github:Rainbow-Dreamer/musicpy: Musicpy is a music programming language in Python designed to write music in very handy syntax through music theory and algorithms. (github.com)

版权声明:
作者:Zad
链接:https://www.techfm.club/p/62726.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>