【问题标题】:midioutshortmsg no soundmidioutshortmsg 没有声音
【发布时间】:2017-03-16 11:59:16
【问题描述】:

所以我尝试使用 midiOutShortMsg() 在 C# 中播放单个音符。问题是没有声音播放。我想出的播放音符的一种方法是将 midiOutShortMsg() 置于 i=0 到 10000 的 for 循环中。但我不相信 API 应该是这样工作的。

在项目的后期,我想将 MIDI 实现到 Kinect 项目中,并且让 for 循环延迟 Kinect 的实时反馈。所以for循环方法是不行的。

以下是我用来播放音符的代码,如果你注释掉 for 循环,则不会播放任何声音。任何帮助将不胜感激。

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace MIDITest
{

    [StructLayout(LayoutKind.Sequential)]
    public struct MidiOutCaps
    {
        public UInt16 wMid;
        public UInt16 wPid;
        public UInt32 vDriverVersion;

        [MarshalAs(UnmanagedType.ByValTStr,
           SizeConst = 32)]
        public String szPname;

        public UInt16 wTechnology;
        public UInt16 wVoices;
        public UInt16 wNotes;
        public UInt16 wChannelMask;
        public UInt32 dwSupport;
    }

class Program
    {
        // MCI INterface
        [DllImport("winmm.dll")]
        private static extern long mciSendString(string command,
           StringBuilder returnValue, int returnLength,
           IntPtr winHandle);

        // Midi API
        [DllImport("winmm.dll")]
        private static extern int midiOutGetNumDevs();

        [DllImport("winmm.dll")]
        private static extern int midiOutGetDevCaps(Int32 uDeviceID,
           ref MidiOutCaps lpMidiOutCaps, UInt32 cbMidiOutCaps);

        [DllImport("winmm.dll")]
        private static extern int midiOutOpen(ref int handle,
           int deviceID, MidiCallBack proc, int instance, int flags);

        [DllImport("winmm.dll")]
        private static extern int midiOutShortMsg(int handle,
           int message);

        [DllImport("winmm.dll")]
        private static extern int midiOutClose(int handle);

        private delegate void MidiCallBack(int handle, int msg,
           int instance, int param1, int param2);

        static void Main()
        {
            int handle = 0;

            var numDevs = midiOutGetNumDevs();
            Console.WriteLine("You have {0} midi output devices", numDevs);
            MidiOutCaps myCaps = new MidiOutCaps();
            var res = midiOutGetDevCaps(0, ref myCaps,
                   (UInt32)Marshal.SizeOf(myCaps));

            res = midiOutOpen(ref handle, 0, null, 0, 0);

            byte[] data = new byte[4];

            data[0] = 0x90;
            data[1] = 50;
            data[2] = 111;
            uint msg = BitConverter.ToUInt32(data, 0);

            for (int i = 0; i < 10000; i++)
            {
                // both hard coding the message and creating it with byte doesn't work
                //res = midiOutShortMsg(handle, 0x007F4A90);
                res = midiOutShortMsg(handle, (int)msg);
            }
            res = midiOutClose(handle);
            Console.ReadLine();
        }
    }
}

【问题讨论】:

  • 我认为这种方法是错误的。使用现有的 MIDI 库来执行此操作。我非常尊重naudio。它已经包含一个 MIDI IO 的托管包装器,应该经过良好测试。您为什么不使用它,而不是浪费大量时间编写 midi 包装器,担心字节顺序之类的问题?它将为您节省大量时间,并提供开箱即用的通用应用程序支持。您可以轻松nuget it into your project
  • 您好,非常感谢您的回复!我今天整天都在使用 naudio,但我发现演奏单音也需要使用 System.Threading.Thread.Sleep()。而且我遇到了同样的问题,即播放单个音符会停止整个程序并且不播放声音。你会碰巧知道任何解决方法吗?
  • 您的声音似乎开始播放了...有一段时间,但您关闭了驱动程序。如果您不想等待,请使用计时:开始一个声音,去其他地方,当时间到了,回来,停止那个声音,和/或开始另一个。如果您的程序结束,请关闭设备。

标签: c# audio visual-studio-2015 midi winmm


【解决方案1】:

这是因为midiOutShortMsg 不会停止代码的执行,这意味着midiOutClose 在音符有时间播放之前被调用。

解决这个问题的一种方法是添加Sleep

res = midiOutShortMsg(handle, (int)msg);

if (res == 0) // Check success code
{
    System.Threading.Thread.Sleep(length);
}

res = midiOutClose(handle);

其中length 是音符完成播放所需的时间(以毫秒为单位)。

但是,几乎可以肯定不推荐这样做。

【讨论】:

  • 使用带有 MIDI 的普通 windows 计时器做任何事情都会使计时变得如此草率,以至于即使是最不懂音乐的人也能认出来。
  • 感谢您的回复,巴斯!不幸的是,使用 System.Threading.Thread.Sleep(),它会暂停整个程序,因为 Kinect 默认是单线程的。我正在研究多线程,但 kinect 以每秒 30 帧的速度更新,我不想意外创建大量线程,只是想知道您是否知道其他解决方法!
  • @Shinsuke 没问题 - 您可以检查应用程序是否正在输出任何声音,并且在声音不再播放之前不要调用 midiOutClose
猜你喜欢
  • 2017-10-19
  • 2021-04-22
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多