読者です 読者をやめる 読者になる 読者になる

echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

Managed DirectXを動かす その3

その2で書いた通り、次は録音ですね。

※下記サイトを参考にさせて頂きました。感謝です。
Managed DirectSoundを使ってマイクから音声を録音してみる2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Runtime.InteropServices;
using System.Threading;	
using System.IO;	
using System.Windows.Forms;
using System.Diagnostics;

using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;

namespace Sample2
{
    public class CaptureSound : IDisposable
    {
        private WaveFormat fmt;
        private string file_path;
        private bool isRecording;
        private Device device;
        private Capture capture;

        private int notifySize;
        private static int notifyPos = 0;
        private const int NUMBER_RECORD_NOTIFICATIONS = 16;

        private BufferPositionNotify[] PositionNotify = new BufferPositionNotify[NUMBER_RECORD_NOTIFICATIONS];
        private CaptureBuffer captureBuffer;
        CaptureBufferDescription captureBufferDescription;

        private Thread notifyThread;
        private AutoResetEvent autoResetEvent;
        private Notify notify;

        BinaryWriter binaryWriter;
        private int dataLength = 0;

        // コンストラクタ
        public CaptureSound ()
        {
            this.fmt = new WaveFormat();
            this.fmt.FormatTag = WaveFormatTag.Pcm;
            this.fmt.SamplesPerSecond = 44100;
            this.fmt.BitsPerSample = 16;
            this.fmt.Channels = 1;
            this.fmt.BlockAlign = (short)(this.fmt.Channels * (this.fmt.BitsPerSample / (short)8));
            this.fmt.AverageBytesPerSecond = this.fmt.BlockAlign * this.fmt.SamplesPerSecond;

            this.isRecording = false;
            this.file_path = @"C:\Sample2\dummy.wav";
        }

        // 録音開始(ボタンクリック時など。'owner'は呼び出し元フォームなど。
        public bool startRecord(Form owner)
        {
            if (isRecording) { return false; }

            this.device = new Device();
            this.device.SetCooperativeLevel(owner, CooperativeLevel.Priority);
            CaptureDevicesCollection capDev = new CaptureDevicesCollection();

            Guid cap_guid = getGuid(capDev);
            if (Guid.Empty == cap_guid) { return "録音デバイスがありません。"; }

            this.capture = new Capture(cap_guid);
            this.isRecording = true;

            RecWave();
            CreateCapBuffer();
            this.captureBuffer.Start(true);

            return true;

        }

