Skip to content

Latest commit

 

History

History
522 lines (407 loc) · 15.8 KB

File metadata and controls

522 lines (407 loc) · 15.8 KB
Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 3 column 1
---
- oeasy Python 0117
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
本教程同步发布在: 

     个人网站: `https://oeasy.org` 
     蓝桥云课: `https://www.lanqiao.cn/courses/3584` 
     GitHub: `https://github.com/overmind1980/oeasy-python-tutorial` 
     Gitee: `https://gitee.com/overmind1980/oeasypython` 
---

二维列表_midi深入_列表_音符四要素_嵌套_embeded

回忆

  • 配套视频
  • 上次 我们了解了 无序
    • 排序 可以 让列表 有序
    • 随机 可以 让列表 无序
  • 无序有序都用
    • 有序带来理性的方便
    • 无序带来混沌的快乐

图片描述

  • 洗牌、码牌、抓牌、看牌
    • 理牌 可以让手里的牌
    • 有适合出牌的次序

图片描述

  • 目前 列表 都是 一维的
    • 可以 升到 有更高维度 吗? 🤔

去游乐场

help(list)
  • 分析append和extend的区别

图片描述

方法名 功能说明
append 追加 列表项
extend 拓展 列表

例子

lst = []
lst.extend([0, 1])
lst
lst.append([0, 1])
lst
  • 效果

图片描述

  • 这还是个列表吗?

观察

type(lst)
len(lst)
lst[0]
lst[1]
lst[2]
lst[3]
  • lst确实还是列表
    • 总共有三个列表项的列表

图片描述

  • 第2个 列表项 是 什么类型 呢?

再观察

lst
lst[2]
type(lst[2])
len(lst[2])
  • 第二个列表项 还是
    • 一个列表
    • 2个元素的 列表

图片描述

  • 可以 继续 索引 吗?

继续索引

lst[2]
lst[2][0]
lst[2][1]
  • 效果

图片描述

  • lst[2] 是 列表
    • 所以 lst是什么

套娃?

  • lst 是 列表的列表

图片描述

  • 容器的容器
    • 不就成了套娃🪆吗?

音高与时值

  • 这种 套娃
    • 我们 之前见过

图片描述

  • 去生成音乐

提问

图片描述

  • 确定之后
    • 再提要求

再提要求

图片描述

结果

  • 得到一个嵌套结构
    • 每个音符
      • 不但 包括 音高
      • 而且 包括 时长
from mido import Message, MidiFile, MidiTrack

# 音符对应字典,按简谱 1 - 7 对应 C 调下的音符(1=C 时的对应)
# 修改核心:将原F调的音符数值调整为C调对应值
NOTE_MAP = {
    '1': 60, '2': 62, '3': 64, '4': 65,
    '5': 67, '6': 69, '7': 71, '_5': 55  # 低音5对应C调低八度的5
}

# 根据简谱和对应时值构建旋律,这里 '-' 表示延长一拍(原旋律结构不变)
MELODY = [
    ['1', 1], ['2', 1], ['3', 1], ['1', 1],  # 两只老虎
    ['1', 1], ['2', 1], ['3', 1], ['1', 1],  # 两只老虎
    ['3', 1], ['4', 1], ['5', 2],  # 跑的快
    ['3', 1], ['4', 1], ['5', 2],  # 跑得快
    ['5', 0.5], ['6', 0.5], ['5', 0.5], ['4', 0.5], ['3', 1], ['1', 1],  # 一只没有眼睛
    ['5', 0.5], ['6', 0.5], ['5', 0.5], ['4', 0.5], ['3', 1], ['1', 1],  # 一只没有尾巴
    ['2', 1], ['_5', 1], ['1', 2],  # 真奇怪
    ['2', 1], ['_5', 1], ['1', 2],  # 真奇怪
]

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

ticks_per_beat = 480  # 标准 MIDI 时钟,每拍 480 ticks
current_time = 0  # 时间增量

