2 * Wine Driver for PulseAudio - WaveIn Functionality
3 * http://pulseaudio.org/
5 * Copyright 2009 Arthur Taylor <theycallhimart@gmail.com>
7 * Contains code from other wine multimedia drivers.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
35 #include <winepulse.h>
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(wave
);
43 /*======================================================================*
44 * WAVE IN specific PulseAudio Callbacks *
45 *======================================================================*/
47 /**************************************************************************
48 * widNotifyClient [internal]
50 static DWORD
widNotifyClient(WINE_WAVEINST
* wwi
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
) {
51 TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg
, dwParam1
, dwParam2
);
57 if (wwi
->wFlags
!= DCB_NULL
&&
58 !DriverCallback(wwi
->waveDesc
.dwCallback
, wwi
->wFlags
, (HDRVR
)wwi
->waveDesc
.hWave
,
59 wMsg
, wwi
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
60 WARN("can't notify client !\n");
61 return MMSYSERR_ERROR
;
65 FIXME("Unknown callback message %u\n", wMsg
);
66 return MMSYSERR_INVALPARAM
;
68 return MMSYSERR_NOERROR
;
71 /**************************************************************************
72 * widRecorder_NextFragment [internal]
74 * Gets the next fragment of data from the server.
76 static size_t widRecorder_NextFragment(WINE_WAVEINST
*wwi
) {
79 pa_stream_peek(wwi
->stream
, &wwi
->buffer
, &nbytes
);
80 wwi
->buffer_length
= nbytes
;
81 wwi
->buffer_read_offset
= 0;
87 /**************************************************************************
88 * widRecorder_CopyData [internal]
90 * Copys data from the fragments pulse returns to queued buffers.
92 static void widRecorder_CopyData(WINE_WAVEINST
*wwi
) {
93 LPWAVEHDR lpWaveHdr
= wwi
->lpQueuePtr
;
96 while (lpWaveHdr
&& wwi
->state
== WINE_WS_PLAYING
) {
98 nbytes
= min(wwi
->buffer_length
- wwi
->buffer_read_offset
, lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
);
99 if (nbytes
== 0) break;
101 TRACE("%u bytes from %p to %p\n", nbytes
, (PBYTE
)wwi
->buffer
+ wwi
->buffer_read_offset
, lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
);
102 memcpy(lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
, (PBYTE
)wwi
->buffer
+ wwi
->buffer_read_offset
, nbytes
);
104 lpWaveHdr
->dwBytesRecorded
+= nbytes
;
105 wwi
->buffer_read_offset
+= nbytes
;
107 if (wwi
->buffer_read_offset
== wwi
->buffer_length
) {
108 pa_threaded_mainloop_lock(PULSE_ml
);
109 pa_stream_drop(wwi
->stream
);
110 if (pa_stream_readable_size(wwi
->stream
))
111 widRecorder_NextFragment(wwi
);
114 wwi
->buffer_length
= 0;
115 wwi
->buffer_read_offset
= 0;
117 pa_threaded_mainloop_unlock(PULSE_ml
);
120 if (lpWaveHdr
->dwBytesRecorded
== lpWaveHdr
->dwBufferLength
) {
121 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
122 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
123 wwi
->lpQueuePtr
= lpWaveHdr
->lpNext
;
124 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
125 lpWaveHdr
= wwi
->lpQueuePtr
;
130 /**************************************************************************
131 * widRecorder [internal]
133 static DWORD CALLBACK
widRecorder(LPVOID lpParam
) {
134 WINE_WAVEINST
*wwi
= (WINE_WAVEINST
*)lpParam
;
136 enum win_wm_message msg
;
141 wwi
->state
= WINE_WS_STOPPED
;
142 SetEvent(wwi
->hStartUpEvent
);
146 if (wwi
->state
!= WINE_WS_PLAYING
) {
149 if (wwi
->buffer
== NULL
&& pa_stream_readable_size(wwi
->stream
)) {
150 pa_threaded_mainloop_lock(PULSE_ml
);
151 wait
= pa_bytes_to_usec(widRecorder_NextFragment(wwi
), &wwi
->sample_spec
)/1000;
152 pa_threaded_mainloop_unlock(PULSE_ml
);
156 widRecorder_CopyData(wwi
);
158 PULSE_WaitRingMessage(&wwi
->msgRing
, wait
);
160 while (PULSE_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
)) {
161 TRACE("Received %s %x\n", PULSE_getCmdString(msg
), param
);
167 case WINE_WM_STARTING
:
168 wwi
->state
= WINE_WS_PLAYING
;
169 wait
= pa_bytes_to_usec(wwi
->lpQueuePtr
->dwBufferLength
, &wwi
->sample_spec
)/1000;
170 wwi
->last_reset
= wwi
->timing_info
->read_index
;
171 pa_threaded_mainloop_lock(PULSE_ml
);
172 PULSE_WaitForOperation(pa_stream_cork(wwi
->stream
, 0, PULSE_StreamSuccessCallback
, NULL
));
173 pa_threaded_mainloop_unlock(PULSE_ml
);
177 lpWaveHdr
= (LPWAVEHDR
)param
;
178 lpWaveHdr
->lpNext
= 0;
180 /* insert buffer at the end of queue */
183 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
187 case WINE_WM_STOPPING
:
188 if (wwi
->state
!= WINE_WS_STOPPED
) {
189 wwi
->state
= WINE_WS_STOPPED
;
190 pa_threaded_mainloop_lock(PULSE_ml
);
191 PULSE_WaitForOperation(pa_stream_cork(wwi
->stream
, 1, PULSE_StreamSuccessCallback
, NULL
));
192 pa_threaded_mainloop_unlock(PULSE_ml
);
194 /* return current buffer to app */
195 lpWaveHdr
= wwi
->lpQueuePtr
;
197 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
198 TRACE("stop %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
199 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
200 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
201 wwi
->lpQueuePtr
= lpNext
;
202 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
207 case WINE_WM_RESETTING
:
208 if (wwi
->state
!= WINE_WS_STOPPED
) {
209 wwi
->state
= WINE_WS_STOPPED
;
210 pa_threaded_mainloop_lock(PULSE_ml
);
211 PULSE_WaitForOperation(pa_stream_cork(wwi
->stream
, 1, PULSE_StreamSuccessCallback
, NULL
));
212 pa_threaded_mainloop_unlock(PULSE_ml
);
215 /* return all buffers to the app */
216 for (lpWaveHdr
= wwi
->lpPlayPtr
? wwi
->lpPlayPtr
: wwi
->lpQueuePtr
; lpWaveHdr
; lpWaveHdr
= wwi
->lpQueuePtr
) {
217 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
218 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
219 wwi
->lpQueuePtr
= lpWaveHdr
->lpNext
;
220 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
225 case WINE_WM_CLOSING
:
227 if ((DWORD
)param
== 1) {
228 /* If we are here, the stream failed */
229 wwi
->state
= WINE_WS_FAILED
;
231 PULSE_DestroyRingMessage(&wwi
->msgRing
);
232 widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
233 wwi
->lpPlayPtr
= wwi
->lpQueuePtr
= NULL
;
234 pa_threaded_mainloop_lock(PULSE_ml
);
235 pa_stream_disconnect(wwi
->stream
);
236 pa_threaded_mainloop_unlock(PULSE_ml
);
237 TRACE("Thread exiting because of failure.\n");
240 wwi
->state
= WINE_WS_CLOSED
;
243 /* shouldn't go here */
245 FIXME("unknown message %d\n", msg
);
248 } /* while(PULSE_RetrieveRingMessage()) */
252 /**************************************************************************
255 static DWORD
widOpen(WORD wDevID
, LPDWORD lpdwUser
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
) {
257 WINE_WAVEINST
*wwi
= NULL
;
258 DWORD ret
= MMSYSERR_NOERROR
;
260 TRACE("(%u, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
261 if (lpDesc
== NULL
) {
262 WARN("Invalid Parameter !\n");
263 return MMSYSERR_INVALPARAM
;
266 if (wDevID
>= PULSE_WidNumDevs
) {
267 TRACE("Asked for device %d, but only %d known!\n", wDevID
, PULSE_WidNumDevs
);
268 return MMSYSERR_BADDEVICEID
;
270 wdi
= &WInDev
[wDevID
];
272 wwi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_WAVEINST
));
273 if (!wwi
) return MMSYSERR_NOMEM
;
274 *lpdwUser
= (DWORD
)wwi
;
276 /* check to see if format is supported and make pa_sample_spec struct */
277 if (!PULSE_SetupFormat(lpDesc
->lpFormat
, &wwi
->sample_spec
)) {
278 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
279 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
280 lpDesc
->lpFormat
->nSamplesPerSec
);
281 ret
= WAVERR_BADFORMAT
;
285 if (TRACE_ON(wave
)) {
286 char t
[PA_SAMPLE_SPEC_SNPRINT_MAX
];
287 pa_sample_spec_snprint(t
, sizeof(t
), &wwi
->sample_spec
);
288 TRACE("Sample spec '%s'\n", t
);
291 if (dwFlags
& WAVE_FORMAT_QUERY
) {
292 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
293 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
294 lpDesc
->lpFormat
->nSamplesPerSec
);
295 ret
= MMSYSERR_NOERROR
;
299 wwi
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
300 wwi
->waveDesc
= *lpDesc
;
301 PULSE_InitRingMessage(&wwi
->msgRing
);
303 wwi
->stream
= pa_stream_new(PULSE_context
, "WaveIn", &wwi
->sample_spec
, NULL
);
305 ret
= WAVERR_BADFORMAT
;
309 pa_stream_set_state_callback(wwi
->stream
, PULSE_StreamStateCallback
, wwi
);
311 pa_threaded_mainloop_lock(PULSE_ml
);
312 TRACE("Asking to open %s for recording.\n", wdi
->device_name
);
313 pa_stream_connect_record(wwi
->stream
, wdi
->device_name
, NULL
,
314 PA_STREAM_START_CORKED
|
315 PA_STREAM_AUTO_TIMING_UPDATE
|
316 PA_STREAM_INTERPOLATE_TIMING
);
319 pa_context_state_t cstate
= pa_context_get_state(PULSE_context
);
320 pa_stream_state_t sstate
= pa_stream_get_state(wwi
->stream
);
322 if (cstate
== PA_CONTEXT_FAILED
|| cstate
== PA_CONTEXT_TERMINATED
||
323 sstate
== PA_STREAM_FAILED
|| sstate
== PA_STREAM_TERMINATED
) {
324 ERR("Failed to connect context object: %s\n", pa_strerror(pa_context_errno(PULSE_context
)));
325 ret
= MMSYSERR_NODRIVER
;
326 pa_threaded_mainloop_unlock(PULSE_ml
);
330 if (sstate
== PA_STREAM_READY
)
333 pa_threaded_mainloop_wait(PULSE_ml
);
335 TRACE("(%p)->stream connected for recording.\n", wwi
);
337 PULSE_WaitForOperation(pa_stream_update_timing_info(wwi
->stream
, PULSE_StreamSuccessCallback
, wwi
));
339 wwi
->timing_info
= pa_stream_get_timing_info(wwi
->stream
);
340 assert(wwi
->timing_info
);
341 pa_threaded_mainloop_unlock(PULSE_ml
);
343 wwi
->hStartUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
344 wwi
->hThread
= CreateThread(NULL
, 0, widRecorder
, (LPVOID
)wwi
, 0, &(wwi
->dwThreadID
));
346 SetThreadPriority(wwi
->hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
348 ERR("Thread creation for the widRecorder failed!\n");
349 ret
= MMSYSERR_NOMEM
;
352 WaitForSingleObject(wwi
->hStartUpEvent
, INFINITE
);
353 CloseHandle(wwi
->hStartUpEvent
);
354 wwi
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
356 return widNotifyClient(wwi
, WIM_OPEN
, 0L, 0L);
362 if (wwi
->hStartUpEvent
!= INVALID_HANDLE_VALUE
)
363 CloseHandle(wwi
->hStartUpEvent
);
365 if (wwi
->msgRing
.ring_buffer_size
> 0)
366 PULSE_DestroyRingMessage(&wwi
->msgRing
);
369 if (pa_stream_get_state(wwi
->stream
) == PA_STREAM_READY
)
370 pa_stream_disconnect(wwi
->stream
);
371 pa_stream_unref(wwi
->stream
);
373 HeapFree(GetProcessHeap(), 0, wwi
);
377 /**************************************************************************
378 * widClose [internal]
380 static DWORD
widClose(WORD wDevID
, WINE_WAVEINST
*wwi
) {
383 TRACE("(%u, %p);\n", wDevID
, wwi
);
384 if (wDevID
>= PULSE_WidNumDevs
) {
385 WARN("Asked for device %d, but only %d known!\n", wDevID
, PULSE_WodNumDevs
);
386 return MMSYSERR_INVALHANDLE
;
388 WARN("Stream instance invalid.\n");
389 return MMSYSERR_INVALHANDLE
;
392 if (wwi
->state
!= WINE_WS_FAILED
) {
393 if (wwi
->lpQueuePtr
) {
394 WARN("buffers recording recording !\n");
395 return WAVERR_STILLPLAYING
;
398 pa_threaded_mainloop_lock(PULSE_ml
);
399 if (pa_stream_get_state(wwi
->stream
) == PA_STREAM_READY
)
400 pa_stream_drop(wwi
->stream
);
401 pa_stream_disconnect(wwi
->stream
);
402 pa_threaded_mainloop_unlock(PULSE_ml
);
404 if (wwi
->hThread
!= INVALID_HANDLE_VALUE
)
405 PULSE_AddRingMessage(&wwi
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
407 PULSE_DestroyRingMessage(&wwi
->msgRing
);
409 ret
= widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
411 pa_stream_unref(wwi
->stream
);
412 TRACE("Deallocating record instance.\n");
413 HeapFree(GetProcessHeap(), 0, wwi
);
417 /**************************************************************************
418 * widAddBuffer [internal]
421 static DWORD
widAddBuffer(WINE_WAVEINST
* wwi
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
) {
422 TRACE("(%p, %p, %08X);\n", wwi
, lpWaveHdr
, dwSize
);
424 if (!wwi
|| wwi
->state
== WINE_WS_FAILED
) {
425 WARN("Stream instance invalid.\n");
426 return MMSYSERR_INVALHANDLE
;
429 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
430 return WAVERR_UNPREPARED
;
432 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
433 return WAVERR_STILLPLAYING
;
435 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
436 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
437 lpWaveHdr
->dwBytesRecorded
= 0;
438 lpWaveHdr
->lpNext
= 0;
440 PULSE_AddRingMessage(&wwi
->msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
442 return MMSYSERR_NOERROR
;
445 /**************************************************************************
446 * widRecorderMessage [internal]
448 static DWORD
widRecorderMessage(WINE_WAVEINST
*wwi
, enum win_wm_message message
) {
449 if (!wwi
|| wwi
->state
== WINE_WS_FAILED
) {
450 WARN("Stream instance invalid.\n");
451 return MMSYSERR_INVALHANDLE
;
454 PULSE_AddRingMessage(&wwi
->msgRing
, message
, 0, TRUE
);
455 return MMSYSERR_NOERROR
;
458 /**************************************************************************
459 * widGetPosition [internal]
461 static DWORD
widGetPosition(WINE_WAVEINST
*wwi
, LPMMTIME lpTime
, DWORD uSize
) {
463 if (!wwi
|| wwi
->state
== WINE_WS_FAILED
) {
464 WARN("Stream instance invalid.\n");
465 return MMSYSERR_INVALHANDLE
;
468 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
470 bytes
= wwi
->timing_info
->read_index
- wwi
->last_reset
;
471 time
= pa_bytes_to_usec(bytes
, &wwi
->sample_spec
) / 1000;
473 switch (lpTime
->wType
) {
475 lpTime
->u
.sample
= bytes
/ pa_frame_size(&wwi
->sample_spec
);
476 TRACE("TIME_SAMPLES=%u\n", lpTime
->u
.sample
);
480 TRACE("TIME_MS=%u\n", lpTime
->u
.ms
);
483 lpTime
->u
.smpte
.fps
= 30;
484 lpTime
->u
.smpte
.sec
= time
/1000;
485 lpTime
->u
.smpte
.min
= lpTime
->u
.smpte
.sec
/ 60;
486 lpTime
->u
.smpte
.sec
-= 60 * lpTime
->u
.smpte
.min
;
487 lpTime
->u
.smpte
.hour
= lpTime
->u
.smpte
.min
/ 60;
488 lpTime
->u
.smpte
.min
-= 60 * lpTime
->u
.smpte
.hour
;
489 lpTime
->u
.smpte
.frame
= time
/ lpTime
->u
.smpte
.fps
* 1000;
490 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
491 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
492 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
495 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime
->wType
);
496 lpTime
->wType
= TIME_BYTES
;
499 lpTime
->u
.cb
= bytes
;
500 TRACE("TIME_BYTES=%u\n", lpTime
->u
.cb
);
503 return MMSYSERR_NOERROR
;
506 /**************************************************************************
507 * widGetDevCaps [internal]
509 static DWORD
widGetDevCaps(DWORD wDevID
, LPWAVEINCAPSW lpCaps
, DWORD dwSize
) {
510 TRACE("(%u, %p, %u);\n", wDevID
, lpCaps
, dwSize
);
512 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
514 if (wDevID
>= PULSE_WidNumDevs
) {
515 TRACE("Asked for device %d, but only %d known!\n", wDevID
, PULSE_WidNumDevs
);
516 return MMSYSERR_INVALHANDLE
;
519 memcpy(lpCaps
, &(WInDev
[wDevID
].caps
.in
), min(dwSize
, sizeof(*lpCaps
)));
520 return MMSYSERR_NOERROR
;
523 /**************************************************************************
524 * widGetNumDevs [internal]
525 * Context-sanity check here, as if we respond with 0, WINE will move on
526 * to the next wavein driver.
528 static DWORD
widGetNumDevs() {
529 if (pa_context_get_state(PULSE_context
) != PA_CONTEXT_READY
)
532 return PULSE_WidNumDevs
;
535 /**************************************************************************
536 * widDevInterfaceSize [internal]
538 static DWORD
widDevInterfaceSize(UINT wDevID
, LPDWORD dwParam1
) {
539 TRACE("(%u, %p)\n", wDevID
, dwParam1
);
541 *dwParam1
= MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
542 NULL
, 0 ) * sizeof(WCHAR
);
543 return MMSYSERR_NOERROR
;
546 /**************************************************************************
547 * widDevInterface [internal]
549 static DWORD
widDevInterface(UINT wDevID
, PWCHAR dwParam1
, DWORD dwParam2
) {
550 if (dwParam2
>= MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
551 NULL
, 0 ) * sizeof(WCHAR
))
553 MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
554 dwParam1
, dwParam2
/ sizeof(WCHAR
));
555 return MMSYSERR_NOERROR
;
557 return MMSYSERR_INVALPARAM
;
560 /**************************************************************************
561 * widDsDesc [internal]
563 DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
565 *desc
= WInDev
[wDevID
].ds_desc
;
566 return MMSYSERR_NOERROR
;
569 /**************************************************************************
570 * widMessage (WINEPULSE.@)
572 DWORD WINAPI
PULSE_widMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
573 DWORD dwParam1
, DWORD dwParam2
) {
580 /* FIXME: Pretend this is supported */
582 case WIDM_OPEN
: return widOpen (wDevID
, (LPDWORD
)dwUser
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
583 case WIDM_CLOSE
: return widClose (wDevID
, (WINE_WAVEINST
*)dwUser
);
584 case WIDM_ADDBUFFER
: return widAddBuffer ((WINE_WAVEINST
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
585 case WIDM_PREPARE
: return MMSYSERR_NOTSUPPORTED
;
586 case WIDM_UNPREPARE
: return MMSYSERR_NOTSUPPORTED
;
587 case WIDM_GETDEVCAPS
: return widGetDevCaps (wDevID
, (LPWAVEINCAPSW
)dwParam1
, dwParam2
);
588 case WIDM_GETNUMDEVS
: return widGetNumDevs ();
589 case WIDM_GETPOS
: return widGetPosition ((WINE_WAVEINST
*)dwUser
, (LPMMTIME
)dwParam1
, dwParam2
);
590 case WIDM_RESET
: return widRecorderMessage((WINE_WAVEINST
*)dwUser
, WINE_WM_RESETTING
);
591 case WIDM_START
: return widRecorderMessage((WINE_WAVEINST
*)dwUser
, WINE_WM_STARTING
);
592 case WIDM_STOP
: return widRecorderMessage((WINE_WAVEINST
*)dwUser
, WINE_WM_STOPPING
);
593 case DRV_QUERYDEVICEINTERFACESIZE
: return widDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
594 case DRV_QUERYDEVICEINTERFACE
: return widDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
595 case DRV_QUERYDSOUNDIFACE
: return MMSYSERR_NOTSUPPORTED
; /* Use emulation, as there is no advantage */
596 case DRV_QUERYDSOUNDDESC
: return widDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
598 FIXME("unknown message %d!\n", wMsg
);
600 return MMSYSERR_NOTSUPPORTED
;
603 #else /* HAVE_PULSEAUDIO */
605 /**************************************************************************
606 * widMessage (WINEPULSE.@)
608 DWORD WINAPI
PULSE_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
609 DWORD dwParam1
, DWORD dwParam2
) {
610 // FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
611 return MMSYSERR_NOTENABLED
;
614 #endif /* HAVE_PULSEAUDIO */