primary.c 35.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*  			DirectSound
 *
 * Copyright 1998 Marcus Meissner
 * Copyright 1998 Rob Riggs
 * Copyright 2000-2002 TransGaming Technologies, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
 *
 * TODO:
 *      When PrimarySetFormat (via ReopenDevice or PrimaryOpen) fails,
 *       it leaves dsound in unusable (not really open) state.
24 25
 */

26
#include <stdarg.h>
27

Robert Reif's avatar
Robert Reif committed
28 29
#define NONAMELESSSTRUCT
#define NONAMELESSUNION
30 31
#include "windef.h"
#include "winbase.h"
32
#include "winuser.h"
33
#include "mmsystem.h"
34
#include "winternl.h"
35 36 37 38 39 40 41 42
#include "mmddk.h"
#include "wine/debug.h"
#include "dsound.h"
#include "dsdriver.h"
#include "dsound_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dsound);

43 44 45 46 47 48 49 50 51
/** Calculate how long a fragment length of about 10 ms should be in frames
 *
 * nSamplesPerSec: Frequency rate in samples per second
 * nBlockAlign: Size of a single blockalign
 *
 * Returns:
 * Size in bytes of a single fragment
 */
DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
52
{
53 54 55 56 57 58 59
    /* Given a timer delay of 10ms, the fragment size is approximately:
     *     fraglen = (nSamplesPerSec * 10 / 1000) * nBlockAlign
     * ==> fraglen = (nSamplesPerSec / 100) * nBlockSize
     *
     * ALSA uses buffers that are powers of 2. Because of this, fraglen
     * is rounded up to the nearest power of 2:
     */
60

61 62
    if (nSamplesPerSec <= 12800)
        return 128 * nBlockAlign;
63

64 65
    if (nSamplesPerSec <= 25600)
        return 256 * nBlockAlign;
66

67 68
    if (nSamplesPerSec <= 51200)
        return 512 * nBlockAlign;
69

70
    return 1024 * nBlockAlign;
71 72 73 74 75
}

static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
{
    TRACE("(%p)\n", device);
76

77 78 79
    device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
    device->helfrags = device->buflen / device->fraglen;
    TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
80

81 82 83 84 85
    if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
        device->writelead = 0;
    else
        /* calculate the 10ms write lead */
        device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
86 87
}

88 89 90
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
	HRESULT hres = DS_OK;
91 92
	TRACE("(%p, %d)\n", device, forcewave);

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	if (device->driver)
	{
		IDsDriver_Close(device->driver);
		if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
			waveOutClose(device->hwo);
		IDsDriver_Release(device->driver);
		device->driver = NULL;
		device->buffer = NULL;
		device->hwo = 0;
	}
	else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
		waveOutClose(device->hwo);

	/* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
	if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
		waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);

	/* Get driver description */
	if (device->driver) {
		DWORD wod = device->drvdesc.dnDevNode;
		hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
		device->drvdesc.dnDevNode = wod;
		if (FAILED(hres)) {
			WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
			IDsDriver_Release(device->driver);
			device->driver = NULL;
		}
        }

        /* if no DirectSound interface available, use WINMM API instead */
	if (!device->driver)
		device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;

	if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
	{
		DWORD flags = CALLBACK_FUNCTION;

		if (device->driver)
			flags |= WAVE_DIRECTSOUND;

133
		hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD_PTR)device, flags));
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
		if (FAILED(hres)) {
			WARN("waveOutOpen failed\n");
			if (device->driver)
			{
				IDsDriver_Release(device->driver);
				device->driver = NULL;
			}
			return hres;
		}
	}

	if (device->driver)
		hres = IDsDriver_Open(device->driver);

	return hres;
}

151
static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
152
{
153
	DWORD buflen;
154
	HRESULT err = DS_OK;
155
	TRACE("(%p)\n", device);
156

157 158 159
	/* on original windows, the buffer it set to a fixed size, no matter what the settings are.
	   on windows this size is always fixed (tested on win-xp) */
	if (!device->buflen)
160 161
		device->buflen = ds_hel_buflen;
	buflen = device->buflen;
162 163 164
	buflen -= buflen % device->pwfx->nBlockAlign;
	device->buflen = buflen;

165 166 167 168 169 170 171 172
	if (device->driver)
	{
		err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
						  DSBCAPS_PRIMARYBUFFER,0,
						  &(device->buflen),&(device->buffer),
						  (LPVOID*)&(device->hwbuf));

		if (err != DS_OK) {
173
			WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
174 175
			err = DSOUND_ReopenDevice(device, TRUE);
			if (FAILED(err))
176 177 178 179 180
			{
				WARN("Falling back to waveout failed too! Giving up\n");
				return err;
			}
		}
181 182 183 184
                if (device->hwbuf)
                    IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);

                DSOUND_RecalcPrimary(device);
