混合和播放 Windows Media Player 的声音






4.66/5 (14投票s)
2006年4月14日
6分钟阅读

79824

1361
创建一个混合声音键盘的简单实用程序。
引言
您是否曾经需要扮演您幼儿学校阴影剧表演中的音效师的角色?这发生在我身上……而且——相信我——我在弄清楚在表演的精确时刻需要发出多少不同的声音和噪音来配合表演动作时遇到了一些麻烦。所以,我开始思考如何创建一个混合声音键盘,能够播放与每个按键关联的不同声音文件(.WAV、.MP3 等),让我能够通过简单地按下按键来重现它们,以便在演员/动画师需要时发出相应的声音。
尽管我确信存在许多程序可以实现这一目标,但我决定用 VB.NET 自己创建一个。这里展示的简单实用程序名为“Sampled Sounds Player”,它在包含采样声音的文件和键盘按键之间创建了一个关联矩阵,并允许您通过编程控制 Windows Media Player 来播放这些文件(还可以循环播放和重叠播放)。
该实用程序的工作方式
该实用程序非常简单:基本上它是一个单窗体的 VB.NET Windows 应用程序,包含一个 DataGrid
,显示您选择的文件名/按键关联。这种关联保存在一个配置 XML 文件中,该文件是通过对网格底层 DataSet
调用 WriteXML()
方法生成的。该 XML 文件也可以通过网格自身的 UI 进行编辑。我们将这种文件称为“声音映射”,因为它们实际上只包含声音文件和键盘按键之间的映射。
如前所述,此实用程序通过使用 Windows Media Player(例如版本 10)来播放声音文件。为了以编程方式控制 Windows Media Player,我向 WMPLib.DLL
组件添加了一个项目引用(这是一个非托管代码库,所以 Visual Studio 为我创建了所需的互操作程序集)。WMPLib
库易于使用;它公开了 WindowsMediaPlayerClass
类,该类代表播放器的实例,您可以在其上调用与交互式播放器相同的操作:您可以以编程方式加载要播放的文件、开始/停止/暂停播放、将“循环”属性设置为重复播放声音,依此类推。
我的目标是将控制声音的逻辑嵌入到用户界面上显示的网格中。然后,我创建了 RunningSound
类,它代表一个准备播放的声音:此类封装了 WindowsMediaPlayerClass
,并被设计为直接用作 UI 网格底层 DataTable
的数据类型。因此,例如,它公开了一个自定义的 ToString()
方法,用于在网格表中显示正在播放的声音的当前状态。事实上,在使用“Sampled Sounds Player”时,跟踪当前正在播放的声音非常重要,如下面的图片所示,您可以看到网格显示了这种情况
- 正在播放按键“P”和“U”关联的声音,“U”声音连续播放(注意播放符号旁边的无限循环符号);
- 按键“J”和“L”关联的声音当前已暂停(“L”声音设置为重复播放);
- 所有其他声音都已准备好播放,只需按下相应的按键即可。
通过拦截每个实例化播放器的 PlayStateChange
事件,可以直观地更新网格上的声音状态。这一切都再次由 RunningSound
包装类完成,该类能够访问当前承载已更改状态的播放器实例的 DataRow
。
为了在用户按下相应按键时播放声音,我只需处理窗体的 KeyDown
事件,并在将窗体的 KeyPreview
属性设置为 True
后(请注意,如果 DataGrid
拥有焦点,窗体将无法触发该事件处理程序)。在 KeyDown
事件处理程序中,会对包含声音/按键映射的表执行简单的查找,如果找到一行,则控制相应的播放器。我决定使用映射的按键与 Shift 和 Control 键组合来执行不同的操作
按键组合 | 操作 |
简单按键 | 播放 / 暂停声音 |
Ctrl + 按键 | 停止声音 |
Shift + 按键 | 切换循环开/关 |
窗体上的“加载”、“编辑”和“保存”控件允许您
- 加载新的声音映射到网格;
- 进入编辑模式,允许您修改网格内容;
- 将当前显示的网格保存为声音映射文件。
一些注意事项
- 当加载新的声音映射时,旧的将被丢弃,并且在每个网格行上,都会创建一个带有相应声音的新播放器;
- 当进入编辑模式时,所有正在播放的声音都会被自动停止,并且相应的播放器会被卸载;
- 当编辑模式结束时,网格会恢复到只读/播放模式;
- 当编辑模式结束时,所有声音和播放器都会被重新加载,以符合修改后的网格;
- 在编辑时,您可以像在标准的
DataGrid
中一样,添加/删除/修改任何网格行和任何字段,但Status
列除外,该列显然始终是只读的; - “保存”按钮仅在编辑模式下启用;
- 当您保存网格时,它会自动恢复到只读/播放模式;
- 您可以通过取消选中“编辑”复选框并重新加载之前保存的声音映射文件来丢弃您在网格上所做的更改;
- 您可以在
Filename
字段中填入任何 Windows Media Player 可以播放的文件名(.WAV、.MP3 等),但请记住,您指定的文件必须位于保存引用它的声音映射文件的同一文件夹中。
关注点
此实用程序中的主要亮点是:使用 WMP 以及对 DataTable
和 DataSet
的操作。老实说,控制 Windows Media Player 非常简单:大部分工作都是由组件本身完成的。我创建的并且直接用作 DataColumn
数据类型的包装类完成了其余的工作。请注意我管理此“特殊”DataSet
的序列化问题的方式:由于 Status
列的性质(它承载一个自定义的 RunningSound
数据类型,该类型与同一行上的其他数据是冗余/依赖的,并且是只读的),它根本不被序列化(实际上:在保存网格时会被抑制,并在稍后重新加载声音映射时重新创建)。
未来增强功能
目前,Key
列中没有重复条目的检查(一个简单的 UNIQUE
约束就足以完成此任务)。
目前,所有声音都以相同的音量级别播放。潜在地,每个 Windows Media Player 实例都可以控制其播放音量,因此实现此增强功能并不困难。对于这个实用程序,唯一的挑战是如何通过非常简单的键盘输入来控制每个声音的音量(一种想法是使用上/下键来修改网格上选定的声音的音量,这也以某种方式绕过了 DataGrid
的 KeyPreview
问题)。
添加一个能够从给定声音文件文件夹的内容自动填充新声音映射的功能将非常有益(System.IO.Directory.GetFiles()
方法使得实现此增强功能变得微不足道)。
致谢
非常感谢我的朋友 Davide Melis 在 Windows Media Player 技术方面提供的一些初步“知识馈赠”。