mixer.c 35.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*  			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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <assert.h>
23
#include <stdarg.h>
24 25
#include <math.h>	/* Insomnia - pow() function */

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

WINE_DEFAULT_DEBUG_CHANNEL(dsound);

void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
{
	double temp;
43
	TRACE("(%p)\n",volpan);
44

45
	TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
46
	/* the AmpFactors are expressed in 16.16 fixed point */
47
	volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 0xffff);
48 49 50 51
	/* FIXME: dwPan{Left|Right}AmpFactor */

	/* FIXME: use calculated vol and pan ampfactors */
	temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
52
	volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
53
	temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
54
	volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
55 56 57 58

	TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
{
    double left,right;
    TRACE("(%p)\n",volpan);

    TRACE("left=%lx, right=%lx\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor);
    if (volpan->dwTotalLeftAmpFactor==0)
        left=-10000;
    else
        left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2);
    if (volpan->dwTotalRightAmpFactor==0)
        right=-10000;
    else
        right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2);
    if (left<right)
    {
75
        volpan->lVolume=right;
76 77 78 79
        volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor;
    }
    else
    {
80
        volpan->lVolume=left;
81 82 83 84
        volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor;
    }
    if (volpan->lVolume < -10000)
        volpan->lVolume=-10000;
85
    volpan->lPan=right-left;
86 87 88 89 90 91
    if (volpan->lPan < -10000)
        volpan->lPan=-10000;

    TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
}

92 93 94
void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
{
	DWORD sw;
95
	TRACE("(%p)\n",dsb);
96

97
	sw = dsb->pwfx->nChannels * (dsb->pwfx->wBitsPerSample / 8);
98 99 100 101 102 103 104 105 106
	/* calculate the 10ms write lead */
	dsb->writelead = (dsb->freq / 100) * sw;
}

void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
{
	int			i;
	DWORD			offset;
	LPDSBPOSITIONNOTIFY	event;
107
	TRACE("(%p,%d)\n",dsb,len);
108

109
	if (dsb->nrofnotifies == 0)
110 111 112 113
		return;

	TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
		dsb, dsb->buflen, dsb->playpos, len);
114 115
	for (i = 0; i < dsb->nrofnotifies ; i++) {
		event = dsb->notifies + i;
116
		offset = event->dwOffset;
117
		TRACE("checking %d, position %ld, event = %p\n",
118 119 120 121 122 123 124 125 126
			i, offset, event->hEventNotify);
		/* DSBPN_OFFSETSTOP has to be the last element. So this is */
		/* OK. [Inside DirectX, p274] */
		/*  */
		/* This also means we can't sort the entries by offset, */
		/* because DSBPN_OFFSETSTOP == -1 */
		if (offset == DSBPN_OFFSETSTOP) {
			if (dsb->state == STATE_STOPPED) {
				SetEvent(event->hEventNotify);
127
				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
128 129 130 131 132 133 134
				return;
			} else
				return;
		}
		if ((dsb->playpos + len) >= dsb->buflen) {
			if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
			    (offset >= dsb->playpos)) {
135
				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
136 137 138 139
				SetEvent(event->hEventNotify);
			}
		} else {
			if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
140
				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
141 142 143 144 145 146
				SetEvent(event->hEventNotify);
			}
		}
	}
}

147 148 149 150 151 152 153 154 155 156 157
/* WAV format info can be found at:
 *
 *    http://www.cwi.nl/ftp/audio/AudioFormats.part2
 *    ftp://ftp.cwi.nl/pub/audio/RIFF-format
 *
 * Import points to remember:
 *    8-bit WAV is unsigned
 *    16-bit WAV is signed
 */
 /* Use the same formulas as pcmconverter.c */
static inline INT16 cvtU8toS16(BYTE b)
158
{
159
    return (short)((b+(b << 8))-32768);
160 161
}

162
static inline BYTE cvtS16toU8(INT16 s)
163
{
164
    return (s >> 8) ^ (unsigned char)0x80;
165 166 167 168
}

