midioutshortmsg нет звука

Итак, я пытаюсь воспроизвести отдельные ноты на С#, используя midiOutShortMsg(). Проблема в том, что не воспроизводится звук. Один из способов воспроизведения заметки, который я придумал, — это поместить midiOutShortMsg() в цикл for от i=0 до 10000. Но я не верю, что 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();
        }
    }
}

person Shinsuke    schedule 03.11.2016    source источник
comment
Я считаю, что такой подход ошибочен. Для этого используйте существующую библиотеку MIDI. Я очень уважаю naudio. Он уже содержит управляемую оболочку для MIDI IO, которая должна быть хорошо протестирована. Почему бы вам не использовать его вместо того, чтобы тратить огромное количество времени на написание миди-обертки, заботясь о порядке байтов и тому подобном? Это сэкономит вам массу времени и имеет универсальную поддержку приложений из коробки. Вы можете легко вставить его в свой проект.   -  person spender    schedule 03.11.2016
comment
Здравствуй, покупатель! Большое спасибо за ответ! Сегодня я весь день работал с naudio, но обнаружил, что для воспроизведения отдельных нот также требуется использование System.Threading.Thread.Sleep(). И я получаю ту же проблему, когда воспроизведение одной ноты останавливает всю программу, и звук не воспроизводится. Вы случайно не знаете какие-нибудь обходные пути?   -  person Shinsuke    schedule 07.11.2016
comment
Кажется, Ваш звук начинает воспроизводиться... на некоторое время, но Вы Закрываете драйвер. Если Вы не хотите ждать, то используйте тайминг: запустите звук, уйдите в другое место, а когда время придет, вернитесь, и остановите этот звук, и/или запустите другой. Если ваша программа закончилась, закройте устройство.   -  person CLS    schedule 09.06.2020


Ответы (1)


Это связано с тем, что midiOutShortMsg не останавливает выполнение кода, а это означает, что midiOutClose вызывается до того, как нота успела сыграть.

Один из способов обойти это — добавить Sleep:

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

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

res = midiOutClose(handle);

Где length — это количество времени в мс, которое требуется для завершения воспроизведения ноты.

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

person Bassie    schedule 03.11.2016
comment
Делая что-либо с обычными оконными таймерами с MIDI, синхронизация будет настолько неряшливой, что это будет узнаваемо даже для самых музыкально неграмотных. - person spender; 03.11.2016
comment
Басси спасибо за ответ! К сожалению, System.Threading.Thread.Sleep() останавливает всю программу, поскольку Kinect по умолчанию является однопоточным. Я изучаю многопоточность, но kinect обновляется со скоростью 30 кадров в секунду, я не хочу случайно создавать сумасшедшее количество потоков, и мне просто интересно, знаете ли вы другие обходные пути! - person Shinsuke; 07.11.2016
comment
@Shinsuke Нет проблем - вы можете проверить, выводит ли приложение какой-либо звук, и не вызывать midiOutClose, пока звук не перестанет воспроизводиться. - person Bassie; 10.11.2016