本篇文章从律学开始,从十二平均律出发,介绍一些基础必要的乐理知识,然后编写python文件,输出和弦音频文件。



乐理知识部分:

一、律学简述(temperament)


  1、概论

  律学,又称“音律学”,是研究律制构成与应用的科学。律学须对音乐所用的音律进行研究。音乐所用的音绝大多数是确定的,律制则是以某特定音程为基础,用数学方法规定的一系列乐音的体系。体系中的每个单位称为“律”;音阶是按照音程关系的一定规格从律制中选择若干律而构成的音列,其中的每个单位称为“音”。“音”与“律”合称“音律”时,除指律制外,兼指作精确规定的所有乐音。 ----百度百科

  

  简而言之,律就是用数学的方法规定各个音高(不止)的振动频率。“律”是构成律制的基本单位。“律”和“音”的概念相近但略有不同,律制中每个单位称为“律”,而音阶中每个单位称为“音”,律制与音阶的关系十分密切。

  2、律的计算

  音律计算法即音程的计算法,使用频率比或音程值(interval value)来表示和计算音程的大小。

  从古至今,律法不断更替。不同的律制由不同的生律法决定。

  音程值(interval value)有四种,分别为对数值、八度值、音分值和平均音程值。

  

  3、五度相生律(circle-of-fifths system)

  五度相生律,规定构成纯五度音程的两个音的频率规定为2:3。这种每隔五度产生一律,继续相生而得各律的做法,称为“五度相生法”。

  其中,由于最大音差的存在,使五度相生律无法在十二律上循环构成各调音阶,即从主音出发,生律十二次(或更多次)并纳入同一八度后,无法回到主音,这对五度相生律的使用造成了一定障碍。

  4、纯律(just intonation)

  。。。

  5、十二平均律(twelve-tone equal temperament)

  十二平均律是把一个八度均分为频率比相等的十二个半音的律制,又称为“十二等比律”。

  接下来我们所有代码都是采用十二平均律的。原因是,钢琴就是基于十二平均律设计的。

  百度百科写道:十二平均律最早是由我国明朝科学家朱载堉发现(1584年)。后通过丝绸之路传至西方。

  1605年荷兰数学家西蒙·斯特芬在一篇未完成的手稿“Van de Spiegheling der singconst”提出用

  计算十二平均律,但因计算精度不够,他算出的弦长数字,有些偏离正确数字一至二单位之多。

  一个八度的频率比为2:1,则十二平均律各律之间的频率比应为:12√2 =1.05946122

  在音乐实践中,当时的音乐家已深知十二平均律的便利之处,各国的作曲家、演奏家都开始使用十二平均律,同时也致力于十二平均律的开发。例如德国的巴赫(J.S.Bach),作有《十二平均律钢琴曲集》二卷,此二卷虽并非只使用了十二平均律(还使用了一些不规则律),但被认为是充分发挥十二平均律的效能,可以自由转调的典范作品。

  6、三种律制的比较

  三种律制各有其优缺点。十二平均律解决了五度相生律和纯律中存在的一些矛盾,例如不断增加律数仍无法回到出发律的矛盾,但十二平均律又会影响音程的和谐性。总体而言,十二平均律将五度相生律和纯律加以调和与折衷,介于两者之间而又更接近五度相生律。十二平均律是目前使用最为广泛的一种律制。

二、基础乐理----p1----乐音体系及分组


  1、音乐体系、音列

  钢琴有88个键。这些乐音加起来的总和叫做--音乐体系。

  从低到高排起来叫做--音列。

  2、音名

  在乐音体系中,每个乐音都有其固定名称,即:音名 。通常用字母C、D、E、F、G、A、B来表示。

  这七个音级也称为基本音级,在钢琴键盘上的位置是固定不变的。

  3、音的分组

  钢琴划分不同的区域。

  钢琴键盘上中央C开始的这一音组称为小字一组,也是“轴心组”。小字一组往右方依次称为小字二组、小字三组、小字四组、小字五组;小字一组往左方依次称为小字组、大字组、大字一组、大字二组

