Midi-dot-net 声音播放两次
Posted
技术标签:
【中文标题】Midi-dot-net 声音播放两次【英文标题】:Midi-dot-net sounds played twice 【发布时间】:2015-06-19 08:02:28 【问题描述】:首先我不是专业程序员,只是一个对优秀的库 Midi-Dot-Net 有一点问题的学生。
我使用 Midi-Dot-Net 库在 Visual Studio 中用 C# 语言编写了一个简单的应用程序。但我遇到了一个非常令人困惑的问题。
我在 Form1.cs 中放了一些代码
public void NoteOn(NoteOnMessage msg)
if (InvokeRequired)
BeginInvoke(noteOnHandler, msg);
return;
inputStatusLabel.Text = String.Format("0", msg.Pitch);
String nutka = inputStatusLabel.Text;
if (nutka == "A0")
clock.Schedule(new NoteOnMessage(outputDevice, Channel.Channel1, Pitch.A0, 80, 2));
所以,我放置了新的字符串并将其命名为 nutka,而 nutka 将在我的 USB MIDI 键盘上接收按下的音符的名称。然后我放置了 IF 语句,并将 nutka 与“A0”(键盘上的第一个音符)进行比较。
然后,如果它是“A0”,我按下我的 outputDevice 播放特定的音符。它会播放,但会播放两次,一次 - 当我按下键盘上的键(带有音符 A0)时,第二次 - 当我释放该键时。
我在public void NoteOn(NoteOnMessage msg)
上做了一个断点,并注意到一个应用程序返回这里两次并播放了两次该音符,但仍然不知道为什么。
还有一点,有一个方法public void NoteOff(NoteOffMessage message)
,但是好像不行。
我实在想不通,我正在寻求任何帮助。
更新 ... 更新 ... 更新
出现了另一个问题(第一部分得到了解决,感谢 CL 和 Chris Dunaway 的建议和逐步的 Justin 解释)。
谢谢贾斯汀 :) 我看到没有任何问题的生活是不可能的 :)
使用clock.Schedule
我只能播放 MIDI 声音,但我想播放示例钢琴音符(wav 文件格式)和 4-5
几周我的大学将帮助我为每个音符录制自己的钢琴声音。我也想同时播放。
我以为一切都会好起来的,但现在……同时播放出现了问题。我试图检查三种可能性:
1) 我试图从我拥有的基本音符库中弹奏钢琴音色并使用 SoundPlayer:
SoundPlayer noteA0 = new SoundPlayer(Properties.Resources.A0);
noteA0.Play();
我将它用于具有不同 SoundPlayer 名称(取决于音符名称)的每个音符语句,我注意到的是 - 我不能同时播放音符。
2) 所以我使用了下一个 WMP 库和 WindowsMediaPlayer:
例如:
var noteA0 = new WMPLib.WindowsMediaPlayer();
noteA0.URL = @"c:\sounds\piano\A0.wav";
好的......它同时播放,但看起来我播放的音符越多,或者我只是播放一首歌,我得到的延迟就越大,最后我的程序卡在播放任何东西......
3) 我尝试使用 Microsoft.DirectX.AudioVideoPlayback:
Audio noteA1 = new Audio(@"C:\sounds\piano\A1.wav");
noteA1.Play();
我启动了我的程序,按下一个键,然后砰!崩溃并显示错误消息:
An unhandled exception of type 'System.BadImageFormatException' occurred in Midi.dll
Additional information: Could not load file or assembly 'Microsoft.DirectX.AudioVideoPlayback.dll' or one of its dependencies.
It is not a valid Win32 application. (Exception from HRESULT: 0x800700C1)
我当然没有忘记使用:
using System.Media;
using Microsoft.DirectX.AudioVideoPlayback;
using Microsoft.DirectX;
现在我不知道我能做些什么 - 我需要一个有经验的人的帮助:)
【问题讨论】:
我不明白Invoke
部分,你不应该调用NoteOn
吗? noteOnHandler
是什么?你怎么称呼NoteOn
?从哪里来?
我使用了一个完整的源代码:link我编辑了 form1.cs 并且项目依赖于 Midi 文件夹也放在上面的链接中。
您正在编辑的程序希望用户按下 windows 窗体上的播放按钮。通过使用您的 midi 键盘,您可以有效地弹奏音符,但也可以向“NoteOn”方法发送一个事件,然后当您调用 clock.Schedule 时该方法会触发第二个音符。总之,您正在编辑的程序可能不是您要达到的目标的正确起点。
我可以向您 Paul 寻求有关当前代码的任何帮助吗?你能告诉我1)我按下外部midi键和2)playButton_click之间的联系吗?试图理解代码,但找不到声音加倍的连接
【参考方案1】:
在CL.'s answer 上展开,这是可能发生的事件序列:
-
您按下 MIDI 键盘上的一个键,它会发出一个
Note On message, with a Pitch of A0, and a velocity
(确切的速度可能每次都不同)。
您的代码会收到此消息,并且由于它与您的 if
语句匹配,因此会发送一个 Note On message, with a pitch of A0, and a velocity of 80
。
这就是棘手的地方。某些 MIDI 设备会在您松开按键时发送具有相同音高的相应 Note Off
消息。其他设备将发送第二条Note On
消息,其音高相同,但速度为 0。这两种类型的消息在功能上是(或应该是)等效的,每个设备都可以停止播放音符(甚至两个都)。所以,
-
您松开键盘上的键,发送
Note On message, with a Pitch of A0, and a Velocity of 0
。
您的代码会收到此消息,并且由于它也与您的 if
语句匹配,因此会发送一个 Note On message, with a pich of A0, and a velocity of 80
。
由于速度为 0 的 Note On 也是 Note Off,因此您的代码会将键盘的“Note Off”重新变为 Note On,从而播放两次声音。
作为Chris Dunaway suggested,您需要通过测试速度为 0 来确定 Note On 是否实际上是“Note Off”。
if (nutka == "A0" && msg.Velocity != 0)
clock.Schedule(new NoteOnMessage(outputDevice, Channel.Channel1, Pitch.A0, 80, 2));
【讨论】:
我要感谢你们所有人和 CL 的规范!但是亲爱的贾斯汀,我要真诚地感谢你!您准确而详细地向我解释了我的问题,并且您惊人地解决了它。现在我逐步理解了为什么它会播放两次声音,多亏了你,我现在可以处理它了。太好了,有人拥有丰富的实践知识,他们与像我这样的初学者分享:) 是否可以通过使用clock.Schedule来播放资源中的声音? @Martin 对不起,我不太明白你的意思。请随时发布另一个包含新问题的所有详细信息的问题,并将其标记为 [midi]。【参考方案2】:MIDI 1.0 详细规范说:
MIDI 提供了两种大致等效的关闭音符(声音)的方法。可以通过为相同的音符编号和通道发送 Note-Off 消息,或者通过为该音符和通道发送一个力度值为零的 Note-On 消息来关闭一个音符。使用“Note-On at zero velocity”的好处是可以避免在使用 Running Status 时发送额外的状态字节。
由于这种效率,发送速度值为零的 Note-On 消息是最常用的方法。然而,一些键盘乐器实现释放速度,其中使用伴随“速度关闭”字节的 Note-Off 代码 (8nH)。接收者必须能够识别关闭音符的任何一种方法,并且应该以同样的方式对待它们。
【讨论】:
还是想不通,不幸的是我不是一个逻辑思维:(需要任何帮助:) @Martin - 我认为您需要在NoteOn
方法中检查 msg.Velocity
,如果 Velocity 为 0,那么这是一个注释(如果我理解 CL 发布的内容)。
以上是关于Midi-dot-net 声音播放两次的主要内容,如果未能解决你的问题,请参考以下文章