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

Form1.cs
using System;
using System.IO;
using System.Linq;
using System.Media;
using System.Windows.Forms;
 
namespace playwav2
{
    struct Note
    {
        public int num;
        public int tick;
 
        public Note(int num, int tick)
        {
            this.num = num;
            this.tick = tick;
        }
    }
 
    public partial class Form1 : Form
    {
        private const int sampleRate = 48000;
        private const int timebase = 24;
        private int tempo = 120;
 
        private SoundPlayer player = null;
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            var stream = CreateSound();
            PlaySound(stream);
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            StopSound();
        }
 
        private void PlaySound(Stream stream)
        {
            StopSound();
 
            player = new SoundPlayer(stream);
            player.Play();
        }
 
        private void StopSound()
        {
            if (player != null)
            {
                player.Stop();
                player = null;
            }
        }
 
        private MemoryStream CreateSound()
        {
            var nChannels = 1;
            var wBitsPerSample = 8;
            var nBlockAlign = nChannels * (wBitsPerSample / 8);
            var nAvgBytesPerSec = sampleRate * nBlockAlign;
 
            var data = CreateData();
 
            //
            var ms = new MemoryStream();
            var bw = new BinaryWriter(ms);
 
            // RIFF
            bw.Write("RIFF".ToCharArray());
            bw.Write((uint)(36 + data.Length));  // cksize
 
            bw.Write("WAVE".ToCharArray());
 
            // fmt
            bw.Write("fmt ".ToCharArray());
            bw.Write((uint)16);  // cksize
            // WAVEFORMATEX
            bw.Write((ushort)1);  // wFormatTag 1:WAVE_FORMAT_PCM
            bw.Write((ushort)nChannels);
            bw.Write((uint)sampleRate);  // nSamplesPerSec
            bw.Write((uint)nAvgBytesPerSec);
            bw.Write((ushort)nBlockAlign);
            bw.Write((ushort)wBitsPerSample);
 
            // data
            bw.Write("data".ToCharArray());
            bw.Write((uint)data.Length);  // cksize
            bw.Write(data);
 
            //using (var fs = new FileStream(@"c:\tmp\sample.wav", FileMode.Create, FileAccess.Write))
            //{
            //    ms.Position = 0;
            //    ms.CopyTo(fs);
            //}
 
            ms.Position = 0;
            return ms;
        }
 
        private byte[] CreateData()
        {
            tempo = 150;
            int dataLen = (int)(TickToSample(timebase * 8));
            //var data = new byte[dataLen];
            var data = Enumerable.Repeat<byte>(0x80, dataLen).ToArray();
 
            // note number, tick
            byte[,] ch1 = {
                { 60, 24 },
                { 62, 24 },
                { 64, 24 },
                { 65, 24 },
                { 64, 24 },
                { 62, 24 },
                { 60, 24 },
                {  0, 24 },
            };
            byte[,] ch2 = {
                { 48, 24 },
                {  0, 24 },
                { 48, 24 },
                {  0, 24 },
                { 48, 24 },
                {  0, 24 },
                { 48, 24 },
                {  0, 24 },
            };
 
            if (checkBox1.Checked) MakeWaveform(ref data, ch1, 10);
            if (checkBox2.Checked) MakeWaveform(ref data, ch2, 8);
 
            return data;
        }
 
        private void MakeWaveform(ref byte[] data, byte[,] notes, int gain)
        {
            double pos = 0;
            for (var i = 0; i < notes.GetLength(0); i++)
            {
                double sample = TickToSample(notes[i, 1]);
                if (notes[i, 0] > 0)
                {
                    double freq = 440 * Math.Pow(2, (notes[i, 0] - 69) / 12.0);
                    double f = 0;
                    var epos = (int)(pos + sample);
                    for (var j = (int)pos; j < epos; j++)
                    {
                        f += freq;
                        if (sampleRate <= f) f -= sampleRate;
                        double t = f / sampleRate;
                        data[j] += (byte)((t < 0.5 ? 1 : -1) * gain);
                    }
                }
                pos += sample;
            }
        }
 
        double TickToSample(int tick)
        {
            return (tick / (double)timebase) * (60.0 / tempo) * sampleRate;
        }
    }
}
 

最終更新:2018年05月18日 21:05
添付ファイル