primary.c 38.7 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

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

WINE_DEFAULT_DEBUG_CHANNEL(dsound);

43
static DWORD DSOUND_fraglen(DirectSoundDevice *device)
44
{
45 46 47 48 49 50 51 52 53 54 55 56 57 58
    REFERENCE_TIME period;
    HRESULT hr;
    DWORD ret;

    hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL);
    if(FAILED(hr)){
        /* just guess at 10ms */
        WARN("GetDevicePeriod failed: %08x\n", hr);
        ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100);
    }else
        ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000);

    ret -= ret % device->pwfx->nBlockAlign;
    return ret;
59 60
}

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client,
				 BOOL forcewave, WAVEFORMATEX **wfx)
{
    WAVEFORMATEXTENSIBLE *retwfe = NULL;
    WAVEFORMATEX *w;
    HRESULT hr;

    if (!forcewave) {
        WAVEFORMATEXTENSIBLE *mixwfe;
        hr = IAudioClient_GetMixFormat(client, (WAVEFORMATEX**)&mixwfe);

        if (FAILED(hr))
            return hr;

        if (mixwfe->Format.nChannels > 2) {
            static int once;
            if (!once++)
                FIXME("Limiting channels to 2 due to lack of multichannel support\n");

            mixwfe->Format.nChannels = 2;
            mixwfe->Format.nBlockAlign = mixwfe->Format.nChannels * mixwfe->Format.wBitsPerSample / 8;
            mixwfe->Format.nAvgBytesPerSec = mixwfe->Format.nSamplesPerSec * mixwfe->Format.nBlockAlign;
83
            mixwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
84 85 86 87 88 89 90 91 92 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        }

        if (!IsEqualGUID(&mixwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
            WAVEFORMATEXTENSIBLE testwfe = *mixwfe;

            testwfe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
            testwfe.Format.wBitsPerSample = 32;
            testwfe.Format.nBlockAlign = testwfe.Format.nChannels * testwfe.Format.wBitsPerSample / 8;
            testwfe.Format.nAvgBytesPerSec = testwfe.Format.nSamplesPerSec * testwfe.Format.nBlockAlign;
            testwfe.Samples.wValidBitsPerSample = 0;

            if (FAILED(IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe)))
                w = DSOUND_CopyFormat(&mixwfe->Format);
            else if (retwfe)
                w = DSOUND_CopyFormat(&retwfe->Format);
            else
                w = DSOUND_CopyFormat(&testwfe.Format);
            CoTaskMemFree(retwfe);
            retwfe = NULL;
        } else
            w = DSOUND_CopyFormat(&mixwfe->Format);
        CoTaskMemFree(mixwfe);
    } else if (device->primary_pwfx->wFormatTag == WAVE_FORMAT_PCM ||
               device->primary_pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
        WAVEFORMATEX *wi = device->primary_pwfx;
        WAVEFORMATEXTENSIBLE *wfe;

        /* Convert to WAVEFORMATEXTENSIBLE */
        w = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
        wfe = (WAVEFORMATEXTENSIBLE*)w;
        if (!wfe)
            return DSERR_OUTOFMEMORY;

        wfe->Format = *wi;
        w->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
        w->cbSize = sizeof(*wfe) - sizeof(*w);
        w->nBlockAlign = w->nChannels * w->wBitsPerSample / 8;
        w->nAvgBytesPerSec = w->nSamplesPerSec * w->nBlockAlign;

        wfe->dwChannelMask = 0;
        wfe->Samples.wValidBitsPerSample = 0;
        if (wi->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
            w->wBitsPerSample = 32;
            wfe->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
        } else
            wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
    } else
        w = DSOUND_CopyFormat(device->primary_pwfx);

    if (!w)
        return DSERR_OUTOFMEMORY;

    hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe);
    if (retwfe) {
        memcpy(w, retwfe, sizeof(WAVEFORMATEX) + retwfe->Format.cbSize);
        CoTaskMemFree(retwfe);
    }
    if (FAILED(hr)) {
        WARN("IsFormatSupported failed: %08x\n", hr);
        HeapFree(GetProcessHeap(), 0, w);
        return hr;
    }
    *wfx = w;
    return S_OK;
}

150 151
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
152 153
    UINT prebuf_frames;
    REFERENCE_TIME prebuf_rt;
154
    WAVEFORMATEX *wfx = NULL;