三、基础乐理----p2----五线谱&音符


  1、谱表

  谱表由五线四间构成,用于记录音符高低。由于谱表有五条等距离平行横线,因此称为五线谱。

  2、谱号

  这里简单认识一下高音谱号就好了。除此,还有低音谱号、中音谱号。

  3、音符

  音符由三部分组成:符头(空心或实心的椭圆形符号)、符干(短竖线)和符尾(符干右侧的小弧线)。

  4、休止符

  休止符是用来表示音乐休止、间断的符号。

  在音乐进行中,休止符虽然表示短暂的无声,但此时有着特殊的意义,而且音乐并没有中断,因此休止符是音乐作品中的重要组成部分之一。

  5、拍号

  分子的数字代码一个小节有多少拍,分母的数字代表一几分音符为一拍。读法注意不能读作几分之几拍!

  此外,拍号还明确了旋律的强弱变化规律。

四、基础乐理----p3----音程


  1、音程

  两个音之间音高距离就叫做音程。

  

  音程分为两种:旋律音程、和声音程。

  简而言之,旋律音程是两个音先后发出声响。而和声音程指的是同时发出。

  

  2、旋律音程

  按照发声先后读。

  3、和声音程

  下方的音称为根音,上方的音称为冠音。

  4、协和音程与不协和和音程

  协和音程的音响效果听起来悦耳动听、声音融合,可分为完全协和音程和不完全协和音程。

  完全协和音程包括纯一度、纯四度、纯五度、纯八度;不完全协和音程包括小三度、大三度、小六度、大六度。

  纯八度的音响听起来非常融合,像一个音的声音,也因为过于融合声音听起来会比较空洞。

  纯一、纯四度、纯五度音程比起纯八度的音响效果略显饱满一些,但也显空洞。

  因此,所有纯音程(纯一度、纯四度、纯五度、纯八度)都是完全协和音程。

  不协和音程的音响比较刺耳,听起来紧张、不稳定,如大小二度、大小七度、增四度、减五度等音程。

  不协和音程虽然音响效果尖锐,但是它在音乐作品中也是构成乐曲的重要元素。

  

五、基础乐理----p4----调式


  1、调式

  若干高低不同的乐音,围绕某一具有稳定感的中心音(主音),按照一定关系组织起来所构成的体系,称为调式。现在世界上应用最广泛的调式是大小调式。

  其中,巴赫的《十二平均律》更是大小调式的经典中的经典。

  2、主音

  在调式中,主音是处于核心地位的中心音,其稳定感最强,其他的音都倾向于它。在歌(乐)曲中,主音常出现在强拍、音较长或终止处。

  3、大调式

  大调式简称为“大调”,由七个音级构成。它的主音与Ⅲ级音之间为大三度音程关系,这个大三度也是大调式的特征所在。其中Ⅰ、Ⅲ、Ⅴ级音构成大三和弦,因此大调式的色彩是明亮、辉煌的。

  

  大调式共有三种类型:自然大调、和声大调和旋律大调。

    1、自然大调

    自然大调是大调中用得最多的一种,全部由自然音级构成。

    自然大调音阶由五个全音和两个半音组成,

    

    其音阶结构是:全音-全音-半音-全音-全音-全音-半音,即由大二度、大二度、小二度、大二度、大二度、大二度、小二度音程组成。

    

      C和D之间隔了一个音(黑键),所以CD叫全音。

      E和F之间没有间隔,所以叫半音。

    为了便于熟记,自然大调编成口诀为:全、全、半、全、全、全、半。

    即我们平时说的:de re mi fa sol la si

    C自然大调如下图所示:

    2、和声大调

    在自然大调音阶的基础上,将第Ⅵ级音降低半音,即为和声大调。

    其特征是降Ⅵ级音与Ⅶ级音之间所形成的增二度音程。

    这个增二度即为判断和声大调的标志,也是和声大调的特征所在。

    C和声大调音阶如下图所示:

    3、旋律大调

    在自然大调音阶的基础上,将第Ⅵ级音降低半音,即为旋律大调。

    其特征是降Ⅵ级音与Ⅶ级音之间所形成的增二度音程。

    这个增二度即为判断和声大调的标志,也是和声大调的特征所在。

    C旋律大调音阶如下图所示:

  4、小调式

  小调式简称为“小调”,也是由七个音级构成。

  它的主音与Ⅲ级音之间为小三度音程关系,这个小三度也是小调式的特征所在。其中Ⅰ、Ⅲ、Ⅴ级音构成小三和弦,因此小调式的色彩是柔和、暗淡的。

  小调也有三种类型:自然小调、和声小调和旋律小调。

  

    1、自然小调

    自然小调音阶也由五个全音和两个半音组成,其音阶结构是全音-半音-全音-全音-半音-全音-全音,即由大二度、小二度、大二度、大二度、小二度、大二度、大二度音程组成。

    

    a自然小调如下图所示:

    为了便于熟记,编成口诀为:全、半、全、全、半、全、全。

  

    2、和声小调

    在自然小调音阶的基础上,将第Ⅶ级音升高半音,即为和声小调。

    其特征是Ⅵ级音与升Ⅶ级音之间所形成的增二度音程,并且Ⅶ级音在升高半音之后具有了导音倾向于主音的功能,和自然小调相比紧张度更大。

    a和声小调如下图所示:

    3、旋律小调

    在自然小调音阶的基础上,将自然小调上行音阶中的第Ⅵ级和第Ⅶ级升高半音,下行音阶中再将这两个音还原,即为旋律小调。

    

    a旋律小调如下图所示:

六、基础乐理----p5----调号


  1、调号

  调号作为表示一首歌(乐)曲的调高(即主音高度)的符号,位于每行谱表起首处(谱号之后)或乐曲进行中出现新调的地方。

  调号是用升、降记号来记写的。

  



python编曲部分:

一、认识MIDO库


1、导入mido

from mido import Message,MidiFile,MidiTrack

2、mido基本框架

创建两个mido库的对象:MidiFile() , MidiTrack()

前者用于编辑、生成、输出Midi文件,后者用于midi文件轨道编辑。

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

在轨道对象中添加信息,'program_change'意为切换轨道。

直观理解就是:编辑某条轨道前需要先把轨道选好。(此为官方固定套路,看不懂也无所谓,照着写即可)

track.append(Message('program_change', program=0, time=0))

3、编写音符

选好轨道之后,直接在轨道上添加音符即可。注意,此处的时间单位是毫秒。

track.append(Message('note_on', note=60, velocity=64, time=0))

添加完后再添加一条结束标记,此处才算真正完成一个音符的书写。

track.append(Message('note_off', note=60, velocity=64, time=2000))

添加完以后,就可以直接生成一个midi文件了

mid.save('MyFirstDamnSong.mid')

4、代码

代码如下:

二、结合乐理


1、midi文件频率编号表格:

由上表可知,中央C的midi编号是60 。

2、编写工具库

  1、任务分析

  我们首先编写一个工具库Notes_Toolbox.py,定义一些常用的,根基的东西。

  定义好基础的东西:音程、音名、调式、和弦、节拍、基础音等等。

  然后编写一些简单的方法供调用,比如返回一组和弦、自动转调、通过五线谱得到MIDI频率编号等方法。

  2、定义变量

编号 变量名 Is static? Default value 作用
1
bpm No
125 节拍
2 timePerBeat No 60 / bpm * 100 每拍持续时间(毫秒)
3 base_note static 60 中央C对应MIDI编号
4 note_name[] static
[ 'C','D','E','F','G','A','B']
音名
5 major_notes[] static
 [0, 2, 2, 1, 2, 2, 2, 1]
自然音阶
6 Cmajor_notes[] static <intermediate variable>  
7 Eflatmajor_notes[] static <intermediate variable>  
8 Cmajor{} static
{'C': 60, 'D': 62, 'E': 64, 'F': 65, 'G': 67, 'A': 69, 'B': 71}
C大调字典
9 Eflatmajor{} static
{'C': 63, 'D': 65, 'E': 67, 'F': 68, 'G': 70, 'A': 72, 'B': 74}
E小调字典
... ...
...
...
...

  3、定义函数

编号 方法名 返回 传入参数 作用
1
get_note
MIDI编号
note,group=0,**kw
输入音名与音符区域,返回对应的MIDI编号。(需要改进,没有黑键)
2
get_chord
和弦数组
name,**kw
输入和弦名称,返回和弦数组
3
originToEflatMajor
新E小调MIDI编号数组
list,**kw
输入C大调,返回E小调。(需要改进,支持指定什么调到什么调)
...
...
...
...
...
bpm = 125 #why 125:
#bpm = 1 * 1000 / 8
timePerBeat = 60 / bpm * 1000
base_note = 60 # C4
note_name =[
'C','D','E','F','G','A','B'
] major_notes = [0, 2, 2, 1, 2, 2, 2, 1]
Cmajor_notes = []
Eflatmajor_notes = [] for num in range(12):
Cmajor_notes.append(base_note+sum(major_notes[0:num+1]))
Eflatmajor_notes.append(base_note+3+sum(major_notes[0:num+1]))
#这里只有一个区
Cmajor = dict(zip(note_name,Cmajor_notes))
# Cmajor = {'C':60,'D':62,'E':64,'F':65,'G':67,'A':69,'B':71}
Eflatmajor = dict(zip(note_name,Eflatmajor_notes))
# Eflatmajor = {'C': 63, 'D': 65, 'E': 67, 'F': 68, 'G': 70, 'A': 72, 'B': 74} def get_note(note,group=0,**kw):#Group = 0 means 4Group
global base_note,major_notes
return base_note + group*12 + sum(major_notes[0,note]) def originToEflatMajor(list,**kw):
Ef=[]
for x in list:
Ef.append(x+3)
return Ef #get_note(1,group=0) return 60
#get_note(2,group=0) return 62
def get_chord(name):
chord = {
"Major3":[0,4,7,12],#大三和弦
"Minor3":[0,3,7,12],#小三和弦
"Augmented3":[0,4,8,12],#增三和弦
"Diminished3":[0,3,6,12],#减三和弦 "M7":[0,4,7,11],#大七和弦
"Mm7":[0,4,7,10],#属七和弦
"m7":[0,3,7,10],#小七和弦
"mM7":[],
#...
}
return chord[name]
#get_chord(“Major”) return [0,4,7,12]