185 186 187 188 189
		device->prebuf = ds_snd_queue_max;
		if (device->helfrags < ds_snd_queue_min)
		{
			WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
			device->buflen = buflen;
190 191
			IDsDriverBuffer_Release(device->hwbuf);
			device->hwbuf = NULL;
192 193 194 195 196 197 198 199 200
			err = DSOUND_ReopenDevice(device, TRUE);
			if (FAILED(err))
			{
				WARN("Falling back to waveout failed too! Giving up\n");
				return err;
			}
		}
		else if (device->helfrags < ds_snd_queue_max)
			device->prebuf = device->helfrags;
201 202
	}

203 204 205 206 207 208 209 210 211 212
	device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
	device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len);
	if (!device->mix_buffer)
	{
		if (device->hwbuf)
			IDsDriverBuffer_Release(device->hwbuf);
		device->hwbuf = NULL;
		return DSERR_OUTOFMEMORY;
	}

213 214 215
	if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
	else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;

216
	/* are we using waveOut stuff? */
217
	if (!device->driver) {
218
		LPBYTE newbuf;
219
		LPWAVEHDR headers = NULL;
220
		DWORD overshot;
221 222
		unsigned int c;

223
		/* Start in pause mode, to allow buffers to get filled */
224
		waveOutPause(device->hwo);
225

226
		TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
227

228
		/* reallocate emulated primary buffer */
229
		if (device->buffer)
230
			newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
231
		else
232
			newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
233

234
		if (!newbuf) {
235
			ERR("failed to allocate primary buffer\n");
236
			return DSERR_OUTOFMEMORY;
237
			/* but the old buffer might still exist and must be re-prepared */
238
		}
239 240 241 242 243 244 245 246 247

		DSOUND_RecalcPrimary(device);
		if (device->pwave)
			headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
		else
			headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));

		if (!headers) {
			ERR("failed to allocate wave headers\n");
248 249 250
			HeapFree(GetProcessHeap(), 0, newbuf);
			DSOUND_RecalcPrimary(device);
			return DSERR_OUTOFMEMORY;
251
		}
252

253 254 255 256 257 258 259
		device->buffer = newbuf;
		device->pwave = headers;

		/* prepare fragment headers */
		for (c=0; c<device->helfrags; c++) {
			device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
			device->pwave[c].dwBufferLength = device->fraglen;
260
			device->pwave[c].dwUser = (DWORD_PTR)device;
261 262 263 264 265 266 267
			device->pwave[c].dwFlags = 0;
			device->pwave[c].dwLoops = 0;
			err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
			if (err != DS_OK) {
				while (c--)
					waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
				break;
268 269
			}
		}
270

271
		overshot = device->buflen % device->fraglen;
272 273 274
		/* sanity */
		if(overshot)
		{
275
			overshot -= overshot % device->pwfx->nBlockAlign;
276
			device->pwave[device->helfrags - 1].dwBufferLength += overshot;
277
		}
278

279
		TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
280
	}
281 282
	device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
	device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
283
	FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
284
	FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
285
	device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
286 287 288 289
	return err;
}


290
static void DSOUND_PrimaryClose(DirectSoundDevice *device)
291
{
292
	TRACE("(%p)\n", device);
293

294
	/* are we using waveOut stuff? */
295
	if (!device->hwbuf) {
296 297
		unsigned c;

298 299 300
		/* get out of CS when calling the wave system */
		LeaveCriticalSection(&(device->mixlock));
		/* **** */
301 302
		device->pwqueue = (DWORD)-1; /* resetting queues */
		waveOutReset(device->hwo);
303 304
		for (c=0; c<device->helfrags; c++)
			waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
305 306 307 308
		/* **** */
		EnterCriticalSection(&(device->mixlock));

		/* clear the queue */
309
		device->pwqueue = 0;
310
	} else {
311 312
		ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
		if (!ref)
313
			device->hwbuf = 0;
314 315
		else
			ERR("Still %d references on primary buffer, refcount leak?\n", ref);
316 317 318
	}
}

319
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
320 321
{
	HRESULT err = DS_OK;
322
	TRACE("(%p)\n", device);
323

324
	device->buflen = ds_hel_buflen;
325
	err = DSOUND_PrimaryOpen(device);
326 327 328

	if (err != DS_OK) {
		WARN("DSOUND_PrimaryOpen failed\n");
329
		return err;
330 331
	}

332
	device->state = STATE_STOPPED;
333 334 335
	return DS_OK;
}