for note_info in MELODY:
    note_name = note_info[0]
    beats = note_info[1]
    if note_name == '-':
        # 处理延长音,增加时间增量
        current_time += int(beats * ticks_per_beat)
        continue
    note = NOTE_MAP[note_name]
    duration = int(beats * ticks_per_beat)
    # 发送音符开启(当前时间增量)和关闭(持续时间)
    track.append(Message('note_on', note=note, velocity=64, time=current_time))
    track.append(Message('note_off', note=note, velocity=64, time=duration))
    current_time = 0  # 每个音符独立,无间隔

mid.save('two_tigers_c调.mid')
print("已生成《两只老虎》C 调 MIDI 文件:two_tigers_c调.mid")
  • 想要从c大调改成f大调

修改

图片描述

# 音符对应字典,按简谱 1 - 7 对应 F 调下的音符(1=F 时的对应)
NOTE_MAP = {
    '1': 65, '2': 67, '3': 69, '4': 70,
    '5': 72, '6': 74, '7': 76, '_5': 60
}
  • 除了 音高、时长 之外
    • 音符 还有什么 特征指标吗?

音符 属性

图片描述

要素 定义 影响因素 作用 举例
音高 音符的高低 发声体振动频率 构建旋律与和声框架 钢琴从左到右音高渐高
时长 音符发声持续的时间 发声体振动持续时长 时间上的长短比例 形成节奏 全音符、四分音符
音强 音符发声的强弱程度 发声体振动幅度 情感起伏 动态变化 激昂用强音 轻柔 用弱音
音色 音符声音的特色 发声体的材质、结构、发声方式等 赋予声音独特个性 钢琴清脆明亮 二胡悠扬

修改音强

图片描述

  • 定义了 四档 音强
from mido import Message, MidiFile, MidiTrack

# 音符对应字典(F调)
NOTE_MAP = {'_5': 60, '1': 65, '2': 67, '3': 69, '4': 70, '5': 72, '6': 74, '7': 76}

# 定义强弱规律(四四拍:强=90,弱=50,次强=70,最弱=40)
BEAT_VELOCITY = {
    1: 90,   # 第一拍(强)
    2: 50,   # 第二拍(弱)
    3: 70,   # 第三拍(次强)
    4: 50    # 第四拍(弱)
}

# 带节拍位置的旋律(格式:[音符, 时值, 节拍位置],节拍位置从1开始)
MELODY = [
    # 两只老虎(四四拍,每小节4拍)
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第一小节
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第二小节
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 3],              # 第三小节(第三拍延长2拍,占第3、4拍)
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 3],              # 第四小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 2],  # 第五小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 2],  # 第六小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 3],              # 第七小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 3],              # 第八小节
]

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

ticks_per_beat = 480
current_time = 0
current_beat = 1  # 记录当前节拍位置(每小节从1开始)

for note_info in MELODY:
    note_name, beats, beat_pos = note_info
    if note_name == '-':
        # 处理延长音
        current_time += int(beats * ticks_per_beat)
        current_beat = (current_beat + int(beats)) % 4  # 更新节拍位置(4拍循环)
        continue
    
    # 获取音强(velocity),若节拍位置超出预设,默认中等音量64
    velocity = BEAT_VELOCITY.get(beat_pos, 64)
    note = NOTE_MAP[note_name]
    duration = int(beats * ticks_per_beat)
    
    # 发送音符消息(带音强)
    track.append(Message('note_on', note=note, velocity=velocity, time=current_time))
    track.append(Message('note_off', note=note, velocity=velocity, time=duration))
    
    # 更新节拍位置(每拍前进1,超过4则重置为1)
    current_beat = (beat_pos + int(beats)) % 4 if int(beats) == 1 else (beat_pos + int(beats))  # 处理连音符
    current_time = 0  # 音符间无间隔

mid.save('two_tigers_f调_强弱.mid')
print("已生成带强弱规律的MIDI文件:two_tigers_f调_强弱.mid")

导出效果

  • 每个音符 音强 已经有了变化
    • 次强

图片描述

  • 如果想要改变音强呢?

改变音强

  • 弱起节奏
    • 从 正拍子 改成 反拍子
