push 0aee684398866a49d7bd56376e3acea1a895a88c
[wine/hacks.git] / dlls / winepulse.drv / waveout.c
blobe7454fd2d48756d350e3dd0d99b217ce52a33d52
1 /*
2 * Wine Driver for PulseAudio - WaveOut 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 "winerror.h"
34 #include "mmddk.h"
36 #include <winepulse.h>
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(wave);
42 #if HAVE_PULSEAUDIO
44 /* state diagram for waveOut writing:
46 * +---------+-------------+---------------+---------------------------------+
47 * | state | function | event | new state |
48 * +---------+-------------+---------------+---------------------------------+
49 * | | open() | | STOPPED |
50 * | PAUSED | write() | | PAUSED |
51 * | STOPPED | write() | <thrd create> | PLAYING |
52 * | PLAYING | write() | HEADER | PLAYING |
53 * | (other) | write() | <error> | |
54 * | (any) | pause() | PAUSING | PAUSED |
55 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
56 * | PAUSED | reset() | RESETTING | PAUSED |
57 * | (other) | reset() | RESETTING | STOPPED |
58 * | (any) | close() | CLOSING | CLOSED |
59 * +---------+-------------+---------------+---------------------------------+
63 * - It is currently unknown if pausing in a loop works the same as expected.
66 /*======================================================================*
67 * WAVE OUT specific PulseAudio Callbacks *
68 *======================================================================*/
70 /**************************************************************************
71 * WAVEOUT_StreamRequestCallback
73 * Called by the pulse mainloop whenever it wants audio data.
75 static void WAVEOUT_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userdata) {
76 WINE_WAVEINST *ww = (WINE_WAVEINST*)userdata;
78 TRACE("Asking to be fed %u bytes\n", nbytes);
80 /* Make sure that the player/recorder is running */
81 if (ww->hThread != INVALID_HANDLE_VALUE && ww->msgRing.messages) {
82 PULSE_AddRingMessage(&ww->msgRing, WINE_WM_FEED, (DWORD)nbytes, FALSE);
86 /**************************************************************************
87 * WAVEOUT_SinkInputInfoCallback [internal]
89 * Called by the pulse thread. Used for wodGetVolume.
91 static void WAVEOUT_SinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
92 WINE_WAVEINST* wwo = (WINE_WAVEINST*)userdata;
93 if (!eol && i) {
94 for (wwo->volume.channels = 0; wwo->volume.channels != i->volume.channels; wwo->volume.channels++)
95 wwo->volume.values[wwo->volume.channels] = i->volume.values[wwo->volume.channels];
96 pa_threaded_mainloop_signal(PULSE_ml, 0);
100 /*======================================================================*
101 * "Low level" WAVE OUT implementation *
102 *======================================================================*/
104 /**************************************************************************
105 * wodPlayer_NotifyClient [internal]
107 static DWORD wodPlayer_NotifyClient(WINE_WAVEINST* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2) {
108 /* TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2); */
110 switch (wMsg) {
111 case WOM_OPEN:
112 case WOM_CLOSE:
113 case WOM_DONE:
114 if (wwo->wFlags != DCB_NULL &&
115 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
116 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
117 WARN("can't notify client !\n");
118 return MMSYSERR_ERROR;
120 break;
121 default:
122 FIXME("Unknown callback message %u\n", wMsg);
123 return MMSYSERR_INVALPARAM;
125 return MMSYSERR_NOERROR;
128 /**************************************************************************
129 * wodPlayer_BeginWaveHdr [internal]
131 * Makes the specified lpWaveHdr the currently playing wave header.
132 * If the specified wave header is a begin loop and we're not already in
133 * a loop, setup the loop.
135 static void wodPlayer_BeginWaveHdr(WINE_WAVEINST* wwo, LPWAVEHDR lpWaveHdr) {
136 wwo->lpPlayPtr = lpWaveHdr;
138 if (!lpWaveHdr) return;
140 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
141 if (wwo->lpLoopPtr) {
142 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
143 } else {
144 TRACE("Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
145 wwo->lpLoopPtr = lpWaveHdr;
146 /* Windows does not touch WAVEHDR.dwLoops,
147 * so we need to make an internal copy */
148 wwo->dwLoops = lpWaveHdr->dwLoops;
151 wwo->dwPartialOffset = 0;
154 /**************************************************************************
155 * wodPlayer_PlayPtrNext [internal]
157 * Advance the play pointer to the next waveheader, looping if required.
159 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEINST* wwo) {
160 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
162 wwo->dwPartialOffset = 0;
163 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
164 /* We're at the end of a loop, loop if required */
165 if (--wwo->dwLoops > 0) {
166 wwo->lpPlayPtr = wwo->lpLoopPtr;
167 } else {
168 /* Handle overlapping loops correctly */
169 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
170 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
171 /* shall we consider the END flag for the closing loop or for
172 * the opening one or for both ???
173 * code assumes for closing loop only
175 } else {
176 lpWaveHdr = lpWaveHdr->lpNext;
178 wwo->lpLoopPtr = NULL;
179 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
181 } else {
182 /* We're not in a loop. Advance to the next wave header */
183 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
186 return lpWaveHdr;
189 /**************************************************************************
190 * wodPlayer_CheckReleasing [internal]
192 * Check to make sure that playback has not stalled. If stalled ask to reduce
193 * the size of the buffer on the pulse server side.
195 static void wodPlayer_CheckReleasing(WINE_WAVEINST *wwo) {
196 LPWAVEHDR lpWaveHdr;
198 if (wwo->buffer_attr.tlength == -1) {
199 pa_threaded_mainloop_lock(PULSE_ml);
200 if (!wwo->timing_info->playing) {
202 /* Calculate how large a buffer the application has made so far */
203 wwo->buffer_attr.tlength = 0;
204 wwo->buffer_attr.minreq = wwo->lpQueuePtr->dwBufferLength;
205 for (lpWaveHdr = wwo->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
206 wwo->buffer_attr.tlength += lpWaveHdr->dwBufferLength;
208 WARN("Asking for new buffer target length of %llums (%u bytes)\n",
209 pa_bytes_to_usec(wwo->buffer_attr.tlength, &wwo->sample_spec) / 1000,
210 wwo->buffer_attr.tlength);
212 /* Try and adjust the buffer attributes so that playback can start.
213 * Because of bugs pa_stream_set_buffer_attr() does not work on started
214 * streams for server version 0.9.11 to 0.9.14 */
215 PULSE_WaitForOperation(pa_stream_set_buffer_attr(wwo->stream, &wwo->buffer_attr, PULSE_StreamSuccessCallback, wwo));
217 pa_threaded_mainloop_unlock(PULSE_ml);
221 /**************************************************************************
222 * wodPlayer_NotifyCompletions [internal]
224 * Notifies the client of wavehdr completion starting from lpQueuePtr and
225 * stopping when hitting an unwritten wavehdr, the beginning of a loop or a
226 * wavehdr that has not been played, when referenced to the time parameter.
228 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEINST* wwo, BOOL force, pa_usec_t time) {
229 LPWAVEHDR lpWaveHdr = wwo->lpQueuePtr;
230 pa_usec_t wait;
232 while (lpWaveHdr) {
233 if (!force) {
234 /* Start from lpQueuePtr and keep notifying until:
235 * - we hit an unwritten wavehdr
236 * - we hit the beginning of a running loop
237 * - we hit a wavehdr which hasn't finished playing
239 if (lpWaveHdr == wwo->lpLoopPtr) { TRACE("loop %p\n", lpWaveHdr); return INFINITE; }
240 if (lpWaveHdr == wwo->lpPlayPtr) { TRACE("play %p\n", lpWaveHdr); return INFINITE; }
242 /* See if this data has been played, and if not, return when it will have been */
243 wait = pa_bytes_to_usec(lpWaveHdr->reserved + lpWaveHdr->dwBufferLength, &wwo->sample_spec);
244 if (wait >= time) {
245 wait = ((wait - time) + (pa_usec_t)999) / (pa_usec_t)1000;
246 return wait ?: 1;
249 TRACE("Returning %p.[%i]\n", lpWaveHdr, (DWORD)lpWaveHdr->reserved);
251 /* return the wavehdr */
252 wwo->lpQueuePtr = lpWaveHdr->lpNext;
253 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
254 lpWaveHdr->dwFlags |= WHDR_DONE;
256 wodPlayer_NotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
257 lpWaveHdr = wwo->lpQueuePtr;
259 /* No more wavehdrs */
260 TRACE("Empty queue\n");
261 return INFINITE;
264 /**************************************************************************
265 * wodPlayer_WriteMax [internal]
267 * Write either how much free space or how much data we have, depending on
268 * which is less
270 static DWORD wodPlayer_WriteMax(WINE_WAVEINST *wwo, size_t *space) {
271 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
272 size_t nbytes;
274 nbytes = min(lpWaveHdr->dwBufferLength - wwo->dwPartialOffset, *space);
276 TRACE("Writing wavehdr %p.%u[%u]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
277 pa_stream_write(wwo->stream, lpWaveHdr->lpData + wwo->dwPartialOffset, nbytes, NULL, 0, PA_SEEK_RELATIVE);
279 /* Check to see if we wrote all of the wavehdr */
280 if ((wwo->dwPartialOffset += nbytes) >= lpWaveHdr->dwBufferLength)
281 wodPlayer_PlayPtrNext(wwo);
283 *space -= nbytes;
285 return nbytes;
288 /**************************************************************************
289 * wodPlayer_Feed [internal]
291 * Feed as much sound data as we can into pulse using wodPlayer_WriteMax.
292 * size_t space _must_ have come from either pa_stream_writable_size() or
293 * the value from a stream write callback, as if it isn't you run the risk
294 * of a buffer overflow in which audio data will be lost.
296 static void wodPlayer_Feed(WINE_WAVEINST* wwo, size_t space) {
298 if (!space || !wwo->stream || !PULSE_context ||
299 pa_context_get_state(PULSE_context) != PA_CONTEXT_READY ||
300 pa_stream_get_state(wwo->stream) != PA_STREAM_READY)
301 return;
303 pa_threaded_mainloop_lock(PULSE_ml);
304 /* Feed from a partial wavehdr */
305 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
306 wodPlayer_WriteMax(wwo, &space);
308 /* Feed wavehdrs until we run out of wavehdrs or buffer space */
309 if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
310 do {
311 wwo->lpPlayPtr->reserved = wwo->timing_info->write_index;
312 } while (wodPlayer_WriteMax(wwo, &space) && wwo->lpPlayPtr && space > 0);
315 pa_threaded_mainloop_unlock(PULSE_ml);
318 /**************************************************************************
319 * wodPlayer_Reset [internal]
321 * wodPlayer helper. Resets current output stream.
323 static void wodPlayer_Reset(WINE_WAVEINST* wwo) {
324 enum win_wm_message msg;
325 DWORD param;
326 HANDLE ev;
328 TRACE("(%p)\n", wwo);
330 /* Remove any buffer */
331 wodPlayer_NotifyCompletions(wwo, TRUE, 0);
333 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
334 if (wwo->state != WINE_WS_PAUSED)
335 wwo->state = WINE_WS_STOPPED;
337 wwo->dwPartialOffset = 0;
339 if (!wwo->stream ||
340 !PULSE_context ||
341 pa_context_get_state(PULSE_context) != PA_CONTEXT_READY ||
342 pa_stream_get_state(wwo->stream) != PA_STREAM_READY) {
343 return;
346 pa_threaded_mainloop_lock(PULSE_ml);
348 /* Flush the output buffer of written data*/
349 PULSE_WaitForOperation(pa_stream_flush(wwo->stream, PULSE_StreamSuccessCallback, NULL));
351 /* Reset the written byte count as some data may have been flushed */
352 if (wwo->timing_info->write_index_corrupt)
353 PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_StreamSuccessCallback, wwo));
355 wwo->dwLastReset = wwo->timing_info->write_index;
357 /* Return all pending headers in queue */
358 EnterCriticalSection(&wwo->msgRing.msg_crst);
359 while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
360 if (msg != WINE_WM_HEADER) {
361 SetEvent(ev);
362 continue;
364 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
365 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
366 wodPlayer_NotifyClient(wwo, WOM_DONE, param, 0);
368 PULSE_ResetRingMessage(&wwo->msgRing);
369 LeaveCriticalSection(&wwo->msgRing.msg_crst);
371 pa_threaded_mainloop_unlock(PULSE_ml);
374 /**************************************************************************
375 * wodPlayer_GetStreamTime [internal]
377 * Returns how many microseconds into the playback the audio stream is. Does
378 * not reset to 0 on Reset() calls. Better than pa_stream_get_time() as it is
379 * more constant.
381 static pa_usec_t wodPlayer_GetStreamTime(WINE_WAVEINST *wwo) {
382 pa_usec_t time, temp;
383 const pa_timing_info *t;
385 t = wwo->timing_info;
387 pa_threaded_mainloop_lock(PULSE_ml);
389 time = pa_bytes_to_usec(t->read_index, &wwo->sample_spec);
390 if (t->read_index_corrupt) {
391 WARN("Read index corrupt?!\n");
392 pa_threaded_mainloop_unlock(PULSE_ml);
393 return time;
396 if (t->playing) {
397 time += pa_timeval_age(&t->timestamp);
398 temp = t->transport_usec + t->configured_sink_usec;
399 if (temp > wwo->buffer_attr.tlength) temp = wwo->buffer_attr.tlength;
400 if (time > temp) time -= temp; else time = 0;
403 /* Make sure we haven't claimed to have played more than we have written */
404 temp = pa_bytes_to_usec(t->write_index, &wwo->sample_spec);
405 if (time > temp) time = temp;
407 /* No queued buffer shows an underrun, so we lie */
408 if (!wwo->lpQueuePtr) time = temp;
410 pa_threaded_mainloop_unlock(PULSE_ml);
412 return time;
415 /**************************************************************************
416 * wodPlayer_ProcessMessages [internal]
418 static void wodPlayer_ProcessMessages(WINE_WAVEINST* wwo) {
419 LPWAVEHDR lpWaveHdr;
420 enum win_wm_message msg;
421 DWORD param;
422 HANDLE ev;
424 while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
425 TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
427 switch (msg) {
428 case WINE_WM_PAUSING:
429 wwo->state = WINE_WS_PAUSED;
430 pa_threaded_mainloop_lock(PULSE_ml);
431 PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 1, PULSE_StreamSuccessCallback, wwo));
432 pa_threaded_mainloop_unlock(PULSE_ml);
433 SetEvent(ev);
434 break;
436 case WINE_WM_RESTARTING:
437 if (wwo->state == WINE_WS_PAUSED) {
438 wwo->state = WINE_WS_PLAYING;
439 pa_threaded_mainloop_lock(PULSE_ml);
440 PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 0, PULSE_StreamSuccessCallback, wwo));
441 /* If the serverside buffer was near full before pausing, we
442 * need to have space to write soon, so force playback start */
443 PULSE_WaitForOperation(pa_stream_trigger(wwo->stream, PULSE_StreamSuccessCallback, wwo));
444 pa_threaded_mainloop_unlock(PULSE_ml);
446 SetEvent(ev);
447 break;
449 case WINE_WM_HEADER:
450 lpWaveHdr = (LPWAVEHDR)param;
451 /* insert buffer at the end of queue */
453 LPWAVEHDR *wh;
454 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
455 *wh = lpWaveHdr;
458 if (!wwo->lpPlayPtr)
459 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
460 if (wwo->state == WINE_WS_STOPPED)
461 wwo->state = WINE_WS_PLAYING;
463 wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream));
464 SetEvent(ev);
465 break;
467 case WINE_WM_RESETTING:
468 wodPlayer_Reset(wwo);
469 SetEvent(ev);
470 break;
472 case WINE_WM_BREAKLOOP:
473 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL)
474 /* ensure exit at end of current loop */
475 wwo->dwLoops = 1;
476 SetEvent(ev);
477 break;
479 case WINE_WM_FEED: /* Sent by the pulse thread */
480 wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream));
481 SetEvent(ev);
482 break;
484 case WINE_WM_XRUN: /* Sent by the pulse thread */
485 WARN("Trying to recover from underrun.\n");
486 /* Return all the queued wavehdrs, so the app will send more data */
487 wodPlayer_NotifyCompletions(wwo, FALSE, (pa_usec_t)-1);
489 SetEvent(ev);
490 break;
492 case WINE_WM_CLOSING:
493 wwo->hThread = NULL;
494 wwo->state = WINE_WS_CLOSED;
495 /* sanity check: this should not happen since the device must have been reset before */
496 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
497 SetEvent(ev);
498 TRACE("Thread exiting.\n");
499 ExitThread(0);
500 /* shouldn't go here */
502 default:
503 FIXME("unknown message %d\n", msg);
504 break;
509 /**************************************************************************
510 * wodPlayer [internal]
512 * The thread which is responsible for returning WaveHdrs via DriverCallback,
513 * the writing of queued WaveHdrs, and all pause / reset stream management.
515 static DWORD CALLBACK wodPlayer(LPVOID lpParam) {
516 WINE_WAVEINST *wwo = (WINE_WAVEINST*)lpParam;
517 DWORD dwSleepTime = INFINITE;
518 int64_t delta_write;
520 wwo->state = WINE_WS_STOPPED;
521 SetEvent(wwo->hStartUpEvent);
523 /* Wait for the shortest time before an action is required. If there are
524 * no pending actions, wait forever for a command. */
525 for (;;) {
526 TRACE("Waiting %u ms\n", dwSleepTime);
527 PULSE_WaitRingMessage(&wwo->msgRing, dwSleepTime);
529 delta_write = wwo->timing_info->write_index;
530 wodPlayer_ProcessMessages(wwo);
532 /* Check for a stall situaiton */
533 if (delta_write == wwo->timing_info->write_index
534 && wwo->lpQueuePtr && !wwo->lpPlayPtr
535 && wwo->state != WINE_WS_STOPPED)
536 wodPlayer_CheckReleasing(wwo);
538 /* If there is audio playing, return headers and get next timeout */
539 if (wwo->state == WINE_WS_PLAYING) {
540 dwSleepTime = wodPlayer_NotifyCompletions(wwo, FALSE, wodPlayer_GetStreamTime(wwo));
541 } else
542 dwSleepTime = INFINITE;
545 return 0;
548 /**************************************************************************
549 * wodOpen [internal]
551 * Create a new pa_stream and connect it to a sink while creating a new
552 * WINE_WAVEINST to represent the device to the windows application.
554 static DWORD wodOpen(WORD wDevID, DWORD_PTR lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) {
555 WINE_WAVEDEV *wdo;
556 WINE_WAVEINST *wwo = NULL;
557 DWORD ret = MMSYSERR_NOERROR;
559 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
560 if (lpDesc == NULL) {
561 WARN("Invalid Parameter!\n");
562 return MMSYSERR_INVALPARAM;
565 if (wDevID >= PULSE_WodNumDevs) {
566 WARN("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs);
567 return MMSYSERR_BADDEVICEID;
569 wdo = &WOutDev[wDevID];
571 wwo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_WAVEINST));
572 if (!wwo) {
573 WARN("Out of memory?!\n");
574 return MMSYSERR_NOMEM;
576 *(WINE_WAVEINST**)lpdwUser = wwo;
578 /* check to see if format is supported and make pa_sample_spec struct */
579 if (!PULSE_SetupFormat(lpDesc->lpFormat, &wwo->sample_spec)) {
580 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
581 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
582 lpDesc->lpFormat->nSamplesPerSec);
583 ret = WAVERR_BADFORMAT;
584 goto exit;
587 /* Check to see if this was just a query */
588 if (dwFlags & WAVE_FORMAT_QUERY) {
589 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
590 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
591 lpDesc->lpFormat->nSamplesPerSec);
592 ret = MMSYSERR_NOERROR;
593 goto exit;
596 if (TRACE_ON(wave)) {
597 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
598 pa_sample_spec_snprint(t, sizeof(t), &wwo->sample_spec);
599 TRACE("Sample spec '%s'\n", t);
602 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
603 wwo->waveDesc = *lpDesc;
604 PULSE_InitRingMessage(&wwo->msgRing);
606 wwo->stream = pa_stream_new(PULSE_context, "WaveOut", &wwo->sample_spec, NULL);
607 /* If server doesn't support sample_spec, it will error out here (re: 24bit) */
608 if (!wwo->stream) {
609 ret = WAVERR_BADFORMAT;
610 goto exit;
613 /* Setup callbacks */
614 pa_stream_set_write_callback (wwo->stream, WAVEOUT_StreamRequestCallback, wwo);
615 pa_stream_set_state_callback (wwo->stream, PULSE_StreamStateCallback, wwo);
616 pa_stream_set_underflow_callback (wwo->stream, PULSE_StreamUnderflowCallback, wwo);
617 pa_stream_set_moved_callback (wwo->stream, PULSE_StreamMovedCallback, wwo);
618 pa_stream_set_suspended_callback (wwo->stream, PULSE_StreamSuspendedCallback, wwo);
620 /* Blank Buffer Attributes */
621 wwo->buffer_attr.prebuf = (uint32_t)-1;
622 wwo->buffer_attr.tlength = (uint32_t)-1;
623 wwo->buffer_attr.minreq = (uint32_t)-1;
624 wwo->buffer_attr.maxlength = (uint32_t)-1;
626 /* Try and connect */
627 TRACE("Connecting stream for playback on %s.\n", wdo->device_name);
628 pa_threaded_mainloop_lock(PULSE_ml);
629 pa_stream_connect_playback(wwo->stream, wdo->device_name, NULL, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY, NULL, NULL);
631 /* Wait for connection */
632 for (;;) {
633 pa_context_state_t cstate = pa_context_get_state(PULSE_context);
634 pa_stream_state_t sstate = pa_stream_get_state(wwo->stream);
636 if (cstate == PA_CONTEXT_FAILED || cstate == PA_CONTEXT_TERMINATED ||
637 sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED) {
638 ERR("Failed to connect stream context object: %s\n", pa_strerror(pa_context_errno(PULSE_context)));
639 pa_threaded_mainloop_unlock(PULSE_ml);
640 ret = MMSYSERR_NODRIVER;
641 goto exit;
644 if (sstate == PA_STREAM_READY)
645 break;
647 pa_threaded_mainloop_wait(PULSE_ml);
649 TRACE("(%p)->stream connected for playback.\n", wwo);
651 /* Get the pa_timing_info structure */
652 PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_StreamSuccessCallback, wwo));
653 wwo->timing_info = pa_stream_get_timing_info(wwo->stream);
654 assert(wwo->timing_info);
655 pa_threaded_mainloop_unlock(PULSE_ml);
657 /* Create and start the wodPlayer() thread to manage playback */
658 wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
659 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)wwo, 0, &(wwo->dwThreadID));
660 if (wwo->hThread)
661 SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL);
662 else {
663 ERR("Thread creation for the wodPlayer failed!\n");
664 ret = MMSYSERR_NOMEM;
665 goto exit;
667 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
668 CloseHandle(wwo->hStartUpEvent);
669 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
671 return wodPlayer_NotifyClient (wwo, WOM_OPEN, 0L, 0L);
673 exit:
674 if (!wwo)
675 return ret;
677 if (wwo->hStartUpEvent != INVALID_HANDLE_VALUE)
678 CloseHandle(wwo->hStartUpEvent);
680 if (wwo->msgRing.ring_buffer_size > 0)
681 PULSE_DestroyRingMessage(&wwo->msgRing);
683 if (wwo->stream) {
684 if (pa_stream_get_state(wwo->stream) == PA_STREAM_READY)
685 pa_stream_disconnect(wwo->stream);
686 pa_stream_unref(wwo->stream);
687 wwo->stream = NULL;
689 HeapFree(GetProcessHeap(), 0, wwo);
691 return ret;
694 /**************************************************************************
695 * wodClose [internal]
697 static DWORD wodClose(WINE_WAVEINST *wwo) {
698 DWORD ret;
700 TRACE("(%p);\n", wwo);
701 if (!wwo) {
702 WARN("Stream instance invalid.\n");
703 return MMSYSERR_INVALHANDLE;
706 if (wwo->state != WINE_WS_FAILED) {
707 if (wwo->lpQueuePtr && wwo->lpPlayPtr) {
708 WARN("buffers still playing !\n");
709 return WAVERR_STILLPLAYING;
712 pa_threaded_mainloop_lock(PULSE_ml);
713 PULSE_WaitForOperation(pa_stream_drain(wwo->stream, PULSE_StreamSuccessCallback, NULL));
714 pa_stream_disconnect(wwo->stream);
715 pa_threaded_mainloop_unlock(PULSE_ml);
717 if (wwo->hThread != INVALID_HANDLE_VALUE)
718 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
720 PULSE_DestroyRingMessage(&wwo->msgRing);
723 if (wwo->stream)
724 pa_stream_unref(wwo->stream);
725 ret = wodPlayer_NotifyClient(wwo, WOM_CLOSE, 0L, 0L);
727 HeapFree(GetProcessHeap(), 0, wwo);
729 return ret;
732 /**************************************************************************
733 * wodWrite [internal]
735 static DWORD wodWrite(WINE_WAVEINST *wwo, LPWAVEHDR lpWaveHdr, DWORD dwSize) {
736 if (!wwo || wwo->state == WINE_WS_FAILED) {
737 WARN("Stream instance invalid.\n");
738 return MMSYSERR_INVALHANDLE;
741 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
742 return WAVERR_UNPREPARED;
744 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
745 return WAVERR_STILLPLAYING;
747 lpWaveHdr->dwFlags &= ~WHDR_DONE;
748 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
749 lpWaveHdr->lpNext = 0;
750 lpWaveHdr->reserved = 0;
752 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
753 return MMSYSERR_NOERROR;
756 /**************************************************************************
757 * wodPause [internal]
759 static DWORD wodPause(WINE_WAVEINST *wwo) {
760 if (!wwo || wwo->state == WINE_WS_FAILED) {
761 WARN("Stream instance invalid.\n");
762 return MMSYSERR_INVALHANDLE;
765 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_PAUSING, 0, TRUE);
766 return MMSYSERR_NOERROR;
769 /**************************************************************************
770 * wodGetPosition [internal]
772 static DWORD wodGetPosition(WINE_WAVEINST *wwo, LPMMTIME lpTime, DWORD uSize) {
773 pa_usec_t time, temp;
775 if (!wwo || wwo->state == WINE_WS_FAILED) {
776 WARN("Stream instance invalid.\n");
777 return MMSYSERR_INVALHANDLE;
780 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
782 time = wodPlayer_GetStreamTime(wwo);
784 temp = pa_bytes_to_usec(wwo->dwLastReset, &wwo->sample_spec);
785 if (time > temp) time -= temp; else time = 0;
787 return PULSE_UsecToMMTime(time, lpTime, &wwo->sample_spec);
789 /**************************************************************************
790 * wodBreakLoop [internal]
792 static DWORD wodBreakLoop(WINE_WAVEINST *wwo) {
793 if (!wwo || wwo->state == WINE_WS_FAILED) {
794 WARN("Stream instance invalid.\n");
795 return MMSYSERR_INVALHANDLE;
798 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
799 return MMSYSERR_NOERROR;
802 /**************************************************************************
803 * wodGetDevCaps [internal]
805 static DWORD wodGetDevCaps(DWORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize) {
806 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
808 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
810 if (wDevID >= PULSE_WodNumDevs) {
811 TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs);
812 return MMSYSERR_INVALHANDLE;
815 memcpy(lpCaps, &(WOutDev[wDevID].caps.out), min(dwSize, sizeof(*lpCaps)));
816 return MMSYSERR_NOERROR;
819 /**************************************************************************
820 * wodGetNumDevs [internal]
821 * Context-sanity check here, as if we respond with 0, WINE will move on
822 * to the next waveout driver.
824 static DWORD wodGetNumDevs(void) {
825 if (!PULSE_ml || !PULSE_context || pa_context_get_state(PULSE_context) != PA_CONTEXT_READY)
826 return 0;
828 return PULSE_WodNumDevs;
831 /**************************************************************************
832 * wodGetVolume [internal]
834 static DWORD wodGetVolume(WINE_WAVEINST *wwo, LPDWORD lpdwVol) {
835 double value1, value2;
836 DWORD wleft, wright;
838 if (!wwo || wwo->state == WINE_WS_FAILED) {
839 WARN("Stream instance invalid.\n");
840 return MMSYSERR_INVALHANDLE;
843 TRACE("(%p, %p);\n", wwo, lpdwVol);
845 if (lpdwVol == NULL)
846 return MMSYSERR_NOTENABLED;
848 pa_threaded_mainloop_lock(PULSE_ml);
849 if (wwo->stream && PULSE_context && pa_context_get_state(PULSE_context) == PA_CONTEXT_READY &&
850 pa_stream_get_state(wwo->stream) == PA_STREAM_READY) {
851 PULSE_WaitForOperation(pa_context_get_sink_input_info(PULSE_context, pa_stream_get_index(wwo->stream), WAVEOUT_SinkInputInfoCallback, wwo));
853 pa_threaded_mainloop_unlock(PULSE_ml);
856 if (wwo->volume.channels == 2) {
857 value1 = pa_sw_volume_to_linear(wwo->volume.values[0]);
858 value2 = pa_sw_volume_to_linear(wwo->volume.values[1]);
859 } else {
860 value1 = pa_sw_volume_to_linear(pa_cvolume_avg(&wwo->volume));
861 value2 = value1;
864 wleft = 0xFFFFl * value1;
865 wright = 0xFFFFl * value2;
867 if (wleft > 0xFFFFl)
868 wleft = 0xFFFFl;
869 if (wright > 0xFFFFl)
870 wright = 0xFFFFl;
872 *lpdwVol = (WORD)wleft + (WORD)(wright << 16);
874 return MMSYSERR_NOERROR;
877 /**************************************************************************
878 * wodSetVolume [internal]
880 static DWORD wodSetVolume(WINE_WAVEINST *wwo, DWORD dwParam1) {
881 double value1, value2;
883 TRACE("(%p, %08X);\n", wwo, dwParam1);
884 if (!wwo || wwo->state == WINE_WS_FAILED) {
885 WARN("Stream instance invalid.\n");
886 return MMSYSERR_INVALHANDLE;
889 value1 = (double)LOWORD(dwParam1)/(double)0xFFFFl;
890 value2 = (double)HIWORD(dwParam1)/(double)0xFFFFl;
892 if (wwo->sample_spec.channels == 2) {
893 wwo->volume.channels = 2;
894 wwo->volume.values[0] = pa_sw_volume_from_linear(value1);
895 wwo->volume.values[1] = pa_sw_volume_from_linear(value2);
896 } else {
897 if (value1 != value2) FIXME("Non-stereo streams can't pan!\n");
898 wwo->volume.channels = wwo->sample_spec.channels;
899 pa_cvolume_set(&wwo->volume, wwo->volume.channels, pa_sw_volume_from_linear(value1 > value2 ? value1 : value2));
902 if (TRACE_ON(wave)) {
903 char s[PA_CVOLUME_SNPRINT_MAX];
904 pa_cvolume_snprint(s, PA_CVOLUME_SNPRINT_MAX, &wwo->volume);
905 TRACE("%s\n", s);
908 pa_threaded_mainloop_lock(PULSE_ml);
909 if (!wwo->stream || !PULSE_context || pa_context_get_state(PULSE_context) != PA_CONTEXT_READY ||
910 pa_stream_get_state(wwo->stream) != PA_STREAM_READY || !pa_cvolume_valid(&wwo->volume)) {
911 pa_threaded_mainloop_unlock(PULSE_ml);
912 return MMSYSERR_NOERROR;
915 PULSE_WaitForOperation(pa_context_set_sink_input_volume(PULSE_context,
916 pa_stream_get_index(wwo->stream), &wwo->volume,
917 PULSE_ContextSuccessCallback, wwo));
918 pa_threaded_mainloop_unlock(PULSE_ml);
919 return MMSYSERR_NOERROR;
922 /**************************************************************************
923 * wodRestart [internal]
925 static DWORD wodRestart(WINE_WAVEINST *wwo) {
926 if (!wwo || wwo->state == WINE_WS_FAILED) {
927 WARN("Stream instance invalid.\n");
928 return MMSYSERR_INVALHANDLE;
931 if (wwo->state == WINE_WS_PAUSED)
932 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESTARTING, 0, TRUE);
933 return MMSYSERR_NOERROR;
936 /**************************************************************************
937 * wodReset [internal]
939 static DWORD wodReset(WINE_WAVEINST *wwo) {
940 if (!wwo || wwo->state == WINE_WS_FAILED) {
941 WARN("Stream instance invalid.\n");
942 return MMSYSERR_INVALHANDLE;
945 PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESETTING, 0, TRUE);
946 return MMSYSERR_NOERROR;
949 /**************************************************************************
950 * wodDevInterfaceSize [internal]
952 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
954 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1, NULL, 0) * sizeof(WCHAR);
955 return MMSYSERR_NOERROR;
958 /**************************************************************************
959 * wodDevInterface [internal]
961 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) {
962 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
963 NULL, 0 ) * sizeof(WCHAR))
965 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
966 dwParam1, dwParam2 / sizeof(WCHAR));
967 return MMSYSERR_NOERROR;
969 return MMSYSERR_INVALPARAM;
972 DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) {
973 TRACE("(%u, %p)\n", wDevID, desc);
974 *desc = WOutDev[wDevID].ds_desc;
975 return MMSYSERR_NOERROR;
978 /**************************************************************************
979 * wodMessage (WINEPULSE.@)
981 DWORD WINAPI PULSE_wodMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
983 switch (wMsg) {
985 case DRVM_INIT:
986 case DRVM_EXIT:
987 case DRVM_ENABLE:
988 case DRVM_DISABLE:
989 return 0;
991 /* WaveOut Playback related functions */
992 case WODM_OPEN: return wodOpen (wDevID, dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
993 case WODM_CLOSE: return wodClose ((WINE_WAVEINST*)dwUser);
994 case WODM_WRITE: return wodWrite ((WINE_WAVEINST*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
995 case WODM_PAUSE: return wodPause ((WINE_WAVEINST*)dwUser);
996 case WODM_GETPOS: return wodGetPosition ((WINE_WAVEINST*)dwUser, (LPMMTIME)dwParam1, dwParam2);
997 case WODM_BREAKLOOP: return wodBreakLoop ((WINE_WAVEINST*)dwUser);
998 case WODM_RESTART: return wodRestart ((WINE_WAVEINST*)dwUser);
999 case WODM_RESET: return wodReset ((WINE_WAVEINST*)dwUser);
1001 case WODM_GETVOLUME: return wodGetVolume ((WINE_WAVEINST*)dwUser, (LPDWORD)dwParam1);
1002 case WODM_SETVOLUME: return wodSetVolume ((WINE_WAVEINST*)dwUser, dwParam1);
1004 case WODM_PREPARE:
1005 case WODM_UNPREPARE:
1007 case WODM_GETPITCH:
1008 case WODM_SETPITCH:
1010 case WODM_GETPLAYBACKRATE:
1011 case WODM_SETPLAYBACKRATE:
1012 return MMSYSERR_NOTSUPPORTED;
1014 /* Device enumeration, directsound and capabilities */
1015 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
1016 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1017 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1018 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1019 case DRV_QUERYDSOUNDIFACE: return MMSYSERR_NOTSUPPORTED;
1020 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
1022 default:
1023 FIXME("unknown message %d!\n", wMsg);
1025 return MMSYSERR_NOTSUPPORTED;
1028 #else /* !HAVE_PULSEAUDIO */
1030 /**************************************************************************
1031 * wodMessage (WINEPULSE.@)
1033 DWORD WINAPI PULSE_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1034 DWORD dwParam1, DWORD dwParam2) {
1035 FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser,
1036 dwParam1, dwParam2);
1037 return MMSYSERR_NOTENABLED;
1040 #endif /* HAVE_PULSEAUDIO */