開発環境 Microsoft Visual Studio Community 2017
実行環境 Microsoft Windows 10 Home (64-bit)
プロジェクトの種類 Visual C# / Windows フォーム アプリケーション (.NET Framework)
プロジェクト名 waveout

Form1.cs
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
[StructLayout(LayoutKind.Sequential)]
struct WAVEFORMATEX
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}
[StructLayout(LayoutKind.Sequential)]
struct WAVEHDR
{
    public IntPtr lpData;
    public uint dwBufferLength;
    public uint dwBytesRecorded;
    public uint dwUser;
    public uint dwFlags;
    public uint dwLoops;
    public IntPtr lpNext;
    public uint reserved;
}
 
namespace waveout
{
    public partial class Form1 : Form
    {
        [DllImport("winmm.dll")]
        private static extern uint waveOutOpen(ref IntPtr phwo, uint uDeviceID,
            ref WAVEFORMATEX pwfx, IntPtr dwCallback, uint dwCallbackInstance, uint fdwOpen);
        [DllImport("winmm.dll")]
        private static extern uint waveOutClose(IntPtr hwo);
        [DllImport("winmm.dll")]
        private static extern uint waveOutPrepareHeader(IntPtr hwo, ref WAVEHDR pwh, uint cbwh);
        [DllImport("winmm.dll")]
        private static extern uint waveOutUnprepareHeader(IntPtr hwo, ref WAVEHDR pwh, uint cbwh);
        [DllImport("winmm.dll")]
        private static extern uint waveOutWrite(IntPtr hwo, ref WAVEHDR pwh, uint cbwh);
        [DllImport("winmm.dll")]
        private static extern uint waveOutReset(IntPtr hwo);
 
        private const ushort WAVE_FORMAT_PCM = 1;
        private const uint WAVE_MAPPER = unchecked((uint)-1);
        private const uint CALLBACK_NULL = 0;
        private const uint CALLBACK_WINDOW = 0x00010000;
        private const int MM_WOM_OPEN = 0x3BB;
        private const int MM_WOM_CLOSE = 0x3BC;
        private const int MM_WOM_DONE = 0x3BD;
 
        private const int SAMPLE_RATE = 48000;
        private readonly uint CBWH = (uint)Marshal.SizeOf(typeof(WAVEHDR));
 
        private IntPtr hwo = IntPtr.Zero;
        private WAVEHDR wh = new WAVEHDR();
        private IntPtr lpData = IntPtr.Zero;
 
        public Form1()
        {
            InitializeComponent();
        }
 
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
 
            switch (m.Msg)
            {
                case MM_WOM_OPEN:
                    Trace.WriteLine(">> MM_WOM_OPEN:");
                    OnWomOpen();
                    break;
                case MM_WOM_CLOSE:
                    Trace.WriteLine(">> MM_WOM_CLOSE:");
                    break;
                case MM_WOM_DONE:
                    Trace.WriteLine(">> MM_WOM_DONE:");
                    Stop();
                    break;
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            Play();
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            Stop();
        }
 
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Stop();
        }
 
        private void OnWomOpen()
        {
            uint mmr;
            mmr = waveOutWrite(hwo, ref wh, CBWH);
            Trace.WriteLine("waveOutWrite:" + mmr);
        }
 
        private void Play()
        {
            byte[] data = GenerateWaveform();
 
            lpData = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, lpData, data.Length);
 
            //
            uint mmr;
 
            WAVEFORMATEX wfx = new WAVEFORMATEX();
            wfx.wFormatTag = WAVE_FORMAT_PCM;
            wfx.nChannels = 1;
            wfx.nSamplesPerSec = SAMPLE_RATE;
            wfx.nAvgBytesPerSec = SAMPLE_RATE;
            wfx.nBlockAlign = 1;
            wfx.wBitsPerSample = 8;
            wfx.cbSize = 0;
            mmr = waveOutOpen(ref hwo, WAVE_MAPPER, ref wfx, Handle, 0, CALLBACK_WINDOW);
            Trace.WriteLine("waveOutOpen:" + mmr);
 
            wh.lpData = lpData;
            wh.dwBufferLength = (uint)data.Length;
            wh.dwFlags = 0;
            mmr = waveOutPrepareHeader(hwo, ref wh, CBWH);
            Trace.WriteLine("waveOutPrepareHeader:" + mmr);
 
            button1.Enabled = false;
        }
 
        private void Stop()
        {
            Trace.WriteLine("Stop()");
            if (hwo != IntPtr.Zero)
            {
                uint mmr;
                mmr = waveOutReset(hwo);
                Trace.WriteLine("waveOutReset:" + mmr);
                mmr = waveOutUnprepareHeader(hwo, ref wh, CBWH);
                Trace.WriteLine("waveOutUnprepareHeader:" + mmr);
                mmr = waveOutClose(hwo);
                Trace.WriteLine("waveOutClose:" + mmr);
                hwo = IntPtr.Zero;
            }
            if (lpData != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(lpData);
                lpData = IntPtr.Zero;
            }
            button1.Enabled = true;
        }
 
        private byte[] GenerateWaveform()
        {
            int[] note = new int[] { 0, 2, 4, 5, 7, 9, 11, 12 };
 
            int data_len = SAMPLE_RATE * 4;
            byte[] data = Enumerable.Repeat((byte)128, data_len).ToArray();
 
            int pos = 0;
            for (int i = 0; i < 8; i++)
            {
                double freq = 440 * Math.Pow(2, (note[i] - 9) / 12.0);
                int gate = SAMPLE_RATE / 2;
                for (int j = 0; j < gate; j++)
                {
                    double t = (j * freq / SAMPLE_RATE) % 1.0;
                    data[pos + j] += (byte)((t < 0.5 ? 1 : -1) * 8);
                }
                pos += gate;
            }
 
            return data;
        }
    }
}
 
最終更新:2018年03月27日 10:20