Release 20030408.
[wine/gsoc-2012-control.git] / dlls / dsound / buffer.c
blobfd2a5ab049aad39a486f8ceaa5a40cadb64d6261
1 /* DirectSound
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include <assert.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <sys/fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h> /* Insomnia - pow() function */
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winerror.h"
39 #include "mmsystem.h"
40 #include "winternl.h"
41 #include "mmddk.h"
42 #include "wine/windef16.h"
43 #include "wine/debug.h"
44 #include "dsound.h"
45 #include "dsdriver.h"
46 #include "dsound_private.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
50 /*******************************************************************************
51 * IDirectSoundNotify
53 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
54 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
55 ) {
56 ICOM_THIS(IDirectSoundNotifyImpl,iface);
58 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
59 if (This->dsb)
60 return IDirectSoundBuffer8_QueryInterface((LPDIRECTSOUNDBUFFER8)This->dsb, riid, ppobj);
61 else if (This->dscb)
62 return IDirectSoundCaptureBuffer8_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER8)This->dscb, riid, ppobj);
63 return DSERR_GENERIC;
66 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
67 ICOM_THIS(IDirectSoundNotifyImpl,iface);
68 DWORD ref;
70 TRACE("(%p) ref was %ld\n", This, This->ref);
72 ref = InterlockedIncrement(&(This->ref));
73 return ref;
76 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
77 ICOM_THIS(IDirectSoundNotifyImpl,iface);
78 DWORD ref;
80 TRACE("(%p) ref was %ld\n", This, This->ref);
82 ref = InterlockedDecrement(&(This->ref));
83 if (!ref) {
84 if (This->dsb)
85 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->dsb);
86 else if (This->dscb)
87 IDirectSoundCaptureBuffer8_Release((LPDIRECTSOUNDCAPTUREBUFFER8)This->dscb);
88 HeapFree(GetProcessHeap(),0,This);
89 return 0;
91 return ref;
94 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
95 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
96 ) {
97 ICOM_THIS(IDirectSoundNotifyImpl,iface);
98 int i;
100 if (TRACE_ON(dsound)) {
101 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
102 for (i=0;i<howmuch;i++)
103 TRACE("notify at %ld to 0x%08lx\n",
104 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
106 if (This->dsb) {
107 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
108 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
109 notify,
110 howmuch*sizeof(DSBPOSITIONNOTIFY)
112 This->dsb->nrofnotifies+=howmuch;
113 } else if (This->dscb) {
114 TRACE("notifies = %p, nrofnotifies = %d\n", This->dscb->notifies, This->dscb->nrofnotifies);
115 This->dscb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dscb->notifies,(This->dscb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
116 memcpy( This->dscb->notifies+This->dscb->nrofnotifies,
117 notify,
118 howmuch*sizeof(DSBPOSITIONNOTIFY)
120 This->dscb->nrofnotifies+=howmuch;
121 TRACE("notifies = %p, nrofnotifies = %d\n", This->dscb->notifies, This->dscb->nrofnotifies);
123 else
124 return DSERR_INVALIDPARAM;
126 return S_OK;
129 ICOM_VTABLE(IDirectSoundNotify) dsnvt =
131 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
132 IDirectSoundNotifyImpl_QueryInterface,
133 IDirectSoundNotifyImpl_AddRef,
134 IDirectSoundNotifyImpl_Release,
135 IDirectSoundNotifyImpl_SetNotificationPositions,
138 /*******************************************************************************
139 * IDirectSoundBuffer
142 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
143 LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex
145 ICOM_THIS(IDirectSoundBufferImpl,iface);
147 TRACE("(%p,%p)\n",This,wfex);
148 /* This method is not available on secondary buffers */
149 return DSERR_INVALIDCALL;
152 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
153 LPDIRECTSOUNDBUFFER8 iface,LONG vol
155 ICOM_THIS(IDirectSoundBufferImpl,iface);
156 LONG oldVol;
158 TRACE("(%p,%ld)\n",This,vol);
160 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
161 return DSERR_CONTROLUNAVAIL;
163 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
164 return DSERR_INVALIDPARAM;
166 /* **** */
167 EnterCriticalSection(&(This->lock));
169 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) {
170 oldVol = This->ds3db->lVolume;
171 This->ds3db->lVolume = vol;
172 } else {
173 oldVol = This->volpan.lVolume;
174 This->volpan.lVolume = vol;
175 if (vol != oldVol) DSOUND_RecalcVolPan(&(This->volpan));
178 if (vol != oldVol) {
179 if (This->hwbuf) {
180 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
182 else DSOUND_ForceRemix(This);
185 LeaveCriticalSection(&(This->lock));
186 /* **** */
188 return DS_OK;
191 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
192 LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
194 ICOM_THIS(IDirectSoundBufferImpl,iface);
195 TRACE("(%p,%p)\n",This,vol);
197 if (vol == NULL)
198 return DSERR_INVALIDPARAM;
200 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
201 *vol = This->ds3db->lVolume;
202 else
203 *vol = This->volpan.lVolume;
204 return DS_OK;
207 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
208 LPDIRECTSOUNDBUFFER8 iface,DWORD freq
210 ICOM_THIS(IDirectSoundBufferImpl,iface);
211 DWORD oldFreq;
213 TRACE("(%p,%ld)\n",This,freq);
215 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY))
216 return DSERR_CONTROLUNAVAIL;
218 if (freq == DSBFREQUENCY_ORIGINAL)
219 freq = This->wfx.nSamplesPerSec;
221 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
222 return DSERR_INVALIDPARAM;
224 /* **** */
225 EnterCriticalSection(&(This->lock));
227 oldFreq = This->freq;
228 This->freq = freq;
229 if (freq != oldFreq) {
230 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->dsound->wfx.nSamplesPerSec;
231 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
232 DSOUND_RecalcFormat(This);
233 if (!This->hwbuf) DSOUND_ForceRemix(This);
236 LeaveCriticalSection(&(This->lock));
237 /* **** */
239 return DS_OK;
242 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
243 LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
245 ICOM_THIS(IDirectSoundBufferImpl,iface);
246 TRACE("(%p,%08lx,%08lx,%08lx)\n",
247 This,reserved1,reserved2,flags
250 /* **** */
251 EnterCriticalSection(&(This->lock));
253 This->playflags = flags;
254 if (This->state == STATE_STOPPED) {
255 This->leadin = TRUE;
256 This->startpos = This->buf_mixpos;
257 This->state = STATE_STARTING;
258 } else if (This->state == STATE_STOPPING)
259 This->state = STATE_PLAYING;
260 if (This->hwbuf) {
261 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
262 This->state = STATE_PLAYING;
265 LeaveCriticalSection(&(This->lock));
266 /* **** */
268 return DS_OK;
271 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
273 ICOM_THIS(IDirectSoundBufferImpl,iface);
274 TRACE("(%p)\n",This);
276 /* **** */
277 EnterCriticalSection(&(This->lock));
279 if (This->state == STATE_PLAYING)
280 This->state = STATE_STOPPING;
281 else if (This->state == STATE_STARTING)
282 This->state = STATE_STOPPED;
283 if (This->hwbuf) {
284 IDsDriverBuffer_Stop(This->hwbuf);
285 This->state = STATE_STOPPED;
287 DSOUND_CheckEvent(This, 0);
289 LeaveCriticalSection(&(This->lock));
290 /* **** */
292 return DS_OK;
295 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
296 ICOM_THIS(IDirectSoundBufferImpl,iface);
297 DWORD ref;
299 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
301 ref = InterlockedIncrement(&(This->ref));
302 if (!ref) {
303 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
305 return ref;
307 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
308 ICOM_THIS(IDirectSoundBufferImpl,iface);
309 int i;
310 DWORD ref;
312 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
314 ref = InterlockedDecrement(&(This->ref));
315 if (ref) return ref;
317 RtlAcquireResourceExclusive(&(This->dsound->lock), TRUE);
318 for (i=0;i<This->dsound->nrofbuffers;i++)
319 if (This->dsound->buffers[i] == This)
320 break;
322 if (i < This->dsound->nrofbuffers) {
323 /* Put the last buffer of the list in the (now empty) position */
324 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
325 This->dsound->nrofbuffers--;
326 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER8)*This->dsound->nrofbuffers);
327 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
328 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
330 RtlReleaseResource(&(This->dsound->lock));
332 DeleteCriticalSection(&(This->lock));
333 if (This->hwbuf) {
334 IDsDriverBuffer_Release(This->hwbuf);
335 if (This->dsound->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY)
336 HeapFree(GetProcessHeap(),0,This->buffer);
338 else if (!This->parent)
339 HeapFree(GetProcessHeap(),0,This->buffer);
340 if (This->ds3db) {
341 DeleteCriticalSection(&This->ds3db->lock);
342 HeapFree(GetProcessHeap(), 0, This->ds3db);
344 if (This->iks) {
345 HeapFree(GetProcessHeap(), 0, This->iks);
347 if (This->parent)
348 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->parent);
350 HeapFree(GetProcessHeap(),0,This);
352 return 0;
355 DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
356 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
358 DWORD bplay;
360 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
361 TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
363 /* the actual primary play position (pplay) is always behind last mixed (pmix),
364 * unless the computer is too slow or something */
365 /* we need to know how far away we are from there */
366 #if 0 /* we'll never fill the primary entirely */
367 if (pmix == pplay) {
368 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
369 /* wow, the software mixer is really doing well,
370 * seems the entire primary buffer is filled! */
371 pmix += This->dsound->buflen;
373 /* else: the primary buffer is not playing, so probably empty */
375 #endif
376 if (pmix < pplay) pmix += This->dsound->buflen; /* wraparound */
377 pmix -= pplay;
378 /* detect buffer underrun */
379 if (pwrite < pplay) pwrite += This->dsound->buflen; /* wraparound */
380 pwrite -= pplay;
381 if (pmix > (ds_snd_queue_max * This->dsound->fraglen + pwrite + This->dsound->writelead)) {
382 WARN("detected an underrun: primary queue was %ld\n",pmix);
383 pmix = 0;
385 /* divide the offset by its sample size */
386 pmix /= This->dsound->wfx.nBlockAlign;
387 TRACE("primary back-samples=%ld\n",pmix);
388 /* adjust for our frequency */
389 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
390 /* multiply by our own sample size */
391 pmix *= This->wfx.nBlockAlign;
392 TRACE("this back-offset=%ld\n", pmix);
393 /* subtract from our last mixed position */
394 bplay = bmix;
395 while (bplay < pmix) bplay += This->buflen; /* wraparound */
396 bplay -= pmix;
397 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
398 /* seems we haven't started playing yet */
399 TRACE("this still in lead-in phase\n");
400 bplay = This->startpos;
402 /* return the result */
403 return bplay;
406 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
407 LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
409 HRESULT hres;
410 ICOM_THIS(IDirectSoundBufferImpl,iface);
411 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
412 if (This->hwbuf) {
413 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
414 if (hres)
415 return hres;
417 else {
418 if (playpos && (This->state != STATE_PLAYING)) {
419 /* we haven't been merged into the primary buffer (yet) */
420 *playpos = This->buf_mixpos;
422 else if (playpos) {
423 DWORD pplay, pwrite, lplay, splay, pstate;
424 /* let's get this exact; first, recursively call GetPosition on the primary */
425 EnterCriticalSection(&(This->dsound->mixlock));
426 DSOUND_PrimaryGetPosition(This->dsound, &pplay, &pwrite);
427 /* detect HEL mode underrun */
428 pstate = This->dsound->state;
429 if (!(This->dsound->hwbuf || This->dsound->pwqueue)) {
430 TRACE("detected an underrun\n");
431 /* pplay = ? */
432 if (pstate == STATE_PLAYING)
433 pstate = STATE_STARTING;
434 else if (pstate == STATE_STOPPING)
435 pstate = STATE_STOPPED;
437 /* get data for ourselves while we still have the lock */
438 pstate &= This->state;
439 lplay = This->primary_mixpos;
440 splay = This->buf_mixpos;
441 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->hwbuf) {
442 /* calculate play position using this */
443 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
444 } else {
445 /* (unless the app isn't using GETCURRENTPOSITION2) */
446 /* don't know exactly how this should be handled...
447 * the docs says that play cursor is reported as directly
448 * behind write cursor, hmm... */
449 /* let's just do what might work for Half-Life */
450 DWORD wp;
451 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
452 while (wp >= This->dsound->buflen)
453 wp -= This->dsound->buflen;
454 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
456 LeaveCriticalSection(&(This->dsound->mixlock));
458 if (writepos) *writepos = This->buf_mixpos;
460 if (writepos) {
461 if (This->state != STATE_STOPPED)
462 /* apply the documented 10ms lead to writepos */
463 *writepos += This->writelead;
464 while (*writepos >= This->buflen) *writepos -= This->buflen;
466 if (playpos) This->last_playpos = *playpos;
467 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
468 return DS_OK;
471 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
472 LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
474 ICOM_THIS(IDirectSoundBufferImpl,iface);
475 TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
477 if (status == NULL)
478 return DSERR_INVALIDPARAM;
480 *status = 0;
481 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
482 *status |= DSBSTATUS_PLAYING;
483 if (This->playflags & DSBPLAY_LOOPING)
484 *status |= DSBSTATUS_LOOPING;
487 TRACE("status=%lx\n", *status);
488 return DS_OK;
492 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
493 LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
495 ICOM_THIS(IDirectSoundBufferImpl,iface);
496 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
498 if (wfsize>sizeof(This->wfx))
499 wfsize = sizeof(This->wfx);
500 if (lpwf) { /* NULL is valid */
501 memcpy(lpwf,&(This->wfx),wfsize);
502 if (wfwritten)
503 *wfwritten = wfsize;
504 } else
505 if (wfwritten)
506 *wfwritten = sizeof(This->wfx);
507 else
508 return DSERR_INVALIDPARAM;
510 return DS_OK;
513 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
514 LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
516 ICOM_THIS(IDirectSoundBufferImpl,iface);
518 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
519 This,
520 writecursor,
521 writebytes,
522 lplpaudioptr1,
523 audiobytes1,
524 lplpaudioptr2,
525 audiobytes2,
526 flags,
527 GetTickCount()
530 if (flags & DSBLOCK_FROMWRITECURSOR) {
531 DWORD writepos;
532 /* GetCurrentPosition does too much magic to duplicate here */
533 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
534 writecursor += writepos;
536 while (writecursor >= This->buflen)
537 writecursor -= This->buflen;
538 if (flags & DSBLOCK_ENTIREBUFFER)
539 writebytes = This->buflen;
540 if (writebytes > This->buflen)
541 writebytes = This->buflen;
543 assert(audiobytes1!=audiobytes2);
544 assert(lplpaudioptr1!=lplpaudioptr2);
546 EnterCriticalSection(&(This->lock));
548 if ((writebytes == This->buflen) &&
549 ((This->state == STATE_STARTING) ||
550 (This->state == STATE_PLAYING)))
551 /* some games, like Half-Life, try to be clever (not) and
552 * keep one secondary buffer, and mix sounds into it itself,
553 * locking the entire buffer every time... so we can just forget
554 * about tracking the last-written-to-position... */
555 This->probably_valid_to = (DWORD)-1;
556 else
557 This->probably_valid_to = writecursor;
559 if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
560 IDsDriverBuffer_Lock(This->hwbuf,
561 lplpaudioptr1, audiobytes1,
562 lplpaudioptr2, audiobytes2,
563 writecursor, writebytes,
566 else {
567 BOOL remix = FALSE;
568 if (writecursor+writebytes <= This->buflen) {
569 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
570 *audiobytes1 = writebytes;
571 if (lplpaudioptr2)
572 *(LPBYTE*)lplpaudioptr2 = NULL;
573 if (audiobytes2)
574 *audiobytes2 = 0;
575 TRACE("->%ld.0\n",writebytes);
576 } else {
577 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
578 *audiobytes1 = This->buflen-writecursor;
579 if (lplpaudioptr2)
580 *(LPBYTE*)lplpaudioptr2 = This->buffer;
581 if (audiobytes2)
582 *audiobytes2 = writebytes-(This->buflen-writecursor);
583 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
585 if (This->state == STATE_PLAYING) {
586 /* if the segment between playpos and buf_mixpos is touched,
587 * we need to cancel some mixing */
588 /* we'll assume that the app always calls GetCurrentPosition before
589 * locking a playing buffer, so that last_playpos is up-to-date */
590 if (This->buf_mixpos >= This->last_playpos) {
591 if (This->buf_mixpos > writecursor &&
592 This->last_playpos < writecursor+writebytes)
593 remix = TRUE;
595 else {
596 if (This->buf_mixpos > writecursor ||
597 This->last_playpos < writecursor+writebytes)
598 remix = TRUE;
600 if (remix) {
601 TRACE("locking prebuffered region, ouch\n");
602 DSOUND_MixCancelAt(This, writecursor);
607 LeaveCriticalSection(&(This->lock));
608 return DS_OK;
611 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
612 LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
614 ICOM_THIS(IDirectSoundBufferImpl,iface);
615 TRACE("(%p,%ld)\n",This,newpos);
617 /* **** */
618 EnterCriticalSection(&(This->lock));
620 while (newpos >= This->buflen)
621 newpos -= This->buflen;
622 This->buf_mixpos = newpos;
623 if (This->hwbuf)
624 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
626 LeaveCriticalSection(&(This->lock));
627 /* **** */
629 return DS_OK;
632 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
633 LPDIRECTSOUNDBUFFER8 iface,LONG pan
635 ICOM_THIS(IDirectSoundBufferImpl,iface);
636 LONG oldPan;
638 TRACE("(%p,%ld)\n",This,pan);
640 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
641 return DSERR_INVALIDPARAM;
643 /* You cannot use both pan and 3D controls */
644 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
645 (This->dsbd.dwFlags & DSBCAPS_CTRL3D))
646 return DSERR_CONTROLUNAVAIL;
648 /* **** */
649 EnterCriticalSection(&(This->lock));
651 oldPan = This->volpan.lPan;
652 This->volpan.lPan = pan;
654 if (pan != oldPan) {
655 DSOUND_RecalcVolPan(&(This->volpan));
657 if (This->hwbuf) {
658 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
660 else DSOUND_ForceRemix(This);
663 LeaveCriticalSection(&(This->lock));
664 /* **** */
666 return DS_OK;
669 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
670 LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
672 ICOM_THIS(IDirectSoundBufferImpl,iface);
673 TRACE("(%p,%p)\n",This,pan);
675 if (pan == NULL)
676 return DSERR_INVALIDPARAM;
678 *pan = This->volpan.lPan;
680 return DS_OK;
683 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
684 LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
686 ICOM_THIS(IDirectSoundBufferImpl,iface);
687 DWORD probably_valid_to;
689 TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
691 #if 0
692 /* Preprocess 3D buffers... */
694 /* This is highly experimental and liable to break things */
695 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
696 DSOUND_Create3DBuffer(This);
697 #endif
699 if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
700 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
703 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
704 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
705 while (probably_valid_to >= This->buflen)
706 probably_valid_to -= This->buflen;
707 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
708 ((This->state == STATE_STARTING) ||
709 (This->state == STATE_PLAYING)))
710 /* see IDirectSoundBufferImpl_Lock */
711 probably_valid_to = (DWORD)-1;
712 This->probably_valid_to = probably_valid_to;
714 return DS_OK;
717 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
718 LPDIRECTSOUNDBUFFER8 iface
720 ICOM_THIS(IDirectSoundBufferImpl,iface);
721 FIXME("(%p):stub\n",This);
722 return DS_OK;
725 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
726 LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
728 ICOM_THIS(IDirectSoundBufferImpl,iface);
729 TRACE("(%p,%p)\n",This,freq);
731 if (freq == NULL)
732 return DSERR_INVALIDPARAM;
734 *freq = This->freq;
735 TRACE("-> %ld\n", *freq);
737 return DS_OK;
740 static HRESULT WINAPI IDirectSoundBufferImpl_SetFX(
741 LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
743 ICOM_THIS(IDirectSoundBufferImpl,iface);
744 DWORD u;
746 FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
748 if (pdwResultCodes)
749 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
751 return DSERR_CONTROLUNAVAIL;
754 static HRESULT WINAPI IDirectSoundBufferImpl_AcquireResources(
755 LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
757 ICOM_THIS(IDirectSoundBufferImpl,iface);
758 DWORD u;
760 FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
762 if (pdwResultCodes)
763 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
765 return DSERR_CONTROLUNAVAIL;
768 static HRESULT WINAPI IDirectSoundBufferImpl_GetObjectInPath(
769 LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
771 ICOM_THIS(IDirectSoundBufferImpl,iface);
773 FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
775 return DSERR_CONTROLUNAVAIL;
778 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
779 LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
781 ICOM_THIS(IDirectSoundBufferImpl,iface);
782 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
783 DPRINTF("Re-Init!!!\n");
784 return DSERR_ALREADYINITIALIZED;
787 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
788 LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
790 ICOM_THIS(IDirectSoundBufferImpl,iface);
791 TRACE("(%p)->(%p)\n",This,caps);
793 if (caps == NULL || caps->dwSize!=sizeof(*caps))
794 return DSERR_INVALIDPARAM;
796 caps->dwFlags = This->dsbd.dwFlags;
797 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
798 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
800 caps->dwBufferBytes = This->buflen;
802 /* This value represents the speed of the "unlock" command.
803 As unlock is quite fast (it does not do anything), I put
804 4096 ko/s = 4 Mo / s */
805 /* FIXME: hwbuf speed */
806 caps->dwUnlockTransferRate = 4096;
807 caps->dwPlayCpuOverhead = 0;
809 return DS_OK;
812 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
813 LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
815 ICOM_THIS(IDirectSoundBufferImpl,iface);
817 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
819 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
820 IDirectSoundNotifyImpl *dsn;
822 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
823 dsn->ref = 1;
824 dsn->dsb = This;
825 dsn->dscb = 0;
826 IDirectSoundBuffer8_AddRef(iface);
827 ICOM_VTBL(dsn) = &dsnvt;
828 *ppobj = (LPVOID)dsn;
829 return S_OK;
832 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
833 if (!This->ds3db)
834 IDirectSound3DBufferImpl_Create(This, &This->ds3db);
835 *ppobj = This->ds3db;
836 if (*ppobj) {
837 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)*ppobj);
838 return S_OK;
840 return E_FAIL;
843 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
844 ERR("app requested IDirectSound3DListener on secondary buffer\n");
845 *ppobj = NULL;
846 return E_FAIL;
849 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
850 if (!This->iks)
851 IKsPropertySetImpl_Create(This, &This->iks);
852 *ppobj = This->iks;
853 if (*ppobj) {
854 IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
855 return S_OK;
857 return E_FAIL;
860 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
861 IDirectSoundBuffer8_AddRef(iface);
862 *ppobj = This;
863 return NO_ERROR;
866 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
868 *ppobj = NULL;
870 return E_NOINTERFACE;
873 static ICOM_VTABLE(IDirectSoundBuffer8) dsbvt =
875 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
876 IDirectSoundBufferImpl_QueryInterface,
877 IDirectSoundBufferImpl_AddRef,
878 IDirectSoundBufferImpl_Release,
879 IDirectSoundBufferImpl_GetCaps,
880 IDirectSoundBufferImpl_GetCurrentPosition,
881 IDirectSoundBufferImpl_GetFormat,
882 IDirectSoundBufferImpl_GetVolume,
883 IDirectSoundBufferImpl_GetPan,
884 IDirectSoundBufferImpl_GetFrequency,
885 IDirectSoundBufferImpl_GetStatus,
886 IDirectSoundBufferImpl_Initialize,
887 IDirectSoundBufferImpl_Lock,
888 IDirectSoundBufferImpl_Play,
889 IDirectSoundBufferImpl_SetCurrentPosition,
890 IDirectSoundBufferImpl_SetFormat,
891 IDirectSoundBufferImpl_SetVolume,
892 IDirectSoundBufferImpl_SetPan,
893 IDirectSoundBufferImpl_SetFrequency,
894 IDirectSoundBufferImpl_Stop,
895 IDirectSoundBufferImpl_Unlock,
896 IDirectSoundBufferImpl_Restore,
897 IDirectSoundBufferImpl_SetFX,
898 IDirectSoundBufferImpl_AcquireResources,
899 IDirectSoundBufferImpl_GetObjectInPath
902 HRESULT WINAPI SecondaryBuffer_Create(
903 IDirectSoundImpl *This,
904 IDirectSoundBufferImpl **pdsb,
905 LPDSBUFFERDESC dsbd)
907 IDirectSoundBufferImpl *dsb;
908 LPWAVEFORMATEX wfex = dsbd->lpwfxFormat;
909 HRESULT err = DS_OK;
910 DWORD capf = 0;
911 int use_hw;
913 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
914 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
915 return DSERR_INVALIDPARAM; /* FIXME: which error? */
918 dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
919 dsb->ref = 1;
920 dsb->dsound = This;
921 dsb->parent = NULL;
922 ICOM_VTBL(dsb) = &dsbvt;
924 memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
925 if (wfex)
926 memcpy(&dsb->wfx, wfex, sizeof(dsb->wfx));
928 TRACE("Created buffer at %p\n", dsb);
930 dsb->buflen = dsbd->dwBufferBytes;
931 dsb->freq = dsbd->lpwfxFormat->nSamplesPerSec;
933 /* Check necessary hardware mixing capabilities */
934 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
935 else capf |= DSCAPS_SECONDARYMONO;
936 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
937 else capf |= DSCAPS_SECONDARY8BIT;
938 use_hw = (This->drvcaps.dwFlags & capf) == capf;
940 /* FIXME: check hardware sample rate mixing capabilities */
941 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
942 /* FIXME: check whether any hardware buffers are left */
943 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
945 /* Allocate system memory if applicable */
946 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
947 dsb->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
948 if (dsb->buffer == NULL)
949 err = DSERR_OUTOFMEMORY;
952 /* Allocate the hardware buffer */
953 if (use_hw && (err == DS_OK)) {
954 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
955 &(dsb->buflen),&(dsb->buffer),
956 (LPVOID*)&(dsb->hwbuf));
959 if (err != DS_OK) {
960 if (dsb->buffer)
961 HeapFree(GetProcessHeap(),0,dsb->buffer);
962 HeapFree(GetProcessHeap(),0,dsb);
963 dsb = NULL;
964 return err;
966 /* calculate fragment size and write lead */
967 DSOUND_RecalcFormat(dsb);
969 /* It's not necessary to initialize values to zero since */
970 /* we allocated this structure with HEAP_ZERO_MEMORY... */
971 dsb->playpos = 0;
972 dsb->buf_mixpos = 0;
973 dsb->state = STATE_STOPPED;
975 dsb->freqAdjust = (dsb->freq << DSOUND_FREQSHIFT) /
976 This->wfx.nSamplesPerSec;
977 dsb->nAvgBytesPerSec = dsb->freq *
978 dsbd->lpwfxFormat->nBlockAlign;
980 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
981 IDirectSound3DBufferImpl_Create(dsb, &dsb->ds3db);
983 else
984 DSOUND_RecalcVolPan(&(dsb->volpan));
986 InitializeCriticalSection(&(dsb->lock));
988 /* register buffer */
989 RtlAcquireResourceExclusive(&(This->lock), TRUE);
990 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
991 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
992 if (newbuffers) {
993 This->buffers = newbuffers;
994 This->buffers[This->nrofbuffers] = dsb;
995 This->nrofbuffers++;
996 TRACE("buffer count is now %d\n", This->nrofbuffers);
997 } else {
998 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
999 err = DSERR_OUTOFMEMORY;
1002 RtlReleaseResource(&(This->lock));
1003 IDirectSound8_AddRef((LPDIRECTSOUND8)This);
1005 if (err != DS_OK) {
1006 /* oops... */
1007 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)dsb);
1008 *pdsb = NULL;
1009 return err;
1012 *pdsb = dsb;
1013 return S_OK;