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