Don't let the primary buffer's reference count go less than 0
[dsound-openal.git] / primary.c
blobc305ea147c3864645a7b9898d92fcb0c34254ae7
1 /* DirectSound COM interface
3 * Copyright 2009 Maarten Lankhorst
5 * Some code taken from the original dsound-openal implementation
6 * Copyright 2007-2009 Chris Robinson
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define CONST_VTABLE
24 #include <stdarg.h>
25 #include <string.h>
27 #include <windows.h>
28 #include <dsound.h>
29 #include <mmsystem.h>
31 #include "dsound_private.h"
33 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
34 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
36 #ifndef E_PROP_ID_UNSUPPORTED
37 #define E_PROP_ID_UNSUPPORTED ((HRESULT)0x80070490)
38 #endif
41 static const IDirectSoundBufferVtbl DS8Primary_Vtbl;
42 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl;
43 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl;
46 static inline DS8Primary *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
48 return CONTAINING_RECORD(iface, DS8Primary, IDirectSoundBuffer_iface);
51 static inline DS8Primary *impl_from_IDirectSound3DListener(IDirectSound3DListener *iface)
53 return CONTAINING_RECORD(iface, DS8Primary, IDirectSound3DListener_iface);
56 static inline DS8Primary *impl_from_IKsPropertySet(IKsPropertySet *iface)
58 return CONTAINING_RECORD(iface, DS8Primary, IKsPropertySet_iface);
62 static void AL_APIENTRY wrap_DeferUpdates(void)
63 { alcSuspendContext(alcGetCurrentContext()); }
64 static void AL_APIENTRY wrap_ProcessUpdates(void)
65 { alcProcessContext(alcGetCurrentContext()); }
68 static void trigger_elapsed_notifies(DS8Buffer *buf, DWORD lastpos, DWORD curpos)
70 DSBPOSITIONNOTIFY *not = buf->notify;
71 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
72 for(;not != not_end;++not)
74 HANDLE event = not->hEventNotify;
75 DWORD ofs = not->dwOffset;
77 if(ofs == (DWORD)DSBPN_OFFSETSTOP)
78 continue;
80 if(curpos < lastpos) /* Wraparound case */
82 if(ofs < curpos || ofs >= lastpos)
84 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
85 SetEvent(event);
88 else if(ofs >= lastpos && ofs < curpos) /* Normal case */
90 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
91 SetEvent(event);
96 static void trigger_stop_notifies(DS8Buffer *buf)
98 DSBPOSITIONNOTIFY *not = buf->notify;
99 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
100 for(;not != not_end;++not)
102 if(not->dwOffset != (DWORD)DSBPN_OFFSETSTOP)
103 continue;
104 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
105 SetEvent(not->hEventNotify);
109 void DS8Primary_triggernots(DS8Primary *prim)
111 DS8Buffer **curnot, **endnot;
113 curnot = prim->notifies;
114 endnot = curnot + prim->nnotifies;
115 while(curnot != endnot)
117 DS8Buffer *buf = *curnot;
118 DS8Data *data = buf->buffer;
119 DWORD curpos = buf->lastpos;
120 ALint state = 0;
121 ALint ofs;
123 alGetSourcei(buf->source, AL_BYTE_OFFSET, &ofs);
124 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
125 if(buf->segsize == 0)
126 curpos = (state == AL_STOPPED) ? data->buf_size : ofs;
127 else
129 if(state != AL_STOPPED)
130 curpos = ofs + buf->queue_base;
131 else
133 ALint queued;
134 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
135 curpos = buf->segsize*queued + buf->queue_base;
138 if(curpos >= (DWORD)data->buf_size)
140 if(buf->islooping)
141 curpos %= (DWORD)data->buf_size;
142 else if(buf->isplaying)
144 curpos = data->buf_size;
145 alSourceStop(buf->source);
146 alSourcei(buf->source, AL_BUFFER, 0);
147 buf->curidx = 0;
148 buf->isplaying = FALSE;
152 if(state != AL_PLAYING)
153 state = buf->isplaying ? AL_PLAYING : AL_PAUSED;
155 checkALError();
157 if(buf->lastpos != curpos)
159 trigger_elapsed_notifies(buf, buf->lastpos, curpos);
160 buf->lastpos = curpos;
162 if(state != AL_PLAYING)
164 /* Remove this buffer from list and put another at the current
165 * position; don't increment i
167 trigger_stop_notifies(buf);
168 *curnot = *(--endnot);
169 prim->nnotifies--;
170 continue;
172 curnot++;
174 checkALError();
177 static void do_buffer_stream(DS8Buffer *buf, BYTE *scratch_mem)
179 DS8Data *data = buf->buffer;
180 ALint ofs, done = 0, queued = QBUFFERS, state = AL_PLAYING;
181 ALuint which;
183 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
184 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
185 alGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
187 if(done > 0)
189 ALuint bids[QBUFFERS];
190 queued -= done;
192 alSourceUnqueueBuffers(buf->source, done, bids);
193 buf->queue_base = (buf->queue_base + buf->segsize*done) % data->buf_size;
195 while(queued < QBUFFERS)
197 which = buf->stream_bids[buf->curidx];
198 ofs = buf->data_offset;
200 if(buf->segsize < data->buf_size - ofs)
202 alBufferData(which, data->buf_format, data->data + ofs, buf->segsize,
203 data->format.Format.nSamplesPerSec);
204 buf->data_offset = ofs + buf->segsize;
206 else if(buf->islooping)
208 ALsizei rem = data->buf_size - ofs;
209 if(rem > 2048) rem = 2048;
211 memcpy(scratch_mem, data->data + ofs, rem);
212 while(rem < buf->segsize)
214 ALsizei todo = buf->segsize - rem;
215 if(todo > data->buf_size)
216 todo = data->buf_size;
217 memcpy(scratch_mem + rem, data->data, todo);
218 rem += todo;
220 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
221 data->format.Format.nSamplesPerSec);
222 buf->data_offset = (ofs+buf->segsize) % data->buf_size;
224 else
226 ALsizei rem = data->buf_size - ofs;
227 if(rem > 2048) rem = 2048;
228 if(rem == 0) break;
230 memcpy(scratch_mem, data->data + ofs, rem);
231 memset(scratch_mem+rem, (data->format.Format.wBitsPerSample==8) ? 128 : 0,
232 buf->segsize - rem);
233 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
234 data->format.Format.nSamplesPerSec);
235 buf->data_offset = data->buf_size;
238 alSourceQueueBuffers(buf->source, 1, &which);
239 buf->curidx = (buf->curidx+1)%QBUFFERS;
240 queued++;
243 if(!queued)
245 buf->data_offset = 0;
246 buf->queue_base = data->buf_size;
247 buf->curidx = 0;
248 buf->isplaying = FALSE;
250 else if(state != AL_PLAYING)
251 alSourcePlay(buf->source);
254 void DS8Primary_streamfeeder(DS8Primary *prim, BYTE *scratch_mem)
256 /* OpenAL doesn't support our lovely buffer extensions so just make sure
257 * enough buffers are queued for streaming
259 if(prim->write_emu)
261 DS8Buffer *buf = &prim->writable_buf;
262 if(buf->segsize != 0 && buf->isplaying)
263 do_buffer_stream(buf, scratch_mem);
265 else
267 struct DSBufferGroup *bufgroup = prim->BufferGroups;
268 struct DSBufferGroup *endgroup = bufgroup + prim->NumBufferGroups;
269 for(;bufgroup != endgroup;++bufgroup)
271 DWORD64 usemask = ~bufgroup->FreeBuffers;
272 while(usemask)
274 int idx = CTZ64(usemask);
275 DS8Buffer *buf = bufgroup->Buffers + idx;
276 usemask &= ~(U64(1) << idx);
278 if(buf->segsize != 0 && buf->isplaying)
279 do_buffer_stream(buf, scratch_mem);
283 checkALError();
287 HRESULT DS8Primary_PreInit(DS8Primary *This, DS8Impl *parent)
289 DS3DLISTENER *listener;
290 WAVEFORMATEX *wfx;
291 DWORD num_srcs;
292 DWORD count;
293 HRESULT hr;
294 DWORD i;
296 This->IDirectSoundBuffer_iface.lpVtbl = &DS8Primary_Vtbl;
297 This->IDirectSound3DListener_iface.lpVtbl = &DS8Primary3D_Vtbl;
298 This->IKsPropertySet_iface.lpVtbl = &DS8PrimaryProp_Vtbl;
300 This->parent = parent;
301 This->crst = &parent->share->crst;
302 This->ctx = parent->share->ctx;
303 This->refresh = parent->share->refresh;
304 This->SupportedExt = parent->share->SupportedExt;
305 This->ExtAL = &parent->share->ExtAL;
306 This->sources = parent->share->sources;
307 This->auxslot = parent->share->auxslot;
309 wfx = &This->format.Format;
310 wfx->wFormatTag = WAVE_FORMAT_PCM;
311 wfx->nChannels = 2;
312 wfx->wBitsPerSample = 8;
313 wfx->nSamplesPerSec = 22050;
314 wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
315 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
316 wfx->cbSize = 0;
318 This->stopped = TRUE;
320 /* Apparently primary buffer size is always 32k,
321 * tested on windows with 192k 24 bits sound @ 6 channels
322 * where it will run out in 60 ms and it isn't pointer aligned
324 This->buf_size = 32768;
326 setALContext(This->ctx);
327 if(This->SupportedExt[SOFT_DEFERRED_UPDATES])
329 This->DeferUpdates = This->ExtAL->DeferUpdatesSOFT;
330 This->ProcessUpdates = This->ExtAL->ProcessUpdatesSOFT;
332 else
334 This->DeferUpdates = wrap_DeferUpdates;
335 This->ProcessUpdates = wrap_ProcessUpdates;
338 This->eax_prop = EnvironmentDefaults[EAX_ENVIRONMENT_GENERIC];
339 if(This->SupportedExt[EXT_EFX] && This->auxslot != 0)
341 ALint revid = alGetEnumValue("AL_EFFECT_REVERB");
342 if(revid != 0 && revid != -1)
344 This->ExtAL->GenEffects(1, &This->effect);
345 This->ExtAL->Effecti(This->effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
346 checkALError();
349 popALContext();
351 listener = &This->params;
352 listener->dwSize = sizeof(This->params);
353 listener->vPosition.x = 0.0f;
354 listener->vPosition.y = 0.0f;
355 listener->vPosition.z = 0.0f;
356 listener->vVelocity.x = 0.0f;
357 listener->vVelocity.y = 0.0f;
358 listener->vVelocity.z = 0.0f;
359 listener->vOrientFront.x = 0.0f;
360 listener->vOrientFront.y = 0.0f;
361 listener->vOrientFront.z = 1.0f;
362 listener->vOrientTop.x = 0.0f;
363 listener->vOrientTop.y = 1.0f;
364 listener->vOrientTop.z = 0.0f;
365 listener->flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
366 listener->flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
367 listener->flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
369 num_srcs = parent->share->max_sources;
371 hr = DSERR_OUTOFMEMORY;
372 This->notifies = HeapAlloc(GetProcessHeap(), 0, num_srcs*sizeof(*This->notifies));
373 if(!This->notifies) goto fail;
374 This->sizenotifies = num_srcs;
376 count = (num_srcs+63) / 64;
377 This->BufferGroups = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
378 count*sizeof(*This->BufferGroups));
379 if(!This->BufferGroups) goto fail;
380 This->NumBufferGroups = count;
382 /* Only flag usable buffers as free. */
383 count = 0;
384 for(i = 0;i < This->NumBufferGroups;++i)
386 DWORD count_rem = num_srcs - count;
387 if(count_rem >= 64)
389 This->BufferGroups[i].FreeBuffers = ~(DWORD64)0;
390 count += 64;
392 else
394 This->BufferGroups[i].FreeBuffers = (U64(1) << count_rem) - 1;
395 count += count_rem;
399 return S_OK;
401 fail:
402 DS8Primary_Clear(This);
403 return hr;
406 void DS8Primary_Clear(DS8Primary *This)
408 struct DSBufferGroup *bufgroup;
409 DWORD i;
411 TRACE("Clearing primary %p\n", This);
413 if(!This->parent)
414 return;
416 setALContext(This->ctx);
417 if(This->effect)
418 This->ExtAL->DeleteEffects(1, &This->effect);
419 popALContext();
421 bufgroup = This->BufferGroups;
422 for(i = 0;i < This->NumBufferGroups;++i)
424 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
425 while(usemask)
427 int idx = CTZ64(usemask);
428 DS8Buffer *buf = bufgroup[i].Buffers + idx;
429 usemask &= ~(U64(1) << idx);
431 DS8Buffer_Destroy(buf);
435 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
436 HeapFree(GetProcessHeap(), 0, This->notifies);
437 memset(This, 0, sizeof(*This));
440 static HRESULT WINAPI DS8Primary_QueryInterface(IDirectSoundBuffer *iface, REFIID riid, LPVOID *ppv)
442 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
444 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
446 *ppv = NULL;
447 if(IsEqualIID(riid, &IID_IUnknown) ||
448 IsEqualIID(riid, &IID_IDirectSoundBuffer))
449 *ppv = &This->IDirectSoundBuffer_iface;
450 else if(IsEqualIID(riid, &IID_IDirectSound3DListener))
452 if((This->flags&DSBCAPS_CTRL3D))
453 *ppv = &This->IDirectSound3DListener_iface;
455 else if(IsEqualIID(riid, &IID_IKsPropertySet))
456 *ppv = &This->IKsPropertySet_iface;
457 else
458 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
460 if(*ppv)
462 IUnknown_AddRef((IUnknown*)*ppv);
463 return S_OK;
466 return E_NOINTERFACE;
469 static ULONG WINAPI DS8Primary_AddRef(IDirectSoundBuffer *iface)
471 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
472 LONG ret;
474 ret = InterlockedIncrement(&This->ref);
475 if(ret == 1) This->flags = 0;
477 return ret;
480 static ULONG WINAPI DS8Primary_Release(IDirectSoundBuffer *iface)
482 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
483 LONG ref, oldval;
485 oldval = *(volatile LONG*)&This->ref;
486 do {
487 ref = oldval;
488 if(!ref) return 0;
489 oldval = InterlockedCompareExchange(&This->ref, ref-1, ref);
490 } while(oldval != ref);
492 return ref-1;
495 static HRESULT WINAPI DS8Primary_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
497 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
499 TRACE("(%p)->(%p)\n", iface, caps);
501 if(!caps || caps->dwSize < sizeof(*caps))
503 WARN("Invalid DSBCAPS (%p, %lu)\n", caps, caps ? caps->dwSize : 0);
504 return DSERR_INVALIDPARAM;
507 caps->dwFlags = This->flags;
508 caps->dwBufferBytes = This->buf_size;
509 caps->dwUnlockTransferRate = 0;
510 caps->dwPlayCpuOverhead = 0;
512 return DS_OK;
515 static HRESULT WINAPI DS8Primary_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *curpos)
517 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
518 HRESULT hr = DSERR_PRIOLEVELNEEDED;
520 EnterCriticalSection(This->crst);
521 if(This->write_emu)
522 hr = IDirectSoundBuffer8_GetCurrentPosition(This->write_emu, playpos, curpos);
523 LeaveCriticalSection(This->crst);
525 return hr;
528 static HRESULT WINAPI DS8Primary_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
530 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
531 HRESULT hr = S_OK;
532 UINT size;
534 if(!wfx && !written)
536 WARN("Cannot report format or format size\n");
537 return DSERR_INVALIDPARAM;
540 EnterCriticalSection(This->crst);
541 size = sizeof(This->format.Format) + This->format.Format.cbSize;
542 if(written)
543 *written = size;
544 if(wfx)
546 if(allocated < size)
547 hr = DSERR_INVALIDPARAM;
548 else
549 memcpy(wfx, &This->format.Format, size);
551 LeaveCriticalSection(This->crst);
553 return hr;
556 static HRESULT WINAPI DS8Primary_GetVolume(IDirectSoundBuffer *iface, LONG *volume)
558 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
559 ALfloat gain;
561 TRACE("(%p)->(%p)\n", iface, volume);
563 if(!volume)
564 return DSERR_INVALIDPARAM;
565 *volume = 0;
567 if(!(This->flags&DSBCAPS_CTRLVOLUME))
568 return DSERR_CONTROLUNAVAIL;
570 setALContext(This->ctx);
571 alGetListenerf(AL_GAIN, &gain);
572 checkALError();
573 popALContext();
575 *volume = clampI(gain_to_mB(gain), DSBVOLUME_MIN, DSBVOLUME_MAX);
576 return DS_OK;
579 static HRESULT WINAPI DS8Primary_GetPan(IDirectSoundBuffer *iface, LONG *pan)
581 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
582 HRESULT hr = DS_OK;
584 WARN("(%p)->(%p): semi-stub\n", iface, pan);
586 if(!pan)
587 return DSERR_INVALIDPARAM;
589 EnterCriticalSection(This->crst);
590 if(This->write_emu)
591 hr = IDirectSoundBuffer8_GetPan(This->write_emu, pan);
592 else if(!(This->flags & DSBCAPS_CTRLPAN))
593 hr = DSERR_CONTROLUNAVAIL;
594 else
595 *pan = 0;
596 LeaveCriticalSection(This->crst);
598 return hr;
601 static HRESULT WINAPI DS8Primary_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
603 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
604 HRESULT hr = DS_OK;
606 WARN("(%p)->(%p): semi-stub\n", iface, freq);
608 if(!freq)
609 return DSERR_INVALIDPARAM;
611 if(!(This->flags&DSBCAPS_CTRLFREQUENCY))
612 return DSERR_CONTROLUNAVAIL;
614 EnterCriticalSection(This->crst);
615 *freq = This->format.Format.nSamplesPerSec;
616 LeaveCriticalSection(This->crst);
618 return hr;
621 static HRESULT WINAPI DS8Primary_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
623 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
625 TRACE("(%p)->(%p)\n", iface, status);
627 if(!status)
628 return DSERR_INVALIDPARAM;
630 EnterCriticalSection(This->crst);
631 *status = DSBSTATUS_PLAYING|DSBSTATUS_LOOPING;
632 if((This->flags&DSBCAPS_LOCDEFER))
633 *status |= DSBSTATUS_LOCHARDWARE;
635 if(This->stopped)
637 struct DSBufferGroup *bufgroup = This->BufferGroups;
638 DWORD i, state = 0;
639 HRESULT hr;
641 for(i = 0;i < This->NumBufferGroups;++i)
643 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
644 while(usemask)
646 int idx = CTZ64(usemask);
647 DS8Buffer *buf = bufgroup[i].Buffers + idx;
648 usemask &= ~(U64(1) << idx);
650 hr = DS8Buffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state);
651 if(SUCCEEDED(hr) && (state&DSBSTATUS_PLAYING)) break;
654 if(!(state&DSBSTATUS_PLAYING))
656 /* Primary stopped and no buffers playing.. */
657 *status = 0;
660 LeaveCriticalSection(This->crst);
662 return DS_OK;
665 HRESULT WINAPI DS8Primary_Initialize(IDirectSoundBuffer *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
667 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
668 HRESULT hr;
670 TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
672 if(!desc || desc->lpwfxFormat || desc->dwBufferBytes)
674 WARN("Bad DSBDESC for primary buffer\n");
675 return DSERR_INVALIDPARAM;
677 if((desc->dwFlags&DSBCAPS_CTRLFX) ||
678 (desc->dwFlags&DSBCAPS_CTRLPOSITIONNOTIFY) ||
679 (desc->dwFlags&DSBCAPS_LOCSOFTWARE))
681 WARN("Bad dwFlags %08lx\n", desc->dwFlags);
682 return DSERR_INVALIDPARAM;
685 /* Should be 0 if not initialized */
686 if(This->flags)
687 return DSERR_ALREADYINITIALIZED;
689 hr = DS_OK;
690 if(This->parent->prio_level == DSSCL_WRITEPRIMARY)
692 DSBUFFERDESC emudesc;
693 DS8Buffer *emu;
695 if(This->write_emu)
697 ERR("There shouldn't be a write_emu!\n");
698 IDirectSoundBuffer8_Release(This->write_emu);
699 This->write_emu = NULL;
702 memset(&emudesc, 0, sizeof(emudesc));
703 emudesc.dwSize = sizeof(emudesc);
704 emudesc.dwFlags = DSBCAPS_LOCHARDWARE | (desc->dwFlags&DSBCAPS_CTRLPAN);
705 /* Dont play last incomplete sample */
706 emudesc.dwBufferBytes = This->buf_size - (This->buf_size%This->format.Format.nBlockAlign);
707 emudesc.lpwfxFormat = &This->format.Format;
709 hr = DS8Buffer_Create(&emu, This, NULL, TRUE);
710 if(SUCCEEDED(hr))
712 This->write_emu = &emu->IDirectSoundBuffer8_iface;
713 hr = DS8Buffer_Initialize(This->write_emu, ds, &emudesc);
714 if(FAILED(hr))
716 IDirectSoundBuffer8_Release(This->write_emu);
717 This->write_emu = NULL;
722 if(SUCCEEDED(hr))
723 This->flags = desc->dwFlags | DSBCAPS_LOCHARDWARE;
724 return hr;
727 static HRESULT WINAPI DS8Primary_Lock(IDirectSoundBuffer *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
729 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
730 HRESULT hr = DSERR_PRIOLEVELNEEDED;
732 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %lu)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
734 EnterCriticalSection(This->crst);
735 if(This->write_emu)
736 hr = IDirectSoundBuffer8_Lock(This->write_emu, ofs, bytes, ptr1, len1, ptr2, len2, flags);
737 LeaveCriticalSection(This->crst);
739 return hr;
742 static HRESULT WINAPI DS8Primary_Play(IDirectSoundBuffer *iface, DWORD res1, DWORD res2, DWORD flags)
744 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
745 HRESULT hr;
747 TRACE("(%p)->(%lu, %lu, %lu)\n", iface, res1, res2, flags);
749 if(!(flags & DSBPLAY_LOOPING))
751 WARN("Flags (%08lx) not set to DSBPLAY_LOOPING\n", flags);
752 return DSERR_INVALIDPARAM;
755 EnterCriticalSection(This->crst);
756 hr = S_OK;
757 if(This->write_emu)
758 hr = IDirectSoundBuffer8_Play(This->write_emu, res1, res2, flags);
759 if(SUCCEEDED(hr))
760 This->stopped = FALSE;
761 LeaveCriticalSection(This->crst);
763 return hr;
766 static HRESULT WINAPI DS8Primary_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD pos)
768 WARN("(%p)->(%lu)\n", iface, pos);
769 return DSERR_INVALIDCALL;
772 /* Just assume the format is crap, and clean up the damage */
773 static HRESULT copy_waveformat(WAVEFORMATEX *wfx, const WAVEFORMATEX *from)
775 if(from->nBlockAlign <= 0 || from->nChannels <= 0)
776 return DSERR_INVALIDPARAM;
777 if(from->nSamplesPerSec < DSBFREQUENCY_MIN || from->nSamplesPerSec > DSBFREQUENCY_MAX)
778 return DSERR_INVALIDPARAM;
779 if(from->wBitsPerSample == 0 || (from->wBitsPerSample%8) != 0)
780 return DSERR_INVALIDPARAM;
781 if(from->nBlockAlign != from->nChannels*from->wBitsPerSample/8)
782 return DSERR_INVALIDPARAM;
783 if(from->nAvgBytesPerSec != from->nBlockAlign*from->nSamplesPerSec)
784 return DSERR_INVALIDPARAM;
786 if(from->wFormatTag == WAVE_FORMAT_PCM)
788 if(from->wBitsPerSample > 32)
789 return DSERR_INVALIDPARAM;
790 wfx->cbSize = 0;
791 wfx->wBitsPerSample = from->wBitsPerSample;
793 else if(from->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
795 if(from->wBitsPerSample != 32)
796 return DSERR_INVALIDPARAM;
797 wfx->cbSize = 0;
798 wfx->wBitsPerSample = from->wBitsPerSample;
800 else if(from->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
802 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
803 const WAVEFORMATEXTENSIBLE *fromx = (const WAVEFORMATEXTENSIBLE*)from;
804 const WORD size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
806 /* Fail silently.. */
807 if(from->cbSize < size) return DS_OK;
808 if(fromx->Samples.wValidBitsPerSample > fromx->Format.wBitsPerSample)
809 return DSERR_INVALIDPARAM;
811 if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
812 IsEqualGUID(&wfe->SubFormat, &GUID_NULL))
814 if(from->wBitsPerSample > 32)
815 return DSERR_INVALIDPARAM;
817 else if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
819 if(from->wBitsPerSample != 32)
820 return DSERR_INVALIDPARAM;
822 else
824 ERR("Unhandled extensible format: %s\n", debugstr_guid(&wfe->SubFormat));
825 return DSERR_INVALIDPARAM;
828 wfe->Format.wBitsPerSample = from->wBitsPerSample;
829 wfe->Samples.wValidBitsPerSample = fromx->Samples.wValidBitsPerSample;
830 if(!wfe->Samples.wValidBitsPerSample)
831 wfe->Samples.wValidBitsPerSample = wfe->Format.wBitsPerSample;
832 wfe->Format.cbSize = size;
833 wfe->dwChannelMask = fromx->dwChannelMask;
834 wfe->SubFormat = fromx->SubFormat;
836 else
838 ERR("Unhandled format tag %04x\n", from->wFormatTag);
839 return DSERR_INVALIDPARAM;
842 wfx->nChannels = from->nChannels;
843 wfx->wFormatTag = from->wFormatTag;
844 wfx->nSamplesPerSec = from->nSamplesPerSec;
845 wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
846 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
847 return DS_OK;
850 static HRESULT WINAPI DS8Primary_SetFormat(IDirectSoundBuffer *iface, const WAVEFORMATEX *wfx)
852 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
853 HRESULT hr = S_OK;
855 TRACE("(%p)->(%p)\n", iface, wfx);
857 if(!wfx)
859 WARN("Missing format\n");
860 return DSERR_INVALIDPARAM;
863 EnterCriticalSection(This->crst);
865 if(This->parent->prio_level < DSSCL_PRIORITY)
867 hr = DSERR_PRIOLEVELNEEDED;
868 goto out;
871 TRACE("Requested primary format:\n"
872 " FormatTag = %04x\n"
873 " Channels = %u\n"
874 " SamplesPerSec = %lu\n"
875 " AvgBytesPerSec = %lu\n"
876 " BlockAlign = %u\n"
877 " BitsPerSample = %u\n",
878 wfx->wFormatTag, wfx->nChannels,
879 wfx->nSamplesPerSec, wfx->nAvgBytesPerSec,
880 wfx->nBlockAlign, wfx->wBitsPerSample);
882 hr = copy_waveformat(&This->format.Format, wfx);
883 if(SUCCEEDED(hr) && This->write_emu)
885 DS8Buffer *buf;
886 DSBUFFERDESC desc;
888 memset(&desc, 0, sizeof(desc));
889 desc.dwSize = sizeof(desc);
890 desc.dwFlags = DSBCAPS_LOCHARDWARE|DSBCAPS_CTRLPAN;
891 desc.dwBufferBytes = This->buf_size - (This->buf_size % This->format.Format.nBlockAlign);
892 desc.lpwfxFormat = &This->format.Format;
894 hr = DS8Buffer_Create(&buf, This, NULL, TRUE);
895 if(FAILED(hr)) goto out;
897 hr = DS8Buffer_Initialize(&buf->IDirectSoundBuffer8_iface, &This->parent->IDirectSound_iface, &desc);
898 if(FAILED(hr))
899 DS8Buffer_Destroy(buf);
900 else
902 IDirectSoundBuffer8_Release(This->write_emu);
903 This->write_emu = &buf->IDirectSoundBuffer8_iface;
907 out:
908 LeaveCriticalSection(This->crst);
909 return hr;
912 static HRESULT WINAPI DS8Primary_SetVolume(IDirectSoundBuffer *iface, LONG vol)
914 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
916 TRACE("(%p)->(%ld)\n", iface, vol);
918 if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
920 WARN("Invalid volume (%ld)\n", vol);
921 return DSERR_INVALIDPARAM;
924 if(!(This->flags&DSBCAPS_CTRLVOLUME))
925 return DSERR_CONTROLUNAVAIL;
927 setALContext(This->ctx);
928 alListenerf(AL_GAIN, mB_to_gain(vol));
929 popALContext();
931 return DS_OK;
934 static HRESULT WINAPI DS8Primary_SetPan(IDirectSoundBuffer *iface, LONG pan)
936 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
937 HRESULT hr;
939 TRACE("(%p)->(%ld)\n", iface, pan);
941 if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
943 WARN("invalid parameter: pan = %ld\n", pan);
944 return DSERR_INVALIDPARAM;
947 EnterCriticalSection(This->crst);
948 if(!(This->flags&DSBCAPS_CTRLPAN))
950 WARN("control unavailable\n");
951 hr = DSERR_CONTROLUNAVAIL;
953 else if(This->write_emu)
954 hr = IDirectSoundBuffer8_SetPan(This->write_emu, pan);
955 else
957 FIXME("Not supported\n");
958 hr = E_NOTIMPL;
960 LeaveCriticalSection(This->crst);
962 return hr;
965 static HRESULT WINAPI DS8Primary_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
967 WARN("(%p)->(%lu)\n", iface, freq);
968 return DSERR_CONTROLUNAVAIL;
971 static HRESULT WINAPI DS8Primary_Stop(IDirectSoundBuffer *iface)
973 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
974 HRESULT hr = S_OK;
976 TRACE("(%p)->()\n", iface);
978 EnterCriticalSection(This->crst);
979 if(This->write_emu)
980 hr = IDirectSoundBuffer8_Stop(This->write_emu);
981 if(SUCCEEDED(hr))
982 This->stopped = TRUE;
983 LeaveCriticalSection(This->crst);
985 return hr;
988 static HRESULT WINAPI DS8Primary_Unlock(IDirectSoundBuffer *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
990 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
991 HRESULT hr = DSERR_INVALIDCALL;
993 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
995 EnterCriticalSection(This->crst);
996 if(This->write_emu)
997 hr = IDirectSoundBuffer8_Unlock(This->write_emu, ptr1, len1, ptr2, len2);
998 LeaveCriticalSection(This->crst);
1000 return hr;
1003 static HRESULT WINAPI DS8Primary_Restore(IDirectSoundBuffer *iface)
1005 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
1006 HRESULT hr = S_OK;
1008 TRACE("(%p)->()\n", iface);
1010 EnterCriticalSection(This->crst);
1011 if(This->write_emu)
1012 hr = IDirectSoundBuffer8_Restore(This->write_emu);
1013 LeaveCriticalSection(This->crst);
1015 return hr;
1018 static const IDirectSoundBufferVtbl DS8Primary_Vtbl =
1020 DS8Primary_QueryInterface,
1021 DS8Primary_AddRef,
1022 DS8Primary_Release,
1023 DS8Primary_GetCaps,
1024 DS8Primary_GetCurrentPosition,
1025 DS8Primary_GetFormat,
1026 DS8Primary_GetVolume,
1027 DS8Primary_GetPan,
1028 DS8Primary_GetFrequency,
1029 DS8Primary_GetStatus,
1030 DS8Primary_Initialize,
1031 DS8Primary_Lock,
1032 DS8Primary_Play,
1033 DS8Primary_SetCurrentPosition,
1034 DS8Primary_SetFormat,
1035 DS8Primary_SetVolume,
1036 DS8Primary_SetPan,
1037 DS8Primary_SetFrequency,
1038 DS8Primary_Stop,
1039 DS8Primary_Unlock,
1040 DS8Primary_Restore
1044 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags)
1046 union PrimaryParamFlags dirty = { flags };
1047 DWORD i;
1049 if(dirty.bit.pos)
1050 alListener3f(AL_POSITION, params->vPosition.x, params->vPosition.y,
1051 -params->vPosition.z);
1052 if(dirty.bit.vel)
1053 alListener3f(AL_VELOCITY, params->vVelocity.x, params->vVelocity.y,
1054 -params->vVelocity.z);
1055 if(dirty.bit.orientation)
1057 ALfloat orient[6] = {
1058 params->vOrientFront.x, params->vOrientFront.y, -params->vOrientFront.z,
1059 params->vOrientTop.x, params->vOrientTop.y, -params->vOrientTop.z
1061 alListenerfv(AL_ORIENTATION, orient);
1063 if(dirty.bit.distancefactor)
1065 alSpeedOfSound(343.3f/params->flDistanceFactor);
1066 if(This->SupportedExt[EXT_EFX])
1067 alListenerf(AL_METERS_PER_UNIT, params->flDistanceFactor);
1069 if(dirty.bit.rollofffactor)
1071 struct DSBufferGroup *bufgroup = This->BufferGroups;
1072 ALfloat rolloff = params->flRolloffFactor;
1073 This->rollofffactor = rolloff;
1075 for(i = 0;i < This->NumBufferGroups;++i)
1077 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1078 while(usemask)
1080 int idx = CTZ64(usemask);
1081 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1082 usemask &= ~(U64(1) << idx);
1084 if(buf->ds3dmode != DS3DMODE_DISABLE)
1085 alSourcef(buf->source, AL_ROLLOFF_FACTOR, rolloff);
1089 if(dirty.bit.dopplerfactor)
1090 alDopplerFactor(params->flDopplerFactor);
1091 if(dirty.bit.effect)
1092 This->ExtAL->AuxiliaryEffectSloti(This->auxslot, AL_EFFECTSLOT_EFFECT, This->effect);
1095 static HRESULT WINAPI DS8Primary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1097 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1098 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1101 static ULONG WINAPI DS8Primary3D_AddRef(IDirectSound3DListener *iface)
1103 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1104 LONG ret;
1106 ret = InterlockedIncrement(&This->ds3d_ref);
1107 TRACE("new refcount %ld\n", ret);
1109 return ret;
1112 static ULONG WINAPI DS8Primary3D_Release(IDirectSound3DListener *iface)
1114 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1115 LONG ret;
1117 ret = InterlockedDecrement(&This->ds3d_ref);
1118 TRACE("new refcount %ld\n", ret);
1120 return ret;
1124 static HRESULT WINAPI DS8Primary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1126 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1128 TRACE("(%p)->(%p)\n", iface, distancefactor);
1130 if(!distancefactor)
1132 WARN("Invalid parameter %p\n", distancefactor);
1133 return DSERR_INVALIDPARAM;
1136 setALContext(This->ctx);
1137 *distancefactor = 343.3f/alGetFloat(AL_SPEED_OF_SOUND);
1138 checkALError();
1139 popALContext();
1141 return S_OK;
1144 static HRESULT WINAPI DS8Primary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1146 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1148 TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1150 if(!dopplerfactor)
1152 WARN("Invalid parameter %p\n", dopplerfactor);
1153 return DSERR_INVALIDPARAM;
1156 setALContext(This->ctx);
1157 *dopplerfactor = alGetFloat(AL_DOPPLER_FACTOR);
1158 checkALError();
1159 popALContext();
1161 return S_OK;
1164 static HRESULT WINAPI DS8Primary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1166 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1167 ALfloat orient[6];
1169 TRACE("(%p)->(%p, %p)\n", iface, front, top);
1171 if(!front || !top)
1173 WARN("Invalid parameter %p %p\n", front, top);
1174 return DSERR_INVALIDPARAM;
1177 setALContext(This->ctx);
1178 alGetListenerfv(AL_ORIENTATION, orient);
1179 checkALError();
1180 popALContext();
1182 front->x = orient[0];
1183 front->y = orient[1];
1184 front->z = -orient[2];
1185 top->x = orient[3];
1186 top->y = orient[4];
1187 top->z = -orient[5];
1188 return S_OK;
1191 static HRESULT WINAPI DS8Primary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1193 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1194 ALfloat alpos[3];
1196 TRACE("(%p)->(%p)\n", iface, pos);
1198 if(!pos)
1200 WARN("Invalid parameter %p\n", pos);
1201 return DSERR_INVALIDPARAM;
1204 setALContext(This->ctx);
1205 alGetListenerfv(AL_POSITION, alpos);
1206 checkALError();
1207 popALContext();
1209 pos->x = alpos[0];
1210 pos->y = alpos[1];
1211 pos->z = -alpos[2];
1212 return S_OK;
1215 static HRESULT WINAPI DS8Primary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1217 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1219 TRACE("(%p)->(%p)\n", iface, rollofffactor);
1221 if(!rollofffactor)
1223 WARN("Invalid parameter %p\n", rollofffactor);
1224 return DSERR_INVALIDPARAM;
1227 EnterCriticalSection(This->crst);
1228 *rollofffactor = This->rollofffactor;
1229 LeaveCriticalSection(This->crst);
1231 return S_OK;
1234 static HRESULT WINAPI DS8Primary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1236 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1237 ALfloat vel[3];
1239 TRACE("(%p)->(%p)\n", iface, velocity);
1241 if(!velocity)
1243 WARN("Invalid parameter %p\n", velocity);
1244 return DSERR_INVALIDPARAM;
1247 setALContext(This->ctx);
1248 alGetListenerfv(AL_VELOCITY, vel);
1249 checkALError();
1250 popALContext();
1252 velocity->x = vel[0];
1253 velocity->y = vel[1];
1254 velocity->z = -vel[2];
1255 return S_OK;
1258 static HRESULT WINAPI DS8Primary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1260 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1262 TRACE("(%p)->(%p)\n", iface, listener);
1264 if(!listener || listener->dwSize < sizeof(*listener))
1266 WARN("Invalid DS3DLISTENER %p %lu\n", listener, listener ? listener->dwSize : 0);
1267 return DSERR_INVALIDPARAM;
1270 EnterCriticalSection(This->crst);
1271 setALContext(This->ctx);
1272 DS8Primary3D_GetPosition(iface, &listener->vPosition);
1273 DS8Primary3D_GetVelocity(iface, &listener->vVelocity);
1274 DS8Primary3D_GetOrientation(iface, &listener->vOrientFront, &listener->vOrientTop);
1275 DS8Primary3D_GetDistanceFactor(iface, &listener->flDistanceFactor);
1276 DS8Primary3D_GetRolloffFactor(iface, &listener->flRolloffFactor);
1277 DS8Primary3D_GetDopplerFactor(iface, &listener->flDopplerFactor);
1278 popALContext();
1279 LeaveCriticalSection(This->crst);
1281 return DS_OK;
1285 static HRESULT WINAPI DS8Primary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1287 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1289 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1291 if(factor < DS3D_MINDISTANCEFACTOR ||
1292 factor > DS3D_MAXDISTANCEFACTOR)
1294 WARN("Invalid parameter %f\n", factor);
1295 return DSERR_INVALIDPARAM;
1298 if(apply == DS3D_DEFERRED)
1300 EnterCriticalSection(This->crst);
1301 This->params.flDistanceFactor = factor;
1302 This->dirty.bit.distancefactor = 1;
1303 LeaveCriticalSection(This->crst);
1305 else
1307 setALContext(This->ctx);
1308 alSpeedOfSound(343.3f/factor);
1309 if(This->SupportedExt[EXT_EFX])
1310 alListenerf(AL_METERS_PER_UNIT, factor);
1311 checkALError();
1312 popALContext();
1315 return S_OK;
1318 static HRESULT WINAPI DS8Primary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1320 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1322 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1324 if(factor < DS3D_MINDOPPLERFACTOR ||
1325 factor > DS3D_MAXDOPPLERFACTOR)
1327 WARN("Invalid parameter %f\n", factor);
1328 return DSERR_INVALIDPARAM;
1331 if(apply == DS3D_DEFERRED)
1333 EnterCriticalSection(This->crst);
1334 This->params.flDopplerFactor = factor;
1335 This->dirty.bit.dopplerfactor = 1;
1336 LeaveCriticalSection(This->crst);
1338 else
1340 setALContext(This->ctx);
1341 alDopplerFactor(factor);
1342 checkALError();
1343 popALContext();
1346 return S_OK;
1349 static HRESULT WINAPI DS8Primary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1351 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1353 TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %lu)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1355 if(apply == DS3D_DEFERRED)
1357 EnterCriticalSection(This->crst);
1358 This->params.vOrientFront.x = xFront;
1359 This->params.vOrientFront.y = yFront;
1360 This->params.vOrientFront.z = zFront;
1361 This->params.vOrientTop.x = xTop;
1362 This->params.vOrientTop.y = yTop;
1363 This->params.vOrientTop.z = zTop;
1364 This->dirty.bit.orientation = 1;
1365 LeaveCriticalSection(This->crst);
1367 else
1369 ALfloat orient[6] = {
1370 xFront, yFront, -zFront,
1371 xTop, yTop, -zTop
1373 setALContext(This->ctx);
1374 alListenerfv(AL_ORIENTATION, orient);
1375 checkALError();
1376 popALContext();
1379 return S_OK;
1382 static HRESULT WINAPI DS8Primary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1384 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1386 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1388 if(apply == DS3D_DEFERRED)
1390 EnterCriticalSection(This->crst);
1391 This->params.vPosition.x = x;
1392 This->params.vPosition.y = y;
1393 This->params.vPosition.z = z;
1394 This->dirty.bit.pos = 1;
1395 LeaveCriticalSection(This->crst);
1397 else
1399 setALContext(This->ctx);
1400 alListener3f(AL_POSITION, x, y, -z);
1401 checkALError();
1402 popALContext();
1405 return S_OK;
1408 static HRESULT WINAPI DS8Primary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1410 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1412 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1414 if(factor < DS3D_MINROLLOFFFACTOR ||
1415 factor > DS3D_MAXROLLOFFFACTOR)
1417 WARN("Invalid parameter %f\n", factor);
1418 return DSERR_INVALIDPARAM;
1421 EnterCriticalSection(This->crst);
1422 if(apply == DS3D_DEFERRED)
1424 This->params.flRolloffFactor = factor;
1425 This->dirty.bit.rollofffactor = 1;
1427 else
1429 struct DSBufferGroup *bufgroup = This->BufferGroups;
1430 DWORD i;
1432 setALContext(This->ctx);
1433 for(i = 0;i < This->NumBufferGroups;++i)
1435 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1436 while(usemask)
1438 int idx = CTZ64(usemask);
1439 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1440 usemask &= ~(U64(1) << idx);
1442 if(buf->ds3dmode != DS3DMODE_DISABLE)
1443 alSourcef(buf->source, AL_ROLLOFF_FACTOR, factor);
1446 checkALError();
1447 popALContext();
1449 This->rollofffactor = factor;
1451 LeaveCriticalSection(This->crst);
1453 return S_OK;
1456 static HRESULT WINAPI DS8Primary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1458 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1460 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1462 if(apply == DS3D_DEFERRED)
1464 EnterCriticalSection(This->crst);
1465 This->params.vVelocity.x = x;
1466 This->params.vVelocity.y = y;
1467 This->params.vVelocity.z = z;
1468 This->dirty.bit.vel = 1;
1469 LeaveCriticalSection(This->crst);
1471 else
1473 setALContext(This->ctx);
1474 alListener3f(AL_VELOCITY, x, y, -z);
1475 checkALError();
1476 popALContext();
1479 return S_OK;
1482 static HRESULT WINAPI DS8Primary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1484 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1486 TRACE("(%p)->(%p, %lu)\n", iface, listen, apply);
1488 if(!listen || listen->dwSize < sizeof(*listen))
1490 WARN("Invalid parameter %p %lu\n", listen, listen ? listen->dwSize : 0);
1491 return DSERR_INVALIDPARAM;
1494 if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1495 listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1497 WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1498 return DSERR_INVALIDPARAM;
1501 if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1502 listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1504 WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1505 return DSERR_INVALIDPARAM;
1508 if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1509 listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1511 WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1512 return DSERR_INVALIDPARAM;
1515 if(apply == DS3D_DEFERRED)
1517 EnterCriticalSection(This->crst);
1518 This->params = *listen;
1519 This->params.dwSize = sizeof(This->params);
1520 This->dirty.bit.pos = 1;
1521 This->dirty.bit.vel = 1;
1522 This->dirty.bit.orientation = 1;
1523 This->dirty.bit.distancefactor = 1;
1524 This->dirty.bit.rollofffactor = 1;
1525 This->dirty.bit.dopplerfactor = 1;
1526 LeaveCriticalSection(This->crst);
1528 else
1530 union PrimaryParamFlags dirty = { 0l };
1531 dirty.bit.pos = 1;
1532 dirty.bit.vel = 1;
1533 dirty.bit.orientation = 1;
1534 dirty.bit.distancefactor = 1;
1535 dirty.bit.rollofffactor = 1;
1536 dirty.bit.dopplerfactor = 1;
1538 EnterCriticalSection(This->crst);
1539 setALContext(This->ctx);
1540 DS8Primary_SetParams(This, listen, dirty.flags);
1541 checkALError();
1542 popALContext();
1543 LeaveCriticalSection(This->crst);
1546 return S_OK;
1549 HRESULT WINAPI DS8Primary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1551 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1552 struct DSBufferGroup *bufgroup;
1553 LONG flags;
1554 DWORD i;
1556 EnterCriticalSection(This->crst);
1557 setALContext(This->ctx);
1558 This->DeferUpdates();
1560 if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1562 DS8Primary_SetParams(This, &This->params, flags);
1563 /* checkALError is here for debugging */
1564 checkALError();
1566 TRACE("Dirty flags was: 0x%02lx\n", flags);
1568 bufgroup = This->BufferGroups;
1569 for(i = 0;i < This->NumBufferGroups;++i)
1571 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1572 while(usemask)
1574 int idx = CTZ64(usemask);
1575 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1576 usemask &= ~(U64(1) << idx);
1578 if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1579 DS8Buffer_SetParams(buf, &buf->params, flags);
1582 checkALError();
1584 This->ProcessUpdates();
1585 popALContext();
1586 LeaveCriticalSection(This->crst);
1588 return DS_OK;
1591 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl =
1593 DS8Primary3D_QueryInterface,
1594 DS8Primary3D_AddRef,
1595 DS8Primary3D_Release,
1596 DS8Primary3D_GetAllParameters,
1597 DS8Primary3D_GetDistanceFactor,
1598 DS8Primary3D_GetDopplerFactor,
1599 DS8Primary3D_GetOrientation,
1600 DS8Primary3D_GetPosition,
1601 DS8Primary3D_GetRolloffFactor,
1602 DS8Primary3D_GetVelocity,
1603 DS8Primary3D_SetAllParameters,
1604 DS8Primary3D_SetDistanceFactor,
1605 DS8Primary3D_SetDopplerFactor,
1606 DS8Primary3D_SetOrientation,
1607 DS8Primary3D_SetPosition,
1608 DS8Primary3D_SetRolloffFactor,
1609 DS8Primary3D_SetVelocity,
1610 DS8Primary3D_CommitDeferredSettings
1614 static HRESULT WINAPI DS8PrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1616 DS8Primary *This = impl_from_IKsPropertySet(iface);
1617 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1620 static ULONG WINAPI DS8PrimaryProp_AddRef(IKsPropertySet *iface)
1622 DS8Primary *This = impl_from_IKsPropertySet(iface);
1623 LONG ret;
1625 ret = InterlockedIncrement(&This->prop_ref);
1626 TRACE("new refcount %ld\n", ret);
1628 return ret;
1631 static ULONG WINAPI DS8PrimaryProp_Release(IKsPropertySet *iface)
1633 DS8Primary *This = impl_from_IKsPropertySet(iface);
1634 LONG ret;
1636 ret = InterlockedDecrement(&This->prop_ref);
1637 TRACE("new refcount %ld\n", ret);
1639 return ret;
1642 static HRESULT WINAPI DS8PrimaryProp_Get(IKsPropertySet *iface,
1643 REFGUID guidPropSet, ULONG dwPropID,
1644 LPVOID pInstanceData, ULONG cbInstanceData,
1645 LPVOID pPropData, ULONG cbPropData,
1646 ULONG *pcbReturned)
1648 (void)iface;
1649 (void)dwPropID;
1650 (void)pInstanceData;
1651 (void)cbInstanceData;
1652 (void)pPropData;
1653 (void)cbPropData;
1654 (void)pcbReturned;
1656 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1658 return E_PROP_ID_UNSUPPORTED;
1661 static HRESULT WINAPI DS8PrimaryProp_Set(IKsPropertySet *iface,
1662 REFGUID guidPropSet, ULONG dwPropID,
1663 LPVOID pInstanceData, ULONG cbInstanceData,
1664 LPVOID pPropData, ULONG cbPropData)
1666 (void)iface;
1667 (void)dwPropID;
1668 (void)pInstanceData;
1669 (void)cbInstanceData;
1670 (void)pPropData;
1671 (void)cbPropData;
1673 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1675 return E_PROP_ID_UNSUPPORTED;
1678 static HRESULT WINAPI DS8PrimaryProp_QuerySupport(IKsPropertySet *iface,
1679 REFGUID guidPropSet, ULONG dwPropID,
1680 ULONG *pTypeSupport)
1682 (void)iface;
1683 (void)dwPropID;
1684 (void)pTypeSupport;
1686 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1688 return E_PROP_ID_UNSUPPORTED;
1691 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl =
1693 DS8PrimaryProp_QueryInterface,
1694 DS8PrimaryProp_AddRef,
1695 DS8PrimaryProp_Release,
1696 DS8PrimaryProp_Get,
1697 DS8PrimaryProp_Set,
1698 DS8PrimaryProp_QuerySupport