C#で streamを使って、secondary bufferに書き込む

入出力音を聞ける。Reset無しでsampling 周波数、FFT ポイント数を、可変できます。

音の取りこぼしも、capturedで、確認できます。

 

1. C#::  write to DirectSound secondary buffer using stream 

   実は、この例は、managed DirectX examples に、無いので、非常に手間取りました。

    なんとか、できました。

memory streamを使います。

作戦は

    サウンドカードでcaptureした、 short型のデータ capture_dataを、streamにコピーし

    このstreamから、DirectSoundの secondary bufferに、書き込もうと、言う訳です。

    結論を先に申しますと、streamに書き込む必要は、ありませんでした、なんのこっちゃ (^_^;;

    つまり、streamを通さないで、capture_dataから、直接、secondary bufferに書き込める事を、発見したのです。

    先に、言っときますけど、

SecondaryBuffer(stream,BufferDescription,Device)のコンストラクターは、作動しません。

streamが入る、コンストラクターは、エラーが出て、作動しないのです、訳わからん....

私は、SecondaryBuffer(BufferDescription,Device)を、使いました。

このコンストラクターを使っても、streamを通して secondarybufferに書き込む事は、可能でした。

streamが要らないのは、secondarybufferの write methodに、

    Write(int,Array,LockFlag)

methodが、あるからです。

2. streamを使う練習

先ずは、secondary buffer及び、 capture dataを想定して、streamを使って書き込む練習です。

class Program

{

    //MemoryStream を、試す

    static void Main(string[] args)

    {

        short[] capture_data = new short[4096*2]; //short型の配列、capture_dataを想定

        byte[] secondary_buf =new byte[4096*4]; //byte型の配列 secondary_bufferを想定

        byte[] dummy=new byte[2];//書き込みに必要な dummy

        int before,after,time;

        // capture_dataを、streamに書き込んで、stream --> secondarybuffer に

        // 書き込み、secondarybuffer --> stream を通して、readに要するtimeを計測。

        MemoryStream mem_st = new MemoryStream(); //MemStream だけで、read, writeできる

        mem_st.Position = 0;

       //--------------------------------------------------------------

        before = Environment.TickCount;//時間計測開始

 

        //capture_dataは short型で、

        //MemoeryStreamの扱う型は、byte型だから変換が必要

        for (int i = 0; i < capture_data.Length; i++)

        {

            dummy = BitConverter.GetBytes(capture_data[i]);

            //MemStreamへ、書き込み

            mem_st.Write(dummy, 0, 2);

        }

        mem_st.Position = 0; //Positionを、巻戻しておく。これが、常に必要

        //MemStreamから、secondary_bufへ、データを読み込む

        mem_st.Read(secondary_buf, 0, capture_data.Length * 2);

        after = Environment.TickCount;//時間計測終了

        time = after - before;

    //--------------------------------------------------------------

        mem_st.Close();

8192byteのデータを、capture したとして、これを、streamを使ってsecondary bufferに書き込む練習です。

streamに書き込み始めてから、secondary bufferにデータを書き込むのに、どれ位の時間が掛かるのか?

興味あるところですね。

debugしてみました所、所要時間は、1ms以下でした、凄い! ( CeleronD 2.53GHz,  256K L2 cache, 533MHz FSB )

しかし、

配列から、secondarybufferに直接書き込めるなら、

streamに書き込んでから、読み書きするのは、無駄ですよね。

3. 実際にDirectSoundを使って、Notifyも使う、実戦寸前のプログラム

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.DirectSound;

using System.IO;

using System.Threading;//for Notification

//using System.Runtime.InteropServices;

namespace stream_test_with_window1

    {

     public partial class Form1 : Form

        {

            my_stream_test st;

            Thread waitThread;

             public Form1()

            {

                InitializeComponent();

            }

            byte[] byte_array;

            private void button1_Click(object sender, EventArgs e)

            {

                try

                {

                    st = new my_stream_test(this);

                }

                catch

                {

                }

                st.prepare_for_soundThread();

                waitThread = new Thread(st.doWait);

                   waitThread.Start();

                while (!waitThread.IsAlive) ;

            }

    private void button2_Click(object sender, EventArgs e)

    {

                st.RequestStop();

                waitThread.Join();

                label2.Text = "Sound end";

                st.Dispose();

                waitThread.Abort();

                 //Close();

             }

//-----------------------------------------------------

   public class my_stream_test : IDisposable

    {

        Form1 frm;

        WaveFormat my_waveformat;

        Device device;

        BufferDescription desc;

        public SecondaryBuffer secondaryBuffer;

        byte[] mem_byte;

        MemoryStream mem_st;

        public AutoResetEvent autoResetEvent;

        int play_pos = 0;

        int write_pos = 0;

        int count = 0;

        public int NotifySize = 0;

        int NotifyPos = 0;

        int writeStartPos = 0;

        BufferPositionNotify[] bufferPositionNotifications;

        // Thread NotifyThread = null;

        Notify notify;

 

        volatile bool _shouldStop=false;

        public void RequestStop()

        {

            _shouldStop = true;

        }

 

        public void doWait()

        {

            write_secondaryBuffer(0);//bufferの最初に、まず、書き込む

            secondaryBuffer.Play(0, BufferPlayFlags.Looping);//ここで、再生を始める

            // データを書き込む無限ループに入る.

            while (!_shouldStop)

            {

                autoResetEvent.WaitOne(Timeout.Infinite, true);

                writeStartPos = NotifySize * NotifyPos;

                for (int i = 0; i < mem_byte.Length; i++)

                {

                    if (writeStartPos == 0)//ここらは、適当に作ったデータです。

                    mem_byte[i] = (byte)(255 * Math.Sin(2 * Math.PI * 1000 * i /mem_byte.Length));

                    else

                    mem_byte[i] = (byte)(255 * Math.Sin(2 * Math.PI * 1000 * i / mem_byte.Length));   

                    }

                    secondaryBuffer.Write(writeStartPos, mem_byte, LockFlag.None);

                    NotifyPos++;

                    NotifyPos %= 2;//loopするから

            }

            secondaryBuffer.Stop();

            secondaryBuffer.SetCurrentPosition(0);

        }

        public void Dispose()

        {

            if (device != null)

                device.Dispose();

            if (desc != null)

                desc.Dispose();

            if (secondaryBuffer != null)

                secondaryBuffer.Dispose();

            if (mem_st != null)

                mem_st.Dispose();

            device = null;

            desc = null;

            secondaryBuffer = null;

            mem_st = null;

         }

    public void set_Notification(SecondaryBuffer sec_buf, int offset)

    {

        autoResetEvent = new AutoResetEvent(false);

        try

        {

            notify = new Notify(sec_buf);

        }

        catch

        {

        }

        bufferPositionNotifications = new BufferPositionNotify[3];

        bufferPositionNotifications[0].Offset = 0;

        bufferPositionNotifications[0].EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();

        bufferPositionNotifications[1].Offset = offset;

        bufferPositionNotifications[1].EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();

        bufferPositionNotifications[2].Offset = (int)PositionNotifyFlag.OffsetStop;//再生停止

        bufferPositionNotifications[2].EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();

         try

        {

            notify.SetNotificationPositions(bufferPositionNotifications, 2);

        }

        catch

        {

        }

    }

    public void prepare_for_soundThread()

    {

        Create_secondaryBuffer();

        set_Notification(secondaryBuffer, NotifySize);

    }

    public my_stream_test(Form1 frm1)

    {

        frm = frm1;

        mem_byte = new byte[24000];

         mem_st = new MemoryStream(mem_byte);

        device = new Device();

        device.SetCooperativeLevel(frm, CooperativeLevel.Normal);

        my_waveformat.SamplesPerSecond = 48000;

        my_waveformat.BitsPerSample = 8;

        my_waveformat.Channels = 1;

        my_waveformat.FormatTag = WaveFormatTag.Pcm;

        my_waveformat.BlockAlign = (short)((my_waveformat.Channels) * (my_waveformat.BitsPerSample / 8));

        my_waveformat.AverageBytesPerSecond = my_waveformat.BlockAlign * my_waveformat.SamplesPerSecond;

        desc = new BufferDescription(my_waveformat);

        //desc.BufferBytes = 256*20;

        desc.BufferBytes = mem_byte.Length * 2;

        desc.ControlPositionNotify = true;

        desc.GlobalFocus = true;

        for (int i = 0; i < mem_byte.Length; i++)

            mem_byte[i] = (byte)i;//適当なデータ

            //mem_byte[i] = (byte)(255 * Math.Sin(2 * Math.PI * 10 * i / mem_byte.Length));

            // mem_byte[i] = (byte)(i % 20 == 0 ? 0 : 128);

        NotifySize = mem_byte.Length;

    }

    void Create_secondaryBuffer()

    {

        //secondaryBuffer = new SecondaryBuffer(mem_st, desc, device); いずれも、不発、

        //secondaryBuffer = new SecondaryBuffer(mem_st, 256, desc, device); 作動しない

        try

        {

            secondaryBuffer = new SecondaryBuffer(desc, device);

        }

        catch

        {

        }

    }

    public void write_secondaryBuffer(int pos)

    {

        //C++と違って、lockする必要がない!

        // mem_st.Position = 0;

        // secondaryBuffer.Write(pos, mem_st, (int)mem_st.Length, LockFlag.None);

        secondaryBuffer.Write(pos, mem_byte, LockFlag.None);

    }

    public void write2_secondaryBuffer()

    {

        for (int i = 0; i < mem_byte.Length; i++)

            mem_byte[i] = (byte)(38);

            mem_st.Position = 0; //これは、必要。

            mem_st.Write(mem_byte, 19, (int)(mem_st.Length - 19));

        secondaryBuffer.Write(0, mem_st, 19, LockFlag.None);

        secondaryBuffer.GetCurrentPosition(out play_pos, out write_pos);

 

    }

    public byte[] read_secondaryBuffer()

    {

        for (int i = 0; i < mem_byte.Length; i++)

            mem_byte[i] = 0; //mem_byte.initializeでは、うまく初期化されない?

        mem_st.Position = 0; //これは必要。

        secondaryBuffer.Read(0, mem_st, mem_byte.Length, LockFlag.None);   

        return mem_byte;

    }

 

 

    }

 

}

streamを使って、secondarybufferに書き込むところは、

「書籍版」 DirectX9 実践プログラミング 工学社 DirectSound [4] ストリームバッファによる再生 p.197

が、ないと、理解不可能でした。御礼申し上げます。

managedDirectXは、発展途上らしくて、C++で書かれた書籍を、参考にしています。

やっぱり、C++を、触り始めましょうかねえ.....ASIOも、触りたいし....面倒なんよね C++って。.

何でも、managedDirextX2.0は、消滅するとか.....

XNA GameStudio Expressが、これからの主流を占めるのか?

先行きが、さっぱり解りませんね。

H.18.11.14