[oeasy]python094_使用python控制音符列表_midi_文件制作

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 本文介绍了如何使用Python控制音符列表制作MIDI文件。首先回顾了列表下标索引(正数和负数)的用法,接着通过`mido`库实现MIDI文件生成。以《两只老虎》为例,详细解析了代码逻辑:定义音高映射、构建旋律列表、创建MIDI文件框架,并将音符插入音轨。还探讨了音符时值与八度扩展的实现方法。最终生成的MIDI文件可通过不同平台播放或编辑。总结中提到,此技术可用于随机生成符合调性的旋律,同时引发对列表其他实际应用的思考。

使用python控制音符列表_midi_文件制作

回忆

  • 上次了解了 列表的下标索引
  • 可以是 正数
  • 也可以是 负数
0 1 2 3 4
o e a s y
-5 -4 -3 -2 -1
  • 2个函数 可以
  • 根据 列表项
  • 找到 索引位置
函数 输入 输出 区别
index 列表项 索引值 找不到 就报ValueError
find 列表项 索引值 找不到 不报错 返回-1
  • 这列表 还有什么 好玩的 吗?🤔

midi文件

  • 我想生成 可以播放的mid文件

  • 先进入 Code文件夹
cd Code
  • 便于 生成mid 的 下载

制作midi

  • 先来个 《两只老虎》的 mid

构建环境

pip install mido
  • 先安装需要的包mido

复制代码

from mido import Message, MidiFile, MidiTrack
# 定义简谱数字与MIDI note number的映射(以C调为例)
note_mapping = {
    1: 60,  # do
    2: 62,  # re
    3: 64,  # mi
    4: 65,  # fa
    5: 67,  # sol
    6: 69,  # la
    7: 71   # si
}
# 《两只老虎》的简谱音符序列
tune_notes = [
    1, 2, 3, 1,
    1, 2, 3, 1,
    3, 4, 5, 
    3, 4, 5,
    5, 6, 5, 4, 3, 1,
    5, 6, 5, 4, 3, 1,
    2, 5, 1,
    2, 5, 1
]
# 定义每个音符的拍子(这里假设每个音符为1拍,可根据需要调整)
beat_per_note = 1
# 定义每拍的tick数(MIDI文件中时间的基本单位)
ticks_per_beat = 480
# 创建MIDI文件和音轨
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
# 设置乐器为钢琴(program number 0)
track.append(Message('program_change', program=0, time=0))
# 遍历音符序列,生成MIDI消息
for note in tune_notes:
    if note in note_mapping:
        note_num = note_mapping[note]
        # 音符开启消息
        track.append(Message('note_on', note=note_num, velocity=64, time=0))
        # 计算音符持续的tick数
        tick_duration = int(beat_per_note * ticks_per_beat)
        # 音符关闭消息
        track.append(Message('note_off', note=note_num, velocity=64, time=tick_duration))
# 保存MIDI文件
mid.save('两只老虎.mid')

保存并运行

  • 编辑m.py
vi m.py
  • "+p 将代码粘过来

  • 运行得到结果

下载试听

  • 点击侧边栏
  • 下载代码
  • 把Code文件夹
  • 打包下载

  • 下载之后 双击解压

windows下打开mid

  • 在windows系统中
  • 用媒体播放器 打开

mac下打开mid

  • mac使用库乐队打开

  • 在蓝桥上 直接看看
  • 这mid 长
  • 可以 吗?

安装midi软件

sudo apt update
yes | sudo apt install rosegarden
  • 安装jackd2
  • 可以降低延迟

  • 装好 就可以运行了

rosegarden

  • 可以在开始菜单中
  • 找到 rosegarden 点击

  • 也可以在 终端 直接运行
rosegarden &

运行效果

  • 出现 软件的界面

打开midi

  • 运行之后
  • 文件 - 导入
  • 两只老虎.mid

  • 在音轨上
  • 右键 查看 钢琴卷帘

钢琴卷帘效果

  • midi文件长成这个样子
  • 一段段 的 黄色格子
  • 对应着 一个个音符
  • 可以 上下左右 移动

  • 音符的 上下位置 代表什么?

音高

  • 上下位置 代表 音高
  • 钢琴卷帘 总是 循环
  • 一会儿 两个黑键 像筷子
  • 一会儿 三个黑键 像叉子
  • 筷子 下面的 是
  • 123
  • 叉子 下面的 是
  • 4567

  • 这七个音 1234567 音高
  • 构成了 大调音阶
  • 大调音阶 写进代码 了吗?

