• 【mido之架子鼓编曲】




    前言

    midi是基于事件的通用音乐表现格式,是计算机音乐的基础数据结构。mido为用于解析和生成Midi文件的python库,本文以mido库来生成常见的架子鼓节奏。


    一、mido基本操作

    官方示例:

    from mido import Message, MidiFile, MidiTrack
    
    mid = MidiFile() # 创建一个MidiFile对象
    track = MidiTrack() # 创建一个(或多个)MidiTrack对象
    mid.tracks.append(track) # 将track添加到MidiFile对象中
    
    track.append(Message('program_change', program=12, time=0)) # 添加Message对象
    track.append(Message('note_on', note=64, velocity=64, time=32)) # 添加note_on 事件
    track.append(Message('note_off', note=64, velocity=127, time=32)) # 添加note_off 事件
    
    mid.save('new_song.mid') # 保存mid文件
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    基本概念

    message

    message:mido提供的消息类,包含很多的属性和方法。
    其第一个参数为messagetype,列举最常见的3个:
    note_on:音符的开始事件
    note_off:音符的结束事件
    control_change:更改乐器
    参数note:音符的音高。60代表中央C,每增加12,音高升高一个八度
    参数velocity:音符的音量,取值在0-127之间
    time:音符的演奏时间,这里要重点留意,因为它不是单纯的每个音符的持续时间。
    官方解释:In MIDI file tracks, it is used as delta time (in ticks), and it must be a non-negative integer.
    意思是它是一个增量时间,代表上一个音符结束后到此音符的间隔时间。这个时间以tick为单位,而在mido的默认配置中,1拍中有480个tick。所以要想生成一个长度为1拍的音符,应该设置其time值为480,而不是1。后面在同时按下多个音符的时候再举例说明

    meta message

    MetaMessage是mido中另一个重要的类,是用于track的一些基本设置
    time_signature是拍号设置,numerator 为一小节几拍,denominator 为几分音符,如3/4的设置为一小节3拍,4分音符为1拍
    set_tempo是用于设置音乐的节奏快慢,由于这里tempo的单位不是BPM(Beat Per Minute),故一般配合bpm2tempo来使用
    key_signature是用于设置音乐的调式的,C为C大调,若是小调的话仅需要在后面添加小写字母m,如Cm表示C小调

    二、架子鼓mido编曲实现

    1.基本设置

    为了方便操作,定义了一个鼓的演奏类DrusmPlay

    from mido import Message, MidiFile, MidiTrack, bpm2tempo, MetaMessage
    
    class DrusmPlay:
        def __init__(self, track):
            self.bpm = 80
            self.track = track
            self.tempo = bpm2tempo(self.bpm)
            self.meta_time = 480
            # 一拍的时间,用于Message中time的值。用480*n来表示
            self.track.append(MetaMessage('set_tempo', tempo=self.tempo, time=0))
            # 设置节奏快慢
            self.track.append(MetaMessage('time_signature', numerator=4, denominator=4))
            # 拍号设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.midi音色对应表

    用一个字典来保存对应鼓的note,后面用**是我个人觉得在架子鼓演奏中较常用的音色

    self.drum_dict = {
                'acoustic_bass': 35,  # 大鼓
                'bass1': 36,  # 低音鼓 **
                'side_stick': 37,  # 边击
                'acoustic_snare': 38,  # 小鼓(松) **
                'hand_clap': 39,  # 拍手
                'electric_snare': 40,  # 小鼓(紧)
                'low_floor_tom': 41,  # 通鼓(最低) **
                'closed_hi-hat': 42,  # 立镲(闭) **
                'high_floor_tom': 43,  # 通鼓(低) **
                'pedal_hi-hat': 44,  # 踩镲
                'low_tom': 45,  # 通鼓(中低) **
                'open_hi-hat': 46,  # 立镲(开) **
                'low-mid_tom': 47,  # 通鼓(中) **
                'hi-mid_tom': 48,  # 通鼓(中高) **
                'crash_cymbal1': 49,  # 低砸音镲 **
                'high_tom': 50,  # 通鼓(高) **
                'ride_cymbal1': 51,  # 厚吊镲(低) **
                'chinese_cymbal': 52,  # 锣
                'ride_bell': 53,  # 厚吊镲(中)
                'tambourine': 54,  # 铃鼓
                'splash_cymbal': 55,  # 小吊镲
                'cowbell': 56,  # 牛铃
                'crash_cymbal2': 57,  # 薄吊镲(高) 高砸音镲
                'vibraslap': 58,  # 振音梆盒
                'ride_cymbal2': 59,  # 厚吊镲(高)
                'hi_bongo': 60,  # 邦戈鼓(高)
                'low_bongo': 61,  # 邦戈鼓(低)
                'mute_hi_bongo': 62,  # 康加鼓(高闭)
                'open_hi_bongo': 63,  # 康加鼓(高开)
                'low_conga': 64,  # 康加鼓(低)
                'high_timbale': 65,  # 边鼓(高)
                'low_timbale': 66,  # 边鼓(低)
                'high_agogo': 67,  # 拉丁打铃(高)
                'low_agogo': 68,  # 拉丁打铃(低)
                'cabasa': 69,  # 喀吧萨
                'maracas': 70,  # 沙锤
                'short_whistle': 71,  # 哨子(短)
                'long_whistle': 72,  # 哨子(长)
                'short_guiro': 73,  # 刮板(短)
                'long_guiro': 74,  # 刮板(长)
                'claves': 75,  # 响棒
                'hi_wood_block': 76,  # 梆盒(高)
                'low_wood_block': 77,  # 梆盒(低)
                'mute_cuica': 78,  # 拉鼓(闭)
                'open_cuica': 79,  # 拉鼓(开)
                'mute_triangle': 80,  # 三角铁(闭)
                'open_triangle': 81  # 三角铁(开)
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    3.基本节奏的添加

    接下来来实现一个基本的“动次打次”节奏型的演奏,4小节,每小节4拍,8个8分音符来组成:

        def dongcidaci(self):
            for i in range(4):
                # 写4小节
    
                # 第一个八分音符:bass鼓和crash_cymbal同时击下
                self.track.append(
                    Message('note_on', note=self.drum_dict['bass1'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_on', note=self.drum_dict['crash_cymbal1'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['bass1'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['crash_cymbal1'], velocity=64, time=0, channel=9))
                # 留意crash_cymbal1的note_off事件,time是0,代表前一个音符结束也同时结束
    
                # 第二个八分音符:闭镲
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
    
                # 第三个八分音符:军鼓和闭镲同时击下
                self.track.append(
                    Message('note_on', note=self.drum_dict['acoustic_snare'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['acoustic_snare'], velocity=64,
                            time=round(self.meta_time * 0.5), channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
    
                # 第四个八分音符:闭镲
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
    
                # 第五个八分音符:bass鼓和closed_hi-hat同时击下
                self.track.append(
                    Message('note_on', note=self.drum_dict['bass1'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['bass1'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                # 留意crash_cymbal1的note_off事件,time是0,代表前一个音符结束也同时结束
    
                # 第六个八分音符:闭镲
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
    
                # 第七个八分音符:军鼓和闭镲同时击下
                self.track.append(
                    Message('note_on', note=self.drum_dict['acoustic_snare'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['acoustic_snare'], velocity=64,
                            time=round(self.meta_time * 0.5), channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
    
                # 第八个八分音符:闭镲
                self.track.append(
                    Message('note_on', note=self.drum_dict['closed_hi-hat'], velocity=64, time=0, channel=9))
                self.track.append(
                    Message('note_off', note=self.drum_dict['closed_hi-hat'], velocity=64, time=round(self.meta_time * 0.5),
                            channel=9))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    4.生成mid文件

    生成并保存mid文件

    if __name__ == "__main__":
        mid = MidiFile()
        track = MidiTrack()
        mid.tracks.append(track)
    
        dp = DrusmPlay(track)
        dp.dongcidaci()
        mid.save('drums.mid')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、评估

    评估方法:用MuseScore写一段group truch的节奏,用于与上面生成的mid进行对比。

    musescore打谱

    先用musescore打一段4小节4拍的谱:
    在这里插入图片描述

    用musescore对比

    用musescore打开刚刚生成的drums.mid文件,会自动转换成曲谱:
    在这里插入图片描述
    可以看到,两段曲谱是一致的。

    LMMS对比

    也可以通过LMMS软件打开两段mid来对比
    在这里插入图片描述

    四、总结

    本文尝试了mido来生成鼓的基本节奏,可以作为计算机混音,编曲,转录的基础。
    后续工作:
    1.进一步抽象更多的节奏型,简化输入
    2.鼓谱转录的相关工作

    参考:
    https://blog.csdn.net/TruedickDing/article/details/101780003
    https://blog.csdn.net/TruedickDing/article/details/102014762
    https://blog.csdn.net/weixin_43572492/article/details/84987309
    https://blog.csdn.net/X_i_n_w_e_i/article/details/104434139

  • 相关阅读:
    安装docker,docker-compose
    python使用selenium webDriver时 报错
    51单片机ADC模数转换
    实战Docker未授权访问提权
    JVM基础知识(内存区域划分,类加载,GC垃圾回收)
    【LeetCode每日一题:1779. 找到最近的有相同 X 或 Y 坐标的点~~~模拟遍历+曼哈顿距离】
    滤波器::常用滤波器
    堆排序代码模板
    在实际的项目需求中了解技术架构的演进
    vue真实项目还原
  • 原文地址:https://blog.csdn.net/rain2211/article/details/127607539