155
    HRESULT hres;
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    TRACE("(%p, %d)\n", device, forcewave);

    if(device->client){
        IAudioClient_Release(device->client);
        device->client = NULL;
    }
    if(device->render){
        IAudioRenderClient_Release(device->render);
        device->render = NULL;
    }
    if(device->clock){
        IAudioClock_Release(device->clock);
        device->clock = NULL;
    }
    if(device->volume){
        IAudioStreamVolume_Release(device->volume);
        device->volume = NULL;
    }
175

176 177
    hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
            CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
178
    if(FAILED(hres)) {
179 180 181 182
        WARN("Activate failed: %08x\n", hres);
        return hres;
    }

183 184 185 186 187 188 189 190 191
    hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
    if (FAILED(hres)) {
        IAudioClient_Release(device->client);
        device->client = NULL;
        return hres;
    }
    HeapFree(GetProcessHeap(), 0, device->pwfx);
    device->pwfx = wfx;

192
    prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
193 194
    prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;

195 196
    hres = IAudioClient_Initialize(device->client,
            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
197
            prebuf_rt, 0, device->pwfx, NULL);
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("Initialize failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
            (void**)&device->render);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
            (void**)&device->clock);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        device->client = NULL;
        device->render = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }
224

225 226 227 228 229 230 231 232 233 234
    hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
            (void**)&device->volume);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        IAudioClock_Release(device->clock);
        device->client = NULL;
        device->render = NULL;
        device->clock = NULL;
        WARN("GetService failed: %08x\n", hres);
235 236
        return hres;
    }
237

238
    return S_OK;
239 240
}

241
HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
242
{
243
	IDirectSoundBufferImpl** dsb = device->buffers;
244
	LPBYTE newbuf;
245
	DWORD i;
246

247
	TRACE("(%p)\n", device);
248

249 250
	device->fraglen = DSOUND_fraglen(device);

251 252 253
	/* 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)
254
		device->buflen = ds_hel_buflen;
255 256 257 258 259 260
	device->buflen -= device->buflen % device->pwfx->nBlockAlign;
	while(device->buflen < device->fraglen * device->prebuf){
		device->buflen += ds_hel_buflen;
		device->buflen -= device->buflen % device->pwfx->nBlockAlign;
	}

261 262
	HeapFree(GetProcessHeap(), 0, device->mix_buffer);
	device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
263
	device->mix_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, device->mix_buffer_len);
264 265 266
	if (!device->mix_buffer)
		return DSERR_OUTOFMEMORY;

267 268 269
	if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
	else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;

270 271
    /* reallocate emulated primary buffer */
    if (device->buffer)
272
        newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen);
273
    else
274
        newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen);
275

276 277 278 279 280
    if (!newbuf) {
        ERR("failed to allocate primary buffer\n");
        return DSERR_OUTOFMEMORY;
        /* but the old buffer might still exist and must be re-prepared */
    }
281

282
    device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
283

284 285
    device->buffer = newbuf;

286 287
    TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
            device->buflen, device->fraglen, device->mix_buffer_len);
288

289 290 291
    if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
            (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
             IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat,
292
                 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
293
        device->normfunction = normfunctions[4];
294
    else
295 296
        device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];

297
	FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
298
	FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
	device->playpos = 0;

    if (device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
	 (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
	  IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
        device->normfunction = normfunctions[4];
    else
        device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];

    for (i = 0; i < device->nrofbuffers; i++) {
        RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE);
        DSOUND_RecalcFormat(dsb[i]);
        RtlReleaseResource(&dsb[i]->lock);
    }

    return DS_OK;
315 316 317
}


318
static void DSOUND_PrimaryClose(DirectSoundDevice *device)
319
{
320
    HRESULT hr;
321

322
    TRACE("(%p)\n", device);
323

324 325 326 327 328
    if(device->client){
        hr = IAudioClient_Stop(device->client);
        if(FAILED(hr))
            WARN("Stop failed: %08x\n", hr);
    }
329

330
    /* clear the queue */
331
    device->in_mmdev_bytes = 0;
332 333
}

334
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
335 336
{
	HRESULT err = DS_OK;
337
	TRACE("(%p)\n", device);
338

339
	device->buflen = ds_hel_buflen;
340
	err = DSOUND_PrimaryOpen(device);
341 342 343

	if (err != DS_OK) {
		WARN("DSOUND_PrimaryOpen failed\n");
344
		return err;
345 346
	}

347
	device->state = STATE_STOPPED;
348 349 350
	return DS_OK;
}

351
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
352
{
353
	TRACE("(%p)\n", device);
354

355 356
	/* **** */
	EnterCriticalSection(&(device->mixlock));
357

358
	DSOUND_PrimaryClose(device);
359 360 361 362 363 364 365

	if(device->primary && (device->primary->ref || device->primary->numIfaces))
		WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);

	HeapFree(GetProcessHeap(), 0, device->primary);
	device->primary = NULL;

