Added aRts driver.
[wine.git] / dlls / winmm / winearts / audio.c
blob80ba610296a32cd78ed4ba56085e0ced3db67a63
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Wine Driver for aRts Sound Server
4 * http://www.arts-project.org
6 * Copyright 1994 Martin Ayotte
7 * 1999 Eric Pouech (async playing in waveOut/waveIn)
8 * 2000 Eric Pouech (loops in waveOut)
9 * 2002 Chris Morgan (aRts version of this file)
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * FIXME:
27 * pause in waveOut does not work correctly in loop mode
29 * TODO:
30 * implement wave-in support with artsc
31 * only one sound card is currently supported, add more
34 /*#define EMULATE_SB16*/
36 #include "config.h"
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include "windef.h"
45 #include "wingdi.h"
46 #include "winerror.h"
47 #include "wine/winuser16.h"
48 #include "mmddk.h"
49 #include "dsound.h"
50 #include "dsdriver.h"
51 #include "arts.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(wave);
56 /* Allow 1% deviation for sample rates (some ES137x cards) */
57 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
59 #ifdef HAVE_ARTS
61 #include <artsc.h>
63 #define BUFFER_SIZE 16 * 1024
64 #define SPACE_THRESHOLD 5 * 1024
66 #define MAX_WAVEOUTDRV (1)
68 /* state diagram for waveOut writing:
70 * +---------+-------------+---------------+---------------------------------+
71 * | state | function | event | new state |
72 * +---------+-------------+---------------+---------------------------------+
73 * | | open() | | STOPPED |
74 * | PAUSED | write() | | PAUSED |
75 * | STOPPED | write() | <thrd create> | PLAYING |
76 * | PLAYING | write() | HEADER | PLAYING |
77 * | (other) | write() | <error> | |
78 * | (any) | pause() | PAUSING | PAUSED |
79 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
80 * | (any) | reset() | RESETTING | STOPPED |
81 * | (any) | close() | CLOSING | CLOSED |
82 * +---------+-------------+---------------+---------------------------------+
85 /* states of the playing device */
86 #define WINE_WS_PLAYING 0
87 #define WINE_WS_PAUSED 1
88 #define WINE_WS_STOPPED 2
89 #define WINE_WS_CLOSED 3
91 /* events to be send to device */
92 enum win_wm_message {
93 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
94 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
97 typedef struct {
98 enum win_wm_message msg; /* message identifier */
99 DWORD param; /* parameter for this message */
100 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
101 } RING_MSG;
103 /* implement an in-process message ring for better performance
104 * (compared to passing thru the server)
105 * this ring will be used by the input (resp output) record (resp playback) routine
107 typedef struct {
108 #define ARTS_RING_BUFFER_SIZE 30
109 RING_MSG messages[ARTS_RING_BUFFER_SIZE];
110 int msg_tosave;
111 int msg_toget;
112 HANDLE msg_event;
113 CRITICAL_SECTION msg_crst;
114 } ARTS_MSG_RING;
116 typedef struct {
117 volatile int state; /* one of the WINE_WS_ manifest constants */
118 WAVEOPENDESC waveDesc;
119 WORD wFlags;
120 PCMWAVEFORMAT format;
121 WAVEOUTCAPSA caps;
123 /* arts information */
124 arts_stream_t play_stream; /* the stream structure we get from arts when opening a stream for playing */
125 DWORD dwBufferSize; /* size of whole buffer in bytes */
127 char* sound_buffer;
128 long buffer_size;
131 DWORD volume_left; /* volume control information */
132 DWORD volume_right;
134 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
135 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
136 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
138 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
139 DWORD dwLoops; /* private copy of loop counter */
141 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
142 DWORD dwWrittenTotal; /* number of bytes written to the audio device since opening */
144 /* synchronization stuff */
145 HANDLE hStartUpEvent;
146 HANDLE hThread;
147 DWORD dwThreadID;
148 ARTS_MSG_RING msgRing;
149 } WINE_WAVEOUT;
151 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
153 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
155 /* These strings used only for tracing */
156 static const char *wodPlayerCmdString[] = {
157 "WINE_WM_PAUSING",
158 "WINE_WM_RESTARTING",
159 "WINE_WM_RESETTING",
160 "WINE_WM_HEADER",
161 "WINE_WM_UPDATE",
162 "WINE_WM_BREAKLOOP",
163 "WINE_WM_CLOSING",
166 /*======================================================================*
167 * Low level WAVE implementation *
168 *======================================================================*/
170 /* Volume functions derived from Alsaplayer source */
171 /* length is the number of 16 bit samples */
172 void volume_effect16(void *bufin, void* bufout, int length, int left,
173 int right, int nChannels)
175 short *d_out = (short *)bufout;
176 short *d_in = (short *)bufin;
177 int i, v;
180 TRACE("length == %d, nChannels == %d\n", length, nChannels);
183 if (right == -1) right = left;
185 for(i = 0; i < length; i+=(nChannels))
187 v = (int) ((*(d_in++) * left) / 100);
188 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
189 if(nChannels == 2)
191 v = (int) ((*(d_in++) * right) / 100);
192 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
197 /* length is the number of 8 bit samples */
198 void volume_effect8(void *bufin, void* bufout, int length, int left,
199 int right, int nChannels)
201 char *d_out = (char *)bufout;
202 char *d_in = (char *)bufin;
203 int i, v;
206 TRACE("length == %d, nChannels == %d\n", length, nChannels);
209 if (right == -1) right = left;
211 for(i = 0; i < length; i+=(nChannels))
213 v = (char) ((*(d_in++) * left) / 100);
214 *(d_out++) = (v>127) ? 127 : ((v<-128) ? -128 : v);
215 if(nChannels == 2)
217 v = (char) ((*(d_in++) * right) / 100);
218 *(d_out++) = (v>127) ? 127 : ((v<-128) ? -128 : v);
223 /******************************************************************
224 * ARTS_OpenDevice
226 static int ARTS_OpenDevice(void)
228 return arts_init(); /* initialize arts and return errorcode */
231 /******************************************************************
232 * ARTS_CloseDevice
235 LONG ARTS_CloseDevice(void)
237 arts_free(); /* free up arts */
238 return 1;
241 /******************************************************************
242 * ARTS_WaveInit
244 * Initialize internal structures from ARTS server info
246 LONG ARTS_WaveInit(void)
248 int i;
249 int errorcode;
251 TRACE("called\n");
253 /* start with output device */
255 /* initialize all device handles to -1 */
256 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
257 WOutDev[i].play_stream = (arts_stream_t*)-1;
259 /* FIXME: only one device is supported */
260 memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
262 if ((errorcode = ARTS_OpenDevice()) < 0)
264 ERR("arts_init() failed (%d)\n", errorcode);
265 return -1;
268 /* FIXME: some programs compare this string against the content of the registry
269 * for MM drivers. The names have to match in order for the program to work
270 * (e.g. MS win9x mplayer.exe)
272 #ifdef EMULATE_SB16
273 WOutDev[0].caps.wMid = 0x0002;
274 WOutDev[0].caps.wPid = 0x0104;
275 strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
276 #else
277 WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */
278 WOutDev[0].caps.wPid = 0x0001; /* Product ID */
279 /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
280 strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");
281 #endif
282 WOutDev[0].caps.vDriverVersion = 0x0100;
283 WOutDev[0].caps.dwFormats = 0x00000000;
284 WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
286 WOutDev[0].caps.wChannels = 2;
287 WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
289 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
290 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
291 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
292 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
293 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
294 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
295 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
296 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
297 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
298 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
299 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
300 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
302 return 0;
305 /******************************************************************
306 * ARTS_InitRingMessage
308 * Initialize the ring of messages for passing between driver's caller and playback/record
309 * thread
311 static int ARTS_InitRingMessage(ARTS_MSG_RING* mr)
313 mr->msg_toget = 0;
314 mr->msg_tosave = 0;
315 mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
316 memset(mr->messages, 0, sizeof(RING_MSG) * ARTS_RING_BUFFER_SIZE);
317 InitializeCriticalSection(&mr->msg_crst);
318 return 0;
321 /******************************************************************
322 * ARTS_DestroyRingMessage
325 static int ARTS_DestroyRingMessage(ARTS_MSG_RING* mr)
327 CloseHandle(mr->msg_event);
328 DeleteCriticalSection(&mr->msg_crst);
329 return 0;
332 /******************************************************************
333 * ARTS_AddRingMessage
335 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
337 static int ARTS_AddRingMessage(ARTS_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
339 HANDLE hEvent = INVALID_HANDLE_VALUE;
341 EnterCriticalSection(&mr->msg_crst);
342 if ((mr->msg_toget == ((mr->msg_tosave + 1) % ARTS_RING_BUFFER_SIZE))) /* buffer overflow? */
344 ERR("buffer overflow !?\n");
345 LeaveCriticalSection(&mr->msg_crst);
346 return 0;
348 if (wait)
350 hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
351 if (hEvent == INVALID_HANDLE_VALUE)
353 ERR("can't create event !?\n");
354 LeaveCriticalSection(&mr->msg_crst);
355 return 0;
357 if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
358 FIXME("two fast messages in the queue!!!!\n");
360 /* fast messages have to be added at the start of the queue */
361 mr->msg_toget = (mr->msg_toget + ARTS_RING_BUFFER_SIZE - 1) % ARTS_RING_BUFFER_SIZE;
363 mr->messages[mr->msg_toget].msg = msg;
364 mr->messages[mr->msg_toget].param = param;
365 mr->messages[mr->msg_toget].hEvent = hEvent;
367 else
369 mr->messages[mr->msg_tosave].msg = msg;
370 mr->messages[mr->msg_tosave].param = param;
371 mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
372 mr->msg_tosave = (mr->msg_tosave + 1) % ARTS_RING_BUFFER_SIZE;
375 LeaveCriticalSection(&mr->msg_crst);
377 SetEvent(mr->msg_event); /* signal a new message */
379 if (wait)
381 /* wait for playback/record thread to have processed the message */
382 WaitForSingleObject(hEvent, INFINITE);
383 CloseHandle(hEvent);
386 return 1;
389 /******************************************************************
390 * ARTS_RetrieveRingMessage
392 * Get a message from the ring. Should be called by the playback/record thread.
394 static int ARTS_RetrieveRingMessage(ARTS_MSG_RING* mr,
395 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
397 EnterCriticalSection(&mr->msg_crst);
399 if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
401 LeaveCriticalSection(&mr->msg_crst);
402 return 0;
405 *msg = mr->messages[mr->msg_toget].msg;
406 mr->messages[mr->msg_toget].msg = 0;
407 *param = mr->messages[mr->msg_toget].param;
408 *hEvent = mr->messages[mr->msg_toget].hEvent;
409 mr->msg_toget = (mr->msg_toget + 1) % ARTS_RING_BUFFER_SIZE;
410 LeaveCriticalSection(&mr->msg_crst);
411 return 1;
414 /*======================================================================*
415 * Low level WAVE OUT implementation *
416 *======================================================================*/
418 /**************************************************************************
419 * wodNotifyClient [internal]
421 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
423 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
425 switch (wMsg) {
426 case WOM_OPEN:
427 case WOM_CLOSE:
428 case WOM_DONE:
429 if (wwo->wFlags != DCB_NULL &&
430 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave,
431 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
432 WARN("can't notify client !\n");
433 return MMSYSERR_ERROR;
435 break;
436 default:
437 FIXME("Unknown callback message %u\n", wMsg);
438 return MMSYSERR_INVALPARAM;
440 return MMSYSERR_NOERROR;
443 /**************************************************************************
444 * wodUpdatePlayedTotal [internal]
447 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
449 /* total played is the bytes written less the bytes to write ;-) */
450 wwo->dwPlayedTotal = wwo->dwWrittenTotal -
451 (wwo->dwBufferSize -
452 arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE));
453 return TRUE;
456 /**************************************************************************
457 * wodPlayer_BeginWaveHdr [internal]
459 * Makes the specified lpWaveHdr the currently playing wave header.
460 * If the specified wave header is a begin loop and we're not already in
461 * a loop, setup the loop.
463 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
465 wwo->lpPlayPtr = lpWaveHdr;
467 if (!lpWaveHdr) return;
469 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
470 if (wwo->lpLoopPtr) {
471 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
472 TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
473 } else {
474 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
475 wwo->lpLoopPtr = lpWaveHdr;
476 /* Windows does not touch WAVEHDR.dwLoops,
477 * so we need to make an internal copy */
478 wwo->dwLoops = lpWaveHdr->dwLoops;
481 wwo->dwPartialOffset = 0;
484 /**************************************************************************
485 * wodPlayer_PlayPtrNext [internal]
487 * Advance the play pointer to the next waveheader, looping if required.
489 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
491 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
493 wwo->dwPartialOffset = 0;
494 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
495 /* We're at the end of a loop, loop if required */
496 if (--wwo->dwLoops > 0) {
497 wwo->lpPlayPtr = wwo->lpLoopPtr;
498 } else {
499 /* Handle overlapping loops correctly */
500 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
501 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
502 /* shall we consider the END flag for the closing loop or for
503 * the opening one or for both ???
504 * code assumes for closing loop only
506 } else {
507 lpWaveHdr = lpWaveHdr->lpNext;
509 wwo->lpLoopPtr = NULL;
510 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
512 } else {
513 /* We're not in a loop. Advance to the next wave header */
514 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
517 return lpWaveHdr;
520 /**************************************************************************
521 * wodPlayer_DSPWait [internal]
522 * Returns the number of milliseconds to wait for the DSP buffer to clear.
523 * This is based on the number of fragments we want to be clear before
524 * writing and the number of free fragments we already have.
526 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
528 int waitvalue = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream,
529 ARTS_P_BUFFER_SPACE)) / ((wwo->format.wf.nSamplesPerSec *
530 wwo->format.wBitsPerSample * wwo->format.wf.nChannels)
531 /1000);
533 /* FIXME: this is a hack since the waitvalue we calculate is too
534 large for some reason.... */
535 waitvalue/=4;
537 TRACE("wait value of %d\n", waitvalue);
539 /* return the time left to play the buffer */
540 return waitvalue;
543 /**************************************************************************
544 * wodPlayer_NotifyWait [internal]
545 * Returns the number of milliseconds to wait before attempting to notify
546 * completion of the specified wavehdr.
547 * This is based on the number of bytes remaining to be written in the
548 * wave.
550 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
552 DWORD dwMillis;
554 if(lpWaveHdr->reserved < wwo->dwPlayedTotal)
556 dwMillis = 1;
558 else
560 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
561 if(!dwMillis) dwMillis = 1;
564 TRACE("dwMillis = %ld\n", dwMillis);
566 return dwMillis;
570 /**************************************************************************
571 * wodPlayer_WriteMaxFrags [internal]
572 * Writes the maximum number of bytes possible to the DSP and returns
573 * the number of bytes written.
575 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
577 /* Only attempt to write to free bytes */
578 DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
579 int toWrite = min(dwLength, *bytes);
580 int written;
582 TRACE("Writing wavehdr %p.%lu[%lu]\n",
583 wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
585 /* see if our buffer isn't large enough for the data we are writing */
586 if(wwo->buffer_size < toWrite)
588 if(wwo->sound_buffer)
589 HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
592 /* if we don't have a buffer then get one */
593 if(!wwo->sound_buffer)
595 /* allocate some memory for the buffer */
596 wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);
597 wwo->buffer_size = toWrite;
600 /* if we don't have a buffer then error out */
601 if(!wwo->sound_buffer)
603 ERR("error allocating sound_buffer memory\n");
604 return 0;
607 TRACE("toWrite == %d\n", toWrite);
609 /* apply volume to the bits */
610 /* for single channel audio streams we only use the LEFT volume */
611 if(wwo->format.wBitsPerSample == 16)
613 /* apply volume to the buffer we are about to send */
614 /* divide toWrite(bytes) by 2 as volume processes by 16 bits */
615 volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
616 wwo->sound_buffer, toWrite>>1, wwo->volume_left,
617 wwo->volume_right, wwo->format.wf.nChannels);
618 } else if(wwo->format.wBitsPerSample == 8)
620 /* apply volume to the buffer we are about to send */
621 volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
622 wwo->sound_buffer, toWrite>>1, wwo->volume_left,
623 wwo->volume_right, wwo->format.wf.nChannels);
624 } else
626 FIXME("unsupported wwo->format.wBitsPerSample of %d\n",
627 wwo->format.wBitsPerSample);
630 /* send the audio data to arts for playing */
631 written = arts_write(wwo->play_stream, wwo->sound_buffer, toWrite);
633 TRACE("written = %d\n", written);
635 if (written <= 0) return written; /* if we wrote nothing just return */
637 if (written >= dwLength)
638 wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
639 else
640 wwo->dwPartialOffset += written; /* Remove the amount written */
642 *bytes -= written;
643 wwo->dwWrittenTotal += written; /* update stats on this wave device */
645 return written; /* return the number of bytes written */
649 /**************************************************************************
650 * wodPlayer_NotifyCompletions [internal]
652 * Notifies and remove from queue all wavehdrs which have been played to
653 * the speaker (ie. they have cleared the audio device). If force is true,
654 * we notify all wavehdrs and remove them all from the queue even if they
655 * are unplayed or part of a loop.
657 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
659 LPWAVEHDR lpWaveHdr;
661 /* Start from lpQueuePtr and keep notifying until:
662 * - we hit an unwritten wavehdr
663 * - we hit the beginning of a running loop
664 * - we hit a wavehdr which hasn't finished playing
666 while ((lpWaveHdr = wwo->lpQueuePtr) &&
667 (force ||
668 (lpWaveHdr != wwo->lpPlayPtr &&
669 lpWaveHdr != wwo->lpLoopPtr &&
670 lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
672 wwo->lpQueuePtr = lpWaveHdr->lpNext;
674 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
675 lpWaveHdr->dwFlags |= WHDR_DONE;
677 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
679 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
680 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
683 /**************************************************************************
684 * wodPlayer_Reset [internal]
686 * wodPlayer helper. Resets current output stream.
688 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
690 wodUpdatePlayedTotal(wwo);
692 /* updates current notify list */
693 wodPlayer_NotifyCompletions(wwo, FALSE);
695 /* flush all possible output */
696 /* close and open the stream to end the playback and prepare for
697 new playback */
698 arts_close_stream(wwo->play_stream);
699 wwo->play_stream = arts_play_stream(wwo->format.wf.nSamplesPerSec,
700 wwo->format.wBitsPerSample, wwo->format.wf.nChannels, "winearts");
702 if (reset) {
703 enum win_wm_message msg;
704 DWORD param;
705 HANDLE ev;
707 /* remove any buffer */
708 wodPlayer_NotifyCompletions(wwo, TRUE);
710 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
711 wwo->state = WINE_WS_STOPPED;
712 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
714 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
716 /* remove any existing message in the ring */
717 EnterCriticalSection(&wwo->msgRing.msg_crst);
719 /* return all pending headers in queue */
720 while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
722 TRACE("flushing msg\n");
723 if (msg != WINE_WM_HEADER)
725 FIXME("shouldn't have headers left\n");
726 SetEvent(ev);
727 continue;
729 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
730 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
732 wodNotifyClient(wwo, WOM_DONE, param, 0);
734 ResetEvent(wwo->msgRing.msg_event);
735 LeaveCriticalSection(&wwo->msgRing.msg_crst);
736 } else {
737 if (wwo->lpLoopPtr) {
738 /* complicated case, not handled yet (could imply modifying the loop counter */
739 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
740 wwo->lpPlayPtr = wwo->lpLoopPtr;
741 wwo->dwPartialOffset = 0;
742 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
743 } else {
744 LPWAVEHDR ptr;
745 DWORD sz = wwo->dwPartialOffset;
747 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
748 /* compute the max size playable from lpQueuePtr */
749 for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
750 sz += ptr->dwBufferLength;
752 /* because the reset lpPlayPtr will be lpQueuePtr */
753 if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
754 wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
755 wwo->dwWrittenTotal = wwo->dwPlayedTotal;
756 wwo->lpPlayPtr = wwo->lpQueuePtr;
758 wwo->state = WINE_WS_PAUSED;
762 /**************************************************************************
763 * wodPlayer_ProcessMessages [internal]
765 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
767 LPWAVEHDR lpWaveHdr;
768 enum win_wm_message msg;
769 DWORD param;
770 HANDLE ev;
772 while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
773 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
774 switch (msg) {
775 case WINE_WM_PAUSING:
776 wodPlayer_Reset(wwo, FALSE);
777 SetEvent(ev);
778 break;
779 case WINE_WM_RESTARTING:
780 wwo->state = WINE_WS_PLAYING;
781 SetEvent(ev);
782 break;
783 case WINE_WM_HEADER:
784 lpWaveHdr = (LPWAVEHDR)param;
786 /* insert buffer at the end of queue */
788 LPWAVEHDR* wh;
789 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
790 *wh = lpWaveHdr;
792 if (!wwo->lpPlayPtr)
793 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
794 if (wwo->state == WINE_WS_STOPPED)
795 wwo->state = WINE_WS_PLAYING;
796 break;
797 case WINE_WM_RESETTING:
798 wodPlayer_Reset(wwo, TRUE);
799 SetEvent(ev);
800 break;
801 case WINE_WM_UPDATE:
802 wodUpdatePlayedTotal(wwo);
803 SetEvent(ev);
804 break;
805 case WINE_WM_BREAKLOOP:
806 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
807 /* ensure exit at end of current loop */
808 wwo->dwLoops = 1;
810 SetEvent(ev);
811 break;
812 case WINE_WM_CLOSING:
813 /* sanity check: this should not happen since the device must have been reset before */
814 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
815 wwo->hThread = 0;
816 wwo->state = WINE_WS_CLOSED;
817 SetEvent(ev);
818 ExitThread(0);
819 /* shouldn't go here */
820 default:
821 FIXME("unknown message %d\n", msg);
822 break;
827 /**************************************************************************
828 * wodPlayer_FeedDSP [internal]
829 * Feed as much sound data as we can into the DSP and return the number of
830 * milliseconds before it will be necessary to feed the DSP again.
832 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
834 DWORD availInQ;
836 wodUpdatePlayedTotal(wwo);
837 availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);
838 TRACE("availInQ = %ld\n", availInQ);
840 /* input queue empty and output buffer with no space */
841 if (!wwo->lpPlayPtr && availInQ) {
842 TRACE("Run out of wavehdr:s... flushing\n");
843 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
844 return INFINITE;
847 /* no more room... no need to try to feed */
848 if(!availInQ)
850 TRACE("no more room, no need to try to feed\n");
851 return wodPlayer_DSPWait(wwo);
854 /* Feed from partial wavehdr */
855 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
857 TRACE("feeding from partial wavehdr\n");
858 wodPlayer_WriteMaxFrags(wwo, &availInQ);
861 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
862 if (!wwo->dwPartialOffset)
864 while(wwo->lpPlayPtr && availInQ > SPACE_THRESHOLD)
866 TRACE("feeding waveheaders until we run out of space\n");
867 /* note the value that dwPlayedTotal will return when this wave finishes playing */
868 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
869 wodPlayer_WriteMaxFrags(wwo, &availInQ);
873 return wodPlayer_DSPWait(wwo);
877 /**************************************************************************
878 * wodPlayer [internal]
880 static DWORD CALLBACK wodPlayer(LPVOID pmt)
882 WORD uDevID = (DWORD)pmt;
883 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
884 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
885 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
886 DWORD dwSleepTime;
888 wwo->state = WINE_WS_STOPPED;
889 SetEvent(wwo->hStartUpEvent);
891 for (;;) {
892 /** Wait for the shortest time before an action is required. If there
893 * are no pending actions, wait forever for a command.
895 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
896 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
897 WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
898 wodPlayer_ProcessMessages(wwo);
899 if (wwo->state == WINE_WS_PLAYING) {
900 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
901 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
902 } else {
903 dwNextFeedTime = dwNextNotifyTime = INFINITE;
908 /**************************************************************************
909 * wodGetDevCaps [internal]
911 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
913 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
915 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
917 if (wDevID >= MAX_WAVEOUTDRV) {
918 TRACE("MAX_WAVOUTDRV reached !\n");
919 return MMSYSERR_BADDEVICEID;
922 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
923 return MMSYSERR_NOERROR;
926 /**************************************************************************
927 * wodOpen [internal]
929 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
931 WINE_WAVEOUT* wwo;
933 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
934 if (lpDesc == NULL) {
935 WARN("Invalid Parameter !\n");
936 return MMSYSERR_INVALPARAM;
938 if (wDevID >= MAX_WAVEOUTDRV) {
939 TRACE("MAX_WAVOUTDRV reached !\n");
940 return MMSYSERR_BADDEVICEID;
943 /* only PCM format is supported so far... */
944 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
945 lpDesc->lpFormat->nChannels == 0 ||
946 lpDesc->lpFormat->nSamplesPerSec == 0) {
947 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
948 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
949 lpDesc->lpFormat->nSamplesPerSec);
950 return WAVERR_BADFORMAT;
953 if (dwFlags & WAVE_FORMAT_QUERY) {
954 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
955 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
956 lpDesc->lpFormat->nSamplesPerSec);
957 return MMSYSERR_NOERROR;
960 wwo = &WOutDev[wDevID];
962 /* direct sound not supported, ignore the flag */
963 dwFlags &= ~WAVE_DIRECTSOUND;
965 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
967 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
968 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
970 if (wwo->format.wBitsPerSample == 0) {
971 WARN("Resetting zeroed wBitsPerSample\n");
972 wwo->format.wBitsPerSample = 8 *
973 (wwo->format.wf.nAvgBytesPerSec /
974 wwo->format.wf.nSamplesPerSec) /
975 wwo->format.wf.nChannels;
978 wwo->play_stream = arts_play_stream(wwo->format.wf.nSamplesPerSec,
979 wwo->format.wBitsPerSample, wwo->format.wf.nChannels, "winearts");
981 /* clear these so we don't have any confusion ;-) */
982 wwo->sound_buffer = 0;
983 wwo->buffer_size = 0;
985 arts_stream_set(wwo->play_stream, ARTS_P_BLOCKING, 0); /* disable blocking on this stream */
987 if(!wwo->play_stream) return MMSYSERR_ALLOCATED;
989 /* Try to set buffer size from constant and store the value that it
990 was set to for future use */
991 wwo->dwBufferSize = arts_stream_set(wwo->play_stream,
992 ARTS_P_BUFFER_SIZE, BUFFER_SIZE);
993 TRACE("Tried to set BUFFER_SIZE of %d, wwo->dwBufferSize is actually
994 %ld\n", BUFFER_SIZE, wwo->dwBufferSize);
995 wwo->dwPlayedTotal = 0;
996 wwo->dwWrittenTotal = 0;
998 ARTS_InitRingMessage(&wwo->msgRing);
1000 /* create player thread */
1001 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1002 wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1003 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1004 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1005 CloseHandle(wwo->hStartUpEvent);
1006 } else {
1007 wwo->hThread = INVALID_HANDLE_VALUE;
1008 wwo->dwThreadID = 0;
1010 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1012 TRACE("stream=0x%lx, dwBufferSize=%ld\n",
1013 (long)wwo->play_stream, wwo->dwBufferSize);
1015 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1016 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1017 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1018 wwo->format.wf.nBlockAlign);
1020 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1023 /**************************************************************************
1024 * wodClose [internal]
1026 static DWORD wodClose(WORD wDevID)
1028 DWORD ret = MMSYSERR_NOERROR;
1029 WINE_WAVEOUT* wwo;
1031 TRACE("(%u);\n", wDevID);
1033 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1034 (arts_stream_t*)-1)
1036 WARN("bad device ID !\n");
1037 return MMSYSERR_BADDEVICEID;
1040 wwo = &WOutDev[wDevID];
1041 if (wwo->lpQueuePtr) {
1042 WARN("buffers still playing !\n");
1043 ret = WAVERR_STILLPLAYING;
1044 } else {
1045 TRACE("imhere[3-close]\n");
1046 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1047 ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1050 ARTS_DestroyRingMessage(&wwo->msgRing);
1052 arts_close_stream(wwo->play_stream); /* close the arts stream */
1054 /* free up the buffer we use for volume and reset the size */
1055 if(wwo->sound_buffer)
1056 HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
1057 wwo->buffer_size = 0;
1059 wwo->play_stream = (arts_stream_t*)-1;
1060 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1062 return ret;
1065 /**************************************************************************
1066 * wodWrite [internal]
1069 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1071 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1073 /* first, do the sanity checks... */
1074 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1075 (arts_stream_t*)-1)
1077 WARN("bad dev ID !\n");
1078 return MMSYSERR_BADDEVICEID;
1081 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1083 TRACE("unprepared\n");
1084 return WAVERR_UNPREPARED;
1087 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1089 TRACE("still playing\n");
1090 return WAVERR_STILLPLAYING;
1093 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1094 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1095 lpWaveHdr->lpNext = 0;
1097 TRACE("adding ring message\n");
1098 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1100 return MMSYSERR_NOERROR;
1103 /**************************************************************************
1104 * wodPrepare [internal]
1106 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1108 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1110 if (wDevID >= MAX_WAVEOUTDRV) {
1111 WARN("bad device ID !\n");
1112 return MMSYSERR_BADDEVICEID;
1115 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1116 return WAVERR_STILLPLAYING;
1118 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1119 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1120 return MMSYSERR_NOERROR;
1123 /**************************************************************************
1124 * wodUnprepare [internal]
1126 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1128 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1130 if (wDevID >= MAX_WAVEOUTDRV) {
1131 WARN("bad device ID !\n");
1132 return MMSYSERR_BADDEVICEID;
1135 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1136 return WAVERR_STILLPLAYING;
1138 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1139 lpWaveHdr->dwFlags |= WHDR_DONE;
1141 return MMSYSERR_NOERROR;
1144 /**************************************************************************
1145 * wodPause [internal]
1147 static DWORD wodPause(WORD wDevID)
1149 TRACE("(%u);!\n", wDevID);
1151 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1152 (arts_stream_t*)-1)
1154 WARN("bad device ID !\n");
1155 return MMSYSERR_BADDEVICEID;
1158 TRACE("imhere[3-PAUSING]\n");
1159 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1161 return MMSYSERR_NOERROR;
1164 /**************************************************************************
1165 * wodRestart [internal]
1167 static DWORD wodRestart(WORD wDevID)
1169 TRACE("(%u);\n", wDevID);
1171 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1172 (arts_stream_t*)-1)
1174 WARN("bad device ID !\n");
1175 return MMSYSERR_BADDEVICEID;
1178 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1179 TRACE("imhere[3-RESTARTING]\n");
1180 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1183 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1184 /* FIXME: Myst crashes with this ... hmm -MM
1185 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1188 return MMSYSERR_NOERROR;
1191 /**************************************************************************
1192 * wodReset [internal]
1194 static DWORD wodReset(WORD wDevID)
1196 TRACE("(%u);\n", wDevID);
1198 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1199 (arts_stream_t*)-1)
1201 WARN("bad device ID !\n");
1202 return MMSYSERR_BADDEVICEID;
1205 TRACE("imhere[3-RESET]\n");
1206 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1208 return MMSYSERR_NOERROR;
1211 /**************************************************************************
1212 * wodGetPosition [internal]
1214 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1216 int time;
1217 DWORD val;
1218 WINE_WAVEOUT* wwo;
1220 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1222 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1223 (arts_stream_t*)-1)
1225 WARN("bad device ID !\n");
1226 return MMSYSERR_BADDEVICEID;
1229 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1231 wwo = &WOutDev[wDevID];
1232 ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1233 val = wwo->dwPlayedTotal;
1235 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1236 lpTime->wType, wwo->format.wBitsPerSample,
1237 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1238 wwo->format.wf.nAvgBytesPerSec);
1239 TRACE("dwPlayedTotal=%lu\n", val);
1241 switch (lpTime->wType) {
1242 case TIME_BYTES:
1243 lpTime->u.cb = val;
1244 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1245 break;
1246 case TIME_SAMPLES:
1247 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
1248 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1249 break;
1250 case TIME_SMPTE:
1251 time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1252 lpTime->u.smpte.hour = time / 108000;
1253 time -= lpTime->u.smpte.hour * 108000;
1254 lpTime->u.smpte.min = time / 1800;
1255 time -= lpTime->u.smpte.min * 1800;
1256 lpTime->u.smpte.sec = time / 30;
1257 time -= lpTime->u.smpte.sec * 30;
1258 lpTime->u.smpte.frame = time;
1259 lpTime->u.smpte.fps = 30;
1260 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1261 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1262 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1263 break;
1264 default:
1265 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1266 lpTime->wType = TIME_MS;
1267 case TIME_MS:
1268 lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1269 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1270 break;
1272 return MMSYSERR_NOERROR;
1275 /**************************************************************************
1276 * wodBreakLoop [internal]
1278 static DWORD wodBreakLoop(WORD wDevID)
1280 TRACE("(%u);\n", wDevID);
1282 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1283 (arts_stream_t*)-1)
1285 WARN("bad device ID !\n");
1286 return MMSYSERR_BADDEVICEID;
1288 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1289 return MMSYSERR_NOERROR;
1292 /**************************************************************************
1293 * wodGetVolume [internal]
1295 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1297 DWORD left, right;
1299 left = WOutDev[wDevID].volume_left;
1300 right = WOutDev[wDevID].volume_right;
1302 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1304 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) <<
1305 16);
1307 return MMSYSERR_NOERROR;
1310 /**************************************************************************
1311 * wodSetVolume [internal]
1313 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1315 DWORD left, right;
1317 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1318 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1320 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1322 WOutDev[wDevID].volume_left = left;
1323 WOutDev[wDevID].volume_right = right;
1325 return MMSYSERR_NOERROR;
1328 /**************************************************************************
1329 * wodGetNumDevs [internal]
1331 static DWORD wodGetNumDevs(void)
1333 DWORD ret = 1;
1335 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1336 return ret;
1339 /**************************************************************************
1340 * wodMessage (WINEARTS.@)
1342 DWORD WINAPI ARTS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1343 DWORD dwParam1, DWORD dwParam2)
1345 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1346 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1348 switch (wMsg) {
1349 case DRVM_INIT:
1350 case DRVM_EXIT:
1351 case DRVM_ENABLE:
1352 case DRVM_DISABLE:
1353 /* FIXME: Pretend this is supported */
1354 return 0;
1355 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1356 case WODM_CLOSE: return wodClose (wDevID);
1357 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1358 case WODM_PAUSE: return wodPause (wDevID);
1359 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1360 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
1361 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1362 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1363 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1364 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1365 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1366 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1367 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1368 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1369 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1370 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1371 case WODM_RESTART: return wodRestart (wDevID);
1372 case WODM_RESET: return wodReset (wDevID);
1374 case DRV_QUERYDSOUNDIFACE: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1375 default:
1376 FIXME("unknown message %d!\n", wMsg);
1378 return MMSYSERR_NOTSUPPORTED;
1381 /*======================================================================*
1382 * Low level DSOUND implementation *
1383 *======================================================================*/
1385 typedef struct IDsDriverImpl IDsDriverImpl;
1386 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1388 struct IDsDriverImpl
1390 /* IUnknown fields */
1391 ICOM_VFIELD(IDsDriver);
1392 DWORD ref;
1393 /* IDsDriverImpl fields */
1394 UINT wDevID;
1395 IDsDriverBufferImpl*primary;
1398 struct IDsDriverBufferImpl
1400 /* IUnknown fields */
1401 ICOM_VFIELD(IDsDriverBuffer);
1402 DWORD ref;
1403 /* IDsDriverBufferImpl fields */
1404 IDsDriverImpl* drv;
1405 DWORD buflen;
1408 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1410 /* we can't perform memory mapping as we don't have a file stream
1411 interface with arts like we do with oss */
1412 MESSAGE("This sound card's driver does not support direct access\n");
1413 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1414 return MMSYSERR_NOTSUPPORTED;
1417 #else /* !HAVE_ARTS */
1419 /**************************************************************************
1420 * wodMessage (WINEARTS.@)
1422 DWORD WINAPI ARTS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1423 DWORD dwParam1, DWORD dwParam2)
1425 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1426 return MMSYSERR_NOTENABLED;
1429 #endif /* HAVE_ARTS */