336
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
337
{
338
	TRACE("(%p)\n", device);
339

340 341
	/* **** */
	EnterCriticalSection(&(device->mixlock));
342

343 344 345 346 347
	DSOUND_PrimaryClose(device);
	if (device->driver) {
		if (device->hwbuf) {
			if (IDsDriverBuffer_Release(device->hwbuf) == 0)
				device->hwbuf = 0;
348
		}
349 350
	} else
                HeapFree(GetProcessHeap(),0,device->pwave);
351 352
        HeapFree(GetProcessHeap(),0,device->pwfx);
        device->pwfx=NULL;
353 354 355 356

	LeaveCriticalSection(&(device->mixlock));
	/* **** */

357 358 359
	return DS_OK;
}

360
HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
361 362
{
	HRESULT err = DS_OK;
363
	TRACE("(%p)\n", device);
364

365 366
	if (device->hwbuf) {
		err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
367 368 369
		if (err != DS_OK)
			WARN("IDsDriverBuffer_Play failed\n");
	} else {
370
		err = mmErr(waveOutRestart(device->hwo));
371 372 373 374
		if (err != DS_OK)
			WARN("waveOutRestart failed\n");
	}

375 376 377
	return err;
}

378
HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
379 380
{
	HRESULT err = DS_OK;
381
	TRACE("(%p)\n", device);
382

383 384
	if (device->hwbuf) {
		err = IDsDriverBuffer_Stop(device->hwbuf);
385
		if (err == DSERR_BUFFERLOST) {
386
			DSOUND_PrimaryClose(device);
387
			err = DSOUND_ReopenDevice(device, FALSE);
388
			if (FAILED(err))
389 390 391 392 393 394 395
				ERR("DSOUND_ReopenDevice failed\n");
			else
			{
				err = DSOUND_PrimaryOpen(device);
				if (FAILED(err))
					WARN("DSOUND_PrimaryOpen failed\n");
			}
396 397
		} else if (err != DS_OK) {
			WARN("IDsDriverBuffer_Stop failed\n");
398
		}
399
	} else {
400

401
		/* don't call the wave system with the lock set */
402 403 404
		LeaveCriticalSection(&(device->mixlock));
		/* **** */

405
		err = mmErr(waveOutPause(device->hwo));
406 407 408 409

		/* **** */
		EnterCriticalSection(&(device->mixlock));

410 411 412
		if (err != DS_OK)
			WARN("waveOutPause failed\n");
	}
413

414 415 416
	return err;
}

417
HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
418
{
419
	TRACE("(%p,%p,%p)\n", device, playpos, writepos);
420

421 422
	if (device->hwbuf) {
		HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
423
		if (err != S_OK) {
424 425 426
			WARN("IDsDriverBuffer_GetPosition failed\n");
			return err;
		}
427
	} else {
428
		TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
429 430

		/* check if playpos was requested */
431
		if (playpos)
432 433 434 435
			/* use the cached play position */
			*playpos = device->pwplay * device->fraglen;

		/* check if writepos was requested */
436
		if (writepos)
437
			/* the writepos is the first non-queued position */
438
			*writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
439
	}
440
	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
441 442 443
	return DS_OK;
}

444 445 446 447 448 449 450 451
static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
{
	if (wfex->wFormatTag == WAVE_FORMAT_PCM)
		return sizeof(WAVEFORMATEX);
	else
		return sizeof(WAVEFORMATEX) + wfex->cbSize;
}

452 453
LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
{
454
	DWORD size = DSOUND_GetFormatSize(wfex);
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
	LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(),0,size);
	if (pwfx == NULL) {
		WARN("out of memory\n");
	} else if (wfex->wFormatTag != WAVE_FORMAT_PCM) {
		CopyMemory(pwfx, wfex, size);
	} else {
		CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
		pwfx->cbSize=0;
		if (pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample/8) {
			WARN("Fixing bad nBlockAlign (%u)\n", pwfx->nBlockAlign);
			pwfx->nBlockAlign  = pwfx->nChannels * pwfx->wBitsPerSample/8;
		}
		if (pwfx->nAvgBytesPerSec != pwfx->nSamplesPerSec * pwfx->nBlockAlign) {
			WARN("Fixing bad nAvgBytesPerSec (%u)\n", pwfx->nAvgBytesPerSec);
			pwfx->nAvgBytesPerSec  = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
		}
	}
	return pwfx;
}

475
static HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
476
{
477
	HRESULT err = DSERR_BUFFERLOST;
478
	int i;
479
	DWORD nSamplesPerSec, bpp, chans;
480
	LPWAVEFORMATEX oldpwfx;
481
	TRACE("(%p,%p)\n", device, wfex);
482

483
	if (device->priolevel == DSSCL_NORMAL) {
484
		WARN("failed priority check!\n");
485 486 487 488
		return DSERR_PRIOLEVELNEEDED;
	}

	/* Let's be pedantic! */
489
	if (wfex == NULL) {
490
		WARN("invalid parameter: wfex==NULL!\n");
491 492
		return DSERR_INVALIDPARAM;
	}
493 494
	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
              "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
495 496 497 498
	      wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
	      wfex->nAvgBytesPerSec, wfex->nBlockAlign,
	      wfex->wBitsPerSample, wfex->cbSize);

