xref: /reactos/dll/directx/wine/dsound/primary.c (revision 8a978a17)
1 /*  			DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * TODO:
22  *      When PrimarySetFormat (via ReopenDevice or PrimaryOpen) fails,
23  *       it leaves dsound in unusable (not really open) state.
24  */
25 
26 #include "dsound_private.h"
27 
28 /** Calculate how long a fragment length of about 10 ms should be in frames
29  *
30  * nSamplesPerSec: Frequency rate in samples per second
31  * nBlockAlign: Size of a single blockalign
32  *
33  * Returns:
34  * Size in bytes of a single fragment
35  */
36 DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
37 {
38     /* Given a timer delay of 10ms, the fragment size is approximately:
39      *     fraglen = (nSamplesPerSec * 10 / 1000) * nBlockAlign
40      * ==> fraglen = (nSamplesPerSec / 100) * nBlockSize
41      *
42      * ALSA uses buffers that are powers of 2. Because of this, fraglen
43      * is rounded up to the nearest power of 2:
44      */
45 
46     if (nSamplesPerSec <= 12800)
47         return 128 * nBlockAlign;
48 
49     if (nSamplesPerSec <= 25600)
50         return 256 * nBlockAlign;
51 
52     if (nSamplesPerSec <= 51200)
53         return 512 * nBlockAlign;
54 
55     return 1024 * nBlockAlign;
56 }
57 
58 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
59 {
60     TRACE("(%p)\n", device);
61 
62     device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
63     device->helfrags = device->buflen / device->fraglen;
64     TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
65 
66     if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
67         device->writelead = 0;
68     else
69         /* calculate the 10ms write lead */
70         device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
71 }
72 
73 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
74 {
75 	HRESULT hres = DS_OK;
76 	TRACE("(%p, %d)\n", device, forcewave);
77 
78 	if (device->driver)
79 	{
80 		IDsDriver_Close(device->driver);
81 		if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
82 			waveOutClose(device->hwo);
83 		IDsDriver_Release(device->driver);
84 		device->driver = NULL;
85 		device->buffer = NULL;
86 		device->hwo = 0;
87 	}
88 	else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
89 		waveOutClose(device->hwo);
90 
91 	/* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
92 	if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
93 		waveOutMessage((HWAVEOUT)(DWORD_PTR)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
94 
95 	/* Get driver description */
96 	if (device->driver) {
97 		DWORD wod = device->drvdesc.dnDevNode;
98 		hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
99 		device->drvdesc.dnDevNode = wod;
100 		if (FAILED(hres)) {
101 			WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
102 			IDsDriver_Release(device->driver);
103 			device->driver = NULL;
104 		}
105         }
106 
107         /* if no DirectSound interface available, use WINMM API instead */
108 	if (!device->driver)
109 		device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
110 
111 	if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
112 	{
113 		DWORD flags = CALLBACK_FUNCTION | WAVE_MAPPED;
114 
115 		if (device->driver)
116 			flags |= WAVE_DIRECTSOUND;
117 
118 		hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD_PTR)device, flags));
119 		if (FAILED(hres)) {
120 			WARN("waveOutOpen failed\n");
121 			if (device->driver)
122 			{
123 				IDsDriver_Release(device->driver);
124 				device->driver = NULL;
125 			}
126 			return hres;
127 		}
128 	}
129 
130 	if (device->driver)
131 		hres = IDsDriver_Open(device->driver);
132 
133 	return hres;
134 }
135 
136 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
137 {
138 	DWORD buflen;
139 	HRESULT err = DS_OK;
140 	TRACE("(%p)\n", device);
141 
142 	/* on original windows, the buffer it set to a fixed size, no matter what the settings are.
143 	   on windows this size is always fixed (tested on win-xp) */
144 	if (!device->buflen)
145 		device->buflen = ds_hel_buflen;
146 	buflen = device->buflen;
147 	buflen -= buflen % device->pwfx->nBlockAlign;
148 	device->buflen = buflen;
149 
150 	if (device->driver)
151 	{
152 		err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
153 						  DSBCAPS_PRIMARYBUFFER,0,
154 						  &(device->buflen),&(device->buffer),
155 						  (LPVOID*)&(device->hwbuf));
156 
157 		if (err != DS_OK) {
158 			WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
159 			err = DSOUND_ReopenDevice(device, TRUE);
160 			if (FAILED(err))
161 			{
162 				WARN("Falling back to waveout failed too! Giving up\n");
163 				return err;
164 			}
165 		}
166                 if (device->hwbuf)
167                     IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
168 
169                 DSOUND_RecalcPrimary(device);
170 		device->prebuf = ds_snd_queue_max;
171 		if (device->helfrags < ds_snd_queue_min)
172 		{
173 			WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
174 			device->buflen = buflen;
175 			IDsDriverBuffer_Release(device->hwbuf);
176 			device->hwbuf = NULL;
177 			err = DSOUND_ReopenDevice(device, TRUE);
178 			if (FAILED(err))
179 			{
180 				WARN("Falling back to waveout failed too! Giving up\n");
181 				return err;
182 			}
183 		}
184 		else if (device->helfrags < ds_snd_queue_max)
185 			device->prebuf = device->helfrags;
186 	}
187 
188 	device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
189 	device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len);
190 	if (!device->mix_buffer)
191 	{
192 		if (device->hwbuf)
193 			IDsDriverBuffer_Release(device->hwbuf);
194 		device->hwbuf = NULL;
195 		return DSERR_OUTOFMEMORY;
196 	}
197 
198 	if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
199 	else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
200 
201 	/* are we using waveOut stuff? */
202 	if (!device->driver) {
203 		LPBYTE newbuf;
204 		LPWAVEHDR headers = NULL;
205 		DWORD overshot;
206 		unsigned int c;
207 
208 		/* Start in pause mode, to allow buffers to get filled */
209 		waveOutPause(device->hwo);
210 
211 		TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
212 
213 		/* reallocate emulated primary buffer */
214 		if (device->buffer)
215 			newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
216 		else
217 			newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
218 
219 		if (!newbuf) {
220 			ERR("failed to allocate primary buffer\n");
221 			return DSERR_OUTOFMEMORY;
222 			/* but the old buffer might still exist and must be re-prepared */
223 		}
224 
225 		DSOUND_RecalcPrimary(device);
226 		if (device->pwave)
227 			headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
228 		else
229 			headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
230 
231 		if (!headers) {
232 			ERR("failed to allocate wave headers\n");
233 			HeapFree(GetProcessHeap(), 0, newbuf);
234 			DSOUND_RecalcPrimary(device);
235 			return DSERR_OUTOFMEMORY;
236 		}
237 
238 		device->buffer = newbuf;
239 		device->pwave = headers;
240 
241 		/* prepare fragment headers */
242 		for (c=0; c<device->helfrags; c++) {
243 			device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
244 			device->pwave[c].dwBufferLength = device->fraglen;
245 			device->pwave[c].dwUser = (DWORD_PTR)device;
246 			device->pwave[c].dwFlags = 0;
247 			device->pwave[c].dwLoops = 0;
248 			err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
249 			if (err != DS_OK) {
250 				while (c--)
251 					waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
252 				break;
253 			}
254 		}
255 
256 		overshot = device->buflen % device->fraglen;
257 		/* sanity */
258 		if(overshot)
259 		{
260 			overshot -= overshot % device->pwfx->nBlockAlign;
261 			device->pwave[device->helfrags - 1].dwBufferLength += overshot;
262 		}
263 
264 		TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
265 	}
266 	device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
267 	device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
268 	FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
269 	FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
270 	device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
271 	return err;
272 }
273 
274 
275 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
276 {
277 	TRACE("(%p)\n", device);
278 
279 	/* are we using waveOut stuff? */
280 	if (!device->hwbuf) {
281 		unsigned c;
282 
283 		/* get out of CS when calling the wave system */
284 		LeaveCriticalSection(&(device->mixlock));
285 		/* **** */
286 		device->pwqueue = (DWORD)-1; /* resetting queues */
287 		waveOutReset(device->hwo);
288 		for (c=0; c<device->helfrags; c++)
289 			waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
290 		/* **** */
291 		EnterCriticalSection(&(device->mixlock));
292 
293 		/* clear the queue */
294 		device->pwqueue = 0;
295 	} else {
296 		ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
297 		if (!ref)
298 			device->hwbuf = 0;
299 		else
300 			ERR("Still %d references on primary buffer, refcount leak?\n", ref);
301 	}
302 }
303 
304 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
305 {
306 	HRESULT err = DS_OK;
307 	TRACE("(%p)\n", device);
308 
309 	device->buflen = ds_hel_buflen;
310 	err = DSOUND_PrimaryOpen(device);
311 
312 	if (err != DS_OK) {
313 		WARN("DSOUND_PrimaryOpen failed\n");
314 		return err;
315 	}
316 
317 	device->state = STATE_STOPPED;
318 	return DS_OK;
319 }
320 
321 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
322 {
323 	TRACE("(%p)\n", device);
324 
325 	/* **** */
326 	EnterCriticalSection(&(device->mixlock));
327 
328 	DSOUND_PrimaryClose(device);
329 	if (device->driver) {
330 		if (device->hwbuf) {
331 			if (IDsDriverBuffer_Release(device->hwbuf) == 0)
332 				device->hwbuf = 0;
333 		}
334 	} else
335                 HeapFree(GetProcessHeap(),0,device->pwave);
336         HeapFree(GetProcessHeap(),0,device->pwfx);
337         device->pwfx=NULL;
338 
339 	LeaveCriticalSection(&(device->mixlock));
340 	/* **** */
341 
342 	return DS_OK;
343 }
344 
345 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
346 {
347 	HRESULT err = DS_OK;
348 	TRACE("(%p)\n", device);
349 
350 	if (device->hwbuf) {
351 		err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
352 		if (err != DS_OK)
353 			WARN("IDsDriverBuffer_Play failed\n");
354 	} else {
355 		err = mmErr(waveOutRestart(device->hwo));
356 		if (err != DS_OK)
357 			WARN("waveOutRestart failed\n");
358 	}
359 
360 	return err;
361 }
362 
363 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
364 {
365 	HRESULT err = DS_OK;
366 	TRACE("(%p)\n", device);
367 
368 	if (device->hwbuf) {
369 		err = IDsDriverBuffer_Stop(device->hwbuf);
370 		if (err == DSERR_BUFFERLOST) {
371 			DSOUND_PrimaryClose(device);
372 			err = DSOUND_ReopenDevice(device, FALSE);
373 			if (FAILED(err))
374 				ERR("DSOUND_ReopenDevice failed\n");
375 			else
376 			{
377 				err = DSOUND_PrimaryOpen(device);
378 				if (FAILED(err))
379 					WARN("DSOUND_PrimaryOpen failed\n");
380 			}
381 		} else if (err != DS_OK) {
382 			WARN("IDsDriverBuffer_Stop failed\n");
383 		}
384 	} else {
385 
386 		/* don't call the wave system with the lock set */
387 		LeaveCriticalSection(&(device->mixlock));
388 		/* **** */
389 
390 		err = mmErr(waveOutPause(device->hwo));
391 
392 		/* **** */
393 		EnterCriticalSection(&(device->mixlock));
394 
395 		if (err != DS_OK)
396 			WARN("waveOutPause failed\n");
397 	}
398 
399 	return err;
400 }
401 
402 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
403 {
404 	TRACE("(%p,%p,%p)\n", device, playpos, writepos);
405 
406 	if (device->hwbuf) {
407 		HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
408 		if (err != S_OK) {
409 			WARN("IDsDriverBuffer_GetPosition failed\n");
410 			return err;
411 		}
412 	} else {
413 		TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
414 
415 		/* check if playpos was requested */
416 		if (playpos)
417 			/* use the cached play position */
418 			*playpos = device->pwplay * device->fraglen;
419 
420 		/* check if writepos was requested */
421 		if (writepos)
422 			/* the writepos is the first non-queued position */
423 			*writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
424 	}
425 	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
426 	return DS_OK;
427 }
428 
429 static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
430 {
431 	if (wfex->wFormatTag == WAVE_FORMAT_PCM)
432 		return sizeof(WAVEFORMATEX);
433 	else
434 		return sizeof(WAVEFORMATEX) + wfex->cbSize;
435 }
436 
437 LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
438 {
439 	DWORD size = DSOUND_GetFormatSize(wfex);
440 	LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(),0,size);
441 	if (pwfx == NULL) {
442 		WARN("out of memory\n");
443 	} else if (wfex->wFormatTag != WAVE_FORMAT_PCM) {
444 		CopyMemory(pwfx, wfex, size);
445 	} else {
446 		CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
447 		pwfx->cbSize=0;
448 		if (pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample/8) {
449 			WARN("Fixing bad nBlockAlign (%u)\n", pwfx->nBlockAlign);
450 			pwfx->nBlockAlign  = pwfx->nChannels * pwfx->wBitsPerSample/8;
451 		}
452 		if (pwfx->nAvgBytesPerSec != pwfx->nSamplesPerSec * pwfx->nBlockAlign) {
453 			WARN("Fixing bad nAvgBytesPerSec (%u)\n", pwfx->nAvgBytesPerSec);
454 			pwfx->nAvgBytesPerSec  = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
455 		}
456 	}
457 	return pwfx;
458 }
459 
460 HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
461 {
462 	HRESULT err = DSERR_BUFFERLOST;
463 	int i;
464 	DWORD nSamplesPerSec, bpp, chans;
465 	LPWAVEFORMATEX oldpwfx;
466         BOOL forced = device->priolevel == DSSCL_WRITEPRIMARY;
467 
468 	TRACE("(%p,%p)\n", device, wfex);
469 
470 	if (device->priolevel == DSSCL_NORMAL) {
471 		WARN("failed priority check!\n");
472 		return DSERR_PRIOLEVELNEEDED;
473 	}
474 
475 	/* Let's be pedantic! */
476 	if (wfex == NULL) {
477 		WARN("invalid parameter: wfex==NULL!\n");
478 		return DSERR_INVALIDPARAM;
479 	}
480 	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
481               "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
482 	      wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
483 	      wfex->nAvgBytesPerSec, wfex->nBlockAlign,
484 	      wfex->wBitsPerSample, wfex->cbSize);
485 
486 	/* **** */
487 	RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
488 	EnterCriticalSection(&(device->mixlock));
489 
490 	nSamplesPerSec = device->pwfx->nSamplesPerSec;
491 	bpp = device->pwfx->wBitsPerSample;
492 	chans = device->pwfx->nChannels;
493 
494 	oldpwfx = device->pwfx;
495 	device->pwfx = DSOUND_CopyFormat(wfex);
496 	if (device->pwfx == NULL) {
497 		device->pwfx = oldpwfx;
498 		oldpwfx = NULL;
499 		err = DSERR_OUTOFMEMORY;
500 		goto done;
501 	}
502 
503 	if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
504 		err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
505 
506 		/* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
507 		if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
508 		{
509 			DWORD cp_size = wfex->wFormatTag == WAVE_FORMAT_PCM ?
510 				sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + wfex->cbSize;
511 			err = DSERR_BUFFERLOST;
512 			CopyMemory(device->pwfx, wfex, cp_size);
513 		}
514 
515 		if (err != DSERR_BUFFERLOST && FAILED(err)) {
516 			DWORD size = DSOUND_GetFormatSize(oldpwfx);
517 			WARN("IDsDriverBuffer_SetFormat failed\n");
518 			if (!forced) {
519 				CopyMemory(device->pwfx, oldpwfx, size);
520 				err = DS_OK;
521 			}
522 			goto done;
523 		}
524 
525 		if (err == S_FALSE)
526 		{
527 			/* ALSA specific: S_FALSE tells that recreation was successful,
528 			 * but size and location may be changed, and buffer has to be restarted
529 			 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
530 			 * and the entire re-initialization will occur anyway
531 			 */
532 			IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
533 			IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
534 
535 			if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
536 			else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
537 			device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
538 			err = DS_OK;
539 		}
540 		DSOUND_RecalcPrimary(device);
541 	}
542 
543 	if (err == DSERR_BUFFERLOST)
544 	{
545 		DSOUND_PrimaryClose(device);
546 
547 		err = DSOUND_ReopenDevice(device, FALSE);
548 		if (FAILED(err))
549 		{
550 			WARN("DSOUND_ReopenDevice failed: %08x\n", err);
551 			goto done;
552 		}
553 		err = DSOUND_PrimaryOpen(device);
554 		if (err != DS_OK) {
555 			WARN("DSOUND_PrimaryOpen failed\n");
556 			goto done;
557 		}
558 
559 		if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
560 		{
561 			DSOUND_PrimaryClose(device);
562 			device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
563 			err = DSOUND_ReopenDevice(device, TRUE);
564 			if (FAILED(err))
565 				WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
566 			else if (FAILED((err = DSOUND_PrimaryOpen(device))))
567 				WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
568 		}
569 	}
570 
571 	device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
572 	device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len);
573 	FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
574 	device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
575 	device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
576 
577 	if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
578 		IDirectSoundBufferImpl** dsb = device->buffers;
579 		for (i = 0; i < device->nrofbuffers; i++, dsb++) {
580 			/* **** */
581 			RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
582 
583 			(*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
584 			DSOUND_RecalcFormat((*dsb));
585 			DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE);
586 			(*dsb)->primary_mixpos = 0;
587 
588 			RtlReleaseResource(&(*dsb)->lock);
589 			/* **** */
590 		}
591 	}
592 
593 done:
594 	LeaveCriticalSection(&(device->mixlock));
595 	RtlReleaseResource(&(device->buffer_list_lock));
596 	/* **** */
597 
598 	HeapFree(GetProcessHeap(), 0, oldpwfx);
599 	return err;
600 }
601 
602 /*******************************************************************************
603  *		PrimaryBuffer
604  */
605 static inline IDirectSoundBufferImpl *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
606 {
607     /* IDirectSoundBuffer and IDirectSoundBuffer8 use the same iface. */
608     return CONTAINING_RECORD(iface, IDirectSoundBufferImpl, IDirectSoundBuffer8_iface);
609 }
610 
611 /* This sets this format for the <em>Primary Buffer Only</em> */
612 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
613 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
614     LPDIRECTSOUNDBUFFER iface,
615     LPCWAVEFORMATEX wfex)
616 {
617     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
618     TRACE("(%p,%p)\n", iface, wfex);
619     return primarybuffer_SetFormat(This->device, wfex);
620 }
621 
622 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
623 	LPDIRECTSOUNDBUFFER iface,LONG vol
624 ) {
625         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
626         DirectSoundDevice *device = This->device;
627 	DWORD ampfactors;
628         HRESULT hres = DS_OK;
629 	TRACE("(%p,%d)\n", iface, vol);
630 
631 	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
632 		WARN("control unavailable\n");
633 		return DSERR_CONTROLUNAVAIL;
634 	}
635 
636 	if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
637 		WARN("invalid parameter: vol = %d\n", vol);
638 		return DSERR_INVALIDPARAM;
639 	}
640 
641 	/* **** */
642 	EnterCriticalSection(&(device->mixlock));
643 
644         waveOutGetVolume(device->hwo, &ampfactors);
645         device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
646         device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
647         DSOUND_AmpFactorToVolPan(&device->volpan);
648         if (vol != device->volpan.lVolume) {
649             device->volpan.lVolume=vol;
650             DSOUND_RecalcVolPan(&device->volpan);
651             if (device->hwbuf) {
652                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
653                 if (hres != DS_OK)
654                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
655             } else {
656                 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
657                 waveOutSetVolume(device->hwo, ampfactors);
658             }
659         }
660 
661 	LeaveCriticalSection(&(device->mixlock));
662 	/* **** */
663 
664 	return hres;
665 }
666 
667 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
668 	LPDIRECTSOUNDBUFFER iface,LPLONG vol
669 ) {
670         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
671         DirectSoundDevice *device = This->device;
672 	DWORD ampfactors;
673 	TRACE("(%p,%p)\n", iface, vol);
674 
675 	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
676 		WARN("control unavailable\n");
677 		return DSERR_CONTROLUNAVAIL;
678 	}
679 
680 	if (vol == NULL) {
681 		WARN("invalid parameter: vol = NULL\n");
682 		return DSERR_INVALIDPARAM;
683 	}
684 
685         if (!device->hwbuf)
686         {
687 	    waveOutGetVolume(device->hwo, &ampfactors);
688 	    device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
689 	    device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
690 	    DSOUND_AmpFactorToVolPan(&device->volpan);
691         }
692         *vol = device->volpan.lVolume;
693 	return DS_OK;
694 }
695 
696 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
697 	LPDIRECTSOUNDBUFFER iface,DWORD freq
698 ) {
699         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
700 	TRACE("(%p,%d)\n",This,freq);
701 
702 	/* You cannot set the frequency of the primary buffer */
703 	WARN("control unavailable\n");
704 	return DSERR_CONTROLUNAVAIL;
705 }
706 
707 static HRESULT WINAPI PrimaryBufferImpl_Play(
708 	LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
709 ) {
710         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
711         DirectSoundDevice *device = This->device;
712 	TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
713 
714 	if (!(flags & DSBPLAY_LOOPING)) {
715 		WARN("invalid parameter: flags = %08x\n", flags);
716 		return DSERR_INVALIDPARAM;
717 	}
718 
719 	/* **** */
720 	EnterCriticalSection(&(device->mixlock));
721 
722 	if (device->state == STATE_STOPPED)
723 		device->state = STATE_STARTING;
724 	else if (device->state == STATE_STOPPING)
725 		device->state = STATE_PLAYING;
726 
727 	LeaveCriticalSection(&(device->mixlock));
728 	/* **** */
729 
730 	return DS_OK;
731 }
732 
733 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
734 {
735         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
736         DirectSoundDevice *device = This->device;
737 	TRACE("(%p)\n", iface);
738 
739 	/* **** */
740 	EnterCriticalSection(&(device->mixlock));
741 
742 	if (device->state == STATE_PLAYING)
743 		device->state = STATE_STOPPING;
744 	else if (device->state == STATE_STARTING)
745 		device->state = STATE_STOPPED;
746 
747 	LeaveCriticalSection(&(device->mixlock));
748 	/* **** */
749 
750 	return DS_OK;
751 }
752 
753 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
754 {
755     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
756     ULONG ref = InterlockedIncrement(&(This->ref));
757     TRACE("(%p) ref was %d\n", This, ref - 1);
758     if(ref == 1)
759         InterlockedIncrement(&This->numIfaces);
760     return ref;
761 }
762 
763 void primarybuffer_destroy(IDirectSoundBufferImpl *This)
764 {
765     This->device->primary = NULL;
766     HeapFree(GetProcessHeap(), 0, This);
767     TRACE("(%p) released\n", This);
768 }
769 
770 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
771 {
772     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
773     DWORD ref = InterlockedDecrement(&(This->ref));
774     TRACE("(%p) ref was %d\n", This, ref + 1);
775 
776     if (!ref && !InterlockedDecrement(&This->numIfaces))
777         primarybuffer_destroy(This);
778     return ref;
779 }
780 
781 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
782 	LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
783 ) {
784 	HRESULT	hres;
785         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
786         DirectSoundDevice *device = This->device;
787 	TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
788 
789 	/* **** */
790 	EnterCriticalSection(&(device->mixlock));
791 
792 	hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
793 	if (hres != DS_OK) {
794 		WARN("DSOUND_PrimaryGetPosition failed\n");
795 		LeaveCriticalSection(&(device->mixlock));
796 		return hres;
797 	}
798 	if (writepos) {
799 		if (device->state != STATE_STOPPED)
800 			/* apply the documented 10ms lead to writepos */
801 			*writepos += device->writelead;
802 		while (*writepos >= device->buflen) *writepos -= device->buflen;
803 	}
804 
805 	LeaveCriticalSection(&(device->mixlock));
806 	/* **** */
807 
808 	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
809 	return DS_OK;
810 }
811 
812 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
813 	LPDIRECTSOUNDBUFFER iface,LPDWORD status
814 ) {
815         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
816         DirectSoundDevice *device = This->device;
817 	TRACE("(%p,%p)\n", iface, status);
818 
819 	if (status == NULL) {
820 		WARN("invalid parameter: status == NULL\n");
821 		return DSERR_INVALIDPARAM;
822 	}
823 
824 	*status = 0;
825 	if ((device->state == STATE_STARTING) ||
826 	    (device->state == STATE_PLAYING))
827 		*status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
828 
829 	TRACE("status=%x\n", *status);
830 	return DS_OK;
831 }
832 
833 
834 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
835     LPDIRECTSOUNDBUFFER iface,
836     LPWAVEFORMATEX lpwf,
837     DWORD wfsize,
838     LPDWORD wfwritten)
839 {
840     DWORD size;
841     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
842     DirectSoundDevice *device = This->device;
843     TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
844 
845     size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
846 
847     if (lpwf) {	/* NULL is valid */
848         if (wfsize >= size) {
849             CopyMemory(lpwf,device->pwfx,size);
850             if (wfwritten)
851                 *wfwritten = size;
852         } else {
853             WARN("invalid parameter: wfsize too small\n");
854             if (wfwritten)
855                 *wfwritten = 0;
856             return DSERR_INVALIDPARAM;
857         }
858     } else {
859         if (wfwritten)
860             *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
861         else {
862             WARN("invalid parameter: wfwritten == NULL\n");
863             return DSERR_INVALIDPARAM;
864         }
865     }
866 
867     return DS_OK;
868 }
869 
870 static HRESULT WINAPI PrimaryBufferImpl_Lock(
871 	LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
872 ) {
873 	HRESULT hres;
874         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
875         DirectSoundDevice *device = This->device;
876 	TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
877 		iface,
878 		writecursor,
879 		writebytes,
880 		lplpaudioptr1,
881 		audiobytes1,
882 		lplpaudioptr2,
883 		audiobytes2,
884 		flags,
885 		GetTickCount()
886 	);
887 
888         if (!audiobytes1)
889             return DSERR_INVALIDPARAM;
890 
891 	if (device->priolevel != DSSCL_WRITEPRIMARY) {
892 		WARN("failed priority check!\n");
893 		return DSERR_PRIOLEVELNEEDED;
894 	}
895 
896         /* when this flag is set, writecursor is meaningless and must be calculated */
897 	if (flags & DSBLOCK_FROMWRITECURSOR) {
898 		/* GetCurrentPosition does too much magic to duplicate here */
899 		hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
900 		if (hres != DS_OK) {
901 			WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
902 			return hres;
903 		}
904 	}
905 
906         /* when this flag is set, writebytes is meaningless and must be set */
907 	if (flags & DSBLOCK_ENTIREBUFFER)
908 		writebytes = device->buflen;
909 
910         if (writecursor >= device->buflen) {
911                 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
912 		     writecursor, device->buflen);
913                 return DSERR_INVALIDPARAM;
914         }
915 
916         if (writebytes > device->buflen) {
917                 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
918 		     writebytes, device->buflen);
919                 return DSERR_INVALIDPARAM;
920         }
921 
922 	if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
923 		hres = IDsDriverBuffer_Lock(device->hwbuf,
924 					    lplpaudioptr1, audiobytes1,
925 					    lplpaudioptr2, audiobytes2,
926 					    writecursor, writebytes,
927 					    0);
928 		if (hres != DS_OK) {
929 			WARN("IDsDriverBuffer_Lock failed\n");
930 			return hres;
931 		}
932 	} else {
933 		if (writecursor+writebytes <= device->buflen) {
934 			*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
935 			*audiobytes1 = writebytes;
936 			if (lplpaudioptr2)
937 				*(LPBYTE*)lplpaudioptr2 = NULL;
938 			if (audiobytes2)
939 				*audiobytes2 = 0;
940 			TRACE("->%d.0\n",writebytes);
941 		} else {
942 			*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
943 			*audiobytes1 = device->buflen-writecursor;
944 			if (lplpaudioptr2)
945 				*(LPBYTE*)lplpaudioptr2 = device->buffer;
946 			if (audiobytes2)
947 				*audiobytes2 = writebytes-(device->buflen-writecursor);
948 			TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
949 		}
950 	}
951 	return DS_OK;
952 }
953 
954 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
955 	LPDIRECTSOUNDBUFFER iface,DWORD newpos
956 ) {
957         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
958 	TRACE("(%p,%d)\n",This,newpos);
959 
960 	/* You cannot set the position of the primary buffer */
961 	WARN("invalid call\n");
962 	return DSERR_INVALIDCALL;
963 }
964 
965 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
966 	LPDIRECTSOUNDBUFFER iface,LONG pan
967 ) {
968         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
969         DirectSoundDevice *device = This->device;
970 	DWORD ampfactors;
971         HRESULT hres = DS_OK;
972 	TRACE("(%p,%d)\n", iface, pan);
973 
974 	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
975 		WARN("control unavailable\n");
976 		return DSERR_CONTROLUNAVAIL;
977 	}
978 
979 	if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
980 		WARN("invalid parameter: pan = %d\n", pan);
981 		return DSERR_INVALIDPARAM;
982 	}
983 
984 	/* **** */
985 	EnterCriticalSection(&(device->mixlock));
986 
987         if (!device->hwbuf)
988         {
989             waveOutGetVolume(device->hwo, &ampfactors);
990             device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
991             device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
992             DSOUND_AmpFactorToVolPan(&device->volpan);
993         }
994         if (pan != device->volpan.lPan) {
995             device->volpan.lPan=pan;
996             DSOUND_RecalcVolPan(&device->volpan);
997             if (device->hwbuf) {
998                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
999                 if (hres != DS_OK)
1000                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
1001             } else {
1002                 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
1003                 waveOutSetVolume(device->hwo, ampfactors);
1004             }
1005         }
1006 
1007 	LeaveCriticalSection(&(device->mixlock));
1008 	/* **** */
1009 
1010 	return hres;
1011 }
1012 
1013 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
1014 	LPDIRECTSOUNDBUFFER iface,LPLONG pan
1015 ) {
1016         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1017         DirectSoundDevice *device = This->device;
1018 	DWORD ampfactors;
1019 	TRACE("(%p,%p)\n", iface, pan);
1020 
1021 	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1022 		WARN("control unavailable\n");
1023 		return DSERR_CONTROLUNAVAIL;
1024 	}
1025 
1026 	if (pan == NULL) {
1027 		WARN("invalid parameter: pan == NULL\n");
1028 		return DSERR_INVALIDPARAM;
1029 	}
1030 
1031         if (!device->hwbuf)
1032         {
1033 	    waveOutGetVolume(device->hwo, &ampfactors);
1034 	    device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
1035 	    device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
1036 	    DSOUND_AmpFactorToVolPan(&device->volpan);
1037         }
1038 	*pan = device->volpan.lPan;
1039 	return DS_OK;
1040 }
1041 
1042 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
1043 	LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1044 ) {
1045         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1046         DirectSoundDevice *device = This->device;
1047 	TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1048 
1049 	if (device->priolevel != DSSCL_WRITEPRIMARY) {
1050 		WARN("failed priority check!\n");
1051 		return DSERR_PRIOLEVELNEEDED;
1052 	}
1053 
1054 	if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
1055 		HRESULT	hres;
1056 
1057 		if ((char *)p1 - (char *)device->buffer + x1 > device->buflen)
1058 		    hres = DSERR_INVALIDPARAM;
1059 		else
1060 		    hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
1061 
1062 		if (hres != DS_OK) {
1063 			WARN("IDsDriverBuffer_Unlock failed\n");
1064 			return hres;
1065 		}
1066 	}
1067 
1068 	return DS_OK;
1069 }
1070 
1071 static HRESULT WINAPI PrimaryBufferImpl_Restore(
1072 	LPDIRECTSOUNDBUFFER iface
1073 ) {
1074         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1075 	FIXME("(%p):stub\n",This);
1076 	return DS_OK;
1077 }
1078 
1079 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
1080 	LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1081 ) {
1082         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1083         DirectSoundDevice *device = This->device;
1084 	TRACE("(%p,%p)\n", iface, freq);
1085 
1086 	if (freq == NULL) {
1087 		WARN("invalid parameter: freq == NULL\n");
1088 		return DSERR_INVALIDPARAM;
1089 	}
1090 
1091 	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1092 		WARN("control unavailable\n");
1093 		return DSERR_CONTROLUNAVAIL;
1094 	}
1095 
1096 	*freq = device->pwfx->nSamplesPerSec;
1097 	TRACE("-> %d\n", *freq);
1098 
1099 	return DS_OK;
1100 }
1101 
1102 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1103 	LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1104 ) {
1105         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1106 	WARN("(%p) already initialized\n", This);
1107 	return DSERR_ALREADYINITIALIZED;
1108 }
1109 
1110 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1111 	LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1112 ) {
1113         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1114         DirectSoundDevice *device = This->device;
1115   	TRACE("(%p,%p)\n", iface, caps);
1116 
1117 	if (caps == NULL) {
1118 		WARN("invalid parameter: caps == NULL\n");
1119 		return DSERR_INVALIDPARAM;
1120 	}
1121 
1122 	if (caps->dwSize < sizeof(*caps)) {
1123 		WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1124 		return DSERR_INVALIDPARAM;
1125 	}
1126 
1127 	caps->dwFlags = This->dsbd.dwFlags;
1128 	caps->dwBufferBytes = device->buflen;
1129 
1130 	/* Windows reports these as zero */
1131 	caps->dwUnlockTransferRate = 0;
1132 	caps->dwPlayCpuOverhead = 0;
1133 
1134 	return DS_OK;
1135 }
1136 
1137 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1138 	LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1139 ) {
1140         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1141         DirectSoundDevice *device = This->device;
1142 	TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1143 
1144 	if (ppobj == NULL) {
1145 		WARN("invalid parameter\n");
1146 		return E_INVALIDARG;
1147 	}
1148 
1149 	*ppobj = NULL;	/* assume failure */
1150 
1151 	if ( IsEqualGUID(riid, &IID_IUnknown) ||
1152 	     IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1153 		IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1154 		*ppobj = This;
1155 		return S_OK;
1156 	}
1157 
1158 	/* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1159 	/* a primary buffer can't have a DirectSoundBuffer8 interface */
1160 	if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1161 		WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1162 		return E_NOINTERFACE;
1163 	}
1164 
1165 	if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1166 		ERR("app requested IDirectSoundNotify on primary buffer\n");
1167 		/* FIXME: should we support this? */
1168 		return E_NOINTERFACE;
1169 	}
1170 
1171 	if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1172 		ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1173 		return E_NOINTERFACE;
1174 	}
1175 
1176         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1177 		if (!device->listener)
1178 			IDirectSound3DListenerImpl_Create(device, &device->listener);
1179 		if (device->listener) {
1180 			*ppobj = device->listener;
1181 			IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1182 			return S_OK;
1183 		}
1184 
1185 		WARN("IID_IDirectSound3DListener failed\n");
1186 		return E_NOINTERFACE;
1187 	}
1188 
1189 	if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1190 		FIXME("app requested IKsPropertySet on primary buffer\n");
1191 		return E_NOINTERFACE;
1192 	}
1193 
1194 	FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1195 	return E_NOINTERFACE;
1196 }
1197 
1198 static const IDirectSoundBufferVtbl dspbvt =
1199 {
1200 	PrimaryBufferImpl_QueryInterface,
1201 	PrimaryBufferImpl_AddRef,
1202 	PrimaryBufferImpl_Release,
1203 	PrimaryBufferImpl_GetCaps,
1204 	PrimaryBufferImpl_GetCurrentPosition,
1205 	PrimaryBufferImpl_GetFormat,
1206 	PrimaryBufferImpl_GetVolume,
1207 	PrimaryBufferImpl_GetPan,
1208         PrimaryBufferImpl_GetFrequency,
1209 	PrimaryBufferImpl_GetStatus,
1210 	PrimaryBufferImpl_Initialize,
1211 	PrimaryBufferImpl_Lock,
1212 	PrimaryBufferImpl_Play,
1213 	PrimaryBufferImpl_SetCurrentPosition,
1214 	PrimaryBufferImpl_SetFormat,
1215 	PrimaryBufferImpl_SetVolume,
1216 	PrimaryBufferImpl_SetPan,
1217 	PrimaryBufferImpl_SetFrequency,
1218 	PrimaryBufferImpl_Stop,
1219 	PrimaryBufferImpl_Unlock,
1220 	PrimaryBufferImpl_Restore
1221 };
1222 
1223 HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
1224 	const DSBUFFERDESC *dsbd)
1225 {
1226 	IDirectSoundBufferImpl *dsb;
1227 	TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1228 
1229 	if (dsbd->lpwfxFormat) {
1230 		WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1231 		*ppdsb = NULL;
1232 		return DSERR_INVALIDPARAM;
1233 	}
1234 
1235 	dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1236 
1237 	if (dsb == NULL) {
1238 		WARN("out of memory\n");
1239 		*ppdsb = NULL;
1240 		return DSERR_OUTOFMEMORY;
1241 	}
1242 
1243         dsb->ref = 1;
1244         dsb->numIfaces = 1;
1245 	dsb->device = device;
1246 	dsb->IDirectSoundBuffer8_iface.lpVtbl = (IDirectSoundBuffer8Vtbl *)&dspbvt;
1247 	dsb->dsbd = *dsbd;
1248 
1249 	TRACE("Created primary buffer at %p\n", dsb);
1250 	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1251 		"bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1252 		device->pwfx->wFormatTag, device->pwfx->nChannels,
1253                 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1254                 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1255                 device->pwfx->cbSize);
1256 
1257 	*ppdsb = dsb;
1258 	return S_OK;
1259 }
1260