366
	HeapFree(GetProcessHeap(),0,device->primary_pwfx);
367 368
	HeapFree(GetProcessHeap(),0,device->pwfx);
	device->pwfx=NULL;
369 370 371 372

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

373 374 375
	return DS_OK;
}

376
HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
377
{
378
    HRESULT hr;
379

380
    TRACE("(%p)\n", device);
381

382 383 384 385 386 387 388
    hr = IAudioClient_Start(device->client);
    if(FAILED(hr)){
        WARN("Start failed: %08x\n", hr);
        return hr;
    }

    return DS_OK;
389 390
}

391
HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
392
{
393
    HRESULT hr;
394

395
    TRACE("(%p)\n", device);
396

397 398 399 400 401
    hr = IAudioClient_Stop(device->client);
    if(FAILED(hr)){
        WARN("Stop failed: %08x\n", hr);
        return hr;
    }
402

403
    return DS_OK;
404 405
}

406
HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
407
{
408
	TRACE("(%p,%p,%p)\n", device, playpos, writepos);
409

410 411
	/* check if playpos was requested */
	if (playpos)
412
		*playpos = device->playing_offs_bytes;
413 414 415 416

	/* check if writepos was requested */
	if (writepos)
		/* the writepos is the first non-queued position */
417
		*writepos = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
418

419
	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
420 421 422
	return DS_OK;
}

423 424 425 426 427 428 429 430
static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
{
	if (wfex->wFormatTag == WAVE_FORMAT_PCM)
		return sizeof(WAVEFORMATEX);
	else
		return sizeof(WAVEFORMATEX) + wfex->cbSize;
}

431 432
LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
{
433
	DWORD size = DSOUND_GetFormatSize(wfex);
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
	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;
}

454
HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passed_fmt)
455
{
456
	HRESULT err = S_OK;
457
	WAVEFORMATEX *old_fmt;
458
	WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
459
	BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
460

461
	TRACE("(%p,%p)\n", device, passed_fmt);
462

463
	if (device->priolevel == DSSCL_NORMAL) {
464
		WARN("failed priority check!\n");
465 466 467 468
		return DSERR_PRIOLEVELNEEDED;
	}

	/* Let's be pedantic! */
469 470
	if (passed_fmt == NULL) {
		WARN("invalid parameter: passed_fmt==NULL!\n");
471 472
		return DSERR_INVALIDPARAM;
	}
473
	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
474 475 476 477
			  "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
		  passed_fmt->wFormatTag, passed_fmt->nChannels, passed_fmt->nSamplesPerSec,
		  passed_fmt->nAvgBytesPerSec, passed_fmt->nBlockAlign,
		  passed_fmt->wBitsPerSample, passed_fmt->cbSize);
478

479 480 481 482 483 484
	if(passed_fmt->wBitsPerSample < 8 || passed_fmt->wBitsPerSample % 8 != 0 ||
			passed_fmt->nChannels == 0 || passed_fmt->nSamplesPerSec == 0 ||
			passed_fmt->nAvgBytesPerSec == 0 ||
			passed_fmt->nBlockAlign != passed_fmt->nChannels * passed_fmt->wBitsPerSample / 8)
		return DSERR_INVALIDPARAM;

485 486 487 488 489
	if(passed_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
		if(passed_fmtex->Samples.wValidBitsPerSample > passed_fmtex->Format.wBitsPerSample)
			return DSERR_INVALIDPARAM;
	}

490
	/* **** */
491 492
	RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
	EnterCriticalSection(&(device->mixlock));
493