499
	/* **** */
500 501
	RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
	EnterCriticalSection(&(device->mixlock));
502

503
	nSamplesPerSec = device->pwfx->nSamplesPerSec;
504 505
	bpp = device->pwfx->wBitsPerSample;
	chans = device->pwfx->nChannels;
506

507 508 509 510
	oldpwfx = device->pwfx;
	device->pwfx = DSOUND_CopyFormat(wfex);
	if (device->pwfx == NULL) {
		device->pwfx = oldpwfx;
511
		oldpwfx = NULL;
512 513 514
		err = DSERR_OUTOFMEMORY;
		goto done;
	}
515

516 517
	if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
		err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
518 519 520 521

		/* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
		if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
		{
522 523
			DWORD cp_size = wfex->wFormatTag == WAVE_FORMAT_PCM ?
				sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + wfex->cbSize;
524
			err = DSERR_BUFFERLOST;
525
			CopyMemory(device->pwfx, wfex, cp_size);
526 527
		}

528
		if (err != DSERR_BUFFERLOST && FAILED(err)) {
529
			DWORD size = DSOUND_GetFormatSize(oldpwfx);
530
			WARN("IDsDriverBuffer_SetFormat failed\n");
531 532
			if (!forced) {
				CopyMemory(device->pwfx, oldpwfx, size);
533
				err = DS_OK;
534
			}
535
			goto done;
536
		}
537

538 539
		if (err == S_FALSE)
		{
540
			/* ALSA specific: S_FALSE tells that recreation was successful,
541
			 * but size and location may be changed, and buffer has to be restarted
542
			 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
543 544 545 546 547 548 549 550 551 552 553
			 * and the entire re-initialization will occur anyway
			 */
			IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
			IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);

			if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
			else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
			device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
			err = DS_OK;
		}
		DSOUND_RecalcPrimary(device);
554 555
	}

556
	if (err == DSERR_BUFFERLOST)
557 558 559
	{
		DSOUND_PrimaryClose(device);

560 561
		err = DSOUND_ReopenDevice(device, FALSE);
		if (FAILED(err))
562
		{
563 564
			WARN("DSOUND_ReopenDevice failed: %08x\n", err);
			goto done;
565 566 567 568
		}
		err = DSOUND_PrimaryOpen(device);
		if (err != DS_OK) {
			WARN("DSOUND_PrimaryOpen failed\n");
569
			goto done;
570
		}
571 572 573 574 575 576 577 578 579 580 581

		if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
		{
			DSOUND_PrimaryClose(device);
			device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
			err = DSOUND_ReopenDevice(device, TRUE);
			if (FAILED(err))
				WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
			else if (FAILED((err = DSOUND_PrimaryOpen(device))))
				WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
		}
582 583
	}

584 585 586 587 588 589
	device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
	device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len);
	FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
	device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
	device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];

590
	if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
591 592
		IDirectSoundBufferImpl** dsb = device->buffers;
		for (i = 0; i < device->nrofbuffers; i++, dsb++) {
593
			/* **** */
594
			RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
595

596 597
			(*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
			DSOUND_RecalcFormat((*dsb));
598
			DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE);
599
			(*dsb)->primary_mixpos = 0;
600

601
			RtlReleaseResource(&(*dsb)->lock);
602 603 604 605
			/* **** */
		}
	}

606
done:
607 608
	LeaveCriticalSection(&(device->mixlock));
	RtlReleaseResource(&(device->buffer_list_lock));
609 610
	/* **** */

611
	HeapFree(GetProcessHeap(), 0, oldpwfx);
612 613 614
	return err;
}

615 616 617 618 619 620
/*******************************************************************************
 *		PrimaryBuffer
 */
/* This sets this format for the <em>Primary Buffer Only</em> */
/* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
621
    LPDIRECTSOUNDBUFFER iface,
622 623
    LPCWAVEFORMATEX wfex)
{
624
    DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
625
    TRACE("(%p,%p)\n", iface, wfex);
626
    return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
627 628
}

629
static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
630
	LPDIRECTSOUNDBUFFER iface,LONG vol
631
) {
Robert Reif's avatar
Robert Reif committed
632
	DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
633
	DWORD ampfactors;
634
        HRESULT hres = DS_OK;
635
	TRACE("(%p,%d)\n", iface, vol);
636

637
	if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
638
		WARN("control unavailable\n");
639
		return DSERR_CONTROLUNAVAIL;
640
	}
641

642
	if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
643
		WARN("invalid parameter: vol = %d\n", vol);
644
		return DSERR_INVALIDPARAM;
645
	}
646 647

	/* **** */
