Don't bother with an alternate mB-to-gain converter function
[dsound-openal.git] / primary.c
blobe0fbed3ea8cb4acd3a8cc930d38e4058babfa7a7
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;
45 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags);
48 static inline DS8Primary *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
50 return CONTAINING_RECORD(iface, DS8Primary, IDirectSoundBuffer_iface);
53 static inline DS8Primary *impl_from_IDirectSound3DListener(IDirectSound3DListener *iface)
55 return CONTAINING_RECORD(iface, DS8Primary, IDirectSound3DListener_iface);
58 static inline DS8Primary *impl_from_IKsPropertySet(IKsPropertySet *iface)
60 return CONTAINING_RECORD(iface, DS8Primary, IKsPropertySet_iface);
64 static void trigger_elapsed_notifies(DS8Buffer *buf, DWORD lastpos, DWORD curpos)
66 DSBPOSITIONNOTIFY *not = buf->notify;
67 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
68 for(;not != not_end;++not)
70 HANDLE event = not->hEventNotify;
71 DWORD ofs = not->dwOffset;
73 if(ofs == (DWORD)DSBPN_OFFSETSTOP)
74 continue;
76 if(curpos < lastpos) /* Wraparound case */
78 if(ofs < curpos || ofs >= lastpos)
80 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
81 SetEvent(event);
84 else if(ofs >= lastpos && ofs < curpos) /* Normal case */
86 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
87 SetEvent(event);
92 static void trigger_stop_notifies(DS8Buffer *buf)
94 DSBPOSITIONNOTIFY *not = buf->notify;
95 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
96 for(;not != not_end;++not)
98 if(not->dwOffset != (DWORD)DSBPN_OFFSETSTOP)
99 continue;
100 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
101 SetEvent(not->hEventNotify);
105 void DS8Primary_triggernots(DS8Primary *prim)
107 DS8Buffer **curnot, **endnot;
109 curnot = prim->notifies;
110 endnot = curnot + prim->nnotifies;
111 while(curnot != endnot)
113 DS8Buffer *buf = *curnot;
114 DS8Data *data = buf->buffer;
115 DWORD curpos = buf->lastpos;
116 ALint state = 0;
117 ALint ofs;
119 alGetSourcei(buf->source, AL_BYTE_OFFSET, &ofs);
120 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
121 if(buf->segsize == 0)
122 curpos = (state == AL_STOPPED) ? data->buf_size : ofs;
123 else
125 if(state != AL_STOPPED)
126 curpos = ofs + buf->queue_base;
127 else
129 ALint queued;
130 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
131 curpos = buf->segsize*queued + buf->queue_base;
134 if(curpos >= (DWORD)data->buf_size)
136 if(buf->islooping)
137 curpos %= (DWORD)data->buf_size;
138 else if(buf->isplaying)
140 curpos = data->buf_size;
141 alSourceStop(buf->source);
142 alSourcei(buf->source, AL_BUFFER, 0);
143 buf->curidx = 0;
144 buf->isplaying = FALSE;
148 if(state != AL_PLAYING)
149 state = buf->isplaying ? AL_PLAYING : AL_PAUSED;
151 checkALError();
153 if(buf->lastpos != curpos)
155 trigger_elapsed_notifies(buf, buf->lastpos, curpos);
156 buf->lastpos = curpos;
158 if(state != AL_PLAYING)
160 /* Remove this buffer from list and put another at the current
161 * position; don't increment i
163 trigger_stop_notifies(buf);
164 *curnot = *(--endnot);
165 prim->nnotifies--;
166 continue;
168 curnot++;
170 checkALError();
173 static void do_buffer_stream(DS8Buffer *buf, BYTE *scratch_mem)
175 DS8Data *data = buf->buffer;
176 ALint ofs, done = 0, queued = QBUFFERS, state = AL_PLAYING;
177 ALuint which;
179 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
180 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
181 alGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
183 if(done > 0)
185 ALuint bids[QBUFFERS];
186 queued -= done;
188 alSourceUnqueueBuffers(buf->source, done, bids);
189 buf->queue_base = (buf->queue_base + buf->segsize*done) % data->buf_size;
191 while(queued < QBUFFERS)
193 which = buf->stream_bids[buf->curidx];
194 ofs = buf->data_offset;
196 if(buf->segsize < data->buf_size - ofs)
198 alBufferData(which, data->buf_format, data->data + ofs, buf->segsize,
199 data->format.Format.nSamplesPerSec);
200 buf->data_offset = ofs + buf->segsize;
202 else if(buf->islooping)
204 ALsizei rem = data->buf_size - ofs;
205 if(rem > 2048) rem = 2048;
207 memcpy(scratch_mem, data->data + ofs, rem);
208 while(rem < buf->segsize)
210 ALsizei todo = buf->segsize - rem;
211 if(todo > data->buf_size)
212 todo = data->buf_size;
213 memcpy(scratch_mem + rem, data->data, todo);
214 rem += todo;
216 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
217 data->format.Format.nSamplesPerSec);
218 buf->data_offset = (ofs+buf->segsize) % data->buf_size;
220 else
222 ALsizei rem = data->buf_size - ofs;
223 if(rem > 2048) rem = 2048;
224 if(rem == 0) break;
226 memcpy(scratch_mem, data->data + ofs, rem);
227 memset(scratch_mem+rem, (data->format.Format.wBitsPerSample==8) ? 128 : 0,
228 buf->segsize - rem);
229 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
230 data->format.Format.nSamplesPerSec);
231 buf->data_offset = data->buf_size;
234 alSourceQueueBuffers(buf->source, 1, &which);
235 buf->curidx = (buf->curidx+1)%QBUFFERS;
236 queued++;
239 if(!queued)
241 buf->data_offset = 0;
242 buf->queue_base = data->buf_size;
243 buf->curidx = 0;
244 buf->isplaying = FALSE;
246 else if(state != AL_PLAYING)
247 alSourcePlay(buf->source);
250 void DS8Primary_streamfeeder(DS8Primary *prim, BYTE *scratch_mem)
252 /* OpenAL doesn't support our lovely buffer extensions so just make sure
253 * enough buffers are queued for streaming
255 if(prim->write_emu)
257 DS8Buffer *buf = &prim->writable_buf;
258 if(buf->segsize != 0 && buf->isplaying)
259 do_buffer_stream(buf, scratch_mem);
261 else
263 struct DSBufferGroup *bufgroup = prim->BufferGroups;
264 struct DSBufferGroup *endgroup = bufgroup + prim->NumBufferGroups;
265 for(;bufgroup != endgroup;++bufgroup)
267 DWORD64 usemask = ~bufgroup->FreeBuffers;
268 while(usemask)
270 int idx = CTZ64(usemask);
271 DS8Buffer *buf = bufgroup->Buffers + idx;
272 usemask &= ~(U64(1) << idx);
274 if(buf->segsize != 0 && buf->isplaying)
275 do_buffer_stream(buf, scratch_mem);
279 checkALError();
283 HRESULT DS8Primary_PreInit(DS8Primary *This, DS8Impl *parent)
285 WAVEFORMATEX *wfx;
286 DWORD num_srcs;
287 DWORD count;
288 HRESULT hr;
289 DWORD i;
291 This->IDirectSoundBuffer_iface.lpVtbl = &DS8Primary_Vtbl;
292 This->IDirectSound3DListener_iface.lpVtbl = &DS8Primary3D_Vtbl;
293 This->IKsPropertySet_iface.lpVtbl = &DS8PrimaryProp_Vtbl;
295 This->parent = parent;
296 This->crst = &parent->share->crst;
297 This->ctx = parent->share->ctx;
298 This->refresh = parent->share->refresh;
299 This->Exts = parent->share->Exts;
300 This->sources = &parent->share->sources;
301 This->auxslot = parent->share->auxslot;
303 wfx = &This->format.Format;
304 wfx->wFormatTag = WAVE_FORMAT_PCM;
305 wfx->nChannels = 2;
306 wfx->wBitsPerSample = 8;
307 wfx->nSamplesPerSec = 22050;
308 wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
309 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
310 wfx->cbSize = 0;
312 This->stopped = TRUE;
314 /* Apparently primary buffer size is always 32k,
315 * tested on windows with 192k 24 bits sound @ 6 channels
316 * where it will run out in 60 ms and it isn't pointer aligned
318 This->buf_size = 32768;
320 setALContext(This->ctx);
321 This->eax_prop = EnvironmentDefaults[EAX_ENVIRONMENT_GENERIC];
322 if(This->auxslot != 0)
324 ALint revid = alGetEnumValue("AL_EFFECT_REVERB");
325 if(revid != 0 && revid != -1)
327 alGenEffects(1, &This->effect);
328 alEffecti(This->effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
329 checkALError();
332 popALContext();
334 num_srcs = This->sources->max_alloc;
336 hr = DSERR_OUTOFMEMORY;
337 This->notifies = HeapAlloc(GetProcessHeap(), 0, num_srcs*sizeof(*This->notifies));
338 if(!This->notifies) goto fail;
339 This->sizenotifies = num_srcs;
341 count = (num_srcs+63) / 64;
342 This->BufferGroups = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
343 count*sizeof(*This->BufferGroups));
344 if(!This->BufferGroups) goto fail;
345 This->NumBufferGroups = count;
347 /* Only flag usable buffers as free. */
348 count = 0;
349 for(i = 0;i < This->NumBufferGroups;++i)
351 DWORD count_rem = num_srcs - count;
352 if(count_rem >= 64)
354 This->BufferGroups[i].FreeBuffers = ~(DWORD64)0;
355 count += 64;
357 else
359 This->BufferGroups[i].FreeBuffers = (U64(1) << count_rem) - 1;
360 count += count_rem;
364 return S_OK;
366 fail:
367 DS8Primary_Clear(This);
368 return hr;
371 void DS8Primary_Clear(DS8Primary *This)
373 struct DSBufferGroup *bufgroup;
374 DWORD i;
376 TRACE("Clearing primary %p\n", This);
378 if(!This->parent)
379 return;
381 setALContext(This->ctx);
382 if(This->effect)
383 alDeleteEffects(1, &This->effect);
384 popALContext();
386 bufgroup = This->BufferGroups;
387 for(i = 0;i < This->NumBufferGroups;++i)
389 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
390 while(usemask)
392 int idx = CTZ64(usemask);
393 DS8Buffer *buf = bufgroup[i].Buffers + idx;
394 usemask &= ~(U64(1) << idx);
396 DS8Buffer_Destroy(buf);
400 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
401 HeapFree(GetProcessHeap(), 0, This->notifies);
402 memset(This, 0, sizeof(*This));
405 static HRESULT WINAPI DS8Primary_QueryInterface(IDirectSoundBuffer *iface, REFIID riid, LPVOID *ppv)
407 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
409 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
411 *ppv = NULL;
412 if(IsEqualIID(riid, &IID_IUnknown) ||
413 IsEqualIID(riid, &IID_IDirectSoundBuffer))
414 *ppv = &This->IDirectSoundBuffer_iface;
415 else if(IsEqualIID(riid, &IID_IDirectSound3DListener))
417 if((This->flags&DSBCAPS_CTRL3D))
418 *ppv = &This->IDirectSound3DListener_iface;
420 else if(IsEqualIID(riid, &IID_IKsPropertySet))
421 *ppv = &This->IKsPropertySet_iface;
422 else
423 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
425 if(*ppv)
427 IUnknown_AddRef((IUnknown*)*ppv);
428 return S_OK;
431 return E_NOINTERFACE;
434 static ULONG WINAPI DS8Primary_AddRef(IDirectSoundBuffer *iface)
436 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
437 LONG ret;
439 ret = InterlockedIncrement(&This->ref);
440 if(ret == 1) This->flags = 0;
442 return ret;
445 static ULONG WINAPI DS8Primary_Release(IDirectSoundBuffer *iface)
447 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
448 LONG ref, oldval;
450 oldval = *(volatile LONG*)&This->ref;
451 do {
452 ref = oldval;
453 if(!ref) return 0;
454 oldval = InterlockedCompareExchange(&This->ref, ref-1, ref);
455 } while(oldval != ref);
457 return ref-1;
460 static HRESULT WINAPI DS8Primary_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
462 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
464 TRACE("(%p)->(%p)\n", iface, caps);
466 if(!caps || caps->dwSize < sizeof(*caps))
468 WARN("Invalid DSBCAPS (%p, %lu)\n", caps, caps ? caps->dwSize : 0);
469 return DSERR_INVALIDPARAM;
472 caps->dwFlags = This->flags;
473 caps->dwBufferBytes = This->buf_size;
474 caps->dwUnlockTransferRate = 0;
475 caps->dwPlayCpuOverhead = 0;
477 return DS_OK;
480 static HRESULT WINAPI DS8Primary_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *curpos)
482 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
483 HRESULT hr = DSERR_PRIOLEVELNEEDED;
485 EnterCriticalSection(This->crst);
486 if(This->write_emu)
487 hr = IDirectSoundBuffer8_GetCurrentPosition(This->write_emu, playpos, curpos);
488 LeaveCriticalSection(This->crst);
490 return hr;
493 static HRESULT WINAPI DS8Primary_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
495 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
496 HRESULT hr = S_OK;
497 UINT size;
499 if(!wfx && !written)
501 WARN("Cannot report format or format size\n");
502 return DSERR_INVALIDPARAM;
505 EnterCriticalSection(This->crst);
506 size = sizeof(This->format.Format) + This->format.Format.cbSize;
507 if(written)
508 *written = size;
509 if(wfx)
511 if(allocated < size)
512 hr = DSERR_INVALIDPARAM;
513 else
514 memcpy(wfx, &This->format.Format, size);
516 LeaveCriticalSection(This->crst);
518 return hr;
521 static HRESULT WINAPI DS8Primary_GetVolume(IDirectSoundBuffer *iface, LONG *volume)
523 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
524 ALfloat gain;
526 TRACE("(%p)->(%p)\n", iface, volume);
528 if(!volume)
529 return DSERR_INVALIDPARAM;
530 *volume = 0;
532 if(!(This->flags&DSBCAPS_CTRLVOLUME))
533 return DSERR_CONTROLUNAVAIL;
535 setALContext(This->ctx);
536 alGetListenerf(AL_GAIN, &gain);
537 checkALError();
538 popALContext();
540 *volume = clampI(gain_to_mB(gain), DSBVOLUME_MIN, DSBVOLUME_MAX);
541 return DS_OK;
544 static HRESULT WINAPI DS8Primary_GetPan(IDirectSoundBuffer *iface, LONG *pan)
546 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
547 HRESULT hr = DS_OK;
549 WARN("(%p)->(%p): semi-stub\n", iface, pan);
551 if(!pan)
552 return DSERR_INVALIDPARAM;
554 EnterCriticalSection(This->crst);
555 if(This->write_emu)
556 hr = IDirectSoundBuffer8_GetPan(This->write_emu, pan);
557 else if(!(This->flags & DSBCAPS_CTRLPAN))
558 hr = DSERR_CONTROLUNAVAIL;
559 else
560 *pan = 0;
561 LeaveCriticalSection(This->crst);
563 return hr;
566 static HRESULT WINAPI DS8Primary_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
568 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
569 HRESULT hr = DS_OK;
571 WARN("(%p)->(%p): semi-stub\n", iface, freq);
573 if(!freq)
574 return DSERR_INVALIDPARAM;
576 if(!(This->flags&DSBCAPS_CTRLFREQUENCY))
577 return DSERR_CONTROLUNAVAIL;
579 EnterCriticalSection(This->crst);
580 *freq = This->format.Format.nSamplesPerSec;
581 LeaveCriticalSection(This->crst);
583 return hr;
586 static HRESULT WINAPI DS8Primary_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
588 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
590 TRACE("(%p)->(%p)\n", iface, status);
592 if(!status)
593 return DSERR_INVALIDPARAM;
595 EnterCriticalSection(This->crst);
596 *status = DSBSTATUS_PLAYING|DSBSTATUS_LOOPING;
597 if((This->flags&DSBCAPS_LOCDEFER))
598 *status |= DSBSTATUS_LOCHARDWARE;
600 if(This->stopped)
602 struct DSBufferGroup *bufgroup = This->BufferGroups;
603 DWORD i, state = 0;
604 HRESULT hr;
606 for(i = 0;i < This->NumBufferGroups;++i)
608 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
609 while(usemask)
611 int idx = CTZ64(usemask);
612 DS8Buffer *buf = bufgroup[i].Buffers + idx;
613 usemask &= ~(U64(1) << idx);
615 hr = DS8Buffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state);
616 if(SUCCEEDED(hr) && (state&DSBSTATUS_PLAYING)) break;
619 if(!(state&DSBSTATUS_PLAYING))
621 /* Primary stopped and no buffers playing.. */
622 *status = 0;
625 LeaveCriticalSection(This->crst);
627 return DS_OK;
630 HRESULT WINAPI DS8Primary_Initialize(IDirectSoundBuffer *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
632 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
633 HRESULT hr;
635 TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
637 if(!desc || desc->lpwfxFormat || desc->dwBufferBytes)
639 WARN("Bad DSBDESC for primary buffer\n");
640 return DSERR_INVALIDPARAM;
642 if((desc->dwFlags&DSBCAPS_CTRLFX) ||
643 (desc->dwFlags&DSBCAPS_CTRLPOSITIONNOTIFY) ||
644 (desc->dwFlags&DSBCAPS_LOCSOFTWARE))
646 WARN("Bad dwFlags %08lx\n", desc->dwFlags);
647 return DSERR_INVALIDPARAM;
650 /* Should be 0 if not initialized */
651 if(This->flags)
652 return DSERR_ALREADYINITIALIZED;
654 hr = DS_OK;
655 if(This->parent->prio_level == DSSCL_WRITEPRIMARY)
657 DSBUFFERDESC emudesc;
658 DS8Buffer *emu;
660 if(This->write_emu)
662 ERR("There shouldn't be a write_emu!\n");
663 IDirectSoundBuffer8_Release(This->write_emu);
664 This->write_emu = NULL;
667 memset(&emudesc, 0, sizeof(emudesc));
668 emudesc.dwSize = sizeof(emudesc);
669 emudesc.dwFlags = DSBCAPS_LOCHARDWARE | (desc->dwFlags&DSBCAPS_CTRLPAN);
670 /* Dont play last incomplete sample */
671 emudesc.dwBufferBytes = This->buf_size - (This->buf_size%This->format.Format.nBlockAlign);
672 emudesc.lpwfxFormat = &This->format.Format;
674 hr = DS8Buffer_Create(&emu, This, NULL, TRUE);
675 if(SUCCEEDED(hr))
677 This->write_emu = &emu->IDirectSoundBuffer8_iface;
678 hr = DS8Buffer_Initialize(This->write_emu, ds, &emudesc);
679 if(FAILED(hr))
681 IDirectSoundBuffer8_Release(This->write_emu);
682 This->write_emu = NULL;
687 if(SUCCEEDED(hr))
689 DS3DLISTENER *listener = &This->params;
690 listener->dwSize = sizeof(This->params);
691 listener->vPosition.x = 0.0f;
692 listener->vPosition.y = 0.0f;
693 listener->vPosition.z = 0.0f;
694 listener->vVelocity.x = 0.0f;
695 listener->vVelocity.y = 0.0f;
696 listener->vVelocity.z = 0.0f;
697 listener->vOrientFront.x = 0.0f;
698 listener->vOrientFront.y = 0.0f;
699 listener->vOrientFront.z = 1.0f;
700 listener->vOrientTop.x = 0.0f;
701 listener->vOrientTop.y = 1.0f;
702 listener->vOrientTop.z = 0.0f;
703 listener->flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
704 listener->flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
705 listener->flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
707 This->flags = desc->dwFlags | DSBCAPS_LOCHARDWARE;
709 if((This->flags&DSBCAPS_CTRL3D))
711 union PrimaryParamFlags dirty = { 0l };
713 dirty.bit.pos = 1;
714 dirty.bit.vel = 1;
715 dirty.bit.orientation = 1;
716 dirty.bit.distancefactor = 1;
717 dirty.bit.rollofffactor = 1;
718 dirty.bit.dopplerfactor = 1;
719 DS8Primary_SetParams(This, listener, dirty.flags);
722 return hr;
725 static HRESULT WINAPI DS8Primary_Lock(IDirectSoundBuffer *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
727 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
728 HRESULT hr = DSERR_PRIOLEVELNEEDED;
730 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %lu)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
732 EnterCriticalSection(This->crst);
733 if(This->write_emu)
734 hr = IDirectSoundBuffer8_Lock(This->write_emu, ofs, bytes, ptr1, len1, ptr2, len2, flags);
735 LeaveCriticalSection(This->crst);
737 return hr;
740 static HRESULT WINAPI DS8Primary_Play(IDirectSoundBuffer *iface, DWORD res1, DWORD res2, DWORD flags)
742 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
743 HRESULT hr;
745 TRACE("(%p)->(%lu, %lu, %lu)\n", iface, res1, res2, flags);
747 if(!(flags & DSBPLAY_LOOPING))
749 WARN("Flags (%08lx) not set to DSBPLAY_LOOPING\n", flags);
750 return DSERR_INVALIDPARAM;
753 EnterCriticalSection(This->crst);
754 hr = S_OK;
755 if(This->write_emu)
756 hr = IDirectSoundBuffer8_Play(This->write_emu, res1, res2, flags);
757 if(SUCCEEDED(hr))
758 This->stopped = FALSE;
759 LeaveCriticalSection(This->crst);
761 return hr;
764 static HRESULT WINAPI DS8Primary_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD pos)
766 WARN("(%p)->(%lu)\n", iface, pos);
767 return DSERR_INVALIDCALL;
770 /* Just assume the format is crap, and clean up the damage */
771 static HRESULT copy_waveformat(WAVEFORMATEX *wfx, const WAVEFORMATEX *from)
773 if(from->nChannels <= 0)
775 WARN("Invalid Channels %d\n", from->nChannels);
776 return DSERR_INVALIDPARAM;
778 if(from->nSamplesPerSec < DSBFREQUENCY_MIN || from->nSamplesPerSec > DSBFREQUENCY_MAX)
780 WARN("Invalid SamplesPerSec %lu\n", from->nSamplesPerSec);
781 return DSERR_INVALIDPARAM;
783 if(from->nBlockAlign <= 0)
785 WARN("Invalid BlockAlign %d\n", from->nBlockAlign);
786 return DSERR_INVALIDPARAM;
788 if(from->wBitsPerSample == 0 || (from->wBitsPerSample%8) != 0)
790 WARN("Invalid BitsPerSample %d\n", from->wBitsPerSample);
791 return DSERR_INVALIDPARAM;
793 if(from->nBlockAlign != from->nChannels*from->wBitsPerSample/8)
795 WARN("Invalid BlockAlign %d (expected %u = %u*%u/8)\n",
796 from->nBlockAlign, from->nChannels*from->wBitsPerSample/8,
797 from->nChannels, from->wBitsPerSample);
798 return DSERR_INVALIDPARAM;
800 if(from->nAvgBytesPerSec != from->nBlockAlign*from->nSamplesPerSec)
802 WARN("Invalid AvgBytesPerSec %lu (expected %lu = %lu*%u)\n",
803 from->nAvgBytesPerSec, from->nSamplesPerSec*from->nBlockAlign,
804 from->nSamplesPerSec, from->nBlockAlign);
805 return DSERR_INVALIDPARAM;
808 if(from->wFormatTag == WAVE_FORMAT_PCM)
810 if(from->wBitsPerSample > 32)
811 return DSERR_INVALIDPARAM;
812 wfx->cbSize = 0;
814 else if(from->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
816 if(from->wBitsPerSample != 32)
817 return DSERR_INVALIDPARAM;
818 wfx->cbSize = 0;
820 else if(from->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
822 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
823 const WAVEFORMATEXTENSIBLE *fromx = (const WAVEFORMATEXTENSIBLE*)from;
824 const WORD size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
826 /* Fail silently.. */
827 if(from->cbSize < size) return DS_OK;
828 if(fromx->Samples.wValidBitsPerSample > fromx->Format.wBitsPerSample)
829 return DSERR_INVALIDPARAM;
831 if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
833 if(from->wBitsPerSample > 32)
834 return DSERR_INVALIDPARAM;
836 else if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
838 if(from->wBitsPerSample != 32)
839 return DSERR_INVALIDPARAM;
841 else
843 ERR("Unhandled extensible format: %s\n", debugstr_guid(&fromx->SubFormat));
844 return DSERR_INVALIDPARAM;
847 wfe->Format.cbSize = size;
848 wfe->Samples.wValidBitsPerSample = fromx->Samples.wValidBitsPerSample;
849 if(!wfe->Samples.wValidBitsPerSample)
850 wfe->Samples.wValidBitsPerSample = fromx->Format.wBitsPerSample;
851 wfe->dwChannelMask = fromx->dwChannelMask;
852 wfe->SubFormat = fromx->SubFormat;
854 else
856 ERR("Unhandled format tag %04x\n", from->wFormatTag);
857 return DSERR_INVALIDPARAM;
860 wfx->wFormatTag = from->wFormatTag;
861 wfx->nChannels = from->nChannels;
862 wfx->nSamplesPerSec = from->nSamplesPerSec;
863 wfx->nAvgBytesPerSec = from->nSamplesPerSec * from->nBlockAlign;
864 wfx->nBlockAlign = from->wBitsPerSample * from->nChannels / 8;
865 wfx->wBitsPerSample = from->wBitsPerSample;
866 return DS_OK;
869 static HRESULT WINAPI DS8Primary_SetFormat(IDirectSoundBuffer *iface, const WAVEFORMATEX *wfx)
871 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
872 HRESULT hr = S_OK;
874 TRACE("(%p)->(%p)\n", iface, wfx);
876 if(!wfx)
878 WARN("Missing format\n");
879 return DSERR_INVALIDPARAM;
882 EnterCriticalSection(This->crst);
884 if(This->parent->prio_level < DSSCL_PRIORITY)
886 hr = DSERR_PRIOLEVELNEEDED;
887 goto out;
890 TRACE("Requested primary format:\n"
891 " FormatTag = %04x\n"
892 " Channels = %u\n"
893 " SamplesPerSec = %lu\n"
894 " AvgBytesPerSec = %lu\n"
895 " BlockAlign = %u\n"
896 " BitsPerSample = %u\n",
897 wfx->wFormatTag, wfx->nChannels,
898 wfx->nSamplesPerSec, wfx->nAvgBytesPerSec,
899 wfx->nBlockAlign, wfx->wBitsPerSample);
901 hr = copy_waveformat(&This->format.Format, wfx);
902 if(SUCCEEDED(hr) && This->write_emu)
904 DS8Buffer *buf;
905 DSBUFFERDESC desc;
907 memset(&desc, 0, sizeof(desc));
908 desc.dwSize = sizeof(desc);
909 desc.dwFlags = DSBCAPS_LOCHARDWARE|DSBCAPS_CTRLPAN;
910 desc.dwBufferBytes = This->buf_size - (This->buf_size % This->format.Format.nBlockAlign);
911 desc.lpwfxFormat = &This->format.Format;
913 hr = DS8Buffer_Create(&buf, This, NULL, TRUE);
914 if(FAILED(hr)) goto out;
916 hr = DS8Buffer_Initialize(&buf->IDirectSoundBuffer8_iface, &This->parent->IDirectSound_iface, &desc);
917 if(FAILED(hr))
918 DS8Buffer_Destroy(buf);
919 else
921 IDirectSoundBuffer8_Release(This->write_emu);
922 This->write_emu = &buf->IDirectSoundBuffer8_iface;
926 out:
927 LeaveCriticalSection(This->crst);
928 return hr;
931 static HRESULT WINAPI DS8Primary_SetVolume(IDirectSoundBuffer *iface, LONG vol)
933 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
935 TRACE("(%p)->(%ld)\n", iface, vol);
937 if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
939 WARN("Invalid volume (%ld)\n", vol);
940 return DSERR_INVALIDPARAM;
943 if(!(This->flags&DSBCAPS_CTRLVOLUME))
944 return DSERR_CONTROLUNAVAIL;
946 setALContext(This->ctx);
947 alListenerf(AL_GAIN, mB_to_gain(vol));
948 popALContext();
950 return DS_OK;
953 static HRESULT WINAPI DS8Primary_SetPan(IDirectSoundBuffer *iface, LONG pan)
955 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
956 HRESULT hr;
958 TRACE("(%p)->(%ld)\n", iface, pan);
960 if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
962 WARN("invalid parameter: pan = %ld\n", pan);
963 return DSERR_INVALIDPARAM;
966 EnterCriticalSection(This->crst);
967 if(!(This->flags&DSBCAPS_CTRLPAN))
969 WARN("control unavailable\n");
970 hr = DSERR_CONTROLUNAVAIL;
972 else if(This->write_emu)
973 hr = IDirectSoundBuffer8_SetPan(This->write_emu, pan);
974 else
976 FIXME("Not supported\n");
977 hr = E_NOTIMPL;
979 LeaveCriticalSection(This->crst);
981 return hr;
984 static HRESULT WINAPI DS8Primary_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
986 WARN("(%p)->(%lu)\n", iface, freq);
987 return DSERR_CONTROLUNAVAIL;
990 static HRESULT WINAPI DS8Primary_Stop(IDirectSoundBuffer *iface)
992 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
993 HRESULT hr = S_OK;
995 TRACE("(%p)->()\n", iface);
997 EnterCriticalSection(This->crst);
998 if(This->write_emu)
999 hr = IDirectSoundBuffer8_Stop(This->write_emu);
1000 if(SUCCEEDED(hr))
1001 This->stopped = TRUE;
1002 LeaveCriticalSection(This->crst);
1004 return hr;
1007 static HRESULT WINAPI DS8Primary_Unlock(IDirectSoundBuffer *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
1009 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
1010 HRESULT hr = DSERR_INVALIDCALL;
1012 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
1014 EnterCriticalSection(This->crst);
1015 if(This->write_emu)
1016 hr = IDirectSoundBuffer8_Unlock(This->write_emu, ptr1, len1, ptr2, len2);
1017 LeaveCriticalSection(This->crst);
1019 return hr;
1022 static HRESULT WINAPI DS8Primary_Restore(IDirectSoundBuffer *iface)
1024 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
1025 HRESULT hr = S_OK;
1027 TRACE("(%p)->()\n", iface);
1029 EnterCriticalSection(This->crst);
1030 if(This->write_emu)
1031 hr = IDirectSoundBuffer8_Restore(This->write_emu);
1032 LeaveCriticalSection(This->crst);
1034 return hr;
1037 static const IDirectSoundBufferVtbl DS8Primary_Vtbl =
1039 DS8Primary_QueryInterface,
1040 DS8Primary_AddRef,
1041 DS8Primary_Release,
1042 DS8Primary_GetCaps,
1043 DS8Primary_GetCurrentPosition,
1044 DS8Primary_GetFormat,
1045 DS8Primary_GetVolume,
1046 DS8Primary_GetPan,
1047 DS8Primary_GetFrequency,
1048 DS8Primary_GetStatus,
1049 DS8Primary_Initialize,
1050 DS8Primary_Lock,
1051 DS8Primary_Play,
1052 DS8Primary_SetCurrentPosition,
1053 DS8Primary_SetFormat,
1054 DS8Primary_SetVolume,
1055 DS8Primary_SetPan,
1056 DS8Primary_SetFrequency,
1057 DS8Primary_Stop,
1058 DS8Primary_Unlock,
1059 DS8Primary_Restore
1063 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags)
1065 union PrimaryParamFlags dirty = { flags };
1066 DWORD i;
1068 if(dirty.bit.pos)
1069 alListener3f(AL_POSITION, params->vPosition.x, params->vPosition.y,
1070 -params->vPosition.z);
1071 if(dirty.bit.vel)
1072 alListener3f(AL_VELOCITY, params->vVelocity.x, params->vVelocity.y,
1073 -params->vVelocity.z);
1074 if(dirty.bit.orientation)
1076 ALfloat orient[6] = {
1077 params->vOrientFront.x, params->vOrientFront.y, -params->vOrientFront.z,
1078 params->vOrientTop.x, params->vOrientTop.y, -params->vOrientTop.z
1080 alListenerfv(AL_ORIENTATION, orient);
1082 if(dirty.bit.distancefactor)
1084 alSpeedOfSound(343.3f/params->flDistanceFactor);
1085 if(BITFIELD_TEST(This->Exts, EXT_EFX))
1086 alListenerf(AL_METERS_PER_UNIT, params->flDistanceFactor);
1088 if(dirty.bit.rollofffactor)
1090 struct DSBufferGroup *bufgroup = This->BufferGroups;
1091 ALfloat rolloff = params->flRolloffFactor;
1092 This->rollofffactor = rolloff;
1094 for(i = 0;i < This->NumBufferGroups;++i)
1096 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1097 while(usemask)
1099 int idx = CTZ64(usemask);
1100 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1101 usemask &= ~(U64(1) << idx);
1103 if(buf->ds3dmode != DS3DMODE_DISABLE)
1104 alSourcef(buf->source, AL_ROLLOFF_FACTOR, rolloff);
1108 if(dirty.bit.dopplerfactor)
1109 alDopplerFactor(params->flDopplerFactor);
1110 if(dirty.bit.effect)
1111 alAuxiliaryEffectSloti(This->auxslot, AL_EFFECTSLOT_EFFECT, This->effect);
1114 static HRESULT WINAPI DS8Primary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1116 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1117 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1120 static ULONG WINAPI DS8Primary3D_AddRef(IDirectSound3DListener *iface)
1122 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1123 LONG ret;
1125 ret = InterlockedIncrement(&This->ds3d_ref);
1126 TRACE("new refcount %ld\n", ret);
1128 return ret;
1131 static ULONG WINAPI DS8Primary3D_Release(IDirectSound3DListener *iface)
1133 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1134 LONG ret;
1136 ret = InterlockedDecrement(&This->ds3d_ref);
1137 TRACE("new refcount %ld\n", ret);
1139 return ret;
1143 static HRESULT WINAPI DS8Primary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1145 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1147 TRACE("(%p)->(%p)\n", iface, distancefactor);
1149 if(!distancefactor)
1151 WARN("Invalid parameter %p\n", distancefactor);
1152 return DSERR_INVALIDPARAM;
1155 setALContext(This->ctx);
1156 *distancefactor = 343.3f/alGetFloat(AL_SPEED_OF_SOUND);
1157 checkALError();
1158 popALContext();
1160 return S_OK;
1163 static HRESULT WINAPI DS8Primary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1165 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1167 TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1169 if(!dopplerfactor)
1171 WARN("Invalid parameter %p\n", dopplerfactor);
1172 return DSERR_INVALIDPARAM;
1175 setALContext(This->ctx);
1176 *dopplerfactor = alGetFloat(AL_DOPPLER_FACTOR);
1177 checkALError();
1178 popALContext();
1180 return S_OK;
1183 static HRESULT WINAPI DS8Primary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1185 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1186 ALfloat orient[6];
1188 TRACE("(%p)->(%p, %p)\n", iface, front, top);
1190 if(!front || !top)
1192 WARN("Invalid parameter %p %p\n", front, top);
1193 return DSERR_INVALIDPARAM;
1196 setALContext(This->ctx);
1197 alGetListenerfv(AL_ORIENTATION, orient);
1198 checkALError();
1199 popALContext();
1201 front->x = orient[0];
1202 front->y = orient[1];
1203 front->z = -orient[2];
1204 top->x = orient[3];
1205 top->y = orient[4];
1206 top->z = -orient[5];
1207 return S_OK;
1210 static HRESULT WINAPI DS8Primary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1212 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1213 ALfloat alpos[3];
1215 TRACE("(%p)->(%p)\n", iface, pos);
1217 if(!pos)
1219 WARN("Invalid parameter %p\n", pos);
1220 return DSERR_INVALIDPARAM;
1223 setALContext(This->ctx);
1224 alGetListenerfv(AL_POSITION, alpos);
1225 checkALError();
1226 popALContext();
1228 pos->x = alpos[0];
1229 pos->y = alpos[1];
1230 pos->z = -alpos[2];
1231 return S_OK;
1234 static HRESULT WINAPI DS8Primary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1236 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1238 TRACE("(%p)->(%p)\n", iface, rollofffactor);
1240 if(!rollofffactor)
1242 WARN("Invalid parameter %p\n", rollofffactor);
1243 return DSERR_INVALIDPARAM;
1246 EnterCriticalSection(This->crst);
1247 *rollofffactor = This->rollofffactor;
1248 LeaveCriticalSection(This->crst);
1250 return S_OK;
1253 static HRESULT WINAPI DS8Primary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1255 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1256 ALfloat vel[3];
1258 TRACE("(%p)->(%p)\n", iface, velocity);
1260 if(!velocity)
1262 WARN("Invalid parameter %p\n", velocity);
1263 return DSERR_INVALIDPARAM;
1266 setALContext(This->ctx);
1267 alGetListenerfv(AL_VELOCITY, vel);
1268 checkALError();
1269 popALContext();
1271 velocity->x = vel[0];
1272 velocity->y = vel[1];
1273 velocity->z = -vel[2];
1274 return S_OK;
1277 static HRESULT WINAPI DS8Primary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1279 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1281 TRACE("(%p)->(%p)\n", iface, listener);
1283 if(!listener || listener->dwSize < sizeof(*listener))
1285 WARN("Invalid DS3DLISTENER %p %lu\n", listener, listener ? listener->dwSize : 0);
1286 return DSERR_INVALIDPARAM;
1289 EnterCriticalSection(This->crst);
1290 setALContext(This->ctx);
1291 DS8Primary3D_GetPosition(iface, &listener->vPosition);
1292 DS8Primary3D_GetVelocity(iface, &listener->vVelocity);
1293 DS8Primary3D_GetOrientation(iface, &listener->vOrientFront, &listener->vOrientTop);
1294 DS8Primary3D_GetDistanceFactor(iface, &listener->flDistanceFactor);
1295 DS8Primary3D_GetRolloffFactor(iface, &listener->flRolloffFactor);
1296 DS8Primary3D_GetDopplerFactor(iface, &listener->flDopplerFactor);
1297 popALContext();
1298 LeaveCriticalSection(This->crst);
1300 return DS_OK;
1304 static HRESULT WINAPI DS8Primary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1306 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1308 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1310 if(factor < DS3D_MINDISTANCEFACTOR ||
1311 factor > DS3D_MAXDISTANCEFACTOR)
1313 WARN("Invalid parameter %f\n", factor);
1314 return DSERR_INVALIDPARAM;
1317 if(apply == DS3D_DEFERRED)
1319 EnterCriticalSection(This->crst);
1320 This->params.flDistanceFactor = factor;
1321 This->dirty.bit.distancefactor = 1;
1322 LeaveCriticalSection(This->crst);
1324 else
1326 setALContext(This->ctx);
1327 alSpeedOfSound(343.3f/factor);
1328 if(BITFIELD_TEST(This->Exts, EXT_EFX))
1329 alListenerf(AL_METERS_PER_UNIT, factor);
1330 checkALError();
1331 popALContext();
1334 return S_OK;
1337 static HRESULT WINAPI DS8Primary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1339 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1341 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1343 if(factor < DS3D_MINDOPPLERFACTOR ||
1344 factor > DS3D_MAXDOPPLERFACTOR)
1346 WARN("Invalid parameter %f\n", factor);
1347 return DSERR_INVALIDPARAM;
1350 if(apply == DS3D_DEFERRED)
1352 EnterCriticalSection(This->crst);
1353 This->params.flDopplerFactor = factor;
1354 This->dirty.bit.dopplerfactor = 1;
1355 LeaveCriticalSection(This->crst);
1357 else
1359 setALContext(This->ctx);
1360 alDopplerFactor(factor);
1361 checkALError();
1362 popALContext();
1365 return S_OK;
1368 static HRESULT WINAPI DS8Primary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1370 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1372 TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %lu)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1374 if(apply == DS3D_DEFERRED)
1376 EnterCriticalSection(This->crst);
1377 This->params.vOrientFront.x = xFront;
1378 This->params.vOrientFront.y = yFront;
1379 This->params.vOrientFront.z = zFront;
1380 This->params.vOrientTop.x = xTop;
1381 This->params.vOrientTop.y = yTop;
1382 This->params.vOrientTop.z = zTop;
1383 This->dirty.bit.orientation = 1;
1384 LeaveCriticalSection(This->crst);
1386 else
1388 ALfloat orient[6] = {
1389 xFront, yFront, -zFront,
1390 xTop, yTop, -zTop
1392 setALContext(This->ctx);
1393 alListenerfv(AL_ORIENTATION, orient);
1394 checkALError();
1395 popALContext();
1398 return S_OK;
1401 static HRESULT WINAPI DS8Primary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1403 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1405 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1407 if(apply == DS3D_DEFERRED)
1409 EnterCriticalSection(This->crst);
1410 This->params.vPosition.x = x;
1411 This->params.vPosition.y = y;
1412 This->params.vPosition.z = z;
1413 This->dirty.bit.pos = 1;
1414 LeaveCriticalSection(This->crst);
1416 else
1418 setALContext(This->ctx);
1419 alListener3f(AL_POSITION, x, y, -z);
1420 checkALError();
1421 popALContext();
1424 return S_OK;
1427 static HRESULT WINAPI DS8Primary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1429 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1431 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1433 if(factor < DS3D_MINROLLOFFFACTOR ||
1434 factor > DS3D_MAXROLLOFFFACTOR)
1436 WARN("Invalid parameter %f\n", factor);
1437 return DSERR_INVALIDPARAM;
1440 EnterCriticalSection(This->crst);
1441 if(apply == DS3D_DEFERRED)
1443 This->params.flRolloffFactor = factor;
1444 This->dirty.bit.rollofffactor = 1;
1446 else
1448 struct DSBufferGroup *bufgroup = This->BufferGroups;
1449 DWORD i;
1451 This->rollofffactor = factor;
1453 setALContext(This->ctx);
1454 for(i = 0;i < This->NumBufferGroups;++i)
1456 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1457 while(usemask)
1459 int idx = CTZ64(usemask);
1460 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1461 usemask &= ~(U64(1) << idx);
1463 if(buf->ds3dmode != DS3DMODE_DISABLE)
1464 alSourcef(buf->source, AL_ROLLOFF_FACTOR, factor);
1467 checkALError();
1468 popALContext();
1470 LeaveCriticalSection(This->crst);
1472 return S_OK;
1475 static HRESULT WINAPI DS8Primary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1477 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1479 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1481 if(apply == DS3D_DEFERRED)
1483 EnterCriticalSection(This->crst);
1484 This->params.vVelocity.x = x;
1485 This->params.vVelocity.y = y;
1486 This->params.vVelocity.z = z;
1487 This->dirty.bit.vel = 1;
1488 LeaveCriticalSection(This->crst);
1490 else
1492 setALContext(This->ctx);
1493 alListener3f(AL_VELOCITY, x, y, -z);
1494 checkALError();
1495 popALContext();
1498 return S_OK;
1501 static HRESULT WINAPI DS8Primary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1503 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1505 TRACE("(%p)->(%p, %lu)\n", iface, listen, apply);
1507 if(!listen || listen->dwSize < sizeof(*listen))
1509 WARN("Invalid parameter %p %lu\n", listen, listen ? listen->dwSize : 0);
1510 return DSERR_INVALIDPARAM;
1513 if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1514 listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1516 WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1517 return DSERR_INVALIDPARAM;
1520 if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1521 listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1523 WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1524 return DSERR_INVALIDPARAM;
1527 if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1528 listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1530 WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1531 return DSERR_INVALIDPARAM;
1534 if(apply == DS3D_DEFERRED)
1536 EnterCriticalSection(This->crst);
1537 This->params = *listen;
1538 This->params.dwSize = sizeof(This->params);
1539 This->dirty.bit.pos = 1;
1540 This->dirty.bit.vel = 1;
1541 This->dirty.bit.orientation = 1;
1542 This->dirty.bit.distancefactor = 1;
1543 This->dirty.bit.rollofffactor = 1;
1544 This->dirty.bit.dopplerfactor = 1;
1545 LeaveCriticalSection(This->crst);
1547 else
1549 union PrimaryParamFlags dirty = { 0l };
1550 dirty.bit.pos = 1;
1551 dirty.bit.vel = 1;
1552 dirty.bit.orientation = 1;
1553 dirty.bit.distancefactor = 1;
1554 dirty.bit.rollofffactor = 1;
1555 dirty.bit.dopplerfactor = 1;
1557 EnterCriticalSection(This->crst);
1558 setALContext(This->ctx);
1559 DS8Primary_SetParams(This, listen, dirty.flags);
1560 checkALError();
1561 popALContext();
1562 LeaveCriticalSection(This->crst);
1565 return S_OK;
1568 HRESULT WINAPI DS8Primary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1570 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1571 struct DSBufferGroup *bufgroup;
1572 LONG flags;
1573 DWORD i;
1575 EnterCriticalSection(This->crst);
1576 setALContext(This->ctx);
1577 alDeferUpdatesSOFT();
1579 if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1581 DS8Primary_SetParams(This, &This->params, flags);
1582 /* checkALError is here for debugging */
1583 checkALError();
1585 TRACE("Dirty flags was: 0x%02lx\n", flags);
1587 bufgroup = This->BufferGroups;
1588 for(i = 0;i < This->NumBufferGroups;++i)
1590 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1591 while(usemask)
1593 int idx = CTZ64(usemask);
1594 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1595 usemask &= ~(U64(1) << idx);
1597 if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1598 DS8Buffer_SetParams(buf, &buf->params, &buf->eax_prop, flags);
1601 alProcessUpdatesSOFT();
1602 checkALError();
1604 popALContext();
1605 LeaveCriticalSection(This->crst);
1607 return DS_OK;
1610 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl =
1612 DS8Primary3D_QueryInterface,
1613 DS8Primary3D_AddRef,
1614 DS8Primary3D_Release,
1615 DS8Primary3D_GetAllParameters,
1616 DS8Primary3D_GetDistanceFactor,
1617 DS8Primary3D_GetDopplerFactor,
1618 DS8Primary3D_GetOrientation,
1619 DS8Primary3D_GetPosition,
1620 DS8Primary3D_GetRolloffFactor,
1621 DS8Primary3D_GetVelocity,
1622 DS8Primary3D_SetAllParameters,
1623 DS8Primary3D_SetDistanceFactor,
1624 DS8Primary3D_SetDopplerFactor,
1625 DS8Primary3D_SetOrientation,
1626 DS8Primary3D_SetPosition,
1627 DS8Primary3D_SetRolloffFactor,
1628 DS8Primary3D_SetVelocity,
1629 DS8Primary3D_CommitDeferredSettings
1633 static HRESULT WINAPI DS8PrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1635 DS8Primary *This = impl_from_IKsPropertySet(iface);
1636 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1639 static ULONG WINAPI DS8PrimaryProp_AddRef(IKsPropertySet *iface)
1641 DS8Primary *This = impl_from_IKsPropertySet(iface);
1642 LONG ret;
1644 ret = InterlockedIncrement(&This->prop_ref);
1645 TRACE("new refcount %ld\n", ret);
1647 return ret;
1650 static ULONG WINAPI DS8PrimaryProp_Release(IKsPropertySet *iface)
1652 DS8Primary *This = impl_from_IKsPropertySet(iface);
1653 LONG ret;
1655 ret = InterlockedDecrement(&This->prop_ref);
1656 TRACE("new refcount %ld\n", ret);
1658 return ret;
1661 static HRESULT WINAPI DS8PrimaryProp_Get(IKsPropertySet *iface,
1662 REFGUID guidPropSet, ULONG dwPropID,
1663 LPVOID pInstanceData, ULONG cbInstanceData,
1664 LPVOID pPropData, ULONG cbPropData,
1665 ULONG *pcbReturned)
1667 (void)iface;
1668 (void)dwPropID;
1669 (void)pInstanceData;
1670 (void)cbInstanceData;
1671 (void)pPropData;
1672 (void)cbPropData;
1673 (void)pcbReturned;
1675 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1677 return E_PROP_ID_UNSUPPORTED;
1680 static HRESULT WINAPI DS8PrimaryProp_Set(IKsPropertySet *iface,
1681 REFGUID guidPropSet, ULONG dwPropID,
1682 LPVOID pInstanceData, ULONG cbInstanceData,
1683 LPVOID pPropData, ULONG cbPropData)
1685 (void)iface;
1686 (void)dwPropID;
1687 (void)pInstanceData;
1688 (void)cbInstanceData;
1689 (void)pPropData;
1690 (void)cbPropData;
1692 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1694 return E_PROP_ID_UNSUPPORTED;
1697 static HRESULT WINAPI DS8PrimaryProp_QuerySupport(IKsPropertySet *iface,
1698 REFGUID guidPropSet, ULONG dwPropID,
1699 ULONG *pTypeSupport)
1701 (void)iface;
1702 (void)dwPropID;
1703 (void)pTypeSupport;
1705 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1707 return E_PROP_ID_UNSUPPORTED;
1710 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl =
1712 DS8PrimaryProp_QueryInterface,
1713 DS8PrimaryProp_AddRef,
1714 DS8PrimaryProp_Release,
1715 DS8PrimaryProp_Get,
1716 DS8PrimaryProp_Set,
1717 DS8PrimaryProp_QuerySupport