Better implementation of GetCalendarInfo{A,W}, not perfect.
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  * 
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2001 TransGaming Technologies, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 /*
22  * Most thread locking is complete. There may be a few race
23  * conditions still lurking.
24  *
25  * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26  * and a Turtle Beach Tropez+.
27  *
28  * TODO:
29  *      Implement DirectSoundCapture API
30  *      Implement SetCooperativeLevel properly (need to address focus issues)
31  *      Implement DirectSound3DBuffers (stubs in place)
32  *      Use hardware 3D support if available
33  *      Add critical section locking inside Release and AddRef methods
34  *      Handle static buffers - put those in hardware, non-static not in hardware
35  *      Hardware DuplicateSoundBuffer
36  *      Proper volume calculation, and setting volume in HEL primary buffer
37  *      Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
38  */
39
40 #include "config.h"
41 #include "wine/port.h"
42
43 #include <assert.h>
44 #include <stdio.h>
45 #include <sys/types.h>
46 #include <sys/fcntl.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <math.h>       /* Insomnia - pow() function */
51
52 #include "windef.h"
53 #include "winbase.h"
54 #include "winreg.h"
55 #include "winuser.h"
56 #include "wingdi.h"
57 #include "winuser.h"
58 #include "winerror.h"
59 #include "mmsystem.h"
60 #include "mmddk.h"
61 #include "wine/windef16.h"
62 #include "wine/winbase16.h"
63 #include "wine/debug.h"
64 #include "dsound.h"
65 #include "dsdriver.h"
66
67 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
68
69 /* these are eligible for tuning... they must be high on slow machines... */
70 /* some stuff may get more responsive with lower values though... */
71 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
72                                 emulated dsound devices. set to 0 ! */ 
73 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
74                          * (changing this won't help you) */
75 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
76                          * (keep this close or equal to DS_HEL_QUEUE for best results) */
77 #define DS_HEL_QUEUE  5 /* HEL only: number of waveOut fragments ahead to queue to driver
78                          * (this will affect HEL sound reliability and latency) */
79
80 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
81 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
82
83 /* Linux does not support better timing than 10ms */
84 #define DS_TIME_RES 10  /* Resolution of multimedia timer */
85 #define DS_TIME_DEL 10  /* Delay of multimedia timer callback, and duration of HEL fragment */
86
87 /*****************************************************************************
88  * Predeclare the interface implementation structures
89  */
90 typedef struct IDirectSoundImpl IDirectSoundImpl;
91 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
92 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
93 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
94 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
95 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
96 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
97 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
98
99
100 /*****************************************************************************
101  * IDirectSound implementation structure
102  */
103 struct IDirectSoundImpl
104 {
105     /* IUnknown fields */
106     ICOM_VFIELD(IDirectSound);
107     DWORD                      ref;
108     /* IDirectSoundImpl fields */
109     PIDSDRIVER                  driver;
110     DSDRIVERDESC                drvdesc;
111     DSDRIVERCAPS                drvcaps;
112     HWAVEOUT                    hwo;
113     LPWAVEHDR                   pwave[DS_HEL_FRAGS];
114     UINT                        timerID, pwplay, pwwrite, pwqueue, prebuf;
115     DWORD                       fraglen;
116     DWORD                       priolevel;
117     int                         nrofbuffers;
118     IDirectSoundBufferImpl**    buffers;
119     IDirectSoundBufferImpl*     primary;
120     IDirectSound3DListenerImpl* listener;
121     WAVEFORMATEX                wfx; /* current main waveformat */
122     CRITICAL_SECTION            lock;
123 };
124
125 /*****************************************************************************
126  * IDirectSoundBuffer implementation structure
127  */
128 struct IDirectSoundBufferImpl
129 {
130     /* FIXME: document */
131     /* IUnknown fields */
132     ICOM_VFIELD(IDirectSoundBuffer);
133     DWORD                            ref;
134     /* IDirectSoundBufferImpl fields */
135     PIDSDRIVERBUFFER          hwbuf;
136     WAVEFORMATEX              wfx;
137     LPBYTE                    buffer;
138     IDirectSound3DBufferImpl* ds3db;
139     DWORD                     playflags,state,leadin;
140     DWORD                     playpos,startpos,writelead,buflen;
141     DWORD                     nAvgBytesPerSec;
142     DWORD                     freq;
143     DSVOLUMEPAN               volpan;
144     IDirectSoundBufferImpl*   parent;         /* for duplicates */
145     IDirectSoundImpl*         dsound;
146     DSBUFFERDESC              dsbd;
147     LPDSBPOSITIONNOTIFY       notifies;
148     int                       nrofnotifies;
149     CRITICAL_SECTION          lock;
150     /* used for frequency conversion (PerfectPitch) */
151     ULONG                     freqAdjust, freqAcc;
152     /* used for intelligent (well, sort of) prebuffering */
153     DWORD                     probably_valid_to;
154     DWORD                     primary_mixpos, buf_mixpos;
155     BOOL                      need_remix;
156 };
157
158 #define STATE_STOPPED  0
159 #define STATE_STARTING 1
160 #define STATE_PLAYING  2
161 #define STATE_STOPPING 3
162
163 /*****************************************************************************
164  * IDirectSoundNotify implementation structure
165  */
166 struct IDirectSoundNotifyImpl
167 {
168     /* IUnknown fields */
169     ICOM_VFIELD(IDirectSoundNotify);
170     DWORD                            ref;
171     /* IDirectSoundNotifyImpl fields */
172     IDirectSoundBufferImpl* dsb;
173 };
174
175 /*****************************************************************************
176  *  IDirectSound3DListener implementation structure
177  */
178 struct IDirectSound3DListenerImpl
179 {
180     /* IUnknown fields */
181     ICOM_VFIELD(IDirectSound3DListener);
182     DWORD                                ref;
183     /* IDirectSound3DListenerImpl fields */
184     IDirectSoundBufferImpl* dsb;
185     DS3DLISTENER            ds3dl;
186     CRITICAL_SECTION        lock;   
187 };
188
189 struct IKsPropertySetImpl 
190 {
191     /* IUnknown fields */
192     ICOM_VFIELD(IKsPropertySet);
193     DWORD                       ref;
194     /* IKsPropertySetImpl fields */
195     IDirectSound3DBufferImpl    *ds3db; /* backptr, no ref */
196 };
197
198 /*****************************************************************************
199  * IDirectSound3DBuffer implementation structure
200  */
201 struct IDirectSound3DBufferImpl
202 {
203     /* IUnknown fields */
204     ICOM_VFIELD(IDirectSound3DBuffer);
205     DWORD                              ref;
206     /* IDirectSound3DBufferImpl fields */
207     IDirectSoundBufferImpl* dsb;
208     DS3DBUFFER              ds3db;
209     LPBYTE                  buffer;
210     DWORD                   buflen;
211     CRITICAL_SECTION        lock;
212     IKsPropertySetImpl*     iks;
213 };
214
215
216 /*****************************************************************************
217  * IDirectSoundCapture implementation structure
218  */
219 struct IDirectSoundCaptureImpl
220 {
221     /* IUnknown fields */
222     ICOM_VFIELD(IDirectSoundCapture);
223     DWORD                              ref;
224
225     /* IDirectSoundCaptureImpl fields */
226     CRITICAL_SECTION        lock;
227 };
228
229 /*****************************************************************************
230  * IDirectSoundCapture implementation structure
231  */
232 struct IDirectSoundCaptureBufferImpl
233 {
234     /* IUnknown fields */
235     ICOM_VFIELD(IDirectSoundCaptureBuffer);
236     DWORD                              ref;
237
238     /* IDirectSoundCaptureBufferImpl fields */
239     CRITICAL_SECTION        lock;
240 };
241
242
243 /* #define USE_DSOUND3D 1 */
244
245 #define DSOUND_FREQSHIFT (14)
246
247 static IDirectSoundImpl*        dsound = NULL;
248
249 static IDirectSoundBufferImpl*  primarybuf = NULL;
250
251 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
252 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos);
253
254 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq);
255 static void DSOUND_PerformMix(void);
256 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
257
258 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
259 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
260
261 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
262 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
263
264 static HRESULT mmErr(UINT err)
265 {
266         switch(err) {
267         case MMSYSERR_NOERROR:
268                 return DS_OK;
269         case MMSYSERR_ALLOCATED:
270                 return DSERR_ALLOCATED;
271         case MMSYSERR_INVALHANDLE:
272                 return DSERR_GENERIC; /* FIXME */
273         case MMSYSERR_NODRIVER:
274                 return DSERR_NODRIVER;
275         case MMSYSERR_NOMEM:
276                 return DSERR_OUTOFMEMORY;
277         case MMSYSERR_INVALPARAM:
278                 return DSERR_INVALIDPARAM;
279         default:
280                 FIXME("Unknown MMSYS error %d\n",err);
281                 return DSERR_GENERIC;
282         }
283 }
284
285 static int      ds_emuldriver = DS_EMULDRIVER;
286 static int      ds_hel_margin = DS_HEL_MARGIN;
287 static int      ds_hel_queue = DS_HEL_QUEUE;
288 static int      ds_snd_queue_max = DS_SND_QUEUE_MAX;
289 static int      ds_snd_queue_min = DS_SND_QUEUE_MIN;
290
291 /*
292  * Get a config key from either the app-specific or the default config
293  */
294
295 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
296                                     char *buffer, DWORD size )
297 {
298     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
299     return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
300 }
301
302
303 /*
304  * Setup the dsound options.
305  */
306
307 inline static void setup_dsound_options(void)
308 {
309     char buffer[MAX_PATH+1];
310     HKEY hkey, appkey = 0;
311     
312     buffer[MAX_PATH]='\0';
313     
314     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
315                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
316     {
317         ERR("Cannot create config registry key\n" );
318         ExitProcess(1);
319     }
320
321     if (GetModuleFileName16( GetCurrentTask(), buffer, MAX_PATH ) ||
322         GetModuleFileNameA( 0, buffer, MAX_PATH ))
323     {
324         HKEY tmpkey;
325
326         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
327         {
328             char appname[MAX_PATH+16];
329             char *p = strrchr( buffer, '\\' );
330             if (p!=NULL) {
331                     appname[MAX_PATH]='\0';
332                     strncpy(appname,p+1,MAX_PATH);
333                     strcat(appname,"\\dsound");
334                     TRACE("appname = [%s] \n",appname);
335                     if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
336                         RegCloseKey( tmpkey );
337             }
338         }
339     }
340
341     /* get options */
342
343     if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH )) 
344         ds_emuldriver = atoi(buffer);
345  
346     if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH )) 
347         ds_hel_margin = atoi(buffer);
348
349     if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
350         ds_hel_queue = atoi(buffer);
351
352     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
353         ds_snd_queue_max = atoi(buffer);
354
355     if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
356         ds_snd_queue_min = atoi(buffer);
357
358     if (appkey) RegCloseKey( appkey );
359     RegCloseKey( hkey );
360
361     if (ds_emuldriver != DS_EMULDRIVER )
362         WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
363     if (ds_hel_margin != DS_HEL_MARGIN ) 
364         WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
365     if (ds_hel_queue != DS_HEL_QUEUE )
366         WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );          
367     if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
368         WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
369     if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
370         WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
371
372 }
373
374
375
376
377 /***************************************************************************
378  * DirectSoundEnumerateA [DSOUND.2]  
379  *
380  * Enumerate all DirectSound drivers installed in the system
381  *
382  * RETURNS
383  *    Success: DS_OK
384  *    Failure: DSERR_INVALIDPARAM
385  */
386 HRESULT WINAPI DirectSoundEnumerateA(
387         LPDSENUMCALLBACKA lpDSEnumCallback,
388         LPVOID lpContext)
389 {
390         TRACE("lpDSEnumCallback = %p, lpContext = %p\n", 
391               lpDSEnumCallback, lpContext);
392
393 #ifdef HAVE_OSS
394         if (lpDSEnumCallback != NULL)
395                 if (lpDSEnumCallback(NULL, "Primary DirectSound Driver",
396                                      "sound", lpContext))
397                         lpDSEnumCallback((LPGUID)&DSDEVID_WinePlayback,
398                                          "WINE DirectSound", "sound",
399                                          lpContext);
400 #endif
401
402         return DS_OK;
403 }
404
405 /***************************************************************************
406  * DirectSoundEnumerateW [DSOUND.3]  
407  *
408  * Enumerate all DirectSound drivers installed in the system
409  *
410  * RETURNS
411  *    Success: DS_OK
412  *    Failure: DSERR_INVALIDPARAM
413  */
414 HRESULT WINAPI DirectSoundEnumerateW(
415         LPDSENUMCALLBACKW lpDSEnumCallback, 
416         LPVOID lpContext )
417 {
418         FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n", 
419                 lpDSEnumCallback, lpContext);
420
421         return DS_OK;
422 }
423
424
425 static void _dump_DSBCAPS(DWORD xmask) {
426         struct {
427                 DWORD   mask;
428                 char    *name;
429         } flags[] = {
430 #define FE(x) { x, #x },
431                 FE(DSBCAPS_PRIMARYBUFFER)
432                 FE(DSBCAPS_STATIC)
433                 FE(DSBCAPS_LOCHARDWARE)
434                 FE(DSBCAPS_LOCSOFTWARE)
435                 FE(DSBCAPS_CTRL3D)
436                 FE(DSBCAPS_CTRLFREQUENCY)
437                 FE(DSBCAPS_CTRLPAN)
438                 FE(DSBCAPS_CTRLVOLUME)
439                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
440                 FE(DSBCAPS_CTRLDEFAULT)
441                 FE(DSBCAPS_CTRLALL)
442                 FE(DSBCAPS_STICKYFOCUS)
443                 FE(DSBCAPS_GLOBALFOCUS)
444                 FE(DSBCAPS_GETCURRENTPOSITION2)
445                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
446 #undef FE
447         };
448         int     i;
449
450         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
451                 if ((flags[i].mask & xmask) == flags[i].mask)
452                         DPRINTF("%s ",flags[i].name);
453 }
454
455 /*******************************************************************************
456  *              IKsPropertySet
457  */
458
459 /* IUnknown methods */
460 #ifdef USE_DSOUND3D
461 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
462         LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
463 ) {
464         ICOM_THIS(IKsPropertySetImpl,iface);
465
466         FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
467         return E_FAIL;
468 }
469 #endif
470
471 #ifdef USE_DSOUND3D
472 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
473         ICOM_THIS(IKsPropertySetImpl,iface);
474
475         This->ref++;
476         return This->ref;
477 }
478 #endif
479
480 #ifdef USE_DSOUND3D
481 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
482         ICOM_THIS(IKsPropertySetImpl,iface);
483
484         This->ref--;
485         return This->ref;
486 }
487 #endif
488
489 #ifdef USE_DSOUND3D
490 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
491         REFGUID guidPropSet, ULONG dwPropID,
492         LPVOID pInstanceData, ULONG cbInstanceData,
493         LPVOID pPropData, ULONG cbPropData,
494         PULONG pcbReturned
495 ) {
496         ICOM_THIS(IKsPropertySetImpl,iface);
497
498         FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
499         return E_PROP_ID_UNSUPPORTED;
500 }
501 #endif
502
503 #ifdef USE_DSOUND3D
504 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
505         REFGUID guidPropSet, ULONG dwPropID,
506         LPVOID pInstanceData, ULONG cbInstanceData,
507         LPVOID pPropData, ULONG cbPropData
508 ) {
509         ICOM_THIS(IKsPropertySetImpl,iface);
510
511         FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
512         return E_PROP_ID_UNSUPPORTED;
513 }
514 #endif
515
516 #ifdef USE_DSOUND3D
517 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
518         REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
519 ) {
520         ICOM_THIS(IKsPropertySetImpl,iface);
521
522         FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
523         return E_PROP_ID_UNSUPPORTED;
524 }
525 #endif
526
527 #ifdef USE_DSOUND3D
528 static ICOM_VTABLE(IKsPropertySet) iksvt = {
529         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
530         IKsPropertySetImpl_QueryInterface,
531         IKsPropertySetImpl_AddRef,
532         IKsPropertySetImpl_Release,
533         IKsPropertySetImpl_Get,
534         IKsPropertySetImpl_Set,
535         IKsPropertySetImpl_QuerySupport
536 };
537 #endif
538
539 /*******************************************************************************
540  *              IDirectSound3DBuffer
541  */
542
543 /* IUnknown methods */
544 #ifdef USE_DSOUND3D
545 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
546         LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
547 {
548         ICOM_THIS(IDirectSound3DBufferImpl,iface);
549
550         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
551             IDirectSound3DBuffer_AddRef(iface);
552             *ppobj = This->iks;
553             return S_OK;
554         }
555
556         FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
557         return E_FAIL;
558 }
559 #endif
560
561 #ifdef USE_DSOUND3D
562 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
563 {
564         ICOM_THIS(IDirectSound3DBufferImpl,iface);
565         This->ref++;
566         return This->ref;
567 }
568 #endif
569
570 #ifdef USE_DSOUND3D
571 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
572 {
573         ICOM_THIS(IDirectSound3DBufferImpl,iface);
574
575         TRACE("(%p) ref was %ld\n", This, This->ref);
576
577         if(--This->ref)
578                 return This->ref;
579
580         if (This->dsb)
581                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
582
583         DeleteCriticalSection(&This->lock);
584
585         HeapFree(GetProcessHeap(),0,This->buffer);
586         HeapFree(GetProcessHeap(),0,This);
587
588         return 0;
589 }
590 #endif
591
592 /* IDirectSound3DBuffer methods */
593 #ifdef USE_DSOUND3D
594 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
595         LPDIRECTSOUND3DBUFFER iface,
596         LPDS3DBUFFER lpDs3dBuffer)
597 {
598         FIXME("stub\n");
599         return DS_OK;
600 }
601 #endif
602
603 #ifdef USE_DSOUND3D
604 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
605         LPDIRECTSOUND3DBUFFER iface,
606         LPDWORD lpdwInsideConeAngle,
607         LPDWORD lpdwOutsideConeAngle)
608 {
609         FIXME("stub\n");
610         return DS_OK;
611 }
612 #endif
613
614 #ifdef USE_DSOUND3D
615 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
616         LPDIRECTSOUND3DBUFFER iface,
617         LPD3DVECTOR lpvConeOrientation)
618 {
619         FIXME("stub\n");
620         return DS_OK;
621 }
622 #endif
623
624 #ifdef USE_DSOUND3D
625 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
626         LPDIRECTSOUND3DBUFFER iface,
627         LPLONG lplConeOutsideVolume)
628 {
629         FIXME("stub\n");
630         return DS_OK;
631 }
632 #endif
633
634 #ifdef USE_DSOUND3D
635 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
636         LPDIRECTSOUND3DBUFFER iface,
637         LPD3DVALUE lpfMaxDistance)
638 {
639         FIXME("stub\n");
640         return DS_OK;
641 }
642 #endif
643
644 #ifdef USE_DSOUND3D
645 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
646         LPDIRECTSOUND3DBUFFER iface,
647         LPD3DVALUE lpfMinDistance)
648 {
649         FIXME("stub\n");
650         return DS_OK;
651 }
652 #endif
653
654 #ifdef USE_DSOUND3D
655 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
656         LPDIRECTSOUND3DBUFFER iface,
657         LPDWORD lpdwMode)
658 {
659         FIXME("stub\n");
660         return DS_OK;
661 }
662 #endif
663
664 #ifdef USE_DSOUND3D
665 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
666         LPDIRECTSOUND3DBUFFER iface,
667         LPD3DVECTOR lpvPosition)
668 {
669         FIXME("stub\n");
670         return DS_OK;
671 }
672 #endif
673
674 #ifdef USE_DSOUND3D
675 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
676         LPDIRECTSOUND3DBUFFER iface,
677         LPD3DVECTOR lpvVelocity)
678 {
679         FIXME("stub\n");
680         return DS_OK;
681 }
682 #endif
683
684 #ifdef USE_DSOUND3D
685 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
686         LPDIRECTSOUND3DBUFFER iface,
687         LPCDS3DBUFFER lpcDs3dBuffer,
688         DWORD dwApply)
689 {
690         FIXME("stub\n");
691         return DS_OK;
692 }
693 #endif
694
695 #ifdef USE_DSOUND3D
696 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
697         LPDIRECTSOUND3DBUFFER iface,
698         DWORD dwInsideConeAngle,
699         DWORD dwOutsideConeAngle,
700         DWORD dwApply)
701 {
702         FIXME("stub\n");
703         return DS_OK;
704 }
705 #endif
706
707 #ifdef USE_DSOUND3D
708 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
709         LPDIRECTSOUND3DBUFFER iface,
710         D3DVALUE x, D3DVALUE y, D3DVALUE z,
711         DWORD dwApply)
712 {
713         FIXME("stub\n");
714         return DS_OK;
715 }
716 #endif
717
718 #ifdef USE_DSOUND3D
719 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
720         LPDIRECTSOUND3DBUFFER iface,
721         LONG lConeOutsideVolume,
722         DWORD dwApply)
723 {
724         FIXME("stub\n");
725         return DS_OK;
726 }
727 #endif
728
729 #ifdef USE_DSOUND3D
730 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
731         LPDIRECTSOUND3DBUFFER iface,
732         D3DVALUE fMaxDistance,
733         DWORD dwApply)
734 {
735         FIXME("stub\n");
736         return DS_OK;
737 }
738 #endif
739
740 #ifdef USE_DSOUND3D
741 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
742         LPDIRECTSOUND3DBUFFER iface,
743         D3DVALUE fMinDistance,
744         DWORD dwApply)
745 {
746         FIXME("stub\n");
747         return DS_OK;
748 }
749 #endif
750
751 #ifdef USE_DSOUND3D
752 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
753         LPDIRECTSOUND3DBUFFER iface,
754         DWORD dwMode,
755         DWORD dwApply)
756 {
757         ICOM_THIS(IDirectSound3DBufferImpl,iface);
758         TRACE("mode = %lx\n", dwMode);
759         This->ds3db.dwMode = dwMode;
760         return DS_OK;
761 }
762 #endif
763
764 #ifdef USE_DSOUND3D
765 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
766         LPDIRECTSOUND3DBUFFER iface,
767         D3DVALUE x, D3DVALUE y, D3DVALUE z,
768         DWORD dwApply)
769 {
770         FIXME("stub\n");
771         return DS_OK;
772 }
773 #endif
774
775 #ifdef USE_DSOUND3D
776 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
777         LPDIRECTSOUND3DBUFFER iface,
778         D3DVALUE x, D3DVALUE y, D3DVALUE z,
779         DWORD dwApply)
780 {
781         FIXME("stub\n");
782         return DS_OK;
783 }
784 #endif
785
786 #ifdef USE_DSOUND3D
787 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt = 
788 {
789         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
790         /* IUnknown methods */
791         IDirectSound3DBufferImpl_QueryInterface,
792         IDirectSound3DBufferImpl_AddRef,
793         IDirectSound3DBufferImpl_Release,
794         /* IDirectSound3DBuffer methods */
795         IDirectSound3DBufferImpl_GetAllParameters,
796         IDirectSound3DBufferImpl_GetConeAngles,
797         IDirectSound3DBufferImpl_GetConeOrientation,
798         IDirectSound3DBufferImpl_GetConeOutsideVolume,
799         IDirectSound3DBufferImpl_GetMaxDistance,
800         IDirectSound3DBufferImpl_GetMinDistance,
801         IDirectSound3DBufferImpl_GetMode,
802         IDirectSound3DBufferImpl_GetPosition,
803         IDirectSound3DBufferImpl_GetVelocity,
804         IDirectSound3DBufferImpl_SetAllParameters,
805         IDirectSound3DBufferImpl_SetConeAngles,
806         IDirectSound3DBufferImpl_SetConeOrientation,
807         IDirectSound3DBufferImpl_SetConeOutsideVolume,
808         IDirectSound3DBufferImpl_SetMaxDistance,
809         IDirectSound3DBufferImpl_SetMinDistance,
810         IDirectSound3DBufferImpl_SetMode,
811         IDirectSound3DBufferImpl_SetPosition,
812         IDirectSound3DBufferImpl_SetVelocity,
813 };
814 #endif
815
816 #ifdef USE_DSOUND3D
817 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
818 {
819         DWORD   i, temp, iSize, oSize, offset;
820         LPBYTE  bIbuf, bObuf, bTbuf = NULL;
821         LPWORD  wIbuf, wObuf, wTbuf = NULL;
822
823         /* Inside DirectX says it's stupid but allowed */
824         if (dsb->wfx.nChannels == 2) {
825                 /* Convert to mono */
826                 if (dsb->wfx.wBitsPerSample == 16) {
827                         iSize = dsb->buflen / 4;
828                         wTbuf = malloc(dsb->buflen / 2);
829                         if (wTbuf == NULL)
830                                 return DSERR_OUTOFMEMORY;
831                         for (i = 0; i < iSize; i++)
832                                 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
833                         wIbuf = wTbuf;
834                 } else {
835                         iSize = dsb->buflen / 2;
836                         bTbuf = malloc(dsb->buflen / 2);
837                         if (bTbuf == NULL)
838                                 return DSERR_OUTOFMEMORY;
839                         for (i = 0; i < iSize; i++)
840                                 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
841                         bIbuf = bTbuf;
842                 }
843         } else {
844                 if (dsb->wfx.wBitsPerSample == 16) {
845                         iSize = dsb->buflen / 2;
846                         wIbuf = (LPWORD) dsb->buffer;
847                 } else {
848                         bIbuf = (LPBYTE) dsb->buffer;
849                         iSize = dsb->buflen;
850                 }
851         }
852
853         if (primarybuf->wfx.wBitsPerSample == 16) {
854                 wObuf = (LPWORD) dsb->ds3db->buffer;
855                 oSize = dsb->ds3db->buflen / 2;
856         } else {
857                 bObuf = (LPBYTE) dsb->ds3db->buffer;
858                 oSize = dsb->ds3db->buflen;
859         }
860
861         offset = primarybuf->wfx.nSamplesPerSec / 100;          /* 10ms */
862         if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
863                 for (i = 0; i < iSize; i++) {
864                         temp = wIbuf[i];
865                         if (i >= offset)
866                                 temp += wIbuf[i - offset] >> 9;
867                         else
868                                 temp += wIbuf[i + iSize - offset] >> 9;
869                         wObuf[i * 2] = temp;
870                         wObuf[(i * 2) + 1] = temp;
871                 }
872         else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
873                 for (i = 0; i < iSize; i++) {
874                         temp = bIbuf[i];
875                         if (i >= offset)
876                                 temp += bIbuf[i - offset] >> 5;
877                         else
878                                 temp += bIbuf[i + iSize - offset] >> 5;
879                         bObuf[i * 2] = temp;
880                         bObuf[(i * 2) + 1] = temp;
881                 }
882         
883         if (wTbuf)
884                 free(wTbuf);
885         if (bTbuf)
886                 free(bTbuf);
887
888         return DS_OK;
889 }
890 #endif
891 /*******************************************************************************
892  *              IDirectSound3DListener
893  */
894
895 /* IUnknown methods */
896 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
897         LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
898 {
899         ICOM_THIS(IDirectSound3DListenerImpl,iface);
900
901         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
902         return E_FAIL;
903 }
904         
905 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
906 {
907         ICOM_THIS(IDirectSound3DListenerImpl,iface);
908         This->ref++;
909         return This->ref;
910 }
911
912 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
913 {
914         ULONG ulReturn;
915         ICOM_THIS(IDirectSound3DListenerImpl,iface);
916
917         TRACE("(%p) ref was %ld\n", This, This->ref);
918
919         ulReturn = --This->ref;
920
921         /* Free all resources */
922         if( ulReturn == 0 ) {
923                 if(This->dsb)
924                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
925                 DeleteCriticalSection(&This->lock);
926                 HeapFree(GetProcessHeap(),0,This);
927         }
928
929         return ulReturn;
930 }
931
932 /* IDirectSound3DListener methods */
933 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
934         LPDIRECTSOUND3DLISTENER iface,
935         LPDS3DLISTENER lpDS3DL)
936 {
937         FIXME("stub\n");
938         return DS_OK;
939 }
940
941 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
942         LPDIRECTSOUND3DLISTENER iface,
943         LPD3DVALUE lpfDistanceFactor)
944 {
945         FIXME("stub\n");
946         return DS_OK;
947 }
948
949 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
950         LPDIRECTSOUND3DLISTENER iface,
951         LPD3DVALUE lpfDopplerFactor)
952 {
953         FIXME("stub\n");
954         return DS_OK;
955 }
956
957 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
958         LPDIRECTSOUND3DLISTENER iface,
959         LPD3DVECTOR lpvOrientFront,
960         LPD3DVECTOR lpvOrientTop)
961 {
962         FIXME("stub\n");
963         return DS_OK;
964 }
965
966 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
967         LPDIRECTSOUND3DLISTENER iface,
968         LPD3DVECTOR lpvPosition)
969 {
970         FIXME("stub\n");
971         return DS_OK;
972 }
973
974 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
975         LPDIRECTSOUND3DLISTENER iface,
976         LPD3DVALUE lpfRolloffFactor)
977 {
978         FIXME("stub\n");
979         return DS_OK;
980 }
981
982 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
983         LPDIRECTSOUND3DLISTENER iface,
984         LPD3DVECTOR lpvVelocity)
985 {
986         FIXME("stub\n");
987         return DS_OK;
988 }
989
990 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
991         LPDIRECTSOUND3DLISTENER iface,
992         LPCDS3DLISTENER lpcDS3DL,
993         DWORD dwApply)
994 {
995         FIXME("stub\n");
996         return DS_OK;
997 }
998
999 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
1000         LPDIRECTSOUND3DLISTENER iface,
1001         D3DVALUE fDistanceFactor,
1002         DWORD dwApply)
1003 {
1004         FIXME("stub\n");
1005         return DS_OK;
1006 }
1007
1008 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
1009         LPDIRECTSOUND3DLISTENER iface,
1010         D3DVALUE fDopplerFactor,
1011         DWORD dwApply)
1012 {
1013         FIXME("stub\n");
1014         return DS_OK;
1015 }
1016
1017 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
1018         LPDIRECTSOUND3DLISTENER iface,
1019         D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
1020         D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
1021         DWORD dwApply)
1022 {
1023         FIXME("stub\n");
1024         return DS_OK;
1025 }
1026
1027 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
1028         LPDIRECTSOUND3DLISTENER iface,
1029         D3DVALUE x, D3DVALUE y, D3DVALUE z,
1030         DWORD dwApply)
1031 {
1032         FIXME("stub\n");
1033         return DS_OK;
1034 }
1035
1036 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
1037         LPDIRECTSOUND3DLISTENER iface,
1038         D3DVALUE fRolloffFactor,
1039         DWORD dwApply)
1040 {
1041         FIXME("stub\n");
1042         return DS_OK;
1043 }
1044
1045 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
1046         LPDIRECTSOUND3DLISTENER iface,
1047         D3DVALUE x, D3DVALUE y, D3DVALUE z,
1048         DWORD dwApply)
1049 {
1050         FIXME("stub\n");
1051         return DS_OK;
1052 }
1053
1054 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
1055         LPDIRECTSOUND3DLISTENER iface)
1056
1057 {
1058         FIXME("stub\n");
1059         return DS_OK;
1060 }
1061
1062 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt = 
1063 {
1064         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1065         /* IUnknown methods */
1066         IDirectSound3DListenerImpl_QueryInterface,
1067         IDirectSound3DListenerImpl_AddRef,
1068         IDirectSound3DListenerImpl_Release,
1069         /* IDirectSound3DListener methods */
1070         IDirectSound3DListenerImpl_GetAllParameter,
1071         IDirectSound3DListenerImpl_GetDistanceFactor,
1072         IDirectSound3DListenerImpl_GetDopplerFactor,
1073         IDirectSound3DListenerImpl_GetOrientation,
1074         IDirectSound3DListenerImpl_GetPosition,
1075         IDirectSound3DListenerImpl_GetRolloffFactor,
1076         IDirectSound3DListenerImpl_GetVelocity,
1077         IDirectSound3DListenerImpl_SetAllParameters,
1078         IDirectSound3DListenerImpl_SetDistanceFactor,
1079         IDirectSound3DListenerImpl_SetDopplerFactor,
1080         IDirectSound3DListenerImpl_SetOrientation,
1081         IDirectSound3DListenerImpl_SetPosition,
1082         IDirectSound3DListenerImpl_SetRolloffFactor,
1083         IDirectSound3DListenerImpl_SetVelocity,
1084         IDirectSound3DListenerImpl_CommitDeferredSettings,
1085 };
1086
1087 /*******************************************************************************
1088  *              IDirectSoundNotify
1089  */
1090 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
1091         LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
1092 ) {
1093         ICOM_THIS(IDirectSoundNotifyImpl,iface);
1094
1095         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1096         return E_FAIL;
1097 }
1098
1099 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
1100         ICOM_THIS(IDirectSoundNotifyImpl,iface);
1101         return ++(This->ref);
1102 }
1103
1104 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
1105         ICOM_THIS(IDirectSoundNotifyImpl,iface);
1106
1107         TRACE("(%p) ref was %ld\n", This, This->ref);
1108
1109         This->ref--;
1110         if (!This->ref) {
1111                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
1112                 HeapFree(GetProcessHeap(),0,This);
1113                 return 0;
1114         }
1115         return This->ref;
1116 }
1117
1118 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
1119         LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
1120 ) {
1121         ICOM_THIS(IDirectSoundNotifyImpl,iface);
1122         int     i;
1123
1124         if (TRACE_ON(dsound)) {
1125             TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1126             for (i=0;i<howmuch;i++)
1127                     TRACE("notify at %ld to 0x%08lx\n",
1128                             notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1129         }
1130         This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1131         memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1132                 notify,
1133                 howmuch*sizeof(DSBPOSITIONNOTIFY)
1134         );
1135         This->dsb->nrofnotifies+=howmuch;
1136
1137         return S_OK;
1138 }
1139
1140 static ICOM_VTABLE(IDirectSoundNotify) dsnvt = 
1141 {
1142         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1143         IDirectSoundNotifyImpl_QueryInterface,
1144         IDirectSoundNotifyImpl_AddRef,
1145         IDirectSoundNotifyImpl_Release,
1146         IDirectSoundNotifyImpl_SetNotificationPositions,
1147 };
1148
1149 /*******************************************************************************
1150  *              IDirectSoundBuffer
1151  */
1152
1153 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1154 {
1155         double temp;
1156
1157         /* the AmpFactors are expressed in 16.16 fixed point */
1158         volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1159         /* FIXME: dwPan{Left|Right}AmpFactor */
1160
1161         /* FIXME: use calculated vol and pan ampfactors */
1162         temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1163         volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1164         temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1165         volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1166
1167         TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1168 }
1169
1170 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1171 {
1172         DWORD sw;
1173
1174         sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1175         if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1176                 DWORD fraglen;
1177                 /* let fragment size approximate the timer delay */
1178                 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1179                 /* reduce fragment size until an integer number of them fits in the buffer */
1180                 /* (FIXME: this may or may not be a good idea) */
1181                 while (dsb->buflen % fraglen) fraglen -= sw;
1182                 dsb->dsound->fraglen = fraglen;
1183                 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1184         }
1185         /* calculate the 10ms write lead */
1186         dsb->writelead = (dsb->freq / 100) * sw;
1187 }
1188
1189 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1190 {
1191         HRESULT err = DS_OK;
1192
1193         /* are we using waveOut stuff? */
1194         if (!dsb->hwbuf) {
1195                 LPBYTE newbuf;
1196                 DWORD buflen;
1197                 HRESULT merr = DS_OK;
1198                 /* Start in pause mode, to allow buffers to get filled */
1199                 waveOutPause(dsb->dsound->hwo);
1200                 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1201                 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1202                 /* use fragments of 10ms (1/100s) each (which should get us within
1203                  * the documented write cursor lead of 10-15ms) */
1204                 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1205                 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1206                 /* reallocate emulated primary buffer */
1207                 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1208                 if (newbuf == NULL) {
1209                         ERR("failed to allocate primary buffer\n");
1210                         merr = DSERR_OUTOFMEMORY;
1211                         /* but the old buffer might still exists and must be re-prepared */
1212                 } else {
1213                         dsb->buffer = newbuf;
1214                         dsb->buflen = buflen;
1215                 }
1216                 if (dsb->buffer) {
1217                         unsigned c;
1218                         IDirectSoundImpl *ds = dsb->dsound;
1219
1220                         ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1221
1222                         /* prepare fragment headers */
1223                         for (c=0; c<DS_HEL_FRAGS; c++) {
1224                                 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1225                                 ds->pwave[c]->dwBufferLength = ds->fraglen;
1226                                 ds->pwave[c]->dwUser = (DWORD)dsb;
1227                                 ds->pwave[c]->dwFlags = 0;
1228                                 ds->pwave[c]->dwLoops = 0;
1229                                 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1230                                 if (err != DS_OK) {
1231                                         while (c--)
1232                                                 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1233                                         break;
1234                                 }
1235                         }
1236
1237                         ds->pwplay = 0;
1238                         ds->pwwrite = 0;
1239                         ds->pwqueue = 0;
1240                         memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1241                         TRACE("fraglen=%ld\n", ds->fraglen);
1242                         DSOUND_WaveQueue(dsb->dsound, (DWORD)-1);
1243                 }
1244                 if ((err == DS_OK) && (merr != DS_OK))
1245                         err = merr;
1246         }
1247         return err;
1248 }
1249
1250
1251 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1252 {
1253         /* are we using waveOut stuff? */
1254         if (!dsb->hwbuf) {
1255                 unsigned c;
1256                 IDirectSoundImpl *ds = dsb->dsound;
1257
1258                 ds->pwqueue = (DWORD)-1; /* resetting queues */
1259                 waveOutReset(ds->hwo);
1260                 for (c=0; c<DS_HEL_FRAGS; c++)
1261                         waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1262                 ds->pwqueue = 0;
1263         }
1264 }
1265
1266 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1267 {
1268         HRESULT err = DS_OK;
1269         if (dsb->hwbuf)
1270                 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1271         else
1272                 err = mmErr(waveOutRestart(dsb->dsound->hwo));
1273         return err;
1274 }
1275
1276 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1277 {
1278         HRESULT err = DS_OK;
1279
1280         TRACE("\n");
1281
1282         if (dsb->hwbuf) {
1283                 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1284                 if (err == DSERR_BUFFERLOST) {
1285                         /* Wine-only: the driver wants us to reopen the device */
1286                         /* FIXME: check for errors */
1287                         IDsDriverBuffer_Release(primarybuf->hwbuf);
1288                         waveOutClose(dsb->dsound->hwo);
1289                         dsb->dsound->hwo = 0;
1290                         err = mmErr(waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1291                                                 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)dsb->dsound,
1292                                                 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
1293                         if (err == DS_OK)
1294                             err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1295                                                               &(dsb->buflen),&(dsb->buffer),
1296                                                               (LPVOID)&(dsb->hwbuf));
1297                 }
1298         }
1299         else
1300                 err = mmErr(waveOutPause(dsb->dsound->hwo));
1301         return err;
1302 }
1303
1304 /* This sets this format for the <em>Primary Buffer Only</em> */
1305 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1306 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1307         LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1308 ) {
1309         ICOM_THIS(IDirectSoundBufferImpl,iface);
1310         IDirectSoundBufferImpl** dsb;
1311         HRESULT err = DS_OK;
1312         int                     i;
1313
1314         /* Let's be pedantic! */
1315         if ((wfex == NULL) ||
1316             (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1317             (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1318             (wfex->nSamplesPerSec < 1) ||
1319             (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1320             ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1321                 TRACE("failed pedantic check!\n");
1322                 return DSERR_INVALIDPARAM;
1323         }
1324
1325         /* **** */
1326         EnterCriticalSection(&(This->dsound->lock));
1327
1328         if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1329                 dsb = dsound->buffers;
1330                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1331                         /* **** */
1332                         EnterCriticalSection(&((*dsb)->lock));
1333
1334                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1335                                 wfex->nSamplesPerSec;
1336
1337                         LeaveCriticalSection(&((*dsb)->lock));
1338                         /* **** */
1339                 }
1340         }
1341
1342         memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1343
1344         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1345                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1346                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1347                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
1348                    wfex->wBitsPerSample, wfex->cbSize);
1349
1350         primarybuf->wfx.nAvgBytesPerSec =
1351                 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1352
1353         if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1354                 /* FIXME: check for errors */
1355                 DSOUND_PrimaryClose(primarybuf);
1356                 waveOutClose(This->dsound->hwo);
1357                 This->dsound->hwo = 0;
1358                 err = mmErr(waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1359                                         &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)This->dsound,
1360                                         CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
1361                 if (err == DS_OK)
1362                     DSOUND_PrimaryOpen(primarybuf);
1363         }
1364         if (primarybuf->hwbuf) {
1365                 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1366                 if (err == DSERR_BUFFERLOST) {
1367                         /* Wine-only: the driver wants us to recreate the HW buffer */
1368                         IDsDriverBuffer_Release(primarybuf->hwbuf);
1369                         err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1370                                                           &(primarybuf->buflen),&(primarybuf->buffer),
1371                                                           (LPVOID)&(primarybuf->hwbuf));
1372                         if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1373                         else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1374                 }
1375                 /* FIXME: should we set err back to DS_OK in all cases ? */
1376         }
1377         DSOUND_RecalcFormat(primarybuf);
1378
1379         LeaveCriticalSection(&(This->dsound->lock));
1380         /* **** */
1381
1382         return err;
1383 }
1384
1385 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1386         LPDIRECTSOUNDBUFFER iface,LONG vol
1387 ) {
1388         ICOM_THIS(IDirectSoundBufferImpl,iface);
1389
1390         TRACE("(%p,%ld)\n",This,vol);
1391
1392         /* I'm not sure if we need this for primary buffer */
1393         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1394                 return DSERR_CONTROLUNAVAIL;
1395
1396         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1397                 return DSERR_INVALIDPARAM;
1398
1399         /* **** */
1400         EnterCriticalSection(&(This->lock));
1401
1402         This->volpan.lVolume = vol;
1403
1404         DSOUND_RecalcVolPan(&(This->volpan));
1405
1406         if (This->hwbuf) {
1407                 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1408         }
1409         else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1410 #if 0 /* should we really do this? */
1411                 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1412                 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1413                 WORD cvol = 0xffff + vol*6 + vol/2;
1414                 DWORD vol = cvol | ((DWORD)cvol << 16)
1415                 waveOutSetVolume(This->dsound->hwo, vol);
1416 #endif
1417         }
1418
1419         LeaveCriticalSection(&(This->lock));
1420         /* **** */
1421
1422         return DS_OK;
1423 }
1424
1425 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1426         LPDIRECTSOUNDBUFFER iface,LPLONG vol
1427 ) {
1428         ICOM_THIS(IDirectSoundBufferImpl,iface);
1429         TRACE("(%p,%p)\n",This,vol);
1430
1431         if (vol == NULL)
1432                 return DSERR_INVALIDPARAM;
1433
1434         *vol = This->volpan.lVolume;
1435         return DS_OK;
1436 }
1437
1438 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1439         LPDIRECTSOUNDBUFFER iface,DWORD freq
1440 ) {
1441         ICOM_THIS(IDirectSoundBufferImpl,iface);
1442         TRACE("(%p,%ld)\n",This,freq);
1443
1444         /* You cannot set the frequency of the primary buffer */
1445         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1446             (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1447                 return DSERR_CONTROLUNAVAIL;
1448
1449         if (!freq) freq = This->wfx.nSamplesPerSec;
1450
1451         if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1452                 return DSERR_INVALIDPARAM;
1453
1454         /* **** */
1455         EnterCriticalSection(&(This->lock));
1456
1457         This->freq = freq;
1458         This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1459         This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1460         DSOUND_RecalcFormat(This);
1461
1462         LeaveCriticalSection(&(This->lock));
1463         /* **** */
1464
1465         return DS_OK;
1466 }
1467
1468 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1469         LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1470 ) {
1471         ICOM_THIS(IDirectSoundBufferImpl,iface);
1472         TRACE("(%p,%08lx,%08lx,%08lx)\n",
1473                 This,reserved1,reserved2,flags
1474         );
1475
1476         /* **** */
1477         EnterCriticalSection(&(This->lock));
1478
1479         This->playflags = flags;
1480         if (This->state == STATE_STOPPED) {
1481                 This->leadin = TRUE;
1482                 This->startpos = This->buf_mixpos;
1483                 This->state = STATE_STARTING;
1484         } else if (This->state == STATE_STOPPING)
1485                 This->state = STATE_PLAYING;
1486         if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1487                 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1488                 This->state = STATE_PLAYING;
1489         }
1490
1491         LeaveCriticalSection(&(This->lock));
1492         /* **** */
1493
1494         return DS_OK;
1495 }
1496
1497 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1498 {
1499         ICOM_THIS(IDirectSoundBufferImpl,iface);
1500         TRACE("(%p)\n",This);
1501
1502         /* **** */
1503         EnterCriticalSection(&(This->lock));
1504
1505         if (This->state == STATE_PLAYING)
1506                 This->state = STATE_STOPPING;
1507         else if (This->state == STATE_STARTING)
1508                 This->state = STATE_STOPPED;
1509         if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1510                 IDsDriverBuffer_Stop(This->hwbuf);
1511                 This->state = STATE_STOPPED;
1512         }
1513         DSOUND_CheckEvent(This, 0);
1514
1515         LeaveCriticalSection(&(This->lock));
1516         /* **** */
1517
1518         return DS_OK;
1519 }
1520
1521 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1522         ICOM_THIS(IDirectSoundBufferImpl,iface);
1523         DWORD ref;
1524
1525         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1526
1527         ref = InterlockedIncrement(&(This->ref));
1528         if (!ref) {
1529                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1530         }
1531         return ref;
1532 }
1533 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1534         ICOM_THIS(IDirectSoundBufferImpl,iface);
1535         int     i;
1536         DWORD ref;
1537
1538         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1539
1540         ref = InterlockedDecrement(&(This->ref));
1541         if (ref) return ref;
1542
1543         EnterCriticalSection(&(This->dsound->lock));
1544         for (i=0;i<This->dsound->nrofbuffers;i++)
1545                 if (This->dsound->buffers[i] == This)
1546                         break;
1547
1548         if (i < This->dsound->nrofbuffers) {
1549                 /* Put the last buffer of the list in the (now empty) position */
1550                 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1551                 This->dsound->nrofbuffers--;
1552                 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1553                 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1554                 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1555         }
1556         LeaveCriticalSection(&(This->dsound->lock));
1557
1558         DeleteCriticalSection(&(This->lock));
1559         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1560                 DSOUND_PrimaryClose(This);
1561         if (This->hwbuf) {
1562                 IDsDriverBuffer_Release(This->hwbuf);
1563         }
1564         if (This->ds3db)
1565                 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1566         if (This->parent)
1567                 /* this is a duplicate buffer */
1568                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1569         else
1570                 /* this is a toplevel buffer */
1571                 HeapFree(GetProcessHeap(),0,This->buffer);
1572
1573         HeapFree(GetProcessHeap(),0,This);
1574         
1575         if (This == primarybuf)
1576                 primarybuf = NULL;
1577
1578         return 0;
1579 }
1580
1581 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1582                                      DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1583 {
1584         DWORD bplay;
1585
1586         TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1587         TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
1588
1589         /* the actual primary play position (pplay) is always behind last mixed (pmix),
1590          * unless the computer is too slow or something */
1591         /* we need to know how far away we are from there */
1592 #if 0 /* we'll never fill the primary entirely */
1593         if (pmix == pplay) {
1594                 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1595                         /* wow, the software mixer is really doing well,
1596                          * seems the entire primary buffer is filled! */
1597                         pmix += primarybuf->buflen;
1598                 }
1599                 /* else: the primary buffer is not playing, so probably empty */
1600         }
1601 #endif
1602         if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1603         pmix -= pplay;
1604         /* detect buffer underrun */
1605         if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1606         pwrite -= pplay;
1607         if (pmix > (ds_snd_queue_max * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1608                 WARN("detected an underrun: primary queue was %ld\n",pmix);
1609                 pmix = 0;
1610         }
1611         /* divide the offset by its sample size */
1612         pmix /= primarybuf->wfx.nBlockAlign;
1613         TRACE("primary back-samples=%ld\n",pmix);
1614         /* adjust for our frequency */
1615         pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1616         /* multiply by our own sample size */
1617         pmix *= This->wfx.nBlockAlign;
1618         TRACE("this back-offset=%ld\n", pmix);
1619         /* subtract from our last mixed position */
1620         bplay = bmix;
1621         while (bplay < pmix) bplay += This->buflen; /* wraparound */
1622         bplay -= pmix;
1623         if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1624                 /* seems we haven't started playing yet */
1625                 TRACE("this still in lead-in phase\n");
1626                 bplay = This->startpos;
1627         }
1628         /* return the result */
1629         return bplay;
1630 }
1631
1632 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1633         LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1634 ) {
1635         HRESULT hres; 
1636         ICOM_THIS(IDirectSoundBufferImpl,iface);
1637         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1638         if (This->hwbuf) {
1639                 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1640                 if (hres)
1641                     return hres;
1642         }
1643         else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1644                 if (playpos) {
1645                         MMTIME mtime;
1646                         mtime.wType = TIME_BYTES;
1647                         waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1648                         mtime.u.cb = mtime.u.cb % This->buflen;
1649                         *playpos = mtime.u.cb;
1650                 }
1651                 if (writepos) {
1652                         /* the writepos should only be used by apps with WRITEPRIMARY priority,
1653                          * in which case our software mixer is disabled anyway */
1654                         *writepos = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
1655                         while (*writepos >= This->buflen)
1656                                 *writepos -= This->buflen;
1657                 }
1658         } else {
1659                 if (playpos && (This->state != STATE_PLAYING)) {
1660                         /* we haven't been merged into the primary buffer (yet) */
1661                         *playpos = This->buf_mixpos;
1662                 }
1663                 else if (playpos) {
1664                         DWORD pplay, pwrite, lplay, splay, pstate;
1665                         /* let's get this exact; first, recursively call GetPosition on the primary */
1666                         EnterCriticalSection(&(primarybuf->lock));
1667                         IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1668                         /* detect HEL mode underrun */
1669                         pstate = primarybuf->state;
1670                         if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1671                                 TRACE("detected an underrun\n");
1672                                 /* pplay = ? */
1673                                 if (pstate == STATE_PLAYING)
1674                                         pstate = STATE_STARTING;
1675                                 else if (pstate == STATE_STOPPING)
1676                                         pstate = STATE_STOPPED;
1677                         }
1678                         /* get data for ourselves while we still have the lock */
1679                         pstate &= This->state;
1680                         lplay = This->primary_mixpos;
1681                         splay = This->buf_mixpos;
1682                         if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
1683                                 /* calculate play position using this */
1684                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1685                         } else {
1686                                 /* (unless the app isn't using GETCURRENTPOSITION2) */
1687                                 /* don't know exactly how this should be handled...
1688                                  * the docs says that play cursor is reported as directly
1689                                  * behind write cursor, hmm... */
1690                                 /* let's just do what might work for Half-Life */
1691                                 DWORD wp;
1692                                 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
1693                                 while (wp >= primarybuf->buflen)
1694                                         wp -= primarybuf->buflen;
1695                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
1696                         }
1697                         LeaveCriticalSection(&(primarybuf->lock));
1698                 }
1699                 if (writepos) *writepos = This->buf_mixpos;
1700         }
1701         if (writepos) {
1702                 if (This->state != STATE_STOPPED)
1703                         /* apply the documented 10ms lead to writepos */
1704                         *writepos += This->writelead;
1705                 while (*writepos >= This->buflen) *writepos -= This->buflen;
1706         }
1707         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1708         return DS_OK;
1709 }
1710
1711 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1712         LPDIRECTSOUNDBUFFER iface,LPDWORD status
1713 ) {
1714         ICOM_THIS(IDirectSoundBufferImpl,iface);
1715         TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1716
1717         if (status == NULL)
1718                 return DSERR_INVALIDPARAM;
1719
1720         *status = 0;
1721         if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
1722                 *status |= DSBSTATUS_PLAYING;
1723                 if (This->playflags & DSBPLAY_LOOPING)
1724                         *status |= DSBSTATUS_LOOPING;
1725         }
1726
1727         TRACE("status=%lx\n", *status);
1728         return DS_OK;
1729 }
1730
1731
1732 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1733         LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1734 ) {
1735         ICOM_THIS(IDirectSoundBufferImpl,iface);
1736         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1737
1738         if (wfsize>sizeof(This->wfx))
1739                 wfsize = sizeof(This->wfx);
1740         if (lpwf) {     /* NULL is valid */
1741                 memcpy(lpwf,&(This->wfx),wfsize);
1742                 if (wfwritten)
1743                         *wfwritten = wfsize;
1744         } else
1745                 if (wfwritten)
1746                         *wfwritten = sizeof(This->wfx);
1747                 else
1748                         return DSERR_INVALIDPARAM;
1749
1750         return DS_OK;
1751 }
1752
1753 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1754         LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1755 ) {
1756         ICOM_THIS(IDirectSoundBufferImpl,iface);
1757         DWORD capf;
1758
1759         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1760                 This,
1761                 writecursor,
1762                 writebytes,
1763                 lplpaudioptr1,
1764                 audiobytes1,
1765                 lplpaudioptr2,
1766                 audiobytes2,
1767                 flags,
1768                 GetTickCount()
1769         );
1770
1771         if (flags & DSBLOCK_FROMWRITECURSOR) {
1772                 DWORD writepos;
1773                 /* GetCurrentPosition does too much magic to duplicate here */
1774                 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1775                 writecursor += writepos;
1776         }
1777         if (flags & DSBLOCK_ENTIREBUFFER)
1778                 writebytes = This->buflen;
1779         if (writebytes > This->buflen)
1780                 writebytes = This->buflen;
1781
1782         assert(audiobytes1!=audiobytes2);
1783         assert(lplpaudioptr1!=lplpaudioptr2);
1784
1785         if ((writebytes == This->buflen) &&
1786             ((This->state == STATE_STARTING) ||
1787              (This->state == STATE_PLAYING)))
1788                 /* some games, like Half-Life, try to be clever (not) and
1789                  * keep one secondary buffer, and mix sounds into it itself,
1790                  * locking the entire buffer every time... so we can just forget
1791                  * about tracking the last-written-to-position... */
1792                 This->probably_valid_to = (DWORD)-1;
1793         else
1794                 This->probably_valid_to = writecursor;
1795
1796         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1797                 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1798         else
1799                 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1800         if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1801                 IDsDriverBuffer_Lock(This->hwbuf,
1802                                      lplpaudioptr1, audiobytes1,
1803                                      lplpaudioptr2, audiobytes2,
1804                                      writecursor, writebytes,
1805                                      0);
1806         }
1807         else {
1808                 BOOL remix = FALSE;
1809                 if (writecursor+writebytes <= This->buflen) {
1810                         *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1811                         *audiobytes1 = writebytes;
1812                         if (lplpaudioptr2)
1813                                 *(LPBYTE*)lplpaudioptr2 = NULL;
1814                         if (audiobytes2)
1815                                 *audiobytes2 = 0;
1816                         TRACE("->%ld.0\n",writebytes);
1817                 } else {
1818                         *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1819                         *audiobytes1 = This->buflen-writecursor;
1820                         if (lplpaudioptr2)
1821                                 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1822                         if (audiobytes2)
1823                                 *audiobytes2 = writebytes-(This->buflen-writecursor);
1824                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1825                 }
1826                 /* if the segment between playpos and buf_mixpos is touched,
1827                  * we need to cancel some mixing */
1828                 if (This->buf_mixpos >= This->playpos) {
1829                         if (This->buf_mixpos > writecursor &&
1830                             This->playpos <= writecursor+writebytes)
1831                                 remix = TRUE;
1832                 }
1833                 else {
1834                         if (This->buf_mixpos > writecursor ||
1835                             This->playpos <= writecursor+writebytes)
1836                                 remix = TRUE;
1837                 }
1838                 if (remix) {
1839                         TRACE("locking prebuffered region, ouch\n");
1840                         DSOUND_MixCancelAt(This, writecursor);
1841                 }
1842         }
1843         return DS_OK;
1844 }
1845
1846 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1847         LPDIRECTSOUNDBUFFER iface,DWORD newpos
1848 ) {
1849         ICOM_THIS(IDirectSoundBufferImpl,iface);
1850         TRACE("(%p,%ld)\n",This,newpos);
1851
1852         /* **** */
1853         EnterCriticalSection(&(This->lock));
1854
1855         This->buf_mixpos = newpos;
1856         if (This->hwbuf)
1857                 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1858
1859         LeaveCriticalSection(&(This->lock));
1860         /* **** */
1861
1862         return DS_OK;
1863 }
1864
1865 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1866         LPDIRECTSOUNDBUFFER iface,LONG pan
1867 ) {
1868         ICOM_THIS(IDirectSoundBufferImpl,iface);
1869
1870         TRACE("(%p,%ld)\n",This,pan);
1871
1872         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1873                 return DSERR_INVALIDPARAM;
1874
1875         /* You cannot set the pan of the primary buffer */
1876         /* and you cannot use both pan and 3D controls */
1877         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1878             (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1879             (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1880                 return DSERR_CONTROLUNAVAIL;
1881
1882         /* **** */
1883         EnterCriticalSection(&(This->lock));
1884
1885         This->volpan.lPan = pan;
1886
1887         DSOUND_RecalcVolPan(&(This->volpan));
1888
1889         if (This->hwbuf) {
1890                 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1891         }
1892
1893         LeaveCriticalSection(&(This->lock));
1894         /* **** */
1895
1896         return DS_OK;
1897 }
1898
1899 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1900         LPDIRECTSOUNDBUFFER iface,LPLONG pan
1901 ) {
1902         ICOM_THIS(IDirectSoundBufferImpl,iface);
1903         TRACE("(%p,%p)\n",This,pan);
1904
1905         if (pan == NULL)
1906                 return DSERR_INVALIDPARAM;
1907
1908         *pan = This->volpan.lPan;
1909
1910         return DS_OK;
1911 }
1912
1913 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1914         LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1915 ) {
1916         ICOM_THIS(IDirectSoundBufferImpl,iface);
1917         DWORD capf, probably_valid_to;
1918
1919         TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1920
1921 #if 0
1922         /* Preprocess 3D buffers... */
1923
1924         /* This is highly experimental and liable to break things */
1925         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1926                 DSOUND_Create3DBuffer(This);
1927 #endif
1928
1929         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1930                 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1931         else
1932                 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1933         if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1934                 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1935         }
1936
1937         if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1938         else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1939         while (probably_valid_to >= This->buflen)
1940                 probably_valid_to -= This->buflen;
1941         if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1942             ((This->state == STATE_STARTING) ||
1943              (This->state == STATE_PLAYING)))
1944                 /* see IDirectSoundBufferImpl_Lock */
1945                 probably_valid_to = (DWORD)-1;
1946         This->probably_valid_to = probably_valid_to;
1947
1948         return DS_OK;
1949 }
1950
1951 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1952         LPDIRECTSOUNDBUFFER iface
1953 ) {
1954         ICOM_THIS(IDirectSoundBufferImpl,iface);
1955         FIXME("(%p):stub\n",This);
1956         return DS_OK;
1957 }
1958
1959 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1960         LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1961 ) {
1962         ICOM_THIS(IDirectSoundBufferImpl,iface);
1963         TRACE("(%p,%p)\n",This,freq);
1964
1965         if (freq == NULL)
1966                 return DSERR_INVALIDPARAM;
1967
1968         *freq = This->freq;
1969         TRACE("-> %ld\n", *freq);
1970
1971         return DS_OK;
1972 }
1973
1974 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1975         LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1976 ) {
1977         ICOM_THIS(IDirectSoundBufferImpl,iface);
1978         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1979         DPRINTF("Re-Init!!!\n");
1980         return DSERR_ALREADYINITIALIZED;
1981 }
1982
1983 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1984         LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1985 ) {
1986         ICOM_THIS(IDirectSoundBufferImpl,iface);
1987         TRACE("(%p)->(%p)\n",This,caps);
1988   
1989         if (caps == NULL)
1990                 return DSERR_INVALIDPARAM;
1991
1992         /* I think we should check this value, not set it. See */
1993         /* Inside DirectX, p215. That should apply here, too. */
1994         caps->dwSize = sizeof(*caps);
1995
1996         caps->dwFlags = This->dsbd.dwFlags;
1997         if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1998         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1999
2000         caps->dwBufferBytes = This->buflen;
2001
2002         /* This value represents the speed of the "unlock" command.
2003            As unlock is quite fast (it does not do anything), I put
2004            4096 ko/s = 4 Mo / s */
2005         /* FIXME: hwbuf speed */
2006         caps->dwUnlockTransferRate = 4096;
2007         caps->dwPlayCpuOverhead = 0;
2008
2009         return DS_OK;
2010 }
2011
2012 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
2013         LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
2014 ) {
2015         ICOM_THIS(IDirectSoundBufferImpl,iface);
2016
2017         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2018
2019         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
2020                 IDirectSoundNotifyImpl  *dsn;
2021
2022                 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
2023                 dsn->ref = 1;
2024                 dsn->dsb = This;
2025                 IDirectSoundBuffer_AddRef(iface);
2026                 ICOM_VTBL(dsn) = &dsnvt;
2027                 *ppobj = (LPVOID)dsn;
2028                 return S_OK;
2029         }
2030
2031 #ifdef USE_DSOUND3D
2032         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
2033                 IDirectSound3DBufferImpl        *ds3db;
2034
2035                 *ppobj = This->ds3db;
2036                 if (*ppobj) {
2037                         IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
2038                         return S_OK;
2039                 }
2040
2041                 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2042                         0,sizeof(*ds3db));
2043                 ds3db->ref = 1;
2044                 ds3db->dsb = This;
2045                 ICOM_VTBL(ds3db) = &ds3dbvt;
2046                 InitializeCriticalSection(&ds3db->lock);
2047
2048                 IDirectSoundBuffer_AddRef(iface);
2049
2050                 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2051                 ds3db->ds3db.vPosition.u1.x = 0.0;
2052                 ds3db->ds3db.vPosition.u2.y = 0.0;
2053                 ds3db->ds3db.vPosition.u3.z = 0.0;
2054                 ds3db->ds3db.vVelocity.u1.x = 0.0;
2055                 ds3db->ds3db.vVelocity.u2.y = 0.0;
2056                 ds3db->ds3db.vVelocity.u3.z = 0.0;
2057                 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2058                 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2059                 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2060                 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2061                 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2062                 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;                ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2063                 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2064                 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2065                 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
2066                         This->wfx.nBlockAlign;
2067                 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2068                 if (ds3db->buffer == NULL) {
2069                         ds3db->buflen = 0;
2070                         ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2071                 }
2072
2073                 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2074                 ds3db->iks->ref = 1;
2075                 ds3db->iks->ds3db = ds3db;
2076                 ICOM_VTBL(ds3db->iks) = &iksvt;
2077
2078                 return S_OK;
2079         }
2080 #else
2081         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
2082                 FIXME("%s: I know about this GUID, but don't support it yet\n",
2083                       debugstr_guid( riid ));
2084                 *ppobj = NULL;
2085                 return E_FAIL;
2086         }
2087 #endif
2088
2089 #if USE_DSOUND3D
2090         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2091                 IDirectSound3DListenerImpl* dsl;
2092
2093                 if (This->dsound->listener) {
2094                         *ppobj = This->dsound->listener;
2095                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
2096                         return DS_OK;
2097                 }
2098
2099                 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
2100                 dsl->ref = 1;
2101                 ICOM_VTBL(dsl) = &ds3dlvt;
2102                 *ppobj = (LPVOID)dsl;
2103
2104                 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
2105                 dsl->ds3dl.vPosition.u1.x = 0.0;
2106                 dsl->ds3dl.vPosition.u2.y = 0.0;
2107                 dsl->ds3dl.vPosition.u3.z = 0.0;
2108                 dsl->ds3dl.vVelocity.u1.x = 0.0;
2109                 dsl->ds3dl.vVelocity.u2.y = 0.0;
2110                 dsl->ds3dl.vVelocity.u3.z = 0.0;
2111                 dsl->ds3dl.vOrientFront.u1.x = 0.0;
2112                 dsl->ds3dl.vOrientFront.u2.y = 0.0;
2113                 dsl->ds3dl.vOrientFront.u3.z = 1.0;
2114                 dsl->ds3dl.vOrientTop.u1.x = 0.0;
2115                 dsl->ds3dl.vOrientTop.u2.y = 1.0;
2116                 dsl->ds3dl.vOrientTop.u3.z = 0.0;
2117                 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2118                 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2119
2120                 InitializeCriticalSection(&dsl->lock);
2121
2122                 dsl->dsb = This;
2123                 IDirectSoundBuffer_AddRef(iface);
2124
2125                 This->dsound->listener = dsl;
2126                 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
2127
2128                 return S_OK;
2129         }
2130 #else
2131         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2132                 FIXME("%s: I know about this GUID, but don't support it yet\n",
2133                       debugstr_guid( riid ));
2134                 *ppobj = NULL;
2135                 return E_FAIL;
2136         }
2137 #endif
2138
2139         FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
2140
2141         *ppobj = NULL;
2142
2143         return E_FAIL;
2144 }
2145
2146 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt = 
2147 {
2148         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2149         IDirectSoundBufferImpl_QueryInterface,
2150         IDirectSoundBufferImpl_AddRef,
2151         IDirectSoundBufferImpl_Release,
2152         IDirectSoundBufferImpl_GetCaps,
2153         IDirectSoundBufferImpl_GetCurrentPosition,
2154         IDirectSoundBufferImpl_GetFormat,
2155         IDirectSoundBufferImpl_GetVolume,
2156         IDirectSoundBufferImpl_GetPan,
2157         IDirectSoundBufferImpl_GetFrequency,
2158         IDirectSoundBufferImpl_GetStatus,
2159         IDirectSoundBufferImpl_Initialize,
2160         IDirectSoundBufferImpl_Lock,
2161         IDirectSoundBufferImpl_Play,
2162         IDirectSoundBufferImpl_SetCurrentPosition,
2163         IDirectSoundBufferImpl_SetFormat,
2164         IDirectSoundBufferImpl_SetVolume,
2165         IDirectSoundBufferImpl_SetPan,
2166         IDirectSoundBufferImpl_SetFrequency,
2167         IDirectSoundBufferImpl_Stop,
2168         IDirectSoundBufferImpl_Unlock,
2169         IDirectSoundBufferImpl_Restore
2170 };
2171
2172 /*******************************************************************************
2173  *              IDirectSound
2174  */
2175
2176 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2177         LPDIRECTSOUND iface,HWND hwnd,DWORD level
2178 ) {
2179         ICOM_THIS(IDirectSoundImpl,iface);
2180
2181         FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2182
2183         This->priolevel = level;
2184
2185         return DS_OK;
2186 }
2187
2188 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2189         LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2190 ) {
2191         ICOM_THIS(IDirectSoundImpl,iface);
2192         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2193         LPWAVEFORMATEX  wfex;
2194         HRESULT err = DS_OK;
2195
2196         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2197         
2198         if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2199                 return DSERR_INVALIDPARAM;
2200         
2201         if (TRACE_ON(dsound)) {
2202                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2203                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2204                 _dump_DSBCAPS(dsbd->dwFlags);
2205                 DPRINTF(")\n");
2206                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2207                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2208         }
2209
2210         wfex = dsbd->lpwfxFormat;
2211
2212         if (wfex)
2213                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2214                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2215                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2216                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
2217                    wfex->wBitsPerSample, wfex->cbSize);
2218
2219         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2220                 if (primarybuf) {
2221                         IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2222                         *ippdsb = primarybuf;
2223                         primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2224                         return DS_OK;
2225                 } /* Else create primary buffer */
2226         } else {
2227                 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
2228                         ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
2229                         return DSERR_INVALIDPARAM; /* FIXME: which error? */
2230                 }
2231         }
2232
2233         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2234         if (*ippdsb == NULL)
2235                 return DSERR_OUTOFMEMORY;
2236         ICOM_VTBL(*ippdsb) = &dsbvt;
2237         (*ippdsb)->ref = 1;
2238         (*ippdsb)->dsound = This;
2239         (*ippdsb)->parent = NULL;
2240         (*ippdsb)->buffer = NULL;
2241
2242         memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2243         if (dsbd->lpwfxFormat)
2244                 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2245
2246         TRACE("Created buffer at %p\n", *ippdsb);
2247
2248         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2249                 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2250                 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2251
2252                 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2253
2254                 if (This->driver) {
2255                         err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2256                                                           &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2257                                                           (LPVOID*)&((*ippdsb)->hwbuf));
2258                 }
2259                 if (err == DS_OK)
2260                         err = DSOUND_PrimaryOpen(*ippdsb);
2261         } else {
2262                 DWORD capf = 0;
2263                 int use_hw;
2264
2265                 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2266                 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2267
2268                 /* Check necessary hardware mixing capabilities */
2269                 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2270                 else capf |= DSCAPS_SECONDARYMONO;
2271                 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2272                 else capf |= DSCAPS_SECONDARY8BIT;
2273                 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2274
2275                 /* FIXME: check hardware sample rate mixing capabilities */
2276                 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2277                 /* FIXME: check whether any hardware buffers are left */
2278                 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2279
2280                 /* Allocate system memory if applicable */
2281                 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2282                         (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2283                         if ((*ippdsb)->buffer == NULL)
2284                                 err = DSERR_OUTOFMEMORY;
2285                 }
2286
2287                 /* Allocate the hardware buffer */
2288                 if (use_hw && (err == DS_OK)) {
2289                         err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2290                                                           &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2291                                                           (LPVOID*)&((*ippdsb)->hwbuf));
2292                 }
2293         }
2294
2295         if (err != DS_OK) {
2296                 if ((*ippdsb)->buffer)
2297                         HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2298                 HeapFree(GetProcessHeap(),0,(*ippdsb));
2299                 *ippdsb = NULL;
2300                 return err;
2301         }
2302         /* calculate fragment size and write lead */
2303         DSOUND_RecalcFormat(*ippdsb);
2304
2305         /* It's not necessary to initialize values to zero since */
2306         /* we allocated this structure with HEAP_ZERO_MEMORY... */
2307         (*ippdsb)->playpos = 0;
2308         (*ippdsb)->buf_mixpos = 0;
2309         (*ippdsb)->state = STATE_STOPPED;
2310         DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2311
2312         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2313                 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2314                         primarybuf->wfx.nSamplesPerSec;
2315                 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2316                         dsbd->lpwfxFormat->nBlockAlign;
2317         }
2318
2319         EnterCriticalSection(&(This->lock));
2320         /* register buffer */
2321         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2322                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2323                 if (newbuffers) {
2324                         This->buffers = newbuffers;
2325                         This->buffers[This->nrofbuffers] = *ippdsb;
2326                         This->nrofbuffers++;
2327                         TRACE("buffer count is now %d\n", This->nrofbuffers);
2328                 } else {
2329                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2330                         err = DSERR_OUTOFMEMORY;
2331                 }
2332         }
2333         LeaveCriticalSection(&(This->lock));
2334
2335         IDirectSound_AddRef(iface);
2336
2337         InitializeCriticalSection(&((*ippdsb)->lock));
2338
2339         if (err != DS_OK) {
2340                 /* oops... */
2341                 IDirectSoundBuffer_Release(*ppdsb);
2342                 *ippdsb = NULL;
2343                 return err;
2344         }
2345         
2346 #ifdef USE_DSOUND3D
2347         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2348                 IDirectSound3DBufferImpl        *ds3db;
2349
2350                 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2351                         0,sizeof(*ds3db));
2352                 ICOM_VTBL(ds3db) = &ds3dbvt;
2353                 ds3db->ref = 1;
2354                 (*ippdsb)->ds3db = ds3db;
2355
2356                 ds3db->dsb = (*ippdsb);
2357                 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2358
2359                 InitializeCriticalSection(&ds3db->lock);
2360
2361                 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2362                 ds3db->ds3db.vPosition.u1.x = 0.0;
2363                 ds3db->ds3db.vPosition.u2.y = 0.0;
2364                 ds3db->ds3db.vPosition.u3.z = 0.0;
2365                 ds3db->ds3db.vVelocity.u1.x = 0.0;
2366                 ds3db->ds3db.vVelocity.u2.y = 0.0;
2367                 ds3db->ds3db.vVelocity.u3.z = 0.0;
2368                 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2369                 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2370                 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2371                 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2372                 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2373                 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2374                 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2375                 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2376                 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2377                 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2378                         (*ippdsb)->wfx.nBlockAlign;
2379                 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2380                 if (ds3db->buffer == NULL) {
2381                         ds3db->buflen = 0;
2382                         ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2383                 }
2384                 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2385                 ds3db->iks->ref = 1;
2386                 ds3db->iks->ds3db = ds3db;
2387                 ICOM_VTBL(ds3db->iks) = &iksvt;
2388
2389         }
2390 #endif
2391         return DS_OK;
2392 }
2393
2394 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2395         LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2396 ) {
2397         ICOM_THIS(IDirectSoundImpl,iface);
2398         IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2399         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2400         TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2401
2402         if (ipdsb->hwbuf) {
2403                 FIXME("need to duplicate hardware buffer\n");
2404         }
2405
2406         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2407
2408         IDirectSoundBuffer_AddRef(pdsb);
2409         memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2410         (*ippdsb)->ref = 1;
2411         (*ippdsb)->state = STATE_STOPPED;
2412         (*ippdsb)->playpos = 0;
2413         (*ippdsb)->buf_mixpos = 0;
2414         (*ippdsb)->dsound = This;
2415         (*ippdsb)->parent = ipdsb;
2416         memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2417         InitializeCriticalSection(&(*ippdsb)->lock);
2418         /* register buffer */
2419         EnterCriticalSection(&(This->lock));
2420         {
2421                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2422                 if (newbuffers) {
2423                         This->buffers = newbuffers;
2424                         This->buffers[This->nrofbuffers] = *ippdsb;
2425                         This->nrofbuffers++;
2426                         TRACE("buffer count is now %d\n", This->nrofbuffers);
2427                 } else {
2428                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2429                         /* FIXME: release buffer */
2430                 }
2431         }
2432         LeaveCriticalSection(&(This->lock));
2433         IDirectSound_AddRef(iface);
2434         return DS_OK;
2435 }
2436
2437
2438 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2439         ICOM_THIS(IDirectSoundImpl,iface);
2440         TRACE("(%p,%p)\n",This,caps);
2441         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2442
2443         if (caps == NULL)
2444                 return DSERR_INVALIDPARAM;
2445
2446         /* We should check this value, not set it. See Inside DirectX, p215. */
2447         caps->dwSize = sizeof(*caps);
2448
2449         caps->dwFlags = This->drvcaps.dwFlags;
2450
2451         /* FIXME: copy caps from This->drvcaps */
2452         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
2453         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
2454
2455         caps->dwPrimaryBuffers                  = 1;
2456
2457         caps->dwMaxHwMixingAllBuffers           = 0;
2458         caps->dwMaxHwMixingStaticBuffers        = 0;
2459         caps->dwMaxHwMixingStreamingBuffers     = 0;
2460
2461         caps->dwFreeHwMixingAllBuffers          = 0;
2462         caps->dwFreeHwMixingStaticBuffers       = 0;
2463         caps->dwFreeHwMixingStreamingBuffers    = 0;
2464
2465         caps->dwMaxHw3DAllBuffers               = 0;
2466         caps->dwMaxHw3DStaticBuffers            = 0;
2467         caps->dwMaxHw3DStreamingBuffers         = 0;
2468
2469         caps->dwFreeHw3DAllBuffers              = 0;
2470         caps->dwFreeHw3DStaticBuffers           = 0;
2471         caps->dwFreeHw3DStreamingBuffers        = 0;
2472
2473         caps->dwTotalHwMemBytes                 = 0;
2474
2475         caps->dwFreeHwMemBytes                  = 0;
2476
2477         caps->dwMaxContigFreeHwMemBytes         = 0;
2478
2479         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
2480
2481         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
2482
2483         return DS_OK;
2484 }
2485
2486 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2487         ICOM_THIS(IDirectSoundImpl,iface);
2488         return ++(This->ref);
2489 }
2490
2491 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2492         ICOM_THIS(IDirectSoundImpl,iface);
2493         TRACE("(%p), ref was %ld\n",This,This->ref);
2494         if (!--(This->ref)) {
2495                 UINT i;
2496
2497                 timeKillEvent(This->timerID);
2498                 timeEndPeriod(DS_TIME_RES);
2499
2500                 if (primarybuf)
2501                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2502
2503                 if (This->buffers) {
2504                         for( i=0;i<This->nrofbuffers;i++)       
2505                                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2506                 }
2507
2508                 if (This->primary)
2509                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2510
2511                 DeleteCriticalSection(&This->lock);
2512                 if (This->driver) {
2513                         IDsDriver_Close(This->driver);
2514                 } else {
2515                         unsigned c;
2516                         for (c=0; c<DS_HEL_FRAGS; c++)
2517                                 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2518                 }
2519                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2520                         waveOutClose(This->hwo);
2521                 }
2522                 if (This->driver)
2523                         IDsDriver_Release(This->driver);
2524
2525                 HeapFree(GetProcessHeap(),0,This);
2526                 dsound = NULL;
2527                 return 0;
2528         }
2529         return This->ref;
2530 }
2531
2532 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2533         LPDIRECTSOUND iface,DWORD config
2534 ) {
2535         ICOM_THIS(IDirectSoundImpl,iface);
2536         FIXME("(%p,0x%08lx):stub\n",This,config);
2537         return DS_OK;
2538 }
2539
2540 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2541         LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2542 ) {
2543         ICOM_THIS(IDirectSoundImpl,iface);
2544
2545         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2546
2547                 if (This->listener) {
2548                         *ppobj = This->listener;
2549                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2550                         return DS_OK;
2551                 }
2552
2553                 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2554                         GetProcessHeap(), 0, sizeof(*(This->listener)));
2555                 This->listener->ref = 1;
2556                 ICOM_VTBL(This->listener) = &ds3dlvt;
2557                 *ppobj = (LPVOID)This->listener;
2558                 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj); 
2559
2560                 This->listener->dsb = NULL; 
2561
2562                 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2563                 This->listener->ds3dl.vPosition.u1.x = 0.0;
2564                 This->listener->ds3dl.vPosition.u2.y = 0.0;
2565                 This->listener->ds3dl.vPosition.u3.z = 0.0;
2566                 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2567                 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2568                 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2569                 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2570                 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2571                 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2572                 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2573                 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2574                 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2575                 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2576                 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2577                 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2578
2579                 InitializeCriticalSection(&This->listener->lock);
2580
2581                 return DS_OK;
2582         }
2583
2584         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2585         return E_FAIL;
2586 }
2587
2588 static HRESULT WINAPI IDirectSoundImpl_Compact(
2589         LPDIRECTSOUND iface)
2590 {
2591         ICOM_THIS(IDirectSoundImpl,iface);
2592         TRACE("(%p)\n", This);
2593         return DS_OK;
2594 }
2595
2596 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2597         LPDIRECTSOUND iface,
2598         LPDWORD lpdwSpeakerConfig)
2599 {
2600         ICOM_THIS(IDirectSoundImpl,iface);
2601         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2602         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2603         return DS_OK;
2604 }
2605
2606 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2607         LPDIRECTSOUND iface,
2608         LPCGUID lpcGuid)
2609 {
2610         ICOM_THIS(IDirectSoundImpl,iface);
2611         TRACE("(%p, %p)\n", This, lpcGuid);
2612         return DS_OK;
2613 }
2614
2615 static ICOM_VTABLE(IDirectSound) dsvt = 
2616 {
2617         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2618         IDirectSoundImpl_QueryInterface,
2619         IDirectSoundImpl_AddRef,
2620         IDirectSoundImpl_Release,
2621         IDirectSoundImpl_CreateSoundBuffer,
2622         IDirectSoundImpl_GetCaps,
2623         IDirectSoundImpl_DuplicateSoundBuffer,
2624         IDirectSoundImpl_SetCooperativeLevel,
2625         IDirectSoundImpl_Compact,
2626         IDirectSoundImpl_GetSpeakerConfig,
2627         IDirectSoundImpl_SetSpeakerConfig,
2628         IDirectSoundImpl_Initialize
2629 };
2630
2631
2632 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2633 {
2634         int                     i;
2635         DWORD                   offset;
2636         LPDSBPOSITIONNOTIFY     event;
2637
2638         if (dsb->nrofnotifies == 0)
2639                 return;
2640
2641         TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2642                 dsb, dsb->buflen, dsb->playpos, len);
2643         for (i = 0; i < dsb->nrofnotifies ; i++) {
2644                 event = dsb->notifies + i;
2645                 offset = event->dwOffset;
2646                 TRACE("checking %d, position %ld, event = %d\n",
2647                         i, offset, event->hEventNotify);
2648                 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2649                 /* OK. [Inside DirectX, p274] */
2650                 /*  */
2651                 /* This also means we can't sort the entries by offset, */
2652                 /* because DSBPN_OFFSETSTOP == -1 */
2653                 if (offset == DSBPN_OFFSETSTOP) {
2654                         if (dsb->state == STATE_STOPPED) {
2655                                 SetEvent(event->hEventNotify);
2656                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2657                                 return;
2658                         } else
2659                                 return;
2660                 }
2661                 if ((dsb->playpos + len) >= dsb->buflen) {
2662                         if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2663                             (offset >= dsb->playpos)) {
2664                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2665                                 SetEvent(event->hEventNotify);
2666                         }
2667                 } else {
2668                         if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2669                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2670                                 SetEvent(event->hEventNotify);
2671                         }
2672                 }
2673         }
2674 }
2675
2676 /* WAV format info can be found at: */
2677 /* */
2678 /*      http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2679 /*      ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2680 /* */
2681 /* Import points to remember: */
2682 /* */
2683 /*      8-bit WAV is unsigned */
2684 /*      16-bit WAV is signed */
2685
2686 static inline INT16 cvtU8toS16(BYTE byte)
2687 {
2688         INT16   s = (byte - 128) << 8;
2689
2690         return s;
2691 }
2692
2693 static inline BYTE cvtS16toU8(INT16 word)
2694 {
2695         BYTE    b = (word + 32768) >> 8;
2696         
2697         return b;
2698 }
2699
2700
2701 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf )
2702 {
2703         INT     fl = 0, fr = 0;
2704         if (dsb->wfx.nChannels == 2) {
2705                 if (dsb->wfx.wBitsPerSample == 8)  {
2706                         /* avoid needless 8->16->8 conversion */
2707                         if ( (primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2) ) {
2708                                 *obuf=*ibuf;
2709                                 *(obuf+1)=*(ibuf+1);
2710                                 return;
2711                         }
2712                         fl = cvtU8toS16(*ibuf);
2713                         fr = cvtU8toS16(*(ibuf + 1));
2714                 } else if (dsb->wfx.wBitsPerSample == 16) {
2715                         fl = *((INT16 *)ibuf);
2716                         fr = *(((INT16 *)ibuf) + 1);
2717                 }
2718         } else if (dsb->wfx.nChannels == 1) {
2719                 if (dsb->wfx.wBitsPerSample == 8) {
2720                         /* avoid needless 8->16->8 conversion */
2721                         if ( (primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1) ) {
2722                                 *obuf=*ibuf;
2723                                 return;
2724                         }
2725                         fl = cvtU8toS16(*ibuf);
2726                         fr = fl;
2727                 } else if (dsb->wfx.wBitsPerSample == 16) {
2728                         fl = *((INT16 *)ibuf);
2729                         fr = fl;
2730                 }
2731         }
2732         if (primarybuf->wfx.nChannels == 2) {
2733                 if (primarybuf->wfx.wBitsPerSample == 8) {
2734                         *obuf = cvtS16toU8(fl);
2735                         *(obuf + 1) = cvtS16toU8(fr);
2736                         return;
2737                 }
2738                 if (primarybuf->wfx.wBitsPerSample == 16) {
2739                         *((INT16 *)obuf) = fl;
2740                         *(((INT16 *)obuf) + 1) = fr;
2741                         return;
2742                 }
2743         }       
2744         if (primarybuf->wfx.nChannels == 1) {
2745                 fl = (fl + fr) >> 1;
2746                 if (primarybuf->wfx.wBitsPerSample == 8) {
2747                         *obuf = cvtS16toU8(fl);
2748                         return;
2749                 }
2750                 if (primarybuf->wfx.wBitsPerSample == 16) {
2751                         *((INT16 *)obuf) = fl;
2752                         return;
2753                 }
2754         }
2755 }
2756
2757 /* Now with PerfectPitch (tm) technology */
2758 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2759 {
2760         INT     i, size, ipos, ilen;
2761         BYTE    *ibp, *obp;
2762         INT     iAdvance = dsb->wfx.nBlockAlign;
2763         INT     oAdvance = primarybuf->wfx.nBlockAlign;
2764
2765         ibp = dsb->buffer + dsb->buf_mixpos;
2766         obp = buf;
2767
2768         TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2769         /* Check for the best case */
2770         if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2771             (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2772             (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2773                 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2774                 TRACE("(%p) Best case\n", dsb);
2775                 if (len <= bytesleft )
2776                         memcpy(obp, ibp, len);
2777                 else { /* wrap */
2778                         memcpy(obp, ibp, bytesleft );
2779                         memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2780                 }
2781                 return len;
2782         }
2783         
2784         /* Check for same sample rate */
2785         if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2786                 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2787                         dsb->freq, primarybuf->wfx.nSamplesPerSec);
2788                 ilen = 0;
2789                 for (i = 0; i < len; i += oAdvance) {
2790                         cp_fields(dsb, ibp, obp );
2791                         ibp += iAdvance;
2792                         ilen += iAdvance;
2793                         obp += oAdvance;
2794                         if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2795                                 ibp = dsb->buffer;      /* wrap */
2796                 }
2797                 return (ilen);  
2798         }
2799
2800         /* Mix in different sample rates */
2801         /* */
2802         /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2803         /* Patent Pending :-] */
2804
2805         /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2806          * TransGaming Technologies Inc. */
2807
2808 /*      FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2809                 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2810 */
2811         size = len / oAdvance;
2812         ilen = 0;
2813         ipos = dsb->buf_mixpos;
2814         for (i = 0; i < size; i++) {
2815                 cp_fields(dsb, (dsb->buffer + ipos), obp);
2816                 obp += oAdvance;
2817                 dsb->freqAcc += dsb->freqAdjust;
2818                 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2819                         ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2820                         dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2821                         ipos += adv; ilen += adv;
2822                         while (ipos >= dsb->buflen)
2823                                 ipos -= dsb->buflen;
2824                 }
2825         }
2826         return ilen;
2827 }
2828
2829 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2830 {
2831         INT     i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2832         BYTE    *bpc = buf;
2833         INT16   *bps = (INT16 *) buf;
2834         
2835         TRACE("(%p) left = %lx, right = %lx\n", dsb,
2836                 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2837         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2838             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2839             !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2840                 return;         /* Nothing to do */
2841
2842         /* If we end up with some bozo coder using panning or 3D sound */
2843         /* with a mono primary buffer, it could sound very weird using */
2844         /* this method. Oh well, tough patooties. */
2845
2846         for (i = 0; i < len; i += inc) {
2847                 INT     val;
2848
2849                 switch (inc) {
2850
2851                 case 1:
2852                         /* 8-bit WAV is unsigned, but we need to operate */
2853                         /* on signed data for this to work properly */
2854                         val = *bpc - 128;
2855                         val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2856                         *bpc = val + 128;
2857                         bpc++;
2858                         break;
2859                 case 2:
2860                         /* 16-bit WAV is signed -- much better */
2861                         val = *bps;
2862                         val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2863                         *bps = val;
2864                         bps++;
2865                         break;
2866                 default:
2867                         /* Very ugly! */
2868                         FIXME("MixerVol had a nasty error\n");
2869                 }
2870         }               
2871 }
2872
2873 #ifdef USE_DSOUND3D
2874 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2875 {
2876         BYTE    *ibp, *obp;
2877         DWORD   buflen, buf_mixpos;
2878
2879         buflen = dsb->ds3db->buflen;
2880         buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2881         ibp = dsb->ds3db->buffer + buf_mixpos;
2882         obp = buf;
2883
2884         if (buf_mixpos > buflen) {
2885                 FIXME("Major breakage\n");
2886                 return;
2887         }
2888
2889         if (len <= (buf_mixpos + buflen))
2890                 memcpy(obp, ibp, len);
2891         else { /* wrap */
2892                 memcpy(obp, ibp, buflen - buf_mixpos);
2893                 memcpy(obp + (buflen - buf_mixpos),
2894                     dsb->buffer,
2895                     len - (buflen - buf_mixpos));
2896         }
2897         return;
2898 }
2899 #endif
2900
2901 static void *tmp_buffer;
2902 static size_t tmp_buffer_len = 0;
2903
2904 static void *DSOUND_tmpbuffer(size_t len)
2905 {
2906   if (len>tmp_buffer_len) {
2907     void *new_buffer = realloc(tmp_buffer, len);
2908     if (new_buffer) {
2909       tmp_buffer = new_buffer;
2910       tmp_buffer_len = len;
2911     }
2912     return new_buffer;
2913   }
2914   return tmp_buffer;
2915 }
2916
2917 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2918 {
2919         INT     i, len, ilen, temp, field;
2920         INT     advance = primarybuf->wfx.wBitsPerSample >> 3;
2921         BYTE    *buf, *ibuf, *obuf;
2922         INT16   *ibufs, *obufs;
2923
2924         len = fraglen;
2925         if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2926                 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2927                         dsb->nAvgBytesPerSec) -
2928                        MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2929                         dsb->nAvgBytesPerSec);
2930                 len = (len > temp) ? temp : len;
2931         }
2932         len &= ~3;                              /* 4 byte alignment */
2933
2934         if (len == 0) {
2935                 /* This should only happen if we aren't looping and temp < 4 */
2936
2937                 /* We skip the remainder, so check for possible events */
2938                 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2939                 /* Stop */
2940                 dsb->state = STATE_STOPPED;
2941                 dsb->playpos = 0;
2942                 dsb->buf_mixpos = 0;
2943                 dsb->leadin = FALSE;
2944                 /* Check for DSBPN_OFFSETSTOP */
2945                 DSOUND_CheckEvent(dsb, 0);
2946                 return 0;
2947         }
2948
2949         /* Been seeing segfaults in malloc() for some reason... */
2950         TRACE("allocating buffer (size = %d)\n", len);
2951         if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2952                 return 0;
2953
2954         TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2955
2956         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2957         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2958             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2959                 DSOUND_MixerVol(dsb, ibuf, len);
2960
2961         obuf = primarybuf->buffer + writepos;
2962         for (i = 0; i < len; i += advance) {
2963                 obufs = (INT16 *) obuf;
2964                 ibufs = (INT16 *) ibuf;
2965                 if (primarybuf->wfx.wBitsPerSample == 8) {
2966                         /* 8-bit WAV is unsigned */
2967                         field = (*ibuf - 128);
2968                         field += (*obuf - 128);
2969                         field = field > 127 ? 127 : field;
2970                         field = field < -128 ? -128 : field;
2971                         *obuf = field + 128;
2972                 } else {
2973                         /* 16-bit WAV is signed */
2974                         field = *ibufs;
2975                         field += *obufs;
2976                         field = field > 32767 ? 32767 : field;
2977                         field = field < -32768 ? -32768 : field;
2978                         *obufs = field;
2979                 }
2980                 ibuf += advance;
2981                 obuf += advance;
2982                 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2983                         obuf = primarybuf->buffer;
2984         }
2985         /* free(buf); */
2986
2987         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2988                 DSOUND_CheckEvent(dsb, ilen);
2989
2990         if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2991                 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2992                  * not the MIX position... but if the sound buffer is bigger than our prebuffering
2993                  * (which must be the case for the streaming buffers that need this hack anyway)
2994                  * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2995                 dsb->leadin = FALSE;
2996         }
2997
2998         dsb->buf_mixpos += ilen;
2999         
3000         if (dsb->buf_mixpos >= dsb->buflen) {
3001                 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
3002                         dsb->state = STATE_STOPPED;
3003                         dsb->playpos = 0;
3004                         dsb->buf_mixpos = 0;
3005                         dsb->leadin = FALSE;
3006                         DSOUND_CheckEvent(dsb, 0);              /* For DSBPN_OFFSETSTOP */
3007                 } else {
3008                         /* wrap */
3009                         while (dsb->buf_mixpos >= dsb->buflen)
3010                                 dsb->buf_mixpos -= dsb->buflen;
3011                         if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
3012                                 dsb->leadin = FALSE; /* HACK: see above */
3013                 }
3014         }
3015
3016         return len;
3017 }
3018
3019 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
3020 {
3021         INT     i, ilen, field;
3022         INT     advance = primarybuf->wfx.wBitsPerSample >> 3;
3023         BYTE    *buf, *ibuf, *obuf;
3024         INT16   *ibufs, *obufs;
3025
3026         len &= ~3;                              /* 4 byte alignment */
3027
3028         TRACE("allocating buffer (size = %ld)\n", len);
3029         if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
3030                 return;
3031
3032         TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
3033
3034         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
3035         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
3036             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
3037                 DSOUND_MixerVol(dsb, ibuf, len);
3038
3039         /* subtract instead of add, to phase out premixed data */
3040         obuf = primarybuf->buffer + writepos;
3041         for (i = 0; i < len; i += advance) {
3042                 obufs = (INT16 *) obuf;
3043                 ibufs = (INT16 *) ibuf;
3044                 if (primarybuf->wfx.wBitsPerSample == 8) {
3045                         /* 8-bit WAV is unsigned */
3046                         field = (*ibuf - 128);
3047                         field -= (*obuf - 128);
3048                         field = field > 127 ? 127 : field;
3049                         field = field < -128 ? -128 : field;
3050                         *obuf = field + 128;
3051                 } else {
3052                         /* 16-bit WAV is signed */
3053                         field = *ibufs;
3054                         field -= *obufs;
3055                         field = field > 32767 ? 32767 : field;
3056                         field = field < -32768 ? -32768 : field;
3057                         *obufs = field;
3058                 }
3059                 ibuf += advance;
3060                 obuf += advance;
3061                 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
3062                         obuf = primarybuf->buffer;
3063         }
3064         /* free(buf); */
3065 }
3066
3067 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
3068 {
3069         DWORD   size, flen, len, npos, nlen;
3070         INT     iAdvance = dsb->wfx.nBlockAlign;
3071         INT     oAdvance = primarybuf->wfx.nBlockAlign;
3072         /* determine amount of premixed data to cancel */
3073         DWORD primary_done =
3074                 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3075                 dsb->primary_mixpos - writepos;
3076
3077         TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
3078
3079         /* backtrack the mix position */
3080         size = primary_done / oAdvance;
3081         flen = size * dsb->freqAdjust;
3082         len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
3083         flen &= (1<<DSOUND_FREQSHIFT)-1;
3084         while (dsb->freqAcc < flen) {
3085                 len += iAdvance;
3086                 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
3087         }
3088         len %= dsb->buflen;
3089         npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
3090                 dsb->buf_mixpos - len;
3091         if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
3092                 /* stop backtracking at startpos */
3093                 npos = dsb->startpos;
3094                 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
3095                         dsb->buf_mixpos - npos;
3096                 flen = dsb->freqAcc;
3097                 nlen = len / dsb->wfx.nBlockAlign;
3098                 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
3099                 nlen *= primarybuf->wfx.nBlockAlign;
3100                 writepos =
3101                         ((dsb->primary_mixpos < nlen) ? primarybuf->buflen : 0) +
3102                         dsb->primary_mixpos - nlen;
3103         }
3104
3105         dsb->freqAcc -= flen;
3106         dsb->buf_mixpos = npos;
3107         dsb->primary_mixpos = writepos;
3108
3109         TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3110               dsb->buf_mixpos, dsb->primary_mixpos, len);
3111
3112         if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
3113 }
3114
3115 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
3116 {
3117 #if 0
3118         DWORD   i, size, flen, len, npos, nlen;
3119         INT     iAdvance = dsb->wfx.nBlockAlign;
3120         INT     oAdvance = primarybuf->wfx.nBlockAlign;
3121         /* determine amount of premixed data to cancel */
3122         DWORD buf_done =
3123                 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3124                 dsb->buf_mixpos - buf_writepos;
3125 #endif
3126
3127         WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
3128         /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3129          * (which is faster anyway when there's one a single secondary buffer) */
3130         primarybuf->need_remix = TRUE;
3131 }
3132
3133 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
3134 {
3135         DWORD len, slen;
3136         /* determine this buffer's write position */
3137         DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
3138                                                      writepos, dsb->primary_mixpos, dsb->buf_mixpos);
3139         /* determine how much already-mixed data exists */
3140         DWORD buf_done =
3141                 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3142                 dsb->buf_mixpos - buf_writepos;
3143         DWORD primary_done =
3144                 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3145                 dsb->primary_mixpos - writepos;
3146         DWORD adv_done =
3147                 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
3148                 primarybuf->buf_mixpos - writepos;
3149         int still_behind;
3150
3151         TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
3152         TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
3153         TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
3154               mixlen);
3155         TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
3156
3157         /* save write position for non-GETCURRENTPOSITION2... */
3158         dsb->playpos = buf_writepos;
3159
3160         /* check whether CalcPlayPosition detected a mixing underrun */
3161         if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
3162                 /* it did, but did we have more to play? */
3163                 if ((dsb->playflags & DSBPLAY_LOOPING) ||
3164                     (dsb->buf_mixpos < dsb->buflen)) {
3165                         /* yes, have to recover */
3166                         ERR("underrun on sound buffer %p\n", dsb);
3167                         TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
3168                 }
3169                 dsb->primary_mixpos = writepos;
3170                 primary_done = 0;
3171         }
3172         /* determine how far ahead we should mix */
3173         if (((dsb->playflags & DSBPLAY_LOOPING) ||
3174              (dsb->leadin && (dsb->probably_valid_to != 0))) &&
3175             !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
3176                 /* if this is a streaming buffer, it typically means that
3177                  * we should defer mixing past probably_valid_to as long
3178                  * as we can, to avoid unnecessary remixing */
3179                 /* the heavy-looking calculations shouldn't be that bad,
3180                  * as any game isn't likely to be have more than 1 or 2
3181                  * streaming buffers in use at any time anyway... */
3182                 DWORD probably_valid_left =
3183                         (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
3184                         ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
3185                         dsb->probably_valid_to - buf_writepos;
3186                 /* check for leadin condition */
3187                 if ((probably_valid_left == 0) &&
3188                     (dsb->probably_valid_to == dsb->startpos) &&
3189                     dsb->leadin)
3190                         probably_valid_left = dsb->buflen;
3191                 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3192                       dsb->probably_valid_to, probably_valid_left);
3193                 /* check whether the app's time is already up */
3194                 if (probably_valid_left < dsb->writelead) {
3195                         WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3196                         /* once we pass the point of no return,
3197                          * no reason to hold back anymore */
3198                         dsb->probably_valid_to = (DWORD)-1;
3199                         /* we just have to go ahead and mix what we have,
3200                          * there's no telling what the app is thinking anyway */
3201                 } else {
3202                         /* adjust for our frequency and our sample size */
3203                         probably_valid_left = MulDiv(probably_valid_left,
3204                                                      1 << DSOUND_FREQSHIFT,
3205                                                      dsb->wfx.nBlockAlign * dsb->freqAdjust) *
3206                                               primarybuf->wfx.nBlockAlign;
3207                         /* check whether to clip mix_len */
3208                         if (probably_valid_left < mixlen) {
3209                                 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
3210                                 mixlen = probably_valid_left;
3211                         }
3212                 }
3213         }
3214         /* cut mixlen with what's already been mixed */
3215         if (mixlen < primary_done) {
3216                 /* huh? and still CalcPlayPosition didn't
3217                  * detect an underrun? */
3218                 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
3219                 return 0;
3220         }
3221         len = mixlen - primary_done;
3222         TRACE("remaining mixlen=%ld\n", len);
3223
3224         if (len < primarybuf->dsound->fraglen) {
3225                 /* smaller than a fragment, wait until it gets larger
3226                  * before we take the mixing overhead */
3227                 TRACE("mixlen not worth it, deferring mixing\n");
3228                 return 0;
3229         }
3230
3231         /* ok, we know how much to mix, let's go */
3232         still_behind = (adv_done > primary_done);
3233         while (len) {
3234                 slen = primarybuf->buflen - dsb->primary_mixpos;
3235                 if (slen > len) slen = len;
3236                 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
3237
3238                 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
3239                     (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
3240                         still_behind = FALSE;
3241
3242                 dsb->primary_mixpos += slen; len -= slen;
3243                 while (dsb->primary_mixpos >= primarybuf->buflen)
3244                         dsb->primary_mixpos -= primarybuf->buflen;
3245
3246                 if ((dsb->state == STATE_STOPPED) || !slen) break;
3247         }
3248         TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
3249         TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
3250         /* return how far we think the primary buffer can
3251          * advance its underrun detector...*/
3252         if (still_behind) return 0;
3253         if ((mixlen - len) < primary_done) return 0;
3254         slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
3255                 primarybuf->buflen : 0) + dsb->primary_mixpos -
3256                 primarybuf->buf_mixpos;
3257         if (slen > mixlen) {
3258                 /* the primary_done and still_behind checks above should have worked */
3259                 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
3260                 slen = 0;
3261         }
3262         return slen;
3263 }
3264
3265 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
3266 {
3267         INT                     i, len, maxlen = 0;
3268         IDirectSoundBufferImpl  *dsb;
3269
3270         TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
3271         for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3272                 dsb = dsound->buffers[i];
3273
3274                 if (!dsb || !(ICOM_VTBL(dsb)))
3275                         continue;
3276                 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3277                         TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3278                         EnterCriticalSection(&(dsb->lock));
3279                         if (dsb->state == STATE_STOPPING) {
3280                                 DSOUND_MixCancel(dsb, writepos, TRUE);
3281                                 dsb->state = STATE_STOPPED;
3282                         } else {
3283                                 if ((dsb->state == STATE_STARTING) || recover)
3284                                         dsb->primary_mixpos = writepos;
3285                                 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3286                                 if (dsb->state == STATE_STARTING)
3287                                         dsb->state = STATE_PLAYING;
3288                                 maxlen = (len > maxlen) ? len : maxlen;
3289                         }
3290                         LeaveCriticalSection(&(dsb->lock));
3291                 }
3292         }
3293         
3294         return maxlen;
3295 }
3296
3297 static void DSOUND_MixReset(DWORD writepos)
3298 {
3299         INT                     i;
3300         IDirectSoundBufferImpl  *dsb;
3301         int nfiller;
3302
3303         TRACE("(%ld)\n", writepos);
3304
3305         /* the sound of silence */
3306         nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3307
3308         /* reset all buffer mix positions */
3309         for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3310                 dsb = dsound->buffers[i];
3311
3312                 if (!dsb || !(ICOM_VTBL(dsb)))
3313                         continue;
3314                 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3315                         TRACE("Resetting %p\n", dsb);
3316                         EnterCriticalSection(&(dsb->lock));
3317                         if (dsb->state == STATE_STOPPING) {
3318                                 dsb->state = STATE_STOPPED;
3319                         }
3320                         else if (dsb->state == STATE_STARTING) {
3321                                 /* nothing */
3322                         } else {
3323                                 DSOUND_MixCancel(dsb, writepos, FALSE);
3324                         }
3325                         LeaveCriticalSection(&(dsb->lock));
3326                 }
3327         }
3328
3329         /* wipe out premixed data */
3330         if (primarybuf->buf_mixpos < writepos) {
3331                 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buflen - writepos);
3332                 memset(primarybuf->buffer, nfiller, primarybuf->buf_mixpos);
3333         } else {
3334                 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buf_mixpos - writepos);
3335         }
3336
3337         /* reset primary mix position */
3338         primarybuf->buf_mixpos = writepos;
3339 }
3340
3341 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
3342 {
3343         if (primarybuf->need_remix) {
3344                 DSOUND_MixReset(writepos);
3345                 primarybuf->need_remix = FALSE;
3346                 /* maximize Half-Life performance */
3347                 dsound->prebuf = ds_snd_queue_min;
3348         } else {
3349                 /* if (dsound->prebuf < ds_snd_queue_max) dsound->prebuf++; */
3350         }
3351         TRACE("premix adjust: %d\n", dsound->prebuf);
3352 }
3353
3354 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
3355 {
3356         if (mixq + dsound->pwqueue > ds_hel_queue) mixq = ds_hel_queue - dsound->pwqueue;
3357         TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
3358         for (; mixq; mixq--) {
3359                 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3360                 dsound->pwwrite++;
3361                 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3362                 dsound->pwqueue++;
3363         }
3364 }
3365
3366 /* #define SYNC_CALLBACK */
3367
3368 static void DSOUND_PerformMix(void)
3369 {
3370         int nfiller;
3371         BOOL forced;
3372         HRESULT hres;
3373
3374         EnterCriticalSection(&(dsound->lock));
3375
3376         if (!primarybuf || !primarybuf->ref) {
3377                 /* seems the primary buffer is currently being released */
3378                 LeaveCriticalSection(&(dsound->lock));
3379                 return;
3380         }
3381
3382         /* the sound of silence */
3383         nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3384
3385         /* whether the primary is forced to play even without secondary buffers */
3386         forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3387
3388         TRACE("entering at %ld\n", GetTickCount());
3389         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3390                 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3391                 /* FIXME: document variables */
3392                 DWORD playpos, writepos, inq, maxq, frag;
3393                 if (primarybuf->hwbuf) {
3394                         hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3395                         if (hres) {
3396                             LeaveCriticalSection(&(dsound->lock));
3397                             return;
3398                         }
3399                         /* Well, we *could* do Just-In-Time mixing using the writepos,
3400                          * but that's a little bit ambitious and unnecessary... */
3401                         /* rather add our safety margin to the writepos, if we're playing */
3402                         if (!paused) {
3403                                 writepos += primarybuf->writelead;
3404                                 while (writepos >= primarybuf->buflen)
3405                                         writepos -= primarybuf->buflen;
3406                         } else writepos = playpos;
3407                 }
3408                 else {
3409                         playpos = dsound->pwplay * dsound->fraglen;
3410                         writepos = playpos;
3411                         if (!paused) {
3412                                 writepos += ds_hel_margin * dsound->fraglen;
3413                                 while (writepos >= primarybuf->buflen)
3414                                         writepos -= primarybuf->buflen;
3415                         }
3416                 }
3417                 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3418                       playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3419                 /* wipe out just-played sound data */
3420                 if (playpos < primarybuf->playpos) {
3421                         memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3422                         memset(primarybuf->buffer, nfiller, playpos);
3423                 } else {
3424                         memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3425                 }
3426                 primarybuf->playpos = playpos;
3427
3428                 EnterCriticalSection(&(primarybuf->lock));
3429
3430                 /* reset mixing if necessary */
3431                 DSOUND_CheckReset(dsound, writepos);
3432
3433                 /* check how much prebuffering is left */
3434                 inq = primarybuf->buf_mixpos;
3435                 if (inq < writepos)
3436                         inq += primarybuf->buflen;
3437                 inq -= writepos;
3438
3439                 /* find the maximum we can prebuffer */
3440                 if (!paused) {
3441                         maxq = playpos;
3442                         if (maxq < writepos)
3443                                 maxq += primarybuf->buflen;
3444                         maxq -= writepos;
3445                 } else maxq = primarybuf->buflen;
3446
3447                 /* clip maxq to dsound->prebuf */
3448                 frag = dsound->prebuf * dsound->fraglen;
3449                 if (maxq > frag) maxq = frag;
3450
3451                 /* check for consistency */
3452                 if (inq > maxq) {
3453                         /* the playback position must have passed our last
3454                          * mixed position, i.e. it's an underrun, or we have
3455                          * nothing more to play */
3456                         TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3457                         inq = 0;
3458                         /* stop the playback now, to allow buffers to refill */
3459                         if (primarybuf->state == STATE_PLAYING) {
3460                                 primarybuf->state = STATE_STARTING;
3461                         }
3462                         else if (primarybuf->state == STATE_STOPPING) {
3463                                 primarybuf->state = STATE_STOPPED;
3464                         }
3465                         else {
3466                                 /* how can we have an underrun if we aren't playing? */
3467                                 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3468                         }
3469 #ifdef SYNC_CALLBACK
3470                         /* DSOUND_callback may need this lock */
3471                         LeaveCriticalSection(&(primarybuf->lock));
3472 #endif
3473                         DSOUND_PrimaryStop(primarybuf);
3474 #ifdef SYNC_CALLBACK
3475                         EnterCriticalSection(&(primarybuf->lock));
3476 #endif
3477                         if (primarybuf->hwbuf) {
3478                                 /* the Stop is supposed to reset play position to beginning of buffer */
3479                                 /* unfortunately, OSS is not able to do so, so get current pointer */
3480                                 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3481                                 if (hres) {
3482                                         LeaveCriticalSection(&(dsound->lock));
3483                                         LeaveCriticalSection(&(primarybuf->lock));
3484                                         return;
3485                                 }
3486                         } else {
3487                                 playpos = dsound->pwplay * dsound->fraglen;
3488                         }
3489                         writepos = playpos;
3490                         primarybuf->playpos = playpos;
3491                         primarybuf->buf_mixpos = writepos;
3492                         inq = 0;
3493                         maxq = primarybuf->buflen;
3494                         if (maxq > frag) maxq = frag;
3495                         memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3496                         paused = TRUE;
3497                 }
3498
3499                 /* do the mixing */
3500                 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3501                 if (forced) frag = maxq - inq;
3502                 primarybuf->buf_mixpos += frag;
3503                 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3504                         primarybuf->buf_mixpos -= primarybuf->buflen;
3505
3506                 if (frag) {
3507                         /* buffers have been filled, restart playback */
3508                         if (primarybuf->state == STATE_STARTING) {
3509                                 primarybuf->state = STATE_PLAYING;
3510                         }
3511                         else if (primarybuf->state == STATE_STOPPED) {
3512                                 /* the primarybuf is supposed to play if there's something to play
3513                                  * even if it is reported as stopped, so don't let this confuse you */
3514                                 primarybuf->state = STATE_STOPPING;
3515                         }
3516                         LeaveCriticalSection(&(primarybuf->lock));
3517                         if (paused) {
3518                                 DSOUND_PrimaryPlay(primarybuf);
3519                                 TRACE("starting playback\n");
3520                         }
3521                 }
3522                 else
3523                         LeaveCriticalSection(&(primarybuf->lock));
3524         } else {
3525                 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3526                 if (primarybuf->state == STATE_STARTING) {
3527                         DSOUND_PrimaryPlay(primarybuf);
3528                         primarybuf->state = STATE_PLAYING;
3529                 } 
3530                 else if (primarybuf->state == STATE_STOPPING) {
3531                         DSOUND_PrimaryStop(primarybuf);
3532                         primarybuf->state = STATE_STOPPED;
3533                 }
3534         }
3535         TRACE("completed processing at %ld\n", GetTickCount());
3536         LeaveCriticalSection(&(dsound->lock));
3537 }
3538
3539 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3540 {
3541         if (!dsound || !primarybuf) {
3542                 ERR("dsound died without killing us?\n");
3543                 timeKillEvent(timerID);
3544                 timeEndPeriod(DS_TIME_RES);
3545                 return;
3546         }
3547
3548         TRACE("entered\n");
3549         DSOUND_PerformMix();
3550 }
3551
3552 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3553 {
3554         IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
3555         TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg);
3556         if (msg == MM_WOM_DONE) {
3557                 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
3558                 if (This->pwqueue == (DWORD)-1) {
3559                         TRACE("completed due to reset\n");
3560                         return;
3561                 }
3562 /* it could be a bad idea to enter critical section here... if there's lock contention,
3563  * the resulting scheduling delays might obstruct the winmm player thread */
3564 #ifdef SYNC_CALLBACK
3565                 EnterCriticalSection(&(primarybuf->lock));
3566 #endif
3567                 /* retrieve current values */
3568                 fraglen = dsound->fraglen;
3569                 buflen = primarybuf->buflen;
3570                 pwplay = dsound->pwplay;
3571                 playpos = pwplay * fraglen;
3572                 mixpos = primarybuf->buf_mixpos;
3573                 /* check remaining mixed data */
3574                 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
3575                 mixq = inq / fraglen;
3576                 if ((inq - (mixq * fraglen)) > 0) mixq++;
3577                 /* complete the playing buffer */
3578                 TRACE("done playing primary pos=%ld\n", playpos);
3579                 pwplay++;
3580                 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
3581                 /* write new values */
3582                 dsound->pwplay = pwplay;
3583                 dsound->pwqueue--;
3584                 /* queue new buffer if we have data for it */
3585                 if (inq>1) DSOUND_WaveQueue(This, inq-1);
3586 #ifdef SYNC_CALLBACK
3587                 LeaveCriticalSection(&(primarybuf->lock));
3588 #endif
3589         }
3590         TRACE("completed\n");
3591 }
3592
3593 /*******************************************************************************
3594  *              DirectSoundCreate (DSOUND.1)
3595  */
3596 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3597 {
3598         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3599         PIDSDRIVER drv = NULL;
3600         WAVEOUTCAPSA wcaps;
3601         unsigned wod, wodn;
3602         HRESULT err = DS_OK;    
3603
3604         if (lpGUID)
3605                 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3606         else
3607                 TRACE("DirectSoundCreate (%p)\n", ippDS);
3608
3609         if (ippDS == NULL)
3610                 return DSERR_INVALIDPARAM;
3611
3612         if (primarybuf) {
3613                 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3614                 *ippDS = dsound;
3615                 return DS_OK;
3616         }
3617
3618         /* get dsound configuration */
3619         setup_dsound_options();
3620
3621         /* Enumerate WINMM audio devices and find the one we want */
3622         wodn = waveOutGetNumDevs();
3623         if (!wodn) return DSERR_NODRIVER;
3624
3625         /* FIXME: How do we find the GUID of an audio device? */
3626         wod = 0; /* start at the first audio device */
3627
3628         /* Get output device caps */
3629         waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3630         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3631         waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3632
3633         /* Allocate memory */
3634         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3635         if (*ippDS == NULL)
3636                 return DSERR_OUTOFMEMORY;
3637
3638         ICOM_VTBL(*ippDS)       = &dsvt;
3639         (*ippDS)->ref           = 1;
3640
3641         (*ippDS)->driver        = drv;
3642         (*ippDS)->fraglen       = 0;
3643         (*ippDS)->priolevel     = DSSCL_NORMAL; 
3644         (*ippDS)->nrofbuffers   = 0;
3645         (*ippDS)->buffers       = NULL;
3646         (*ippDS)->primary       = NULL; 
3647         (*ippDS)->listener      = NULL; 
3648
3649         (*ippDS)->prebuf        = ds_snd_queue_max;
3650
3651         /* Get driver description */
3652         if (drv) {
3653                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3654         } else {
3655                 /* if no DirectSound interface available, use WINMM API instead */
3656                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3657                 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3658         }
3659
3660         /* Set default wave format (may need it for waveOutOpen) */
3661         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
3662         (*ippDS)->wfx.nChannels         = 2;
3663         (*ippDS)->wfx.nSamplesPerSec    = 22050;
3664         (*ippDS)->wfx.nAvgBytesPerSec   = 44100;
3665         (*ippDS)->wfx.nBlockAlign       = 2;
3666         (*ippDS)->wfx.wBitsPerSample    = 8;
3667
3668         /* If the driver requests being opened through MMSYSTEM
3669          * (which is recommended by the DDK), it is supposed to happen
3670          * before the DirectSound interface is opened */
3671         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
3672         {
3673                 /* FIXME: is this right? */
3674
3675                 (*ippDS)->drvdesc.dnDevNode = 0;
3676                 err = DSERR_ALLOCATED;
3677
3678                 /* if this device is busy try the next one */
3679                 while((err == DSERR_ALLOCATED) && 
3680                         ((*ippDS)->drvdesc.dnDevNode < wodn))
3681                 {
3682                   err = mmErr(waveOutOpen(&((*ippDS)->hwo), 
3683                         (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3684                         (DWORD)DSOUND_callback, (DWORD)(*ippDS),
3685                         CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
3686                   (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
3687                 }
3688
3689                 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
3690                 
3691         }
3692
3693         if (drv && (err == DS_OK))
3694                 err = IDsDriver_Open(drv);
3695
3696         /* FIXME: do we want to handle a temporarily busy device? */
3697         if (err != DS_OK) {
3698                 HeapFree(GetProcessHeap(),0,*ippDS);
3699                 *ippDS = NULL;
3700                 return err;
3701         }
3702
3703         /* the driver is now open, so it's now allowed to call GetCaps */
3704         if (drv) {
3705                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3706         } else {
3707                 unsigned c;
3708
3709
3710                 /* FIXME: look at wcaps */
3711                 (*ippDS)->drvcaps.dwFlags =
3712                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3713                 if (ds_emuldriver)
3714                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3715
3716                 /* Allocate memory for HEL buffer headers */
3717                 for (c=0; c<DS_HEL_FRAGS; c++) {
3718                         (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3719                         if (!(*ippDS)->pwave[c]) {
3720                                 /* Argh, out of memory */
3721                                 while (c--) {
3722                                         HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3723                                         waveOutClose((*ippDS)->hwo);
3724                                         HeapFree(GetProcessHeap(),0,*ippDS);
3725                                         *ippDS = NULL;
3726                                         return DSERR_OUTOFMEMORY;
3727                                 }
3728                         }
3729                 }
3730         }
3731
3732         InitializeCriticalSection(&((*ippDS)->lock));
3733
3734         if (!dsound) {
3735                 dsound = (*ippDS);
3736                 if (primarybuf == NULL) {
3737                         DSBUFFERDESC    dsbd;
3738                         HRESULT         hr;
3739
3740                         dsbd.dwSize = sizeof(DSBUFFERDESC);
3741                         dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3742                         dsbd.dwBufferBytes = 0;
3743                         dsbd.lpwfxFormat = &(dsound->wfx);
3744                         hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3745                         if (hr != DS_OK)
3746                                 return hr;
3747
3748                         /* dsound->primary is NULL - don't need to Release */
3749                         dsound->primary = primarybuf;
3750                         IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3751                 }
3752                 timeBeginPeriod(DS_TIME_RES);
3753                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3754                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3755         }
3756         return DS_OK;
3757 }
3758
3759 /***************************************************************************
3760  * DirectSoundCaptureCreate [DSOUND.6]
3761  *
3762  * Create and initialize a DirectSoundCapture interface
3763  *
3764  * RETURNS
3765  *    Success: DS_OK
3766  *    Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3767  *             DSERR_OUTOFMEMORY
3768  */
3769 HRESULT WINAPI DirectSoundCaptureCreate(
3770         LPCGUID lpcGUID,
3771         LPDIRECTSOUNDCAPTURE* lplpDSC,
3772         LPUNKNOWN pUnkOuter )
3773 {
3774         TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3775
3776         if( pUnkOuter ) {
3777                 return DSERR_NOAGGREGATION;
3778         }
3779
3780         /* Default device? */
3781         if ( !lpcGUID ) {
3782                 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3783         }
3784
3785         FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3786         *lplpDSC = NULL;
3787
3788         return DSERR_OUTOFMEMORY;
3789 }
3790
3791 /***************************************************************************
3792  * DirectSoundCaptureEnumerateA [DSOUND.7]
3793  *
3794  * Enumerate all DirectSound drivers installed in the system
3795  *
3796  * RETURNS
3797  *    Success: DS_OK
3798  *    Failure: DSERR_INVALIDPARAM
3799  */
3800 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3801         LPDSENUMCALLBACKA lpDSEnumCallback,
3802         LPVOID lpContext)
3803 {
3804         TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3805
3806         if ( lpDSEnumCallback )
3807                 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3808                     "SoundCap",lpContext);
3809
3810
3811         return DS_OK;
3812 }
3813
3814 /***************************************************************************
3815  * DirectSoundCaptureEnumerateW [DSOUND.8]
3816  *
3817  * Enumerate all DirectSound drivers installed in the system
3818  *
3819  * RETURNS
3820  *    Success: DS_OK
3821  *    Failure: DSERR_INVALIDPARAM
3822  */
3823 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3824         LPDSENUMCALLBACKW lpDSEnumCallback,
3825         LPVOID lpContext)
3826 {
3827         FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3828         return DS_OK;
3829
3830
3831 static HRESULT
3832 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3833 {
3834         *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3835
3836         if ( *ppobj == NULL ) {
3837                 return DSERR_OUTOFMEMORY;
3838         }
3839
3840         {
3841                 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3842
3843                 This->ref = 1;
3844                 ICOM_VTBL(This) = &dscvt;
3845
3846                 InitializeCriticalSection( &This->lock );
3847         }
3848
3849         return S_OK;
3850 }
3851
3852 static HRESULT WINAPI
3853 IDirectSoundCaptureImpl_QueryInterface(
3854         LPDIRECTSOUNDCAPTURE iface,
3855         REFIID riid,
3856         LPVOID* ppobj )
3857 {
3858         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3859
3860         FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3861
3862         return E_FAIL;
3863 }
3864
3865 static ULONG WINAPI
3866 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3867 {
3868         ULONG uRef;
3869         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3870
3871         EnterCriticalSection( &This->lock );
3872
3873         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3874         uRef = ++(This->ref);
3875
3876         LeaveCriticalSection( &This->lock );
3877
3878         return uRef;
3879 }
3880
3881 static ULONG WINAPI
3882 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3883 {
3884         ULONG uRef;
3885         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3886
3887         EnterCriticalSection( &This->lock );
3888
3889         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3890         uRef = --(This->ref);
3891
3892         LeaveCriticalSection( &This->lock );
3893
3894         if ( uRef == 0 ) {
3895                 DeleteCriticalSection( &This->lock );
3896                 HeapFree( GetProcessHeap(), 0, This );
3897         }
3898
3899         return uRef;
3900 }
3901
3902 static HRESULT WINAPI
3903 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3904         LPDIRECTSOUNDCAPTURE iface,
3905         LPCDSCBUFFERDESC lpcDSCBufferDesc,
3906         LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer, 
3907         LPUNKNOWN pUnk )
3908 {
3909         HRESULT hr;
3910         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3911
3912         TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3913
3914         if ( pUnk ) {
3915                 return DSERR_INVALIDPARAM;
3916         }
3917
3918         hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3919
3920         return hr;
3921 }
3922
3923 static HRESULT WINAPI
3924 IDirectSoundCaptureImpl_GetCaps(
3925         LPDIRECTSOUNDCAPTURE iface,
3926         LPDSCCAPS lpDSCCaps )
3927 {
3928         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3929
3930         FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3931
3932         return DS_OK;
3933 }
3934
3935 static HRESULT WINAPI
3936 IDirectSoundCaptureImpl_Initialize(
3937         LPDIRECTSOUNDCAPTURE iface,
3938         LPCGUID lpcGUID )
3939 {
3940         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3941
3942         FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3943
3944         return DS_OK;
3945 }
3946
3947
3948 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3949 {
3950         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3951         /* IUnknown methods */
3952         IDirectSoundCaptureImpl_QueryInterface,
3953         IDirectSoundCaptureImpl_AddRef,
3954         IDirectSoundCaptureImpl_Release,
3955
3956         /* IDirectSoundCapture methods */
3957         IDirectSoundCaptureImpl_CreateCaptureBuffer,
3958         IDirectSoundCaptureImpl_GetCaps,
3959         IDirectSoundCaptureImpl_Initialize 
3960 };
3961
3962 static HRESULT
3963 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3964 {
3965
3966         FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3967
3968         *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3969
3970         if ( *ppobj == NULL ) {
3971                 return DSERR_OUTOFMEMORY;
3972         }
3973
3974         {
3975                 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3976
3977                 This->ref = 1;
3978                 ICOM_VTBL(This) = &dscbvt;
3979
3980                 InitializeCriticalSection( &This->lock );
3981         }
3982
3983         return S_OK;
3984 }
3985
3986
3987 static HRESULT WINAPI
3988 IDirectSoundCaptureBufferImpl_QueryInterface(
3989         LPDIRECTSOUNDCAPTUREBUFFER iface,
3990         REFIID riid,
3991         LPVOID* ppobj )
3992 {
3993         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3994
3995         FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3996
3997         return E_FAIL;
3998 }
3999
4000 static ULONG WINAPI
4001 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
4002 {
4003         ULONG uRef;
4004         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4005
4006         EnterCriticalSection( &This->lock );
4007
4008         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
4009         uRef = ++(This->ref);
4010
4011         LeaveCriticalSection( &This->lock );
4012
4013         return uRef;
4014 }
4015
4016 static ULONG WINAPI
4017 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
4018 {
4019         ULONG uRef;
4020         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4021
4022         EnterCriticalSection( &This->lock );
4023
4024         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
4025         uRef = --(This->ref);
4026
4027         LeaveCriticalSection( &This->lock );
4028
4029         if ( uRef == 0 ) {
4030                 DeleteCriticalSection( &This->lock );
4031                 HeapFree( GetProcessHeap(), 0, This );
4032         }
4033
4034         return uRef;
4035 }
4036
4037 static HRESULT WINAPI
4038 IDirectSoundCaptureBufferImpl_GetCaps(
4039         LPDIRECTSOUNDCAPTUREBUFFER iface,
4040         LPDSCBCAPS lpDSCBCaps )
4041 {
4042         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4043
4044         FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
4045
4046         return DS_OK;
4047 }
4048
4049 static HRESULT WINAPI
4050 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
4051         LPDIRECTSOUNDCAPTUREBUFFER iface,
4052         LPDWORD lpdwCapturePosition,
4053         LPDWORD lpdwReadPosition )
4054 {
4055         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4056
4057         FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
4058
4059         return DS_OK;
4060 }
4061
4062 static HRESULT WINAPI
4063 IDirectSoundCaptureBufferImpl_GetFormat(
4064         LPDIRECTSOUNDCAPTUREBUFFER iface,
4065         LPWAVEFORMATEX lpwfxFormat, 
4066         DWORD dwSizeAllocated, 
4067         LPDWORD lpdwSizeWritten )
4068 {
4069         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4070
4071         FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
4072
4073         return DS_OK;
4074 }
4075
4076 static HRESULT WINAPI
4077 IDirectSoundCaptureBufferImpl_GetStatus(
4078         LPDIRECTSOUNDCAPTUREBUFFER iface,
4079         LPDWORD lpdwStatus )
4080 {
4081         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4082
4083         FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
4084
4085         return DS_OK;
4086 }
4087
4088 static HRESULT WINAPI
4089 IDirectSoundCaptureBufferImpl_Initialize(
4090         LPDIRECTSOUNDCAPTUREBUFFER iface,
4091         LPDIRECTSOUNDCAPTURE lpDSC, 
4092         LPCDSCBUFFERDESC lpcDSCBDesc )
4093 {
4094         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4095
4096         FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
4097
4098         return DS_OK;
4099 }
4100
4101 static HRESULT WINAPI
4102 IDirectSoundCaptureBufferImpl_Lock(
4103         LPDIRECTSOUNDCAPTUREBUFFER iface,
4104         DWORD dwReadCusor, 
4105         DWORD dwReadBytes, 
4106         LPVOID* lplpvAudioPtr1, 
4107         LPDWORD lpdwAudioBytes1, 
4108         LPVOID* lplpvAudioPtr2, 
4109         LPDWORD lpdwAudioBytes2, 
4110         DWORD dwFlags )
4111 {
4112         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4113
4114         FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
4115
4116         return DS_OK;
4117 }
4118
4119 static HRESULT WINAPI
4120 IDirectSoundCaptureBufferImpl_Start(
4121         LPDIRECTSOUNDCAPTUREBUFFER iface,
4122         DWORD dwFlags )
4123 {
4124         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4125
4126         FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
4127
4128         return DS_OK;
4129 }
4130
4131 static HRESULT WINAPI
4132 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
4133 {
4134         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4135
4136         FIXME( "(%p): stub\n", This );
4137
4138         return DS_OK;
4139 }
4140
4141 static HRESULT WINAPI
4142 IDirectSoundCaptureBufferImpl_Unlock(
4143         LPDIRECTSOUNDCAPTUREBUFFER iface,
4144         LPVOID lpvAudioPtr1, 
4145         DWORD dwAudioBytes1, 
4146         LPVOID lpvAudioPtr2, 
4147         DWORD dwAudioBytes2 )
4148 {
4149         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4150
4151         FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
4152
4153         return DS_OK;
4154 }
4155
4156
4157 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
4158 {
4159         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4160         /* IUnknown methods */
4161         IDirectSoundCaptureBufferImpl_QueryInterface,
4162         IDirectSoundCaptureBufferImpl_AddRef,
4163         IDirectSoundCaptureBufferImpl_Release,
4164
4165         /* IDirectSoundCaptureBuffer methods */
4166         IDirectSoundCaptureBufferImpl_GetCaps,
4167         IDirectSoundCaptureBufferImpl_GetCurrentPosition,
4168         IDirectSoundCaptureBufferImpl_GetFormat,
4169         IDirectSoundCaptureBufferImpl_GetStatus,
4170         IDirectSoundCaptureBufferImpl_Initialize,
4171         IDirectSoundCaptureBufferImpl_Lock,
4172         IDirectSoundCaptureBufferImpl_Start,
4173         IDirectSoundCaptureBufferImpl_Stop,
4174         IDirectSoundCaptureBufferImpl_Unlock
4175 };
4176
4177 /*******************************************************************************
4178  * DirectSound ClassFactory
4179  */
4180 typedef struct
4181 {
4182     /* IUnknown fields */
4183     ICOM_VFIELD(IClassFactory);
4184     DWORD                       ref;
4185 } IClassFactoryImpl;
4186
4187 static HRESULT WINAPI 
4188 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
4189         ICOM_THIS(IClassFactoryImpl,iface);
4190
4191         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
4192         return E_NOINTERFACE;
4193 }
4194
4195 static ULONG WINAPI
4196 DSCF_AddRef(LPCLASSFACTORY iface) {
4197         ICOM_THIS(IClassFactoryImpl,iface);
4198         return ++(This->ref);
4199 }
4200
4201 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
4202         ICOM_THIS(IClassFactoryImpl,iface);
4203         /* static class, won't be  freed */
4204         return --(This->ref);
4205 }
4206
4207 static HRESULT WINAPI DSCF_CreateInstance(
4208         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
4209 ) {
4210         ICOM_THIS(IClassFactoryImpl,iface);
4211
4212         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
4213         if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
4214                 /* FIXME: reuse already created dsound if present? */
4215                 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
4216         }
4217         return E_NOINTERFACE;
4218 }
4219
4220 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
4221         ICOM_THIS(IClassFactoryImpl,iface);
4222         FIXME("(%p)->(%d),stub!\n",This,dolock);
4223         return S_OK;
4224 }
4225
4226 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
4227         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4228         DSCF_QueryInterface,
4229         DSCF_AddRef,
4230         DSCF_Release,
4231         DSCF_CreateInstance,
4232         DSCF_LockServer
4233 };
4234 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
4235
4236 /*******************************************************************************
4237  * DllGetClassObject [DSOUND.5]
4238  * Retrieves class object from a DLL object
4239  *
4240  * NOTES
4241  *    Docs say returns STDAPI
4242  *
4243  * PARAMS
4244  *    rclsid [I] CLSID for the class object
4245  *    riid   [I] Reference to identifier of interface for class object
4246  *    ppv    [O] Address of variable to receive interface pointer for riid
4247  *
4248  * RETURNS
4249  *    Success: S_OK
4250  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4251  *             E_UNEXPECTED
4252  */
4253 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
4254 {
4255     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4256     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
4257         *ppv = (LPVOID)&DSOUND_CF;
4258         IClassFactory_AddRef((IClassFactory*)*ppv);
4259     return S_OK;
4260     }
4261
4262     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4263     return CLASS_E_CLASSNOTAVAILABLE;
4264 }
4265
4266
4267 /*******************************************************************************
4268  * DllCanUnloadNow [DSOUND.4]  Determines whether the DLL is in use.
4269  *
4270  * RETURNS
4271  *    Success: S_OK
4272  *    Failure: S_FALSE
4273  */
4274 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
4275 {
4276     FIXME("(void): stub\n");
4277     return S_FALSE;
4278 }