648
	EnterCriticalSection(&(device->mixlock));
649

650
        waveOutGetVolume(device->hwo, &ampfactors);
651 652 653 654 655 656
        device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
        device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
        DSOUND_AmpFactorToVolPan(&device->volpan);
        if (vol != device->volpan.lVolume) {
            device->volpan.lVolume=vol;
            DSOUND_RecalcVolPan(&device->volpan);
657
            if (device->hwbuf) {
658
                hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
659
                if (hres != DS_OK)
660 661
                    WARN("IDsDriverBuffer_SetVolumePan failed\n");
            } else {
662
                ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
663
                waveOutSetVolume(device->hwo, ampfactors);
664 665
            }
        }
666

667
	LeaveCriticalSection(&(device->mixlock));
668 669
	/* **** */

670
	return hres;
671 672 673
}

static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
674
	LPDIRECTSOUNDBUFFER iface,LPLONG vol
675
) {
Robert Reif's avatar
Robert Reif committed
676
	DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
677
	DWORD ampfactors;
678
	TRACE("(%p,%p)\n", iface, vol);
679

680
	if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
681 682 683 684
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

685 686
	if (vol == NULL) {
		WARN("invalid parameter: vol = NULL\n");
687
		return DSERR_INVALIDPARAM;
688
	}
689

690 691 692 693 694 695 696 697
        if (!device->hwbuf)
        {
	    waveOutGetVolume(device->hwo, &ampfactors);
	    device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
	    device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
	    DSOUND_AmpFactorToVolPan(&device->volpan);
        }
        *vol = device->volpan.lVolume;
698 699 700 701
	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
702
	LPDIRECTSOUNDBUFFER iface,DWORD freq
703
) {
704
	PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
705
	TRACE("(%p,%d)\n",This,freq);
706 707

	/* You cannot set the frequency of the primary buffer */
708
	WARN("control unavailable\n");
709 710 711 712
	return DSERR_CONTROLUNAVAIL;
}

static HRESULT WINAPI PrimaryBufferImpl_Play(
713
	LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
714
) {
Robert Reif's avatar
Robert Reif committed
715
	DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
716
	TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
717

718
	if (!(flags & DSBPLAY_LOOPING)) {
719
		WARN("invalid parameter: flags = %08x\n", flags);
720
		return DSERR_INVALIDPARAM;
721
	}
722 723

	/* **** */
724
	EnterCriticalSection(&(device->mixlock));
725

726 727 728 729
	if (device->state == STATE_STOPPED)
		device->state = STATE_STARTING;
	else if (device->state == STATE_STOPPING)
		device->state = STATE_PLAYING;
730

731
	LeaveCriticalSection(&(device->mixlock));
732 733 734 735 736
	/* **** */

	return DS_OK;
}

737
static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
738
{
Robert Reif's avatar
Robert Reif committed
739
	DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
740
	TRACE("(%p)\n", iface);
741 742

	/* **** */
743
	EnterCriticalSection(&(device->mixlock));
744

745 746 747 748
	if (device->state == STATE_PLAYING)
		device->state = STATE_STOPPING;
	else if (device->state == STATE_STARTING)
		device->state = STATE_STOPPED;
749

750
	LeaveCriticalSection(&(device->mixlock));
751 752 753 754 755
	/* **** */

	return DS_OK;
}

756
static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
757 758 759
{
    PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
    ULONG ref = InterlockedIncrement(&(This->ref));
760
    TRACE("(%p) ref was %d\n", This, ref - 1);
761
    return ref;
762
}
763

764
static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
765 766 767
{
    PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
    DWORD ref = InterlockedDecrement(&(This->ref));
768
    TRACE("(%p) ref was %d\n", This, ref + 1);
769 770

    if (!ref) {
Robert Reif's avatar
Robert Reif committed
771
        This->device->primary = NULL;
772 773 774 775
        HeapFree(GetProcessHeap(), 0, This);
        TRACE("(%p) released\n", This);
    }
    return ref;
776 777 778
}

static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
779
	LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
780
) {
781
	HRESULT	hres;
Robert Reif's avatar
Robert Reif committed
782
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
783
	TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
784

785 786 787
	/* **** */
	EnterCriticalSection(&(device->mixlock));

788
	hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
789 790
	if (hres != DS_OK) {
		WARN("DSOUND_PrimaryGetPosition failed\n");
791
		LeaveCriticalSection(&(device->mixlock));
792 793
		return hres;
	}
794
	if (writepos) {
795
		if (device->state != STATE_STOPPED)
796
			/* apply the documented 10ms lead to writepos */
797 798
			*writepos += device->writelead;
		while (*writepos >= device->buflen) *writepos -= device->buflen;
799
	}
800 801 802 803

	LeaveCriticalSection(&(device->mixlock));
	/* **** */

804
	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
805 806 807 808
	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
809
	LPDIRECTSOUNDBUFFER iface,LPDWORD status
810
) {
Robert Reif's avatar
Robert Reif committed
811
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
812
	TRACE("(%p,%p)\n", iface, status);
813

814 815
	if (status == NULL) {
		WARN("invalid parameter: status == NULL\n");
816
		return DSERR_INVALIDPARAM;
817
	}
818 819

	*status = 0;
820 821
	if ((device->state == STATE_STARTING) ||
	    (device->state == STATE_PLAYING))
822 823
		*status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;

824
	TRACE("status=%x\n", *status);
825 826 827 828 829
	return DS_OK;
}


