1 /*
2 * $Id: dsound_wrapper.c,v 1.1 2006/04/30 16:23:59 jcr13 Exp $
3 * Simplified DirectSound interface.
4 *
5 * Author: Phil Burk & Robert Marsanyi
6 *
7 * PortAudio Portable Real-Time Audio Library
8 * For more information see: http://www.softsynth.com/portaudio/
9 * DirectSound Implementation
10 * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files
14 * (the "Software"), to deal in the Software without restriction,
15 * including without limitation the rights to use, copy, modify, merge,
16 * publish, distribute, sublicense, and/or sell copies of the Software,
17 * and to permit persons to whom the Software is furnished to do so,
18 * subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
22 *
23 * Any person wishing to distribute modifications to the Software is
24 * requested to send the modifications to the original developer so that
25 * they can be incorporated into the canonical version.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
30 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
31 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
32 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 *
35 */
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info.
40 #include <objbase.h>
41 #include <unknwn.h>
42 #include "dsound_wrapper.h"
43 #include "pa_trace.h"
44
45 /************************************************************************************/
DSW_Term(DSoundWrapper * dsw)46 void DSW_Term( DSoundWrapper *dsw )
47 {
48 // Cleanup the sound buffers
49 if (dsw->dsw_OutputBuffer)
50 {
51 IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
52 IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
53 dsw->dsw_OutputBuffer = NULL;
54 }
55 #if SUPPORT_AUDIO_CAPTURE
56 if (dsw->dsw_InputBuffer)
57 {
58 IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
59 IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
60 dsw->dsw_InputBuffer = NULL;
61 }
62 if (dsw->dsw_pDirectSoundCapture)
63 {
64 IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
65 dsw->dsw_pDirectSoundCapture = NULL;
66 }
67 #endif /* SUPPORT_AUDIO_CAPTURE */
68 if (dsw->dsw_pDirectSound)
69 {
70 IDirectSound_Release( dsw->dsw_pDirectSound );
71 dsw->dsw_pDirectSound = NULL;
72 }
73 }
74 /************************************************************************************/
DSW_Init(DSoundWrapper * dsw)75 HRESULT DSW_Init( DSoundWrapper *dsw )
76 {
77 memset( dsw, 0, sizeof(DSoundWrapper) );
78 return 0;
79 }
80 /************************************************************************************/
DSW_InitOutputDevice(DSoundWrapper * dsw,LPGUID lpGUID)81 HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
82 {
83 // Create the DS object
84 HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
85 if( hr != DS_OK ) return hr;
86 return hr;
87 }
88
89 /************************************************************************************/
DSW_InitOutputBuffer(DSoundWrapper * dsw,unsigned long nFrameRate,int nChannels,int bytesPerBuffer)90 HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
91 {
92 DWORD dwDataLen;
93 DWORD playCursor;
94 HRESULT result;
95 LPDIRECTSOUNDBUFFER pPrimaryBuffer;
96 HWND hWnd;
97 HRESULT hr;
98 WAVEFORMATEX wfFormat;
99 DSBUFFERDESC primaryDesc;
100 DSBUFFERDESC secondaryDesc;
101 unsigned char* pDSBuffData;
102 LARGE_INTEGER counterFrequency;
103 dsw->dsw_OutputSize = bytesPerBuffer;
104 dsw->dsw_OutputRunning = FALSE;
105 dsw->dsw_OutputUnderflows = 0;
106 dsw->dsw_FramesWritten = 0;
107 dsw->dsw_BytesPerFrame = nChannels * sizeof(short);
108 // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
109 // applications's window. Also if that window is closed before the Buffer is closed
110 // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
111 // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
112 // hWnd = GetForegroundWindow();
113 hWnd = GetDesktopWindow();
114 // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
115 // Exclusize also prevents unexpected sounds from other apps during a performance.
116 if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
117 hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
118 {
119 return hr;
120 }
121 // -----------------------------------------------------------------------
122 // Create primary buffer and set format just so we can specify our custom format.
123 // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
124 // Setup the primary buffer description
125 ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
126 primaryDesc.dwSize = sizeof(DSBUFFERDESC);
127 primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
128 primaryDesc.dwBufferBytes = 0;
129 primaryDesc.lpwfxFormat = NULL;
130 // Create the buffer
131 if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
132 &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
133 // Define the buffer format
134 wfFormat.wFormatTag = WAVE_FORMAT_PCM;
135 wfFormat.nChannels = nChannels;
136 wfFormat.nSamplesPerSec = nFrameRate;
137 wfFormat.wBitsPerSample = 8 * sizeof(short);
138 wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
139 wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
140 wfFormat.cbSize = 0; /* No extended format info. */
141 // Set the primary buffer's format
142 if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
143 // ----------------------------------------------------------------------
144 // Setup the secondary buffer description
145 ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
146 secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
147 secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
148 secondaryDesc.dwBufferBytes = bytesPerBuffer;
149 secondaryDesc.lpwfxFormat = &wfFormat;
150 // Create the secondary buffer
151 if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
152 &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
153 // Lock the DS buffer
154 if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
155 &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
156 // Zero the DS buffer
157 ZeroMemory(pDSBuffData, dwDataLen);
158 // Unlock the DS buffer
159 if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
160 if( QueryPerformanceFrequency( &counterFrequency ) )
161 {
162 int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
163 dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
164 AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart );
165 }
166 else
167 {
168 dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
169 }
170 // Let DSound set the starting write position because if we set it to zero, it looks like the
171 // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
172 hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
173 if( hr != DS_OK )
174 {
175 return hr;
176 }
177 dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame;
178 /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
179 return DS_OK;
180 }
181
182 /************************************************************************************/
DSW_StartOutput(DSoundWrapper * dsw)183 HRESULT DSW_StartOutput( DSoundWrapper *dsw )
184 {
185 HRESULT hr;
186 QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
187 dsw->dsw_LastPlayCursor = 0;
188 dsw->dsw_FramesPlayed = 0;
189 hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
190 if( hr != DS_OK )
191 {
192 return hr;
193 }
194 // Start the buffer playback in a loop.
195 if( dsw->dsw_OutputBuffer != NULL )
196 {
197 hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
198 if( hr != DS_OK )
199 {
200 return hr;
201 }
202 dsw->dsw_OutputRunning = TRUE;
203 }
204
205 return 0;
206 }
207 /************************************************************************************/
DSW_StopOutput(DSoundWrapper * dsw)208 HRESULT DSW_StopOutput( DSoundWrapper *dsw )
209 {
210 // Stop the buffer playback
211 if( dsw->dsw_OutputBuffer != NULL )
212 {
213 dsw->dsw_OutputRunning = FALSE;
214 return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
215 }
216 else return 0;
217 }
218
219 /************************************************************************************/
DSW_QueryOutputSpace(DSoundWrapper * dsw,long * bytesEmpty)220 HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
221 {
222 HRESULT hr;
223 DWORD playCursor;
224 DWORD writeCursor;
225 long numBytesEmpty;
226 long playWriteGap;
227 // Query to see how much room is in buffer.
228 // Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing
229 // under WinNT. The Microsoft documentation says we can pass NULL but apparently not.
230 // Thanks to Max Rheiner for the fix.
231 hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
232 if( hr != DS_OK )
233 {
234 return hr;
235 }
236 AddTraceMessage("playCursor", playCursor);
237 AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset);
238 // Determine size of gap between playIndex and WriteIndex that we cannot write into.
239 playWriteGap = writeCursor - playCursor;
240 if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
241 /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
242 /* Attempt to detect playCursor wrap-around and correct it. */
243 if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
244 {
245 /* How much time has elapsed since last check. */
246 LARGE_INTEGER currentTime;
247 LARGE_INTEGER elapsedTime;
248 long bytesPlayed;
249 long bytesExpected;
250 long buffersWrapped;
251 QueryPerformanceCounter( ¤tTime );
252 elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
253 dsw->dsw_LastPlayTime = currentTime;
254 /* How many bytes does DirectSound say have been played. */
255 bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
256 if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
257 dsw->dsw_LastPlayCursor = playCursor;
258 /* Calculate how many bytes we would have expected to been played by now. */
259 bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
260 buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
261 if( buffersWrapped > 0 )
262 {
263 AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed );
264 AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected );
265 playCursor += (buffersWrapped * dsw->dsw_OutputSize);
266 bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
267 }
268 /* Maintain frame output cursor. */
269 dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame);
270 }
271 numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
272 if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
273 /* Have we underflowed? */
274 if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
275 {
276 if( dsw->dsw_OutputRunning )
277 {
278 dsw->dsw_OutputUnderflows += 1;
279 AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty );
280 }
281 dsw->dsw_WriteOffset = writeCursor;
282 numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
283 }
284 *bytesEmpty = numBytesEmpty;
285 return hr;
286 }
287
288 /************************************************************************************/
DSW_ZeroEmptySpace(DSoundWrapper * dsw)289 HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
290 {
291 HRESULT hr;
292 LPBYTE lpbuf1 = NULL;
293 LPBYTE lpbuf2 = NULL;
294 DWORD dwsize1 = 0;
295 DWORD dwsize2 = 0;
296 long bytesEmpty;
297 hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
298 if (hr != DS_OK) return hr;
299 if( bytesEmpty == 0 ) return DS_OK;
300 // Lock free space in the DS
301 hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
302 (void **) &lpbuf2, &dwsize2, 0);
303 if (hr == DS_OK)
304 {
305 // Copy the buffer into the DS
306 ZeroMemory(lpbuf1, dwsize1);
307 if(lpbuf2 != NULL)
308 {
309 ZeroMemory(lpbuf2, dwsize2);
310 }
311 // Update our buffer offset and unlock sound buffer
312 dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
313 IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
314 dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerFrame;
315 }
316 return hr;
317 }
318
319 /************************************************************************************/
DSW_WriteBlock(DSoundWrapper * dsw,char * buf,long numBytes)320 HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
321 {
322 HRESULT hr;
323 LPBYTE lpbuf1 = NULL;
324 LPBYTE lpbuf2 = NULL;
325 DWORD dwsize1 = 0;
326 DWORD dwsize2 = 0;
327 // Lock free space in the DS
328 hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
329 (void **) &lpbuf2, &dwsize2, 0);
330 if (hr == DS_OK)
331 {
332 // Copy the buffer into the DS
333 CopyMemory(lpbuf1, buf, dwsize1);
334 if(lpbuf2 != NULL)
335 {
336 CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
337 }
338 // Update our buffer offset and unlock sound buffer
339 dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
340 IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
341 dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerFrame;
342 }
343 return hr;
344 }
345
346 /************************************************************************************/
DSW_GetOutputStatus(DSoundWrapper * dsw)347 DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
348 {
349 DWORD status;
350 if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
351 return( DSERR_INVALIDPARAM );
352 else
353 return( status );
354 }
355
356 #if SUPPORT_AUDIO_CAPTURE
357 /* These routines are used to support audio input.
358 * Do NOT compile these calls when using NT4 because it does
359 * not support the entry points.
360 */
361 /************************************************************************************/
DSW_InitInputDevice(DSoundWrapper * dsw,LPGUID lpGUID)362 HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
363 {
364 HRESULT hr = DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
365 if( hr != DS_OK ) return hr;
366 return hr;
367 }
368 /************************************************************************************/
DSW_InitInputBuffer(DSoundWrapper * dsw,unsigned long nFrameRate,int nChannels,int bytesPerBuffer)369 HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
370 {
371 DSCBUFFERDESC captureDesc;
372 WAVEFORMATEX wfFormat;
373 HRESULT result;
374 // Define the buffer format
375 wfFormat.wFormatTag = WAVE_FORMAT_PCM;
376 wfFormat.nChannels = nChannels;
377 wfFormat.nSamplesPerSec = nFrameRate;
378 wfFormat.wBitsPerSample = 8 * sizeof(short);
379 wfFormat.nBlockAlign = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8);
380 wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
381 wfFormat.cbSize = 0; /* No extended format info. */
382 dsw->dsw_InputSize = bytesPerBuffer;
383 // ----------------------------------------------------------------------
384 // Setup the secondary buffer description
385 ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
386 captureDesc.dwSize = sizeof(DSCBUFFERDESC);
387 captureDesc.dwFlags = 0;
388 captureDesc.dwBufferBytes = bytesPerBuffer;
389 captureDesc.lpwfxFormat = &wfFormat;
390 // Create the capture buffer
391 if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
392 &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
393 dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
394 return DS_OK;
395 }
396
397 /************************************************************************************/
DSW_StartInput(DSoundWrapper * dsw)398 HRESULT DSW_StartInput( DSoundWrapper *dsw )
399 {
400 // Start the buffer playback
401 if( dsw->dsw_InputBuffer != NULL )
402 {
403 return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
404 }
405 else return 0;
406 }
407
408 /************************************************************************************/
DSW_StopInput(DSoundWrapper * dsw)409 HRESULT DSW_StopInput( DSoundWrapper *dsw )
410 {
411 // Stop the buffer playback
412 if( dsw->dsw_InputBuffer != NULL )
413 {
414 return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
415 }
416 else return 0;
417 }
418
419 /************************************************************************************/
DSW_QueryInputFilled(DSoundWrapper * dsw,long * bytesFilled)420 HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
421 {
422 HRESULT hr;
423 DWORD capturePos;
424 DWORD readPos;
425 long filled;
426 // Query to see how much data is in buffer.
427 // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
428 // so let's pass a pointer just to be safe.
429 hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
430 if( hr != DS_OK )
431 {
432 return hr;
433 }
434 filled = readPos - dsw->dsw_ReadOffset;
435 if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
436 *bytesFilled = filled;
437 return hr;
438 }
439
440 /************************************************************************************/
DSW_ReadBlock(DSoundWrapper * dsw,char * buf,long numBytes)441 HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
442 {
443 HRESULT hr;
444 LPBYTE lpbuf1 = NULL;
445 LPBYTE lpbuf2 = NULL;
446 DWORD dwsize1 = 0;
447 DWORD dwsize2 = 0;
448 // Lock free space in the DS
449 hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
450 (void **) &lpbuf2, &dwsize2, 0);
451 if (hr == DS_OK)
452 {
453 // Copy from DS to the buffer
454 CopyMemory( buf, lpbuf1, dwsize1);
455 if(lpbuf2 != NULL)
456 {
457 CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
458 }
459 // Update our buffer offset and unlock sound buffer
460 dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
461 IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
462 }
463 return hr;
464 }
465
466 #endif /* SUPPORT_AUDIO_CAPTURE */
467