494 495 496 497 498 499 500 501
	if (device->priolevel == DSSCL_WRITEPRIMARY) {
		old_fmt = device->primary_pwfx;
		device->primary_pwfx = DSOUND_CopyFormat(passed_fmt);
		fmtex = (WAVEFORMATEXTENSIBLE *)device->primary_pwfx;
		if (device->primary_pwfx == NULL) {
			err = DSERR_OUTOFMEMORY;
			goto out;
		}
502

503 504
		if (fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
		    fmtex->Samples.wValidBitsPerSample == 0) {
505 506 507
			TRACE("Correcting 0 valid bits per sample\n");
			fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
		}
508

509
		DSOUND_PrimaryClose(device);
510

511 512 513 514 515
		err = DSOUND_ReopenDevice(device, forced);
		if (FAILED(err)) {
			ERR("No formats could be opened\n");
			goto done;
		}
516

517 518 519 520 521
		err = DSOUND_PrimaryOpen(device);
		if (err != DS_OK) {
			ERR("DSOUND_PrimaryOpen failed\n");
			goto done;
		}
522

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
done:
		if (err != DS_OK)
			device->primary_pwfx = old_fmt;
		else
			HeapFree(GetProcessHeap(), 0, old_fmt);
	} else if (passed_fmt->wFormatTag == WAVE_FORMAT_PCM ||
		   passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
		/* Fill in "real" values to primary_pwfx */
		WAVEFORMATEX *fmt = device->primary_pwfx;

		*fmt = *device->pwfx;
		fmtex = (void*)device->pwfx;

		if (IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
		    passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
			fmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
		} else {
			fmt->wFormatTag = WAVE_FORMAT_PCM;
			fmt->wBitsPerSample = 16;
542
		}
543 544 545 546 547 548
		fmt->nBlockAlign = fmt->nChannels * fmt->wBitsPerSample / 8;
		fmt->nAvgBytesPerSec = fmt->nBlockAlign * fmt->nSamplesPerSec;
		fmt->cbSize = 0;
	} else {
		device->primary_pwfx = HeapReAlloc(GetProcessHeap(), 0, device->primary_pwfx, sizeof(*fmtex));
		memcpy(device->primary_pwfx, device->pwfx, sizeof(*fmtex));
549 550
	}

551
out:
552 553
	LeaveCriticalSection(&(device->mixlock));
	RtlReleaseResource(&(device->buffer_list_lock));
554 555 556 557 558
	/* **** */

	return err;
}

559 560 561
/*******************************************************************************
 *		PrimaryBuffer
 */
562 563 564 565 566 567
static inline IDirectSoundBufferImpl *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
{
    /* IDirectSoundBuffer and IDirectSoundBuffer8 use the same iface. */
    return CONTAINING_RECORD(iface, IDirectSoundBufferImpl, IDirectSoundBuffer8_iface);
}

568 569 570
/* This sets this format for the primary buffer only */
static HRESULT WINAPI PrimaryBufferImpl_SetFormat(IDirectSoundBuffer *iface,
        const WAVEFORMATEX *wfex)
571
{
572
    IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
573
    TRACE("(%p,%p)\n", iface, wfex);
574
    return primarybuffer_SetFormat(This->device, wfex);
575 576
}

577 578
static HRESULT WINAPI PrimaryBufferImpl_SetVolume(IDirectSoundBuffer *iface, LONG vol)
{
579 580 581 582 583
	IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
	DirectSoundDevice *device = This->device;
	HRESULT hr;
	float lvol, rvol;

584
	TRACE("(%p,%d)\n", iface, vol);
585

586
	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
587
		WARN("control unavailable\n");
588
		return DSERR_CONTROLUNAVAIL;
589
	}
590

591
	if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
592
		WARN("invalid parameter: vol = %d\n", vol);
593
		return DSERR_INVALIDPARAM;
594
	}
595 596

	/* **** */
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
	EnterCriticalSection(&device->mixlock);

	hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
	if(FAILED(hr)){
		LeaveCriticalSection(&device->mixlock);
		WARN("GetChannelVolume failed: %08x\n", hr);
		return hr;
	}

	if(device->pwfx->nChannels > 1){
		hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("GetChannelVolume failed: %08x\n", hr);
			return hr;
		}
	}else
		rvol = 1;

	device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
	device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
618

619 620
	DSOUND_AmpFactorToVolPan(&device->volpan);
	if (vol != device->volpan.lVolume) {
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
		device->volpan.lVolume=vol;
		DSOUND_RecalcVolPan(&device->volpan);
		lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
		hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("SetChannelVolume failed: %08x\n", hr);
			return hr;
		}

		if(device->pwfx->nChannels > 1){
			rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
			hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
			if(FAILED(hr)){
				LeaveCriticalSection(&device->mixlock);
				WARN("SetChannelVolume failed: %08x\n", hr);
				return hr;
			}
		}
640
	}
641

642
	LeaveCriticalSection(&(device->mixlock));
643 644
	/* **** */

645
	return DS_OK;
646 647
}

648 649
static HRESULT WINAPI PrimaryBufferImpl_GetVolume(IDirectSoundBuffer *iface, LONG *vol)
{
650 651 652 653
	IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
	DirectSoundDevice *device = This->device;
	float lvol, rvol;
	HRESULT hr;
654
	TRACE("(%p,%p)\n", iface, vol);
655

656
	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
657 658 659 660
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

661 662
	if (vol == NULL) {
		WARN("invalid parameter: vol = NULL\n");
663
		return DSERR_INVALIDPARAM;
664
	}
665

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
	EnterCriticalSection(&device->mixlock);

	hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
	if(FAILED(hr)){
		LeaveCriticalSection(&device->mixlock);
		WARN("GetChannelVolume failed: %08x\n", hr);
		return hr;
	}

	if(device->pwfx->nChannels > 1){
		hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("GetChannelVolume failed: %08x\n", hr);
			return hr;
		}
	}else
		rvol = 1;

	device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
	device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));