static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf )
{
169 170
        INT fl,fr;

171 172 173
        if (dsb->pwfx->wBitsPerSample == 8)  {
                if (dsb->dsound->pwfx->wBitsPerSample == 8 &&
                    dsb->dsound->pwfx->nChannels == dsb->pwfx->nChannels) {
174
                        /* avoid needless 8->16->8 conversion */
175
                        *obuf=*ibuf;
176
                        if (dsb->pwfx->nChannels==2)
177
                                *(obuf+1)=*(ibuf+1);
178
                        return;
179
                }
180
                fl = cvtU8toS16(*ibuf);
181
                fr = (dsb->pwfx->nChannels==2 ? cvtU8toS16(*(ibuf + 1)) : fl);
182 183
        } else {
                fl = *((INT16 *)ibuf);
184
                fr = (dsb->pwfx->nChannels==2 ? *(((INT16 *)ibuf) + 1)  : fl);
185
        }
186

187 188
        if (dsb->dsound->pwfx->nChannels == 2) {
                if (dsb->dsound->pwfx->wBitsPerSample == 8) {
189 190 191 192
                        *obuf = cvtS16toU8(fl);
                        *(obuf + 1) = cvtS16toU8(fr);
                        return;
                }
193
                if (dsb->dsound->pwfx->wBitsPerSample == 16) {
194 195 196 197 198
                        *((INT16 *)obuf) = fl;
                        *(((INT16 *)obuf) + 1) = fr;
                        return;
                }
        }
199
        if (dsb->dsound->pwfx->nChannels == 1) {
200
                fl = (fl + fr) >> 1;
201
                if (dsb->dsound->pwfx->wBitsPerSample == 8) {
202 203 204
                        *obuf = cvtS16toU8(fl);
                        return;
                }
205
                if (dsb->dsound->pwfx->wBitsPerSample == 16) {
206 207 208 209 210 211 212 213 214 215 216
                        *((INT16 *)obuf) = fl;
                        return;
                }
        }
}

/* Now with PerfectPitch (tm) technology */
static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
{
	INT	i, size, ipos, ilen;
	BYTE	*ibp, *obp;
217 218
	INT	iAdvance = dsb->pwfx->nBlockAlign;
	INT	oAdvance = dsb->dsound->pwfx->nBlockAlign;
219

220
	ibp = dsb->buffer->memory + dsb->buf_mixpos;
221 222 223 224
	obp = buf;

	TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
	/* Check for the best case */
225 226 227
	if ((dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) &&
	    (dsb->pwfx->wBitsPerSample == dsb->dsound->pwfx->wBitsPerSample) &&
	    (dsb->pwfx->nChannels == dsb->dsound->pwfx->nChannels)) {
228 229 230
	        DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
		TRACE("(%p) Best case\n", dsb);
	    	if (len <= bytesleft )
231
			CopyMemory(obp, ibp, len);
232
		else { /* wrap */
233 234
			CopyMemory(obp, ibp, bytesleft);
			CopyMemory(obp + bytesleft, dsb->buffer->memory, len - bytesleft);
235 236 237 238 239
		}
		return len;
	}

	/* Check for same sample rate */
240
	if (dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) {
241
		TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
242
			dsb->freq, dsb->dsound->pwfx->nSamplesPerSec);
243 244 245 246 247 248
		ilen = 0;
		for (i = 0; i < len; i += oAdvance) {
			cp_fields(dsb, ibp, obp );
			ibp += iAdvance;
			ilen += iAdvance;
			obp += oAdvance;
249 250
			if (ibp >= (BYTE *)(dsb->buffer->memory + dsb->buflen))
				ibp = dsb->buffer->memory;	/* wrap */
251 252 253 254 255 256 257 258 259 260 261 262 263
		}
		return (ilen);
	}

	/* Mix in different sample rates */
	/* */
	/* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
	/* Patent Pending :-] */

	/* Patent enhancements (c) 2000 Ove Kven,
	 * TransGaming Technologies Inc. */

	/* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
264
	   dsb, dsb->freq, dsb->dsound->pwfx->nSamplesPerSec); */
265 266 267 268 269

	size = len / oAdvance;
	ilen = 0;
	ipos = dsb->buf_mixpos;
	for (i = 0; i < size; i++) {
270
                cp_fields(dsb, (dsb->buffer->memory + ipos), obp);
271 272 273 274 275 276
		obp += oAdvance;
		dsb->freqAcc += dsb->freqAdjust;
		if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
			ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
			dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
			ipos += adv; ilen += adv;
277
			ipos %= dsb->buflen;
278 279 280 281 282 283 284
		}
	}
	return ilen;
}

static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
{
285
	INT	i;
286 287 288
	BYTE	*bpc = buf;
	INT16	*bps = (INT16 *) buf;

289 290 291 292
	TRACE("(%p,%p,%d)\n",dsb,buf,len);
	TRACE("left = %lx, right = %lx\n", dsb->cvolpan.dwTotalLeftAmpFactor, 
		dsb->cvolpan.dwTotalRightAmpFactor);

293 294 295 296 297 298 299 300 301
	if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->cvolpan.lPan == 0)) &&
	    (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->cvolpan.lVolume == 0)) &&
	    !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
		return;		/* Nothing to do */

	/* If we end up with some bozo coder using panning or 3D sound */
	/* with a mono primary buffer, it could sound very weird using */
	/* this method. Oh well, tough patooties. */

