Magenta魔改记-1:原始数据转换

前言

本文主要讲述Magenta项目原始数据整合的过程,并介绍了读取MIDI和XML的函数。通过本文我们可以看到,在原始音乐数据整合的过程中,Magenta将不同格式的数据转换到了一个接近MusicXML的统一格式中统一存储。

Magenta中有很多自动作曲模型,它们都使用不同格式的数据输入。在Magenta中,原始数据(MIDI,MusicXML等)先被转换成基于protocol buffers的NoteSequence,之后,根据模型的不同,再将NoteSequence转换成该模型需要的输入。

Magenta支持MIDI(.mid/.midi)、MusicXML(.xml/.mxl)、ABC(http://abcnotation.com,没有测试过)等格式的原始数据文件做训练数据。

通过convert_dir_to_note_sequences.py,这些原始数据被转换为NoteSequence,并以tfrecord格式储存。

接下来我们分析在将convert_dir_to_note_sequences.py中如何将MIDI/MusicXML文件转换成NoteSequence。

Magenta version:1.1.1

魔改-1.0:从命令行输入参数:

在Magenta的github中提供了如何将原始数据通过命令行转换为NoteSequence protocol buffers的方法:
https://github.com/tensorflow/magenta/tree/master/magenta/scripts#building-your-dataset

上述链接中提供的Linux命令行如下:

INPUT_DIRECTORY=<folder containing MIDI and/or MusicXML files. can have child folders.># TFRecord file that will contain NoteSequence protocol buffers.
SEQUENCES_TFRECORD=/tmp/notesequences.tfrecordconvert_dir_to_note_sequences \--input_dir=$INPUT_DIRECTORY \--output_file=$SEQUENCES_TFRECORD \--recursive

这一步的python命令行如下(摘自convert_dir_to_note_sequences.py源代码注释):

Example usage:$ python magenta/scripts/convert_dir_to_note_sequences.py \--input_dir=/path/to/input/dir \--output_file=/path/to/tfrecord/file \--log=INFO

那么下面介绍如何在代码中直接修改这一步预处理的参数。

这一步运行的文件位置如下:

convert_dir_to_note_sequences.py

打开源代码我们可以看到,程序一开始就定义了一系列tf.flag:

FLAGS = tf.app.flags.FLAGStf.app.flags.DEFINE_string('input_dir', None,'Directory containing files to convert.')
#输入路径
tf.app.flags.DEFINE_string('output_file', None,'Path to output TFRecord file. Will be overwritten ''if it already exists.')
#输出路径
tf.app.flags.DEFINE_bool('recursive', False,'Whether or not to recurse into subdirectories.')
#是否递归查找子路径的文件tf.app.flags.DEFINE_string('log', 'INFO','The threshold for what messages will be logged ''DEBUG, INFO, WARN, ERROR, or FATAL.')
#显示消息的类型

tf.app.flags是Tensorflow中用于从命令行传递参数的模块,基于argparse。如果在运行时不输入参数,则会按程序中默认填写的参数运行。

通过python convert_dir_to_note_sequences.py –h可以显示注释信息和参数及其详情。
因此,我们在自定义参数时,既可以在命令行运行时输入:

python convert_dir_to_note_sequences.py --input_dir=XXX --output_file=YYY --recursive=True

同样,我们也可以把前面这几行当做超参数变量声明,直接在convert_dir_to_note_sequences.py中修改,然后运行这个文件。

除了命令行之外,我们接下来介绍如何在python文件中直接修改参数以及如何在jupyter环境中修改参数并调试。

魔改-2.0:在jupyter notebook中调试:

接下来,我们介绍如何在jupyter notebook中调试,并展现这个程序的详细原理以及文件储存的数据类型。

程序源代码地址:
https://github.com/tensorflow/magenta/blob/master/magenta/scripts/convert_dir_to_note_sequences.py

在本程序中,大致的运行步骤为:

  1. 先检测输入路径(以及子路径)中所有符合要求的文件,生成文件路径列表。
  2. 再根据路径列表多线程地处理数据,转换为NoteSequence。
  3. 保存为.tfrecord文件。

第一步对应queue_conversions(root_dir, sub_dir, pool, recursive=False)函数,在此不多展开。

第二步对应convert_midi(root_dir, sub_dir, full_file_path)
convert_musicxml(root_dir, sub_dir, full_file_path)两个函数。顾名思义就是针对midi和xml文件的处理函数(一开始说的ABC数据处理函数未知)。它们的参数以及返回值可以在函数注释中找到详细的介绍。简单来说就是输入文件路径、文件所在文件夹路径、上一级路径,输出NoteSequence proto,一个在Magenta项目中用来表示音符序列的数据类型。

第三步则对应convert_directory(root_dir, output_file, num_threads,recursive=False),是总的函数。

首先我们可以把这个文件导入:

import tensorflow as tf
import magenta as mgt
import magenta.scripts.convert_dir_to_note_sequences as cvrt
WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.
For more information, please see:* https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md* https://github.com/tensorflow/addons
If you depend on functionality not listed there, please file an issue.

导入之后我们就可以用查看子类的方式查看它的FLAGS参数:

print(cvrt.FLAGS)
magenta.scripts.convert_dir_to_note_sequences:--input_dir: Directory containing files to convert.--log: The threshold for what messages will be logged DEBUG, INFO, WARN,ERROR, or FATAL.(default: 'INFO')--output_file: Path to output TFRecord file. Will be overwritten if it alreadyexists.--[no]recursive: Whether or not to recurse into subdirectories.(default: 'false')absl.flags:--flagfile: Insert flag definitions from the given file into the command line.(default: '')--undefok: comma-separated list of flag names that it is okay to specify onthe command line even if the program does not define a flag with that name.IMPORTANT: flags in this list that have arguments MUST use the --flag=valueformat.(default: '')
# 加这行是因为jupyter notebook对tf.app.flags.FLAGS有bug
# 见https://github.com/tensorflow/tensorflow/issues/17702
tf.app.flags.DEFINE_string('f', '', 'kernel')

因此我们也可以用修改FLAGS子类参数的方法运行本程序:

首先给参数赋值:

cvrt.FLAGS.input_dir = r'Dataset\raw\example-musicxml'
cvrt.FLAGS.output_file = r'Dataset\pre\example-musicxml.tfrecord'
cvrt.FLAGS.recursive = True
cvrt.FLAGS.log = 'INFO'

接着,运行main函数:

如果使用convert_dir_to_note_sequences.py文件中的运行方法,可以替换成tf.app.run(cvrt.main),但是使用tf.app.run会使进程结束并抛出异常,这里我们先使用直接运行main函数的方法。在convert_dir_to_note_sequences.py中,main函数有一个不使用的占位参数unused_argv

unused_argv = ''
cvrt.main(unused_argv)
INFO:tensorflow:Converting files in 'Dataset\raw\example-musicxml\'.
INFO:tensorflow:0 files converted.
INFO:tensorflow:Converted MusicXML file Dataset\raw\example-musicxml\bwv1.6.mxl.

这样,我们就完成了第一步NoteSequences的创建。

如上所说,转换MIDI和MusicXML对应convert_midi(root_dir, sub_dir, full_file_path)convert_musicxml(root_dir, sub_dir, full_file_path)两个函数。

下面我们选取一个MusicXML文件和一个MIDI,分别来运行一下转换函数并看一下它们返回的结果。

MusicXML转换:

full_file_path_xml = r'Dataset\raw\example-musicxml\bwv1.6.mxl'
root_dir_xml = r'Dataset\raw\example-musicxml'
sub_dir_xml = r'Dataset\raw\example-musicxml'
sequence_xml = cvrt.convert_musicxml(root_dir_xml, sub_dir_xml,full_file_path_xml)
INFO:tensorflow:Converted MusicXML file Dataset\raw\example-musicxml\bwv1.6.mxl.

查看转换结果sequence_xml的类型

print(type(sequence_xml))
<class 'music_pb2.NoteSequence'>

我们可以看到sequence_xml是一个基于Google protobuf的数据类型。

接下来,我们查看sequence_xml的内容:

print(str(sequence_xml)[:1000])
id: "/id/musicxml/example-musicxml/b916b4d6787e8de96484206b4c617879add937ce"
filename: "Dataset\\raw\\example-musicxml\\bwv1.6.mxl"
collection_name: "example-musicxml"
ticks_per_quarter: 220
time_signatures {numerator: 4denominator: 4
}
time_signatures {time: 38.5numerator: 3denominator: 4
}
time_signatures {numerator: 1denominator: 4
}
time_signatures {time: 0.5numerator: 4denominator: 4
}
key_signatures {key: F
}
tempos {qpm: 120.0
}
notes {pitch: 65velocity: 64end_time: 0.5numerator: 1denominator: 4instrument: 7program: 1voice: 1
}
notes {pitch: 67velocity: 64start_time: 0.5end_time: 0.75numerator: 1denominator: 8instrument: 7program: 1voice: 1
}
notes {pitch: 60velocity: 64start_time: 0.75end_time: 1.0numerator: 1denominator: 8instrument: 7program: 1voice: 1
}
notes {pitch: 65velocity: 64start_time: 1.0end_time: 1.25numerator: 1denominator: 8instrum

从上面我们可以看到这里面包含了路径、id、以及xml中的内容。数据格式很像MusicXML与MIDI的结合,但将它们以类的形式结构化储存了。

于是,我们也可以直接访问它的子类:

print(sequence_xml.id)
print(sequence_xml.filename)
print(sequence_xml.source_info)
print(sequence_xml.total_time)
/id/musicxml/example-musicxml/b916b4d6787e8de96484206b4c617879add937ce
Dataset\raw\example-musicxml\bwv1.6.mxl
source_type: SCORE_BASED
encoding_type: MUSIC_XML
parser: MAGENTA_MUSIC_XML40.0
print(type(sequence_xml.notes))
<class 'google.protobuf.pyext._message.RepeatedCompositeContainer'>

sequence_xmlnotes类里面就是最主要的内容了,notes记录了所有的音符。
音符类当然也支持索引,我们可以看到每个音符由音高、音色、起始时间、终止时间等元素组成。

print(sequence_xml.notes[0])
print(sequence_xml.notes[20])
print(sequence_xml.notes[30])
pitch: 65
velocity: 64
end_time: 0.5
numerator: 1
denominator: 4
instrument: 7
program: 1
voice: 1pitch: 70
velocity: 64
start_time: 5.0
end_time: 5.25
numerator: 1
denominator: 8
instrument: 7
program: 1
voice: 1pitch: 71
velocity: 64
start_time: 7.0
end_time: 7.5
numerator: 1
denominator: 4
instrument: 7
program: 1
voice: 1

接下来,我们查看MIDI文件的转换结果:

full_file_path_midi = r'Dataset\raw\example-midi\Bwv0525 Sonate en trio n1.mid'
root_dir_midi = r'Dataset\raw\example-midii'
sub_dir_midi = r'Dataset\raw\example-midi'
sequence_midi = cvrt.convert_midi(root_dir_midi, sub_dir_midi,full_file_path_midi)
INFO:tensorflow:Converted MIDI file Dataset\raw\example-midi\Bwv0525 Sonate en trio n1.mid.
print(str(sequence_midi)[:1000])
id: "/id/midi/example-midii/eaec1aa71ccd1892886c79883c24a044c480a2ef"
filename: "Dataset\\raw\\example-midi\\Bwv0525 Sonate en trio n1.mid"
collection_name: "example-midii"
ticks_per_quarter: 480
time_signatures {numerator: 4denominator: 4
}
time_signatures {time: 187.07416394583333numerator: 12denominator: 8
}
time_signatures {time: 562.6120114458334numerator: 3denominator: 4
}
tempos {qpm: 75.0
}
tempos {time: 179.20000000000002qpm: 70.00007000007
}
tempos {time: 182.62856800000003qpm: 65.000065000065
}
tempos {time: 184.47472000000002qpm: 50.0
}
tempos {time: 185.07972qpm: 45.000011250002814
}
tempos {time: 187.07416394583333qpm: 120.0
}
tempos {time: 189.32416394583333qpm: 55.000004583333705
}
tempos {time: 554.7786789458335qpm: 45.000011250002814
}
tempos {time: 558.1120114458334qpm: 80.0
}
tempos {time: 848.3620114458334qpm: 50.0
}
notes {pitch: 70velocity: 92start_time: 6.4end_time: 6.80
print(sequence_midi.id)
print(sequence_midi.filename)
print(sequence_midi.source_info)
print(sequence_midi.total_time)
/id/midi/example-midii/eaec1aa71ccd1892886c79883c24a044c480a2ef
Dataset\raw\example-midi\Bwv0525 Sonate en trio n1.mid
encoding_type: MIDI
parser: PRETTY_MIDI851.9745114458334
print(sequence_midi.notes[0])
print(sequence_midi.notes[20])
print(sequence_midi.notes[30])
pitch: 70
velocity: 92
start_time: 6.4
end_time: 6.800000000000001pitch: 72
velocity: 92
start_time: 12.8
end_time: 13.200000000000001pitch: 74
velocity: 92
start_time: 16.0
end_time: 16.400000000000002

我们看到,MIDI形式的储存格式和XML大同小异,但是由于MusicXML是用相对最小单位计时,而MIDI中是以绝对时间(秒)计时,MIDI中的起始和终止的时间不是整数。并且,MIDI转换而来的notes中元素更少,这是因为MIDI中包含了更少的信息。

总结

通过本文,我们研究了Magenta项目原始数据整合的过程,并介绍了读取MIDI和XML的函数。在原始音乐数据整合的过程中,Magenta将不同格式的数据转换到了一个接近MusicXML的统一格式中统一存储。

如果你想进行自己的项目的话,直接用Magenta的数据处理函数也是个不错的选择。Magenta中还有用于处理数据间转换的模块Piplines,见https://github.com/tensorflow/magenta/blob/master/magenta/pipelines,但是文档有些难懂。

其他

使用其他Python库读取并处理MIDI与MusicXML文件的方法,见:

MIDI文件以及XML文件的格式介绍,见:

对生成的tfrecord的读取,见下一节:

Magenta魔改记-1:原始数据转换相关推荐

  1. Magenta魔改记-2:数据格式与数据集

    Magenta魔改记-2:数据格式与数据集 数据格式:MusicXML与MIDI 上一节我们主要提到了两种数据格式:MIDI(.mid/.midi)和MusicXML(.xml/.mxl).其实他们二 ...

  2. Magenta魔改记-0:Magetna初见

    Magenta魔改记-0:Magetna初见 前言: 最近在魔改Magenta,所以会涉及到阅读.修改Magenta的源代码.我个人认为Magenta是一个很好的项目,虽然代码可能写的有点乱,但模型的 ...

  3. 【网络结构】小议如何跳出魔改网络结构的火坑

    公众号关注 "ML_NLP" 设为 "星标",重磅干货,第一时间送达! 机器学习算法与自然语言处理出品 @公众号原创专栏作者 纵横 知乎专栏 | 机器不学习 引 ...

  4. 推荐!小议如何跳出魔改网络结构的火坑(完整版)

    点击我爱计算机视觉标星,更快获取CVML新技术 本文原载于知乎,已获作者授权转载,请勿二次转载, https://zhuanlan.zhihu.com/p/108838471 昨天发布过上半部分,不少 ...

  5. 代码已开源,一起魔改大西瓜!

    上了两次微博热搜的<合成大西瓜>,想必大家都玩过了,好多人刚开始嗤之以鼻,最后真香了,说实话有点上头. 其火热程度直登热搜第三,阅读量6.9亿,朋友圈里也在纷纷安利. 合成大西瓜 游戏界面 ...

  6. CobaltStrike魔改与增强

    文章为匿名投稿,该文章仅限提供思路,具体实现请自行研究使用. 文章内用到的代码源码 详见末尾 由于传播.利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担 ...

  7. Oracle魔改linux,【更新】一键网络重装系统 - 魔改版(适用于Linux / Windows)

    本帖最后由 MeowLove 于 2020-9-21 15:34 编辑 简介 这还要介绍??懒得写了. 魔改版本及开源地址 最新版本:3.1.0(2020/09/15) 官方发布:https://ww ...

  8. 一句话就能魔改视频主角,谷歌新「AI导演」惊呆网友:这画质也太赞了

    Alex 发自 凹非寺 量子位 | 公众号 QbitAI 谷歌整出了个新"AI导演",一句话甚至能把视频主角给换了. 你看,青青草地上,一只小熊正在跳舞. 难道现在的熊都这么有艺术 ...

  9. 魔改mammoth支持导入样式

    文章目录 魔改 mammoth 支持导入样式 mammoth.js 简单介绍 跑通 mammoth 源码 配置编辑器格式化方案 Prettier ESLint 不能正常格式化 魔改一:添加 dev 更 ...

最新文章

  1. nginx 通过proxy_next_upstream实现容灾和重复处理问题
  2. 单向链表JAVA代码
  3. ifconfig命令实例
  4. 【C 语言】字符串操作 ( 使用 数组下标 操作字符串 | 使用 char * 指针 操作字符串 )
  5. 装完B就跑,这几个Linux指令真的Diǎo
  6. MySQL配置变量log-bin,重启数据库服务失败
  7. 前端学习(476):web前端行业介绍
  8. Unity3D调用摄像头显示当前拍摄画面
  9. 多云架构落地设计和实施方案【华为云分享】
  10. 读写文件RandomAccessFile
  11. 【BZOJ4025】二分图(可撤销并查集+线段树分治)
  12. 【Linux】常用命令之 awk 常用实例
  13. IS-IS for IPv6技术原理
  14. python 修改ip地址
  15. 诺基亚安卓手机无法清理后台任务
  16. ORACLE 碎片整理小结
  17. MUI-轮播插件实现-UI组件
  18. 北京化工大学本科毕业论文答辩和论文选题PPT模板
  19. cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断)
  20. 《阁夜》杜甫:岁暮阴阳催短景,天涯霜雪霁寒宵。

热门文章

  1. 更换虚拟机的内核版本
  2. 字节跳动面试题.寻找抖音红人
  3. C++实现客户端升级流程
  4. 带你了解5个幽灵攻击与编译器中的消减方法
  5. 【数据聚类】第六章第二节:层次聚类算法之BIRCH算法(算法概述、流程和sklearn实现)
  6. sklearn学习BIRCH聚类
  7. 从睡前行动看男人秘密
  8. 筷子兄弟 -- 《老男孩》
  9. 我的平台运营思路:功能 价值 意义
  10. mix1 android n,因为一条评论引发的讨论——论MIX系列垮掉的第一代