688 689 690
	DSOUND_AmpFactorToVolPan(&device->volpan);
	*vol = device->volpan.lVolume;

691 692
	LeaveCriticalSection(&device->mixlock);

693 694 695
	return DS_OK;
}

696 697
static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
{
698
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
699
	TRACE("(%p,%d)\n",This,freq);
700 701

	/* You cannot set the frequency of the primary buffer */
702
	WARN("control unavailable\n");
703 704 705
	return DSERR_CONTROLUNAVAIL;
}

706 707 708
static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD reserved1,
        DWORD reserved2, DWORD flags)
{
709 710
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
711
	TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
712

713
	if (!(flags & DSBPLAY_LOOPING)) {
714
		WARN("invalid parameter: flags = %08x\n", flags);
715
		return DSERR_INVALIDPARAM;
716
	}
717 718

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

721 722 723 724
	if (device->state == STATE_STOPPED)
		device->state = STATE_STARTING;
	else if (device->state == STATE_STOPPING)
		device->state = STATE_PLAYING;
725

726
	LeaveCriticalSection(&(device->mixlock));
727 728 729 730 731
	/* **** */

	return DS_OK;
}

732
static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface)
733
{
734 735
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
736
	TRACE("(%p)\n", iface);
737 738

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

741 742 743 744
	if (device->state == STATE_PLAYING)
		device->state = STATE_STOPPING;
	else if (device->state == STATE_STARTING)
		device->state = STATE_STOPPED;
745

746
	LeaveCriticalSection(&(device->mixlock));
747 748 749 750 751
	/* **** */

	return DS_OK;
}

752
static ULONG WINAPI PrimaryBufferImpl_AddRef(IDirectSoundBuffer *iface)
753
{
754
    IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
755
    ULONG ref = InterlockedIncrement(&(This->ref));
756
    TRACE("(%p) ref was %d\n", This, ref - 1);
757 758
    if(ref == 1)
        InterlockedIncrement(&This->numIfaces);
759
    return ref;
760
}
761

762 763 764
/* Decreases *out by 1 to no less than 0.
 * Returns the new value of *out. */
LONG capped_refcount_dec(LONG *out)
765
{
766 767 768 769 770 771 772 773
    LONG ref, oldref;
    do {
        ref = *out;
        if(!ref)
            return 0;
        oldref = InterlockedCompareExchange(out, ref - 1, ref);
    } while(oldref != ref);
    return ref - 1;
774 775
}

776
static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface)
777
{
778
    IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
779 780 781 782 783 784 785
    ULONG ref;

    ref = capped_refcount_dec(&This->ref);
    if(!ref)
        capped_refcount_dec(&This->numIfaces);

    TRACE("(%p) primary ref is now %d\n", This, ref);
786 787

    return ref;
788 789
}

790 791 792
static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface,
        DWORD *playpos, DWORD *writepos)
{
793
	HRESULT	hres;
794 795
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
796
	TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
797

798 799 800
	/* **** */
	EnterCriticalSection(&(device->mixlock));

801
	hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
802 803
	if (hres != DS_OK) {
		WARN("DSOUND_PrimaryGetPosition failed\n");
804
		LeaveCriticalSection(&(device->mixlock));
805 806
		return hres;
	}
807
	if (writepos) {
808
		if (device->state != STATE_STOPPED)
809
			/* apply the documented 10ms lead to writepos */
810 811
			*writepos += device->writelead;
		while (*writepos >= device->buflen) *writepos -= device->buflen;
812
	}
813 814 815 816

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

817
	TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
818 819 820
	return DS_OK;
}

821 822
static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
{
823 824
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
825
	TRACE("(%p,%p)\n", iface, status);
826

827 828
	if (status == NULL) {
		WARN("invalid parameter: status == NULL\n");
829
		return DSERR_INVALIDPARAM;
830
	}
831 832

	*status = 0;
833 834
	if ((device->state == STATE_STARTING) ||
	    (device->state == STATE_PLAYING))
835 836
		*status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;

837
	TRACE("status=%x\n", *status);
838 839 840 841
	return DS_OK;
}


