【脑洞手填目标一】通过学习python实现简谱自动转音频的功能

一、目标:数字转音频,保存并可以播放
需求场景:日常生活中发现自己唱歌调很不准,但如果有钢琴演奏每个音符从旁协助就能准确唱对了,由于很多歌并没有专门用琴演奏出演唱的调子,市面上也没有找到符合我需求的工具,刚巧在学习,于是想通过学习强大的mido类库来试试能不能实现自己的需求 。
需求拆分
使用语言工具:
需要实现功能点:word文档扫描,简谱解析,音频转化,生成MIDI文件保存 , 文件可以正常播放 。
功能点实现
1、MIDI文件的生成;
简单生成Do音的DEMO实现代码如下:
import mido# 创建MIDI文件midi_file = mido.MidiFile()# 创建一个声部轨道track = mido.MidiTrack()# 添加轨道midi_file.tracks.append(track)# 设置MIDI音色和速度# program_change确定乐器的类型,0是钢琴track.append(mido.Message('program_change', program=0))#添加音符track.append(mido.Message('note_on', note=60, velocity=64, time=0))#添加该音符的结束标记track.append(mido.Message('note_off', note=60, velocity=64, time=2000))# 保存MIDI文件midi_file.save('output.mid')
此时发现midi文件频率编号有很多 , 一个乐谱对应大量音符节奏等元素,复用率很高,这就需要有个工具类去定义这些常用的音节,于是我百度了下具体音高和MIDI编号对应关系,如下图所示:
根据这个对应关系我单独写了个类方法,通过传参可以转换成对应编号:
def num(yin):if yin == "C2" or yin == "..1" :#低两个八度 doreturn 36if yin == "D2" or yin == "..2":#rereturn 38if yin == "E2" or yin == "..3":#mireturn 40if yin == "F2" or yin == "..4":#fareturn 41if yin == "G2" or yin == "..5":#so 或者说 solreturn 43if yin == "A2" or yin == "..6":#lareturn 45if yin == "B2" or yin == "..7":#sireturn 47if yin == "C3" or yin == ".1":#低八度doreturn 48if yin == "D3" or yin == ".2":#rereturn 50if yin == "E3" or yin == ".3":#mireturn 52if yin == "F3" or yin == ".4":#fareturn 53if yin == "G3" or yin == ".5":#so 或者说 solreturn 55if yin == "A3" or yin == ".6":#lareturn 57if yin == "B3" or yin == ".7":#sireturn 59#中音区if yin == "C4" or yin == "1":#中音doreturn 60if yin == "D4" or yin == "2":#中音rereturn 62if yin == "E4" or yin == "3" or yin == 3:#中音mireturn 64if yin == "F4" or yin == "4" or yin == 4:#中音fareturn 65if yin == "G4" or yin == "5" or yin == 5:#中音so或者说 solreturn 67if yin == "A4" or yin == "6" or yin == 6:#中音lareturn 69if yin == "B4" or yin == "7" or yin == 7:#中音sireturn 71#高音区if yin == "C5" or yin == "1." :#doreturn 72if yin == "D5" or yin == "2.":#re return 74if yin == "E5" or yin == "3.":#mireturn 76if yin == "F5" or yin == "4.":#fareturn 77if yin == "G5" yin == "5."":#so 或者说是 solreturn 79 if yin == "A5" or yin == "6.":#lareturn 81if yin == "B5" or yin == "7." :#sireturn 83
由于我的开发工具是中的,生成的不是py文件,是ipynb文件 , 在引入时会发生找不到库/文件的情况,需用通过shell命令转为py文件,加在后面运行时会生成py文件
try:!jupyter nbconvert --to python name_to_number.ipynbexcept:pass
接下来考虑文本内容读?。悸亲罴虻サ氖迪址绞骄褪莟xt文本读入规定格式的字符串后,对字符串分割,通过循环增加新的音符进去 。
于是我手动新增一个文本文档,将简谱图片中的旋律以音符_拍数的方式录入保存后 , 通过open函数读入文本文档,open函数返回的不是文本内容,是文件对象 , 通过read() 方法可以获取字符串内容,接下来问题就简单了 。代码如下:
# 指定节拍方便计算每拍持续多长时间bpm = 120# 获取文件数据data = http://www.kingceram.com/post/open('music.txt',encoding='utf-8')# open函数返回的是当前打开文件对象的信息,包括编码,只读模式等sheet_music = data.read().split(" ");# 数据格式:音符_拍数# 循环将简谱转化为MIDI消息for note in sheet_music:timePerBeat = 60/bpm*1000note_number = name_to_number.num(note.split("_")[0])# F表示全拍,H半拍if note.split("_")[1] == 'H':timePerBeat = timePerBeat*0.5# 读取到最后返回None表示文件结束if note_number !=None:track.append(mido.Message('note_on', note=note_number, velocity=64, time=0))track.append(mido.Message('note_off', note=note_number, velocity=64, time=int(timePerBeat)))
这里有几个坑点:
1,mido.方法中的time参数要传入整数形式,传入形参需要强转类型,否则会报data type类型必须为int;
2,文件对象read后以None结尾表示文件结束,而我做的自定义类里没有设置None的情况所以转换的时候会因为未转换成int类型导致方法报出参数类型错误的问题 。
【【脑洞手填目标一】通过学习python实现简谱自动转音频的功能】现在可以转换MIDI了,但是不够智能,接下来需要通过识别提取图片中的关键信息,生成需要的文本文档,但是完全不会笑死,由于这个半成品都不如的代码实在太难用了,我想我是会努力优化的 。