ntdll/tests: Add a FILE_APPEND_DATA test.
[wine.git] / dlls / wineesd.drv / audio.c
blob9f7ff006d4050680045b879e12cd738535d93cda
1 /*
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 /* NOTE:
25 * with esd we cannot stop the audio that is already in
26 * the server's buffer.
28 * FIXME:
29 * pause in waveOut does not work correctly in loop mode
31 * does something need to be done in for WaveIn DirectSound?
35 #include "config.h"
37 #include <errno.h>
38 #include <math.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <string.h>
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif
46 #include <fcntl.h>
47 #ifdef HAVE_POLL_H
48 #include <poll.h>
49 #endif
50 #ifdef HAVE_SYS_POLL_H
51 # include <sys/poll.h>
52 #endif
54 #include "windef.h"
55 #include "winbase.h"
56 #include "wingdi.h"
57 #include "winerror.h"
58 #include "wine/winuser16.h"
59 #include "mmddk.h"
60 #include "mmreg.h"
61 #include "dsound.h"
62 #include "dsdriver.h"
63 #include "ks.h"
64 #include "ksguid.h"
65 #include "ksmedia.h"
66 #include "wine/debug.h"
68 WINE_DEFAULT_DEBUG_CHANNEL(wave);
70 #ifdef HAVE_SYS_ERRNO_H
71 #include <sys/errno.h>
72 #endif
74 #ifdef HAVE_ESD
76 #include <esd.h>
78 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
79 #define USE_PIPE_SYNC
81 /* define if you want to use esd_monitor_stream instead of
82 * esd_record_stream for waveIn stream
84 /*#define WID_USE_ESDMON*/
86 #define BUFFER_REFILL_THRESHOLD 4
88 #define MAX_WAVEOUTDRV (10)
89 #define MAX_WAVEINDRV (10)
90 #define MAX_CHANNELS 2
92 /* state diagram for waveOut writing:
94 * +---------+-------------+---------------+---------------------------------+
95 * | state | function | event | new state |
96 * +---------+-------------+---------------+---------------------------------+
97 * | | open() | | STOPPED |
98 * | PAUSED | write() | | PAUSED |
99 * | STOPPED | write() | <thrd create> | PLAYING |
100 * | PLAYING | write() | HEADER | PLAYING |
101 * | (other) | write() | <error> | |
102 * | (any) | pause() | PAUSING | PAUSED |
103 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
104 * | (any) | reset() | RESETTING | STOPPED |
105 * | (any) | close() | CLOSING | CLOSED |
106 * +---------+-------------+---------------+---------------------------------+
109 /* states of the playing device */
110 #define WINE_WS_PLAYING 0
111 #define WINE_WS_PAUSED 1
112 #define WINE_WS_STOPPED 2
113 #define WINE_WS_CLOSED 3
115 /* events to be send to device */
116 enum win_wm_message {
117 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
118 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
121 #ifdef USE_PIPE_SYNC
122 #define SIGNAL_OMR(mr) do { int x = 0; write((mr)->msg_pipe[1], &x, sizeof(x)); } while (0)
123 #define CLEAR_OMR(mr) do { int x = 0; read((mr)->msg_pipe[0], &x, sizeof(x)); } while (0)
124 #define RESET_OMR(mr) do { } while (0)
125 #define WAIT_OMR(mr, sleep) \
126 do { struct pollfd pfd; pfd.fd = (mr)->msg_pipe[0]; \
127 pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
128 #else
129 #define SIGNAL_OMR(mr) do { SetEvent((mr)->msg_event); } while (0)
130 #define CLEAR_OMR(mr) do { } while (0)
131 #define RESET_OMR(mr) do { ResetEvent((mr)->msg_event); } while (0)
132 #define WAIT_OMR(mr, sleep) \
133 do { WaitForSingleObject((mr)->msg_event, sleep); } while (0)
134 #endif
136 typedef struct {
137 enum win_wm_message msg; /* message identifier */
138 DWORD_PTR param; /* parameter for this message */
139 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
140 } RING_MSG;
142 /* implement an in-process message ring for better performance
143 * (compared to passing thru the server)
144 * this ring will be used by the input (resp output) record (resp playback) routine
146 #define ESD_RING_BUFFER_INCREMENT 64
147 typedef struct {
148 RING_MSG * messages;
149 int ring_buffer_size;
150 int msg_tosave;
151 int msg_toget;
152 #ifdef USE_PIPE_SYNC
153 int msg_pipe[2];
154 #else
155 HANDLE msg_event;
156 #endif
157 CRITICAL_SECTION msg_crst;
158 } ESD_MSG_RING;
160 typedef struct {
161 volatile int state; /* one of the WINE_WS_ manifest constants */
162 WAVEOPENDESC waveDesc;
163 WORD wFlags;
164 WAVEFORMATPCMEX waveFormat;
165 WAVEOUTCAPSW caps;
166 char interface_name[32];
168 DWORD dwSleepTime; /* Num of milliseconds to sleep between filling the dsp buffers */
170 /* esd information */
171 char* stream_name; /* a unique name identifying the esd stream */
172 int stream_fd; /* the socket fd we get from esd when opening a stream for playing */
173 int stream_id; /* the stream id (to change the volume) */
174 int esd_fd; /* a socket to communicate with the sound server */
176 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
177 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
178 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
180 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
181 DWORD dwLoops; /* private copy of loop counter */
183 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
184 DWORD dwWrittenTotal; /* number of bytes written to the audio device since opening */
185 DWORD dwLastWrite; /* Time of last write */
186 DWORD dwLatency; /* Num of milliseconds between when data is sent to the server and when it is played */
188 /* synchronization stuff */
189 HANDLE hStartUpEvent;
190 HANDLE hThread;
191 ESD_MSG_RING msgRing;
192 } WINE_WAVEOUT;
194 typedef struct {
195 volatile int state; /* one of the WINE_WS_ manifest constants */
196 WAVEOPENDESC waveDesc;
197 WORD wFlags;
198 WAVEFORMATPCMEX waveFormat;
199 WAVEINCAPSW caps;
200 char interface_name[32];
202 /* esd information */
203 char* stream_name; /* a unique name identifying the esd stream */
204 int stream_fd; /* the socket fd we get from esd when opening a stream for recording */
206 LPWAVEHDR lpQueuePtr;
207 DWORD dwRecordedTotal;
209 /* synchronization stuff */
210 HANDLE hStartUpEvent;
211 HANDLE hThread;
212 ESD_MSG_RING msgRing;
213 } WINE_WAVEIN;
215 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
216 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
218 /* These strings used only for tracing */
219 static const char *wodPlayerCmdString[] = {
220 "WINE_WM_PAUSING",
221 "WINE_WM_RESTARTING",
222 "WINE_WM_RESETTING",
223 "WINE_WM_HEADER",
224 "WINE_WM_UPDATE",
225 "WINE_WM_BREAKLOOP",
226 "WINE_WM_CLOSING",
227 "WINE_WM_STARTING",
228 "WINE_WM_STOPPING",
232 /*======================================================================*
233 * Low level DSOUND implementation *
234 *======================================================================*/
236 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
238 /* we can't perform memory mapping as we don't have a file stream
239 interface with esd like we do with oss */
240 MESSAGE("This sound card's driver does not support direct access\n");
241 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
242 return MMSYSERR_NOTSUPPORTED;
245 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
247 memset(desc, 0, sizeof(*desc));
248 strcpy(desc->szDesc, "Wine EsounD DirectSound Driver");
249 strcpy(desc->szDrvname, "wineesd.drv");
250 return MMSYSERR_NOERROR;
253 /*======================================================================*
254 * Low level WAVE implementation *
255 *======================================================================*/
257 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
258 WAVEFORMATPCMEX* format)
260 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
261 lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
262 format->Format.nChannels, format->Format.nAvgBytesPerSec);
263 TRACE("Position in bytes=%u\n", position);
265 switch (lpTime->wType) {
266 case TIME_SAMPLES:
267 lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
268 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
269 break;
270 case TIME_MS:
271 lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
272 TRACE("TIME_MS=%u\n", lpTime->u.ms);
273 break;
274 case TIME_SMPTE:
275 lpTime->u.smpte.fps = 30;
276 position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
277 position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
278 lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
279 position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
280 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
281 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
282 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
283 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
284 lpTime->u.smpte.fps = 30;
285 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
286 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
287 lpTime->u.smpte.hour, lpTime->u.smpte.min,
288 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
289 break;
290 default:
291 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
292 lpTime->wType = TIME_BYTES;
293 /* fall through */
294 case TIME_BYTES:
295 lpTime->u.cb = position;
296 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
297 break;
299 return MMSYSERR_NOERROR;
302 static BOOL supportedFormat(LPWAVEFORMATEX wf)
304 TRACE("(%p)\n",wf);
306 if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
307 return FALSE;
309 if (wf->wFormatTag == WAVE_FORMAT_PCM) {
310 if (wf->nChannels >= 1 && wf->nChannels <= MAX_CHANNELS) {
311 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
312 return TRUE;
314 } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
315 WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
317 if (wf->cbSize == 22 && IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
318 if (wf->nChannels >=1 && wf->nChannels <= MAX_CHANNELS) {
319 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
320 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
321 return TRUE;
322 } else
323 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
325 } else
326 WARN("only KSDATAFORMAT_SUBTYPE_PCM supported\n");
327 } else
328 WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
330 return FALSE;
333 static void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
335 ZeroMemory(wf2, sizeof(wf2));
336 if (wf1->wFormatTag == WAVE_FORMAT_PCM)
337 memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
338 else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
339 memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
340 else
341 memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
344 static char* get_stream_name(const char* direction, unsigned int dev_id)
346 char exename[MAX_PATH];
347 char *basename, *s;
348 char* stream_name;
350 GetModuleFileNameA(NULL, exename, sizeof(exename));
351 exename[sizeof(exename)-1]='\0';
352 basename = s = exename;
353 while (*s)
355 if (*s == '/' || *s == '\\')
356 basename = s+1;
357 s++;
360 stream_name = HeapAlloc(GetProcessHeap(), 0, 4+strlen(basename)+10+strlen(direction)+10+1);
361 sprintf(stream_name, "%s (%lu:%s%u)", basename, (unsigned long)getpid(), direction, dev_id);
363 return stream_name;
366 /******************************************************************
367 * ESD_CloseWaveOutDevice
370 static void ESD_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
372 HeapFree(GetProcessHeap(), 0, wwo->stream_name);
373 esd_close(wwo->stream_fd);
374 wwo->stream_fd = -1;
375 if (wwo->esd_fd)
376 esd_close(wwo->esd_fd);
379 /******************************************************************
380 * ESD_CloseWaveInDevice
383 static void ESD_CloseWaveInDevice(WINE_WAVEIN* wwi)
385 HeapFree(GetProcessHeap(), 0, wwi->stream_name);
386 esd_close(wwi->stream_fd);
387 wwi->stream_fd = -1;
390 static int WAVE_loadcount;
391 /******************************************************************
392 * ESD_WaveClose
394 static LONG ESD_WaveClose(void)
396 int iDevice;
398 if (--WAVE_loadcount)
399 return 1;
401 /* close all open devices */
402 for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
404 if(WOutDev[iDevice].stream_fd != -1)
406 ESD_CloseWaveOutDevice(&WOutDev[iDevice]);
410 for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
412 if(WInDev[iDevice].stream_fd != -1)
414 ESD_CloseWaveInDevice(&WInDev[iDevice]);
418 return 1;
421 /******************************************************************
422 * ESD_WaveInit
424 * Initialize internal structures from ESD server info
426 static LONG ESD_WaveInit(void)
428 int i;
429 int fd;
431 TRACE("called\n");
432 if (WAVE_loadcount++)
433 return 1;
435 /* Testing whether the esd host is alive. */
436 if ((fd = esd_open_sound(NULL)) < 0)
438 WARN("esd_open_sound() failed (%d)\n", errno);
439 return 0;
441 esd_close(fd);
443 /* initialize all device handles to -1 */
444 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
446 static const WCHAR ini[] = {'E','s','o','u','n','D',' ','W','a','v','e','O','u','t','D','r','i','v','e','r',0};
448 WOutDev[i].stream_fd = -1;
449 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out
450 caps values */
451 WOutDev[i].caps.wMid = 0x00FF; /* Manufacturer ID */
452 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
453 lstrcpyW(WOutDev[i].caps.szPname, ini);
454 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "wineesd: %d", i);
456 WOutDev[i].caps.vDriverVersion = 0x0100;
457 WOutDev[i].caps.dwFormats = 0x00000000;
458 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
460 WOutDev[i].caps.wChannels = 2;
461 WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
463 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
464 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
465 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
466 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
467 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
468 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
469 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
470 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
471 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
472 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
473 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
474 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
477 for (i = 0; i < MAX_WAVEINDRV; ++i)
479 static const WCHAR ini[] = {'E','s','o','u','n','D',' ','W','a','v','e','I','n','D','r','i','v','e','r',0};
481 WInDev[i].stream_fd = -1;
482 memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
483 caps values */
484 WInDev[i].caps.wMid = 0x00FF;
485 WInDev[i].caps.wPid = 0x0001;
486 lstrcpyW(WInDev[i].caps.szPname, ini);
487 snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "wineesd: %d", i);
489 WInDev[i].caps.vDriverVersion = 0x0100;
490 WInDev[i].caps.dwFormats = 0x00000000;
492 WInDev[i].caps.wChannels = 2;
494 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
495 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
496 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
497 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
498 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
499 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
500 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
501 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
502 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
503 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
504 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
505 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
507 WInDev[i].caps.wReserved1 = 0;
509 return 1;
512 /******************************************************************
513 * ESD_InitRingMessage
515 * Initialize the ring of messages for passing between driver's caller and playback/record
516 * thread
518 static int ESD_InitRingMessage(ESD_MSG_RING* mr)
520 mr->msg_toget = 0;
521 mr->msg_tosave = 0;
522 #ifdef USE_PIPE_SYNC
523 if (pipe(mr->msg_pipe) < 0) {
524 mr->msg_pipe[0] = -1;
525 mr->msg_pipe[1] = -1;
526 ERR("could not create pipe, error=%s\n", strerror(errno));
528 #else
529 mr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
530 #endif
531 mr->ring_buffer_size = ESD_RING_BUFFER_INCREMENT;
532 mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
533 InitializeCriticalSection(&mr->msg_crst);
534 mr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ESD_MSG_RING.msg_crst");
535 return 0;
538 /******************************************************************
539 * ESD_DestroyRingMessage
542 static int ESD_DestroyRingMessage(ESD_MSG_RING* mr)
544 #ifdef USE_PIPE_SYNC
545 close(mr->msg_pipe[0]);
546 close(mr->msg_pipe[1]);
547 #else
548 CloseHandle(mr->msg_event);
549 #endif
550 HeapFree(GetProcessHeap(),0,mr->messages);
551 mr->messages=NULL;
552 mr->msg_crst.DebugInfo->Spare[0] = 0;
553 DeleteCriticalSection(&mr->msg_crst);
554 return 0;
557 /******************************************************************
558 * ESD_AddRingMessage
560 * Inserts a new message into the ring (should be called from DriverProc derived routines)
562 static int ESD_AddRingMessage(ESD_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
564 HANDLE hEvent = INVALID_HANDLE_VALUE;
566 EnterCriticalSection(&mr->msg_crst);
567 if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
569 int old_ring_buffer_size = mr->ring_buffer_size;
570 mr->ring_buffer_size += ESD_RING_BUFFER_INCREMENT;
571 TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
572 mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
573 /* Now we need to rearrange the ring buffer so that the new
574 buffers just allocated are in between mr->msg_tosave and
575 mr->msg_toget.
577 if (mr->msg_tosave < mr->msg_toget)
579 memmove(&(mr->messages[mr->msg_toget + ESD_RING_BUFFER_INCREMENT]),
580 &(mr->messages[mr->msg_toget]),
581 sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
583 mr->msg_toget += ESD_RING_BUFFER_INCREMENT;
586 if (wait)
588 hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
589 if (hEvent == INVALID_HANDLE_VALUE)
591 ERR("can't create event !?\n");
592 LeaveCriticalSection(&mr->msg_crst);
593 return 0;
595 if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
596 FIXME("two fast messages in the queue!!!!\n");
598 /* fast messages have to be added at the start of the queue */
599 mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
601 mr->messages[mr->msg_toget].msg = msg;
602 mr->messages[mr->msg_toget].param = param;
603 mr->messages[mr->msg_toget].hEvent = hEvent;
605 else
607 mr->messages[mr->msg_tosave].msg = msg;
608 mr->messages[mr->msg_tosave].param = param;
609 mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
610 mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
613 LeaveCriticalSection(&mr->msg_crst);
615 /* signal a new message */
616 SIGNAL_OMR(mr);
617 if (wait)
619 /* wait for playback/record thread to have processed the message */
620 WaitForSingleObject(hEvent, INFINITE);
621 CloseHandle(hEvent);
624 return 1;
627 /******************************************************************
628 * ESD_RetrieveRingMessage
630 * Get a message from the ring. Should be called by the playback/record thread.
632 static int ESD_RetrieveRingMessage(ESD_MSG_RING* mr, enum win_wm_message *msg,
633 DWORD_PTR *param, HANDLE *hEvent)
635 EnterCriticalSection(&mr->msg_crst);
637 if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
639 LeaveCriticalSection(&mr->msg_crst);
640 return 0;
643 *msg = mr->messages[mr->msg_toget].msg;
644 mr->messages[mr->msg_toget].msg = 0;
645 *param = mr->messages[mr->msg_toget].param;
646 *hEvent = mr->messages[mr->msg_toget].hEvent;
647 mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
648 CLEAR_OMR(mr);
649 LeaveCriticalSection(&mr->msg_crst);
650 return 1;
653 /*======================================================================*
654 * Low level WAVE OUT implementation *
655 *======================================================================*/
657 /**************************************************************************
658 * wodNotifyClient [internal]
660 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD_PTR dwParam1,
661 DWORD_PTR dwParam2)
663 TRACE("wMsg = 0x%04x dwParm1 = %08lX dwParam2 = %08lX\n", wMsg, dwParam1, dwParam2);
665 switch (wMsg) {
666 case WOM_OPEN:
667 case WOM_CLOSE:
668 case WOM_DONE:
669 if (wwo->wFlags != DCB_NULL &&
670 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
671 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
672 WARN("can't notify client !\n");
673 return MMSYSERR_ERROR;
675 break;
676 default:
677 FIXME("Unknown callback message %u\n", wMsg);
678 return MMSYSERR_INVALPARAM;
680 return MMSYSERR_NOERROR;
683 /**************************************************************************
684 * wodUpdatePlayedTotal [internal]
686 * dwPlayedTotal is used for wodPlayer_NotifyCompletions() and
687 * wodGetPosition(), so a byte must only be reported as played once it has
688 * reached the speakers. So give our best estimate based on the latency
689 * reported by the esd server, and on the elapsed time since the last byte
690 * was sent to the server.
692 static void wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
694 DWORD elapsed;
696 if (wwo->dwPlayedTotal == wwo->dwWrittenTotal)
697 return;
699 /* GetTickCount() wraps every now and then, but these being all unsigned it's ok */
700 elapsed = GetTickCount() - wwo->dwLastWrite;
701 if (elapsed < wwo->dwLatency)
703 wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwLatency - elapsed) * wwo->waveFormat.Format.nAvgBytesPerSec / 1000;
704 TRACE("written=%u - elapsed=%u -> played=%u\n", wwo->dwWrittenTotal, elapsed, wwo->dwPlayedTotal);
706 else
708 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
709 TRACE("elapsed=%u -> played=written=%u\n", elapsed, wwo->dwPlayedTotal);
713 /**************************************************************************
714 * wodPlayer_BeginWaveHdr [internal]
716 * Makes the specified lpWaveHdr the currently playing wave header.
717 * If the specified wave header is a begin loop and we're not already in
718 * a loop, setup the loop.
720 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
722 wwo->lpPlayPtr = lpWaveHdr;
724 if (!lpWaveHdr) return;
726 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
727 if (wwo->lpLoopPtr) {
728 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
729 TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
730 } else {
731 TRACE("Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
732 wwo->lpLoopPtr = lpWaveHdr;
733 /* Windows does not touch WAVEHDR.dwLoops,
734 * so we need to make an internal copy */
735 wwo->dwLoops = lpWaveHdr->dwLoops;
738 wwo->dwPartialOffset = 0;
741 /**************************************************************************
742 * wodPlayer_PlayPtrNext [internal]
744 * Advance the play pointer to the next waveheader, looping if required.
746 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
748 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
750 wwo->dwPartialOffset = 0;
751 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
752 /* We're at the end of a loop, loop if required */
753 if (--wwo->dwLoops > 0) {
754 wwo->lpPlayPtr = wwo->lpLoopPtr;
755 } else {
756 /* Handle overlapping loops correctly */
757 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
758 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
759 /* shall we consider the END flag for the closing loop or for
760 * the opening one or for both ???
761 * code assumes for closing loop only
763 } else {
764 lpWaveHdr = lpWaveHdr->lpNext;
766 wwo->lpLoopPtr = NULL;
767 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
769 } else {
770 /* We're not in a loop. Advance to the next wave header */
771 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
774 return lpWaveHdr;
777 /**************************************************************************
778 * wodPlayer_NotifyWait [internal]
779 * Returns the number of milliseconds to wait before attempting to notify
780 * completion of the specified wavehdr.
781 * This is based on the number of bytes remaining to be written in the
782 * wave.
784 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
786 DWORD dwMillis;
788 if(lpWaveHdr->reserved < wwo->dwPlayedTotal)
790 dwMillis = 1;
792 else
794 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->waveFormat.Format.nAvgBytesPerSec;
795 if(!dwMillis) dwMillis = 1;
798 TRACE("dwMillis = %d\n", dwMillis);
800 return dwMillis;
804 /**************************************************************************
805 * wodPlayer_WriteMaxFrags [internal]
807 * Esdlib will have set the stream socket buffer size to an appropriate
808 * value, so now our job is to keep it full. So write what we can, and
809 * return 1 if more can be written and 0 otherwise.
811 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo)
813 DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
814 int written;
815 DWORD now;
817 TRACE("Writing wavehdr %p.%u[%u]\n",
818 wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
820 written = write(wwo->stream_fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, dwLength);
821 if (written <= 0)
823 /* the esd buffer is full or some error occurred */
824 TRACE("write(%u) failed, errno=%d\n", dwLength, errno);
825 return 0;
827 now = GetTickCount();
828 TRACE("Wrote %d bytes out of %u, %ums since last\n", written, dwLength, now-wwo->dwLastWrite);
830 wwo->dwLastWrite = now;
831 wwo->dwWrittenTotal += written; /* update stats on this wave device */
832 if (written == dwLength)
834 /* We're done with this wavehdr, skip to the next one */
835 wodPlayer_PlayPtrNext(wwo);
836 return 1;
839 /* Remove the amount written and wait a bit before trying to write more */
840 wwo->dwPartialOffset += written;
841 return 0;
845 /**************************************************************************
846 * wodPlayer_NotifyCompletions [internal]
848 * Notifies and remove from queue all wavehdrs which have been played to
849 * the speaker (ie. they have cleared the audio device). If force is true,
850 * we notify all wavehdrs and remove them all from the queue even if they
851 * are unplayed or part of a loop.
853 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
855 LPWAVEHDR lpWaveHdr;
857 if (wwo->lpQueuePtr) {
858 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%d), force=(%d)\n",
859 wwo->lpQueuePtr,
860 wwo->lpPlayPtr,
861 wwo->lpLoopPtr,
862 wwo->lpQueuePtr->reserved,
863 wwo->dwWrittenTotal,
864 force);
865 } else {
866 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), dwWrittenTotal=(%d), force=(%d)\n",
867 wwo->lpQueuePtr,
868 wwo->lpPlayPtr,
869 wwo->lpLoopPtr,
870 wwo->dwWrittenTotal,
871 force);
874 /* Start from lpQueuePtr and keep notifying until:
875 * - we hit an unwritten wavehdr
876 * - we hit the beginning of a running loop
877 * - we hit a wavehdr which hasn't finished playing
879 while ((lpWaveHdr = wwo->lpQueuePtr) &&
880 (force ||
881 (lpWaveHdr != wwo->lpPlayPtr &&
882 lpWaveHdr != wwo->lpLoopPtr &&
883 lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
885 wwo->lpQueuePtr = lpWaveHdr->lpNext;
887 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
888 lpWaveHdr->dwFlags |= WHDR_DONE;
890 wodNotifyClient(wwo, WOM_DONE, (DWORD_PTR)lpWaveHdr, 0);
892 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
893 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
896 /**************************************************************************
897 * wodPlayer_Reset [internal]
899 * wodPlayer helper. Resets current output stream.
901 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
903 wodUpdatePlayedTotal(wwo);
905 wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
907 /* we aren't able to flush any data that has already been written */
908 /* to esd, otherwise we would do the flushing here */
910 if (reset) {
911 enum win_wm_message msg;
912 DWORD_PTR param;
913 HANDLE ev;
915 /* remove any buffer */
916 wodPlayer_NotifyCompletions(wwo, TRUE);
918 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
919 wwo->state = WINE_WS_STOPPED;
920 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
922 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
924 /* remove any existing message in the ring */
925 EnterCriticalSection(&wwo->msgRing.msg_crst);
927 /* return all pending headers in queue */
928 while (ESD_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
930 TRACE("flushing msg\n");
931 if (msg != WINE_WM_HEADER)
933 FIXME("shouldn't have headers left\n");
934 SetEvent(ev);
935 continue;
937 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
938 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
940 wodNotifyClient(wwo, WOM_DONE, param, 0);
942 RESET_OMR(&wwo->msgRing);
943 LeaveCriticalSection(&wwo->msgRing.msg_crst);
944 } else {
945 if (wwo->lpLoopPtr) {
946 /* complicated case, not handled yet (could imply modifying the loop counter */
947 FIXME("Pausing while in loop isn't correctly handled yet, expect strange results\n");
948 wwo->lpPlayPtr = wwo->lpLoopPtr;
949 wwo->dwPartialOffset = 0;
950 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
951 } else {
952 /* the data already written is going to be played, so take */
953 /* this fact into account here */
954 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
956 wwo->state = WINE_WS_PAUSED;
960 /**************************************************************************
961 * wodPlayer_ProcessMessages [internal]
963 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
965 LPWAVEHDR lpWaveHdr;
966 enum win_wm_message msg;
967 DWORD_PTR param;
968 HANDLE ev;
970 while (ESD_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
971 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
972 switch (msg) {
973 case WINE_WM_PAUSING:
974 wodPlayer_Reset(wwo, FALSE);
975 SetEvent(ev);
976 break;
977 case WINE_WM_RESTARTING:
978 wwo->state = WINE_WS_PLAYING;
979 SetEvent(ev);
980 break;
981 case WINE_WM_HEADER:
982 lpWaveHdr = (LPWAVEHDR)param;
984 /* insert buffer at the end of queue */
986 LPWAVEHDR* wh;
987 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
988 *wh = lpWaveHdr;
990 if (!wwo->lpPlayPtr)
991 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
992 if (wwo->state == WINE_WS_STOPPED)
993 wwo->state = WINE_WS_PLAYING;
994 break;
995 case WINE_WM_RESETTING:
996 wodPlayer_Reset(wwo, TRUE);
997 SetEvent(ev);
998 break;
999 case WINE_WM_UPDATE:
1000 wodUpdatePlayedTotal(wwo);
1001 SetEvent(ev);
1002 break;
1003 case WINE_WM_BREAKLOOP:
1004 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1005 /* ensure exit at end of current loop */
1006 wwo->dwLoops = 1;
1008 SetEvent(ev);
1009 break;
1010 case WINE_WM_CLOSING:
1011 /* sanity check: this should not happen since the device must have been reset before */
1012 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1013 wwo->hThread = 0;
1014 wwo->state = WINE_WS_CLOSED;
1015 SetEvent(ev);
1016 ExitThread(0);
1017 /* shouldn't go here */
1018 default:
1019 FIXME("unknown message %d\n", msg);
1020 break;
1025 /**************************************************************************
1026 * wodPlayer_FeedDSP [internal]
1027 * Feed as much sound data as we can into the DSP and return the number of
1028 * milliseconds before it will be necessary to feed the DSP again.
1030 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1032 wodUpdatePlayedTotal(wwo);
1034 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1035 while (wwo->lpPlayPtr)
1037 if (wwo->dwPartialOffset != 0)
1038 TRACE("feeding from partial wavehdr\n");
1039 else
1041 /* Note the value that dwPlayedTotal will return when this
1042 * wavehdr finishes playing, for the completion notifications.
1044 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1045 TRACE("new wavehdr: reserved=(%ld) dwWrittenTotal=(%d) dwBufferLength=(%d)\n",
1046 wwo->lpPlayPtr->reserved, wwo->dwWrittenTotal,
1047 wwo->lpPlayPtr->dwBufferLength);
1049 if (!wodPlayer_WriteMaxFrags(wwo))
1051 /* the buffer is full, wait a bit */
1052 return wwo->dwSleepTime;
1056 TRACE("Ran out of wavehdrs or nothing to play\n");
1057 return INFINITE;
1061 /**************************************************************************
1062 * wodPlayer [internal]
1064 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1066 WORD uDevID = (DWORD_PTR)pmt;
1067 WINE_WAVEOUT* wwo = &WOutDev[uDevID];
1068 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1069 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1070 DWORD dwSleepTime;
1072 wwo->state = WINE_WS_STOPPED;
1073 SetEvent(wwo->hStartUpEvent);
1075 for (;;) {
1076 /** Wait for the shortest time before an action is required. If there
1077 * are no pending actions, wait forever for a command.
1079 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1080 TRACE("waiting %ums (%u,%u)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1081 WAIT_OMR(&wwo->msgRing, dwSleepTime);
1082 wodPlayer_ProcessMessages(wwo);
1083 if (wwo->state == WINE_WS_PLAYING) {
1084 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1085 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1086 } else {
1087 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1091 return 0;
1094 /**************************************************************************
1095 * wodGetDevCaps [internal]
1097 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1099 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1101 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1103 if (wDevID >= MAX_WAVEOUTDRV) {
1104 TRACE("MAX_WAVOUTDRV reached !\n");
1105 return MMSYSERR_BADDEVICEID;
1108 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1109 return MMSYSERR_NOERROR;
1112 /**************************************************************************
1113 * wodOpen [internal]
1115 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1117 WINE_WAVEOUT* wwo;
1118 /* output to esound... */
1119 int out_bits = ESD_BITS8, out_channels = ESD_MONO, out_rate;
1120 int out_mode = ESD_STREAM, out_func = ESD_PLAY;
1121 esd_format_t out_format;
1122 int mode;
1124 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1125 if (lpDesc == NULL) {
1126 WARN("Invalid Parameter !\n");
1127 return MMSYSERR_INVALPARAM;
1129 if (wDevID >= MAX_WAVEOUTDRV) {
1130 TRACE("MAX_WAVOUTDRV reached !\n");
1131 return MMSYSERR_BADDEVICEID;
1134 /* if this device is already open tell the app that it is allocated */
1135 if(WOutDev[wDevID].stream_fd != -1)
1137 TRACE("device already allocated\n");
1138 return MMSYSERR_ALLOCATED;
1141 /* only PCM format is supported so far... */
1142 if (!supportedFormat(lpDesc->lpFormat)) {
1143 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1144 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1145 lpDesc->lpFormat->nSamplesPerSec);
1146 return WAVERR_BADFORMAT;
1149 if (dwFlags & WAVE_FORMAT_QUERY) {
1150 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1151 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1152 lpDesc->lpFormat->nSamplesPerSec);
1153 return MMSYSERR_NOERROR;
1156 wwo = &WOutDev[wDevID];
1158 /* direct sound not supported, ignore the flag */
1159 dwFlags &= ~WAVE_DIRECTSOUND;
1161 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1163 wwo->waveDesc = *lpDesc;
1164 copy_format(lpDesc->lpFormat, &wwo->waveFormat);
1166 if (wwo->waveFormat.Format.wBitsPerSample == 0) {
1167 WARN("Resetting zeroed wBitsPerSample\n");
1168 wwo->waveFormat.Format.wBitsPerSample = 8 *
1169 (wwo->waveFormat.Format.nAvgBytesPerSec /
1170 wwo->waveFormat.Format.nSamplesPerSec) /
1171 wwo->waveFormat.Format.nChannels;
1174 if (wwo->waveFormat.Format.wBitsPerSample == 8)
1175 out_bits = ESD_BITS8;
1176 else if (wwo->waveFormat.Format.wBitsPerSample == 16)
1177 out_bits = ESD_BITS16;
1179 if (wwo->waveFormat.Format.nChannels == 1)
1180 out_channels = ESD_MONO;
1181 else if (wwo->waveFormat.Format.nChannels == 2)
1182 out_channels = ESD_STEREO;
1184 out_format = out_bits | out_channels | out_mode | out_func;
1185 out_rate = (int) wwo->waveFormat.Format.nSamplesPerSec;
1186 TRACE("esd output format = 0x%08x, rate = %d\n", out_format, out_rate);
1188 wwo->stream_name = get_stream_name("out", wDevID);
1189 wwo->stream_fd = esd_play_stream(out_format, out_rate, NULL, wwo->stream_name);
1190 TRACE("wwo->stream_fd=%d\n", wwo->stream_fd);
1191 if(wwo->stream_fd < 0)
1193 HeapFree(GetProcessHeap(), 0, wwo->stream_name);
1194 return MMSYSERR_ALLOCATED;
1197 wwo->stream_id = 0;
1198 wwo->dwPlayedTotal = 0;
1199 wwo->dwWrittenTotal = 0;
1201 wwo->esd_fd = esd_open_sound(NULL);
1202 if (wwo->esd_fd >= 0)
1204 wwo->dwLatency = 1000 * esd_get_latency(wwo->esd_fd) * 4 / wwo->waveFormat.Format.nAvgBytesPerSec;
1206 else
1208 WARN("esd_open_sound() failed\n");
1209 /* just do a rough guess at the latency and continue anyway */
1210 wwo->dwLatency = 1000 * (2 * ESD_BUF_SIZE) / out_rate;
1212 TRACE("dwLatency = %ums\n", wwo->dwLatency);
1214 /* ESD_BUF_SIZE is the socket buffer size in samples. Set dwSleepTime
1215 * to a fraction of that so it never get empty.
1217 wwo->dwSleepTime = 1000 * ESD_BUF_SIZE / out_rate / 3;
1219 /* Set the stream socket to O_NONBLOCK, so we can stop playing smoothly */
1220 mode = fcntl(wwo->stream_fd, F_GETFL);
1221 mode |= O_NONBLOCK;
1222 fcntl(wwo->stream_fd, F_SETFL, mode);
1224 ESD_InitRingMessage(&wwo->msgRing);
1226 /* create player thread */
1227 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1228 wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1229 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD_PTR)wDevID,
1230 0, NULL);
1231 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1232 CloseHandle(wwo->hStartUpEvent);
1233 } else {
1234 wwo->hThread = INVALID_HANDLE_VALUE;
1236 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1238 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
1239 wwo->waveFormat.Format.wBitsPerSample, wwo->waveFormat.Format.nAvgBytesPerSec,
1240 wwo->waveFormat.Format.nSamplesPerSec, wwo->waveFormat.Format.nChannels,
1241 wwo->waveFormat.Format.nBlockAlign);
1243 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1246 /**************************************************************************
1247 * wodClose [internal]
1249 static DWORD wodClose(WORD wDevID)
1251 DWORD ret = MMSYSERR_NOERROR;
1252 WINE_WAVEOUT* wwo;
1254 TRACE("(%u);\n", wDevID);
1256 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1257 WARN("bad device ID !\n");
1258 return MMSYSERR_BADDEVICEID;
1261 wwo = &WOutDev[wDevID];
1262 if (wwo->lpQueuePtr) {
1263 WARN("buffers still playing !\n");
1264 ret = WAVERR_STILLPLAYING;
1265 } else {
1266 TRACE("imhere[3-close]\n");
1267 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1268 ESD_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1271 ESD_DestroyRingMessage(&wwo->msgRing);
1273 ESD_CloseWaveOutDevice(wwo); /* close the stream and clean things up */
1275 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1277 return ret;
1280 /**************************************************************************
1281 * wodWrite [internal]
1284 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1286 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1288 /* first, do the sanity checks... */
1289 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1290 WARN("bad dev ID !\n");
1291 return MMSYSERR_BADDEVICEID;
1294 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1296 TRACE("unprepared\n");
1297 return WAVERR_UNPREPARED;
1300 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1302 TRACE("still playing\n");
1303 return WAVERR_STILLPLAYING;
1306 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1307 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1308 lpWaveHdr->lpNext = 0;
1310 TRACE("adding ring message\n");
1311 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER,
1312 (DWORD_PTR)lpWaveHdr, FALSE);
1314 return MMSYSERR_NOERROR;
1317 /**************************************************************************
1318 * wodPause [internal]
1320 static DWORD wodPause(WORD wDevID)
1322 TRACE("(%u);!\n", wDevID);
1324 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1325 WARN("bad device ID !\n");
1326 return MMSYSERR_BADDEVICEID;
1329 TRACE("imhere[3-PAUSING]\n");
1330 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1332 return MMSYSERR_NOERROR;
1335 /**************************************************************************
1336 * wodRestart [internal]
1338 static DWORD wodRestart(WORD wDevID)
1340 TRACE("(%u);\n", wDevID);
1342 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1343 WARN("bad device ID !\n");
1344 return MMSYSERR_BADDEVICEID;
1347 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1348 TRACE("imhere[3-RESTARTING]\n");
1349 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1352 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1353 /* FIXME: Myst crashes with this ... hmm -MM
1354 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1357 return MMSYSERR_NOERROR;
1360 /**************************************************************************
1361 * wodReset [internal]
1363 static DWORD wodReset(WORD wDevID)
1365 TRACE("(%u);\n", wDevID);
1367 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1368 WARN("bad device ID !\n");
1369 return MMSYSERR_BADDEVICEID;
1372 TRACE("imhere[3-RESET]\n");
1373 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1375 return MMSYSERR_NOERROR;
1378 /**************************************************************************
1379 * wodGetPosition [internal]
1381 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1383 WINE_WAVEOUT* wwo;
1385 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1387 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1388 WARN("bad device ID !\n");
1389 return MMSYSERR_BADDEVICEID;
1392 if (lpTime == NULL) {
1393 WARN("invalid parameter: lpTime == NULL\n");
1394 return MMSYSERR_INVALPARAM;
1397 wwo = &WOutDev[wDevID];
1398 ESD_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1400 return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->waveFormat);
1403 /**************************************************************************
1404 * wodBreakLoop [internal]
1406 static DWORD wodBreakLoop(WORD wDevID)
1408 TRACE("(%u);\n", wDevID);
1410 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1) {
1411 WARN("bad device ID !\n");
1412 return MMSYSERR_BADDEVICEID;
1414 ESD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1415 return MMSYSERR_NOERROR;
1418 static esd_player_info_t* wod_get_player(WINE_WAVEOUT* wwo, esd_info_t** esd_all_info)
1420 esd_player_info_t* player;
1422 if (wwo->esd_fd == -1)
1424 wwo->esd_fd = esd_open_sound(NULL);
1425 if (wwo->esd_fd < 0)
1427 WARN("esd_open_sound() failed (%d)\n", errno);
1428 *esd_all_info = NULL;
1429 return NULL;
1433 *esd_all_info = esd_get_all_info(wwo->esd_fd);
1434 if (!*esd_all_info)
1436 WARN("esd_get_all_info() failed (%d)\n", errno);
1437 return NULL;
1440 for (player = (*esd_all_info)->player_list; player != NULL; player = player->next)
1442 if (strcmp(player->name, wwo->stream_name) == 0)
1444 wwo->stream_id = player->source_id;
1445 return player;
1449 return NULL;
1452 /**************************************************************************
1453 * wodGetVolume [internal]
1455 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1457 esd_info_t* esd_all_info;
1458 esd_player_info_t* player;
1459 DWORD ret;
1461 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].stream_fd == -1)
1463 WARN("bad device ID !\n");
1464 return MMSYSERR_BADDEVICEID;
1467 ret = MMSYSERR_ERROR;
1468 player = wod_get_player(WOutDev+wDevID, &esd_all_info);
1469 if (player)
1471 DWORD left, right;
1472 left = (player->left_vol_scale * 0xFFFF) / ESD_VOLUME_BASE;
1473 right = (player->right_vol_scale * 0xFFFF) / ESD_VOLUME_BASE;
1474 TRACE("volume = %u / %u\n", left, right);
1475 *lpdwVol = left + (right << 16);
1476 ret = MMSYSERR_NOERROR;
1479 if (esd_all_info)
1480 esd_free_all_info(esd_all_info);
1481 return ret;
1484 /**************************************************************************
1485 * wodSetVolume [internal]
1487 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1489 WINE_WAVEOUT* wwo = WOutDev+wDevID;
1491 if (wDevID >= MAX_WAVEOUTDRV || wwo->stream_fd == -1)
1493 WARN("bad device ID !\n");
1494 return MMSYSERR_BADDEVICEID;
1497 /* stream_id is the ESD's file descriptor for our stream so it's should
1498 * be non-zero if set.
1500 if (!wwo->stream_id)
1502 esd_info_t* esd_all_info;
1503 /* wod_get_player sets the stream_id as a side effect */
1504 wod_get_player(wwo, &esd_all_info);
1505 if (esd_all_info)
1506 esd_free_all_info(esd_all_info);
1508 if (!wwo->stream_id)
1509 return MMSYSERR_ERROR;
1511 esd_set_stream_pan(wwo->esd_fd, wwo->stream_id,
1512 LOWORD(dwParam) * ESD_VOLUME_BASE / 0xFFFF,
1513 HIWORD(dwParam) * ESD_VOLUME_BASE / 0xFFFF);
1515 return MMSYSERR_NOERROR;
1518 /**************************************************************************
1519 * wodGetNumDevs [internal]
1521 static DWORD wodGetNumDevs(void)
1523 return MAX_WAVEOUTDRV;
1526 /**************************************************************************
1527 * wodDevInterfaceSize [internal]
1529 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1531 TRACE("(%u, %p)\n", wDevID, dwParam1);
1533 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1534 NULL, 0 ) * sizeof(WCHAR);
1535 return MMSYSERR_NOERROR;
1538 /**************************************************************************
1539 * wodDevInterface [internal]
1541 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1543 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1544 NULL, 0 ) * sizeof(WCHAR))
1546 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1547 dwParam1, dwParam2 / sizeof(WCHAR));
1548 return MMSYSERR_NOERROR;
1550 return MMSYSERR_INVALPARAM;
1553 /**************************************************************************
1554 * wodMessage (WINEESD.@)
1556 DWORD WINAPI ESD_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1557 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1559 TRACE("(%u, %04X, %08X, %08lX, %08lX);\n",
1560 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1562 switch (wMsg) {
1563 case DRVM_INIT:
1564 return ESD_WaveInit();
1565 case DRVM_EXIT:
1566 return ESD_WaveClose();
1567 case DRVM_ENABLE:
1568 case DRVM_DISABLE:
1569 /* FIXME: Pretend this is supported */
1570 return 0;
1571 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1572 case WODM_CLOSE: return wodClose (wDevID);
1573 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1574 case WODM_PAUSE: return wodPause (wDevID);
1575 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1576 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
1577 case WODM_PREPARE: return MMSYSERR_NOTSUPPORTED;
1578 case WODM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
1579 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
1580 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1581 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1582 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1583 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1584 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1585 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1586 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1587 case WODM_RESTART: return wodRestart (wDevID);
1588 case WODM_RESET: return wodReset (wDevID);
1590 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1591 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1592 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
1593 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
1594 default:
1595 FIXME("unknown message %d!\n", wMsg);
1597 return MMSYSERR_NOTSUPPORTED;
1600 /*======================================================================*
1601 * Low level WAVE IN implementation *
1602 *======================================================================*/
1604 /**************************************************************************
1605 * widGetNumDevs [internal]
1607 static DWORD widGetNumDevs(void)
1609 TRACE("%d\n", MAX_WAVEINDRV);
1610 return MAX_WAVEINDRV;
1613 /**************************************************************************
1614 * widDevInterfaceSize [internal]
1616 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1618 TRACE("(%u, %p)\n", wDevID, dwParam1);
1621 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1622 NULL, 0 ) * sizeof(WCHAR);
1623 return MMSYSERR_NOERROR;
1626 /**************************************************************************
1627 * widDevInterface [internal]
1629 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1631 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1632 NULL, 0 ) * sizeof(WCHAR))
1634 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1635 dwParam1, dwParam2 / sizeof(WCHAR));
1636 return MMSYSERR_NOERROR;
1638 return MMSYSERR_INVALPARAM;
1641 /**************************************************************************
1642 * widNotifyClient [internal]
1644 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD_PTR dwParam1,
1645 DWORD_PTR dwParam2)
1647 TRACE("wMsg = 0x%04x dwParm1 = %08lX dwParam2 = %08lX\n", wMsg, dwParam1, dwParam2);
1649 switch (wMsg) {
1650 case WIM_OPEN:
1651 case WIM_CLOSE:
1652 case WIM_DATA:
1653 if (wwi->wFlags != DCB_NULL &&
1654 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1655 (HDRVR)wwi->waveDesc.hWave, wMsg,
1656 wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
1657 WARN("can't notify client !\n");
1658 return MMSYSERR_ERROR;
1660 break;
1661 default:
1662 FIXME("Unknown callback message %u\n", wMsg);
1663 return MMSYSERR_INVALPARAM;
1665 return MMSYSERR_NOERROR;
1668 /**************************************************************************
1669 * widGetDevCaps [internal]
1671 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1673 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1675 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1677 if (wDevID >= MAX_WAVEINDRV) {
1678 TRACE("MAX_WAVINDRV reached !\n");
1679 return MMSYSERR_BADDEVICEID;
1682 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1683 return MMSYSERR_NOERROR;
1686 /**************************************************************************
1687 * widRecorder [internal]
1689 static DWORD CALLBACK widRecorder(LPVOID pmt)
1691 WORD uDevID = (DWORD_PTR)pmt;
1692 WINE_WAVEIN* wwi = &WInDev[uDevID];
1693 WAVEHDR* lpWaveHdr;
1694 DWORD dwSleepTime;
1695 int bytesRead;
1696 enum win_wm_message msg;
1697 DWORD_PTR param;
1698 HANDLE ev;
1700 SetEvent(wwi->hStartUpEvent);
1702 /* make sleep time to be # of ms to record one packet */
1703 dwSleepTime = (1024 * 1000) / wwi->waveFormat.Format.nAvgBytesPerSec;
1704 TRACE("sleeptime=%d ms\n", dwSleepTime);
1706 for(;;) {
1707 TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d)\n",wwi->lpQueuePtr,wwi->state);
1709 /* read all data is esd input buffer. */
1710 if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING))
1712 lpWaveHdr = wwi->lpQueuePtr;
1714 TRACE("read as much as we can\n");
1715 while(wwi->lpQueuePtr)
1717 TRACE("attempt to read %d bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1718 bytesRead = read(wwi->stream_fd,
1719 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1720 lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1721 TRACE("bytesRead=%d\n",bytesRead);
1722 if (bytesRead <= 0) break; /* So we can stop recording smoothly */
1724 lpWaveHdr->dwBytesRecorded += bytesRead;
1725 wwi->dwRecordedTotal += bytesRead;
1727 /* buffer full. notify client */
1728 if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
1730 /* must copy the value of next waveHdr, because we have no idea of what
1731 * will be done with the content of lpWaveHdr in callback
1733 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1735 TRACE("waveHdr full.\n");
1737 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1738 lpWaveHdr->dwFlags |= WHDR_DONE;
1740 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
1741 lpWaveHdr = wwi->lpQueuePtr = lpNext;
1746 /* wait for dwSleepTime or an event in thread's queue */
1747 WAIT_OMR(&wwi->msgRing, dwSleepTime);
1749 while (ESD_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
1751 TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
1752 switch(msg) {
1753 case WINE_WM_PAUSING:
1754 wwi->state = WINE_WS_PAUSED;
1756 /* Put code here to "pause" esd recording
1759 SetEvent(ev);
1760 break;
1761 case WINE_WM_STARTING:
1762 wwi->state = WINE_WS_PLAYING;
1764 /* Put code here to "start" esd recording
1767 SetEvent(ev);
1768 break;
1769 case WINE_WM_HEADER:
1770 lpWaveHdr = (LPWAVEHDR)param;
1771 /* insert buffer at end of queue */
1773 LPWAVEHDR* wh;
1774 int num_headers = 0;
1775 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1777 num_headers++;
1780 *wh=lpWaveHdr;
1782 break;
1783 case WINE_WM_STOPPING:
1784 if (wwi->state != WINE_WS_STOPPED)
1787 /* Put code here to "stop" esd recording
1790 /* return current buffer to app */
1791 lpWaveHdr = wwi->lpQueuePtr;
1792 if (lpWaveHdr)
1794 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1795 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1796 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1797 lpWaveHdr->dwFlags |= WHDR_DONE;
1798 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
1799 wwi->lpQueuePtr = lpNext;
1802 wwi->state = WINE_WS_STOPPED;
1803 SetEvent(ev);
1804 break;
1805 case WINE_WM_RESETTING:
1806 wwi->state = WINE_WS_STOPPED;
1807 wwi->dwRecordedTotal = 0;
1809 /* return all buffers to the app */
1810 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1811 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1812 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1813 lpWaveHdr->dwFlags |= WHDR_DONE;
1815 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
1817 wwi->lpQueuePtr = NULL;
1818 SetEvent(ev);
1819 break;
1820 case WINE_WM_CLOSING:
1821 wwi->hThread = 0;
1822 wwi->state = WINE_WS_CLOSED;
1823 SetEvent(ev);
1824 ExitThread(0);
1825 /* shouldn't go here */
1826 default:
1827 FIXME("unknown message %d\n", msg);
1828 break;
1832 ExitThread(0);
1833 /* just for not generating compilation warnings... should never be executed */
1834 return 0;
1837 /**************************************************************************
1838 * widOpen [internal]
1840 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1842 WINE_WAVEIN* wwi;
1843 /* input esound... */
1844 int in_bits = ESD_BITS16, in_channels = ESD_STEREO, in_rate;
1845 #ifdef WID_USE_ESDMON
1846 int in_mode = ESD_STREAM, in_func = ESD_PLAY;
1847 #else
1848 int in_mode = ESD_STREAM, in_func = ESD_RECORD;
1849 #endif
1850 esd_format_t in_format;
1851 int mode;
1853 TRACE("(%u, %p %08X);\n",wDevID, lpDesc, dwFlags);
1854 if (lpDesc == NULL) {
1855 WARN("Invalid Parametr (lpDesc == NULL)!\n");
1856 return MMSYSERR_INVALPARAM;
1859 if (wDevID >= MAX_WAVEINDRV) {
1860 TRACE ("MAX_WAVEINDRV reached !\n");
1861 return MMSYSERR_BADDEVICEID;
1864 /* if this device is already open tell the app that it is allocated */
1865 if(WInDev[wDevID].stream_fd != -1)
1867 TRACE("device already allocated\n");
1868 return MMSYSERR_ALLOCATED;
1871 /* only PCM format is support so far... */
1872 if (!supportedFormat(lpDesc->lpFormat)) {
1873 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1874 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1875 lpDesc->lpFormat->nSamplesPerSec);
1876 return WAVERR_BADFORMAT;
1879 if (dwFlags & WAVE_FORMAT_QUERY) {
1880 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1881 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1882 lpDesc->lpFormat->nSamplesPerSec);
1883 return MMSYSERR_NOERROR;
1886 wwi = &WInDev[wDevID];
1888 /* direct sound not supported, ignore the flag */
1889 dwFlags &= ~WAVE_DIRECTSOUND;
1891 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1893 wwi->waveDesc = *lpDesc;
1894 copy_format(lpDesc->lpFormat, &wwi->waveFormat);
1896 if (wwi->waveFormat.Format.wBitsPerSample == 0) {
1897 WARN("Resetting zerod wBitsPerSample\n");
1898 wwi->waveFormat.Format.wBitsPerSample = 8 *
1899 (wwi->waveFormat.Format.nAvgBytesPerSec /
1900 wwi->waveFormat.Format.nSamplesPerSec) /
1901 wwi->waveFormat.Format.nChannels;
1904 if (wwi->waveFormat.Format.wBitsPerSample == 8)
1905 in_bits = ESD_BITS8;
1906 else if (wwi->waveFormat.Format.wBitsPerSample == 16)
1907 in_bits = ESD_BITS16;
1909 if (wwi->waveFormat.Format.nChannels == 1)
1910 in_channels = ESD_MONO;
1911 else if (wwi->waveFormat.Format.nChannels == 2)
1912 in_channels = ESD_STEREO;
1914 in_format = in_bits | in_channels | in_mode | in_func;
1915 in_rate = (int) wwi->waveFormat.Format.nSamplesPerSec;
1916 TRACE("esd input format = 0x%08x, rate = %d\n", in_format, in_rate);
1918 wwi->stream_name = get_stream_name("in", wDevID);
1919 #ifdef WID_USE_ESDMON
1920 wwi->stream_fd = esd_monitor_stream(in_format, in_rate, NULL, wwi->stream_name);
1921 #else
1922 wwi->stream_fd = esd_record_stream(in_format, in_rate, NULL, wwi->stream_name);
1923 #endif
1924 TRACE("wwi->stream_fd=%d\n",wwi->stream_fd);
1925 if(wwi->stream_fd < 0)
1927 HeapFree(GetProcessHeap(), 0, wwi->stream_name);
1928 return MMSYSERR_ALLOCATED;
1930 wwi->state = WINE_WS_STOPPED;
1932 if (wwi->lpQueuePtr) {
1933 WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1934 wwi->lpQueuePtr = NULL;
1937 /* Set the socket to O_NONBLOCK, so we can stop recording smoothly */
1938 mode = fcntl(wwi->stream_fd, F_GETFL);
1939 mode |= O_NONBLOCK;
1940 fcntl(wwi->stream_fd, F_SETFL, mode);
1942 wwi->dwRecordedTotal = 0;
1943 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1945 ESD_InitRingMessage(&wwi->msgRing);
1947 /* create recorder thread */
1948 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1949 wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1950 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD_PTR)wDevID,
1951 0, NULL);
1952 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
1953 CloseHandle(wwi->hStartUpEvent);
1954 } else {
1955 wwi->hThread = INVALID_HANDLE_VALUE;
1957 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
1959 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
1960 wwi->waveFormat.Format.wBitsPerSample, wwi->waveFormat.Format.nAvgBytesPerSec,
1961 wwi->waveFormat.Format.nSamplesPerSec, wwi->waveFormat.Format.nChannels,
1962 wwi->waveFormat.Format.nBlockAlign);
1963 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1966 /**************************************************************************
1967 * widClose [internal]
1969 static DWORD widClose(WORD wDevID)
1971 WINE_WAVEIN* wwi;
1973 TRACE("(%u);\n", wDevID);
1974 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1975 WARN("can't close !\n");
1976 return MMSYSERR_INVALHANDLE;
1979 wwi = &WInDev[wDevID];
1981 if (wwi->lpQueuePtr != NULL) {
1982 WARN("still buffers open !\n");
1983 return WAVERR_STILLPLAYING;
1986 ESD_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
1987 ESD_CloseWaveInDevice(wwi);
1988 wwi->state = WINE_WS_CLOSED;
1989 ESD_DestroyRingMessage(&wwi->msgRing);
1990 return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1993 /**************************************************************************
1994 * widAddBuffer [internal]
1996 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1998 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
2000 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2001 WARN("can't do it !\n");
2002 return MMSYSERR_INVALHANDLE;
2004 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
2005 TRACE("never been prepared !\n");
2006 return WAVERR_UNPREPARED;
2008 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
2009 TRACE("header already in use !\n");
2010 return WAVERR_STILLPLAYING;
2013 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2014 lpWaveHdr->dwFlags &= ~WHDR_DONE;
2015 lpWaveHdr->dwBytesRecorded = 0;
2016 lpWaveHdr->lpNext = NULL;
2018 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER,
2019 (DWORD_PTR)lpWaveHdr, FALSE);
2020 return MMSYSERR_NOERROR;
2023 /**************************************************************************
2024 * widStart [internal]
2026 static DWORD widStart(WORD wDevID)
2028 TRACE("(%u);\n", wDevID);
2029 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2030 WARN("can't start recording !\n");
2031 return MMSYSERR_INVALHANDLE;
2034 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
2035 return MMSYSERR_NOERROR;
2038 /**************************************************************************
2039 * widStop [internal]
2041 static DWORD widStop(WORD wDevID)
2043 TRACE("(%u);\n", wDevID);
2044 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2045 WARN("can't stop !\n");
2046 return MMSYSERR_INVALHANDLE;
2049 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
2051 return MMSYSERR_NOERROR;
2054 /**************************************************************************
2055 * widReset [internal]
2057 static DWORD widReset(WORD wDevID)
2059 TRACE("(%u);\n", wDevID);
2060 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2061 WARN("can't reset !\n");
2062 return MMSYSERR_INVALHANDLE;
2064 ESD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2065 return MMSYSERR_NOERROR;
2068 /**************************************************************************
2069 * widMessage (WINEESD.6)
2071 DWORD WINAPI ESD_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2072 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2074 TRACE("(%u, %04X, %08X, %08lX, %08lX);\n",
2075 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2076 switch (wMsg) {
2077 case DRVM_INIT:
2078 return ESD_WaveInit();
2079 case DRVM_EXIT:
2080 return ESD_WaveClose();
2081 case DRVM_ENABLE:
2082 case DRVM_DISABLE:
2083 /* FIXME: Pretend this is supported */
2084 return 0;
2085 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2086 case WIDM_CLOSE: return widClose (wDevID);
2087 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2088 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
2089 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
2090 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
2091 case WIDM_GETNUMDEVS: return widGetNumDevs ();
2092 case WIDM_RESET: return widReset (wDevID);
2093 case WIDM_START: return widStart (wDevID);
2094 case WIDM_STOP: return widStop (wDevID);
2095 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2096 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2097 default:
2098 FIXME("unknown message %d!\n", wMsg);
2100 return MMSYSERR_NOTSUPPORTED;
2103 #else /* !HAVE_ESD */
2105 /**************************************************************************
2106 * wodMessage (WINEESD.@)
2108 DWORD WINAPI ESD_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2109 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2111 FIXME("(%u, %04X, %08X, %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_PTR dwParam1, DWORD_PTR dwParam2)
2121 FIXME("(%u, %04X, %08X, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2122 return MMSYSERR_NOTENABLED;
2125 #endif /* HAVE_ESD */