waveOutをIntPtrで動かす〜どうにかしてラッパーDLLなしでやりたい〜
以前,
http://d.hatena.ne.jp/HRM-7/20070125
で,C#上でwaveOutを動かしていたが,ラッパーDLLを使っていて,気になっていた.
とりあえず,
#region DLL [DllImport("winmm.dll", EntryPoint = "waveOutOpen", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutOpen(HWAVEOUT__** phwo, uint uDeviceID, WAVEFORMATEX* pwfx, uint dwCallback, uint dwInstance, uint fdwOpen); [DllImport("winmm.dll", EntryPoint = "waveOutPrepareHeader", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutPrepareHeader(HWAVEOUT__* hwo, wavehdr_tag* pwh, uint cbwh); [DllImport("winmm.dll", EntryPoint = "waveOutWrite", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutWrite(HWAVEOUT__* hwo, wavehdr_tag* pwh, uint cbwh); [DllImport("winmm.dll", EntryPoint = "waveOutClose", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutClose(HWAVEOUT__* hwo); [DllImport("winmm.dll", EntryPoint = "waveOutReset", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutReset(HWAVEOUT__* hwo); [DllImport("winmm.dll", EntryPoint = "waveOutUnprepareHeader", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern unsafe uint waveOutUnprepareHeader(HWAVEOUT__* hwo, wavehdr_tag* pwh, uint cbwh); [StructLayout(LayoutKind.Explicit, Size = 18)] public struct WAVEFORMATEX { [FieldOffset(0)] public ushort wFormatTag; [FieldOffset(2)] public ushort nChannels; [FieldOffset(4)] public uint nSamplesPerSec; [FieldOffset(8)] public uint nAvgBytesPerSec; [FieldOffset(12)] public ushort nBlockAlign; [FieldOffset(14)] public ushort wBitsPerSample; [FieldOffset(16)] public ushort cbSize; } [StructLayout(LayoutKind.Explicit, Size = 4)] public struct HWAVEOUT__ { [FieldOffset(0)] public int unused; } [StructLayout(LayoutKind.Explicit, Size = 32)] public struct wavehdr_tag { [FieldOffset(0)] public unsafe sbyte* lpData; [FieldOffset(4)] public uint dwBufferLength; [FieldOffset(8)] public uint dwBytesRecorded; [FieldOffset(12)] public uint dwUser; [FieldOffset(16)] public uint dwFlags; [FieldOffset(20)] public uint dwLoops; [FieldOffset(24)] public unsafe wavehdr_tag* lpNext; [FieldOffset(28)] public uint reserved; } #endregion unsafe public static void Play(short[] wave_data, IntPtr hWnd) { //WAVEデバイス設定 WAVEFORMATEX wf = new WAVEFORMATEX(); wf.wFormatTag = 0x0001; //PCM ....... wf.cbSize = 0; //PCMでは常に0 HWAVEOUT__ hwo = new HWAVEOUT__(); HWAVEOUT__* hWOut = &hwo; waveOutOpen(&hWOut, 0x0000, &wf, (uint)hWnd, 0, 0x00010000); //WAVE情報設定 wavehdr_tag wt = new wavehdr_tag(); fixed (short* tmp = &wave_data[0]) { wt.lpData = (sbyte*)tmp; ..... wt.reserved = 0; //使用しない //再生 waveOutPrepareHeader(hWOut, &wt, (uint)sizeof(wavehdr_tag)); waveOutWrite(hWOut, &wt, (uint)sizeof(wavehdr_tag)); System.Threading.Thread.Sleep(1000); waveOutReset(hWOut); waveOutUnprepareHeader(hWOut, &wt, (uint)sizeof(wavehdr_tag)); } waveOutClose(hWOut); }
って感じで動くのだが,コールバックを受け取りたいので,OpenとPlayとCloseは分けたいなと.
だけども,
waveOutOpen(HWAVEOUT__** phwo
このポインタのポインタがくせもので,ちょっと困難.
とりあえず,IntPtrにしてみた.
waveOutOpen(IntPtr phwo waveOutPrepareHeader(IntPtr hwo ...
それに伴い,
HWAVEOUT__ hwo = new HWAVEOUT__(); HWAVEOUT__* hWOut = &hwo; IntPtr ptr2 = (IntPtr)(&hWOut); waveOutOpen(ptr2, 0x0000, &wf, (uint)hWnd, 0, 0x00010000);
ただ,ここで,ポインタのポインタってポインタの4バイト小さいだけらしい!?ので,
HWAVEOUT__ hwo = new HWAVEOUT__(); HWAVEOUT__* hWOut = &hwo; IntPtr ptr2 = (IntPtr)(hWOut-1);(これで4つちいさくなる) waveOutOpen(ptr2, 0x0000, &wf, (uint)hWnd, 0, 0x00010000);
とすると,うまくいかない.
HWAVEOUT__ hwo = new HWAVEOUT__(); HWAVEOUT__* hWOut = &hwo; IntPtr ptr2 = (IntPtr)(&hWOut); IntPtr ptr3 = (IntPtr)(hWOut-1); waveOutOpen(ptr3, 0x0000, &wf, (uint)hWnd, 0, 0x00010000);
だと,うまくいく.&hWOutを扱う必要があるんだろうか…
試行錯誤の結果
private HWAVEOUT__ hwo; private HWAVEOUT__* hWOut; private WAVEFORMATEX wf; private wavehdr_tag[] wt; private IntPtr ptr; private int k = 0; unsafe public void Open(IntPtr hWnd, uint SRATE) { //WAVEデバイス設定 wf = new WAVEFORMATEX(); wf.wFormatTag = 0x0001; //PCM wf.nChannels = 1; //モノラル wf.nSamplesPerSec = SRATE; //周波数 wf.wBitsPerSample = 16; //16bit wf.nBlockAlign = (ushort)(wf.nChannels * wf.wBitsPerSample / 8); wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.cbSize = 0; //PCMでは常に0 hwo = new HWAVEOUT__(); fixed (HWAVEOUT__* phwo = &hwo) { hWOut = phwo; fixed (HWAVEOUT__** pphwo = &hWOut) { ptr = (IntPtr)pphwo; fixed (WAVEFORMATEX* pwf = &wf) { waveOutOpen(ptr, 0x0000, pwf, (uint)hWnd, 0, 0x00010000); } } } }
で,できた.