分析代码

  • 将 大调音阶
  • 编码为 midi音高

音级对 黑键存在性 半音差
1、2 2
2、3 2
3、4 1
  • 音符 具体是 怎么插入的呢?

旋律列表

  • 音符 放在 旋律列表 里

  • 这 旋律列表 放哪儿 呢?

基础框架

  • 建立 mid文件
  • 建立 mid音轨
  • 将 音轨 附加进 mid文件
  • 再把 旋律列表中的音符
  • 放到 音轨 上
# 创建MIDI文件和音轨
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
# 设置乐器为钢琴(program number 0)
track.append(Message('program_change', program=0, time=0))
  • 怎么把 旋律列表中的音符 到 音轨 上呢?

生成旋律

# 《两只老虎》的简谱音符序列
tune_notes = [
    1, 2, 3, 1,
    1, 2, 3, 1,
    3, 4, 5, 
    3, 4, 5,
    5, 6, 5, 4, 3, 1,
    5, 6, 5, 4, 3, 1,
    2, 5, 1,
    2, 5, 1
]
  • 遍历 旋律列表
  • 每个列表项 都是 音符音高
  • 将 音符 按次序 放入 音轨
# 遍历音符序列,生成MIDI消息
for note in tune_notes:
    if note in note_mapping:
        note_num = note_mapping[note]
        # 音符开启消息
        track.append(Message('note_on', note=note_num, velocity=64, time=0))
        # 计算音符持续的tick数
        tick_duration = int(beat_per_note * ticks_per_beat)
        # 音符关闭消息
        track.append(Message('note_off', note=note_num, velocity=64, time=tick_duration))
# 保存MIDI文件
mid.save('两只老虎.mid')
  • 目前 音符的 音高 没有问题
  • 但是 时值有问题

继续提要求

  • 让 ai 根据 简谱照片
  • 生成 midi文件

  • 根据 图片
  • 从C大调 变成 F大调

文件分析

  • 主音 变了
  • 从 1 = C (60)
  • 变成了 1 = F(65)
  • 改从 65 开始 了
  • 每个音符
  • 除了 音高
  • 还增加了 时值
from mido import Message, MidiFile, MidiTrack
# 音符对应字典,按简谱 1 - 7 对应 F 调下的音符(1=F 时的对应)
NOTE_MAP = {
    '1': 65, '2': 67, '3': 69, '4': 70,
    '5': 72, '6': 74, '7': 76
}
# 根据简谱和对应时值构建旋律,这里 '-' 表示延长一拍
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],  # 真奇怪
    ['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_f调.mid')
print("已生成《两只老虎》F 调 MIDI 文件:two_tigers_f调.mid")
  • 运行起来 听听效果
  • 最后的 两个 So(5)
  • 应该是 是 低音So(5)

提问

增加音阶

  • 低音So(5) 要求 能够表示出
  • 不同八度 的 音阶
from mido import Message, MidiFile, MidiTrack
# 音符对应字典,按简谱 1 - 7 对应 F 调下的音符(1=F 时的对应)
# 扩展加入高低音的映射
NOTE_MAP = {
    '1': 65, '2': 67, '3': 69, '4': 70,
    '5': 72, '6': 74, '7': 76,
    '1_': 53, '2_': 55, '3_': 57, '4_': 58,
    '5_': 60, '6_': 62, '7_': 64,  # 低音
    '1^': 77, '2^': 79, '3^': 81, '4^': 82,
    '5^': 84, '6^': 86, '7^': 88  # 高音
}
# 根据简谱和对应时值构建旋律,这里 '-' 表示延长一拍
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],  # re so do,so 改为低音 so
    ['2', 1], ['5_', 1], ['1', 2]  # re so do,so 改为低音 so
]
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_f调.mid')
print("已生成《两只老虎》F 调 MIDI 文件:two_tigers_f调.mid")

最终mid

总结

  • 这次研究了 midi音乐的制作
  • 使用 旋律列表 生成音乐
# 《两只老虎》的音符序列
melody = [
    '1', '2', '3', '1',...
]
  • 这有什么用呢?
  • 比如 按照调性  生成 随机旋律?🤔

  • 除了 midi音乐 旋律列表 之外
  • 列表 还有 实际应用 吗???🤔
