2 * Wine Driver for EsounD Sound Server
3 * http://www.tux.org/~ricdude/EsounD.html
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech (async playing in waveOut/waveIn)
7 * 2000 Eric Pouech (loops in waveOut)
8 * 2004 Zhangrong Huang (EsounD version of this file)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * with esd we cannot stop the audio that is already in
29 * pause in waveOut does not work correctly in loop mode
31 * does something need to be done in for WaveIn DirectSound?
50 #include "wine/winuser16.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(wave);
63 /* define if you want to use esd_monitor_stream instead of
64 * esd_record_stream for waveIn stream
66 /*#define WID_USE_ESDMON*/
68 #define BUFFER_REFILL_THRESHOLD 4
70 #define MAX_WAVEOUTDRV (10)
71 #define MAX_WAVEINDRV (10)
73 /* state diagram for waveOut writing:
75 * +---------+-------------+---------------+---------------------------------+
76 * | state | function | event | new state |
77 * +---------+-------------+---------------+---------------------------------+
78 * | | open() | | STOPPED |
79 * | PAUSED | write() | | PAUSED |
80 * | STOPPED | write() | <thrd create> | PLAYING |
81 * | PLAYING | write() | HEADER | PLAYING |
82 * | (other) | write() | <error> | |
83 * | (any) | pause() | PAUSING | PAUSED |
84 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
85 * | (any) | reset() | RESETTING | STOPPED |
86 * | (any) | close() | CLOSING | CLOSED |
87 * +---------+-------------+---------------+---------------------------------+
90 /* states of the playing device */
91 #define WINE_WS_PLAYING 0
92 #define WINE_WS_PAUSED 1
93 #define WINE_WS_STOPPED 2
94 #define WINE_WS_CLOSED 3
96 /* events to be send to device */
98 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
99 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
103 enum win_wm_message msg; /* message identifier */
104 DWORD param; /* parameter for this message */
105 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
108 /* implement an in-process message ring for better performance
109 * (compared to passing thru the server)
110 * this ring will be used by the input (resp output) record (resp playback) routine
112 #define ESD_RING_BUFFER_INCREMENT 64
115 int ring_buffer_size;
119 CRITICAL_SECTION msg_crst;
123 volatile int state; /* one of the WINE_WS_ manifest constants */
124 WAVEOPENDESC waveDesc;
126 PCMWAVEFORMAT format;
128 char interface_name[32];
130 DWORD dwSleepTime; /* Num of milliseconds to sleep between filling the dsp buffers */
132 /* esd information */
133 int esd_fd; /* the socket fd we get from esd when opening a stream for playing */
135 DWORD dwBufferSize; /* size of whole buffer in bytes */
140 DWORD volume_left; /* volume control information */
143 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
144 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
145 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
147 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
148 DWORD dwLoops; /* private copy of loop counter */
150 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
151 DWORD dwWrittenTotal; /* number of bytes written to the audio device since opening */
153 /* synchronization stuff */
154 HANDLE hStartUpEvent;
157 ESD_MSG_RING msgRing;
161 volatile int state; /* one of the WINE_WS_ manifest constants */
162 WAVEOPENDESC waveDesc;
164 PCMWAVEFORMAT format;
166 char interface_name[32];
168 /* esd information */
169 int esd_fd; /* the socket fd we get from esd when opening a stream for recording */
172 LPWAVEHDR lpQueuePtr;
173 DWORD dwRecordedTotal;
175 /* synchronization stuff */
176 HANDLE hStartUpEvent;
179 ESD_MSG_RING msgRing;
182 static char* esd_host; /* the esd host */
184 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
185 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
187 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
188 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
190 /* These strings used only for tracing */
191 static const char *wodPlayerCmdString[] = {
193 "WINE_WM_RESTARTING",
203 /*======================================================================*
204 * Low level WAVE implementation *
205 *======================================================================*/
207 /* Volume functions derived from Alsaplayer source */
208 /* length is the number of 16 bit samples */
209 void volume_effect16(void *bufin, void* bufout, int length, int left,
210 int right, int nChannels)
212 short *d_out = (short *)bufout;
213 short *d_in = (short *)bufin;
217 TRACE("length == %d, nChannels == %d\n", length, nChannels);
220 if (right == -1) right = left;
222 for(i = 0; i < length; i+=(nChannels))
224 v = (int) ((*(d_in++) * left) / 100);
225 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
228 v = (int) ((*(d_in++) * right) / 100);
229 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
234 /* length is the number of 8 bit samples */
235 void volume_effect8(void *bufin, void* bufout, int length, int left,
236 int right, int nChannels)
238 BYTE *d_out = (BYTE *)bufout;
239 BYTE *d_in = (BYTE *)bufin;
243 TRACE("length == %d, nChannels == %d\n", length, nChannels);
246 if (right == -1) right = left;
248 for(i = 0; i < length; i+=(nChannels))
250 v = (BYTE) ((*(d_in++) * left) / 100);
251 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
254 v = (BYTE) ((*(d_in++) * right) / 100);
255 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
260 /******************************************************************
261 * ESD_CloseWaveOutDevice
264 void ESD_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
266 esd_close(wwo->esd_fd); /* close the esd socket fd */
269 /* free up the buffer we use for volume and reset the size */
270 if(wwo->sound_buffer)
272 HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
273 wwo->sound_buffer = NULL;
276 wwo->buffer_size = 0;
279 /******************************************************************
280 * ESD_CloseWaveInDevice
283 void ESD_CloseWaveInDevice(WINE_WAVEIN* wwi)
285 esd_close(wwi->esd_fd); /* close the esd socket fd */
289 /******************************************************************
292 LONG ESD_WaveClose(void)
296 /* close all open devices */
297 for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
299 if(WOutDev[iDevice].esd_fd != -1)
301 ESD_CloseWaveOutDevice(&WOutDev[iDevice]);
305 for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
307 if(WInDev[iDevice].esd_fd != -1)
309 ESD_CloseWaveInDevice(&WInDev[iDevice]);
316 /******************************************************************
319 * Initialize internal structures from ESD server info
321 LONG ESD_WaveInit(void)
328 /* FIXME: Maybe usefully to set the esd host. */
331 /* Testing whether the esd host is alive. */
332 if ((fd = esd_open_sound(esd_host)) < 0)
334 WARN("esd_open_sound() failed (%d)\n", errno);
339 /* initialize all device handles to -1 */
340 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
342 static const WCHAR ini[] = {'E','s','o','u','n','D',' ','W','a','v','e','O','u','t','D','r','i','v','e','r',0};
344 WOutDev[i].esd_fd = -1;
345 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out
347 WOutDev[i].caps.wMid = 0x00FF; /* Manufac ID */
348 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
349 lstrcpyW(WOutDev[i].caps.szPname, ini);
350 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "wineesd: %d", i);
352 WOutDev[i].caps.vDriverVersion = 0x0100;
353 WOutDev[i].caps.dwFormats = 0x00000000;
354 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
356 WOutDev[i].caps.wChannels = 2;
357 WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
359 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
360 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
361 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
362 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
363 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
364 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
365 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
366 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
367 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
368 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
369 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
370 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
373 for (i = 0; i < MAX_WAVEINDRV; ++i)
375 static const WCHAR ini[] = {'E','s','o','u','n','D',' ','W','a','v','e','I','n','D','r','i','v','e','r',0};
377 WInDev[i].esd_fd = -1;
378 memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
380 WInDev[i].caps.wMid = 0x00FF;
381 WInDev[i].caps.wPid = 0x0001;
382 lstrcpyW(WInDev[i].caps.szPname, ini);
383 snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "wineesd: %d", i);
385 WInDev[i].caps.vDriverVersion = 0x0100;
386 WInDev[i].caps.dwFormats = 0x00000000;
388 WInDev[i].caps.wChannels = 2;
390 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
391 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
392 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
393 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
394 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
395 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
396 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
397 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
398 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
399 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
400 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
401 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
403 WInDev[i].caps.wReserved1 = 0;
408 /******************************************************************
409 * ESD_InitRingMessage
411 * Initialize the ring of messages for passing between driver's caller and playback/record
414 static int ESD_InitRingMessage(ESD_MSG_RING* mr)
418 mr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
419 mr->ring_buffer_size = ESD_RING_BUFFER_INCREMENT;
420 mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
421 InitializeCriticalSection(&mr->msg_crst);
425 /******************************************************************
426 * ESD_DestroyRingMessage
429 static int ESD_DestroyRingMessage(ESD_MSG_RING* mr)
431 CloseHandle(mr->msg_event);
432 HeapFree(GetProcessHeap(),0,mr->messages);
434 DeleteCriticalSection(&mr->msg_crst);
438 /******************************************************************
441 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
443 static int ESD_AddRingMessage(ESD_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
445 HANDLE hEvent = INVALID_HANDLE_VALUE;
447 EnterCriticalSection(&mr->msg_crst);
448 if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
450 int old_ring_buffer_size = mr->ring_buffer_size;
451 mr->ring_buffer_size += ESD_RING_BUFFER_INCREMENT;
452 TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
453 mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
454 /* Now we need to rearrange the ring buffer so that the new
455 buffers just allocated are in between mr->msg_tosave and
458 if (mr->msg_tosave < mr->msg_toget)
460 memmove(&(mr->messages[mr->msg_toget + ESD_RING_BUFFER_INCREMENT]),
461 &(mr->messages[mr->msg_toget]),
462 sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
464 mr->msg_toget += ESD_RING_BUFFER_INCREMENT;
469 hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
470 if (hEvent == INVALID_HANDLE_VALUE)
472 ERR("can't create event !?\n");
473 LeaveCriticalSection(&mr->msg_crst);
476 if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
477 FIXME("two fast messages in the queue!!!!\n");
479 /* fast messages have to be added at the start of the queue */
480 mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
482 mr->messages[mr->msg_toget].msg = msg;
483 mr->messages[mr->msg_toget].param = param;
484 mr->messages[mr->msg_toget].hEvent = hEvent;
488 mr->messages[mr->msg_tosave].msg = msg;
489 mr->messages[mr->msg_tosave].param = param;
490 mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
491 mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
494 LeaveCriticalSection(&mr->msg_crst);
496 SetEvent(mr->msg_event); /* signal a new message */
500 /* wait for playback/record thread to have processed the message */
501 WaitForSingleObject(hEvent, INFINITE);
508 /******************************************************************
509 * ESD_RetrieveRingMessage
511 * Get a message from the ring. Should be called by the playback/record thread.
513 static int ESD_RetrieveRingMessage(ESD_MSG_RING* mr,
514 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
516 EnterCriticalSection(&mr->msg_crst);
518 if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
520 LeaveCriticalSection(&mr->msg_crst);
524 *msg = mr->messages[mr->msg_toget].msg;
525 mr->messages[mr->msg_toget].msg = 0;
526 *param = mr->messages[mr->msg_toget].param;
527 *hEvent = mr->messages[mr->msg_toget].hEvent;
528 mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
529 LeaveCriticalSection(&mr->msg_crst);
533 /*======================================================================*
534 * Low level WAVE OUT implementation *
535 *======================================================================*/
537 /**************************************************************************
538 * wodNotifyClient [internal]
540 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
542 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
548 if (wwo->wFlags != DCB_NULL &&
549 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
550 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
551 WARN("can't notify client !\n");
552 return MMSYSERR_ERROR;
556 FIXME("Unknown callback message %u\n", wMsg);
557 return MMSYSERR_INVALPARAM;
559 return MMSYSERR_NOERROR;
562 /**************************************************************************
563 * wodUpdatePlayedTotal [internal]
566 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
568 /* total played is the bytes written less the bytes to write ;-) */
569 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
574 /**************************************************************************
575 * wodPlayer_BeginWaveHdr [internal]
577 * Makes the specified lpWaveHdr the currently playing wave header.
578 * If the specified wave header is a begin loop and we're not already in
579 * a loop, setup the loop.
581 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
583 wwo->lpPlayPtr = lpWaveHdr;
585 if (!lpWaveHdr) return;
587 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
588 if (wwo->lpLoopPtr) {
589 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
590 TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
592 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
593 wwo->lpLoopPtr = lpWaveHdr;
594 /* Windows does not touch WAVEHDR.dwLoops,
595 * so we need to make an internal copy */
596 wwo->dwLoops = lpWaveHdr->dwLoops;
599 wwo->dwPartialOffset = 0;
602 /**************************************************************************
603 * wodPlayer_PlayPtrNext [internal]
605 * Advance the play pointer to the next waveheader, looping if required.
607 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
609 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
611 wwo->dwPartialOffset = 0;
612 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
613 /* We're at the end of a loop, loop if required */
614 if (--wwo->dwLoops > 0) {
615 wwo->lpPlayPtr = wwo->lpLoopPtr;
617 /* Handle overlapping loops correctly */
618 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
619 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
620 /* shall we consider the END flag for the closing loop or for
621 * the opening one or for both ???
622 * code assumes for closing loop only
625 lpWaveHdr = lpWaveHdr->lpNext;
627 wwo->lpLoopPtr = NULL;
628 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
631 /* We're not in a loop. Advance to the next wave header */
632 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
638 /**************************************************************************
639 * wodPlayer_NotifyWait [internal]
640 * Returns the number of milliseconds to wait before attempting to notify
641 * completion of the specified wavehdr.
642 * This is based on the number of bytes remaining to be written in the
645 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
649 if(lpWaveHdr->reserved < wwo->dwPlayedTotal)
655 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
656 if(!dwMillis) dwMillis = 1;
659 TRACE("dwMillis = %ld\n", dwMillis);
665 /**************************************************************************
666 * wodPlayer_WriteMaxFrags [internal]
667 * Writes the maximum number of bytes possible to the DSP and returns
668 * the number of bytes written.
670 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
672 /* Only attempt to write to free bytes */
673 DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
674 int toWrite = min(dwLength, *bytes);
677 TRACE("Writing wavehdr %p.%lu[%lu]\n",
678 wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
680 /* see if our buffer isn't large enough for the data we are writing */
681 if(wwo->buffer_size < toWrite)
683 if(wwo->sound_buffer)
685 wwo->sound_buffer = HeapReAlloc(GetProcessHeap(), 0, wwo->sound_buffer, toWrite);
686 wwo->buffer_size = toWrite;
690 /* if we don't have a buffer then get one */
691 if(!wwo->sound_buffer)
693 /* allocate some memory for the buffer */
694 wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);
695 wwo->buffer_size = toWrite;
698 /* if we don't have a buffer then error out */
699 if(!wwo->sound_buffer)
701 ERR("error allocating sound_buffer memory\n");
705 TRACE("toWrite == %d\n", toWrite);
707 /* apply volume to the bits */
708 /* for single channel audio streams we only use the LEFT volume */
709 if(wwo->format.wBitsPerSample == 16)
711 /* apply volume to the buffer we are about to send */
712 /* divide toWrite(bytes) by 2 as volume processes by 16 bits */
713 volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
714 wwo->sound_buffer, toWrite>>1, wwo->volume_left,
715 wwo->volume_right, wwo->format.wf.nChannels);
716 } else if(wwo->format.wBitsPerSample == 8)
718 /* apply volume to the buffer we are about to send */
719 volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
720 wwo->sound_buffer, toWrite, wwo->volume_left,
721 wwo->volume_right, wwo->format.wf.nChannels);
724 FIXME("unsupported wwo->format.wBitsPerSample of %d\n",
725 wwo->format.wBitsPerSample);
728 /* send the audio data to esd for playing */
729 written = write(wwo->esd_fd, wwo->sound_buffer, toWrite);
731 TRACE("written = %d\n", written);
735 *bytes = 0; /* apparently esd is actually full */
736 return written; /* if we wrote nothing just return */
739 if (written >= dwLength)
740 wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
742 wwo->dwPartialOffset += written; /* Remove the amount written */
744 if (written < toWrite)
749 wwo->dwWrittenTotal += written; /* update stats on this wave device */
751 return written; /* return the number of bytes written */
755 /**************************************************************************
756 * wodPlayer_NotifyCompletions [internal]
758 * Notifies and remove from queue all wavehdrs which have been played to
759 * the speaker (ie. they have cleared the audio device). If force is true,
760 * we notify all wavehdrs and remove them all from the queue even if they
761 * are unplayed or part of a loop.
763 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
767 if (wwo->lpQueuePtr) {
768 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n",
772 wwo->lpQueuePtr->reserved,
776 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), dwWrittenTotal=(%ld), force=(%d)\n",
784 /* Start from lpQueuePtr and keep notifying until:
785 * - we hit an unwritten wavehdr
786 * - we hit the beginning of a running loop
787 * - we hit a wavehdr which hasn't finished playing
789 while ((lpWaveHdr = wwo->lpQueuePtr) &&
791 (lpWaveHdr != wwo->lpPlayPtr &&
792 lpWaveHdr != wwo->lpLoopPtr &&
793 lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
795 wwo->lpQueuePtr = lpWaveHdr->lpNext;
797 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
798 lpWaveHdr->dwFlags |= WHDR_DONE;
800 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
802 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
803 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
806 /**************************************************************************
807 * wodPlayer_Reset [internal]
809 * wodPlayer helper. Resets current output stream.
811 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
813 wodUpdatePlayedTotal(wwo);
815 wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
817 /* we aren't able to flush any data that has already been written */
818 /* to esd, otherwise we would do the flushing here */
821 enum win_wm_message msg;
825 /* remove any buffer */
826 wodPlayer_NotifyCompletions(wwo, TRUE);
828 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
829 wwo->state = WINE_WS_STOPPED;
830 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
832 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
834 /* remove any existing message in the ring */
835 EnterCriticalSection(&wwo->msgRing.msg_crst);
837 /* return all pending headers in queue */
838 while (ESD_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
840 TRACE("flushing msg\n");
841 if (msg != WINE_WM_HEADER)
843 FIXME("shouldn't have headers left\n");
847 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
848 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
850 wodNotifyClient(wwo, WOM_DONE, param, 0);
852 ResetEvent(wwo->msgRing.msg_event);
853 LeaveCriticalSection(&wwo->msgRing.msg_crst);
855 if (wwo->lpLoopPtr) {
856 /* complicated case, not handled yet (could imply modifying the loop counter */
857 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
858 wwo->lpPlayPtr = wwo->lpLoopPtr;
859 wwo->dwPartialOffset = 0;
860 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
862 /* the data already written is going to be played, so take */
863 /* this fact into account here */
864 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
866 wwo->state = WINE_WS_PAUSED;
870 /**************************************************************************
871 * wodPlayer_ProcessMessages [internal]
873 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
876 enum win_wm_message msg;
880 while (ESD_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
881 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
883 case WINE_WM_PAUSING:
884 wodPlayer_Reset(wwo, FALSE);
887 case WINE_WM_RESTARTING:
888 wwo->state = WINE_WS_PLAYING;
892 lpWaveHdr = (LPWAVEHDR)param;
894 /* insert buffer at the end of queue */
897 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
901 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
902 if (wwo->state == WINE_WS_STOPPED)
903 wwo->state = WINE_WS_PLAYING;
905 case WINE_WM_RESETTING:
906 wodPlayer_Reset(wwo, TRUE);
910 wodUpdatePlayedTotal(wwo);
913 case WINE_WM_BREAKLOOP:
914 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
915 /* ensure exit at end of current loop */
920 case WINE_WM_CLOSING:
921 /* sanity check: this should not happen since the device must have been reset before */
922 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
924 wwo->state = WINE_WS_CLOSED;
927 /* shouldn't go here */
929 FIXME("unknown message %d\n", msg);
935 /**************************************************************************
936 * wodPlayer_FeedDSP [internal]
937 * Feed as much sound data as we can into the DSP and return the number of
938 * milliseconds before it will be necessary to feed the DSP again.
940 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
944 wodUpdatePlayedTotal(wwo);
945 /* better way to set availInQ? */
946 availInQ = ESD_BUF_SIZE;
947 TRACE("availInQ = %ld\n", availInQ);
949 /* input queue empty */
950 if (!wwo->lpPlayPtr) {
951 TRACE("Run out of wavehdr:s... flushing\n");
956 /* no more room... no need to try to feed */
959 TRACE("no more room, no need to try to feed\n");
960 return wwo->dwSleepTime;
964 /* Feed from partial wavehdr */
965 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
967 TRACE("feeding from partial wavehdr\n");
968 wodPlayer_WriteMaxFrags(wwo, &availInQ);
971 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
972 if (!wwo->dwPartialOffset)
974 while(wwo->lpPlayPtr && availInQ)
976 TRACE("feeding waveheaders until we run out of space\n");
977 /* note the value that dwPlayedTotal will return when this wave finishes playing */
978 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
979 TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
980 wwo->lpPlayPtr->reserved,
982 wwo->lpPlayPtr->dwBufferLength
984 wodPlayer_WriteMaxFrags(wwo, &availInQ);
988 if (!wwo->lpPlayPtr) {
989 TRACE("Ran out of wavehdrs\n");
993 return wwo->dwSleepTime;
997 /**************************************************************************
998 * wodPlayer [internal]
1000 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1002 WORD uDevID = (DWORD)pmt;
1003 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1004 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1005 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1008 wwo->state = WINE_WS_STOPPED;
1009 SetEvent(wwo->hStartUpEvent);
1012 /** Wait for the shortest time before an action is required. If there
1013 * are no pending actions, wait forever for a command.
1015 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1016 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1017 WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
1018 wodPlayer_ProcessMessages(wwo);
1019 if (wwo->state == WINE_WS_PLAYING) {
1020 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1021 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1023 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1028 /**************************************************************************
1029 * wodGetDevCaps [internal]
1031 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1033 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1035 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1037 if (wDevID >= MAX_WAVEOUTDRV) {
1038 TRACE("MAX_WAVOUTDRV reached !\n");
1039 return MMSYSERR_BADDEVICEID;
1042 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1043 return MMSYSERR_NOERROR;
1046 /**************************************************************************
1047 * wodOpen [internal]
1049 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1052 /* output to esound... */
1053 int out_bits = ESD_BITS8, out_channels = ESD_MONO, out_rate;
1054 int out_mode = ESD_STREAM, out_func = ESD_PLAY;
1055 esd_format_t out_format;
1057 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1058 if (lpDesc == NULL) {
1059 WARN("Invalid Parameter !\n");
1060 return MMSYSERR_INVALPARAM;
1062 if (wDevID >= MAX_WAVEOUTDRV) {
1063 TRACE("MAX_WAVOUTDRV reached !\n");
1064 return MMSYSERR_BADDEVICEID;
1067 /* if this device is already open tell the app that it is allocated */
1068 if(WOutDev[wDevID].esd_fd != -1)
1070 TRACE("device already allocated\n");
1071 return MMSYSERR_ALLOCATED;
1074 /* only PCM format is supported so far... */
1075 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1076 lpDesc->lpFormat->nChannels == 0 ||
1077 lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1078 lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1079 (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1080 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1081 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1082 lpDesc->lpFormat->nSamplesPerSec);
1083 return WAVERR_BADFORMAT;
1086 if (dwFlags & WAVE_FORMAT_QUERY) {
1087 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1088 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1089 lpDesc->lpFormat->nSamplesPerSec);
1090 return MMSYSERR_NOERROR;
1093 wwo = &WOutDev[wDevID];
1095 /* direct sound not supported, ignore the flag */
1096 dwFlags &= ~WAVE_DIRECTSOUND;
1098 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1100 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1101 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1103 if (wwo->format.wBitsPerSample == 0) {
1104 WARN("Resetting zeroed wBitsPerSample\n");
1105 wwo->format.wBitsPerSample = 8 *
1106 (wwo->format.wf.nAvgBytesPerSec /
1107 wwo->format.wf.nSamplesPerSec) /
1108 wwo->format.wf.nChannels;
1111 if (wwo->format.wBitsPerSample == 8)
1112 out_bits = ESD_BITS8;
1113 else if (wwo->format.wBitsPerSample == 16)
1114 out_bits = ESD_BITS16;
1116 wwo->bytes_per_frame = (wwo->format.wBitsPerSample * wwo->format.wf.nChannels) / 8;
1118 if (wwo->format.wf.nChannels == 1)
1119 out_channels = ESD_MONO;
1120 else if (wwo->format.wf.nChannels == 2)
1121 out_channels = ESD_STEREO;
1123 out_format = out_bits | out_channels | out_mode | out_func;
1124 out_rate = (int) wwo->format.wf.nSamplesPerSec;
1125 TRACE("esd output format = 0x%08x, rate = %d\n", out_format, out_rate);
1127 wwo->esd_fd = esd_play_stream(out_format, out_rate, esd_host, "wineesd");
1129 /* clear these so we don't have any confusion ;-) */
1130 wwo->sound_buffer = 0;
1131 wwo->buffer_size = 0;
1133 if(wwo->esd_fd < 0) return MMSYSERR_ALLOCATED;
1135 wwo->dwBufferSize = ESD_BUF_SIZE;
1136 TRACE("Buffer size is now (%ld)\n",wwo->dwBufferSize);
1138 wwo->dwPlayedTotal = 0;
1139 wwo->dwWrittenTotal = 0;
1141 wwo->dwSleepTime = (1024 * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
1143 /* Initialize volume to full level */
1144 wwo->volume_left = 100;
1145 wwo->volume_right = 100;
1147 ESD_InitRingMessage(&wwo->msgRing);
1149 /* create player thread */
1150 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1151 wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1152 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1153 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1154 CloseHandle(wwo->hStartUpEvent);
1156 wwo->hThread = INVALID_HANDLE_VALUE;
1157 wwo->dwThreadID = 0;
1159 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1161 TRACE("esd=0x%lx, dwBufferSize=%ld\n",
1162 (long)wwo->esd_fd, wwo->dwBufferSize);
1164 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1165 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1166 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1167 wwo->format.wf.nBlockAlign);
1169 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1172 /**************************************************************************
1173 * wodClose [internal]
1175 static DWORD wodClose(WORD wDevID)
1177 DWORD ret = MMSYSERR_NOERROR;
1180 TRACE("(%u);\n", wDevID);
1182 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1183 WARN("bad device ID !\n");
1184 return MMSYSERR_BADDEVICEID;
1187 wwo = &WOutDev[wDevID];
1188 if (wwo->lpQueuePtr) {
1189 WARN("buffers still playing !\n");
1190 ret = WAVERR_STILLPLAYING;
1192 TRACE("imhere[3-close]\n");
1193 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1194 ESD_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1197 ESD_DestroyRingMessage(&wwo->msgRing);
1199 ESD_CloseWaveOutDevice(wwo); /* close the stream and clean things up */
1201 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1206 /**************************************************************************
1207 * wodWrite [internal]
1210 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1212 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1214 /* first, do the sanity checks... */
1215 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1216 WARN("bad dev ID !\n");
1217 return MMSYSERR_BADDEVICEID;
1220 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1222 TRACE("unprepared\n");
1223 return WAVERR_UNPREPARED;
1226 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1228 TRACE("still playing\n");
1229 return WAVERR_STILLPLAYING;
1232 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1233 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1234 lpWaveHdr->lpNext = 0;
1236 TRACE("adding ring message\n");
1237 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1239 return MMSYSERR_NOERROR;
1242 /**************************************************************************
1243 * wodPrepare [internal]
1245 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1247 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1249 if (wDevID >= MAX_WAVEOUTDRV) {
1250 WARN("bad device ID !\n");
1251 return MMSYSERR_BADDEVICEID;
1254 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1255 return WAVERR_STILLPLAYING;
1257 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1258 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1259 return MMSYSERR_NOERROR;
1262 /**************************************************************************
1263 * wodUnprepare [internal]
1265 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1267 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1269 if (wDevID >= MAX_WAVEOUTDRV) {
1270 WARN("bad device ID !\n");
1271 return MMSYSERR_BADDEVICEID;
1274 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1275 return WAVERR_STILLPLAYING;
1277 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1278 lpWaveHdr->dwFlags |= WHDR_DONE;
1280 return MMSYSERR_NOERROR;
1283 /**************************************************************************
1284 * wodPause [internal]
1286 static DWORD wodPause(WORD wDevID)
1288 TRACE("(%u);!\n", wDevID);
1290 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1291 WARN("bad device ID !\n");
1292 return MMSYSERR_BADDEVICEID;
1295 TRACE("imhere[3-PAUSING]\n");
1296 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1298 return MMSYSERR_NOERROR;
1301 /**************************************************************************
1302 * wodRestart [internal]
1304 static DWORD wodRestart(WORD wDevID)
1306 TRACE("(%u);\n", wDevID);
1308 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1309 WARN("bad device ID !\n");
1310 return MMSYSERR_BADDEVICEID;
1313 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1314 TRACE("imhere[3-RESTARTING]\n");
1315 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1318 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1319 /* FIXME: Myst crashes with this ... hmm -MM
1320 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1323 return MMSYSERR_NOERROR;
1326 /**************************************************************************
1327 * wodReset [internal]
1329 static DWORD wodReset(WORD wDevID)
1331 TRACE("(%u);\n", wDevID);
1333 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1334 WARN("bad device ID !\n");
1335 return MMSYSERR_BADDEVICEID;
1338 TRACE("imhere[3-RESET]\n");
1339 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1341 return MMSYSERR_NOERROR;
1344 /**************************************************************************
1345 * wodGetPosition [internal]
1347 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1353 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1355 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1356 WARN("bad device ID !\n");
1357 return MMSYSERR_BADDEVICEID;
1360 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1362 wwo = &WOutDev[wDevID];
1363 ESD_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1364 val = wwo->dwPlayedTotal;
1366 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1367 lpTime->wType, wwo->format.wBitsPerSample,
1368 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1369 wwo->format.wf.nAvgBytesPerSec);
1370 TRACE("dwPlayedTotal=%lu\n", val);
1372 switch (lpTime->wType) {
1375 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1378 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
1379 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1382 time = (double)val / (double)wwo->format.wf.nAvgBytesPerSec;
1383 lpTime->u.smpte.hour = time / (60 * 60);
1384 time -= lpTime->u.smpte.hour * (60 * 60);
1385 lpTime->u.smpte.min = time / 60;
1386 time -= lpTime->u.smpte.min * 60;
1387 lpTime->u.smpte.sec = time;
1388 time -= lpTime->u.smpte.sec;
1389 lpTime->u.smpte.frame = ceil(time * 30);
1390 lpTime->u.smpte.fps = 30;
1391 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1392 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1393 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1396 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1397 lpTime->wType = TIME_MS;
1399 lpTime->u.ms = val * 1000.0 / wwo->format.wf.nAvgBytesPerSec;
1400 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1403 return MMSYSERR_NOERROR;
1406 /**************************************************************************
1407 * wodBreakLoop [internal]
1409 static DWORD wodBreakLoop(WORD wDevID)
1411 TRACE("(%u);\n", wDevID);
1413 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].esd_fd == -1) {
1414 WARN("bad device ID !\n");
1415 return MMSYSERR_BADDEVICEID;
1417 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1418 return MMSYSERR_NOERROR;
1421 /**************************************************************************
1422 * wodGetVolume [internal]
1424 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1428 left = WOutDev[wDevID].volume_left;
1429 right = WOutDev[wDevID].volume_right;
1431 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1433 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) <<
1436 return MMSYSERR_NOERROR;
1439 /**************************************************************************
1440 * wodSetVolume [internal]
1442 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1446 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1447 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1449 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1451 WOutDev[wDevID].volume_left = left;
1452 WOutDev[wDevID].volume_right = right;
1454 return MMSYSERR_NOERROR;
1457 /**************************************************************************
1458 * wodGetNumDevs [internal]
1460 static DWORD wodGetNumDevs(void)
1462 return MAX_WAVEOUTDRV;
1465 /**************************************************************************
1466 * wodDevInterfaceSize [internal]
1468 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1470 TRACE("(%u, %p)\n", wDevID, dwParam1);
1472 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1473 NULL, 0 ) * sizeof(WCHAR);
1474 return MMSYSERR_NOERROR;
1477 /**************************************************************************
1478 * wodDevInterface [internal]
1480 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1482 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1483 NULL, 0 ) * sizeof(WCHAR))
1485 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1486 dwParam1, dwParam2 / sizeof(WCHAR));
1487 return MMSYSERR_NOERROR;
1489 return MMSYSERR_INVALPARAM;
1492 /**************************************************************************
1493 * wodMessage (WINEESD.@)
1495 DWORD WINAPI ESD_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1496 DWORD dwParam1, DWORD dwParam2)
1498 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1499 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1506 /* FIXME: Pretend this is supported */
1508 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1509 case WODM_CLOSE: return wodClose (wDevID);
1510 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1511 case WODM_PAUSE: return wodPause (wDevID);
1512 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1513 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
1514 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1515 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1516 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
1517 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1518 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1519 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1520 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1521 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1522 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1523 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1524 case WODM_RESTART: return wodRestart (wDevID);
1525 case WODM_RESET: return wodReset (wDevID);
1527 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1528 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1529 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
1530 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
1532 FIXME("unknown message %d!\n", wMsg);
1534 return MMSYSERR_NOTSUPPORTED;
1537 /*======================================================================*
1538 * Low level WAVE IN implementation *
1539 *======================================================================*/
1541 /**************************************************************************
1542 * widGetNumDevs [internal]
1544 static DWORD widGetNumDevs(void)
1546 TRACE("%d \n",MAX_WAVEINDRV);
1547 return MAX_WAVEINDRV;
1550 /**************************************************************************
1551 * widDevInterfaceSize [internal]
1553 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1555 TRACE("(%u, %p)\n", wDevID, dwParam1);
1558 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1559 NULL, 0 ) * sizeof(WCHAR);
1560 return MMSYSERR_NOERROR;
1563 /**************************************************************************
1564 * widDevInterface [internal]
1566 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1568 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1569 NULL, 0 ) * sizeof(WCHAR))
1571 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1572 dwParam1, dwParam2 / sizeof(WCHAR));
1573 return MMSYSERR_NOERROR;
1575 return MMSYSERR_INVALPARAM;
1578 /**************************************************************************
1579 * widNotifyClient [internal]
1581 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1583 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1589 if (wwi->wFlags != DCB_NULL &&
1590 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1591 (HDRVR)wwi->waveDesc.hWave, wMsg,
1592 wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
1593 WARN("can't notify client !\n");
1594 return MMSYSERR_ERROR;
1598 FIXME("Unknown callback message %u\n", wMsg);
1599 return MMSYSERR_INVALPARAM;
1601 return MMSYSERR_NOERROR;
1604 /**************************************************************************
1605 * widGetDevCaps [internal]
1607 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1609 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1611 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1613 if (wDevID >= MAX_WAVEINDRV) {
1614 TRACE("MAX_WAVINDRV reached !\n");
1615 return MMSYSERR_BADDEVICEID;
1618 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1619 return MMSYSERR_NOERROR;
1622 /**************************************************************************
1623 * widRecorder [internal]
1625 static DWORD CALLBACK widRecorder(LPVOID pmt)
1627 WORD uDevID = (DWORD)pmt;
1628 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1632 enum win_wm_message msg;
1636 SetEvent(wwi->hStartUpEvent);
1638 /* make sleep time to be # of ms to record one packet */
1639 dwSleepTime = (1024 * 1000) / wwi->format.wf.nAvgBytesPerSec;
1640 TRACE("sleeptime=%ld ms\n", dwSleepTime);
1643 TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d)\n",wwi->lpQueuePtr,wwi->state);
1645 /* read all data is esd input buffer. */
1646 if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING))
1648 lpWaveHdr = wwi->lpQueuePtr;
1650 TRACE("read as much as we can\n");
1651 while(wwi->lpQueuePtr)
1653 TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1654 bytesRead = read(wwi->esd_fd,
1655 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1656 lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1657 TRACE("bytesRead=%ld\n",bytesRead);
1658 if (bytesRead == -1 && errno == EAGAIN)
1660 if (bytesRead==0) break; /* So we can stop recording smoothly */
1664 lpWaveHdr->dwBytesRecorded += bytesRead;
1665 wwi->dwRecordedTotal += bytesRead;
1667 /* buffer full. notify client */
1668 if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
1670 /* must copy the value of next waveHdr, because we have no idea of what
1671 * will be done with the content of lpWaveHdr in callback
1673 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1675 TRACE("waveHdr full.\n");
1677 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1678 lpWaveHdr->dwFlags |= WHDR_DONE;
1680 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1681 lpWaveHdr = wwi->lpQueuePtr = lpNext;
1686 /* wait for dwSleepTime or an event in thread's queue */
1687 WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
1689 while (ESD_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
1691 TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
1693 case WINE_WM_PAUSING:
1694 wwi->state = WINE_WS_PAUSED;
1696 /* Put code here to "pause" esd recording
1701 case WINE_WM_STARTING:
1702 wwi->state = WINE_WS_PLAYING;
1704 /* Put code here to "start" esd recording
1709 case WINE_WM_HEADER:
1710 lpWaveHdr = (LPWAVEHDR)param;
1711 /* insert buffer at end of queue */
1714 int num_headers = 0;
1715 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1723 case WINE_WM_STOPPING:
1724 if (wwi->state != WINE_WS_STOPPED)
1727 /* Put code here to "stop" esd recording
1730 /* return current buffer to app */
1731 lpWaveHdr = wwi->lpQueuePtr;
1734 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1735 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1736 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1737 lpWaveHdr->dwFlags |= WHDR_DONE;
1738 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1739 wwi->lpQueuePtr = lpNext;
1742 wwi->state = WINE_WS_STOPPED;
1745 case WINE_WM_RESETTING:
1746 wwi->state = WINE_WS_STOPPED;
1747 wwi->dwRecordedTotal = 0;
1749 /* return all buffers to the app */
1750 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1751 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1752 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1753 lpWaveHdr->dwFlags |= WHDR_DONE;
1755 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1757 wwi->lpQueuePtr = NULL;
1760 case WINE_WM_CLOSING:
1762 wwi->state = WINE_WS_CLOSED;
1765 /* shouldn't go here */
1767 FIXME("unknown message %d\n", msg);
1773 /* just for not generating compilation warnings... should never be executed */
1777 /**************************************************************************
1778 * widOpen [internal]
1780 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1783 /* input esound... */
1784 int in_bits = ESD_BITS16, in_channels = ESD_STEREO, in_rate;
1785 #ifdef WID_USE_ESDMON
1786 int in_mode = ESD_STREAM, in_func = ESD_PLAY;
1788 int in_mode = ESD_STREAM, in_func = ESD_RECORD;
1790 esd_format_t in_format;
1793 TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
1794 if (lpDesc == NULL) {
1795 WARN("Invalid Parametr (lpDesc == NULL)!\n");
1796 return MMSYSERR_INVALPARAM;
1799 if (wDevID >= MAX_WAVEINDRV) {
1800 TRACE ("MAX_WAVEINDRV reached !\n");
1801 return MMSYSERR_BADDEVICEID;
1804 /* if this device is already open tell the app that it is allocated */
1805 if(WInDev[wDevID].esd_fd != -1)
1807 TRACE("device already allocated\n");
1808 return MMSYSERR_ALLOCATED;
1811 /* only PCM format is support so far... */
1812 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1813 lpDesc->lpFormat->nChannels == 0 ||
1814 lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1815 lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1816 (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1817 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1818 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1819 lpDesc->lpFormat->nSamplesPerSec);
1820 return WAVERR_BADFORMAT;
1823 if (dwFlags & WAVE_FORMAT_QUERY) {
1824 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1825 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1826 lpDesc->lpFormat->nSamplesPerSec);
1827 return MMSYSERR_NOERROR;
1830 wwi = &WInDev[wDevID];
1832 /* direct sound not supported, ignore the flag */
1833 dwFlags &= ~WAVE_DIRECTSOUND;
1835 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1837 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1838 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1840 if (wwi->format.wBitsPerSample == 0) {
1841 WARN("Resetting zerod wBitsPerSample\n");
1842 wwi->format.wBitsPerSample = 8 *
1843 (wwi->format.wf.nAvgBytesPerSec /
1844 wwi->format.wf.nSamplesPerSec) /
1845 wwi->format.wf.nChannels;
1848 if (wwi->format.wBitsPerSample == 8)
1849 in_bits = ESD_BITS8;
1850 else if (wwi->format.wBitsPerSample == 16)
1851 in_bits = ESD_BITS16;
1853 wwi->bytes_per_frame = (wwi->format.wBitsPerSample * wwi->format.wf.nChannels) / 8;
1855 if (wwi->format.wf.nChannels == 1)
1856 in_channels = ESD_MONO;
1857 else if (wwi->format.wf.nChannels == 2)
1858 in_channels = ESD_STEREO;
1860 in_format = in_bits | in_channels | in_mode | in_func;
1861 in_rate = (int) wwi->format.wf.nSamplesPerSec;
1862 TRACE("esd input format = 0x%08x, rate = %d\n", in_format, in_rate);
1864 #ifdef WID_USE_ESDMON
1865 wwi->esd_fd = esd_monitor_stream(in_format, in_rate, esd_host, "wineesd");
1867 wwi->esd_fd = esd_record_stream(in_format, in_rate, esd_host, "wineesd");
1869 TRACE("(wwi->esd_fd=%d)\n",wwi->esd_fd);
1870 wwi->state = WINE_WS_STOPPED;
1872 if (wwi->lpQueuePtr) {
1873 WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1874 wwi->lpQueuePtr = NULL;
1877 if(wwi->esd_fd < 0) return MMSYSERR_ALLOCATED;
1879 /* Set the esd socket O_NONBLOCK, so we can stop recording smoothly */
1880 mode = fcntl(wwi->esd_fd, F_GETFL);
1882 fcntl(wwi->esd_fd, F_SETFL, mode);
1884 wwi->dwRecordedTotal = 0;
1885 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1887 ESD_InitRingMessage(&wwi->msgRing);
1889 /* create recorder thread */
1890 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1891 wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1892 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1893 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
1894 CloseHandle(wwi->hStartUpEvent);
1896 wwi->hThread = INVALID_HANDLE_VALUE;
1897 wwi->dwThreadID = 0;
1899 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
1901 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1902 wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
1903 wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
1904 wwi->format.wf.nBlockAlign);
1905 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1908 /**************************************************************************
1909 * widClose [internal]
1911 static DWORD widClose(WORD wDevID)
1915 TRACE("(%u);\n", wDevID);
1916 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1917 WARN("can't close !\n");
1918 return MMSYSERR_INVALHANDLE;
1921 wwi = &WInDev[wDevID];
1923 if (wwi->lpQueuePtr != NULL) {
1924 WARN("still buffers open !\n");
1925 return WAVERR_STILLPLAYING;
1928 ESD_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
1929 ESD_CloseWaveInDevice(wwi);
1930 wwi->state = WINE_WS_CLOSED;
1931 ESD_DestroyRingMessage(&wwi->msgRing);
1932 return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1935 /**************************************************************************
1936 * widAddBuffer [internal]
1938 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1940 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1942 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1943 WARN("can't do it !\n");
1944 return MMSYSERR_INVALHANDLE;
1946 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1947 TRACE("never been prepared !\n");
1948 return WAVERR_UNPREPARED;
1950 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1951 TRACE("header already in use !\n");
1952 return WAVERR_STILLPLAYING;
1955 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1956 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1957 lpWaveHdr->dwBytesRecorded = 0;
1958 lpWaveHdr->lpNext = NULL;
1960 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1961 return MMSYSERR_NOERROR;
1964 /**************************************************************************
1965 * widPrepare [internal]
1967 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1969 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1971 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1973 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1974 return WAVERR_STILLPLAYING;
1976 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1977 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1978 lpWaveHdr->dwBytesRecorded = 0;
1980 return MMSYSERR_NOERROR;
1983 /**************************************************************************
1984 * widUnprepare [internal]
1986 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1988 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1989 if (wDevID >= MAX_WAVEINDRV) {
1990 WARN("bad device ID !\n");
1991 return MMSYSERR_INVALHANDLE;
1994 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1995 TRACE("Still playing...\n");
1996 return WAVERR_STILLPLAYING;
1999 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
2000 lpWaveHdr->dwFlags |= WHDR_DONE;
2002 return MMSYSERR_NOERROR;
2005 /**************************************************************************
2006 * widStart [internal]
2008 static DWORD widStart(WORD wDevID)
2010 TRACE("(%u);\n", wDevID);
2011 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2012 WARN("can't start recording !\n");
2013 return MMSYSERR_INVALHANDLE;
2016 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
2017 return MMSYSERR_NOERROR;
2020 /**************************************************************************
2021 * widStop [internal]
2023 static DWORD widStop(WORD wDevID)
2025 TRACE("(%u);\n", wDevID);
2026 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2027 WARN("can't stop !\n");
2028 return MMSYSERR_INVALHANDLE;
2031 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
2033 return MMSYSERR_NOERROR;
2036 /**************************************************************************
2037 * widReset [internal]
2039 static DWORD widReset(WORD wDevID)
2041 TRACE("(%u);\n", wDevID);
2042 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2043 WARN("can't reset !\n");
2044 return MMSYSERR_INVALHANDLE;
2046 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2047 return MMSYSERR_NOERROR;
2050 /**************************************************************************
2051 * widMessage (WINEESD.6)
2053 DWORD WINAPI ESD_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2054 DWORD dwParam1, DWORD dwParam2)
2056 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2057 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2063 /* FIXME: Pretend this is supported */
2065 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2066 case WIDM_CLOSE: return widClose (wDevID);
2067 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2068 case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2069 case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2070 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
2071 case WIDM_GETNUMDEVS: return widGetNumDevs ();
2072 case WIDM_RESET: return widReset (wDevID);
2073 case WIDM_START: return widStart (wDevID);
2074 case WIDM_STOP: return widStop (wDevID);
2075 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2076 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2078 FIXME("unknown message %d!\n", wMsg);
2080 return MMSYSERR_NOTSUPPORTED;
2083 /*======================================================================*
2084 * Low level DSOUND implementation *
2085 *======================================================================*/
2086 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2088 /* we can't perform memory mapping as we don't have a file stream
2089 interface with esd like we do with oss */
2090 MESSAGE("This sound card's driver does not support direct access\n");
2091 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2092 return MMSYSERR_NOTSUPPORTED;
2095 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2097 memset(desc, 0, sizeof(*desc));
2098 strcpy(desc->szDesc, "Wine EsounD DirectSound Driver");
2099 strcpy(desc->szDrvname, "wineesd.drv");
2100 return MMSYSERR_NOERROR;
2103 #else /* !HAVE_ESD */
2105 /**************************************************************************
2106 * wodMessage (WINEESD.@)
2108 DWORD WINAPI ESD_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2109 DWORD dwParam1, DWORD dwParam2)
2111 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2112 return MMSYSERR_NOTENABLED;
2115 /**************************************************************************
2116 * widMessage (WINEESD.6)
2118 DWORD WINAPI ESD_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2119 DWORD dwParam1, DWORD dwParam2)
2121 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2122 return MMSYSERR_NOTENABLED;
2125 #endif /* HAVE_ESD */