842 843
static HRESULT WINAPI PrimaryBufferImpl_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *lpwf,
        DWORD wfsize, DWORD *wfwritten)
844 845
{
    DWORD size;
846 847
    IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
    DirectSoundDevice *device = This->device;
848
    TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
849

850
    size = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
851 852 853

    if (lpwf) {	/* NULL is valid */
        if (wfsize >= size) {
854
            CopyMemory(lpwf,device->primary_pwfx,size);
855 856 857
            if (wfwritten)
                *wfwritten = size;
        } else {
858
            WARN("invalid parameter: wfsize too small\n");
859 860 861 862 863 864
            if (wfwritten)
                *wfwritten = 0;
            return DSERR_INVALIDPARAM;
        }
    } else {
        if (wfwritten)
865
            *wfwritten = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
866 867 868 869 870
        else {
            WARN("invalid parameter: wfwritten == NULL\n");
            return DSERR_INVALIDPARAM;
        }
    }
871

872
    return DS_OK;
873 874
}

875 876 877 878
static HRESULT WINAPI PrimaryBufferImpl_Lock(IDirectSoundBuffer *iface, DWORD writecursor,
        DWORD writebytes, void **lplpaudioptr1, DWORD *audiobytes1, void **lplpaudioptr2,
        DWORD *audiobytes2, DWORD flags)
{
879
	HRESULT hres;
880 881
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
882
	TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
883
		iface,
884 885 886 887 888 889 890 891 892 893
		writecursor,
		writebytes,
		lplpaudioptr1,
		audiobytes1,
		lplpaudioptr2,
		audiobytes2,
		flags,
		GetTickCount()
	);

894 895 896
        if (!audiobytes1)
            return DSERR_INVALIDPARAM;

897
	if (device->priolevel != DSSCL_WRITEPRIMARY) {
898
		WARN("failed priority check!\n");
899
		return DSERR_PRIOLEVELNEEDED;
900
	}
901

902
        /* when this flag is set, writecursor is meaningless and must be calculated */
903 904
	if (flags & DSBLOCK_FROMWRITECURSOR) {
		/* GetCurrentPosition does too much magic to duplicate here */
905
		hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
906 907 908 909
		if (hres != DS_OK) {
			WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
			return hres;
		}
910
	}
911 912

        /* when this flag is set, writebytes is meaningless and must be set */
913
	if (flags & DSBLOCK_ENTIREBUFFER)
914
		writebytes = device->buflen;
915 916

        if (writecursor >= device->buflen) {
917
                WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
918 919 920
		     writecursor, device->buflen);
                return DSERR_INVALIDPARAM;
        }
921

922
        if (writebytes > device->buflen) {
923
                WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
924 925 926
		     writebytes, device->buflen);
                return DSERR_INVALIDPARAM;
        }
927

928 929 930 931 932 933 934 935
	if (writecursor+writebytes <= device->buflen) {
		*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
		*audiobytes1 = writebytes;
		if (lplpaudioptr2)
			*(LPBYTE*)lplpaudioptr2 = NULL;
		if (audiobytes2)
			*audiobytes2 = 0;
		TRACE("->%d.0\n",writebytes);
936
	} else {
937 938 939 940 941 942 943
		*(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
		*audiobytes1 = device->buflen-writecursor;
		if (lplpaudioptr2)
			*(LPBYTE*)lplpaudioptr2 = device->buffer;
		if (audiobytes2)
			*audiobytes2 = writebytes-(device->buflen-writecursor);
		TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
944 945 946 947
	}
	return DS_OK;
}