相关文章
|
19天前
|
测试技术 数据处理 Python
Python列表推导式:简洁高效的数据处理利器
Python列表推导式:简洁高效的数据处理利器
134 80
|
5月前
|
机器学习/深度学习 存储 算法
解锁文件共享软件背后基于 Python 的二叉搜索树算法密码
文件共享软件在数字化时代扮演着连接全球用户、促进知识与数据交流的重要角色。二叉搜索树作为一种高效的数据结构,通过有序存储和快速检索文件,极大提升了文件共享平台的性能。它依据文件名或时间戳等关键属性排序,支持高效插入、删除和查找操作,显著优化用户体验。本文还展示了用Python实现的简单二叉搜索树代码,帮助理解其工作原理,并展望了该算法在分布式计算和机器学习领域的未来应用前景。
|
3月前
|
人工智能 Python
[oeasy]python089_列表_删除列表项_remove_列表长度_len
本文介绍了Python列表(list)的操作方法,重点讲解了`remove`方法的使用。通过实例演示如何删除列表中的元素,探讨了`ValueError`异常产生的原因,并分析了时间复杂度O(n)的概念。同时提及了`clear`方法清空列表的功能及`len`函数获取列表长度的用法。最后以购物清单为例,展示列表的实际应用场景,并预告快速生成列表的方法将在后续内容中介绍。
153 62
|
1月前
|
JSON API 开发者
深入浅出:拼多多商品列表API接口Python攻略
拼多多是中国领先的社交电商平台,为开发者提供了丰富的API接口,用于集成商品数据到第三方应用。通过Python可以轻松调用这些API获取商品列表。主要步骤包括:1) 安装必要库(如`requests`);2) 使用AppKey和AppSecret获取访问令牌;3) 调用商品列表API,传入参数如商品ID、页码等;4) 解析返回的JSON数据提取所需信息。按照此流程,开发者能够快速实现与拼多多平台的数据交互。
|
3月前
|
Python
使用Python实现multipart/form-data文件接收的http服务器
至此,使用Python实现一个可以接收 'multipart/form-data' 文件的HTTP服务器的步骤就讲解完毕了。希望通过我的讲解,你可以更好地理解其中的逻辑,另外,你也可以尝试在实际项目中运用这方面的知识。
202 69
|
1月前
|
人工智能 前端开发 数据处理
如何将Python元组转换为列表
本文介绍了在Python中将元组转换为列表的方法。通过内置的`list()`函数,可以轻松地将一个元组转换为列表。此外,文章还提供了如何使用列表推导式和`itertools.chain()`方法将包含多个元组的列表展平为单一列表的示例。这些方法对于数据处理和转换非常实用,能够帮助开发者高效操作序列类型数据。文中附有代码实例及输出结果,便于理解与实践。
|
3月前
|
人工智能 索引 Python
[oeasy]python091_列表_索引_index_中括号_索引函数
本文介绍了Python中列表与字符串的索引及index函数用法。通过range生成列表,使用索引[]访问和修改列表元素,index函数查找元素位置。字符串支持索引访问但不可直接修改。还探讨了16进制数在Python中的表示方法,以及日期、月份等特殊字符的Unicode范围。最后总结了列表与字符串操作的区别,并预告后续内容,提供蓝桥云课、GitHub和Gitee链接供进一步学习。
85 20
|
3月前
|
Python 容器
[oeasy]python090_列表_构造_范围_range_start_end_step_步长
本文介绍了Python中列表的生成方法,重点讲解了`range()`函数的使用。通过`range(start, stop, step)`可生成一系列整数,支持正负步长,但不支持小数参数。文章从基础的列表追加、直接赋值到复杂的应用场景(如生成等宽字体的月份列表),结合实例演示了`range()`的灵活性与实用性。最后总结了`range()`的关键特性:前闭后开、支持负数步长,并提供了进一步学习的资源链接。
124 15
|
3月前
|
Shell 开发者 Docker
Python文件打包:一站式指南
本文深入探讨Python文件打包的各种方法,从基础的zip和tar工具到高级的setuptools、PyInstaller、cx_Freeze等,涵盖Docker镜像、虚拟环境及自包含可执行文件的打包方式。通过示例代码与详细解析,帮助开发者根据项目需求选择合适的打包方案,提升代码分发与部署效率。内容全面,适合各水平读者学习参考。
214 7

推荐镜像

更多