Added a dynamically growing ring buffer for oss, alsa, arts, and nas.
[wine] / dlls / winmm / winealsa / audio_05.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Sample Wine Driver for Advanced Linux Sound System (ALSA)
4  *      Based on version 0.5 of the ALSA API
5  *
6  * Copyright    2002 Eric Pouech
7  *              2002 David Hammerton
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <errno.h>
33 #include <fcntl.h>
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
36 #endif
37 #ifdef HAVE_SYS_MMAN_H
38 # include <sys/mman.h>
39 #endif
40 #include "winbase.h"
41 #include "windef.h"
42 #include "wingdi.h"
43 #include "winerror.h"
44 #include "winuser.h"
45 #include "mmddk.h"
46 #include "dsound.h"
47 #include "dsdriver.h"
48 #include "alsa.h"
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(wave);
52
53
54 #if defined(HAVE_ALSA) && (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)
55
56 #define MAX_WAVEOUTDRV  (1)
57 #define MAX_WAVEINDRV   (1)
58
59 /* state diagram for waveOut writing:
60  *
61  * +---------+-------------+---------------+---------------------------------+
62  * |  state  |  function   |     event     |            new state            |
63  * +---------+-------------+---------------+---------------------------------+
64  * |         | open()      |               | STOPPED                         |
65  * | PAUSED  | write()     |               | PAUSED                          |
66  * | STOPPED | write()     | <thrd create> | PLAYING                         |
67  * | PLAYING | write()     | HEADER        | PLAYING                         |
68  * | (other) | write()     | <error>       |                                 |
69  * | (any)   | pause()     | PAUSING       | PAUSED                          |
70  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
71  * | (any)   | reset()     | RESETTING     | STOPPED                         |
72  * | (any)   | close()     | CLOSING       | CLOSED                          |
73  * +---------+-------------+---------------+---------------------------------+
74  */
75
76 /* states of the playing device */
77 #define WINE_WS_PLAYING         0
78 #define WINE_WS_PAUSED          1
79 #define WINE_WS_STOPPED         2
80 #define WINE_WS_CLOSED          3
81
82 /* events to be send to device */
83 enum win_wm_message {
84     WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
85     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
86 };
87
88 typedef struct {
89     enum win_wm_message         msg;    /* message identifier */
90     DWORD                       param;  /* parameter for this message */
91     HANDLE                      hEvent; /* if message is synchronous, handle of event for synchro */
92 } ALSA_MSG;
93
94 /* implement an in-process message ring for better performance
95  * (compared to passing thru the server)
96  * this ring will be used by the input (resp output) record (resp playback) routine
97  */
98 typedef struct {
99     /* FIXME: this could be made a dynamically growing array (if needed) */
100 #define ALSA_RING_BUFFER_SIZE   30
101     ALSA_MSG                    messages[ALSA_RING_BUFFER_SIZE];
102     int                         msg_tosave;
103     int                         msg_toget;
104     HANDLE                      msg_event;
105     CRITICAL_SECTION            msg_crst;
106 } ALSA_MSG_RING;
107
108 typedef struct {
109     /* Windows information */
110     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
111     WAVEOPENDESC                waveDesc;
112     WORD                        wFlags;
113     PCMWAVEFORMAT               format;
114     WAVEOUTCAPSA                caps;
115
116     /* ALSA information */
117     snd_pcm_t*                  handle;                 /* handle to ALSA device */
118     DWORD                       dwFragmentSize;         /* size of ALSA buffer fragment */
119     DWORD                       dwBufferSize;           /* size of whole ALSA buffer in bytes */
120     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
121     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
122     DWORD                       dwPartialOffset;        /* Offset of not yet written bytes in lpPlayPtr */
123
124     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
125     DWORD                       dwLoops;                /* private copy of loop counter */
126
127     DWORD                       dwPlayedTotal;          /* number of bytes actually played since opening */
128     DWORD                       dwWrittenTotal;         /* number of bytes written to ALSA buffer since opening */
129
130     /* synchronization stuff */
131     HANDLE                      hStartUpEvent;
132     HANDLE                      hThread;
133     DWORD                       dwThreadID;
134     ALSA_MSG_RING               msgRing;
135
136     /* DirectSound stuff */
137     void*                       mmap_buffer;
138     snd_pcm_mmap_control_t*     mmap_control;
139     unsigned                    mmap_block_size;
140     unsigned                    mmap_block_number;
141 } WINE_WAVEOUT;
142
143 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
144 static DWORD            ALSA_WodNumDevs;
145
146 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
147
148 /* These strings used only for tracing */
149 static const char *wodPlayerCmdString[] = {
150     "WINE_WM_PAUSING",
151     "WINE_WM_RESTARTING",
152     "WINE_WM_RESETTING",
153     "WINE_WM_HEADER",
154     "WINE_WM_UPDATE",
155     "WINE_WM_BREAKLOOP",
156     "WINE_WM_CLOSING",
157 };
158
159 /*======================================================================*
160  *                  Low level WAVE implementation                       *
161  *======================================================================*/
162
163 /******************************************************************
164  *              ALSA_WaveInit
165  *
166  * Initialize internal structures from ALSA information
167  */
168 LONG ALSA_WaveInit(void)
169 {
170     snd_pcm_t*                  h;
171     snd_pcm_info_t              info;
172     snd_pcm_channel_info_t      chn_info;
173
174     TRACE("There are %d cards\n", snd_cards());
175
176     ALSA_WodNumDevs = 0;
177     if (snd_pcm_open(&h, 0, 0, SND_PCM_OPEN_DUPLEX|SND_PCM_OPEN_NONBLOCK))
178     {
179         ERR("Error open: %s\n", snd_strerror(errno));
180         return -1;
181     }
182     if (snd_pcm_info(h, &info))
183     {
184         ERR("Error info: %s\n", snd_strerror(errno));
185         return -1;
186     }
187     ALSA_WodNumDevs++;
188     TRACE("type=%u, flags=%s%s%s name=%s #pb=%d cp=%d\n",
189           info.type, (info.flags & SND_PCM_INFO_PLAYBACK) ? "playback " : "",
190           (info.flags & SND_PCM_INFO_PLAYBACK) ? "capture " : "",
191           (info.flags & SND_PCM_INFO_DUPLEX) ? "duplex " : "",
192           info.name, info.playback, info.capture);
193     memset(&chn_info, 0, sizeof(chn_info));
194     if (snd_pcm_channel_info(h, &chn_info))
195     {
196         ERR("Error chn info: %s\n", snd_strerror(errno));
197         return -1;
198     }
199 #define X(f,s) ((chn_info.flags & (f)) ? #s " " : "")
200 #define Y(f,s) ((chn_info.rates & (f)) ? #s " " : "")
201     TRACE("subdevice=%d name=%s chn=%d mode=%d\n"
202           "\tflags=%s%s%s%s%s%s%s%s%s%s%s\n"
203           "\tfmts=%u rates=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
204           "\trates=[%d,%d] voices=[%d,%d] buf_size=%d fg_size=[%d,%d] fg_align=%u\n",
205           chn_info.subdevice, chn_info.subname, chn_info.channel,
206           chn_info.mode,
207           X(SND_PCM_CHNINFO_MMAP,MMAP),
208           X(SND_PCM_CHNINFO_STREAM,STREAM),
209           X(SND_PCM_CHNINFO_BLOCK,BLOCK),
210           X(SND_PCM_CHNINFO_BATCH,BATCH),
211           X(SND_PCM_CHNINFO_INTERLEAVE,INTERLEAVE),
212           X(SND_PCM_CHNINFO_NONINTERLEAVE,NONINTERLEAVE),
213           X(SND_PCM_CHNINFO_BLOCK_TRANSFER,BLOCK_TRANSFER),
214           X(SND_PCM_CHNINFO_OVERRANGE,OVERRANGE),
215           X(SND_PCM_CHNINFO_MMAP_VALID,MMAP_VALID),
216           X(SND_PCM_CHNINFO_PAUSE,PAUSE),
217           X(SND_PCM_CHNINFO_GLOBAL_PARAMS,GLOBAL_PARAMS),
218           chn_info.formats,
219           Y(SND_PCM_RATE_CONTINUOUS,CONTINUOUS),
220           Y(SND_PCM_RATE_KNOT,KNOT),
221           Y(SND_PCM_RATE_8000,8000),
222           Y(SND_PCM_RATE_11025,11025),
223           Y(SND_PCM_RATE_16000,16000),
224           Y(SND_PCM_RATE_22050,22050),
225           Y(SND_PCM_RATE_32000,32000),
226           Y(SND_PCM_RATE_44100,44100),
227           Y(SND_PCM_RATE_48000,48000),
228           Y(SND_PCM_RATE_88200,88200),
229           Y(SND_PCM_RATE_96000,96000),
230           Y(SND_PCM_RATE_176400,176400),
231           Y(SND_PCM_RATE_192000,192000),
232           chn_info.min_rate, chn_info.max_rate,
233           chn_info.min_voices, chn_info.max_voices,
234           chn_info.buffer_size,
235           chn_info.min_fragment_size, chn_info.max_fragment_size,
236           chn_info.fragment_align);
237 #undef X
238 #undef Y
239
240     /* FIXME: use better values */
241     WOutDev[0].caps.wMid = 0x0002;
242     WOutDev[0].caps.wPid = 0x0104;
243     strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
244     WOutDev[0].caps.vDriverVersion = 0x0100;
245     WOutDev[0].caps.dwFormats = 0x00000000;
246     WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
247 #define X(r,v) \
248     if (chn_info.rates & SND_PCM_RATE_##r) \
249     { \
250         if (chn_info.formats & SND_PCM_FMT_U8) \
251         { \
252             if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
253                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
254             if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
255                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
256         } \
257         if (chn_info.formats & SND_PCM_FMT_S16_LE) \
258         { \
259             if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
260                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
261             if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
262                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
263         } \
264     }
265     X(11025,1);
266     X(22050,2);
267     X(44100,4);
268 #undef X
269     if (chn_info.min_voices > 1) FIXME("-");
270     WOutDev[0].caps.wChannels = (chn_info.max_voices >= 2) ? 2 : 1;
271     if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices)
272         WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
273
274     /* FIXME: always true ? */
275     WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
276
277     /* FIXME: is test sufficient ? */
278     if (chn_info.flags & SND_PCM_CHNINFO_MMAP)
279         WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;
280
281     TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
282           WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);
283
284     snd_pcm_close(h);
285
286     return 0;
287 }
288
289 /******************************************************************
290  *              ALSA_InitRingMessage
291  *
292  * Initialize the ring of messages for passing between driver's caller and playback/record
293  * thread
294  */
295 static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
296 {
297     omr->msg_toget = 0;
298     omr->msg_tosave = 0;
299     omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
300     memset(omr->messages, 0, sizeof(ALSA_MSG) * ALSA_RING_BUFFER_SIZE);
301     InitializeCriticalSection(&omr->msg_crst);
302     return 0;
303 }
304
305 /******************************************************************
306  *              ALSA_DestroyRingMessage
307  *
308  */
309 static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
310 {
311     CloseHandle(omr->msg_event);
312     DeleteCriticalSection(&omr->msg_crst);
313     return 0;
314 }
315
316 /******************************************************************
317  *              ALSA_AddRingMessage
318  *
319  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
320  */
321 static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
322 {
323     HANDLE      hEvent = INVALID_HANDLE_VALUE;
324
325     EnterCriticalSection(&omr->msg_crst);
326     if ((omr->msg_toget == ((omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE))) /* buffer overflow ? */
327     {
328         ERR("buffer overflow !?\n");
329         LeaveCriticalSection(&omr->msg_crst);
330         return 0;
331     }
332     if (wait)
333     {
334         hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
335         if (hEvent == INVALID_HANDLE_VALUE)
336         {
337             ERR("can't create event !?\n");
338             LeaveCriticalSection(&omr->msg_crst);
339             return 0;
340         }
341         if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
342             FIXME("two fast messages in the queue!!!!\n");
343
344         /* fast messages have to be added at the start of the queue */
345         omr->msg_toget = (omr->msg_toget + ALSA_RING_BUFFER_SIZE - 1) % ALSA_RING_BUFFER_SIZE;
346
347         omr->messages[omr->msg_toget].msg = msg;
348         omr->messages[omr->msg_toget].param = param;
349         omr->messages[omr->msg_toget].hEvent = hEvent;
350     }
351     else
352     {
353         omr->messages[omr->msg_tosave].msg = msg;
354         omr->messages[omr->msg_tosave].param = param;
355         omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
356         omr->msg_tosave = (omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE;
357     }
358     LeaveCriticalSection(&omr->msg_crst);
359     /* signal a new message */
360     SetEvent(omr->msg_event);
361     if (wait)
362     {
363         /* wait for playback/record thread to have processed the message */
364         WaitForSingleObject(hEvent, INFINITE);
365         CloseHandle(hEvent);
366     }
367     return 1;
368 }
369
370 /******************************************************************
371  *              ALSA_RetrieveRingMessage
372  *
373  * Get a message from the ring. Should be called by the playback/record thread.
374  */
375 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
376                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
377 {
378     EnterCriticalSection(&omr->msg_crst);
379
380     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
381     {
382         LeaveCriticalSection(&omr->msg_crst);
383         return 0;
384     }
385
386     *msg = omr->messages[omr->msg_toget].msg;
387     omr->messages[omr->msg_toget].msg = 0;
388     *param = omr->messages[omr->msg_toget].param;
389     *hEvent = omr->messages[omr->msg_toget].hEvent;
390     omr->msg_toget = (omr->msg_toget + 1) % ALSA_RING_BUFFER_SIZE;
391     LeaveCriticalSection(&omr->msg_crst);
392     return 1;
393 }
394
395 /*======================================================================*
396  *                  Low level WAVE OUT implementation                   *
397  *======================================================================*/
398
399 /**************************************************************************
400  *                      wodNotifyClient                 [internal]
401  */
402 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
403 {
404     TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
405
406     switch (wMsg) {
407     case WOM_OPEN:
408     case WOM_CLOSE:
409     case WOM_DONE:
410         if (wwo->wFlags != DCB_NULL &&
411             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave,
412                             wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
413             WARN("can't notify client !\n");
414             return MMSYSERR_ERROR;
415         }
416         break;
417     default:
418         FIXME("Unknown callback message %u\n", wMsg);
419         return MMSYSERR_INVALPARAM;
420     }
421     return MMSYSERR_NOERROR;
422 }
423
424 /**************************************************************************
425  *                              wodUpdatePlayedTotal    [internal]
426  *
427  */
428 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_channel_status_t* ps)
429 {
430     snd_pcm_channel_status_t    s;
431     snd_pcm_channel_status_t*   status = (ps) ? ps : &s;
432
433     if (snd_pcm_channel_status(wwo->handle, status))
434     {
435         ERR("Can't get channel status: %s\n", snd_strerror(errno));
436         return FALSE;
437     }
438     wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - status->count);
439     if (wwo->dwPlayedTotal != status->scount)
440     {
441         FIXME("Ooch: %u played by ALSA, %lu counted by driver\n",
442               status->scount, wwo->dwPlayedTotal);
443         if (wwo->dwPlayedTotal & 0x8000000) wwo->dwPlayedTotal = 0;
444     }
445     return TRUE;
446 }
447
448 /**************************************************************************
449  *                              wodPlayer_BeginWaveHdr          [internal]
450  *
451  * Makes the specified lpWaveHdr the currently playing wave header.
452  * If the specified wave header is a begin loop and we're not already in
453  * a loop, setup the loop.
454  */
455 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
456 {
457     wwo->lpPlayPtr = lpWaveHdr;
458
459     if (!lpWaveHdr) return;
460
461     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
462         if (wwo->lpLoopPtr) {
463             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
464         } else {
465             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
466             wwo->lpLoopPtr = lpWaveHdr;
467             /* Windows does not touch WAVEHDR.dwLoops,
468              * so we need to make an internal copy */
469             wwo->dwLoops = lpWaveHdr->dwLoops;
470         }
471     }
472     wwo->dwPartialOffset = 0;
473 }
474
475 /**************************************************************************
476  *                              wodPlayer_PlayPtrNext           [internal]
477  *
478  * Advance the play pointer to the next waveheader, looping if required.
479  */
480 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
481 {
482     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
483
484     wwo->dwPartialOffset = 0;
485     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
486         /* We're at the end of a loop, loop if required */
487         if (--wwo->dwLoops > 0) {
488             wwo->lpPlayPtr = wwo->lpLoopPtr;
489         } else {
490             /* Handle overlapping loops correctly */
491             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
492                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
493                 /* shall we consider the END flag for the closing loop or for
494                  * the opening one or for both ???
495                  * code assumes for closing loop only
496                  */
497             } else {
498                 lpWaveHdr = lpWaveHdr->lpNext;
499             }
500             wwo->lpLoopPtr = NULL;
501             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
502         }
503     } else {
504         /* We're not in a loop.  Advance to the next wave header */
505         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
506     }
507
508     return lpWaveHdr;
509 }
510
511 /**************************************************************************
512  *                           wodPlayer_DSPWait                  [internal]
513  * Returns the number of milliseconds to wait for the DSP buffer to write
514  * one fragment.
515  */
516 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
517 {
518     /* time for one fragment to be played */
519     return wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec;
520 }
521
522 /**************************************************************************
523  *                           wodPlayer_NotifyWait               [internal]
524  * Returns the number of milliseconds to wait before attempting to notify
525  * completion of the specified wavehdr.
526  * This is based on the number of bytes remaining to be written in the
527  * wave.
528  */
529 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
530 {
531     DWORD dwMillis;
532
533     if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
534         dwMillis = 1;
535     } else {
536         dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
537         if (!dwMillis) dwMillis = 1;
538     }
539
540     return dwMillis;
541 }
542
543
544 /**************************************************************************
545  *                           wodPlayer_WriteMaxFrags            [internal]
546  * Writes the maximum number of bytes palsaible to the DSP and returns
547  * the number of bytes written.
548  */
549 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
550 {
551     /* Only attempt to write to free bytes */
552     DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
553     int toWrite = min(dwLength, *bytes);
554     int written;
555
556     TRACE("Writing wavehdr %p.%lu[%lu]\n",
557           wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
558     written = snd_pcm_write(wwo->handle, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
559     if (written <= 0)
560     {
561         ERR("Wrote: %d bytes (%s)\n", written, snd_strerror(errno));
562         return written;
563     }
564
565     if (written >= dwLength) {
566         /* If we wrote all current wavehdr, skip to the next one */
567         wodPlayer_PlayPtrNext(wwo);
568     } else {
569         /* Remove the amount written */
570         wwo->dwPartialOffset += written;
571     }
572     *bytes -= written;
573     wwo->dwWrittenTotal += written;
574
575     return written;
576 }
577
578
579 /**************************************************************************
580  *                              wodPlayer_NotifyCompletions     [internal]
581  *
582  * Notifies and remove from queue all wavehdrs which have been played to
583  * the speaker (ie. they have cleared the ALSA buffer).  If force is true,
584  * we notify all wavehdrs and remove them all from the queue even if they
585  * are unplayed or part of a loop.
586  */
587 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
588 {
589     LPWAVEHDR           lpWaveHdr;
590
591     /* Start from lpQueuePtr and keep notifying until:
592      * - we hit an unwritten wavehdr
593      * - we hit the beginning of a running loop
594      * - we hit a wavehdr which hasn't finished playing
595      */
596     while ((lpWaveHdr = wwo->lpQueuePtr) &&
597            (force ||
598             (lpWaveHdr != wwo->lpPlayPtr &&
599              lpWaveHdr != wwo->lpLoopPtr &&
600              lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
601
602         wwo->lpQueuePtr = lpWaveHdr->lpNext;
603
604         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
605         lpWaveHdr->dwFlags |= WHDR_DONE;
606
607         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
608     }
609     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
610         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
611 }
612
613 /**************************************************************************
614  *                              wodPlayer_Reset                 [internal]
615  *
616  * wodPlayer helper. Resets current output stream.
617  */
618 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
619 {
620     wodUpdatePlayedTotal(wwo, NULL);
621     /* updates current notify list */
622     wodPlayer_NotifyCompletions(wwo, FALSE);
623
624     if (snd_pcm_playback_flush(wwo->handle) != 0) {
625         FIXME("flush: %s\n", snd_strerror(errno));
626         wwo->hThread = 0;
627         wwo->state = WINE_WS_STOPPED;
628         ExitThread(-1);
629     }
630
631     if (reset) {
632         enum win_wm_message     msg;
633         DWORD                   param;
634         HANDLE                  ev;
635
636         /* remove any buffer */
637         wodPlayer_NotifyCompletions(wwo, TRUE);
638
639         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
640         wwo->state = WINE_WS_STOPPED;
641         wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
642         /* Clear partial wavehdr */
643         wwo->dwPartialOffset = 0;
644
645         /* remove any existing message in the ring */
646         EnterCriticalSection(&wwo->msgRing.msg_crst);
647         /* return all pending headers in queue */
648         while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
649         {
650             if (msg != WINE_WM_HEADER)
651             {
652                 FIXME("shouldn't have headers left\n");
653                 SetEvent(ev);
654                 continue;
655             }
656             ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
657             ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
658
659             wodNotifyClient(wwo, WOM_DONE, param, 0);
660         }
661         ResetEvent(wwo->msgRing.msg_event);
662         LeaveCriticalSection(&wwo->msgRing.msg_crst);
663     } else {
664         if (wwo->lpLoopPtr) {
665             /* complicated case, not handled yet (could imply modifying the loop counter */
666             FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
667             wwo->lpPlayPtr = wwo->lpLoopPtr;
668             wwo->dwPartialOffset = 0;
669             wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
670         } else {
671             LPWAVEHDR   ptr;
672             DWORD       sz = wwo->dwPartialOffset;
673
674             /* reset all the data as if we had written only up to lpPlayedTotal bytes */
675             /* compute the max size playable from lpQueuePtr */
676             for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
677                 sz += ptr->dwBufferLength;
678             }
679             /* because the reset lpPlayPtr will be lpQueuePtr */
680             if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
681             wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
682             wwo->dwWrittenTotal = wwo->dwPlayedTotal;
683             wwo->lpPlayPtr = wwo->lpQueuePtr;
684         }
685         wwo->state = WINE_WS_PAUSED;
686     }
687 }
688
689 /**************************************************************************
690  *                    wodPlayer_ProcessMessages                 [internal]
691  */
692 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
693 {
694     LPWAVEHDR           lpWaveHdr;
695     enum win_wm_message msg;
696     DWORD               param;
697     HANDLE              ev;
698
699     while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
700         TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
701
702         switch (msg) {
703         case WINE_WM_PAUSING:
704             wodPlayer_Reset(wwo, FALSE);
705             SetEvent(ev);
706             break;
707         case WINE_WM_RESTARTING:
708             if (wwo->state == WINE_WS_PAUSED)
709             {
710                 snd_pcm_playback_prepare(wwo->handle);
711                 wwo->state = WINE_WS_PLAYING;
712             }
713             SetEvent(ev);
714             break;
715         case WINE_WM_HEADER:
716             lpWaveHdr = (LPWAVEHDR)param;
717
718             /* insert buffer at the end of queue */
719             {
720                 LPWAVEHDR*      wh;
721                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
722                 *wh = lpWaveHdr;
723             }
724             if (!wwo->lpPlayPtr)
725                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
726             if (wwo->state == WINE_WS_STOPPED)
727                 wwo->state = WINE_WS_PLAYING;
728             break;
729         case WINE_WM_RESETTING:
730             wodPlayer_Reset(wwo, TRUE);
731             SetEvent(ev);
732             break;
733         case WINE_WM_UPDATE:
734             wodUpdatePlayedTotal(wwo, NULL);
735             SetEvent(ev);
736             break;
737         case WINE_WM_BREAKLOOP:
738             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
739                 /* ensure exit at end of current loop */
740                 wwo->dwLoops = 1;
741             }
742             SetEvent(ev);
743             break;
744         case WINE_WM_CLOSING:
745             /* sanity check: this should not happen since the device must have been reset before */
746             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
747             wwo->hThread = 0;
748             wwo->state = WINE_WS_CLOSED;
749             SetEvent(ev);
750             ExitThread(0);
751             /* shouldn't go here */
752         default:
753             FIXME("unknown message %d\n", msg);
754             break;
755         }
756     }
757 }
758
759 /**************************************************************************
760  *                           wodPlayer_FeedDSP                  [internal]
761  * Feed as much sound data as we can into the DSP and return the number of
762  * milliseconds before it will be necessary to feed the DSP again.
763  */
764 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
765 {
766     snd_pcm_channel_status_t    status;
767     DWORD                       availInQ;
768
769     wodUpdatePlayedTotal(wwo, &status);
770     availInQ = status.free;
771
772 #if 0
773     TODO;
774     TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
775           dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
776 #endif
777
778     /* input queue empty and output buffer with less than one fragment to play */
779     /* FIXME: we should be able to catch OVERRUN errors */
780     if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize) {
781         TRACE("Run out of wavehdr:s... flushing\n");
782         snd_pcm_playback_drain(wwo->handle);
783         wwo->dwPlayedTotal = wwo->dwWrittenTotal;
784         return INFINITE;
785     }
786
787     /* no more room... no need to try to feed */
788     if (status.free > 0) {
789         /* Feed from partial wavehdr */
790         if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
791             wodPlayer_WriteMaxFrags(wwo, &availInQ);
792         }
793
794         /* Feed wavehdrs until we run out of wavehdrs or DSP space */
795         if (wwo->dwPartialOffset == 0) {
796             while (wwo->lpPlayPtr && availInQ > 0) {
797                 /* note the value that dwPlayedTotal will return when this wave finishes playing */
798                 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
799                 wodPlayer_WriteMaxFrags(wwo, &availInQ);
800             }
801         }
802     }
803     return wodPlayer_DSPWait(wwo);
804 }
805
806
807 /**************************************************************************
808  *                              wodPlayer                       [internal]
809  */
810 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
811 {
812     WORD          uDevID = (DWORD)pmt;
813     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
814     DWORD         dwNextFeedTime = INFINITE;   /* Time before DSP needs feeding */
815     DWORD         dwNextNotifyTime = INFINITE; /* Time before next wave completion */
816     DWORD         dwSleepTime;
817
818     wwo->state = WINE_WS_STOPPED;
819     SetEvent(wwo->hStartUpEvent);
820
821     for (;;) {
822         /** Wait for the shortest time before an action is required.  If there
823          *  are no pending actions, wait forever for a command.
824          */
825         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
826         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
827         WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
828         wodPlayer_ProcessMessages(wwo);
829         if (wwo->state == WINE_WS_PLAYING) {
830             dwNextFeedTime = wodPlayer_FeedDSP(wwo);
831             dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
832         } else {
833             dwNextFeedTime = dwNextNotifyTime = INFINITE;
834         }
835     }
836 }
837
838 /**************************************************************************
839  *                      wodGetDevCaps                           [internal]
840  */
841 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
842 {
843     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
844
845     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
846
847     if (wDevID >= MAX_WAVEOUTDRV) {
848         TRACE("MAX_WAVOUTDRV reached !\n");
849         return MMSYSERR_BADDEVICEID;
850     }
851
852     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
853     return MMSYSERR_NOERROR;
854 }
855
856 /**************************************************************************
857  *                              wodOpen                         [internal]
858  */
859 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
860 {
861     WINE_WAVEOUT*               wwo;
862     snd_pcm_channel_params_t    params;
863
864     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
865     if (lpDesc == NULL) {
866         WARN("Invalid Parameter !\n");
867         return MMSYSERR_INVALPARAM;
868     }
869     if (wDevID >= MAX_WAVEOUTDRV) {
870         TRACE("MAX_WAVOUTDRV reached !\n");
871         return MMSYSERR_BADDEVICEID;
872     }
873
874     /* only PCM format is supported so far... */
875     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
876         lpDesc->lpFormat->nChannels == 0 ||
877         lpDesc->lpFormat->nSamplesPerSec == 0) {
878         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
879              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
880              lpDesc->lpFormat->nSamplesPerSec);
881         return WAVERR_BADFORMAT;
882     }
883
884     if (dwFlags & WAVE_FORMAT_QUERY) {
885         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
886              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
887              lpDesc->lpFormat->nSamplesPerSec);
888         return MMSYSERR_NOERROR;
889     }
890
891     wwo = &WOutDev[wDevID];
892
893     if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
894         /* not supported, ignore it */
895         dwFlags &= ~WAVE_DIRECTSOUND;
896
897     wwo->handle = 0;
898     if (snd_pcm_open(&wwo->handle, wDevID, 0, SND_PCM_OPEN_DUPLEX|SND_PCM_OPEN_NONBLOCK))
899     {
900         ERR("Error open: %s\n", snd_strerror(errno));
901         return MMSYSERR_NOTENABLED;
902     }
903
904     memset(&params, 0, sizeof(params));
905     params.channel = SND_PCM_CHANNEL_PLAYBACK;
906     params.start_mode = SND_PCM_START_DATA;
907     params.stop_mode = SND_PCM_STOP_STOP;
908     params.mode = SND_PCM_MODE_STREAM;
909     params.buf.stream.queue_size = 0x1000;
910     params.buf.stream.fill = SND_PCM_FILL_SILENCE;
911     params.buf.stream.max_fill = 0x800;
912
913     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
914
915     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
916     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
917
918     if (wwo->format.wBitsPerSample == 0) {
919         WARN("Resetting zeroed wBitsPerSample\n");
920         wwo->format.wBitsPerSample = 8 *
921             (wwo->format.wf.nAvgBytesPerSec /
922              wwo->format.wf.nSamplesPerSec) /
923             wwo->format.wf.nChannels;
924     }
925     params.format.interleave = 1;
926     params.format.format = (wwo->format.wBitsPerSample == 16) ?
927         SND_PCM_SFMT_S16_LE : SND_PCM_SFMT_U8;
928     params.format.rate = wwo->format.wf.nSamplesPerSec;
929     params.format.voices = (wwo->format.wf.nChannels > 1) ? 2 : 1;
930     params.format.special = 0;
931
932     if (snd_pcm_channel_params(wwo->handle, &params))
933     {
934         ERR("Can't set params: %s\n", snd_strerror(errno));
935         snd_pcm_close(wwo->handle);
936         wwo->handle = NULL;
937         return MMSYSERR_INVALPARAM;
938     }
939 #if 0
940     TODO;
941     if (params.format.rate != format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
942         ERR("Can't set format to %d (%d)\n",
943             (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
944     if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0)
945         ERR("Can't set stereo to %u (%d)\n",
946             (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
947     if (!NEAR_MATCH(sample_rate, wwo->format.wf.nSamplesPerSec))
948         ERR("Can't set sample_rate to %lu (%d)\n",
949             wwo->format.wf.nSamplesPerSec, sample_rate);
950 #endif
951
952     snd_pcm_playback_prepare(wwo->handle);
953
954     /* Remember fragsize and total buffer size for future use */
955     wwo->dwBufferSize = params.buf.stream.queue_size;
956     /* FIXME: should get rid off fragment size */
957     wwo->dwFragmentSize = wwo->dwBufferSize >> 4; /* why not */
958     wwo->dwPlayedTotal = 0;
959     wwo->dwWrittenTotal = 0;
960     wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
961
962     ALSA_InitRingMessage(&wwo->msgRing);
963
964     if (!(dwFlags & WAVE_DIRECTSOUND)) {
965         wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
966         wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
967         WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
968         CloseHandle(wwo->hStartUpEvent);
969     } else {
970         wwo->hThread = INVALID_HANDLE_VALUE;
971         wwo->dwThreadID = 0;
972     }
973     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
974
975     TRACE("handle=%08lx fragmentSize=%ld\n",
976           (DWORD)wwo->handle, wwo->dwFragmentSize);
977     if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
978         ERR("Fragment doesn't contain an integral number of data blocks\n");
979
980     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
981           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
982           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
983           wwo->format.wf.nBlockAlign);
984
985     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
986 }
987
988 /**************************************************************************
989  *                              wodClose                        [internal]
990  */
991 static DWORD wodClose(WORD wDevID)
992 {
993     DWORD               ret = MMSYSERR_NOERROR;
994     WINE_WAVEOUT*       wwo;
995
996     TRACE("(%u);\n", wDevID);
997
998     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
999         WARN("bad device ID !\n");
1000         return MMSYSERR_BADDEVICEID;
1001     }
1002
1003     wwo = &WOutDev[wDevID];
1004     if (wwo->lpQueuePtr) {
1005         WARN("buffers still playing !\n");
1006         ret = WAVERR_STILLPLAYING;
1007     } else {
1008         if (wwo->hThread != INVALID_HANDLE_VALUE) {
1009             ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1010         }
1011         if (wwo->mmap_buffer) {
1012             snd_pcm_munmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK);
1013             wwo->mmap_buffer = wwo->mmap_control = NULL;
1014         }
1015
1016         ALSA_DestroyRingMessage(&wwo->msgRing);
1017
1018         snd_pcm_close(wwo->handle);
1019         wwo->handle = NULL;
1020         wwo->dwFragmentSize = 0;
1021         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1022     }
1023     return ret;
1024 }
1025
1026 /**************************************************************************
1027  *                              wodWrite                        [internal]
1028  *
1029  */
1030 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1031 {
1032     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1033
1034     /* first, do the sanity checks... */
1035     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1036         WARN("bad dev ID !\n");
1037         return MMSYSERR_BADDEVICEID;
1038     }
1039
1040     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1041         return WAVERR_UNPREPARED;
1042
1043     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1044         return WAVERR_STILLPLAYING;
1045
1046     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1047     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1048     lpWaveHdr->lpNext = 0;
1049
1050     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1051
1052     return MMSYSERR_NOERROR;
1053 }
1054
1055 /**************************************************************************
1056  *                              wodPrepare                      [internal]
1057  */
1058 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1059 {
1060     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1061
1062     if (wDevID >= MAX_WAVEOUTDRV) {
1063         WARN("bad device ID !\n");
1064         return MMSYSERR_BADDEVICEID;
1065     }
1066
1067     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1068         return WAVERR_STILLPLAYING;
1069
1070     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1071     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1072     return MMSYSERR_NOERROR;
1073 }
1074
1075 /**************************************************************************
1076  *                              wodUnprepare                    [internal]
1077  */
1078 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1079 {
1080     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1081
1082     if (wDevID >= MAX_WAVEOUTDRV) {
1083         WARN("bad device ID !\n");
1084         return MMSYSERR_BADDEVICEID;
1085     }
1086
1087     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1088         return WAVERR_STILLPLAYING;
1089
1090     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1091     lpWaveHdr->dwFlags |= WHDR_DONE;
1092
1093     return MMSYSERR_NOERROR;
1094 }
1095
1096 /**************************************************************************
1097  *                      wodPause                                [internal]
1098  */
1099 static DWORD wodPause(WORD wDevID)
1100 {
1101     TRACE("(%u);!\n", wDevID);
1102
1103     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1104         WARN("bad device ID !\n");
1105         return MMSYSERR_BADDEVICEID;
1106     }
1107
1108     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1109
1110     return MMSYSERR_NOERROR;
1111 }
1112
1113 /**************************************************************************
1114  *                      wodRestart                              [internal]
1115  */
1116 static DWORD wodRestart(WORD wDevID)
1117 {
1118     TRACE("(%u);\n", wDevID);
1119
1120     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1121         WARN("bad device ID !\n");
1122         return MMSYSERR_BADDEVICEID;
1123     }
1124
1125     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1126         ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1127     }
1128
1129     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1130     /* FIXME: Myst crashes with this ... hmm -MM
1131        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1132     */
1133
1134     return MMSYSERR_NOERROR;
1135 }
1136
1137 /**************************************************************************
1138  *                      wodReset                                [internal]
1139  */
1140 static DWORD wodReset(WORD wDevID)
1141 {
1142     TRACE("(%u);\n", wDevID);
1143
1144     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1145         WARN("bad device ID !\n");
1146         return MMSYSERR_BADDEVICEID;
1147     }
1148
1149     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1150
1151     return MMSYSERR_NOERROR;
1152 }
1153
1154 /**************************************************************************
1155  *                              wodGetPosition                  [internal]
1156  */
1157 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1158 {
1159     int                 time;
1160     DWORD               val;
1161     WINE_WAVEOUT*       wwo;
1162
1163     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1164
1165     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1166         WARN("bad device ID !\n");
1167         return MMSYSERR_BADDEVICEID;
1168     }
1169
1170     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1171
1172     wwo = &WOutDev[wDevID];
1173     ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1174     val = wwo->dwPlayedTotal;
1175
1176     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1177           lpTime->wType, wwo->format.wBitsPerSample,
1178           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1179           wwo->format.wf.nAvgBytesPerSec);
1180     TRACE("dwPlayedTotal=%lu\n", val);
1181
1182     switch (lpTime->wType) {
1183     case TIME_BYTES:
1184         lpTime->u.cb = val;
1185         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1186         break;
1187     case TIME_SAMPLES:
1188         lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
1189         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1190         break;
1191     case TIME_SMPTE:
1192         time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1193         lpTime->u.smpte.hour = time / 108000;
1194         time -= lpTime->u.smpte.hour * 108000;
1195         lpTime->u.smpte.min = time / 1800;
1196         time -= lpTime->u.smpte.min * 1800;
1197         lpTime->u.smpte.sec = time / 30;
1198         time -= lpTime->u.smpte.sec * 30;
1199         lpTime->u.smpte.frame = time;
1200         lpTime->u.smpte.fps = 30;
1201         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1202               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1203               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1204         break;
1205     default:
1206         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1207         lpTime->wType = TIME_MS;
1208     case TIME_MS:
1209         lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1210         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1211         break;
1212     }
1213     return MMSYSERR_NOERROR;
1214 }
1215
1216 /**************************************************************************
1217  *                              wodBreakLoop                    [internal]
1218  */
1219 static DWORD wodBreakLoop(WORD wDevID)
1220 {
1221     TRACE("(%u);\n", wDevID);
1222
1223     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1224         WARN("bad device ID !\n");
1225         return MMSYSERR_BADDEVICEID;
1226     }
1227     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1228     return MMSYSERR_NOERROR;
1229 }
1230
1231 /**************************************************************************
1232  *                              wodGetVolume                    [internal]
1233  */
1234 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1235 {
1236 #if 0
1237     int         mixer;
1238 #endif
1239     int         volume;
1240     DWORD       left, right;
1241
1242     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1243
1244     if (lpdwVol == NULL)
1245         return MMSYSERR_NOTENABLED;
1246 #if 0
1247     TODO;
1248     if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
1249         WARN("mixer device not available !\n");
1250         return MMSYSERR_NOTENABLED;
1251     }
1252     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
1253         WARN("unable to read mixer !\n");
1254         return MMSYSERR_NOTENABLED;
1255     }
1256     close(mixer);
1257 #else
1258     volume = 0x2020;
1259 #endif
1260     left = LOBYTE(volume);
1261     right = HIBYTE(volume);
1262     TRACE("left=%ld right=%ld !\n", left, right);
1263     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1264     return MMSYSERR_NOERROR;
1265 }
1266
1267 /**************************************************************************
1268  *                              wodSetVolume                    [internal]
1269  */
1270 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1271 {
1272 #if 0
1273     int         mixer;
1274 #endif
1275     int         volume;
1276     DWORD       left, right;
1277
1278     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1279
1280     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
1281     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1282     volume = left + (right << 8);
1283
1284 #if 0
1285     TODO;
1286     if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
1287         WARN("mixer device not available !\n");
1288         return MMSYSERR_NOTENABLED;
1289     }
1290     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
1291         WARN("unable to set mixer !\n");
1292         return MMSYSERR_NOTENABLED;
1293     } else {
1294         TRACE("volume=%04x\n", (unsigned)volume);
1295     }
1296     close(mixer);
1297 #endif
1298     return MMSYSERR_NOERROR;
1299 }
1300
1301 /**************************************************************************
1302  *                              wodGetNumDevs                   [internal]
1303  */
1304 static  DWORD   wodGetNumDevs(void)
1305 {
1306     return ALSA_WodNumDevs;
1307 }
1308
1309 /**************************************************************************
1310  *                              wodMessage (WINEALSA.@)
1311  */
1312 DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1313                              DWORD dwParam1, DWORD dwParam2)
1314 {
1315     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1316           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1317
1318     switch (wMsg) {
1319     case DRVM_INIT:
1320     case DRVM_EXIT:
1321     case DRVM_ENABLE:
1322     case DRVM_DISABLE:
1323         /* FIXME: Pretend this is supported */
1324         return 0;
1325     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
1326     case WODM_CLOSE:            return wodClose         (wDevID);
1327     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1328     case WODM_PAUSE:            return wodPause         (wDevID);
1329     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
1330     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
1331     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1332     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1333     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
1334     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
1335     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
1336     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
1337     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1338     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1339     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
1340     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
1341     case WODM_RESTART:          return wodRestart       (wDevID);
1342     case WODM_RESET:            return wodReset         (wDevID);
1343
1344     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1345     default:
1346         FIXME("unknown message %d!\n", wMsg);
1347     }
1348     return MMSYSERR_NOTSUPPORTED;
1349 }
1350
1351 /*======================================================================*
1352  *                  Low level DSOUND implementation                     *
1353  *======================================================================*/
1354
1355 typedef struct IDsDriverImpl IDsDriverImpl;
1356 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1357
1358 struct IDsDriverImpl
1359 {
1360     /* IUnknown fields */
1361     ICOM_VFIELD(IDsDriver);
1362     DWORD               ref;
1363     /* IDsDriverImpl fields */
1364     UINT                wDevID;
1365     IDsDriverBufferImpl*primary;
1366 };
1367
1368 struct IDsDriverBufferImpl
1369 {
1370     /* IUnknown fields */
1371     ICOM_VFIELD(IDsDriverBuffer);
1372     DWORD               ref;
1373     /* IDsDriverBufferImpl fields */
1374     IDsDriverImpl*      drv;
1375     DWORD               buflen;
1376 };
1377
1378 static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb)
1379 {
1380     WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1381     if (wwo->mmap_buffer) {
1382         if (snd_pcm_munmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK) < 0) {
1383             ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno);
1384             return DSERR_GENERIC;
1385         }
1386         wwo->mmap_buffer = wwo->mmap_control = NULL;
1387         TRACE("(%p): sound device unmapped\n", dsdb);
1388     }
1389     return DS_OK;
1390 }
1391
1392 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
1393 {
1394     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1395     FIXME("(): stub!\n");
1396     return DSERR_UNSUPPORTED;
1397 }
1398
1399 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
1400 {
1401     ICOM_THIS(IDsDriverBufferImpl,iface);
1402     This->ref++;
1403     return This->ref;
1404 }
1405
1406 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
1407 {
1408     ICOM_THIS(IDsDriverBufferImpl,iface);
1409     if (--This->ref)
1410         return This->ref;
1411     if (This == This->drv->primary)
1412         This->drv->primary = NULL;
1413     DSDB_UnmapPrimary(This);
1414     HeapFree(GetProcessHeap(),0,This);
1415     return 0;
1416 }
1417
1418 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
1419                                                LPVOID*ppvAudio1,LPDWORD pdwLen1,
1420                                                LPVOID*ppvAudio2,LPDWORD pdwLen2,
1421                                                DWORD dwWritePosition,DWORD dwWriteLen,
1422                                                DWORD dwFlags)
1423 {
1424     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1425     /* FIXME: we need to implement it */
1426     TRACE("(%p)\n",iface);
1427     return DSERR_UNSUPPORTED;
1428 }
1429
1430 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
1431                                                  LPVOID pvAudio1,DWORD dwLen1,
1432                                                  LPVOID pvAudio2,DWORD dwLen2)
1433 {
1434     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1435     TRACE("(%p)\n",iface);
1436     return DSERR_UNSUPPORTED;
1437 }
1438
1439 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
1440                                                     LPWAVEFORMATEX pwfx)
1441 {
1442     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1443
1444     TRACE("(%p,%p)\n",iface,pwfx);
1445     /* On our request (GetDriverDesc flags), DirectSound has by now used
1446      * waveOutClose/waveOutOpen to set the format...
1447      * unfortunately, this means our mmap() is now gone...
1448      * so we need to somehow signal to our DirectSound implementation
1449      * that it should completely recreate this HW buffer...
1450      * this unexpected error code should do the trick... */
1451     return DSERR_BUFFERLOST;
1452 }
1453
1454 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
1455 {
1456     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1457     TRACE("(%p,%ld): stub\n",iface,dwFreq);
1458     return DSERR_UNSUPPORTED;
1459 }
1460
1461 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
1462 {
1463     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1464     FIXME("(%p,%p): stub!\n",iface,pVolPan);
1465     return DSERR_UNSUPPORTED;
1466 }
1467
1468 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
1469 {
1470     /* ICOM_THIS(IDsDriverImpl,iface); */
1471     TRACE("(%p,%ld): stub\n",iface,dwNewPos);
1472     return DSERR_UNSUPPORTED;
1473 }
1474
1475 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
1476                                                       LPDWORD lpdwPlay, LPDWORD lpdwWrite)
1477 {
1478 #if 0
1479     ICOM_THIS(IDsDriverBufferImpl,iface);
1480     TODO;
1481     count_info info;
1482     DWORD ptr;
1483
1484     TRACE("(%p)\n",iface);
1485     if (WOutDev[This->drv->wDevID].handle == NULL) {
1486         ERR("device not open, but accessing?\n");
1487         return DSERR_UNINITIALIZED;
1488     }
1489     if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
1490         ERR("ioctl failed (%d)\n", errno);
1491         return DSERR_GENERIC;
1492     }
1493     ptr = info.ptr & ~3; /* align the pointer, just in case */
1494     if (lpdwPlay) *lpdwPlay = ptr;
1495     if (lpdwWrite) {
1496         /* add some safety margin (not strictly necessary, but...) */
1497         if (WOutDev[This->drv->wDevID].caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
1498             *lpdwWrite = ptr + 32;
1499         else
1500             *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
1501         while (*lpdwWrite > This->buflen)
1502             *lpdwWrite -= This->buflen;
1503
1504     }
1505 #endif
1506     TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
1507     return DS_OK;
1508 }
1509
1510 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
1511 {
1512     ICOM_THIS(IDsDriverBufferImpl,iface);
1513
1514     TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
1515
1516     /* FIXME: error handling */
1517     snd_pcm_playback_go(WOutDev[This->drv->wDevID].handle);
1518
1519     return DS_OK;
1520 }
1521
1522 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
1523 {
1524     ICOM_THIS(IDsDriverBufferImpl,iface);
1525
1526     TRACE("(%p)\n",iface);
1527
1528     /* no more playing */
1529     /* FIXME: error handling */
1530     snd_pcm_playback_drain(WOutDev[This->drv->wDevID].handle);
1531
1532     /* Most ALSA drivers just can't stop the playback without closing the device...
1533      * so we need to somehow signal to our DirectSound implementation
1534      * that it should completely recreate this HW buffer...
1535      * this unexpected error code should do the trick... */
1536     return DSERR_BUFFERLOST;
1537 }
1538
1539 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
1540 {
1541     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1542     IDsDriverBufferImpl_QueryInterface,
1543     IDsDriverBufferImpl_AddRef,
1544     IDsDriverBufferImpl_Release,
1545     IDsDriverBufferImpl_Lock,
1546     IDsDriverBufferImpl_Unlock,
1547     IDsDriverBufferImpl_SetFormat,
1548     IDsDriverBufferImpl_SetFrequency,
1549     IDsDriverBufferImpl_SetVolumePan,
1550     IDsDriverBufferImpl_SetPosition,
1551     IDsDriverBufferImpl_GetPosition,
1552     IDsDriverBufferImpl_Play,
1553     IDsDriverBufferImpl_Stop
1554 };
1555
1556 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
1557 {
1558     /* ICOM_THIS(IDsDriverImpl,iface); */
1559     FIXME("(%p): stub!\n",iface);
1560     return DSERR_UNSUPPORTED;
1561 }
1562
1563 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
1564 {
1565     ICOM_THIS(IDsDriverImpl,iface);
1566     This->ref++;
1567     return This->ref;
1568 }
1569
1570 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
1571 {
1572     ICOM_THIS(IDsDriverImpl,iface);
1573     if (--This->ref)
1574         return This->ref;
1575     HeapFree(GetProcessHeap(),0,This);
1576     return 0;
1577 }
1578
1579 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
1580 {
1581     ICOM_THIS(IDsDriverImpl,iface);
1582     TRACE("(%p,%p)\n",iface,pDesc);
1583     pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
1584         DSDDESC_USESYSTEMMEMORY;
1585     strcpy(pDesc->szDesc,"WineALSA DirectSound Driver");
1586     strcpy(pDesc->szDrvName,"winealsa.drv");
1587     pDesc->dnDevNode            = WOutDev[This->wDevID].waveDesc.dnDevNode;
1588     pDesc->wVxdId               = 0;
1589     pDesc->wReserved            = 0;
1590     pDesc->ulDeviceNum          = This->wDevID;
1591     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
1592     pDesc->pvDirectDrawHeap     = NULL;
1593     pDesc->dwMemStartAddress    = 0;
1594     pDesc->dwMemEndAddress      = 0;
1595     pDesc->dwMemAllocExtra      = 0;
1596     pDesc->pvReserved1          = NULL;
1597     pDesc->pvReserved2          = NULL;
1598     return DS_OK;
1599 }
1600
1601 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
1602 {
1603     ICOM_THIS(IDsDriverImpl,iface);
1604
1605     TRACE("(%p)\n",iface);
1606     /* FIXME: error handling */
1607     snd_pcm_channel_prepare(WOutDev[This->wDevID].handle, SND_PCM_CHANNEL_PLAYBACK);
1608
1609     return DS_OK;
1610 }
1611
1612 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
1613 {
1614     ICOM_THIS(IDsDriverImpl,iface);
1615     TRACE("(%p)\n",iface);
1616     if (This->primary) {
1617         ERR("problem with DirectSound: primary not released\n");
1618         return DSERR_GENERIC;
1619     }
1620     return DS_OK;
1621 }
1622
1623 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
1624 {
1625     /* ICOM_THIS(IDsDriverImpl,iface); */
1626     TRACE("(%p,%p)\n",iface,pCaps);
1627     memset(pCaps, 0, sizeof(*pCaps));
1628     /* FIXME: need to check actual capabilities */
1629     pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO |
1630         DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT;
1631     pCaps->dwPrimaryBuffers = 1;
1632     /* the other fields only apply to secondary buffers, which we don't support
1633      * (unless we want to mess with wavetable synthesizers and MIDI) */
1634     return DS_OK;
1635 }
1636
1637 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
1638                                                       LPWAVEFORMATEX pwfx,
1639                                                       DWORD dwFlags, DWORD dwCardAddress,
1640                                                       LPDWORD pdwcbBufferSize,
1641                                                       LPBYTE *ppbBuffer,
1642                                                       LPVOID *ppvObj)
1643 {
1644     ICOM_THIS(IDsDriverImpl,iface);
1645     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
1646     WINE_WAVEOUT *wwo = &(WOutDev[This->wDevID]);
1647     struct snd_pcm_channel_setup setup;
1648
1649     TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
1650     /* we only support primary buffers */
1651     if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
1652         return DSERR_UNSUPPORTED;
1653     if (This->primary)
1654         return DSERR_ALLOCATED;
1655     if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
1656         return DSERR_CONTROLUNAVAIL;
1657
1658     *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
1659     if (*ippdsdb == NULL)
1660         return DSERR_OUTOFMEMORY;
1661     (*ippdsdb)->lpVtbl  = &dsdbvt;
1662     (*ippdsdb)->ref     = 1;
1663     (*ippdsdb)->drv     = This;
1664
1665     if (!wwo->mmap_buffer) {
1666         if (snd_pcm_mmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK, &wwo->mmap_control, &wwo->mmap_buffer))
1667         {
1668             ERR("(%p): Could not map sound device for direct access (%s)\n", *ippdsdb, snd_strerror(errno));
1669             return DSERR_GENERIC;
1670         }
1671
1672         setup.mode = SND_PCM_MODE_BLOCK;
1673         setup.channel = SND_PCM_CHANNEL_PLAYBACK;
1674         if (snd_pcm_channel_setup(wwo->handle, &setup) < 0) {
1675             ERR("Unable to obtain setup\n");
1676             /* FIXME: resource cleanup */
1677             return DSERR_GENERIC;
1678         }
1679         wwo->mmap_block_size = setup.buf.block.frag_size;
1680         wwo->mmap_block_number = setup.buf.block.frags;
1681
1682         TRACE("(%p): sound device has been mapped for direct access at %p, size=%d\n",
1683               *ippdsdb, wwo->mmap_buffer, setup.buf.block.frags * setup.buf.block.frag_size);
1684 #if 0
1685         /* for some reason, es1371 and sblive! sometimes have junk in here.
1686          * clear it, or we get junk noise */
1687         /* some libc implementations are buggy: their memset reads from the buffer...
1688          * to work around it, we have to zero the block by hand. We don't do the expected:
1689          * memset(wwo->mapping, 0, wwo->maplen);
1690          */
1691         {
1692             char*       p1 = wwo->mapping;
1693             unsigned    len = wwo->maplen;
1694
1695             if (len >= 16) /* so we can have at least a 4 long area to store... */
1696             {
1697                 /* the mmap:ed value is (at least) dword aligned
1698                  * so, start filling the complete unsigned long:s
1699                  */
1700                 int             b = len >> 2;
1701                 unsigned long*  p4 = (unsigned long*)p1;
1702
1703                 while (b--) *p4++ = 0;
1704                 /* prepare for filling the rest */
1705                 len &= 3;
1706                 p1 = (unsigned char*)p4;
1707             }
1708             /* in all cases, fill the remaining bytes */
1709             while (len-- != 0) *p1++ = 0;
1710         }
1711 #endif
1712     }
1713
1714     /* primary buffer is ready to go */
1715     *pdwcbBufferSize    = wwo->mmap_block_size * wwo->mmap_block_number;
1716     *ppbBuffer          = wwo->mmap_buffer;
1717
1718     This->primary = *ippdsdb;
1719
1720     return DS_OK;
1721 }
1722
1723 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
1724                                                          PIDSDRIVERBUFFER pBuffer,
1725                                                          LPVOID *ppvObj)
1726 {
1727     /* ICOM_THIS(IDsDriverImpl,iface); */
1728     TRACE("(%p,%p): stub\n",iface,pBuffer);
1729     return DSERR_INVALIDCALL;
1730 }
1731
1732 static ICOM_VTABLE(IDsDriver) dsdvt =
1733 {
1734     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1735     IDsDriverImpl_QueryInterface,
1736     IDsDriverImpl_AddRef,
1737     IDsDriverImpl_Release,
1738     IDsDriverImpl_GetDriverDesc,
1739     IDsDriverImpl_Open,
1740     IDsDriverImpl_Close,
1741     IDsDriverImpl_GetCaps,
1742     IDsDriverImpl_CreateSoundBuffer,
1743     IDsDriverImpl_DuplicateSoundBuffer
1744 };
1745
1746 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1747 {
1748     IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
1749
1750     /* the HAL isn't much better than the HEL if we can't do mmap() */
1751     if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
1752         ERR("DirectSound flag not set\n");
1753         MESSAGE("This sound card's driver does not support direct access\n");
1754         MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1755         return MMSYSERR_NOTSUPPORTED;
1756     }
1757
1758     *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
1759     if (!*idrv)
1760         return MMSYSERR_NOMEM;
1761     (*idrv)->lpVtbl     = &dsdvt;
1762     (*idrv)->ref        = 1;
1763
1764     (*idrv)->wDevID     = wDevID;
1765     (*idrv)->primary    = NULL;
1766     return MMSYSERR_NOERROR;
1767 }
1768
1769 /* we don't need a default wodMessage for audio when we don't have ALSA, the
1770  * audio.c file will provide it for us
1771  */
1772 #endif /* HAVE_ALSA && interface == 0.5 */