Split property sets into two types: buffer and private.
[wine/testsucceed.git] / dlls / dsound / buffer.c
blob10119172a829f2cfafbd67b91c2c86f9a4cc99d5
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>
34 #define NONAMELESSSTRUCT
35 #define NONAMELESSUNION
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "winerror.h"
41 #include "mmsystem.h"
42 #include "winternl.h"
43 #include "mmddk.h"
44 #include "wine/windef16.h"
45 #include "wine/debug.h"
46 #include "dsound.h"
47 #include "dsdriver.h"
48 #include "dsound_private.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
52 /*******************************************************************************
53 * IDirectSoundNotify
55 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
56 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
57 ) {
58 ICOM_THIS(IDirectSoundNotifyImpl,iface);
59 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
61 if (ppobj == NULL) {
62 WARN("invalid parameter\n");
63 return E_INVALIDARG;
66 *ppobj = NULL; /* assume error */
68 if ( IsEqualGUID(riid, &IID_IUnknown) ||
69 IsEqualGUID(riid, &IID_IDirectSoundNotify) ||
70 IsEqualGUID(riid, &IID_IDirectSoundNotify8) ) {
71 IDirectSoundNotify_AddRef(iface);
72 *ppobj = This;
73 return DS_OK;
76 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
77 return E_NOINTERFACE;
80 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
81 ICOM_THIS(IDirectSoundNotifyImpl,iface);
82 DWORD ref;
84 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
86 ref = InterlockedIncrement(&(This->ref));
87 return ref;
90 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
91 ICOM_THIS(IDirectSoundNotifyImpl,iface);
92 DWORD ref;
94 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
96 ref = InterlockedDecrement(&(This->ref));
97 /* FIXME: A notification should be a part of a buffer rather than pointed
98 * to from a buffer. Hence the -1 ref count */
99 if (ref == -1) {
100 if (This->notifies != NULL)
101 HeapFree(GetProcessHeap(), 0, This->notifies);
103 HeapFree(GetProcessHeap(),0,This);
104 return 0;
106 return ref;
109 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
110 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
112 ICOM_THIS(IDirectSoundNotifyImpl,iface);
113 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
115 if (notify == NULL) {
116 WARN("invalid parameter: notify == NULL\n");
117 return DSERR_INVALIDPARAM;
120 if (TRACE_ON(dsound)) {
121 int i;
122 for (i=0;i<howmuch;i++)
123 TRACE("notify at %ld to 0x%08lx\n",
124 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
127 if (This->hwnotify) {
128 HRESULT hres;
129 hres = IDsDriverNotify_SetNotificationPositions(This->hwnotify, howmuch, notify);
130 if (hres != DS_OK)
131 WARN("IDsDriverNotify_SetNotificationPositions failed\n");
132 return hres;
133 } else {
134 /* Make an internal copy of the caller-supplied array.
135 * Replace the existing copy if one is already present. */
136 This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
137 This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
138 if (This->notifies == NULL) {
139 WARN("out of memory\n");
140 return DSERR_OUTOFMEMORY;
142 memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
143 This->nrofnotifies = howmuch;
146 return S_OK;
149 ICOM_VTABLE(IDirectSoundNotify) dsnvt =
151 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
152 IDirectSoundNotifyImpl_QueryInterface,
153 IDirectSoundNotifyImpl_AddRef,
154 IDirectSoundNotifyImpl_Release,
155 IDirectSoundNotifyImpl_SetNotificationPositions,
158 /*******************************************************************************
159 * IDirectSoundBuffer
162 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
163 LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex
165 ICOM_THIS(IDirectSoundBufferImpl,iface);
167 TRACE("(%p,%p)\n",This,wfex);
168 /* This method is not available on secondary buffers */
169 WARN("invalid call\n");
170 return DSERR_INVALIDCALL;
173 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
174 LPDIRECTSOUNDBUFFER8 iface,LONG vol
176 ICOM_THIS(IDirectSoundBufferImpl,iface);
177 LONG oldVol;
179 TRACE("(%p,%ld)\n",This,vol);
181 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
182 WARN("control unavailable: This->dsbd.dwFlags = 0x%08lx\n", This->dsbd.dwFlags);
183 return DSERR_CONTROLUNAVAIL;
186 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
187 WARN("invalid parameter: vol = %ld\n", vol);
188 return DSERR_INVALIDPARAM;
191 /* **** */
192 EnterCriticalSection(&(This->lock));
194 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) {
195 oldVol = This->ds3db_lVolume;
196 This->ds3db_lVolume = vol;
197 } else {
198 oldVol = This->volpan.lVolume;
199 This->volpan.lVolume = vol;
200 if (vol != oldVol)
201 DSOUND_RecalcVolPan(&(This->volpan));
204 if (vol != oldVol) {
205 if (This->hwbuf) {
206 HRESULT hres;
207 hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
208 if (hres != DS_OK)
209 WARN("IDsDriverBuffer_SetVolumePan failed\n");
210 } else
211 DSOUND_ForceRemix(This);
214 LeaveCriticalSection(&(This->lock));
215 /* **** */
217 return DS_OK;
220 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
221 LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
223 ICOM_THIS(IDirectSoundBufferImpl,iface);
224 TRACE("(%p,%p)\n",This,vol);
226 if (vol == NULL) {
227 WARN("invalid parameter: vol == NULL\n");
228 return DSERR_INVALIDPARAM;
231 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
232 *vol = This->ds3db_lVolume;
233 else
234 *vol = This->volpan.lVolume;
235 return DS_OK;
238 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
239 LPDIRECTSOUNDBUFFER8 iface,DWORD freq
241 ICOM_THIS(IDirectSoundBufferImpl,iface);
242 DWORD oldFreq;
244 TRACE("(%p,%ld)\n",This,freq);
246 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
247 WARN("control unavailable\n");
248 return DSERR_CONTROLUNAVAIL;
251 if (freq == DSBFREQUENCY_ORIGINAL)
252 freq = This->wfx.nSamplesPerSec;
254 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX)) {
255 WARN("invalid parameter: freq = %ld\n", freq);
256 return DSERR_INVALIDPARAM;
259 /* **** */
260 EnterCriticalSection(&(This->lock));
262 oldFreq = This->freq;
263 This->freq = freq;
264 if (freq != oldFreq) {
265 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->dsound->wfx.nSamplesPerSec;
266 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
267 DSOUND_RecalcFormat(This);
268 if (!This->hwbuf)
269 DSOUND_ForceRemix(This);
272 LeaveCriticalSection(&(This->lock));
273 /* **** */
275 return DS_OK;
278 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
279 LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
281 HRESULT hres = DS_OK;
282 ICOM_THIS(IDirectSoundBufferImpl,iface);
283 TRACE("(%p,%08lx,%08lx,%08lx)\n",This,reserved1,reserved2,flags);
285 /* **** */
286 EnterCriticalSection(&(This->lock));
288 This->playflags = flags;
289 if (This->state == STATE_STOPPED) {
290 This->leadin = TRUE;
291 This->startpos = This->buf_mixpos;
292 This->state = STATE_STARTING;
293 } else if (This->state == STATE_STOPPING)
294 This->state = STATE_PLAYING;
295 if (This->hwbuf) {
296 hres = IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
297 if (hres != DS_OK)
298 WARN("IDsDriverBuffer_Play failed\n");
299 else
300 This->state = STATE_PLAYING;
303 LeaveCriticalSection(&(This->lock));
304 /* **** */
306 return hres;
309 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
311 HRESULT hres = DS_OK;
312 ICOM_THIS(IDirectSoundBufferImpl,iface);
313 TRACE("(%p)\n",This);
315 /* **** */
316 EnterCriticalSection(&(This->lock));
318 if (This->state == STATE_PLAYING)
319 This->state = STATE_STOPPING;
320 else if (This->state == STATE_STARTING)
321 This->state = STATE_STOPPED;
322 if (This->hwbuf) {
323 hres = IDsDriverBuffer_Stop(This->hwbuf);
324 if (hres != DS_OK)
325 WARN("IDsDriverBuffer_Stop failed\n");
326 else
327 This->state = STATE_STOPPED;
329 DSOUND_CheckEvent(This, 0);
331 LeaveCriticalSection(&(This->lock));
332 /* **** */
334 return hres;
337 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
338 ICOM_THIS(IDirectSoundBufferImpl,iface);
339 DWORD ref;
341 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
343 ref = InterlockedIncrement(&(This->ref));
344 if (!ref) {
345 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
347 return ref;
350 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
351 ICOM_THIS(IDirectSoundBufferImpl,iface);
352 int i;
353 DWORD ref;
355 TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
357 ref = InterlockedDecrement(&(This->ref));
358 if (ref) return ref;
360 RtlAcquireResourceExclusive(&(This->dsound->lock), TRUE);
361 for (i=0;i<This->dsound->nrofbuffers;i++)
362 if (This->dsound->buffers[i] == This)
363 break;
365 if (i < This->dsound->nrofbuffers) {
366 /* Put the last buffer of the list in the (now empty) position */
367 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
368 This->dsound->nrofbuffers--;
369 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER8)*This->dsound->nrofbuffers);
370 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
371 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
373 RtlReleaseResource(&(This->dsound->lock));
375 DeleteCriticalSection(&(This->lock));
377 if (This->hwbuf) {
378 IDsDriverBuffer_Release(This->hwbuf);
379 if (This->dsound->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {
380 This->buffer->ref--;
381 if (This->buffer->ref==0) {
382 HeapFree(GetProcessHeap(),0,This->buffer->memory);
383 HeapFree(GetProcessHeap(),0,This->buffer);
386 } else {
387 This->buffer->ref--;
388 if (This->buffer->ref==0) {
389 HeapFree(GetProcessHeap(),0,This->buffer->memory);
390 HeapFree(GetProcessHeap(),0,This->buffer);
394 if (This->ds3db) {
395 WARN("ds3db still has reference\n");
396 EnterCriticalSection(&(This->ds3db->lock));
397 This->ds3db->dsb = NULL;
398 LeaveCriticalSection(&(This->ds3db->lock));
401 if (This->iks)
402 IKsPropertySet_Release((LPKSPROPERTYSET)This->iks);
404 HeapFree(GetProcessHeap(),0,This);
406 return 0;
409 DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
410 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
412 DWORD bplay;
414 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
415 TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
417 /* the actual primary play position (pplay) is always behind last mixed (pmix),
418 * unless the computer is too slow or something */
419 /* we need to know how far away we are from there */
420 #if 0 /* we'll never fill the primary entirely */
421 if (pmix == pplay) {
422 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
423 /* wow, the software mixer is really doing well,
424 * seems the entire primary buffer is filled! */
425 pmix += This->dsound->buflen;
427 /* else: the primary buffer is not playing, so probably empty */
429 #endif
430 if (pmix < pplay) pmix += This->dsound->buflen; /* wraparound */
431 pmix -= pplay;
432 /* detect buffer underrun */
433 if (pwrite < pplay) pwrite += This->dsound->buflen; /* wraparound */
434 pwrite -= pplay;
435 if (pmix > (ds_snd_queue_max * This->dsound->fraglen + pwrite + This->dsound->writelead)) {
436 WARN("detected an underrun: primary queue was %ld\n",pmix);
437 pmix = 0;
439 /* divide the offset by its sample size */
440 pmix /= This->dsound->wfx.nBlockAlign;
441 TRACE("primary back-samples=%ld\n",pmix);
442 /* adjust for our frequency */
443 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
444 /* multiply by our own sample size */
445 pmix *= This->wfx.nBlockAlign;
446 TRACE("this back-offset=%ld\n", pmix);
447 /* subtract from our last mixed position */
448 bplay = bmix;
449 while (bplay < pmix) bplay += This->buflen; /* wraparound */
450 bplay -= pmix;
451 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
452 /* seems we haven't started playing yet */
453 TRACE("this still in lead-in phase\n");
454 bplay = This->startpos;
456 /* return the result */
457 return bplay;
460 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
461 LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
463 HRESULT hres;
464 ICOM_THIS(IDirectSoundBufferImpl,iface);
465 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
466 if (This->hwbuf) {
467 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
468 if (hres != DS_OK) {
469 WARN("IDsDriverBuffer_GetPosition failed\n");
470 return hres;
473 else {
474 if (playpos && (This->state != STATE_PLAYING)) {
475 /* we haven't been merged into the primary buffer (yet) */
476 *playpos = This->buf_mixpos;
478 else if (playpos) {
479 DWORD pplay, pwrite, lplay, splay, pstate;
480 /* let's get this exact; first, recursively call GetPosition on the primary */
481 EnterCriticalSection(&(This->dsound->mixlock));
482 if (DSOUND_PrimaryGetPosition(This->dsound, &pplay, &pwrite) != DS_OK)
483 WARN("DSOUND_PrimaryGetPosition failed\n");
484 /* detect HEL mode underrun */
485 pstate = This->dsound->state;
486 if (!(This->dsound->hwbuf || This->dsound->pwqueue)) {
487 TRACE("detected an underrun\n");
488 /* pplay = ? */
489 if (pstate == STATE_PLAYING)
490 pstate = STATE_STARTING;
491 else if (pstate == STATE_STOPPING)
492 pstate = STATE_STOPPED;
494 /* get data for ourselves while we still have the lock */
495 pstate &= This->state;
496 lplay = This->primary_mixpos;
497 splay = This->buf_mixpos;
498 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->hwbuf) {
499 /* calculate play position using this */
500 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
501 } else {
502 /* (unless the app isn't using GETCURRENTPOSITION2) */
503 /* don't know exactly how this should be handled...
504 * the docs says that play cursor is reported as directly
505 * behind write cursor, hmm... */
506 /* let's just do what might work for Half-Life */
507 DWORD wp;
508 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
509 while (wp >= This->dsound->buflen)
510 wp -= This->dsound->buflen;
511 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
513 LeaveCriticalSection(&(This->dsound->mixlock));
515 if (writepos) *writepos = This->buf_mixpos;
517 if (writepos) {
518 if (This->state != STATE_STOPPED)
519 /* apply the documented 10ms lead to writepos */
520 *writepos += This->writelead;
521 while (*writepos >= This->buflen) *writepos -= This->buflen;
523 if (playpos) This->last_playpos = *playpos;
524 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
525 return DS_OK;
528 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
529 LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
531 ICOM_THIS(IDirectSoundBufferImpl,iface);
532 TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
534 if (status == NULL) {
535 WARN("invalid parameter: status = NULL\n");
536 return DSERR_INVALIDPARAM;
539 *status = 0;
540 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
541 *status |= DSBSTATUS_PLAYING;
542 if (This->playflags & DSBPLAY_LOOPING)
543 *status |= DSBSTATUS_LOOPING;
546 TRACE("status=%lx\n", *status);
547 return DS_OK;
551 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
552 LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
554 ICOM_THIS(IDirectSoundBufferImpl,iface);
555 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
557 if (wfsize>sizeof(This->wfx))
558 wfsize = sizeof(This->wfx);
559 if (lpwf) { /* NULL is valid */
560 memcpy(lpwf,&(This->wfx),wfsize);
561 if (wfwritten)
562 *wfwritten = wfsize;
563 } else {
564 if (wfwritten)
565 *wfwritten = sizeof(This->wfx);
566 else {
567 WARN("invalid parameter: wfwritten == NULL\n");
568 return DSERR_INVALIDPARAM;
572 return DS_OK;
575 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
576 LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
578 HRESULT hres = DS_OK;
579 ICOM_THIS(IDirectSoundBufferImpl,iface);
581 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
582 This,
583 writecursor,
584 writebytes,
585 lplpaudioptr1,
586 audiobytes1,
587 lplpaudioptr2,
588 audiobytes2,
589 flags,
590 GetTickCount()
593 if (flags & DSBLOCK_FROMWRITECURSOR) {
594 DWORD writepos;
595 /* GetCurrentPosition does too much magic to duplicate here */
596 hres = IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
597 if (hres != DS_OK) {
598 WARN("IDirectSoundBufferImpl_GetCurrentPosition failed\n");
599 return hres;
601 writecursor += writepos;
603 while (writecursor >= This->buflen)
604 writecursor -= This->buflen;
605 if (flags & DSBLOCK_ENTIREBUFFER)
606 writebytes = This->buflen;
607 if (writebytes > This->buflen)
608 writebytes = This->buflen;
610 assert(audiobytes1!=audiobytes2);
611 assert(lplpaudioptr1!=lplpaudioptr2);
613 EnterCriticalSection(&(This->lock));
615 if ((writebytes == This->buflen) &&
616 ((This->state == STATE_STARTING) ||
617 (This->state == STATE_PLAYING)))
618 /* some games, like Half-Life, try to be clever (not) and
619 * keep one secondary buffer, and mix sounds into it itself,
620 * locking the entire buffer every time... so we can just forget
621 * about tracking the last-written-to-position... */
622 This->probably_valid_to = (DWORD)-1;
623 else
624 This->probably_valid_to = writecursor;
626 if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
627 hres = IDsDriverBuffer_Lock(This->hwbuf,
628 lplpaudioptr1, audiobytes1,
629 lplpaudioptr2, audiobytes2,
630 writecursor, writebytes,
632 if (hres != DS_OK) {
633 WARN("IDsDriverBuffer_Lock failed\n");
634 LeaveCriticalSection(&(This->lock));
635 return hres;
637 } else {
638 BOOL remix = FALSE;
639 if (writecursor+writebytes <= This->buflen) {
640 *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
641 *audiobytes1 = writebytes;
642 if (lplpaudioptr2)
643 *(LPBYTE*)lplpaudioptr2 = NULL;
644 if (audiobytes2)
645 *audiobytes2 = 0;
646 TRACE("->%ld.0\n",writebytes);
647 } else {
648 *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
649 *audiobytes1 = This->buflen-writecursor;
650 if (lplpaudioptr2)
651 *(LPBYTE*)lplpaudioptr2 = This->buffer->memory;
652 if (audiobytes2)
653 *audiobytes2 = writebytes-(This->buflen-writecursor);
654 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
656 if (This->state == STATE_PLAYING) {
657 /* if the segment between playpos and buf_mixpos is touched,
658 * we need to cancel some mixing */
659 /* we'll assume that the app always calls GetCurrentPosition before
660 * locking a playing buffer, so that last_playpos is up-to-date */
661 if (This->buf_mixpos >= This->last_playpos) {
662 if (This->buf_mixpos > writecursor &&
663 This->last_playpos < writecursor+writebytes)
664 remix = TRUE;
666 else {
667 if (This->buf_mixpos > writecursor ||
668 This->last_playpos < writecursor+writebytes)
669 remix = TRUE;
671 if (remix) {
672 TRACE("locking prebuffered region, ouch\n");
673 DSOUND_MixCancelAt(This, writecursor);
678 LeaveCriticalSection(&(This->lock));
679 return DS_OK;
682 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
683 LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
685 HRESULT hres = DS_OK;
686 ICOM_THIS(IDirectSoundBufferImpl,iface);
687 TRACE("(%p,%ld)\n",This,newpos);
689 /* **** */
690 EnterCriticalSection(&(This->lock));
692 while (newpos >= This->buflen)
693 newpos -= This->buflen;
694 This->buf_mixpos = newpos;
695 if (This->hwbuf) {
696 hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
697 if (hres != DS_OK)
698 WARN("IDsDriverBuffer_SetPosition failed\n");
701 LeaveCriticalSection(&(This->lock));
702 /* **** */
704 return hres;
707 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
708 LPDIRECTSOUNDBUFFER8 iface,LONG pan
710 HRESULT hres = DS_OK;
711 ICOM_THIS(IDirectSoundBufferImpl,iface);
712 LONG oldPan;
714 TRACE("(%p,%ld)\n",This,pan);
716 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
717 WARN("invalid parameter: pan = %ld\n", pan);
718 return DSERR_INVALIDPARAM;
721 /* You cannot use both pan and 3D controls */
722 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
723 (This->dsbd.dwFlags & DSBCAPS_CTRL3D)) {
724 WARN("control unavailable\n");
725 return DSERR_CONTROLUNAVAIL;
728 /* **** */
729 EnterCriticalSection(&(This->lock));
731 oldPan = This->volpan.lPan;
732 This->volpan.lPan = pan;
734 if (pan != oldPan) {
735 DSOUND_RecalcVolPan(&(This->volpan));
737 if (This->hwbuf) {
738 hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
739 if (hres != DS_OK)
740 WARN("IDsDriverBuffer_SetVolumePan failed\n");
741 } else
742 DSOUND_ForceRemix(This);
745 LeaveCriticalSection(&(This->lock));
746 /* **** */
748 return hres;
751 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
752 LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
754 ICOM_THIS(IDirectSoundBufferImpl,iface);
755 TRACE("(%p,%p)\n",This,pan);
757 if (pan == NULL) {
758 WARN("invalid parameter: pan = NULL\n");
759 return DSERR_INVALIDPARAM;
762 *pan = This->volpan.lPan;
764 return DS_OK;
767 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
768 LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
770 ICOM_THIS(IDirectSoundBufferImpl,iface);
771 DWORD probably_valid_to;
773 TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
775 if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
776 HRESULT hres;
777 hres = IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
778 if (hres != DS_OK) {
779 WARN("IDsDriverBuffer_Unlock failed\n");
780 return hres;
784 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer->memory) + x2;
785 else probably_valid_to = (((LPBYTE)p1)-This->buffer->memory) + x1;
786 while (probably_valid_to >= This->buflen)
787 probably_valid_to -= This->buflen;
788 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
789 ((This->state == STATE_STARTING) ||
790 (This->state == STATE_PLAYING)))
791 /* see IDirectSoundBufferImpl_Lock */
792 probably_valid_to = (DWORD)-1;
793 This->probably_valid_to = probably_valid_to;
795 return DS_OK;
798 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
799 LPDIRECTSOUNDBUFFER8 iface
801 ICOM_THIS(IDirectSoundBufferImpl,iface);
802 FIXME("(%p):stub\n",This);
803 return DS_OK;
806 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
807 LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
809 ICOM_THIS(IDirectSoundBufferImpl,iface);
810 TRACE("(%p,%p)\n",This,freq);
812 if (freq == NULL) {
813 WARN("invalid parameter: freq = NULL\n");
814 return DSERR_INVALIDPARAM;
817 *freq = This->freq;
818 TRACE("-> %ld\n", *freq);
820 return DS_OK;
823 static HRESULT WINAPI IDirectSoundBufferImpl_SetFX(
824 LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
826 ICOM_THIS(IDirectSoundBufferImpl,iface);
827 DWORD u;
829 FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
831 if (pdwResultCodes)
832 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
834 WARN("control unavailable\n");
835 return DSERR_CONTROLUNAVAIL;
838 static HRESULT WINAPI IDirectSoundBufferImpl_AcquireResources(
839 LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
841 ICOM_THIS(IDirectSoundBufferImpl,iface);
842 DWORD u;
844 FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
846 if (pdwResultCodes)
847 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
849 WARN("control unavailable\n");
850 return DSERR_CONTROLUNAVAIL;
853 static HRESULT WINAPI IDirectSoundBufferImpl_GetObjectInPath(
854 LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
856 ICOM_THIS(IDirectSoundBufferImpl,iface);
858 FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
860 WARN("control unavailable\n");
861 return DSERR_CONTROLUNAVAIL;
864 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
865 LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
867 ICOM_THIS(IDirectSoundBufferImpl,iface);
868 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
869 DPRINTF("Re-Init!!!\n");
870 WARN("already initialized\n");
871 return DSERR_ALREADYINITIALIZED;
874 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
875 LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
877 ICOM_THIS(IDirectSoundBufferImpl,iface);
878 TRACE("(%p)->(%p)\n",This,caps);
880 if (caps == NULL) {
881 WARN("invalid parameter: caps == NULL\n");
882 return DSERR_INVALIDPARAM;
885 if (caps->dwSize < sizeof(*caps)) {
886 WARN("invalid parameter: caps->dwSize = %ld < %d\n",caps->dwSize, sizeof(*caps));
887 return DSERR_INVALIDPARAM;
890 caps->dwFlags = This->dsbd.dwFlags;
891 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
892 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
894 caps->dwBufferBytes = This->buflen;
896 /* This value represents the speed of the "unlock" command.
897 As unlock is quite fast (it does not do anything), I put
898 4096 ko/s = 4 Mo / s */
899 /* FIXME: hwbuf speed */
900 caps->dwUnlockTransferRate = 4096;
901 caps->dwPlayCpuOverhead = 0;
903 return DS_OK;
906 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
907 LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
909 ICOM_THIS(IDirectSoundBufferImpl,iface);
911 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
913 if (ppobj == NULL) {
914 WARN("invalid parameter\n");
915 return E_INVALIDARG;
918 *ppobj = NULL; /* assume failure */
920 if ( IsEqualGUID(riid, &IID_IUnknown) ||
921 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ||
922 IsEqualGUID(riid, &IID_IDirectSoundBuffer8) ) {
923 IDirectSoundBuffer8_AddRef((LPDIRECTSOUNDBUFFER8)This);
924 *ppobj = This;
925 return S_OK;
928 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ||
929 IsEqualGUID( &IID_IDirectSoundNotify8, riid ) ) {
930 if (!This->notify) {
931 This->notify = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),
932 HEAP_ZERO_MEMORY,sizeof(*This->notify));
933 if (This->notify) {
934 This->notify->ref = 0; /* release when ref == -1 */
935 This->notify->lpVtbl = &dsnvt;
938 if (This->notify) {
939 IDirectSoundNotify_AddRef((LPDIRECTSOUNDNOTIFY)This->notify);
940 *ppobj = (LPVOID)This->notify;
941 return S_OK;
943 WARN("IID_IDirectSoundNotify\n");
944 return E_NOINTERFACE;
947 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
948 if (!This->ds3db)
949 IDirectSound3DBufferImpl_Create(This, &This->ds3db);
950 *ppobj = This->ds3db;
951 if (*ppobj) {
952 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)*ppobj);
953 return S_OK;
955 WARN("IID_IDirectSound3DBuffer\n");
956 return E_NOINTERFACE;
959 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
960 ERR("app requested IDirectSound3DListener on secondary buffer\n");
961 return E_NOINTERFACE;
964 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
965 /* only supported on hardware 3D secondary buffers */
966 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) &&
967 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) &&
968 (This->dsbd.dwFlags & DSBCAPS_LOCHARDWARE) &&
969 (This->hwbuf != NULL) ) {
970 if (!This->iks)
971 IKsBufferPropertySetImpl_Create(This, &This->iks);
972 *ppobj = This->iks;
973 if (*ppobj) {
974 IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
975 return S_OK;
978 WARN("IID_IKsPropertySet\n");
979 return E_NOINTERFACE;
982 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
984 return E_NOINTERFACE;
987 static ICOM_VTABLE(IDirectSoundBuffer8) dsbvt =
989 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
990 IDirectSoundBufferImpl_QueryInterface,
991 IDirectSoundBufferImpl_AddRef,
992 IDirectSoundBufferImpl_Release,
993 IDirectSoundBufferImpl_GetCaps,
994 IDirectSoundBufferImpl_GetCurrentPosition,
995 IDirectSoundBufferImpl_GetFormat,
996 IDirectSoundBufferImpl_GetVolume,
997 IDirectSoundBufferImpl_GetPan,
998 IDirectSoundBufferImpl_GetFrequency,
999 IDirectSoundBufferImpl_GetStatus,
1000 IDirectSoundBufferImpl_Initialize,
1001 IDirectSoundBufferImpl_Lock,
1002 IDirectSoundBufferImpl_Play,
1003 IDirectSoundBufferImpl_SetCurrentPosition,
1004 IDirectSoundBufferImpl_SetFormat,
1005 IDirectSoundBufferImpl_SetVolume,
1006 IDirectSoundBufferImpl_SetPan,
1007 IDirectSoundBufferImpl_SetFrequency,
1008 IDirectSoundBufferImpl_Stop,
1009 IDirectSoundBufferImpl_Unlock,
1010 IDirectSoundBufferImpl_Restore,
1011 IDirectSoundBufferImpl_SetFX,
1012 IDirectSoundBufferImpl_AcquireResources,
1013 IDirectSoundBufferImpl_GetObjectInPath
1016 HRESULT WINAPI SecondaryBuffer_Create(
1017 IDirectSoundImpl *This,
1018 IDirectSoundBufferImpl **pdsb,
1019 LPDSBUFFERDESC dsbd)
1021 IDirectSoundBufferImpl *dsb;
1022 LPWAVEFORMATEX wfex = dsbd->lpwfxFormat;
1023 HRESULT err = DS_OK;
1024 DWORD capf = 0;
1025 int use_hw;
1027 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
1028 WARN("invalid parameter: dsbd->dwBufferBytes = %ld\n", dsbd->dwBufferBytes);
1029 *pdsb = NULL;
1030 return DSERR_INVALIDPARAM; /* FIXME: which error? */
1033 dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1035 if (dsb == 0) {
1036 WARN("out of memory\n");
1037 *pdsb = NULL;
1038 return DSERR_OUTOFMEMORY;
1040 dsb->ref = 0;
1041 dsb->dsound = This;
1042 dsb->lpVtbl = &dsbvt;
1044 memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
1045 if (wfex)
1046 memcpy(&dsb->wfx, wfex, sizeof(dsb->wfx));
1048 TRACE("Created buffer at %p\n", dsb);
1050 dsb->buflen = dsbd->dwBufferBytes;
1051 dsb->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1053 /* Check necessary hardware mixing capabilities */
1054 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
1055 else capf |= DSCAPS_SECONDARYMONO;
1056 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
1057 else capf |= DSCAPS_SECONDARY8BIT;
1058 use_hw = (This->drvcaps.dwFlags & capf) == capf;
1060 /* FIXME: check hardware sample rate mixing capabilities */
1061 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
1062 /* FIXME: check whether any hardware buffers are left */
1063 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
1065 /* Allocate system memory if applicable */
1066 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
1067 dsb->buffer = HeapAlloc(GetProcessHeap(),0,sizeof(*(dsb->buffer)));
1068 if (dsb->buffer == NULL) {
1069 WARN("out of memory\n");
1070 HeapFree(GetProcessHeap(),0,dsb);
1071 *pdsb = NULL;
1072 return DSERR_OUTOFMEMORY;
1075 dsb->buffer->memory = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
1076 if (dsb->buffer->memory == NULL) {
1077 WARN("out of memory\n");
1078 HeapFree(GetProcessHeap(),0,dsb->buffer);
1079 HeapFree(GetProcessHeap(),0,dsb);
1080 *pdsb = NULL;
1081 return DSERR_OUTOFMEMORY;
1085 /* Allocate the hardware buffer */
1086 if (use_hw) {
1087 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
1088 &(dsb->buflen),&(dsb->buffer->memory),
1089 (LPVOID*)&(dsb->hwbuf));
1090 if (err != DS_OK) {
1091 WARN("IDsDriver_CreateSoundBuffer failed\n");
1092 if (dsb->buffer->memory)
1093 HeapFree(GetProcessHeap(),0,dsb->buffer->memory);
1094 if (dsb->buffer)
1095 HeapFree(GetProcessHeap(),0,dsb->buffer);
1096 HeapFree(GetProcessHeap(),0,dsb);
1097 *pdsb = NULL;
1098 return err;
1102 /* calculate fragment size and write lead */
1103 DSOUND_RecalcFormat(dsb);
1105 /* It's not necessary to initialize values to zero since */
1106 /* we allocated this structure with HEAP_ZERO_MEMORY... */
1107 dsb->playpos = 0;
1108 dsb->buf_mixpos = 0;
1109 dsb->state = STATE_STOPPED;
1111 dsb->freqAdjust = (dsb->freq << DSOUND_FREQSHIFT) /
1112 This->wfx.nSamplesPerSec;
1113 dsb->nAvgBytesPerSec = dsb->freq *
1114 dsbd->lpwfxFormat->nBlockAlign;
1116 if (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
1117 dsb->ds3db_ds3db.dwSize = sizeof(DS3DBUFFER);
1118 dsb->ds3db_ds3db.vPosition.u1.x = 0.0;
1119 dsb->ds3db_ds3db.vPosition.u2.y = 0.0;
1120 dsb->ds3db_ds3db.vPosition.u3.z = 0.0;
1121 dsb->ds3db_ds3db.vVelocity.u1.x = 0.0;
1122 dsb->ds3db_ds3db.vVelocity.u2.y = 0.0;
1123 dsb->ds3db_ds3db.vVelocity.u3.z = 0.0;
1124 dsb->ds3db_ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1125 dsb->ds3db_ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1126 dsb->ds3db_ds3db.vConeOrientation.u1.x = 0.0;
1127 dsb->ds3db_ds3db.vConeOrientation.u2.y = 0.0;
1128 dsb->ds3db_ds3db.vConeOrientation.u3.z = 0.0;
1129 dsb->ds3db_ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1130 dsb->ds3db_ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1131 dsb->ds3db_ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1132 dsb->ds3db_ds3db.dwMode = DS3DMODE_NORMAL;
1134 dsb->ds3db_need_recalc = FALSE;
1135 DSOUND_Calc3DBuffer(dsb);
1136 } else
1137 DSOUND_RecalcVolPan(&(dsb->volpan));
1139 InitializeCriticalSection(&(dsb->lock));
1141 /* register buffer */
1142 RtlAcquireResourceExclusive(&(This->lock), TRUE);
1143 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1144 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
1145 if (newbuffers) {
1146 This->buffers = newbuffers;
1147 This->buffers[This->nrofbuffers] = dsb;
1148 This->nrofbuffers++;
1149 TRACE("buffer count is now %d\n", This->nrofbuffers);
1150 } else {
1151 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
1152 if (dsb->buffer->memory)
1153 HeapFree(GetProcessHeap(),0,dsb->buffer->memory);
1154 if (dsb->buffer)
1155 HeapFree(GetProcessHeap(),0,dsb->buffer);
1156 DeleteCriticalSection(&(dsb->lock));
1157 RtlReleaseResource(&(This->lock));
1158 HeapFree(GetProcessHeap(),0,dsb);
1159 *pdsb = NULL;
1160 return DSERR_OUTOFMEMORY;
1163 RtlReleaseResource(&(This->lock));
1164 IDirectSound8_AddRef((LPDIRECTSOUND8)This);
1165 *pdsb = dsb;
1166 return S_OK;