302
	switch (dsb->dsound->pwfx->wBitsPerSample) {
303 304 305
	case 8:
		/* 8-bit WAV is unsigned, but we need to operate */
		/* on signed data for this to work properly */
306
		switch (dsb->dsound->pwfx->nChannels) {
307
		case 1:
308 309 310 311 312 313
			for (i = 0; i < len; i++) {
				INT val = *bpc - 128;
				val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
				*bpc = val + 128;
				bpc++;
			}
314 315
			break;
		case 2:
316 317 318 319 320 321 322 323 324
			for (i = 0; i < len; i+=2) {
				INT val = *bpc - 128;
				val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
				*bpc++ = val + 128;
				val = *bpc - 128;
				val = (val * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
				*bpc = val + 128;
				bpc++;
			}
325 326
			break;
		default:
327
			FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
328
			break;
329
		}
330 331 332
		break;
	case 16:
		/* 16-bit WAV is signed -- much better */
333
		switch (dsb->dsound->pwfx->nChannels) {
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
		case 1:
			for (i = 0; i < len; i += 2) {
				*bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
				bps++;
			}
			break;
		case 2:
			for (i = 0; i < len; i += 4) {
				*bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
				bps++;
				*bps = (*bps * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
				bps++;
			}
			break;
		default:
349
			FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
350 351 352 353
			break;
		}
		break;
	default:
354
		FIXME("doesn't support %d bit samples\n", dsb->dsound->pwfx->wBitsPerSample);
355
		break;
356 357 358
	}
}

359
static LPBYTE DSOUND_tmpbuffer(IDirectSoundImpl *dsound, DWORD len)
360
{
361 362 363 364 365 366 367 368 369
    TRACE("(%p,%ld)\n",dsound,len);

    if (len > dsound->tmp_buffer_len) {
        if (dsound->tmp_buffer)
            dsound->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsound->tmp_buffer, len);
        else
            dsound->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len);

        dsound->tmp_buffer_len = len;
370
    }
371 372

    return dsound->tmp_buffer;
373 374 375 376
}

static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
{
377 378
	INT	i, len, ilen, temp, field, nBlockAlign, todo;
	BYTE	*buf, *ibuf;
379

380
	TRACE("(%p,%ld,%ld)\n",dsb,writepos,fraglen);
381

382 383
	len = fraglen;
	if (!(dsb->playflags & DSBPLAY_LOOPING)) {
384
		temp = MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buflen,
385
			dsb->nAvgBytesPerSec) -
386
		       MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buf_mixpos,
387 388 389
			dsb->nAvgBytesPerSec);
		len = (len > temp) ? temp : len;
	}
390
	nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
391
	len = len / nBlockAlign * nBlockAlign;	/* data alignment */
392 393

	if (len == 0) {
394
		/* This should only happen if we aren't looping and temp < nBlockAlign */
395 396 397
		return 0;
	}

398
	if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
399 400 401 402 403 404
		return 0;

	TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);

	ilen = DSOUND_MixerNorm(dsb, ibuf, len);
	if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
405 406
	    (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
	    (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
407 408
		DSOUND_MixerVol(dsb, ibuf, len);

409
	if (dsb->dsound->pwfx->wBitsPerSample == 8) {
410 411 412 413 414 415 416 417
		BYTE	*obuf = dsb->dsound->buffer + writepos;

		if ((writepos + len) <= dsb->dsound->buflen)
			todo = len;
		else
			todo = dsb->dsound->buflen - writepos;

		for (i = 0; i < todo; i++) {
418
			/* 8-bit WAV is unsigned */
419
			field = (*ibuf++ - 128);
420
			field += (*obuf - 128);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
			if (field > 127) field = 127;
			else if (field < -128) field = -128;
			*obuf++ = field + 128;
		}
 
		if (todo < len) {
			todo = len - todo;
			obuf = dsb->dsound->buffer;

			for (i = 0; i < todo; i++) {
				/* 8-bit WAV is unsigned */
				field = (*ibuf++ - 128);
				field += (*obuf - 128);
				if (field > 127) field = 127;
				else if (field < -128) field = -128;
				*obuf++ = field + 128;
			}
		}
        } else {
		INT16	*ibufs, *obufs;

		ibufs = (INT16 *) ibuf;
		obufs = (INT16 *)(dsb->dsound->buffer + writepos);

		if ((writepos + len) <= dsb->dsound->buflen)
			todo = len / 2;
		else
			todo = (dsb->dsound->buflen - writepos) / 2;

		for (i = 0; i < todo; i++) {
451
			/* 16-bit WAV is signed */
452
			field = *ibufs++;
453
			field += *obufs;
454 455 456
			if (field > 32767) field = 32767;
			else if (field < -32768) field = -32768;
			*obufs++ = field;
457
		}
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

		if (todo < (len / 2)) {
			todo = (len / 2) - todo;
			obufs = (INT16 *)dsb->dsound->buffer;

			for (i = 0; i < todo; i++) {
				/* 16-bit WAV is signed */
				field = *ibufs++;
				field += *obufs;
				if (field > 32767) field = 32767;
				else if (field < -32768) field = -32768;
				*obufs++ = field;
			}
		}
        }
473 474 475 476 477 478 479 480 481 482 483 484

	if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
		/* HACK... leadin should be reset when the PLAY position reaches the startpos,
		 * not the MIX position... but if the sound buffer is bigger than our prebuffering
		 * (which must be the case for the streaming buffers that need this hack anyway)
		 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
		dsb->leadin = FALSE;
	}

	dsb->buf_mixpos += ilen;

	if (dsb->buf_mixpos >= dsb->buflen) {
485
		if (dsb->playflags & DSBPLAY_LOOPING) {
486
			/* wrap */
487
			dsb->buf_mixpos %= dsb->buflen;
488 489 490 491 492 493 494 495 496 497
			if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
				dsb->leadin = FALSE; /* HACK: see above */
		}
	}

	return len;
}

