- 題名: Win32APIのwaveInXXXXのトラブル
- 日時: 2004/08/03 16:42:43
- ID: 5315
- この記事の返信元:
- (なし)
- この記事への返信:
- [5316] Re[1]: Win32APIのwaveInXXXXのトラブル2004/08/03 16:44:47
- ツリーを表示
― 続き ― 'バッファ領域の確保(更新) Private Sub Update_BufferSize() mWaveBuffer1 = Marshal.AllocHGlobal(mBufferSize) mWaveBuffer2 = Marshal.AllocHGlobal(mBufferSize) ReDim mMyOutPut(mBufferSize) End Sub 'waveInOpenが引数にとるWAVEFORMATEX構造体のメンバの設定(更新) Private Sub Update_WaveFormat() Const WAVE_FORMAT_PCM As Short = 1S With mWaveFormat .wFormatTag = WAVE_FORMAT_PCM .nChannels = 1S .nSamplePerSecond = mSampleRate .nAvgBytesPerSec = .nSamplePerSecond .wBitsPerSample = 8S .nBlockAilgn = 1S .cbSize = 0S End With End Sub '音声データ取得開始 Public Sub OpenDevice() If mIsWaveInDeviceOpened = True Then Exit Sub With mWaveBufferHeader1 .lpData = mWaveBuffer1 .dwBufferLength = mBufferSize .dwBytesRecorded = 0 .dwFlags = 0 .dwLoops = 1 .lpNext = IntPtr.Zero .dwUser = IntPtr.Zero .Reserved = IntPtr.Zero End With With mWaveBufferHeader2 .lpData = mWaveBuffer2 .dwBufferLength = mBufferSize .dwBytesRecorded = 0 .dwFlags = 0 .dwLoops = 1 .lpNext = IntPtr.Zero .dwUser = IntPtr.Zero .Reserved = IntPtr.Zero End With Dim Result As Integer Const CALLBACK_WINDOW As Integer = &H10000 Const WAVE_MAPPER As Integer = &HFFFFFFFF Result = waveInOpen(mDeviceHandle, WAVE_MAPPER, mWaveFormat, Me.Handle, 0, CALLBACK_WINDOW) Result = waveInPrepareHeader(mDeviceHandle, mWaveBufferHeader1, Marshal.SizeOf(mWaveBufferHeader1)) Result = waveInPrepareHeader(mDeviceHandle, mWaveBufferHeader2, Marshal.SizeOf(mWaveBufferHeader2)) End Sub Public Sub CloseDevice() If mIsWaveInDeviceOpened = False Then Exit Sub Call waveInReset(mDeviceHandle) Call waveInClose(mDeviceHandle) End Sub Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) Const MM_WIM_OPEN As Integer = &H3BE Const MM_WIM_CLOSE As Integer = &H3BF Const MM_WIM_DATA As Integer = &H3C0 Const MMSYSERR_NOERROR As Integer = 0 Dim Dbg As Integer Select Case m.Msg Case MM_WIM_OPEN Dim Result As Integer = 100 mIsWaveInDeviceOpened = True Result = waveInAddBuffer(mDeviceHandle, mWaveBufferHeader1, Marshal.SizeOf(mWaveBufferHeader1)) Result = waveInAddBuffer(mDeviceHandle, mWaveBufferHeader2, Marshal.SizeOf(mWaveBufferHeader2)) Result = waveInStart(mDeviceHandle) Case MM_WIM_DATA Dim Temp As WAVEHDR Dim Result As Integer Static DataCount As Integer = 0 DataCount += 1 : Console.WriteLine("DataCount = " & DataCount.ToString) Temp = m.GetLParam(GetType(WAVEHDR)) If Temp.lpData.ToInt32 = mWaveBufferHeader1.lpData.ToInt32 Then '開放されたバッファはmWaveBuffer1 Marshal.Copy(mWaveBuffer1, mMyOutPut, 0, mBufferSize) Result = waveInAddBuffer(mDeviceHandle, mWaveBufferHeader1, Marshal.SizeOf(mWaveBufferHeader1)) If Result = MMSYSERR_NOERROR Then Console.WriteLine("Add Buffer1 OK!") Else Console.WriteLine("Add Buffer1 Falied ! = " & Result.ToString & "Count = " & DataCount.ToString) End If ElseIf Temp.lpData.ToInt32 = mWaveBufferHeader2.lpData.ToInt32 Then '開放されたバッファはmWaveBuffer1 Marshal.Copy(mWaveBuffer2, mMyOutPut, 0, mBufferSize) Result = waveInAddBuffer(mDeviceHandle, mWaveBufferHeader2, Marshal.SizeOf(mWaveBufferHeader2)) If Result = MMSYSERR_NOERROR Then Console.WriteLine("Add Buffer2 OK!") Else Console.WriteLine("Add Buffer2 Falied ! = " & Result.ToString & "Count = " & DataCount.ToString) End If Else '受信したメッセージが指示するバッファのポインタは不正 Console.WriteLine("Invalid Pointer !") End If RaiseEvent WaveDataArrive(mMyOutPut) Case MM_WIM_CLOSE mIsWaveInDeviceOpened = False waveInUnprepareHeader(mDeviceHandle, mWaveBufferHeader1, Marshal.SizeOf(mWaveBufferHeader1)) waveInUnprepareHeader(mDeviceHandle, mWaveBufferHeader2, Marshal.SizeOf(mWaveBufferHeader2)) Case Else End Select MyBase.WndProc(m) End Sub Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 Call Update_BufferSize() Call Update_WaveFormat() End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) Marshal.FreeHGlobal(mWaveBuffer1) Marshal.FreeHGlobal(mWaveBuffer2) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub #Region " Windows フォーム デザイナで生成されたコード " 〜 略 〜 #End Region End Class
― 続き ― メインのフォームでcWaveInFormのインスタンスを作成したあと、 コマンドボタンなど適当なタイミングでOpenDeviceメソッドをコールすると、 音声データの取得を開始し、状況を出力ウィンドウに報告します。 それで、困っている点とは ● データの取得を開始してしばらくすると、MM_WIM_DATAメッセージで OSから返されたバッファを再びキューに追加するwaveInAddBufferが WAVERR_STILLPLAYING( = 33 )を返して失敗します。(ちなみにこのエラーコードは、 「バッファがまだキューにある」という意味らしいのですが、MSDNのwaveInAddBufferの 解説には、この関数が返す値として記述されていません。) ● メインのフォームなど他のオブジェクトでWaveDataArriveイベントを受信して 引数として渡されるデータを使って何らかの処理を行うと、これを行わなかった 時に比べてwaveInAddBufferが失敗するタイミングが目に見えて早くなります。 気づいた点などありましたらご教授よろしくお願いします。 なお、そのほか判明していることを書いてみます。 ○ 音声データ取得開始時にコールされるwaveInOpen、waveInPrepareHeader及び その直後のMM_WIM_OPEN受信時にコールされるwaveInAddBuffer、waveInStartは すべてMMSYSERR_NOERROR(成功)を返し、また取得されたデータを画面にオシロスコープ状に 描画するコードを書いてみると、正しい音声の波形になっているようなので、構造体の定義や APIの宣言が不適切である可能性は低いと思います。 ○ 定数RoughFrameRateにもっと小さな値(例えば2)を設定してもやはり同じ問題がおこるので、 キューに入れるバッファのサイズが小さすぎる可能性は低いと思います。 ○ サンプリングレート(mSampleRate)は、低い値を設定した時の方が、waveInAddBufferが 失敗するタイミングが遅れるようです。 ○ WndProcでRaiseEventしているのがまずいのかとも思ったのですが、 これを削除してもやはりそのうちwaveInAddBufferが失敗します。 ○ MM_WIM_DATA受信時、WndProcを抜ける際に参考にしたサイトのサンプル通り MyBase.WndProc(m)の代わりにm.Result = IntPtr.Zeroとしても結果は変わらず。
■No5319に返信(深山さんの記事) こんにちは、返信ありがとうございます。 > ・WndProc 内の各メッセージ処理が終わった後に Return していない(MyBase に流れている) > ・OpenDevice 内で設定している dwBufferLength の値が元のコードと異なる > > というのが気になりました。 > これらは特に問題ないのでしょうか? 前者については、実は私はいわゆる「Windowsプログラミング」の経験がないので 参考にさせてもらったオリジナルのCコードのWndProcの中のReturn 0の意味が あまりよくわからないのですが、.NETでこれに相当する処理をするには、おそらく WndProcを抜けるときにMyBase.WndProc(m)の代わりにm.Result = IntPtr.Zeroを 実行してやればいいのではないかと思います。 http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemwindowsformsmessageclassresulttopic.asp で、実際やってみると結果に違いはないようでした。 後者については、コードの書き方が違うだけで意味的には同じだと思いますので、 恐らく問題ないのではないかと思います。(いま確認できる環境が手元にないので......。)
■No5324に返信(深山さんの記事) > はい。戻り値に関する扱いについては仰る通り、 m.Result に設定してあげれば大丈夫です。 > 私が言ったのはそういうことではなくて‥‥えっと、 return 0 とすることで戻り値を > 設定するのと同時に、関数から抜け出すというのはご存知ですよね? WndProc内のSelect...CaseのMM_WIM_OPEN、MM_WIM_DATA、MM_WIM_CLOSEの各ブロックの末尾に、 m.Result = IntPtr.Zero Exit Sub を付け加えたコードで試していますが、やはり同じようにwaveInAddBufferが 失敗する問題が発生するようでした。 > 元コードでは SRATE ( 11025 ) を設定してる箇所で、 mBufferSize > ( mSampleRate / mBufferSize = 44100 / 20 = 2205 ) を設定してるように見受けられたので。 ああ、2つのWAVEHDR構造体のdwBufferLengthメンバに設定している値が、件のサイトの サンプルコードの値と違うというご指摘ですね。よく読まずに早とちりしたようですみません。 MSDNによると、単にバッファのサイズをバイト単位で指定するだけのようですので、 実際に確保した領域より大きな値を指定してさえいなければ、たぶん問題ないのでは ないでしょうか? http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_wavehdr_str.asp 実際、件のサンプルコードと条件を全て同じにしたものも確認していますが、 やはり同じように問題が発生するようでした。
分類:[.NET]