3、写一段分解和弦

上面已经写完了工具类模块,接下来就可以专注于和弦的部分了。

  1、编写输出和弦函数

  输出分解和弦到midi文件

  使用此方法时,需要传入:

编号 参数名: 传入: 示例:
1 track  mido库的输出音频接口 MidiTrack()
2 root  音符的名称  'C','D','E','F','G','A','B'
3 name  和弦的名称,在Notes_Toolbox中定义 'Major3'...
4 format  输出分解和弦的方式 [0,1,2] [1,3,2,3]...
5 length  音符持续的时长 4

  直接放上源代码:


def add_broken_chord(root, name, format, length, track, tone_name='Cmajor', root_base=0, channel=0):
#默认是c大调
root_num = Notes_Toolbox.Cmajor
if tone_name == 'Eflat':
root_num = {'C': 63, 'D': 65, 'E': 67, 'F': 68, 'G': 70, 'A': 72, 'B': 74}
root_note = root_num[root] + root_base*12 # 分解和弦的根音 time = (length * 480) / len(format) # 此处为官方文档写法,我也不懂,time指的是音符持续时长
for broken_chord in format: # 通过for循环,逐个输出和弦的音符
note = root_note + Notes_Toolbox.get_chord(name)[broken_chord]
track.append(Message('note_on', note=note,
velocity=60, time=0, channel=channel))
track.append(Message('note_on', note=note, velocity=60,
time=round(time), channel=channel))

  2、调用参考:

format = [0, 1, 2, 3]
add_broken_chord('C', 'Major3', format, 4, track)
add_broken_chord('C', 'Minor3', format, 4, track)
add_broken_chord('C', 'Augmented3', format, 4, track)
add_broken_chord('C', 'Diminished3', format, 4, track)
add_broken_chord('C', 'Diminished3', format, 4, track)

  最后调用保存midi文件即可。



三、规范化代码

为了方便调用,我们对函数的参数顺序做出调整。

另外,重载play_note方法,使其可以接收int类型的note(也就是直接输入MIDI编号)。

同理,所有的有关note的输入都可以进行重载。

def play_note(note,  track, length=1, tone_name='Cmajor', root_base=0, delay=0, velocity=1.0, channel=0):
... def play_note(note:int, track, length=1, tone_name='Cmajor', root_base=0, delay=0, velocity=1.0, channel=0):
... def play_broken_chord(root, name, format, track,length=1, tone_name='Cmajor', delay=0, velocity=1.0,root_base=0, channel=0):
...


四、总结、mido之旅未完待续...

  经过上述学习与实践,浅显地了解了音乐与数学的联系,与一些基础乐理知识。通过代码,实现了各种和弦的输出。

  结合乐理知识,接下来,将进行圈式和弦的书写。


参考文献

[1]. 《音乐理论基础》,李重光编著,人民音乐出版社;