948 949
static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD newpos)
{
950
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(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
	return DSERR_INVALIDCALL;
}

958 959
static HRESULT WINAPI PrimaryBufferImpl_SetPan(IDirectSoundBuffer *iface, LONG pan)
{
960 961 962 963
	IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
	DirectSoundDevice *device = This->device;
	float lvol, rvol;
	HRESULT hr;
964
	TRACE("(%p,%d)\n", iface, pan);
965

966
	if (!(This->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 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
	EnterCriticalSection(&device->mixlock);

	hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
	if(FAILED(hr)){
		LeaveCriticalSection(&device->mixlock);
		WARN("GetChannelVolume failed: %08x\n", hr);
		return hr;
	}

	if(device->pwfx->nChannels > 1){
		hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("GetChannelVolume failed: %08x\n", hr);
			return hr;
		}
	}else
		rvol = 1;

	device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
	device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
998

999 1000 1001 1002
	DSOUND_AmpFactorToVolPan(&device->volpan);
	if (pan != device->volpan.lPan) {
		device->volpan.lPan=pan;
		DSOUND_RecalcVolPan(&device->volpan);
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

		lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
		hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("SetChannelVolume failed: %08x\n", hr);
			return hr;
		}

		if(device->pwfx->nChannels > 1){
			rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
			hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
			if(FAILED(hr)){
				LeaveCriticalSection(&device->mixlock);
				WARN("SetChannelVolume failed: %08x\n", hr);
				return hr;
			}
		}
1021
	}
1022

1023
	LeaveCriticalSection(&device->mixlock);
1024 1025
	/* **** */

1026
	return DS_OK;
1027 1028
}

1029 1030
static HRESULT WINAPI PrimaryBufferImpl_GetPan(IDirectSoundBuffer *iface, LONG *pan)
{
1031 1032 1033 1034
	IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
	DirectSoundDevice *device = This->device;
	float lvol, rvol;
	HRESULT hr;
1035
	TRACE("(%p,%p)\n", iface, pan);
1036

1037
	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1038 1039 1040 1041
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

1042 1043
	if (pan == NULL) {
		WARN("invalid parameter: pan == NULL\n");
1044
		return DSERR_INVALIDPARAM;
1045
	}
1046

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
	EnterCriticalSection(&device->mixlock);

	hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
	if(FAILED(hr)){
		LeaveCriticalSection(&device->mixlock);
		WARN("GetChannelVolume failed: %08x\n", hr);
		return hr;
	}

	if(device->pwfx->nChannels > 1){
		hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
		if(FAILED(hr)){
			LeaveCriticalSection(&device->mixlock);
			WARN("GetChannelVolume failed: %08x\n", hr);
			return hr;
		}
	}else
		rvol = 1;

	device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
	device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));

1069
	DSOUND_AmpFactorToVolPan(&device->volpan);
1070
	*pan = device->volpan.lPan;
1071 1072 1073

	LeaveCriticalSection(&device->mixlock);

1074 1075 1076
	return DS_OK;
}

1077 1078 1079
static HRESULT WINAPI PrimaryBufferImpl_Unlock(IDirectSoundBuffer *iface, void *p1, DWORD x1,
        void *p2, DWORD x2)
{
1080 1081
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
1082
	TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1083

1084
	if (device->priolevel != DSSCL_WRITEPRIMARY) {
1085
		WARN("failed priority check!\n");
1086
		return DSERR_PRIOLEVELNEEDED;
1087
	}
1088

1089 1090 1091 1092 1093 1094
    if((p1 && ((BYTE*)p1 < device->buffer ||
                    (BYTE*)p1 >= device->buffer + device->buflen)) ||
            (p2 && ((BYTE*)p2 < device->buffer ||
                    (BYTE*)p2 >= device->buffer + device->buflen)))
        return DSERR_INVALIDPARAM;

1095 1096 1097
	return DS_OK;
}

1098 1099
static HRESULT WINAPI PrimaryBufferImpl_Restore(IDirectSoundBuffer *iface)
{
1100
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1101 1102 1103 1104
	FIXME("(%p):stub\n",This);
	return DS_OK;
}

1105 1106
static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
{
1107 1108
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
1109
	TRACE("(%p,%p)\n", iface, freq);
1110

1111 1112
	if (freq == NULL) {
		WARN("invalid parameter: freq == NULL\n");
1113
		return DSERR_INVALIDPARAM;
1114
	}
1115

1116
	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1117 1118 1119 1120
		WARN("control unavailable\n");
		return DSERR_CONTROLUNAVAIL;
	}

1121
	*freq = device->pwfx->nSamplesPerSec;
1122
	TRACE("-> %d\n", *freq);
1123 1124 1125 1126

	return DS_OK;
}

1127 1128 1129
static HRESULT WINAPI PrimaryBufferImpl_Initialize(IDirectSoundBuffer *iface, IDirectSound *dsound,
        const DSBUFFERDESC *dbsd)
{
1130
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1131
	WARN("(%p) already initialized\n", This);
1132 1133 1134
	return DSERR_ALREADYINITIALIZED;
}

1135 1136
static HRESULT WINAPI PrimaryBufferImpl_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
{
1137 1138
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
        DirectSoundDevice *device = This->device;
1139
  	TRACE("(%p,%p)\n", iface, caps);
1140

1141 1142
	if (caps == NULL) {
		WARN("invalid parameter: caps == NULL\n");
1143
		return DSERR_INVALIDPARAM;
1144 1145 1146
	}

	if (caps->dwSize < sizeof(*caps)) {
1147
		WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1148 1149
		return DSERR_INVALIDPARAM;
	}
1150

1151
	caps->dwFlags = This->dsbd.dwFlags;
1152
	caps->dwBufferBytes = device->buflen;
1153

1154 1155
	/* Windows reports these as zero */
	caps->dwUnlockTransferRate = 0;
1156 1157 1158 1159 1160
	caps->dwPlayCpuOverhead = 0;

	return DS_OK;
}