        // *.wavファイルのヘッダー部分を書く
        private void RecWave()
        {
            char[] Riff = { 'R', 'I', 'F', 'F' };
            char[] Wave = { 'W', 'A', 'V', 'E' };
            char[] Fmt = { 'f', 'm', 't', ' ' };
            char[] Data = { 'd', 'a', 't', 'a' };
            short padding = (short)this.fmt.FormatTag;
            int formatLength = 0x10;
            int length = 0;
            short shBytePerSample = (short)((this.fmt.BitsPerSample / (short)8) * this.fmt.Channels);

            try
            {
                this.binaryWriter = new BinaryWriter(new FileStream(this.file_path, FileMode.Create));
                this.binaryWriter.Write(Riff);
                this.binaryWriter.Write(length);
                this.binaryWriter.Write(Wave);
                this.binaryWriter.Write(Fmt);
                this.binaryWriter.Write(formatLength);
                this.binaryWriter.Write(padding);
                this.binaryWriter.Write(this.fmt.Channels);
                this.binaryWriter.Write(this.fmt.SamplesPerSecond);
                this.binaryWriter.Write(this.fmt.AverageBytesPerSecond);
                this.binaryWriter.Write(shBytePerSample);
                this.binaryWriter.Write(this.fmt.BitsPerSample);
                this.binaryWriter.Write(Data);
                this.binaryWriter.Write((int)0);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        // キャプチャバッファの作成
        void CreateCapBuffer()
        {
            this.notifySize = this.fmt.AverageBytesPerSecond / NUMBER_RECORD_NOTIFICATIONS;
            this.notifySize -= this.notifySize % this.fmt.BlockAlign;

            try
            {
                this.captureBufferDescription = new CaptureBufferDescription();
                this.captureBufferDescription.BufferBytes = notifySize * NUMBER_RECORD_NOTIFICATIONS;  //waveFormat.AverageBytesPerSecond;
                this.captureBufferDescription.WaveMapped = false;
                this.captureBufferDescription.ControlEffects = false;
                this.captureBufferDescription.Format = this.fmt;

                this.captureBuffer = new CaptureBuffer(this.captureBufferDescription, this.capture);
            }
            catch (BadFormatException e)
            {
                MessageBox.Show(e.ErrorString);
                return;
            }

            // RecWave();
            notifyThread = new Thread(new ThreadStart(CapThreadProc));
            notifyThread.Start();

            this.autoResetEvent = new AutoResetEvent(false);
            this.notify = new Notify(this.captureBuffer);

            for (int i = 0; i < NUMBER_RECORD_NOTIFICATIONS; i++)
            {
                PositionNotify[i].Offset = this.notifySize * (i + 1) - 1;
                PositionNotify[i].EventNotifyHandle = this.autoResetEvent.Handle;
            }
            notify.SetNotificationPositions(PositionNotify, NUMBER_RECORD_NOTIFICATIONS);
        }

        // 実際に音声をキャプチャして、*.wavファイルのデータ部分に書き出す処理
        public void CapThreadProc()
        {
            // Debug.WriteLine(this.isRecording.ToString());

            while (this.isRecording)
            {
                this.autoResetEvent.WaitOne(Timeout.Infinite, true);
                int offset = 0;
                int readPos, capturePos;
                this.captureBuffer.GetCurrentPosition(out capturePos, out readPos);
                int lockSize = this.notifySize - offset;
                capturePos = this.notifySize * notifyPos;

                if (lockSize < 0) { lockSize += captureBufferDescription.BufferBytes; }

                lockSize -= (lockSize % 2);
                if (0 == lockSize) { return; }

                byte[] captureData = (byte[])captureBuffer.Read(capturePos, typeof(byte), LockFlag.None, lockSize);

                this.binaryWriter.Write(captureData, 0, captureData.Length);
                this.dataLength += captureData.Length;

                notifyPos++;
                notifyPos %= NUMBER_RECORD_NOTIFICATIONS;
            }
        }

        // 「プライマリ・デバイス」以外の録音デバイスのGUIDを取得する
        private Guid getGuid(CaptureDevicesCollection _capDev)
        {
            for (int i = 0; i < _capDev.Count; i++)
            {
                Guid c_guid = _capDev[i].DriverGuid;
                string c_description = _capDev[i].Description;

                if (c_guid != System.Guid.Empty && c_description != "") { return c_guid; }
            }

            return Guid.Empty;
        }

        // 録音停止処理
        public void stopRecord()
        {
            if (!this.isRecording)
            {
                MessageBox.Show("録音していません。", Define.CLASS_NAME);
                return;
            }

            this.captureBuffer.Stop();
            this.notifyThread.Abort();

            // *.wavファイルのヘッダー部分の「ファイルサイズ - 8」を書き込む処理
            int dataSizeMinusEight = this.dataLength + 44 - 8;
            this.binaryWriter.Seek(4, SeekOrigin.Begin);
            this.binaryWriter.Write(dataSizeMinusEight);

            // *.wavファイルのヘッダー部分の「実データのサイズ」を書き込む処理
            this.binaryWriter.Seek(40, SeekOrigin.Begin);
            this.binaryWriter.Write(this.dataLength);

            this.binaryWriter.Close();
            this.isRecording = false;

            MessageBox.Show("録音終了。", Define.CLASS_NAME);
        }

        // 録音中かどうかのフラグを返す(呼び出し元フォーム等で使用する)
        public Boolean getIsRecording()
        {
            return this.isRecording;
        }

        // デストラクタ
        public void Dispose() { }
    }
}

しかし、コーディング技法というか、リーダブルなコードの書き方、もっと勉強しなくては…
この本は買って、読んでいるんだけど…
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)