【MIDO】乐理基础 与 python - 从零开始到编写柱式和弦与分解和弦的更多相关文章

  1. Python从零开始编写控制程序(二)

    # Python从零开始编写控制程序(二)前言:终于考完期末了,鸽了很久的远控Python终于有时间更新下了.上篇文章里,我们解决了注册表写入和Python编写为exe程序的问题.那么这篇文章我们来研 ...

  2. D15——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D15 20180926内容纲要: 1.CSS介绍 2.CSS的四种引入方式 3.CSS选择器 4.CSS常用属性 5.小结 6.练习 1 CSS介绍 层叠样式表 ...

  3. D14——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D14 20180919内容纲要: 1.html认识 2.常用标签 3.京东html 4.小结 5.练习(简易淘宝html) 1.html初识(HyperText ...

  4. 用Python从零开始实现K近邻算法

    KNN算法的定义: KNN通过测量不同样本的特征值之间的距离进行分类.它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别.K通 ...

  5. 零基础学python之构建web应用(入门级)

    构建一个web应用 前面的学习回顾: IDLE是Python内置的IDE,用来试验和执行Python代码,可以是单语句代码段,也可以是文本编辑器中的多语句程序. 四个内置数据结构:列表.字典.集合和元 ...

  6. 零基础学习Python数据分析

    网上虽然有很多Python学习的教程,但是大多是围绕Python网页开发等展开.数据分析所需要的Python技能和网页开发等差别非常大,本人就是浪费了很多时间来看这些博客.书籍.所以就有了本文,希望能 ...

  7. python基础26 -----python进程及协成

    一.进程 1.multiprocessing模块实现多进程并发. 1.1multiprocessing包是Python中的多进程管理包,与threading.Thread类似,它可以利用multipr ...

  8. 零基础学习 Python 之前期准备

    写在之前 从今天开始,我将开始新的篇章 -- 零基础学习 Python,在这里我将从最基本的 Python 写起,然后再慢慢涉及到高阶以及具体应用方面.我是完全自学的 Python,所以很是明白自学对 ...

  9. 《零基础学习Python制作ArcGIS自定义工具》课程简介

    Python for ArcGIS Python for ArcGIS是借助Python语言实现ArcGIS自动化行为的综合,它不止是如课程标题所述的“制作ArcGIS自定义工具”,还包括使用Pyth ...

随机推荐

  1. 聊聊C#中的composite模式

    写在前面 Composite组合模式属于设计模式中比较热门的一个,相信大家对它一定不像对访问者模式那么陌生,毕竟谁又没有遇到过树形结构呢.不过所谓温故而知新,我们还是从一个例子出发,起底一下这个模式吧 ...

  2. TopoLVM: 基于LVM的Kubernetes本地持久化方案,容量感知,动态创建PV,轻松使用本地磁盘

    正文 研发测试场景下,一般追求的是一键快速起环境,横向动态复制,一人一套,随起随用,用完即走.作为使用方,其不用关心实际的物理资源是怎样的,环境起在哪里,只要声明自己的使用需求即可.但作为方案构建者以 ...

  3. UiPath官方视频Level2

    [UiPath官方视频Level2]Lesson Orchestrator-第5部分 https://www.bilibili.com/video/av81414017 [UiPath官方视频Leve ...

  4. zabbix监控apache80端口

    1.修改zabbix_agentd.conf 修改# EnableRemoteCommands=0 -->去掉注释修改为1--> EnableRemoteCommands=1 ###允许客 ...

  5. openssl客户端编程:一个不起眼的函数导致的SSL会话失败问题

    我们目前大部分使用的openssl库还是基于TLS1.2协议的1.0.2版本系列,如果要支持更高的TLS1.3协议,就必须使用openssl的1.1.1版本或3.0版本.升级openssl库有可能会导 ...

  6. Python:socket编程教程

    ocket是基于C/S架构的,也就是说进行socket网络编程,通常需要编写两个py文件,一个服务端,一个客户端. 首先,导入Python中的socket模块: import socket Pytho ...

  7. FFT 学习笔记(自认为详细)

    引入 什么是 \(\text{FFT}\) ? 反正我看到 \(\text{wiki}\) 上是一堆奇怪的东西. 快速傅里叶变换(英语:Fast Fourier Transform, FFT),是快速 ...

  8. Tapdata 在“疫”线:携手张家港市卫健委争分夺秒实时抗疫

      "抗疫两年以来最困难的时期,是漫长冬夜还是倒春寒?"--国家传染病医学中心主任张文宏 于3月14日凌晨   "等到疫情结束了,我一定要--",常怀这样的期许 ...

  9. 集合容器和Hash表

    集合容器 集合相当于一个容器,在我们使用Arraylist的时候添加参数相当与放了一个容器中.这里面的元素是可以重复的 在HashSet中添加元素是没有重复的,我们来写一个测试看一下 public s ...

  10. Springboot 启动初始化bin,InitializingBean

    import org.springframework.beans.factory.InitializingBean; @Componentpublic class TestInitializingBe ...