资源行业动态用Python可视化钢琴演奏录音

用Python可视化钢琴演奏录音

2019-10-17 | |  88 |   0

原标题:用Python可视化钢琴演奏录音

来源:AI研习社     链接:https://www.yanxishe.com/TextTranslation/1788


作为一个音乐家兼数据科学家,我对音乐表演可视化的想法很感兴趣。在这篇文章中,我简述了如何使用MAESTRO数据集对钢琴演奏的录音可视化。本文提供了一些例子。下面,我使用Python的Mido,Seaborn和Pandas包,用代码一步一步地说明,打开、清理和可视化MAESTRO数据集中的钢琴表演。文章最后以弗朗兹·李斯特《匈牙利狂想曲第2号》的可视化作结,并录下了这首曲子,让读者可以通过可视化来观看这首曲子的展开。还提供了用于创建可视化的完整Python脚本的链接。

1559726601673054.png

《巴赫D小调前奏曲与赋格》结合部的密度图

数据


MAESTRO数据集包含超过200小时的国际钢琴电子竞赛的钢琴表演。参赛者使用雅马哈自动演奏钢琴演奏,这种原声钢琴还可以捕捉和回放乐器数字接口(MIDI)数据。MAESTRO数据集包含参赛者演奏的MIDI数据和演奏的录音。

MIDI是一种允许数码乐器通过“消息”相互通信的协议。这些消息存储着用于回放的乐器类型、要播放的音符、音符何时开始、何时结束等信息。


简介:在Python中打开MIDI文件


在Python中打开一个MIDI文件,要安装mido包。从mido包里引入MidiFile。下面的代码是一个在Python中用MidiFile打开一个文件的例子。对于MAESTRO数据,这个过程会创建一个包含两个轨道的MidiFile对象。第一个轨道包含演奏的元数据(例如时间、作品签名)。第二个轨道包含演奏的信息。

from mido import MidiFile    
   mid = MidiFile(file_name)

1559726601844399.png

《莫扎特D大调奏鸣曲》联合密度图

阐述:处理数据


在本文中,我使用以下步骤来处理数据以创建可视化。

步骤1:提取第二个轨道,通过对轨道进行遍历来提取数据。其结果应该是一个消息对象的list。遍历的时候,忽略第一个和最后一个消息。第一个消息是程序消息,最后一个是元消息,表示轨道的结束。这两条消息都没有提供所播放的音符的数据。

message_list = []    
   for i in mid.tracks[1][1:-1]:    
       message_list.append(i)

步骤2:遍历消息的list,将消息转换成字符串。可以用python的str()函数或者mido包提供的format_as_string()方法完成到字符换的转换。

message_strings = []    
   for x in message_list:    
       message_strings.append(str(x))

步骤3:使用split('')把消息字符串分割成包含键和值的子串

message_strings_split = []
   for message in message_strings:  
       split_str = message.split(" ")
       message_strings_split.append(split_str)

这将从列表中创造一个列表,单个列表如下所示:

['control_change', 'channel=0', 'control=64', 'value=43', 'time=0']

列表的第一个子字符串只包含一个值(消息类型)。为了将数据转换为字典,必须删除这个子字符串。

步骤4:删除第一个子字符串,将其存储在列表中,并将列表转换为dataframe。

message_type = []    
   for item in message_strings_split:    
       message_type.append(item[0])    
   df1 = pd.DataFrame(message_type)    
df1.columns = ['message_type']


步骤5:把列表中的其它子串转换成字典,再转换成dataframe。=号将子串的键和值分隔开。下面的代码遍历列表和每个子字符串,以创建键值对。然后,它将这些键值对存储在字典中(每个子字符串列表对应一个字典)。最后,它将字典转换为一个dataframe。

attributes = []
   for item in message_strings_split:
       attributes.append(item[1:])
attributes_dict = [{}]    
   for item in attributes:
       for i in item:
           key, val = i.split("=")
           if key in attributes_dict[-1]:
               attributes_dict.append({})
           attributes_dict[-1][key] = val
df2 = pd.DataFrame.from_dict(attributes_dict)


步骤6:将属性(df2)的dataframe与包含消息类型(df1)的dataframe连接起来。

