MIDI on C#

Win32APIを利用して、直接MIDIの音を鳴らせた!

using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace MIDI
{
    class Program
    {
        static void Main(string[] args)
        {
            IntPtr hMidi;
            midiOutOpen(out hMidi, -1, null, 0, CALLBACK.NULL).ToString();


            midiOutShortMsg(hMidi, 0xc, 0, GMProgram.VoiceOohs, 40).ToString();
            midiOutShortMsg(hMidi, 0x9, 0, 0x45, 40).ToString();
            Confirm("ラ");
            midiOutShortMsg(hMidi, 0x9, 0, 0x45, 0).ToString();

            midiOutReset(hMidi);
            midiOutClose(hMidi);
        }

        static private void Confirm(string mes)
        {
            Console.WriteLine(mes);
            Console.ReadKey(true);
        }

        /* =============================================== */


        /// <summary>Callbackの種類</summary>
        public enum CALLBACK
        {
            /// <summary>イベントハンドル</summary>
            EVENT = 0x50000,
            /// <summary>コールバック関数のポインタ</summary>
            FUNCTION = 0x30000,
            /// <summary>使用しない</summary>
            NULL = 0x0,
            /// <summary>スレッドハンドル</summary>
            THREAD = 0x20000,
            /// <summary>ウィンドウハンドル</summary>
            WINDOW = 0x10000,
        }

        public static class MIDI
        {
            /// <summary>優先されるデバイス</summary>
            public const int MAPPAR = -1;
        }


        public static class MIDIERR
        {
            private const uint BASE = 64;
            /// <summary>メッセージにステータスバイトがない</summary>
            public const uint NODEVICE = BASE + 4;
            /// <summary>MIDI ポートが見つからない</summary>
            public const uint BADOPENMODE = BASE + 6;
            /// <summary>デバイスが別のスレッドで使われている </summary>
            public const uint NOTREADY = BASE + 3;
            /// <summary>デバイスが別のスレッドで使われている </summary>
            public const uint STILLPLAYING = BASE + 1;

        }


        public static class MMSYSERR
        {
            private const uint BASE = 0;
            /// <summary>指定のリソースはすでに割り当てられている </summary>
            public const uint ALLOCATED = BASE + 4;
            /// <summary>指定のデバイス識別子が不正である</summary>
            public const uint BADDEVICEID = BASE + 2;
            /// <summary>指定のポインタ、構造体は無効である</summary>
            public const uint INVALPARAM = BASE + 11;
            /// <summary>必要なメモリを割り当てる事ができなかった</summary>
            public const uint NOMEM = BASE + 7;
            /// <summary>指定のデバイスハンドルが無効である</summary>
            public const uint INVALHANDLE = BASE + 5;
        }

        /// <summary>
        /// コールバック関数のデリゲート
        /// </summary>
        /// <param name="hmo">MIDI 出力デバイスのハンドル</param>
        /// <param name="hwnd">MIDI 出力の状態を表すメッセージ</param>
        /// <param name="dwInstance">関数で提供されたユーザー定義のインスタンスデータ</param>
        /// <param name="dwParam1">メッセージの追加情報1</param>
        /// <param name="dwParam2">メッセージの追加情報2</param>
        public delegate void MidiOutProc(
            IntPtr hmo,
            uint hwnd,
            int dwInstance,
            int dwParam1,
            int dwParam2
            );

        [DllImport("Winmm.dll")]
        public static extern uint midiOutGetNumDevs();

        /// <summary>MIDI出力デバイスを開く
        /// </summary>
        /// <param name="lphmo">MIDI出力デバイスのハンドルへのポインタ</param>
        /// <param name="uDeviceID">オープンするMIDI出力デバイスの識別子</param>
        /// <param name="dwCallback">MIDI出力に関連したメッセージを受け取るコールバック関数、ウィンドウハンドル、スレッドハンドルのいずれか</param>
        /// <param name="dwCallbackInstance">コールバック関数に渡す追加データ</param>
        /// <param name="dwFlags">コールバックのタイプ(CALLBACKクラス定数)</param>
        /// <returns>
        /// MIDIERR.NODEVICE,
        /// MMSYSERR.NOERROR,
        /// MMSYSERR.ALLOCATED,
        /// MMSYSERR.BADDEVICEID,
        /// MMSYSERR.INVALPARAM,
        /// MMSYSERR.NOMEM
        /// </returns>
        [DllImport("Winmm.dll")]
        public static extern uint midiOutOpen(
            out IntPtr lphmo,
                int uDeviceID,
                MidiOutProc dwCallback,
                int dwCallbackInstance,
                CALLBACK dwFlags
            );


        /// <summary>開いたデバイスにMIDIメッセージを送信</summary>
        /// <param name="hmo">MIDI 出力デバイスのハンドル</param>
        /// <param name="dwMsg">MIDI メッセージ</param>
        /// <returns></returns>
        [DllImport("Winmm.dll")]
        public static extern uint midiOutShortMsg(IntPtr hmo, int dwMsg);

        public static uint midiOutShortMsg(IntPtr hmo, byte status, byte channel, byte data1, byte data2)
        {
            return midiOutShortMsg(hmo, (status << 4) | channel | (data1 << 8) | (data2 << 16));
        }
        public static uint midiOutShortMsg(IntPtr hmo, byte status, byte channel, GMProgram data1, byte data2) { return midiOutShortMsg(hmo, (status << 4) | channel | ((byte)data1 << 8) | (data2 << 16)); }


        /// <summary>
        /// 再生を停止
        /// </summary>
        /// <param name="hmo">MIDI出力デバイスのハンドル</param>
        /// <returns>
        /// MMSYSERR.NOERROR,
        /// MMSYSERR.INVALHANDLE
        /// </returns>
        [DllImport("Winmm.dll")]
        public static extern uint midiOutReset(IntPtr hmo);

        /// <summary>
        /// デバイスを閉じる
        /// </summary>
        /// <param name="hmo">MIDI出力デバイスのハンドル</param>
        /// <returns>
        /// MMSYSERR.NOERROR,
        /// MIDIERR.STILLPLAYING,
        /// MMSYSERR.INVALHANDLE,
        /// MMSYSERR.NOMEM
        /// </returns>
        [DllImport("Winmm.dll")]
        public static extern uint midiOutClose(IntPtr hmo);

        public enum GMProgram : byte
        {AcousticGrandPiano, BrightAcousticPiano, ElectricGrandPiano, HonkyTonkPiano, ElectricPiano1, ElectricPiano2, Harpsichord, Clavinet, Celesta, Glockenspiel, MusicBox, Vibraphone, Marimba, Xylophone, TubularBells, Dulcimer, DrawbarOrgan, PercussiveOrgan, RockOrgan, ChurchOrgan, ReedOrgan, Accordion, Harmonica, TangoAccordion, AcousticGuitarNylon, AcousticGuitarSteel, ElectricGuitarJazz, ElectricGuitarClean, ElectricGuitarMuted, OverdrivenGuitar, DistortionGuitar, GuitarHarmonics, AcousticBass, ElectricBassFinger, ElectricBassPick, FretlessBass, SlapBass1, SlapBass2, SynthBass1, SynthBass2, Violin, Viola, Cello, Contrabass, TremoloStrings, PizzicatoStrings, OrchestralHarp, Timpani, StringEnsemble1, StringEnsemble2, SynthStrings1, SynthStrings2, ChoirAahs, VoiceOohs, SynthVoice, OrchestraHit, Trumpet, Trombone, Tuba, MutedTrumpet, FrenchHorn, BrassSection, SynthBrass1, SynthBrass2, SopranoSax, AltoSax, TenorSax, BaritoneSax, Oboe, EnglishHorn, Bassoon, Clarinet, Piccolo, Flute, Recorder, PanFlute, BlownBottle, Shakuhachi, Whistle, Ocarina, Lead1Square, Lead2Sawtooth, Lead3Calliope, Lead4Chiff, Lead5Charang, Lead6Voice, Lead7Fifths, Lead8BassAndLead, Pad1NewAge, Pad2Warm, Pad3Polysynth, Pad4Choir, Pad5Bowed, Pad6Metallic, Pad7Halo, Pad8Sweep, Fx1Rain, Fx2Soundtrack, Fx3Crystal, Fx4Atmosphere, Fx5Brightness, Fx6Goblins, Fx7Echoes, Fx8SciFi, Sitar, Banjo, Shamisen, Koto, Kalimba, BagPipe, Fiddle, Shanai, TinkleBell, Agogo, SteelDrums, Woodblock, TaikoDrum, MelodicTom, SynthDrum, ReverseCymbal, GuitarFretNoise, BreathNoise, Seashore, BirdTweet, TelephoneRing, Helicopter, Applause, Gunshot}
    }
}

参考

Win32APIを呼び出してて困るのが、変数の型とか定数とか。定数の型は、Visual C++で入力すると、定義がポップアップになるので、それで定義できる。変数の型は試行錯誤ですねw だいぶ慣れましたが。

今度はWaveを直接出力してみようと思う。