# 定义强弱规律(四四拍:强=90,弱=50,次强=70,最弱=40)
BEAT_VELOCITY = {
    1: 50,   # 第一拍(弱)
    2: 70,   # 第二拍(强)
    3: 40,   # 第三拍(最弱)
    4: 90    # 第四拍(次强)
}
  • 效果一般

图片描述

修改

  • 修改 映射细节
    • 韵脚 强拍
# 带节拍位置的旋律(格式:[音符, 时值, 节拍位置],节拍位置从1开始)
MELODY = [
    # 两只老虎(四四拍,每小节4拍)
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第一小节
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第二小节
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 4],              # 第三小节(第三拍延长2拍,占第3、4拍)
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 4],              # 第四小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 4],  # 第五小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 4],  # 第六小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 4],              # 第七小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 4],              # 第八小节
]
  • 还想改变 音色

音色

图片描述

编码 对应乐器 编码 对应乐器
0 大钢琴(声学钢琴) 64 合成铜管2
1 明亮的钢琴 65 高音萨克斯
2 电钢琴 66 中音萨克斯
3 酒吧钢琴 67 次中音萨克斯
4 柔和的电钢琴 68 上低音萨克斯
5 加合唱效果的电钢琴 69 双簧管
6 羽管键琴(拨弦古钢琴) 70 英国管
7 科拉维科特琴(击弦古钢琴) 71 大管
8 钢片琴 72 单簧管
9 钟琴 73 短笛
10 八音盒 74 长笛
11 颤音琴 75 竖笛
12 马林巴 76 排笛
13 木琴 77 吹瓶口
14 管钟 78 尺八
15 大扬琴 79
16 击杆风琴 80 洋埙
17 打击式风琴 81 合成主音1(方波)
18 摇滚风琴 82 合成主音2(锯齿波)
19 教堂风琴 83 合成主音3(汽笛风琴)
20 簧管风琴 84 合成主音4(吹管)
21 手风琴 85 合成主音5(吉他)
22 口琴 86 合成主音6(人声)
23 探戈手风琴 87 合成主音7(五度)
24 尼龙弦吉他 88 合成主音8(低音加主音)
25 钢弦吉他 89 合成柔音1(新时代)
26 爵士电吉他 90 合成柔音2(暖音)
27 清音电吉他 91 合成柔音3(复合成)
28 闷音电吉他 92 合成柔音4(合唱)
29 加驱动效果的电吉他 93 合成柔音5(弓弦)
30 加失真效果的电吉他 94 合成柔音6(金属)
31 吉他和音 95 合成柔音7(光环)
32 大贝司(声学贝司) 96 合成柔音8(扫弦)
33 电贝司(指弹) 97 合成特效1(雨)
34 电贝司(拨片) 98 合成特效2(音轨)
35 无品贝司 99 合成特效3(水晶)
36 掌击贝司1 100 合成特效4(大气)
37 掌击贝司2 101 合成特效5(亮音)
38 电子合成贝司1 102 合成特效6(小妖)
39 电子合成贝司2 103 合成特效7(回声)
40 小提琴 104 合成特效8(科幻)
41 中提琴 105 锡塔尔
42 大提琴 106 班卓
43 低音大提琴 107 三味线
44 弦乐群颤音音色 108
45 弦乐群拨弦音色 109 卡林巴
46 竖琴 110 风笛
47 定音鼓 111 古提琴
48 弦乐合奏音色1 112 唢呐
49 弦乐合奏音色2 113 铃铛
50 合成弦乐1 114 拉丁打铃
51 合成弦乐2 115 钢鼓
52 合唱“啊”音 116 木块
53 人声“嘟”音 117 太鼓
54 合成人声 118 嗵鼓
55 乐队打击乐 119 合成鼓
56 小号 120 镲波形反转
57 长号 121 磨弦声
58 大号 122 呼吸声
59 弱音小号 123 海浪声
60 圆号 124 鸟鸣声
61 铜管组 125 电话铃声
62 合成铜管1 126 /