df_complete = pd.concat([df1, df2], axis=1)


步骤7:dataframe中的time变量表示自上次消息以来经过的时间。note变量表示弹奏的钢琴键。为了绘制数据,需要测量经过的时间来绘制数据随时间的变化。可以使用Python的.cumsum()方法创建time_elapsed变量。绘制时需要将变量从类型string转换为类型float。

#Transform the time and note attributes from strings to floats    
df_complete.time = df_complete.time.astype(float)    
df_complete.note = df_complete.note.astype(int)    

#Engineer a time elapsed attribute equal to the cumulative sum of time.    
df_complete['time_elapsed'] = df_complete.time.cumsum()


步骤8:过滤掉控制消息和note_off消息,因为这些消息对可视化来说不会被用到。控制消息表示何时按下和释放延音踏板于弱音踏板。控制消息的消息类型等于“control”。note_off消息由速度为0的note_on类型的消息表示。

df_filtered = df_complete[df_complete['message_type']=='note_on']    
df_filtered = df_filtered.loc[df_filtered['velocity'] != '0']


步骤9:删除dataframe中不必要的列

df_filtered.drop(['channel', 'value', 'control', 'time'], axis=1, inplace=True)    
try:    
   df_filtered.drop('program', axis=1, inplace=True)    
except:    
   pass


步骤10:处理过程的最后一步是分别在dataframe开始和结尾添加一行。添加这些行将在可视化的边缘和数据点之间提供一些空间。新的第一行被分配了音符值0(钢琴的较低范围之外的值),并且经过的时间值等于最大经过时间值的-0.05倍。(第一个note_on消息的time_elapsed值大于0)。新的最后一行被分配了一个音符值127(钢琴上限范围之外的值),经过的时间值等于最大经过时间值的1.5倍。添加这些行可以使生成的可视化具有平滑的边缘。


add_first_row = []    
add_first_row.insert(0, {'message_type': 'note_on', 'note': 0, 'time': 0,    
                           'velocity': 0, 'time_elapsed':-df_final.iloc[-1]['time_elapsed']*0.05})    
df_final = pd.concat([pd.DataFrame(add_first_row), df_filtered], ignore_index=True)
 
last_time_elapsed = df_final.iloc[-1]['time_elapsed']*1.05    
add_last_row = []    
add_last_row.insert(0, {'message_type': 'note_on', 'note': 127, 'time': 0,    
                           'velocity': 0, 'time_elapsed':last_time_elapsed})    
df_final = pd.concat([df_final, pd.DataFrame(add_last_row)], ignore_index=True)

1559726600290177.png

 爱德华·格里格小调抒情作品《华尔兹》作品12第2部分的联合密度图


更进一步:可视化


为了将演奏可视化,我用了Python的Seaborn包。x轴表示经过的时间(时间从左往右递增),y轴表示弹奏的音符的音高,从低(下)到高(上)。图中显示了使用十六进制文件时,随时间的推移音符的联合密度。颜色用来表示十六进制文件中包含的音高范围和一个时间段内的音高频率。在上面的图中,较高的频率用较暗的绿色表示。


联合密度图显示了十六进制文件所定义的范围内音高的频率,以及这些频率如何随着演奏的进行而变化。其结果是一个可以随着时间(x轴)可视化音高(y轴)和音高频率(颜色)的图。该图包含了所有必要的信息,是所弹奏片段的可视化(参见下面的匈牙利狂想曲示例中的演示)。


下面的步骤详细说明了本文中的可视化是如何绘制的。

步骤1:将Seaborn的样式设置为“white”。这一步提供了一个干净的白色背景,在此基础上构建可视化。

sns.set_style('white')


步骤2:定义图中x轴和y轴的范围。在第10步处理过程中添加的第一行和最后一行有助于可视化。x轴的范围由经过的最小时间和最大时间来决定。这两个的值都是由额外的行设置的。y轴的范围设置在16和113之间。这样做是为了使图产生平滑的边缘。MIDI音符值(在y轴上绘制)在0到127之间。然而,钢琴只有88个键,它们的MIDI音符值在21到108之间。第一行和最后一行的音符值设置在钢琴的可能值范围之外。因此,通过在第一行和最后一行设置的最小和最大音符值设置y轴范围,可以得到:

  1. 显示钢琴所有可能的值

  2. 不显示第一行和最后一行的人工音符值

  3. 在每个可视化的边缘和最低和最高音符之间有一个空间,并且

  4. 该空间的颜色与画布的背景相同