static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
{
498 499
	INT     ilen, field, nBlockAlign;
	UINT    i, todo;
500 501
	BYTE	*buf, *ibuf;

502
	TRACE("(%p,%ld,%ld)\n",dsb,writepos,len);
503

504
	nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
505
	len = len / nBlockAlign * nBlockAlign;  /* data alignment */
506

507
	if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
508 509 510 511 512 513
		return;

	TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);

	ilen = DSOUND_MixerNorm(dsb, ibuf, len);
	if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
514 515
	    (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
	    (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
516 517 518
		DSOUND_MixerVol(dsb, ibuf, len);

	/* subtract instead of add, to phase out premixed data */
519
	if (dsb->dsound->pwfx->wBitsPerSample == 8) {
520 521 522 523 524 525 526 527
		BYTE	*obuf = dsb->dsound->buffer + writepos;

		if ((writepos + len) <= dsb->dsound->buflen)
			todo = len;
		else
			todo = dsb->dsound->buflen - writepos;

		for (i = 0; i < todo; i++) {
528
			/* 8-bit WAV is unsigned */
529
			field = (*ibuf++ - 128);
530
			field -= (*obuf - 128);
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
			if (field > 127) field = 127;
			else if (field < -128) field = -128;
			*obuf++ = field + 128;
		}
 
		if (todo < len) {
			todo = len - todo;
			obuf = dsb->dsound->buffer;

			for (i = 0; i < todo; i++) {
				/* 8-bit WAV is unsigned */
				field = (*ibuf++ - 128);
				field -= (*obuf - 128);
				if (field > 127) field = 127;
				else if (field < -128) field = -128;
				*obuf++ = field + 128;
			}
		}
        } else {
		INT16	*ibufs, *obufs;

		ibufs = (INT16 *) ibuf;
		obufs = (INT16 *)(dsb->dsound->buffer + writepos);

		if ((writepos + len) <= dsb->dsound->buflen)
			todo = len / 2;
		else
			todo = (dsb->dsound->buflen - writepos) / 2;

		for (i = 0; i < todo; i++) {
561
			/* 16-bit WAV is signed */
562
			field = *ibufs++;
563
			field -= *obufs;
564 565 566
			if (field > 32767) field = 32767;
			else if (field < -32768) field = -32768;
			*obufs++ = field;
567
		}
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

		if (todo < (len / 2)) {
			todo = (len / 2) - todo;
			obufs = (INT16 *)dsb->dsound->buffer;

			for (i = 0; i < todo; i++) {
				/* 16-bit WAV is signed */
				field = *ibufs++;
				field -= *obufs;
				if (field > 32767) field = 32767;
				else if (field < -32768) field = -32768;
				*obufs++ = field;
			}
		}
        }
583 584 585 586 587
}

static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
{
	DWORD   size, flen, len, npos, nlen;
588 589
	INT	iAdvance = dsb->pwfx->nBlockAlign;
	INT	oAdvance = dsb->dsound->pwfx->nBlockAlign;
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
	/* determine amount of premixed data to cancel */
	DWORD primary_done =
		((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
		dsb->primary_mixpos - writepos;

	TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);

	/* backtrack the mix position */
	size = primary_done / oAdvance;
	flen = size * dsb->freqAdjust;
	len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
	flen &= (1<<DSOUND_FREQSHIFT)-1;
	while (dsb->freqAcc < flen) {
		len += iAdvance;
		dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
	}
	len %= dsb->buflen;
	npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
		dsb->buf_mixpos - len;
	if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
		/* stop backtracking at startpos */
		npos = dsb->startpos;
		len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
			dsb->buf_mixpos - npos;
		flen = dsb->freqAcc;
615
		nlen = len / dsb->pwfx->nBlockAlign;
616
		nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
617
		nlen *= dsb->dsound->pwfx->nBlockAlign;
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
		writepos =
			((dsb->primary_mixpos < nlen) ? dsb->dsound->buflen : 0) +
			dsb->primary_mixpos - nlen;
	}

	dsb->freqAcc -= flen;
	dsb->buf_mixpos = npos;
	dsb->primary_mixpos = writepos;

	TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
	      dsb->buf_mixpos, dsb->primary_mixpos, len);

	if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
}

