2 * Wine Driver for PulseAudio - WaveOut Functionality
3 * http://pulseaudio.org/
5 * Copyright 2002 Eric Pouech
6 * 2002 Marco Pietrobono
8 * 2006-2007 Maarten Lankhorst
9 * 2008 Arthur Taylor (PulseAudio version)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
44 #include <winepulse.h>
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(wavein
);
53 * - Under / Over flows not currently handled
54 * - The buffer size is massive usually
57 /*======================================================================*
58 * Low level WAVE IN implementation *
59 *======================================================================*/
61 /**************************************************************************
62 * widNotifyClient [internal]
64 static DWORD
widNotifyClient(WINE_WAVEDEV
* wwi
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
)
66 TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg
, dwParam1
, dwParam2
);
72 if (wwi
->wFlags
!= DCB_NULL
&&
73 !DriverCallback(wwi
->waveDesc
.dwCallback
, wwi
->wFlags
, (HDRVR
)wwi
->waveDesc
.hWave
,
74 wMsg
, wwi
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
75 WARN("can't notify client !\n");
76 return MMSYSERR_ERROR
;
80 FIXME("Unknown callback message %u\n", wMsg
);
81 return MMSYSERR_INVALPARAM
;
83 return MMSYSERR_NOERROR
;
86 /**************************************************************************
87 * widRecorder [internal]
89 static DWORD CALLBACK
widRecorder(LPVOID pmt
) {
90 WORD uDevID
= (DWORD
)pmt
;
91 WINE_WAVEDEV
*wwi
= (WINE_WAVEDEV
*)&WInDev
[uDevID
];
92 DWORD dwSleepTime
= INFINITE
;
94 enum win_wm_message msg
;
99 size_t next_record_size
= 0;
100 size_t record_size
= 0;
101 size_t record_data_offset
= 0;
102 const void *record_data
= NULL
;
104 wwi
->state
= WINE_WS_STOPPED
;
105 SetEvent(wwi
->hStartUpEvent
);
108 if (wwi
->state
== WINE_WS_PLAYING
) {
109 lpWaveHdr
= wwi
->lpQueuePtr
;
110 dwSleepTime
= INFINITE
;
111 /* Fill up any Queue WaveHdrs until we run of out of them or data*/
112 while (lpWaveHdr
&& record_size
- record_data_offset
&& record_data
) {
113 toRead
= min(lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
, record_size
- record_data_offset
);
115 TRACE("BL:%u\tBR:%u\t,RD:%u\tRDO%u\n",lpWaveHdr
->dwBufferLength
, lpWaveHdr
->dwBytesRecorded
, record_size
, record_data_offset
);
117 TRACE("Copying %u bytes to %p from %p.\n", toRead
, lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
, (void*)((size_t)record_data
+ record_data_offset
));
118 memcpy(lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
, (void*)((size_t)record_data
+ record_data_offset
), toRead
);
120 lpWaveHdr
->dwBytesRecorded
+= toRead
;
121 wwi
->dwPartialOffset
+= toRead
;
122 lpWaveHdr
->reserved
= wwi
->dwPartialOffset
;
123 record_data_offset
+= toRead
;
125 if (lpWaveHdr
->dwBufferLength
<= lpWaveHdr
->dwBytesRecorded
) {
126 TRACE("(%p) is done.\n", lpWaveHdr
);
127 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
128 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
129 wwi
->lpQueuePtr
= lpWaveHdr
->lpNext
;
130 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
131 lpWaveHdr
= wwi
->lpQueuePtr
;
132 dwSleepTime
= pa_bytes_to_usec(toRead
, &wwi
->sample_spec
)/1000;
137 /* If we have run out of data, ask for more */
138 if (record_size
- record_data_offset
== 0 || !record_data
) {
139 pa_stream_drop(wwi
->stream
);
140 if (next_record_size
) {
141 if (pa_stream_peek(wwi
->stream
, &record_data
, &record_size
) < 0 || record_data
== NULL
)
142 printf("Failure!\n");
144 record_data_offset
= 0;
145 next_record_size
= 0;
150 TRACE("Waiting %u ms\n", dwSleepTime
);
151 PULSE_WaitRingMessage(&wwi
->msgRing
, dwSleepTime
);
153 while (PULSE_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
)) {
154 TRACE("Received %s %x\n", PULSE_getCmdString(msg
), param
);
157 next_record_size
= (size_t)param
;
161 case WINE_WM_STARTING
:
162 wwi
->state
= WINE_WS_PLAYING
;
163 if (pa_stream_is_corked(wwi
->stream
) && (o
= pa_stream_cork(wwi
->stream
, 0, PULSE_stream_success_callback
, wwi
)))
164 PULSE_wait_for_operation(wwi
->stream
, o
);
168 case WINE_WM_STOPPING
:
169 if (wwi
->state
!= WINE_WS_STOPPED
) {
170 if (!pa_stream_is_corked(wwi
->stream
) && (o
= pa_stream_cork(wwi
->stream
, 1, PULSE_stream_success_callback
, wwi
)))
171 PULSE_wait_for_operation(wwi
->stream
, o
);
173 pa_stream_drop(wwi
->stream
);
175 /* return current buffer to app */
176 lpWaveHdr
= wwi
->lpQueuePtr
;
178 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
180 TRACE("stop %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
182 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
183 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
185 wwi
->lpQueuePtr
= lpNext
;
186 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
189 wwi
->state
= WINE_WS_STOPPED
;
195 lpWaveHdr
= (LPWAVEHDR
)param
;
196 lpWaveHdr
->lpNext
= 0;
197 lpWaveHdr
->dwBytesRecorded
= 0;
199 /* insert buffer at the end of queue */
202 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
208 case WINE_WM_RESETTING
:
209 if (wwi
->state
!= WINE_WS_STOPPED
) {
211 if (!pa_stream_is_corked(wwi
->stream
) &&
212 (o
= pa_stream_cork(wwi
->stream
, 1, PULSE_stream_success_callback
, wwi
)))
213 PULSE_wait_for_operation(wwi
->stream
, o
);
215 if ((o = pa_stream_update_timing_info(wwi->stream, PULSE_stream_success_callback, wwi)))
216 PULSE_wait_for_operation(wwi->stream, o);
218 wwi
->state
= WINE_WS_STOPPED
;
221 /* return all buffers to the app */
222 for (lpWaveHdr
= wwi
->lpQueuePtr
; lpWaveHdr
;) {
223 TRACE("reset %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
224 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
225 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
226 lpWaveHdr
= lpWaveHdr
->lpNext
;
227 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
230 wwi
->dwPartialOffset
= 0;
231 wwi
->lpQueuePtr
= NULL
;
235 case WINE_WM_CLOSING
:
237 wwi
->state
= WINE_WS_CLOSED
;
240 /* shouldn't go here */
242 FIXME("unknown message %d\n", msg
);
249 /**************************************************************************
252 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
) {
256 TRACE("(%u, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
257 if (lpDesc
== NULL
) {
258 WARN("Invalid Parameter !\n");
259 return MMSYSERR_INVALPARAM
;
262 if (wDevID
>= PULSE_WidNumDevs
) {
263 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
264 return MMSYSERR_BADDEVICEID
;
267 wwi
= &WInDev
[wDevID
];
269 if (!PULSE_setupFormat(lpDesc
->lpFormat
, &wwi
->sample_spec
) &&
270 !pa_sample_spec_valid(&wwi
->sample_spec
)) {
271 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
272 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
273 lpDesc
->lpFormat
->nSamplesPerSec
);
274 return WAVERR_BADFORMAT
;
278 char t
[PA_SAMPLE_SPEC_SNPRINT_MAX
];
279 pa_sample_spec_snprint(t
, sizeof(t
), &wwi
->sample_spec
);
280 TRACE("Device opened using sample spec '%s'\n", t
);
283 if (dwFlags
& WAVE_FORMAT_QUERY
) {
284 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
285 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
286 lpDesc
->lpFormat
->nSamplesPerSec
);
287 return MMSYSERR_NOERROR
;
290 if (wwi
->stream
!= NULL
) {
291 WARN("%d already allocated\n", wDevID
);
292 return MMSYSERR_ALLOCATED
;
295 wwi
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
296 wwi
->waveDesc
= *lpDesc
;
298 wwi
->timing_info
= NULL
;
299 wwi
->lpQueuePtr
= NULL
;
300 wwi
->dwPartialOffset
= 0;
302 PULSE_InitRingMessage(&wwi
->msgRing
);
304 wwi
->stream
= pa_stream_new(PULSE_context
, "Wine Recording", &wwi
->sample_spec
, NULL
);
307 /* setup callbacks */
308 pa_stream_set_state_callback(wwi
->stream
, PULSE_stream_state_callback
, wwi
);
309 pa_stream_set_read_callback(wwi
->stream
, PULSE_stream_request_callback
, wwi
);
311 pa_threaded_mainloop_lock(PULSE_ml
);
313 pa_stream_connect_record(wwi
->stream
, wwi
->device_name
, NULL
, PA_STREAM_START_CORKED
);
316 pa_context_state_t cstate
= pa_context_get_state(PULSE_context
);
317 pa_stream_state_t sstate
= pa_stream_get_state(wwi
->stream
);
319 if (cstate
== PA_CONTEXT_FAILED
|| cstate
== PA_CONTEXT_TERMINATED
||
320 sstate
== PA_STREAM_FAILED
|| sstate
== PA_STREAM_TERMINATED
) {
321 WARN("failed to connect context object: %s\n", pa_strerror(pa_context_errno(PULSE_context
)));
322 pa_threaded_mainloop_unlock(PULSE_ml
);
323 pa_stream_unref(wwi
->stream
);
324 return MMSYSERR_NODRIVER
;
327 if (sstate
== PA_STREAM_READY
)
330 pa_threaded_mainloop_wait(PULSE_ml
);
333 if ((o
= pa_stream_update_timing_info(wwi
->stream
, PULSE_stream_success_callback
, wwi
)))
334 PULSE_wait_for_operation(wwi
->stream
, o
);
336 wwi
->timing_info
= pa_stream_get_timing_info(wwi
->stream
);
337 assert(wwi
->timing_info
);
339 pa_threaded_mainloop_unlock(PULSE_ml
);
341 wwi
->hStartUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
342 wwi
->hThread
= CreateThread(NULL
, 0, widRecorder
, (LPVOID
)(DWORD
)wDevID
, 0, &(wwi
->dwThreadID
));
344 SetThreadPriority(wwi
->hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
346 ERR("Thread creation for the widRecorder failed!\n");
347 CloseHandle(wwi
->hStartUpEvent
);
348 if (wwi
->msgRing
.ring_buffer_size
> 0)
349 PULSE_DestroyRingMessage(&wwi
->msgRing
);
350 pa_stream_disconnect(wwi
->stream
);
351 pa_stream_unref(wwi
->stream
);
352 return MMSYSERR_NOMEM
;
354 WaitForSingleObject(wwi
->hStartUpEvent
, INFINITE
);
355 CloseHandle(wwi
->hStartUpEvent
);
356 wwi
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
358 return widNotifyClient(wwi
, WIM_OPEN
, 0L, 0L);
361 /**************************************************************************
362 * widClose [internal]
364 static DWORD
widClose(WORD wDevID
) {
367 TRACE("(%u);\n", wDevID
);
369 if (wDevID
>= PULSE_WidNumDevs
) {
370 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
371 return MMSYSERR_BADDEVICEID
;
374 if (WInDev
[wDevID
].stream
== NULL
) {
375 WARN("Requested to close already closed device %d!\n", wDevID
);
376 return MMSYSERR_BADDEVICEID
;
379 wwi
= &WInDev
[wDevID
];
380 if (wwi
->lpQueuePtr
) {
381 WARN("buffers still playing !\n");
382 return WAVERR_STILLPLAYING
;
385 if (wwi
->hThread
!= INVALID_HANDLE_VALUE
) {
386 PULSE_AddRingMessage(&wwi
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
389 PULSE_DestroyRingMessage(&wwi
->msgRing
);
391 pa_threaded_mainloop_lock(PULSE_ml
);
392 pa_stream_disconnect(wwi
->stream
);
393 pa_stream_unref(wwi
->stream
);
394 pa_threaded_mainloop_unlock(PULSE_ml
);
397 wwi
->state
= WINE_WS_CLOSED
;
399 return widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
402 /**************************************************************************
403 * widAddBuffer [internal]
406 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
) {
407 TRACE("(%u, %p, %08X);\n", wDevID
, lpWaveHdr
, dwSize
);
409 /* first, do the sanity checks... */
410 if (wDevID
>= PULSE_WidNumDevs
) {
411 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
412 return MMSYSERR_BADDEVICEID
;
415 if (WInDev
[wDevID
].stream
== NULL
) {
416 WARN("Requested to add buffer to already closed device %d!\n", wDevID
);
417 return MMSYSERR_BADDEVICEID
;
420 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
421 return WAVERR_UNPREPARED
;
423 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
424 return WAVERR_STILLPLAYING
;
426 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
427 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
428 lpWaveHdr
->dwBytesRecorded
= 0;
429 lpWaveHdr
->lpNext
= 0;
431 PULSE_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
433 return MMSYSERR_NOERROR
;
436 /**************************************************************************
437 * widStart [internal]
440 static DWORD
widStart(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
) {
441 TRACE("(%u, %p, %08X);\n", wDevID
, lpWaveHdr
, dwSize
);
443 /* first, do the sanity checks... */
444 if (wDevID
>= PULSE_WidNumDevs
) {
445 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
446 return MMSYSERR_BADDEVICEID
;
449 if (WInDev
[wDevID
].stream
== NULL
) {
450 WARN("Requested to start closed device %d!\n", wDevID
);
451 return MMSYSERR_BADDEVICEID
;
454 PULSE_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STARTING
, 0, TRUE
);
456 return MMSYSERR_NOERROR
;
459 /**************************************************************************
463 static DWORD
widStop(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
) {
464 TRACE("(%u, %p, %08X);\n", wDevID
, lpWaveHdr
, dwSize
);
466 /* first, do the sanity checks... */
467 if (wDevID
>= PULSE_WidNumDevs
) {
468 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
469 return MMSYSERR_BADDEVICEID
;
472 if (WInDev
[wDevID
].stream
== NULL
) {
473 WARN("Requested to stop closed device %d!\n", wDevID
);
474 return MMSYSERR_BADDEVICEID
;
477 PULSE_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STOPPING
, 0, TRUE
);
479 return MMSYSERR_NOERROR
;
482 /**************************************************************************
483 * widReset [internal]
485 static DWORD
widReset(WORD wDevID
) {
486 TRACE("(%u);\n", wDevID
);
488 if (wDevID
>= PULSE_WidNumDevs
) {
489 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
490 return MMSYSERR_BADDEVICEID
;
493 if (WInDev
[wDevID
].stream
== NULL
) {
494 WARN("Requested to reset closed device %d!\n", wDevID
);
495 return MMSYSERR_BADDEVICEID
;
498 PULSE_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_RESETTING
, 0, TRUE
);
499 return MMSYSERR_NOERROR
;
502 /**************************************************************************
503 * widGetDevCaps [internal]
505 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEINCAPSW lpCaps
, DWORD dwSize
) {
506 TRACE("(%u, %p, %u);\n", wDevID
, lpCaps
, dwSize
);
508 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
510 if (wDevID
>= PULSE_WidNumDevs
) {
511 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
512 return MMSYSERR_BADDEVICEID
;
515 memcpy(lpCaps
, &WInDev
[wDevID
].caps
.in
, min(dwSize
, sizeof(*lpCaps
)));
516 return MMSYSERR_NOERROR
;
519 /**************************************************************************
520 * widGetNumDevs [internal]
522 static DWORD
widGetNumDevs(void) {
523 return PULSE_WidNumDevs
;
526 /**************************************************************************
527 * widGetPosition [internal]
529 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME lpTime
, DWORD uSize
) {
533 TRACE("(%u, %p, %u);\n", wDevID
, lpTime
, uSize
);
535 if (wDevID
>= PULSE_WidNumDevs
) {
536 TRACE("Requested device %d, but only %d are known!\n", wDevID
, PULSE_WidNumDevs
);
537 return MMSYSERR_BADDEVICEID
;
540 if (WInDev
[wDevID
].state
== WINE_WS_CLOSED
|| !WInDev
[wDevID
].stream
) {
541 WARN("Requested position of closed device %d!\n", wDevID
);
542 return MMSYSERR_BADDEVICEID
;
545 if (lpTime
== NULL
) {
546 WARN("invalid parameter: lpTime = NULL\n");
547 return MMSYSERR_INVALPARAM
;
550 wwi
= &WInDev
[wDevID
];
551 time
= pa_bytes_to_usec(wwi
->dwPartialOffset
, &wwi
->sample_spec
); //wwi->timing_info->read_index, &wwi->sample_spec);
553 switch (lpTime
->wType
) {
555 lpTime
->u
.sample
= wwi
->timing_info
->read_index
/ pa_frame_size(&wwi
->sample_spec
);
561 lpTime
->u
.smpte
.fps
= 30;
562 lpTime
->u
.smpte
.sec
= time
/1000;
563 lpTime
->u
.smpte
.min
= lpTime
->u
.smpte
.sec
/ 60;
564 lpTime
->u
.smpte
.sec
-= 60 * lpTime
->u
.smpte
.min
;
565 lpTime
->u
.smpte
.hour
= lpTime
->u
.smpte
.min
/ 60;
566 lpTime
->u
.smpte
.min
-= 60 * lpTime
->u
.smpte
.hour
;
567 lpTime
->u
.smpte
.fps
= 30;
568 lpTime
->u
.smpte
.frame
= time
/ lpTime
->u
.smpte
.fps
* 1000;
569 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
570 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
571 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
574 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime
->wType
);
575 lpTime
->wType
= TIME_BYTES
;
578 lpTime
->u
.cb
= wwi
->timing_info
->read_index
;
579 TRACE("TIME_BYTES=%u\n", lpTime
->u
.cb
);
583 return MMSYSERR_NOERROR
;
586 /**************************************************************************
587 * widDevInterfaceSize [internal]
589 static DWORD
widDevInterfaceSize(UINT wDevID
, LPDWORD dwParam1
) {
590 TRACE("(%u, %p)\n", wDevID
, dwParam1
);
592 *dwParam1
= MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
593 NULL
, 0 ) * sizeof(WCHAR
);
594 return MMSYSERR_NOERROR
;
597 /**************************************************************************
598 * widDevInterface [internal]
600 static DWORD
widDevInterface(UINT wDevID
, PWCHAR dwParam1
, DWORD dwParam2
) {
601 if (dwParam2
>= MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
602 NULL
, 0 ) * sizeof(WCHAR
))
604 MultiByteToWideChar(CP_UNIXCP
, 0, WInDev
[wDevID
].interface_name
, -1,
605 dwParam1
, dwParam2
/ sizeof(WCHAR
));
606 return MMSYSERR_NOERROR
;
608 return MMSYSERR_INVALPARAM
;
611 /*======================================================================*
612 * Low level DSOUND implementation *
613 *======================================================================*/
614 static DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
) {
615 /* Is this possible ?*/
616 return MMSYSERR_NOTSUPPORTED
;
619 static DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
) {
620 memset(desc
, 0, sizeof(*desc
));
621 strcpy(desc
->szDesc
, "Wine PulseAudio DirectSound Driver");
622 strcpy(desc
->szDrvname
, "winepulse.drv");
623 return MMSYSERR_NOERROR
;
626 /**************************************************************************
627 * widMessage (WINEPULSE.@)
629 DWORD WINAPI
PULSE_widMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
630 DWORD dwParam1
, DWORD dwParam2
)
637 /* FIXME: Pretend this is supported */
639 case WIDM_OPEN
: return widOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
640 case WIDM_CLOSE
: return widClose (wDevID
);
641 case WIDM_ADDBUFFER
: return widAddBuffer (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
642 case WIDM_PREPARE
: return MMSYSERR_NOTSUPPORTED
;
643 case WIDM_UNPREPARE
: return MMSYSERR_NOTSUPPORTED
;
644 case WIDM_GETDEVCAPS
: return widGetDevCaps (wDevID
, (LPWAVEINCAPSW
)dwParam1
, dwParam2
);
645 case WIDM_GETNUMDEVS
: return widGetNumDevs ();
646 case WIDM_GETPOS
: return widGetPosition (wDevID
, (LPMMTIME
)dwParam1
, dwParam2
);
647 case WIDM_RESET
: return widReset (wDevID
);
648 case WIDM_START
: return widStart (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
649 case WIDM_STOP
: return widStop (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
650 case DRV_QUERYDEVICEINTERFACESIZE
: return widDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
651 case DRV_QUERYDEVICEINTERFACE
: return widDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
652 case DRV_QUERYDSOUNDIFACE
: return widDsCreate (wDevID
, (PIDSCDRIVER
*)dwParam1
);
653 case DRV_QUERYDSOUNDDESC
: return widDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
655 FIXME("unknown message %d!\n", wMsg
);
657 return MMSYSERR_NOTSUPPORTED
;
660 #else /* HAVE_PULSEAUDIO */
662 /**************************************************************************
663 * widMessage (WINEPULSE.@)
665 DWORD WINAPI
PULSE_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
666 DWORD dwParam1
, DWORD dwParam2
)
668 FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
669 return MMSYSERR_NOTENABLED
;
672 #endif /* HAVE_PULSEAUDIO */