选择小号

  • 第33行
    • 将 乐器
      • 从 1(钢琴🎹)
      • 修改为 56(小号🎺)
from mido import Message, MidiFile, MidiTrack

# 音符对应字典(F调)
NOTE_MAP = {'_5': 60, '1': 65, '2': 67, '3': 69, '4': 70, '5': 72, '6': 74, '7': 76}

# 定义强弱规律(四四拍:强=90,弱=50,次强=70,最弱=40)
BEAT_VELOCITY = {
    1: 50,   # 第一拍(弱)
    2: 70,   # 第二拍(强)
    3: 40,   # 第三拍(最弱)
    4: 90    # 第四拍(次强)
}

# 带节拍位置的旋律(格式:[音符, 时值, 节拍位置],节拍位置从1开始)
MELODY = [
    # 两只老虎(四四拍,每小节4拍)
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第一小节
    ['1', 1, 1], ['2', 1, 2], ['3', 1, 3], ['1', 1, 4],  # 第二小节
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 3],              # 第三小节(5占第3、4拍,节拍位置修正为3)
    ['3', 1, 1], ['4', 1, 2], ['5', 2, 3],              # 第四小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 3],  # 第五小节
    ['5', 0.5, 1], ['6', 0.5, 2], ['5', 0.5, 3], ['4', 0.5, 4], ['3', 1, 1], ['1', 1, 3],  # 第六小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 3],              # 第七小节
    ['2', 1, 1], ['_5', 1, 2], ['1', 2, 3],              # 第八小节
]

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

# 核心修改:添加小号音色选择(MIDI乐器编号:56 = 小号(Trumpet),编号从0开始)
# 乐器编号参考:0=钢琴, 56=小号, 60=萨克斯, 64=长号
track.append(Message('program_change', program=55, time=0))  # 55是小号的MIDI编号(0-based)

ticks_per_beat = 480
current_time = 0
current_beat = 1  # 记录当前节拍位置(每小节从1开始)

for note_info in MELODY:
    note_name, beats, beat_pos = note_info
    
    if note_name == '-':
        # 处理延长音
        current_time += int(beats * ticks_per_beat)
        # 正确更新节拍位置(模4后若为0则等于4)
        current_beat = (current_beat + int(beats)) % 4
        if current_beat == 0:
            current_beat = 4
        continue
    
    # 获取对应节拍的音量强度
    velocity = BEAT_VELOCITY.get(beat_pos, 64)
    note = NOTE_MAP[note_name]
    duration = int(beats * ticks_per_beat)
    
    # 发送音符开启/关闭消息(带指定音量)
    track.append(Message('note_on', note=note, velocity=velocity, time=current_time))
    track.append(Message('note_off', note=note, velocity=velocity, time=duration))
    
    # 修正节拍位置更新逻辑,确保循环正确
    current_beat = (beat_pos + int(beats)) % 4
    if current_beat == 0:
        current_beat = 4
    current_time = 0  # 音符间无间隔

mid.save('two_tigers_f调_强弱_小号.mid')
print("已生成带强弱规律的小号音色MIDI文件:two_tigers_f调_强弱_小号.mid")
  • 试着改成二胡
    • 或者 尝试 配器

总结

  • 这次了解了 音符四要素
要素 定义 影响因素 作用 举例
音高 音符的高低 发声体振动频率 构建旋律与和声框架 钢琴从左到右音高渐高
时长 音符发声持续的时间 发声体振动持续时长 时间上的长短比例 形成节奏 全音符、四分音符
音强 音符发声的强弱程度 发声体振动幅度 情感起伏 动态变化 激昂用强音 轻柔 用弱音
音色 音符声音的特色 发声体的材质、结构、发声方式等 赋予声音独特个性 钢琴清脆明亮 二胡悠扬
  • 设置好 音轨的音色之后

图片描述

  • 每个音符 都有自己的四要素
    • 音色
    • 音高
    • 时值
    • 音强
  • 二维列表 还能怎么玩呢?🤔
  • 下次再说 👋
  • 配套视频

  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。