void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
{
#if 0
	DWORD   i, size, flen, len, npos, nlen;
637 638
	INT	iAdvance = dsb->pwfx->nBlockAlign;
	INT	oAdvance = dsb->dsound->pwfx->nBlockAlign;
639 640 641 642 643 644 645 646 647 648 649 650 651 652
	/* determine amount of premixed data to cancel */
	DWORD buf_done =
		((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
		dsb->buf_mixpos - buf_writepos;
#endif

	WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
	/* since this is not implemented yet, just cancel *ALL* prebuffering for now
	 * (which is faster anyway when there's only a single secondary buffer) */
	dsb->dsound->need_remix = TRUE;
}

void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb)
{
653
	TRACE("(%p)\n",dsb);
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
	EnterCriticalSection(&dsb->lock);
	if (dsb->state == STATE_PLAYING) {
#if 0 /* this may not be quite reliable yet */
		dsb->need_remix = TRUE;
#else
		dsb->dsound->need_remix = TRUE;
#endif
	}
	LeaveCriticalSection(&dsb->lock);
}

static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
{
	DWORD len, slen;
	/* determine this buffer's write position */
	DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & dsb->dsound->state, writepos,
						     writepos, dsb->primary_mixpos, dsb->buf_mixpos);
	/* determine how much already-mixed data exists */
	DWORD buf_done =
		((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
		dsb->buf_mixpos - buf_writepos;
	DWORD primary_done =
		((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
		dsb->primary_mixpos - writepos;
	DWORD adv_done =
		((dsb->dsound->mixpos < writepos) ? dsb->dsound->buflen : 0) +
		dsb->dsound->mixpos - writepos;
681 682 683 684
	DWORD played =
		((buf_writepos < dsb->playpos) ? dsb->buflen : 0) +
		buf_writepos - dsb->playpos;
	DWORD buf_left = dsb->buflen - buf_writepos;
685 686
	int still_behind;

687
	TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen);
688 689 690 691 692 693
	TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
	TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
	TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
	      mixlen);
	TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);

694 695 696 697 698 699
	/* check for notification positions */
	if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
	    dsb->state != STATE_STARTING) {
		DSOUND_CheckEvent(dsb, played);
	}

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
	/* save write position for non-GETCURRENTPOSITION2... */
	dsb->playpos = buf_writepos;

	/* check whether CalcPlayPosition detected a mixing underrun */
	if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
		/* it did, but did we have more to play? */
		if ((dsb->playflags & DSBPLAY_LOOPING) ||
		    (dsb->buf_mixpos < dsb->buflen)) {
			/* yes, have to recover */
			ERR("underrun on sound buffer %p\n", dsb);
			TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
		}
		dsb->primary_mixpos = writepos;
		primary_done = 0;
	}
	/* determine how far ahead we should mix */
	if (((dsb->playflags & DSBPLAY_LOOPING) ||
	     (dsb->leadin && (dsb->probably_valid_to != 0))) &&
	    !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
		/* if this is a streaming buffer, it typically means that
		 * we should defer mixing past probably_valid_to as long
		 * as we can, to avoid unnecessary remixing */
		/* the heavy-looking calculations shouldn't be that bad,
		 * as any game isn't likely to be have more than 1 or 2
		 * streaming buffers in use at any time anyway... */
		DWORD probably_valid_left =
			(dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
			((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
			dsb->probably_valid_to - buf_writepos;
		/* check for leadin condition */
		if ((probably_valid_left == 0) &&
		    (dsb->probably_valid_to == dsb->startpos) &&
		    dsb->leadin)
			probably_valid_left = dsb->buflen;
		TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
		      dsb->probably_valid_to, probably_valid_left);
		/* check whether the app's time is already up */
		if (probably_valid_left < dsb->writelead) {
			WARN("probably_valid_to now within writelead, possible streaming underrun\n");
			/* once we pass the point of no return,
			 * no reason to hold back anymore */
			dsb->probably_valid_to = (DWORD)-1;
			/* we just have to go ahead and mix what we have,
			 * there's no telling what the app is thinking anyway */
		} else {
			/* adjust for our frequency and our sample size */
			probably_valid_left = MulDiv(probably_valid_left,
						     1 << DSOUND_FREQSHIFT,
748 749
						     dsb->pwfx->nBlockAlign * dsb->freqAdjust) *
				              dsb->dsound->pwfx->nBlockAlign;
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
			/* check whether to clip mix_len */
			if (probably_valid_left < mixlen) {
				TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
				mixlen = probably_valid_left;
			}
		}
	}
	/* cut mixlen with what's already been mixed */
	if (mixlen < primary_done) {
		/* huh? and still CalcPlayPosition didn't
		 * detect an underrun? */
		FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
		return 0;
	}
	len = mixlen - primary_done;
	TRACE("remaining mixlen=%ld\n", len);

	if (len < dsb->dsound->fraglen) {
		/* smaller than a fragment, wait until it gets larger
		 * before we take the mixing overhead */
		TRACE("mixlen not worth it, deferring mixing\n");
771 772
		still_behind = 1;
		goto post_mix;
773 774 775 776 777 778 779 780 781 782 783 784 785 786
	}

	/* ok, we know how much to mix, let's go */
	still_behind = (adv_done > primary_done);
	while (len) {
		slen = dsb->dsound->buflen - dsb->primary_mixpos;
		if (slen > len) slen = len;
		slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);

		if ((dsb->primary_mixpos < dsb->dsound->mixpos) &&
		    (dsb->primary_mixpos + slen >= dsb->dsound->mixpos))
			still_behind = FALSE;

		dsb->primary_mixpos += slen; len -= slen;
787
		dsb->primary_mixpos %= dsb->dsound->buflen;
788 789 790 791 792

		if ((dsb->state == STATE_STOPPED) || !slen) break;
	}
	TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, dsb->dsound->mixpos);
	TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
