2016-11-03 100 views
0

所以我試圖在C#中使用midiOutShortMsg()播放單個音符。問題是沒有聲音播放。我已經想出了一個方法來演奏音符,將midiOutShortMsg()放在從i = 0到10000的for循環中。但是我不相信API是如何工作的。C#midioutshortmsg沒有聲音

在後面的項目中,我想將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(); 
     } 
    } 
} 
+1

我認爲這種方法是錯誤的。使用現有的MIDI庫來做到這一點。我非常尊重[naudio](https://github.com/naudio/NAudio)。它已經包含了一個MIDI IO管理包裝,應該經過充分測試。爲什麼不使用它,而不是浪費大量的時間來編寫一個midi包裝,擔心字節排序等?它可以爲您節省大量時間,並提供開箱即用的全面應用支持。您可以輕鬆地將它嵌入您的項目中(https://www.nuget.org/packages/NAudio/)。 – spender

+0

嗨斯佩爾德,非常感謝你的迴應!我今天一直在用naudio工作,但是我發現播放單音符也需要使用System.Threading.Thread.Sleep()。而且我也遇到了同樣的問題,播放單個音符會停止整個節目並且沒有播放聲音。你會碰巧知道任何解決方法嗎? – Shinsuke

回答

0

這是因爲midiOutShortMsg不會停止代碼的執行,這意味着注有時間玩之前midiOutClose被調用。克服這種

一種方法是增加一個Sleep

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

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

res = midiOutClose(handle); 

length是時間(ms)所花費的說明完成播放量。

但是,這幾乎肯定不推薦。

+1

使用正常的Windows定時器和MIDI做任何事情都會給定時間如此sl that以至於即使是音樂最不明的文盲也能識別。 – spender

+0

感謝您的回覆Bassie!不幸的是,使用System.Threading.Thread.Sleep(),它會暫停整個程序,因爲Kinect默認爲單線程。我正在研究多線程,但kinect以每秒30幀的速度更新,我不想意外地創建瘋狂的線程數量,只是想知道是否知道其他解決方法! – Shinsuke

+0

@Shinsuke沒問題 - 你可以檢查應用程序是否輸出任何聲音,並且不要調用'midiOutClose'直到聲音不再播放。 – Bassie