C#でWAVE再生

#概要
C#でwavを動的生成して再生するサンプル。

#環境
Visual Studio Community 2017

#wavファイルの再生
先ずwavファイル再生のおさらい。
Visual C#のフォームアプリケーションを作成し、フォームにボタンとClickイベントハンドラを作る。そして適当なwavファイルを用意する。

```c#
        private void button1_Click(object sender, EventArgs e)
        {
            var player = new SoundPlayer();
            player.SoundLocation = @"c:\tmp\sample.wav";
            player.Load();
            player.Play();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var player = new SoundPlayer(@"c:\tmp\sample.wav");
            player.Play();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var stream = File.OpenRead(@"c:\tmp\sample.wav");
            var player = new SoundPlayer(stream);
            player.Play();
        }
```

#wavの動的生成と再生
波形データを生成し、WaveFileHeaderを付けてMemoryStreamに書き込んで再生する。またFileStreamに書き込めばそのままwavファイルとなる。

```c#:Form1.cs
using System;
using System.IO;
using System.Media;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace GenerateWave
{
    [StructLayout(LayoutKind.Sequential)]
    class WaveFileHeader
    {
        public uint riff_ckid = 0x46464952;    // "RIFF"
        public uint riff_cksize;
        public uint fccType = 0x45564157;      // "WAVE"
        public uint fmt_ckid = 0x20746d66;     // "fmt "
        public uint fmt_cksize = 16;
        public ushort wFormatTag = 0x0001;     // WAVE_FORMAT_PCM
        public ushort nChannels;
        public uint nSamplesPerSec;
        public uint nAvgBytesPerSec;
        public ushort nBlockAlign;
        public ushort wBitsPerSample;
        public uint data_ckid = 0x61746164;    // "data"
        public uint data_cksize;
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            const uint sampleRate = 48000;  // サンプリング周波数

            // 波形データの生成
            uint wavelen = sampleRate / 2;
            byte[] wave = new byte[wavelen];

            double t = 0;
            for (uint i = 0; i < wavelen; i++)
            {
                double freq = (i < wavelen / 2) ? 2000 : 1000;
                t = (t + freq / sampleRate) % 1;
                wave[i] = (byte)(128 + (t < 0.5 ? 1 : -1) * 10);
            }

            // WAVEファイルヘッダ
            var wfh = new WaveFileHeader();

            wfh.nChannels = 1;
            wfh.nSamplesPerSec = sampleRate;
            wfh.nAvgBytesPerSec = sampleRate;
            wfh.nBlockAlign = 1;
            wfh.wBitsPerSample = 8;

            wfh.riff_cksize = 36 + wavelen;
            wfh.data_cksize = wavelen;

            //using (FileStream stream = new FileStream(@"c:\tmp\hoge.wav", FileMode.Create))
            using (MemoryStream stream = new MemoryStream())
            {
                FieldInfo[] infos = typeof(WaveFileHeader).GetFields();
                foreach (FieldInfo info in infos)
                {
                    byte[] ba = BitConverter.GetBytes(Convert.ToUInt32(info.GetValue(wfh)));
                    stream.Write(ba, 0, Marshal.SizeOf(info.FieldType));
                }
                stream.Write(wave, 0, wave.Length);

                stream.Seek(0, SeekOrigin.Begin);
                var player = new SoundPlayer(stream);
                player.Play();
            }
        }
    }
}
```

別の方法

```c#:Form1.cs
using System;
using System.IO;
using System.Media;
using System.Text;
using System.Windows.Forms;

namespace GenerateWave
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            const uint sampleRate = 44100;  // サンプリング周波数

            // 波形データの生成
            uint wavelen = (uint)(sampleRate * 1.0);
            byte[] wave = new byte[wavelen];

            double t = 0;
            for (uint i = 0; i < wavelen; i++)
            {
                t = (t + 880.0 / sampleRate) % 1;
                wave[i] = (byte)(128 + Math.Sin(2 * Math.PI * t) * (1 - i / (double)wavelen) * 10);
            }

            //using (FileStream st = new FileStream(@"c:\tmp\hoge.wav", FileMode.Create))
            using (MemoryStream st = new MemoryStream())
            {
                // WAVEファイルヘッダ
                WriteStr(st, "RIFF");
                WriteVal(st, 4, wavelen + 36);
                WriteStr(st, "WAVE");
                WriteStr(st, "fmt ");
                WriteVal(st, 4, 16);
                WriteVal(st, 2, 0x0001);         // WAVE_FORMAT_PCM
                WriteVal(st, 2, 1);              // nChannels
                WriteVal(st, 4, sampleRate);     // nSamplesPerSec
                WriteVal(st, 4, sampleRate);     // nAvgBytesPerSec
                WriteVal(st, 2, 1);              // nBlockAlign
                WriteVal(st, 2, 8);              // wBitsPerSample
                WriteStr(st, "data");
                WriteVal(st, 4, wavelen);

                st.Write(wave, 0, wave.Length);

                st.Seek(0, SeekOrigin.Begin);
                SoundPlayer player = new SoundPlayer(st);
                player.Play();
            }
        }

        private void WriteVal(Stream st, int len, uint value)
        {
            byte[] ba = BitConverter.GetBytes(value);
            st.Write(ba, 0, len);
        }

        private void WriteStr(Stream st, string str)
        {
            byte[] ba = Encoding.ASCII.GetBytes(str);
            st.Write(ba, 0, ba.Length);
        }
    }
}
```