793 794 795 796 797 798 799 800 801 802 803 804 805

post_mix:
	/* check if buffer should be considered complete */
	if (buf_left < dsb->writelead &&
	    !(dsb->playflags & DSBPLAY_LOOPING)) {
		dsb->state = STATE_STOPPED;
		dsb->playpos = 0;
		dsb->last_playpos = 0;
		dsb->buf_mixpos = 0;
		dsb->leadin = FALSE;
		DSOUND_CheckEvent(dsb, buf_left);
	}

806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
	/* return how far we think the primary buffer can
	 * advance its underrun detector...*/
	if (still_behind) return 0;
	if ((mixlen - len) < primary_done) return 0;
	slen = ((dsb->primary_mixpos < dsb->dsound->mixpos) ?
		dsb->dsound->buflen : 0) + dsb->primary_mixpos -
		dsb->dsound->mixpos;
	if (slen > mixlen) {
		/* the primary_done and still_behind checks above should have worked */
		FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
		slen = 0;
	}
	return slen;
}

821
static DWORD DSOUND_MixToPrimary(IDirectSoundImpl *dsound, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
822 823 824 825
{
	INT			i, len, maxlen = 0;
	IDirectSoundBufferImpl	*dsb;

826
	TRACE("(%ld,%ld,%ld,%d)\n", playpos, writepos, mixlen, recover);
827
	for (i = 0; i < dsound->nrofbuffers; i++) {
828 829 830 831 832 833 834 835
		dsb = dsound->buffers[i];

		if (dsb->buflen && dsb->state && !dsb->hwbuf) {
			TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
			EnterCriticalSection(&(dsb->lock));
			if (dsb->state == STATE_STOPPING) {
				DSOUND_MixCancel(dsb, writepos, TRUE);
				dsb->state = STATE_STOPPED;
836
				DSOUND_CheckEvent(dsb, 0);
837 838 839
			} else {
				if ((dsb->state == STATE_STARTING) || recover) {
					dsb->primary_mixpos = writepos;
840
					dsb->cvolpan = dsb->volpan;
841 842 843 844
					dsb->need_remix = FALSE;
				}
				else if (dsb->need_remix) {
					DSOUND_MixCancel(dsb, writepos, TRUE);
845
					dsb->cvolpan = dsb->volpan;
846 847 848 849 850 851 852 853 854 855 856 857 858 859
					dsb->need_remix = FALSE;
				}
				len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
				if (dsb->state == STATE_STARTING)
					dsb->state = STATE_PLAYING;
				maxlen = (len > maxlen) ? len : maxlen;
			}
			LeaveCriticalSection(&(dsb->lock));
		}
	}

	return maxlen;
}

860
static void DSOUND_MixReset(IDirectSoundImpl *dsound, DWORD writepos)
861 862 863 864 865 866 867 868
{
	INT			i;
	IDirectSoundBufferImpl	*dsb;
	int nfiller;

	TRACE("(%ld)\n", writepos);

	/* the sound of silence */
869
	nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
870 871

	/* reset all buffer mix positions */
872
	for (i = 0; i < dsound->nrofbuffers; i++) {
873 874 875 876 877 878 879 880 881 882 883 884
		dsb = dsound->buffers[i];

		if (dsb->buflen && dsb->state && !dsb->hwbuf) {
			TRACE("Resetting %p\n", dsb);
			EnterCriticalSection(&(dsb->lock));
			if (dsb->state == STATE_STOPPING) {
				dsb->state = STATE_STOPPED;
			}
			else if (dsb->state == STATE_STARTING) {
				/* nothing */
			} else {
				DSOUND_MixCancel(dsb, writepos, FALSE);
885
				dsb->cvolpan = dsb->volpan;
886 887 888 889 890 891 892 893
				dsb->need_remix = FALSE;
			}
			LeaveCriticalSection(&(dsb->lock));
		}
	}

	/* wipe out premixed data */
	if (dsound->mixpos < writepos) {
894 895
		FillMemory(dsound->buffer + writepos, dsound->buflen - writepos, nfiller);
		FillMemory(dsound->buffer, dsound->mixpos, nfiller);
896
	} else {
897
		FillMemory(dsound->buffer + writepos, dsound->mixpos - writepos, nfiller);
898 899 900 901 902 903 904 905
	}

	/* reset primary mix position */
	dsound->mixpos = writepos;
}

static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
{
906
	TRACE("(%p,%ld)\n",dsound,writepos);
907
	if (dsound->need_remix) {
908
		DSOUND_MixReset(dsound, writepos);
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
		dsound->need_remix = FALSE;
		/* maximize Half-Life performance */
		dsound->prebuf = ds_snd_queue_min;
		dsound->precount = 0;
	} else {
		dsound->precount++;
		if (dsound->precount >= 4) {
			if (dsound->prebuf < ds_snd_queue_max)
				dsound->prebuf++;
			dsound->precount = 0;
		}
	}
	TRACE("premix adjust: %d\n", dsound->prebuf);
}

void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
{
926
	TRACE("(%p,%ld)\n",dsound,mixq);
927 928 929 930 931 932 933 934 935 936 937 938
	if (mixq + dsound->pwqueue > ds_hel_queue) mixq = ds_hel_queue - dsound->pwqueue;
	TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
	for (; mixq; mixq--) {
		waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
		dsound->pwwrite++;
		if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
		dsound->pwqueue++;
	}
}

/* #define SYNC_CALLBACK */

939
void DSOUND_PerformMix(IDirectSoundImpl *dsound)
940 941 942 943 944
{
	int nfiller;
	BOOL forced;
	HRESULT hres;

945
	TRACE("(%p)\n", dsound);
946

947
	/* the sound of silence */
948
	nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
949 950 951 952 953 954 955 956 957 958 959

	/* whether the primary is forced to play even without secondary buffers */
	forced = ((dsound->state == STATE_PLAYING) || (dsound->state == STATE_STARTING));

	if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
		BOOL paused = ((dsound->state == STATE_STOPPED) || (dsound->state == STATE_STARTING));
		/* FIXME: document variables */
 		DWORD playpos, writepos, inq, maxq, frag;
 		if (dsound->hwbuf) {
			hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, &writepos);
			if (hres) {
960
			    WARN("IDsDriverBuffer_GetPosition failed\n");
961 962 963 964 965 966 967
			    return;
			}
			/* Well, we *could* do Just-In-Time mixing using the writepos,
			 * but that's a little bit ambitious and unnecessary... */
			/* rather add our safety margin to the writepos, if we're playing */
			if (!paused) {
				writepos += dsound->writelead;
968
				writepos %= dsound->buflen;
969
			} else writepos = playpos;
970
		} else {
971 972 973 974
 			playpos = dsound->pwplay * dsound->fraglen;
 			writepos = playpos;
 			if (!paused) {
	 			writepos += ds_hel_margin * dsound->fraglen;
975
 				writepos %= dsound->buflen;
976 977
	 		}
		}
978 979 980
		TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",
		      playpos,writepos,dsound->playpos,dsound->mixpos,dsound->buflen);
		assert(dsound->playpos < dsound->buflen);