static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
830
    LPDIRECTSOUNDBUFFER iface,
831 832 833 834 835
    LPWAVEFORMATEX lpwf,
    DWORD wfsize,
    LPDWORD wfwritten)
{
    DWORD size;
Robert Reif's avatar
Robert Reif committed
836
    DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
837
    TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
838

839
    size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
840 841 842

    if (lpwf) {	/* NULL is valid */
        if (wfsize >= size) {
843
            CopyMemory(lpwf,device->pwfx,size);
844 845 846
            if (wfwritten)
                *wfwritten = size;
        } else {
847
            WARN("invalid parameter: wfsize too small\n");
848 849 850 851 852 853
            if (wfwritten)
                *wfwritten = 0;
            return DSERR_INVALIDPARAM;
        }
    } else {
        if (wfwritten)
854
            *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
855 856 857 858 859
        else {
            WARN("invalid parameter: wfwritten == NULL\n");
            return DSERR_INVALIDPARAM;
        }
    }
860

861
    return DS_OK;
862 863 864
}

static HRESULT WINAPI PrimaryBufferImpl_Lock(
865
	LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
866
) {
867
	HRESULT hres;
Robert Reif's avatar
Robert Reif committed
868
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
869
	TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
870
		iface,
871 872 873 874 875 876 877 878 879 880
		writecursor,
		writebytes,
		lplpaudioptr1,
		audiobytes1,
		lplpaudioptr2,
		audiobytes2,
		flags,
		GetTickCount()
	);

881 882 883
        if (!audiobytes1)
            return DSERR_INVALIDPARAM;

884
	if (device->priolevel != DSSCL_WRITEPRIMARY) {
885
		WARN("failed priority check!\n");
886
		return DSERR_PRIOLEVELNEEDED;
887
	}
888

889
        /* when this flag is set, writecursor is meaningless and must be calculated */
890 891
	if (flags & DSBLOCK_FROMWRITECURSOR) {
		/* GetCurrentPosition does too much magic to duplicate here */
892
		hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
893 894 895 896
		if (hres != DS_OK) {
			WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
			return hres;
		}
897
	}
898 899

        /* when this flag is set, writebytes is meaningless and must be set */
900
	if (flags & DSBLOCK_ENTIREBUFFER)
901
		writebytes = device->buflen;
902 903

        if (writecursor >= device->buflen) {
904
                WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
905 906 907
		     writecursor, device->buflen);
                return DSERR_INVALIDPARAM;
        }
908

909
        if (writebytes > device->buflen) {
910
                WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
911 912 913
		     writebytes, device->buflen);
                return DSERR_INVALIDPARAM;
        }
914

915 916
	if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
		hres = IDsDriverBuffer_Lock(device->hwbuf,
917 918 919 920 921 922 923 924 925
					    lplpaudioptr1, audiobytes1,
					    lplpaudioptr2, audiobytes2,
					    writecursor, writebytes,
					    0);
		if (hres != DS_OK) {
			WARN("IDsDriverBuffer_Lock failed\n");
			return hres;
		}
	} else {
926 927
		if (writecursor+writebytes <= device->buflen) {
			*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
928 929 930 931 932
			*audiobytes1 = writebytes;
			if (lplpaudioptr2)
				*(LPBYTE*)lplpaudioptr2 = NULL;
			if (audiobytes2)
				*audiobytes2 = 0;
933
			TRACE("->%d.0\n",writebytes);
934
		} else {
935 936
			*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
			*audiobytes1 = device->buflen-writecursor;
937
			if (lplpaudioptr2)
938
				*(LPBYTE*)lplpaudioptr2 = device->buffer;
939
			if (audiobytes2)
940
				*audiobytes2 = writebytes-(device->buflen-writecursor);
941
			TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
942 943 944 945 946 947
		}
	}
	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
948
	LPDIRECTSOUNDBUFFER iface,DWORD newpos
949
) {
950
	PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
951
	TRACE("(%p,%d)\n",This,newpos);
952 953

	/* You cannot set the position of the primary buffer */
954
	WARN("invalid call\n");
955 956 957 958
	return DSERR_INVALIDCALL;
}

