wined3d: Get rid of the swapchain destroy callback.
[wine] / dlls / wineoss.drv / dsrender.c
1 /*
2  * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
3  *
4  * Copyright 1994 Martin Ayotte
5  *           1999 Eric Pouech (async playing in waveOut/waveIn)
6  *           2000 Eric Pouech (loops in waveOut)
7  *           2002 Eric Pouech (full duplex)
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <errno.h>
35 #include <fcntl.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h>
38 #endif
39 #ifdef HAVE_SYS_MMAN_H
40 # include <sys/mman.h>
41 #endif
42 #ifdef HAVE_POLL_H
43 #include <poll.h>
44 #endif
45 #ifdef HAVE_SYS_POLL_H
46 # include <sys/poll.h>
47 #endif
48 #if defined(HAVE_SYS_SOUNDCARD_H)
49 # include <sys/soundcard.h>
50 #elif defined(HAVE_MACHINE_SOUNDCARD_H)
51 # include <machine/soundcard.h>
52 #elif defined(HAVE_SOUNDCARD_H)
53 # include <soundcard.h>
54 #endif
55 #ifdef HAVE_SYS_ERRNO_H
56 #include <sys/errno.h>
57 #endif
58
59 #include "windef.h"
60 #include "winbase.h"
61 #include "wingdi.h"
62 #include "winuser.h"
63 #include "winerror.h"
64 #include "mmddk.h"
65 #include "mmreg.h"
66 #include "dsound.h"
67 #include "dsdriver.h"
68 #include "wine/debug.h"
69
70 #include "audio.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(wave);
73
74 /*======================================================================*
75  *                  Low level DSOUND definitions                        *
76  *======================================================================*/
77
78 typedef struct IDsDriverPropertySetImpl IDsDriverPropertySetImpl;
79 typedef struct IDsDriverNotifyImpl IDsDriverNotifyImpl;
80 typedef struct IDsDriverImpl IDsDriverImpl;
81 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
82
83 struct IDsDriverPropertySetImpl
84 {
85     /* IUnknown fields */
86     const IDsDriverPropertySetVtbl *lpVtbl;
87     LONG                        ref;
88
89     IDsDriverBufferImpl*        buffer;
90 };
91
92 struct IDsDriverNotifyImpl
93 {
94     /* IUnknown fields */
95     const IDsDriverNotifyVtbl  *lpVtbl;
96     LONG                        ref;
97
98     /* IDsDriverNotifyImpl fields */
99     LPDSBPOSITIONNOTIFY         notifies;
100     int                         nrofnotifies;
101
102     IDsDriverBufferImpl*        buffer;
103 };
104
105 struct IDsDriverImpl
106 {
107     /* IUnknown fields */
108     const IDsDriverVtbl        *lpVtbl;
109     LONG                        ref;
110
111     /* IDsDriverImpl fields */
112     UINT                        wDevID;
113     IDsDriverBufferImpl*        primary;
114
115     int                         nrofsecondaries;
116     IDsDriverBufferImpl**       secondaries;
117 };
118
119 struct IDsDriverBufferImpl
120 {
121     /* IUnknown fields */
122     const IDsDriverBufferVtbl  *lpVtbl;
123     LONG                        ref;
124
125     /* IDsDriverBufferImpl fields */
126     IDsDriverImpl*              drv;
127     DWORD                       buflen;
128     WAVEFORMATPCMEX             wfex;
129     LPBYTE                      mapping;
130     DWORD                       maplen;
131     int                         fd;
132     DWORD                       dwFlags;
133
134     /* IDsDriverNotifyImpl fields */
135     IDsDriverNotifyImpl*        notify;
136     int                         notify_index;
137
138     /* IDsDriverPropertySetImpl fields */
139     IDsDriverPropertySetImpl*   property_set;
140 };
141
142 static HRESULT IDsDriverPropertySetImpl_Create(
143     IDsDriverBufferImpl * dsdb,
144     IDsDriverPropertySetImpl **pdsdps);
145
146 static HRESULT IDsDriverNotifyImpl_Create(
147     IDsDriverBufferImpl * dsdb,
148     IDsDriverNotifyImpl **pdsdn);
149
150 /*======================================================================*
151  *                  Low level DSOUND property set implementation        *
152  *======================================================================*/
153
154 static HRESULT WINAPI IDsDriverPropertySetImpl_QueryInterface(
155     PIDSDRIVERPROPERTYSET iface,
156     REFIID riid,
157     LPVOID *ppobj)
158 {
159     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
160     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
161
162     if ( IsEqualGUID(riid, &IID_IUnknown) ||
163          IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
164         IDsDriverPropertySet_AddRef(iface);
165         *ppobj = This;
166         return DS_OK;
167     }
168
169     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
170
171     *ppobj = 0;
172     return E_NOINTERFACE;
173 }
174
175 static ULONG WINAPI IDsDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface)
176 {
177     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
178     ULONG refCount = InterlockedIncrement(&This->ref);
179
180     TRACE("(%p) ref was %d\n", This, refCount - 1);
181
182     return refCount;
183 }
184
185 static ULONG WINAPI IDsDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface)
186 {
187     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
188     ULONG refCount = InterlockedDecrement(&This->ref);
189
190     TRACE("(%p) ref was %d\n", This, refCount + 1);
191
192     if (!refCount) {
193         IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
194         HeapFree(GetProcessHeap(),0,This);
195         TRACE("(%p) released\n",This);
196     }
197     return refCount;
198 }
199
200 static HRESULT WINAPI IDsDriverPropertySetImpl_Get(
201     PIDSDRIVERPROPERTYSET iface,
202     PDSPROPERTY pDsProperty,
203     LPVOID pPropertyParams,
204     ULONG cbPropertyParams,
205     LPVOID pPropertyData,
206     ULONG cbPropertyData,
207     PULONG pcbReturnedData )
208 {
209     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
210     FIXME("(%p,%p,%p,%x,%p,%x,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
211     return DSERR_UNSUPPORTED;
212 }
213
214 static HRESULT WINAPI IDsDriverPropertySetImpl_Set(
215     PIDSDRIVERPROPERTYSET iface,
216     PDSPROPERTY pDsProperty,
217     LPVOID pPropertyParams,
218     ULONG cbPropertyParams,
219     LPVOID pPropertyData,
220     ULONG cbPropertyData )
221 {
222     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
223     FIXME("(%p,%p,%p,%x,%p,%x)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData);
224     return DSERR_UNSUPPORTED;
225 }
226
227 static HRESULT WINAPI IDsDriverPropertySetImpl_QuerySupport(
228     PIDSDRIVERPROPERTYSET iface,
229     REFGUID PropertySetId,
230     ULONG PropertyId,
231     PULONG pSupport )
232 {
233     IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface;
234     FIXME("(%p,%s,%x,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport);
235     return DSERR_UNSUPPORTED;
236 }
237
238 static const IDsDriverPropertySetVtbl dsdpsvt =
239 {
240     IDsDriverPropertySetImpl_QueryInterface,
241     IDsDriverPropertySetImpl_AddRef,
242     IDsDriverPropertySetImpl_Release,
243     IDsDriverPropertySetImpl_Get,
244     IDsDriverPropertySetImpl_Set,
245     IDsDriverPropertySetImpl_QuerySupport,
246 };
247
248 /*======================================================================*
249  *                  Low level DSOUND notify implementation              *
250  *======================================================================*/
251
252 static HRESULT WINAPI IDsDriverNotifyImpl_QueryInterface(
253     PIDSDRIVERNOTIFY iface,
254     REFIID riid,
255     LPVOID *ppobj)
256 {
257     IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface;
258     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
259
260     if ( IsEqualGUID(riid, &IID_IUnknown) ||
261          IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
262         IDsDriverNotify_AddRef(iface);
263         *ppobj = This;
264         return DS_OK;
265     }
266
267     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
268
269     *ppobj = 0;
270     return E_NOINTERFACE;
271 }
272
273 static ULONG WINAPI IDsDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface)
274 {
275     IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface;
276     ULONG refCount = InterlockedIncrement(&This->ref);
277
278     TRACE("(%p) ref was %d\n", This, refCount - 1);
279
280     return refCount;
281 }
282
283 static ULONG WINAPI IDsDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface)
284 {
285     IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface;
286     ULONG refCount = InterlockedDecrement(&This->ref);
287
288     TRACE("(%p) ref was %d\n", This, refCount + 1);
289
290     if (!refCount) {
291         IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
292         HeapFree(GetProcessHeap(), 0, This->notifies);
293         HeapFree(GetProcessHeap(),0,This);
294         TRACE("(%p) released\n",This);
295     }
296     return refCount;
297 }
298
299 static HRESULT WINAPI IDsDriverNotifyImpl_SetNotificationPositions(
300     PIDSDRIVERNOTIFY iface,
301     DWORD howmuch,
302     LPCDSBPOSITIONNOTIFY notify)
303 {
304     IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface;
305     TRACE("(%p,0x%08x,%p)\n",This,howmuch,notify);
306
307     if (!notify) {
308         WARN("invalid parameter\n");
309         return DSERR_INVALIDPARAM;
310     }
311
312     if (TRACE_ON(wave)) {
313         DWORD i;
314         for (i=0;i<howmuch;i++)
315             TRACE("notify at %d to 0x%08lx\n",
316                 notify[i].dwOffset,(DWORD_PTR)notify[i].hEventNotify);
317     }
318
319     /* Make an internal copy of the caller-supplied array.
320      * Replace the existing copy if one is already present. */
321     if (This->notifies)
322         This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
323         This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
324     else
325         This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
326         howmuch * sizeof(DSBPOSITIONNOTIFY));
327
328     memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
329     This->nrofnotifies = howmuch;
330
331     return S_OK;
332 }
333
334 static const IDsDriverNotifyVtbl dsdnvt =
335 {
336     IDsDriverNotifyImpl_QueryInterface,
337     IDsDriverNotifyImpl_AddRef,
338     IDsDriverNotifyImpl_Release,
339     IDsDriverNotifyImpl_SetNotificationPositions,
340 };
341
342 /*======================================================================*
343  *                  Low level DSOUND implementation                     *
344  *======================================================================*/
345
346 static HRESULT DSDB_MapBuffer(IDsDriverBufferImpl *dsdb)
347 {
348     TRACE("(%p), format=%dx%dx%d\n", dsdb, dsdb->wfex.Format.nSamplesPerSec,
349           dsdb->wfex.Format.wBitsPerSample, dsdb->wfex.Format.nChannels);
350     if (!dsdb->mapping) {
351         dsdb->mapping = mmap(NULL, dsdb->maplen, PROT_WRITE, MAP_SHARED,
352                              dsdb->fd, 0);
353         if (dsdb->mapping == (LPBYTE)-1) {
354             WARN("Could not map sound device for direct access (%s)\n", strerror(errno));
355             return DSERR_GENERIC;
356         }
357         TRACE("The sound device has been mapped for direct access at %p, size=%d\n", dsdb->mapping, dsdb->maplen);
358
359         /* for some reason, es1371 and sblive! sometimes have junk in here.
360          * clear it, or we get junk noise */
361         /* some libc implementations are buggy: their memset reads from the buffer...
362          * to work around it, we have to zero the block by hand. We don't do the expected:
363          * memset(dsdb->mapping,0, dsdb->maplen);
364          */
365         {
366             unsigned char*      p1 = dsdb->mapping;
367             unsigned            len = dsdb->maplen;
368             unsigned char       silence = (dsdb->wfex.Format.wBitsPerSample == 8) ? 128 : 0;
369             unsigned long       ulsilence = (dsdb->wfex.Format.wBitsPerSample == 8) ? 0x80808080 : 0;
370
371             if (len >= 16) /* so we can have at least a 4 long area to store... */
372             {
373                 /* the mmap:ed value is (at least) dword aligned
374                  * so, start filling the complete unsigned long:s
375                  */
376                 int             b = len >> 2;
377                 unsigned long*  p4 = (unsigned long*)p1;
378
379                 while (b--) *p4++ = ulsilence;
380                 /* prepare for filling the rest */
381                 len &= 3;
382                 p1 = (unsigned char*)p4;
383             }
384             /* in all cases, fill the remaining bytes */
385             while (len-- != 0) *p1++ = silence;
386         }
387     }
388     return DS_OK;
389 }
390
391 static HRESULT DSDB_UnmapBuffer(IDsDriverBufferImpl *dsdb)
392 {
393     TRACE("(%p)\n",dsdb);
394     if (dsdb->mapping) {
395         if (munmap(dsdb->mapping, dsdb->maplen) < 0) {
396             ERR("(%p): Could not unmap sound device (%s)\n", dsdb, strerror(errno));
397             return DSERR_GENERIC;
398         }
399         dsdb->mapping = NULL;
400         TRACE("(%p): sound device unmapped\n", dsdb);
401     }
402     return DS_OK;
403 }
404
405 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
406 {
407     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
408     TRACE("(%p,%s,%p)\n",iface,debugstr_guid(riid),*ppobj);
409
410     if ( IsEqualGUID(riid, &IID_IUnknown) ||
411          IsEqualGUID(riid, &IID_IDsDriverBuffer) ) {
412         IDsDriverBuffer_AddRef(iface);
413         *ppobj = This;
414         return DS_OK;
415     }
416
417     if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
418         if (!This->notify)
419             IDsDriverNotifyImpl_Create(This, &(This->notify));
420         if (This->notify) {
421             IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
422             *ppobj = This->notify;
423             return DS_OK;
424         }
425         *ppobj = 0;
426         return E_FAIL;
427     }
428
429     if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
430         if (!This->property_set)
431             IDsDriverPropertySetImpl_Create(This, &(This->property_set));
432         if (This->property_set) {
433             IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
434             *ppobj = This->property_set;
435             return DS_OK;
436         }
437         *ppobj = 0;
438         return E_FAIL;
439     }
440
441     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
442
443     *ppobj = 0;
444
445     return E_NOINTERFACE;
446 }
447
448 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
449 {
450     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
451     ULONG refCount = InterlockedIncrement(&This->ref);
452
453     TRACE("(%p) ref was %d\n", This, refCount - 1);
454
455     return refCount;
456 }
457
458 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
459 {
460     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
461     ULONG refCount = InterlockedDecrement(&This->ref);
462
463     TRACE("(%p) ref was %d\n", This, refCount + 1);
464
465     if (refCount)
466         return refCount;
467
468     if (This == This->drv->primary)
469         This->drv->primary = NULL;
470     else {
471         int i;
472         for (i = 0; i < This->drv->nrofsecondaries; i++)
473             if (This->drv->secondaries[i] == This)
474                 break;
475         if (i < This->drv->nrofsecondaries) {
476             /* Put the last buffer of the list in the (now empty) position */
477             This->drv->secondaries[i] = This->drv->secondaries[This->drv->nrofsecondaries - 1];
478             This->drv->nrofsecondaries--;
479             This->drv->secondaries = HeapReAlloc(GetProcessHeap(),0,
480                 This->drv->secondaries,
481                 sizeof(PIDSDRIVERBUFFER)*This->drv->nrofsecondaries);
482             TRACE("(%p) buffer count is now %d\n", This, This->drv->nrofsecondaries);
483         }
484
485         WOutDev[This->drv->wDevID].ossdev.ds_caps.dwFreeHwMixingAllBuffers++;
486         WOutDev[This->drv->wDevID].ossdev.ds_caps.dwFreeHwMixingStreamingBuffers++;
487     }
488
489     DSDB_UnmapBuffer(This);
490     HeapFree(GetProcessHeap(),0,This);
491     TRACE("(%p) released\n",This);
492     return 0;
493 }
494
495 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
496                                                LPVOID*ppvAudio1,LPDWORD pdwLen1,
497                                                LPVOID*ppvAudio2,LPDWORD pdwLen2,
498                                                DWORD dwWritePosition,DWORD dwWriteLen,
499                                                DWORD dwFlags)
500 {
501     /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
502     /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
503      * and that we don't support secondary buffers, this method will never be called */
504     TRACE("(%p): stub\n",iface);
505     return DSERR_UNSUPPORTED;
506 }
507
508 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
509                                                  LPVOID pvAudio1,DWORD dwLen1,
510                                                  LPVOID pvAudio2,DWORD dwLen2)
511 {
512     /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
513     TRACE("(%p): stub\n",iface);
514     return DSERR_UNSUPPORTED;
515 }
516
517 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
518                                                     LPWAVEFORMATEX pwfx)
519 {
520     /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
521
522     TRACE("(%p,%p)\n",iface,pwfx);
523     /* On our request (GetDriverDesc flags), DirectSound has by now used
524      * waveOutClose/waveOutOpen to set the format...
525      * unfortunately, this means our mmap() is now gone...
526      * so we need to somehow signal to our DirectSound implementation
527      * that it should completely recreate this HW buffer...
528      * this unexpected error code should do the trick... */
529     return DSERR_BUFFERLOST;
530 }
531
532 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
533 {
534     /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
535     TRACE("(%p,%d): stub\n",iface,dwFreq);
536     return DSERR_UNSUPPORTED;
537 }
538
539 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
540 {
541     DWORD vol;
542     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
543     TRACE("(%p,%p)\n",This,pVolPan);
544
545     vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
546
547     if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
548         WARN("wodSetVolume failed\n");
549         return DSERR_INVALIDPARAM;
550     }
551
552     return DS_OK;
553 }
554
555 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
556 {
557     /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
558     TRACE("(%p,%d): stub\n",iface,dwNewPos);
559     return DSERR_UNSUPPORTED;
560 }
561
562 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
563                                                       LPDWORD lpdwPlay, LPDWORD lpdwWrite)
564 {
565     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
566     count_info info;
567     DWORD ptr;
568
569     TRACE("(%p)\n",iface);
570     if (WOutDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
571         ERR("device not open, but accessing?\n");
572         return DSERR_UNINITIALIZED;
573     }
574     if (ioctl(This->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
575         ERR("ioctl(%s, SNDCTL_DSP_GETOPTR) failed (%s)\n",
576             WOutDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
577         return DSERR_GENERIC;
578     }
579     ptr = info.ptr & ~3; /* align the pointer, just in case */
580     if (lpdwPlay) *lpdwPlay = ptr;
581     if (lpdwWrite) {
582         /* add some safety margin (not strictly necessary, but...) */
583         if (WOutDev[This->drv->wDevID].ossdev.duplex_out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
584             *lpdwWrite = ptr + 32;
585         else
586             *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
587         while (*lpdwWrite >= This->buflen)
588             *lpdwWrite -= This->buflen;
589     }
590     TRACE("playpos=%d, writepos=%d\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
591     return DS_OK;
592 }
593
594 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
595 {
596     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
597     int enable;
598     TRACE("(%p,%x,%x,%x)\n",iface,dwRes1,dwRes2,dwFlags);
599     WOutDev[This->drv->wDevID].ossdev.bOutputEnabled = TRUE;
600     enable = getEnables(&WOutDev[This->drv->wDevID].ossdev);
601     if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
602         if (errno == EINVAL) {
603             /* Don't give up yet. OSS trigger support is inconsistent. */
604             if (WOutDev[This->drv->wDevID].ossdev.open_count == 1) {
605                 /* try the opposite input enable */
606                 if (WOutDev[This->drv->wDevID].ossdev.bInputEnabled == FALSE)
607                     WOutDev[This->drv->wDevID].ossdev.bInputEnabled = TRUE;
608                 else
609                     WOutDev[This->drv->wDevID].ossdev.bInputEnabled = FALSE;
610                 /* try it again */
611                 enable = getEnables(&WOutDev[This->drv->wDevID].ossdev);
612                 if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0)
613                     return DS_OK;
614             }
615         }
616         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
617             WOutDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
618         WOutDev[This->drv->wDevID].ossdev.bOutputEnabled = FALSE;
619         return DSERR_GENERIC;
620     }
621     return DS_OK;
622 }
623
624 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
625 {
626     IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
627     int enable;
628     TRACE("(%p)\n",iface);
629     /* no more playing */
630     WOutDev[This->drv->wDevID].ossdev.bOutputEnabled = FALSE;
631     enable = getEnables(&WOutDev[This->drv->wDevID].ossdev);
632     if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
633         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
634         return DSERR_GENERIC;
635     }
636 #if 0
637     /* the play position must be reset to the beginning of the buffer */
638     if (ioctl(This->fd, SNDCTL_DSP_RESET, 0) < 0) {
639         ERR("ioctl(%s, SNDCTL_DSP_RESET) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
640         return DSERR_GENERIC;
641     }
642 #endif
643     /* Most OSS drivers just can't stop the playback without closing the device...
644      * so we need to somehow signal to our DirectSound implementation
645      * that it should completely recreate this HW buffer...
646      * this unexpected error code should do the trick... */
647     /* FIXME: ...unless we are doing full duplex, then it's not nice to close the device */
648     if (WOutDev[This->drv->wDevID].ossdev.open_count == 1)
649         return DSERR_BUFFERLOST;
650
651     return DS_OK;
652 }
653
654 static const IDsDriverBufferVtbl dsdbvt =
655 {
656     IDsDriverBufferImpl_QueryInterface,
657     IDsDriverBufferImpl_AddRef,
658     IDsDriverBufferImpl_Release,
659     IDsDriverBufferImpl_Lock,
660     IDsDriverBufferImpl_Unlock,
661     IDsDriverBufferImpl_SetFormat,
662     IDsDriverBufferImpl_SetFrequency,
663     IDsDriverBufferImpl_SetVolumePan,
664     IDsDriverBufferImpl_SetPosition,
665     IDsDriverBufferImpl_GetPosition,
666     IDsDriverBufferImpl_Play,
667     IDsDriverBufferImpl_Stop
668 };
669
670 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
671 {
672     IDsDriverImpl *This = (IDsDriverImpl *)iface;
673     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
674
675     if ( IsEqualGUID(riid, &IID_IUnknown) ||
676          IsEqualGUID(riid, &IID_IDsDriver) ) {
677         IDsDriver_AddRef(iface);
678         *ppobj = This;
679         return DS_OK;
680     }
681
682     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
683
684     *ppobj = 0;
685
686     return E_NOINTERFACE;
687 }
688
689 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
690 {
691     IDsDriverImpl *This = (IDsDriverImpl *)iface;
692     ULONG refCount = InterlockedIncrement(&This->ref);
693
694     TRACE("(%p) ref was %d\n", This, refCount - 1);
695
696     return refCount;
697 }
698
699 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
700 {
701     IDsDriverImpl *This = (IDsDriverImpl *)iface;
702     ULONG refCount = InterlockedDecrement(&This->ref);
703
704     TRACE("(%p) ref was %d\n", This, refCount + 1);
705
706     if (!refCount) {
707         HeapFree(GetProcessHeap(),0,This);
708         TRACE("(%p) released\n",This);
709     }
710     return refCount;
711 }
712
713 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface,
714                                                   PDSDRIVERDESC pDesc)
715 {
716     IDsDriverImpl *This = (IDsDriverImpl *)iface;
717     TRACE("(%p,%p)\n",iface,pDesc);
718
719     /* copy version from driver */
720     *pDesc = WOutDev[This->wDevID].ossdev.ds_desc;
721
722     pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
723         DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK |
724         DSDDESC_DONTNEEDSECONDARYLOCK;
725     pDesc->dnDevNode            = WOutDev[This->wDevID].waveDesc.dnDevNode;
726     pDesc->wVxdId               = 0;
727     pDesc->wReserved            = 0;
728     pDesc->ulDeviceNum          = This->wDevID;
729     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
730     pDesc->pvDirectDrawHeap     = NULL;
731     pDesc->dwMemStartAddress    = 0;
732     pDesc->dwMemEndAddress      = 0;
733     pDesc->dwMemAllocExtra      = 0;
734     pDesc->pvReserved1          = NULL;
735     pDesc->pvReserved2          = NULL;
736     return DS_OK;
737 }
738
739 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
740 {
741     IDsDriverImpl *This = (IDsDriverImpl *)iface;
742     int enable;
743     TRACE("(%p)\n",iface);
744
745     /* make sure the card doesn't start playing before we want it to */
746     WOutDev[This->wDevID].ossdev.bOutputEnabled = FALSE;
747     WOutDev[This->wDevID].ossdev.bInputEnabled = FALSE;
748     enable = getEnables(&WOutDev[This->wDevID].ossdev);
749     if (ioctl(WOutDev[This->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
750         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev[This->wDevID].ossdev.dev_name, strerror(errno));
751         return DSERR_GENERIC;
752     }
753     return DS_OK;
754 }
755
756 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
757 {
758     IDsDriverImpl *This = (IDsDriverImpl *)iface;
759     TRACE("(%p)\n",iface);
760     if (This->primary) {
761         ERR("problem with DirectSound: primary not released\n");
762         return DSERR_GENERIC;
763     }
764     return DS_OK;
765 }
766
767 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
768 {
769     IDsDriverImpl *This = (IDsDriverImpl *)iface;
770     TRACE("(%p,%p)\n",iface,pCaps);
771     *pCaps = WOutDev[This->wDevID].ossdev.ds_caps;
772     return DS_OK;
773 }
774
775 static HRESULT DSD_CreatePrimaryBuffer(PIDSDRIVER iface,
776                                        LPWAVEFORMATEX pwfx,
777                                        DWORD dwFlags,
778                                        DWORD dwCardAddress,
779                                        LPDWORD pdwcbBufferSize,
780                                        LPBYTE *ppbBuffer,
781                                        LPVOID *ppvObj)
782 {
783     IDsDriverImpl *This = (IDsDriverImpl *)iface;
784     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
785     HRESULT err;
786     audio_buf_info info;
787     int enable = 0;
788     TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
789
790     if (This->primary)
791         return DSERR_ALLOCATED;
792     if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
793         return DSERR_CONTROLUNAVAIL;
794
795     *ippdsdb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverBufferImpl));
796     if (*ippdsdb == NULL)
797         return DSERR_OUTOFMEMORY;
798     (*ippdsdb)->lpVtbl  = &dsdbvt;
799     (*ippdsdb)->ref     = 1;
800     (*ippdsdb)->drv     = This;
801     copy_format(pwfx, &(*ippdsdb)->wfex);
802     (*ippdsdb)->fd      = WOutDev[This->wDevID].ossdev.fd;
803     (*ippdsdb)->dwFlags = dwFlags;
804
805     /* check how big the DMA buffer is now */
806     if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
807         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n",
808             WOutDev[This->wDevID].ossdev.dev_name, strerror(errno));
809         HeapFree(GetProcessHeap(),0,*ippdsdb);
810         *ippdsdb = NULL;
811         return DSERR_GENERIC;
812     }
813     (*ippdsdb)->maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize;
814
815     /* map the DMA buffer */
816     err = DSDB_MapBuffer(*ippdsdb);
817     if (err != DS_OK) {
818         HeapFree(GetProcessHeap(),0,*ippdsdb);
819         *ippdsdb = NULL;
820         return err;
821     }
822
823     /* primary buffer is ready to go */
824     *pdwcbBufferSize    = (*ippdsdb)->maplen;
825     *ppbBuffer          = (*ippdsdb)->mapping;
826
827     /* some drivers need some extra nudging after mapping */
828     WOutDev[This->wDevID].ossdev.bInputEnabled = FALSE;
829     WOutDev[This->wDevID].ossdev.bOutputEnabled = FALSE;
830     enable = getEnables(&WOutDev[This->wDevID].ossdev);
831     if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
832         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
833             WOutDev[This->wDevID].ossdev.dev_name, strerror(errno));
834         return DSERR_GENERIC;
835     }
836
837     This->primary = *ippdsdb;
838
839     return DS_OK;
840 }
841
842 static HRESULT DSD_CreateSecondaryBuffer(PIDSDRIVER iface,
843                                          LPWAVEFORMATEX pwfx,
844                                          DWORD dwFlags,
845                                          DWORD dwCardAddress,
846                                          LPDWORD pdwcbBufferSize,
847                                          LPBYTE *ppbBuffer,
848                                          LPVOID *ppvObj)
849 {
850     IDsDriverImpl *This = (IDsDriverImpl *)iface;
851     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
852     FIXME("(%p,%p,%x,%x,%p,%p,%p): stub\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
853
854     *ippdsdb = 0;
855     return DSERR_UNSUPPORTED;
856 }
857
858 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
859                                                       LPWAVEFORMATEX pwfx,
860                                                       DWORD dwFlags,
861                                                       DWORD dwCardAddress,
862                                                       LPDWORD pdwcbBufferSize,
863                                                       LPBYTE *ppbBuffer,
864                                                       LPVOID *ppvObj)
865 {
866     TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
867
868     if (dwFlags & DSBCAPS_PRIMARYBUFFER)
869         return DSD_CreatePrimaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
870
871     return DSD_CreateSecondaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
872 }
873
874 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
875                                                          PIDSDRIVERBUFFER pBuffer,
876                                                          LPVOID *ppvObj)
877 {
878     /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
879     TRACE("(%p,%p): stub\n",iface,pBuffer);
880     return DSERR_INVALIDCALL;
881 }
882
883 static const IDsDriverVtbl dsdvt =
884 {
885     IDsDriverImpl_QueryInterface,
886     IDsDriverImpl_AddRef,
887     IDsDriverImpl_Release,
888     IDsDriverImpl_GetDriverDesc,
889     IDsDriverImpl_Open,
890     IDsDriverImpl_Close,
891     IDsDriverImpl_GetCaps,
892     IDsDriverImpl_CreateSoundBuffer,
893     IDsDriverImpl_DuplicateSoundBuffer
894 };
895
896 static HRESULT IDsDriverPropertySetImpl_Create(
897     IDsDriverBufferImpl * dsdb,
898     IDsDriverPropertySetImpl **pdsdps)
899 {
900     IDsDriverPropertySetImpl * dsdps;
901     TRACE("(%p,%p)\n",dsdb,pdsdps);
902
903     dsdps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dsdps));
904     if (dsdps == NULL) {
905         WARN("out of memory\n");
906         return DSERR_OUTOFMEMORY;
907     }
908
909     dsdps->ref = 0;
910     dsdps->lpVtbl = &dsdpsvt;
911     dsdps->buffer = dsdb;
912     dsdb->property_set = dsdps;
913     IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
914
915     *pdsdps = dsdps;
916     return DS_OK;
917 }
918
919 static HRESULT IDsDriverNotifyImpl_Create(
920     IDsDriverBufferImpl * dsdb,
921     IDsDriverNotifyImpl **pdsdn)
922 {
923     IDsDriverNotifyImpl * dsdn;
924     TRACE("(%p,%p)\n",dsdb,pdsdn);
925
926     dsdn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dsdn));
927
928     if (dsdn == NULL) {
929         WARN("out of memory\n");
930         return DSERR_OUTOFMEMORY;
931     }
932
933     dsdn->ref = 0;
934     dsdn->lpVtbl = &dsdnvt;
935     dsdn->buffer = dsdb;
936     dsdb->notify = dsdn;
937     IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
938
939     *pdsdn = dsdn;
940     return DS_OK;
941 }
942
943 DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
944 {
945     IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
946     TRACE("(%d,%p)\n",wDevID,drv);
947
948     /* the HAL isn't much better than the HEL if we can't do mmap() */
949     if (!(WOutDev[wDevID].ossdev.duplex_out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
950         WARN("Warn DirectSound flag not set, falling back to HEL layer\n");
951         return MMSYSERR_NOTSUPPORTED;
952     }
953
954     *idrv = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverImpl));
955     if (!*idrv)
956         return MMSYSERR_NOMEM;
957     (*idrv)->lpVtbl          = &dsdvt;
958     (*idrv)->ref             = 1;
959     (*idrv)->wDevID          = wDevID;
960     (*idrv)->primary         = NULL;
961     (*idrv)->nrofsecondaries = 0;
962     (*idrv)->secondaries     = NULL;
963
964     return MMSYSERR_NOERROR;
965 }
966
967 DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
968 {
969     TRACE("(%d,%p)\n",wDevID,desc);
970     *desc = WOutDev[wDevID].ossdev.ds_desc;
971     return MMSYSERR_NOERROR;
972 }