981 982
		/* wipe out just-played sound data */
		if (playpos < dsound->playpos) {
983 984
			FillMemory(dsound->buffer + dsound->playpos, dsound->buflen - dsound->playpos, nfiller);
			FillMemory(dsound->buffer, playpos, nfiller);
985
		} else {
986
			FillMemory(dsound->buffer + dsound->playpos, playpos - dsound->playpos, nfiller);
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
		}
		dsound->playpos = playpos;

		EnterCriticalSection(&(dsound->mixlock));

		/* reset mixing if necessary */
		DSOUND_CheckReset(dsound, writepos);

		/* check how much prebuffering is left */
		inq = dsound->mixpos;
		if (inq < writepos)
			inq += dsound->buflen;
		inq -= writepos;

		/* find the maximum we can prebuffer */
		if (!paused) {
			maxq = playpos;
			if (maxq < writepos)
				maxq += dsound->buflen;
			maxq -= writepos;
		} else maxq = dsound->buflen;

		/* clip maxq to dsound->prebuf */
		frag = dsound->prebuf * dsound->fraglen;
		if (maxq > frag) maxq = frag;

		/* check for consistency */
		if (inq > maxq) {
			/* the playback position must have passed our last
			 * mixed position, i.e. it's an underrun, or we have
			 * nothing more to play */
			TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
			inq = 0;
			/* stop the playback now, to allow buffers to refill */
			if (dsound->state == STATE_PLAYING) {
				dsound->state = STATE_STARTING;
			}
			else if (dsound->state == STATE_STOPPING) {
				dsound->state = STATE_STOPPED;
			}
			else {
				/* how can we have an underrun if we aren't playing? */
				WARN("unexpected primary state (%ld)\n", dsound->state);
			}
#ifdef SYNC_CALLBACK
			/* DSOUND_callback may need this lock */
			LeaveCriticalSection(&(dsound->mixlock));
#endif
1035 1036
			if (DSOUND_PrimaryStop(dsound) != DS_OK)
				WARN("DSOUND_PrimaryStop failed\n");
1037 1038 1039 1040 1041 1042 1043 1044 1045
#ifdef SYNC_CALLBACK
			EnterCriticalSection(&(dsound->mixlock));
#endif
			if (dsound->hwbuf) {
				/* the Stop is supposed to reset play position to beginning of buffer */
				/* unfortunately, OSS is not able to do so, so get current pointer */
				hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, NULL);
				if (hres) {
					LeaveCriticalSection(&(dsound->mixlock));
1046
					WARN("IDsDriverBuffer_GetPosition failed\n");
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
					return;
				}
			} else {
	 			playpos = dsound->pwplay * dsound->fraglen;
			}
			writepos = playpos;
			dsound->playpos = playpos;
			dsound->mixpos = writepos;
			inq = 0;
			maxq = dsound->buflen;
			if (maxq > frag) maxq = frag;