static HRESULT WINAPI PrimaryBufferImpl_SetPan(
959
	LPDIRECTSOUNDBUFFER iface,LONG pan
960
) {
Robert Reif's avatar
Robert Reif committed
961
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
962
	DWORD ampfactors;
963
        HRESULT hres = DS_OK;
964
	TRACE("(%p,%d)\n", iface, pan);
965

966
	if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
967 968 969 970 971
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

	if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
972
		WARN("invalid parameter: pan = %d\n", pan);
973 974 975 976
		return DSERR_INVALIDPARAM;
	}

	/* **** */
977
	EnterCriticalSection(&(device->mixlock));
978

979 980 981 982 983 984 985 986 987 988
        if (!device->hwbuf)
        {
            waveOutGetVolume(device->hwo, &ampfactors);
            device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
            device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
            DSOUND_AmpFactorToVolPan(&device->volpan);
        }
        if (pan != device->volpan.lPan) {
            device->volpan.lPan=pan;
            DSOUND_RecalcVolPan(&device->volpan);
989
            if (device->hwbuf) {
990
                hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
991
                if (hres != DS_OK)
992
                    WARN("IDsDriverBuffer_SetVolumePan failed\n");
993
            } else {
994
                ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
995
                waveOutSetVolume(device->hwo, ampfactors);
996 997
            }
        }
998

999
	LeaveCriticalSection(&(device->mixlock));
1000 1001
	/* **** */

1002
	return hres;
1003 1004 1005
}

static HRESULT WINAPI PrimaryBufferImpl_GetPan(
1006
	LPDIRECTSOUNDBUFFER iface,LPLONG pan
1007
) {
Robert Reif's avatar
Robert Reif committed
1008
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1009
	DWORD ampfactors;
1010
	TRACE("(%p,%p)\n", iface, pan);
1011

1012
	if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1013 1014 1015 1016
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

1017 1018
	if (pan == NULL) {
		WARN("invalid parameter: pan == NULL\n");
1019
		return DSERR_INVALIDPARAM;
1020
	}
1021

1022 1023 1024 1025 1026 1027 1028 1029
        if (!device->hwbuf)
        {
	    waveOutGetVolume(device->hwo, &ampfactors);
	    device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
	    device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
	    DSOUND_AmpFactorToVolPan(&device->volpan);
        }
	*pan = device->volpan.lPan;
1030 1031 1032 1033
	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_Unlock(
1034
	LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1035
) {
Robert Reif's avatar
Robert Reif committed
1036
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1037
	TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1038

1039
	if (device->priolevel != DSSCL_WRITEPRIMARY) {
1040
		WARN("failed priority check!\n");
1041
		return DSERR_PRIOLEVELNEEDED;
1042
	}
1043

1044
	if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
1045
		HRESULT	hres;
1046 1047 1048 1049 1050 1051

		if ((char *)p1 - (char *)device->buffer + x1 > device->buflen)
		    hres = DSERR_INVALIDPARAM;
		else
		    hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);

1052 1053 1054 1055
		if (hres != DS_OK) {
			WARN("IDsDriverBuffer_Unlock failed\n");
			return hres;
		}
1056 1057 1058 1059 1060 1061
	}

	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_Restore(
1062
	LPDIRECTSOUNDBUFFER iface
1063
) {
1064
	PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1065 1066 1067 1068 1069
	FIXME("(%p):stub\n",This);
	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
1070
	LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1071
) {
Robert Reif's avatar
Robert Reif committed
1072
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1073
	TRACE("(%p,%p)\n", iface, freq);
1074

1075 1076
	if (freq == NULL) {
		WARN("invalid parameter: freq == NULL\n");
1077
		return DSERR_INVALIDPARAM;
1078
	}
1079

1080
	if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1081 1082 1083 1084
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

1085
	*freq = device->pwfx->nSamplesPerSec;
1086
	TRACE("-> %d\n", *freq);
1087 1088 1089 1090 1091

	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1092
	LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1093
) {
1094
	PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1095
	WARN("(%p) already initialized\n", This);
1096 1097 1098 1099
	return DSERR_ALREADYINITIALIZED;
}

static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1100
	LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1101
) {
Robert Reif's avatar
Robert Reif committed
1102
        DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1103
  	TRACE("(%p,%p)\n", iface, caps);
1104

1105 1106
	if (caps == NULL) {
		WARN("invalid parameter: caps == NULL\n");
1107
		return DSERR_INVALIDPARAM;
1108 1109 1110
	}

	if (caps->dwSize < sizeof(*caps)) {
1111
		WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1112 1113
		return DSERR_INVALIDPARAM;
	}
1114

1115 1116
	caps->dwFlags = device->dsbd.dwFlags;
	caps->dwBufferBytes = device->buflen;
1117

1118 1119
	/* Windows reports these as zero */
	caps->dwUnlockTransferRate = 0;
1120 1121 1122 1123 1124 1125
	caps->dwPlayCpuOverhead = 0;

	return DS_OK;
}

