push 5f793418e14616d83a4fb368b755a8821b49820b
[wine/hacks.git] / dlls / winepulse.drv / wavein.c
blob5495b2accaf3980df584982466b2ce740885e174
1 /*
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
24 #include "config.h"
26 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winnls.h"
33 #include "mmddk.h"
35 #include <winepulse.h>
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(wave);
41 #if HAVE_PULSEAUDIO
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);
53 switch (wMsg) {
54 case WIM_OPEN:
55 case WIM_CLOSE:
56 case WIM_DATA:
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;
63 break;
64 default:
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) {
77 size_t nbytes;
79 pa_stream_peek(wwi->stream, &wwi->buffer, &nbytes);
80 wwi->buffer_length = nbytes;
81 wwi->buffer_read_offset = 0;
83 return nbytes;
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;
94 size_t nbytes;
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);
112 else {
113 wwi->buffer = NULL;
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;
135 LPWAVEHDR lpWaveHdr;
136 enum win_wm_message msg;
137 DWORD param;
138 HANDLE ev;
139 DWORD wait;
141 wwi->state = WINE_WS_STOPPED;
142 SetEvent(wwi->hStartUpEvent);
144 for (;;) {
146 if (wwi->state != WINE_WS_PLAYING) {
147 wait = INFINITE;
148 } else {
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, &param, &ev)) {
161 TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
163 switch (msg) {
164 case WINE_WM_FEED:
165 SetEvent(ev);
166 break;
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);
174 SetEvent(ev);
175 break;
176 case WINE_WM_HEADER:
177 lpWaveHdr = (LPWAVEHDR)param;
178 lpWaveHdr->lpNext = 0;
180 /* insert buffer at the end of queue */
182 LPWAVEHDR* wh;
183 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
184 *wh = lpWaveHdr;
186 break;
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;
196 if (lpWaveHdr) {
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);
205 SetEvent(ev);
206 break;
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);
223 SetEvent(ev);
224 break;
225 case WINE_WM_CLOSING:
226 wwi->hThread = 0;
227 if ((DWORD)param == 1) {
228 /* If we are here, the stream failed */
229 wwi->state = WINE_WS_FAILED;
230 SetEvent(ev);
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");
238 ExitThread(1);
240 wwi->state = WINE_WS_CLOSED;
241 SetEvent(ev);
242 ExitThread(0);
243 /* shouldn't go here */
244 default:
245 FIXME("unknown message %d\n", msg);
246 break;
247 } /* switch(msg) */
248 } /* while(PULSE_RetrieveRingMessage()) */
249 } /* for (;;) */
252 /**************************************************************************
253 * widOpen [internal]
255 static DWORD widOpen(WORD wDevID, LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) {
256 WINE_WAVEDEV *wdi;
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;
282 goto exit;
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;
296 goto exit;
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);
304 if (!wwi->stream) {
305 ret = WAVERR_BADFORMAT;
306 goto exit;
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);
318 for (;;) {
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);
327 goto exit;
330 if (sstate == PA_STREAM_READY)
331 break;
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));
345 if (wwi->hThread)
346 SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
347 else {
348 ERR("Thread creation for the widRecorder failed!\n");
349 ret = MMSYSERR_NOMEM;
350 goto exit;
352 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
353 CloseHandle(wwi->hStartUpEvent);
354 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
356 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
358 exit:
359 if (!wwi)
360 return ret;
362 if (wwi->hStartUpEvent != INVALID_HANDLE_VALUE)
363 CloseHandle(wwi->hStartUpEvent);
365 if (wwi->msgRing.ring_buffer_size > 0)
366 PULSE_DestroyRingMessage(&wwi->msgRing);
368 if (wwi->stream) {
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);
375 return ret;
377 /**************************************************************************
378 * widClose [internal]
380 static DWORD widClose(WORD wDevID, WINE_WAVEINST *wwi) {
381 DWORD ret;
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;
387 } else if (!wwi) {
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);
414 return ret;
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) {
462 DWORD bytes, time;
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) {
474 case TIME_SAMPLES:
475 lpTime->u.sample = bytes / pa_frame_size(&wwi->sample_spec);
476 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
477 break;
478 case TIME_MS:
479 lpTime->u.ms = time;
480 TRACE("TIME_MS=%u\n", lpTime->u.ms);
481 break;
482 case TIME_SMPTE:
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);
493 break;
494 default:
495 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
496 lpTime->wType = TIME_BYTES;
497 /* fall through */
498 case TIME_BYTES:
499 lpTime->u.cb = bytes;
500 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
501 break;
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)
530 return 0;
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) {
575 switch (wMsg) {
576 case DRVM_INIT:
577 case DRVM_EXIT:
578 case DRVM_ENABLE:
579 case DRVM_DISABLE:
580 /* FIXME: Pretend this is supported */
581 return 0;
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);
597 default:
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 */