0

首先我不是专业程序员,但只是一个对优秀的库 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),但它似乎不起作用。

我实在想不通,我正在寻求任何帮助。

更新 ...更新... 更新

出现了另一个问题(由于CLChris 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;

现在我不知道我能一次又一次地做些什么——我需要一个有经验的人的帮助:)

4

2 回答 2

1

MIDI 1.0 详细规范说:

MIDI 提供了两种大致等效的关闭音符(声音)的方法。可以通过为相同的音符编号和通道发送 Note-Off 消息,或者通过为该音符和通道发送一个力度值为零的 Note-On 消息来关闭一个音符。使用“Note-On at zero velocity”的好处是可以避免在使用 Running Status 时发送额外的状态字节。

由于这种效率,发送速度值为零的 Note-On 消息是最常用的方法。然而,一些键盘乐器实现释放速度,其中使用伴随“速度关闭”字节的 Note-Off 代码 (8nH)。接收者必须能够识别关闭音符的任何一种方法,并且应该以相同的方式对待它们。

于 2015-06-19T08:37:40.510 回答
1

扩展CL. 的答案,以下是可能发生的事件序列:

  1. 你按下 MIDI 键盘上的一个键,它会发出一个Note On message, with a Pitch of A0, and a velocity(确切的速度可能每次都不同)。
  2. 您的代码会收到此信息,并且由于它与您的if语句匹配,因此会发送一个Note On message, with a pitch of A0, and a velocity of 80.

这就是棘手的地方。Note Off当您松开按键时,某些 MIDI 设备会发送具有相同音高的相应消息。其他设备将发送Note On具有相同音高但速度为 0 的第二条消息。这两种类型的消息在功能上是(或应该是)等效的,每个设备都可以停止播放音符(甚至两者都可以)。所以,

  1. 您释放键盘上的键,该键发出Note On message, with a Pitch of A0, and a Velocity of 0.
  2. 您的代码会收到此信息,并且由于它也与您的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 建议的那样,您需要通过测试速度为 0 来确定 Note On 是否实际上是“Note Off”。

    if (nutka == "A0" && msg.Velocity != 0) {
        clock.Schedule(new NoteOnMessage(outputDevice, Channel.Channel1, Pitch.A0, 80, 2));
    }
于 2015-06-19T22:55:17.853 回答