2016-11-03 5 views
0

Итак, я пытаюсь играть в одну ноту в C#, используя midiOutShortMsg(). Проблема в том, что звук не воспроизводится. Единственный способ, которым я решил сыграть эту заметку, - это установить midiOutShortMsg() в цикле for от i = 0 до 10000. Но я не верю, что именно так должен работать API.C# midioutshortmsg no sound

Позже в проекте я хочу реализовать MIDI в проекте Kinect, а цикл for задерживает обратную связь Kinect в реальном времени. Таким образом, метод for loop - это не-go.

Ниже приведен код, который я использую для воспроизведения заметок, если вы прокомментируете цикл 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, беспокоясь о порядке байтов и тому подобное? Это сэкономит вам массу времени и будет иметь универсальную поддержку приложений из коробки. Вы можете легко [nuget it в свой проект] (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 это количество времени в миллисекундах, которое требуется для ноты, чтобы закончить игру.

Однако это почти наверняка не рекомендуется.

+1

Выполнение чего-либо с обычными таймерами в Windows с MIDI даст время настолько неряшливо, что оно будет узнаваемо даже самым музыкально-неграмотным. – spender

+0

Спасибо за ваш отзыв Bassie! К сожалению с System.Threading.Thread.Sleep(), он останавливает всю программу, поскольку Kinect по умолчанию является одиночным. Я изучаю многопоточность, но обновление kinect со скоростью 30 кадров в секунду, я не хочу случайно создавать сумасшедшее количество потоков и просто интересно, знаете ли вы другие обходные пути! – Shinsuke

+0

@Shinsuke Нет проблем - вы можете проверить, выводит ли приложение какой-либо звук и не называть 'midiOutClose', пока звук больше не будет воспроизводиться. – Bassie