static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1126
	LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1127
) {
1128
        PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
Robert Reif's avatar
Robert Reif committed
1129
        DirectSoundDevice *device = This->device;
1130
	TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1131

1132 1133 1134 1135 1136
	if (ppobj == NULL) {
		WARN("invalid parameter\n");
		return E_INVALIDARG;
	}

1137 1138
	*ppobj = NULL;	/* assume failure */

1139
	if ( IsEqualGUID(riid, &IID_IUnknown) ||
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
	     IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
		IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
		*ppobj = This;
		return S_OK;
	}

	/* DirectSoundBuffer and DirectSoundBuffer8 are different and */
	/* a primary buffer can't have a DirectSoundBuffer8 interface */
	if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
		WARN("app requested DirectSoundBuffer8 on primary buffer\n");
		return E_NOINTERFACE;
	}

1153 1154
	if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
		ERR("app requested IDirectSoundNotify on primary buffer\n");
1155
		/* FIXME: should we support this? */
1156
		return E_NOINTERFACE;
1157 1158 1159 1160 1161 1162 1163 1164
	}

	if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
		ERR("app requested IDirectSound3DBuffer on primary buffer\n");
		return E_NOINTERFACE;
	}

        if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1165
		if (!device->listener)
Robert Reif's avatar
Robert Reif committed
1166
			IDirectSound3DListenerImpl_Create(device, &device->listener);
1167 1168
		if (device->listener) {
			*ppobj = device->listener;
1169 1170
			IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
			return S_OK;
1171
		}
1172

1173
		WARN("IID_IDirectSound3DListener failed\n");
1174
		return E_NOINTERFACE;
1175 1176 1177 1178
	}

	if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
		FIXME("app requested IKsPropertySet on primary buffer\n");
1179
		return E_NOINTERFACE;
1180 1181 1182 1183 1184 1185
	}

	FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
	return E_NOINTERFACE;
}

1186
static const IDirectSoundBufferVtbl dspbvt =
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
{
	PrimaryBufferImpl_QueryInterface,
	PrimaryBufferImpl_AddRef,
	PrimaryBufferImpl_Release,
	PrimaryBufferImpl_GetCaps,
	PrimaryBufferImpl_GetCurrentPosition,
	PrimaryBufferImpl_GetFormat,
	PrimaryBufferImpl_GetVolume,
	PrimaryBufferImpl_GetPan,
        PrimaryBufferImpl_GetFrequency,
	PrimaryBufferImpl_GetStatus,
	PrimaryBufferImpl_Initialize,
	PrimaryBufferImpl_Lock,
	PrimaryBufferImpl_Play,
	PrimaryBufferImpl_SetCurrentPosition,
	PrimaryBufferImpl_SetFormat,
	PrimaryBufferImpl_SetVolume,
	PrimaryBufferImpl_SetPan,
	PrimaryBufferImpl_SetFrequency,
	PrimaryBufferImpl_Stop,
	PrimaryBufferImpl_Unlock,
1208
	PrimaryBufferImpl_Restore
1209 1210
};

1211
HRESULT PrimaryBufferImpl_Create(
Robert Reif's avatar
Robert Reif committed
1212 1213
	DirectSoundDevice * device,
	PrimaryBufferImpl ** ppdsb,
1214
	LPCDSBUFFERDESC dsbd)
1215 1216
{
	PrimaryBufferImpl *dsb;
Robert Reif's avatar
Robert Reif committed
1217
	TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1218

1219 1220
	if (dsbd->lpwfxFormat) {
		WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
Robert Reif's avatar
Robert Reif committed
1221
		*ppdsb = NULL;
1222
		return DSERR_INVALIDPARAM;
1223
	}
1224

1225
	dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1226 1227 1228

	if (dsb == NULL) {
		WARN("out of memory\n");
Robert Reif's avatar
Robert Reif committed
1229
		*ppdsb = NULL;
1230 1231 1232
		return DSERR_OUTOFMEMORY;
	}

1233
	dsb->ref = 0;
Robert Reif's avatar
Robert Reif committed
1234
	dsb->device = device;
1235
	dsb->lpVtbl = &dspbvt;
1236

1237
	device->dsbd = *dsbd;
1238 1239

	TRACE("Created primary buffer at %p\n", dsb);
1240 1241
	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
		"bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
Robert Reif's avatar
Robert Reif committed
1242 1243 1244 1245
		device->pwfx->wFormatTag, device->pwfx->nChannels,
                device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
                device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
                device->pwfx->cbSize);
1246

Robert Reif's avatar
Robert Reif committed
1247
	*ppdsb = dsb;
1248 1249
	return S_OK;
}