关于绘图函数的另外一个注意事项:网格大小表示x方向上六边形的数量。更改网格大小会更改六边形区域的大小,以及随后的图形中六边形的大小。下图绘制的是里姆斯基-科萨柯夫的《野蜂飞舞》。

g = sns.jointplot(df_final.time_elapsed, df_final.note, cmap=palette,    
   kind='hex', xlim=(min(df_final.time_elapsed),max(df_final.time_elapsed)),    
   ylim=(16,113),    
   joint_kws=dict(gridsize=88)    
   )    
g.fig.set_figwidth(30)    
g.fig.set_figheight(15)

1559726600790291.png

里姆斯基-科萨柯夫《野蜂飞舞》的联合密度图(带标签和边缘)


步骤3:删除图中不需要的元素。为了提供一个艺术性的联合密度图,我删除了图的边缘、轴线、轴标签和坐标轴(注意:删除这些元素不利于提供一个有意义的可视化)。下面的代码删除了轴线、图边缘、轴标签和刻度标签。

sns.despine(left=True, bottom=True) #Remove x and y axes    
plt.setp(g.ax_marg_x, visible=False) #Remove marginal plot of x    
plt.setp(g.ax_marg_y, visible=False) #Remove marginal plot of y    
g.set_axis_labels('', '') #Remove axis labels    
plt.setp(g.ax_joint.get_xticklabels(), visible=False) #Remove x-axis ticks    
plt.setp(g.ax_joint.get_yticklabels(), visible=False) #Remove y-axis ticks

1559726601942350.png

里姆斯基-科萨柯夫《野蜂飞舞》的联合密度图


概述:

本文介绍了从MAESTRO数据集中打开、清理和可视化MIDI演奏数据的步骤。提供了代码片段来展示这些步骤。利用Seaborn的散点图的方法对演奏的数据进行可视化。这些图显示了音高的密度(或所弹奏音符的频率)随时间的变化。其结果是一种艺术的描绘,把一段音乐捕获在一个单一的图像中。


对于那些有兴趣了解可视化如何与音乐作品的表现相一致的读者,下面是一个例子。这是弗朗兹·李斯特《匈牙利狂想曲第2号》的可视化图。在可视化之后,YouTube上有一段Adam Gyorgy演奏该作品的视频。对于有兴趣创建自己的可视化的读者,可以在GitHub上找到我的脚本和文档。

1559726602875857.png

 阿尔班·伯格《奏鸣曲》的散点图


结尾:匈牙利狂想曲第2号


下图是弗朗兹·李斯特《匈牙利狂想曲第2号》的可视化图以及Adam Gyorgy表演这首曲子的视频。你可以一边听视频,一边通过可视化图感受他的演奏。

1559726602874758.png

弗朗兹·李斯特《匈牙利狂想曲第2号》的散点图

视频地址:https://youtu.be/7H99FM6S8rU


发起:唐里 校对:敬爱的勇哥 审核:唐里

参与翻译(1人):路双宁

英文原文:Visualizing Musical Performance

THE END

免责声明:本文来自互联网新闻客户端自媒体,不代表本网的观点和立场。

合作及投稿邮箱:E-mail:editor@tusaishared.com

上一篇:kepler.gl:Jupyter里的地理空间可视化工具

下一篇:自动驾驶汽车能像人类一样思考吗?

用户评价
全部评价

热门资源

  • 国内人才报告:机...

    近日,BOSS 直聘职业科学实验室 &BOSS 直聘研究院...

  • AI使物联网更智能...

    看到微软对物联网和人工智能的结合感兴趣是一个明...

  • 推荐一批学习自然...

    这里推荐一批学习自然语言处理相关的书籍,当然,...

  • 安防智能化大势下...

    大部分传统安防设备不仅拍摄视野有限,而且无法事...

  • 20亿创业基金、10...

    近日,杭州举办了建设国家新一代人工智能创新发展...