1161 1162 1163
static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(IDirectSoundBuffer *iface, REFIID riid,
        void **ppobj)
{
1164
        IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1165

1166
	TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1167

1168 1169 1170 1171 1172
	if (ppobj == NULL) {
		WARN("invalid parameter\n");
		return E_INVALIDARG;
	}

1173 1174
	*ppobj = NULL;	/* assume failure */

1175
	if ( IsEqualGUID(riid, &IID_IUnknown) ||
1176
	     IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1177 1178
		IDirectSoundBuffer_AddRef(iface);
		*ppobj = iface;
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
		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;
	}

1189 1190
	if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
		ERR("app requested IDirectSoundNotify on primary buffer\n");
1191
		/* FIXME: should we support this? */
1192
		return E_NOINTERFACE;
1193 1194 1195 1196 1197 1198 1199 1200
	}

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

        if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1201 1202 1203
                *ppobj = &This->IDirectSound3DListener_iface;
                IDirectSound3DListener_AddRef(&This->IDirectSound3DListener_iface);
                return S_OK;
1204 1205 1206
	}

	if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1207 1208 1209
                *ppobj = &This->IKsPropertySet_iface;
                IKsPropertySet_AddRef(&This->IKsPropertySet_iface);
                return S_OK;
1210 1211 1212 1213 1214 1215
	}

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

1216
static const IDirectSoundBufferVtbl dspbvt =
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
{
	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,
1238
	PrimaryBufferImpl_Restore
1239 1240
};

1241 1242
HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
	const DSBUFFERDESC *dsbd)
1243
{
1244
	IDirectSoundBufferImpl *dsb;
Robert Reif's avatar
Robert Reif committed
1245
	TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1246

1247 1248
	if (dsbd->lpwfxFormat) {
		WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
Robert Reif's avatar
Robert Reif committed
1249
		*ppdsb = NULL;
1250
		return DSERR_INVALIDPARAM;
1251
	}
1252

1253
	dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1254 1255 1256

	if (dsb == NULL) {
		WARN("out of memory\n");
Robert Reif's avatar
Robert Reif committed
1257
		*ppdsb = NULL;
1258 1259 1260
		return DSERR_OUTOFMEMORY;
	}

1261
        dsb->ref = 0;
1262
        dsb->ref3D = 0;
1263
        dsb->refiks = 0;
1264
        dsb->numIfaces = 0;
Robert Reif's avatar
Robert Reif committed
1265
	dsb->device = device;
1266
	dsb->IDirectSoundBuffer8_iface.lpVtbl = (IDirectSoundBuffer8Vtbl *)&dspbvt;
1267
        dsb->IDirectSound3DListener_iface.lpVtbl = &ds3dlvt;
1268
        dsb->IKsPropertySet_iface.lpVtbl = &iksbvt;
1269
	dsb->dsbd = *dsbd;
1270

1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
        /* IDirectSound3DListener */
        device->ds3dl.dwSize = sizeof(DS3DLISTENER);
        device->ds3dl.vPosition.x = 0.0;
        device->ds3dl.vPosition.y = 0.0;
        device->ds3dl.vPosition.z = 0.0;
        device->ds3dl.vVelocity.x = 0.0;
        device->ds3dl.vVelocity.y = 0.0;
        device->ds3dl.vVelocity.z = 0.0;
        device->ds3dl.vOrientFront.x = 0.0;
        device->ds3dl.vOrientFront.y = 0.0;
        device->ds3dl.vOrientFront.z = 1.0;
        device->ds3dl.vOrientTop.x = 0.0;
        device->ds3dl.vOrientTop.y = 1.0;
        device->ds3dl.vOrientTop.z = 0.0;
        device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
        device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
        device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
        device->ds3dl_need_recalc = TRUE;

1290
	TRACE("Created primary buffer at %p\n", dsb);
1291 1292
	TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
		"bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
Robert Reif's avatar
Robert Reif committed
1293 1294 1295 1296
		device->pwfx->wFormatTag, device->pwfx->nChannels,
                device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
                device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
                device->pwfx->cbSize);
1297

1298
        IDirectSoundBuffer_AddRef(&dsb->IDirectSoundBuffer8_iface);
Robert Reif's avatar
Robert Reif committed
1299
	*ppdsb = dsb;
1300 1301
	return S_OK;
}