1058
			FillMemory(dsound->buffer, dsound->buflen, nfiller);
1059 1060 1061 1062
			paused = TRUE;
		}

		/* do the mixing */
1063
		frag = DSOUND_MixToPrimary(dsound, playpos, writepos, maxq, paused);
1064 1065
		if (forced) frag = maxq - inq;
		dsound->mixpos += frag;
1066
		dsound->mixpos %= dsound->buflen;
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079

		if (frag) {
			/* buffers have been filled, restart playback */
			if (dsound->state == STATE_STARTING) {
				dsound->state = STATE_PLAYING;
			}
			else if (dsound->state == STATE_STOPPED) {
				/* the dsound is supposed to play if there's something to play
				 * even if it is reported as stopped, so don't let this confuse you */
				dsound->state = STATE_STOPPING;
			}
			LeaveCriticalSection(&(dsound->mixlock));
			if (paused) {
1080 1081 1082 1083
				if (DSOUND_PrimaryPlay(dsound) != DS_OK)
					WARN("DSOUND_PrimaryPlay failed\n");
				else
					TRACE("starting playback\n");
1084 1085 1086 1087 1088 1089 1090
			}
		}
		else
			LeaveCriticalSection(&(dsound->mixlock));
	} else {
		/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
		if (dsound->state == STATE_STARTING) {
1091 1092 1093 1094
			if (DSOUND_PrimaryPlay(dsound) != DS_OK)
				WARN("DSOUND_PrimaryPlay failed\n");
			else
				dsound->state = STATE_PLAYING;
1095 1096
		}
		else if (dsound->state == STATE_STOPPING) {
1097 1098 1099 1100
			if (DSOUND_PrimaryStop(dsound) != DS_OK)
				WARN("DSOUND_PrimaryStop failed\n");
			else
				dsound->state = STATE_STOPPED;
1101 1102 1103 1104 1105 1106
		}
	}
}

void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
1107
        IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1108 1109
	DWORD start_time =  GetTickCount();
        DWORD end_time;
1110
	TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
1111
        TRACE("entering at %ld\n", start_time);
1112

1113
	if (dsound != This) {
1114 1115 1116 1117 1118 1119
		ERR("dsound died without killing us?\n");
		timeKillEvent(timerID);
		timeEndPeriod(DS_TIME_RES);
		return;
	}

1120
	RtlAcquireResourceShared(&(This->buffer_list_lock), TRUE);
1121

1122
	if (This->ref)
1123
		DSOUND_PerformMix(This);
1124

1125
	RtlReleaseResource(&(This->buffer_list_lock));
1126

1127 1128
	end_time = GetTickCount();
	TRACE("completed processing at %ld, duration = %ld\n", end_time, end_time - start_time);
1129 1130 1131 1132 1133
}

void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
        IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1134
	TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2);
1135 1136 1137
	TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg, 
		msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" : 
		msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
	if (msg == MM_WOM_DONE) {
		DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
		if (This->pwqueue == (DWORD)-1) {
			TRACE("completed due to reset\n");
			return;
		}
/* it could be a bad idea to enter critical section here... if there's lock contention,
 * the resulting scheduling delays might obstruct the winmm player thread */
#ifdef SYNC_CALLBACK
		EnterCriticalSection(&(This->mixlock));
#endif
		/* retrieve current values */
1150 1151 1152
		fraglen = This->fraglen;
		buflen = This->buflen;
		pwplay = This->pwplay;
1153
		playpos = pwplay * fraglen;
1154
		mixpos = This->mixpos;
1155 1156 1157 1158 1159 1160 1161 1162 1163
		/* check remaining mixed data */
		inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
		mixq = inq / fraglen;
		if ((inq - (mixq * fraglen)) > 0) mixq++;
		/* complete the playing buffer */
		TRACE("done playing primary pos=%ld\n", playpos);
		pwplay++;
		if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
		/* write new values */
1164 1165
		This->pwplay = pwplay;
		This->pwqueue--;
1166 1167 1168
		/* queue new buffer if we have data for it */
		if (inq>1) DSOUND_WaveQueue(This, inq-1);
#ifdef SYNC_CALLBACK
1169
		LeaveCriticalSection(&(This->mixlock));
1170 1171 1172 1173
#endif
	}
	TRACE("completed\n");
}