dsound: Add dumb heuristic to find buggy applications, try 3
[wine/multimedia.git] / dlls / dsound / buffer.c
blob28060a34898e08af2a360895a77e2bb051bcaca6
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 #include "config.h"
24 #ifdef HAVE_OPENAL
25 #include <stdarg.h>
27 #define COBJMACROS
28 #define NONAMELESSSTRUCT
29 #define NONAMELESSUNION
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "vfwmsgs.h"
36 #include "mmsystem.h"
37 #include "winternl.h"
38 #include "mmddk.h"
39 #include "wine/debug.h"
40 #include "dsound.h"
41 #include "dsound_private.h"
43 #include "ks.h"
44 #include "ksmedia.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
48 /* TODO: when bufferlost is set, return from all calls except initialize with
49 * DSERR_BUFFERLOST
51 static const IDirectSoundBuffer8Vtbl DS8Buffer_Vtbl;
52 static const IDirectSound3DBufferVtbl DS8Buffer3d_Vtbl;
53 static const IDirectSoundNotifyVtbl DS8BufferNot_Vtbl;
54 static const IKsPropertySetVtbl DS8BufferProp_Vtbl;
56 /* Amount of buffers that have to be queued when
57 * bufferdatastatic and buffersubdata are not available */
58 #define QBUFFERS 3
60 struct DS8Data
62 LONG ref;
64 /* Lock was called and unlock isn't? */
65 BOOL locked;
67 WAVEFORMATEX *format;
69 ALuint buf_size;
70 ALenum buf_format;
71 DWORD dsbflags;
72 BYTE *data;
73 ALuint *buffers;
74 ALuint numsegs;
75 ALuint segsize;
76 ALuint lastsegsize;
77 ALuint buggy_app_detection;
80 static void trigger_notifies(DS8Buffer *buf, DWORD lastpos, DWORD curpos, BOOL stopping)
82 DWORD i;
83 if (lastpos == curpos && !stopping)
84 return;
85 for (i = 0; i < buf->nnotify; ++i)
87 DSBPOSITIONNOTIFY *not = &buf->notify[i];
88 HANDLE event = not->hEventNotify;
89 DWORD ofs = not->dwOffset;
91 if (ofs == (DWORD)DSBPN_OFFSETSTOP)
93 if (stopping)
94 SetEvent(event);
95 continue;
98 /* Wraparound case */
99 if (curpos < lastpos)
101 if (ofs < curpos || ofs >= lastpos)
102 SetEvent(event);
103 continue;
106 /* Normal case */
107 if (ofs >= lastpos && ofs < curpos)
108 SetEvent(event);
112 static void CALLBACK DS8Buffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
113 DWORD_PTR dw1, DWORD_PTR dw2)
115 DS8Primary *prim = (DS8Primary*)dwUser;
116 DWORD i;
117 UINT pad;
118 BYTE *data;
120 EnterCriticalSection(&prim->crst);
121 setALContext(prim->ctx);
123 /* OpenAL doesn't support our lovely buffer extensions
124 * so just make sure enough buffers are queued
126 if(!prim->ExtAL.BufferSubData && !prim->ExtAL.BufferDataStatic)
128 /* FIXME: Should probably use this logic to also
129 * call trigger_notifies
131 for (i = 0; i < prim->nbuffers; ++i)
133 DS8Buffer *buf = prim->buffers[i];
134 ALint done = 0, queued = QBUFFERS, state = AL_PLAYING;
135 ALuint which, ofs;
137 if (buf->buffer->numsegs == 1 || !buf->isplaying)
138 continue;
140 palGetSourcei(buf->source, AL_SOURCE_STATE, &state);
141 palGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
142 palGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
144 queued -= done;
145 while (done--)
146 palSourceUnqueueBuffers(buf->source, 1, &which);
147 while (queued < QBUFFERS)
149 which = buf->buffer->buffers[buf->curidx];
150 ofs = buf->curidx*buf->buffer->segsize;
151 if(buf->curidx < buf->buffer->numsegs-1)
152 palBufferData(which, buf->buffer->buf_format,
153 buf->buffer->data + ofs, buf->buffer->segsize,
154 buf->buffer->format->nSamplesPerSec);
155 else
156 palBufferData(which, buf->buffer->buf_format,
157 buf->buffer->data + ofs, buf->buffer->lastsegsize,
158 buf->buffer->format->nSamplesPerSec);
160 palSourceQueueBuffers(buf->source, 1, &which);
161 buf->curidx = (buf->curidx+1)%buf->buffer->numsegs;
162 queued++;
164 if (!buf->curidx && !buf->islooping)
166 buf->isplaying = FALSE;
167 break;
170 if (state != AL_PLAYING)
172 if (!queued)
174 IDirectSoundBuffer8_Stop(&buf->IDirectSoundBuffer8_iface);
175 continue;
177 palSourcePlay(buf->source);
179 getALError();
183 IAudioClient_GetCurrentPadding(prim->dev, &pad);
184 pad = 2048 - pad;
185 if(pad && !prim->ExtAL.BufferDataStatic && prim->ExtAL.BufferSubData)
187 for (i = 0; i < prim->nbuffers; ++i)
189 DS8Buffer *buf = prim->buffers[i];
190 ALint state;
191 DWORD pos1, pos2, ofs;
192 void *ptr1, *ptr2;
193 if (!buf->buffer->buggy_app_detection)
194 continue;
195 palGetSourcei(buf->source, AL_SOURCE_STATE, &state);
196 if (state != AL_PLAYING)
197 continue;
198 EnterCriticalSection(buf->crst);
199 WARN("Unlock never was called succesfully, doing this manually now\n");
200 IDirectSoundBuffer8_GetCurrentPosition(&buf->IDirectSoundBuffer8_iface, &ofs, NULL);
201 IDirectSoundBuffer8_Lock(&buf->IDirectSoundBuffer8_iface, ofs, pad * buf->buffer->format->nBlockAlign, &ptr1, &pos1, &ptr2, &pos2, 0);
202 IDirectSoundBuffer8_Unlock(&buf->IDirectSoundBuffer8_iface, ptr1, pos1, ptr2, pos2);
203 buf->buffer->buggy_app_detection = 1;
204 LeaveCriticalSection(buf->crst);
207 if (pad)
209 IAudioRenderClient_GetBuffer(prim->render_dev, pad, &data);
210 palcRenderSamplesSOFT(prim->parent->device, data, pad);
211 IAudioRenderClient_ReleaseBuffer(prim->render_dev, pad, 0);
214 for (i = 0; i < prim->nnotifies ; )
216 DS8Buffer *buf = prim->notifies[i];
217 IDirectSoundBuffer8 *dsb = &buf->IDirectSoundBuffer8_iface;
218 DWORD status, curpos;
219 HRESULT hr;
221 hr = IDirectSoundBuffer8_GetStatus(dsb, &status);
222 if (SUCCEEDED(hr))
224 if (!(status & DSBSTATUS_PLAYING))
226 /* Stop will remove this buffer from list,
227 * and put another at the current position
228 * don't increment i
230 IDirectSoundBuffer8_Stop(dsb);
231 continue;
233 hr = IDirectSoundBuffer8_GetCurrentPosition(dsb, &curpos, NULL);
234 if (SUCCEEDED(hr))
236 trigger_notifies(buf, buf->lastpos, curpos, FALSE);
237 buf->lastpos = curpos;
240 i++;
242 popALContext();
243 LeaveCriticalSection(&prim->crst);
246 static void DS8Buffer_starttimer(DS8Primary *prim)
248 TIMECAPS time;
249 DWORD triggertime, res = DS_TIME_RES;
250 INT64 default_period, min_period;
252 if(prim->timer_id)
253 return;
255 timeGetDevCaps(&time, sizeof(TIMECAPS));
256 IAudioClient_GetDevicePeriod(prim->dev, &default_period, &min_period);
257 triggertime = default_period / 10000;
258 getALCError(prim->parent->device);
259 if(triggertime < time.wPeriodMin)
260 triggertime = time.wPeriodMin;
261 TRACE("Calling timer every %u ms\n", triggertime);
262 if (res < time.wPeriodMin)
263 res = time.wPeriodMin;
264 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
265 WARN("Could not set minimum resolution, don't expect sound\n");
266 prim->timer_res = res;
267 prim->timer_id = timeSetEvent(triggertime, res, DS8Buffer_timer, (DWORD_PTR)prim, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
268 IAudioClient_Start(prim->dev);
271 /* Should be called with critsect held and context set.. */
272 static void DS8Buffer_addnotify(DS8Buffer *buf)
274 DS8Buffer **list;
275 DWORD i;
277 list = buf->primary->notifies;
278 for(i = 0; i < buf->primary->nnotifies; ++i)
280 if(buf == list[i])
282 ERR("Buffer %p already in notification list\n", buf);
283 return;
286 if(buf->primary->nnotifies == buf->primary->sizenotifies)
288 list = HeapReAlloc(GetProcessHeap(), 0, list, (buf->primary->nnotifies + 1) * sizeof(*list));
289 if(!list)
290 return;
291 buf->primary->sizenotifies++;
293 list[buf->primary->nnotifies++] = buf;
294 buf->primary->notifies = list;
297 static void DS8Buffer_removenotify(DS8Buffer *buf)
299 DWORD i;
300 for(i = 0; i < buf->primary->nnotifies; ++i)
302 if(buf == buf->primary->notifies[i])
304 buf->primary->notifies[i] =
305 buf->primary->notifies[--buf->primary->nnotifies];
306 return;
311 static const char *get_fmtstr_PCM(const WAVEFORMATEX *format, WAVEFORMATEX **out)
313 *out = HeapAlloc(GetProcessHeap(), 0, sizeof(*format));
314 if(!*out) return NULL;
316 **out = *format;
317 (*out)->cbSize = 0;
319 if(format->nChannels > 2 && !palIsExtensionPresent("AL_EXT_MCFORMATS"))
321 WARN("Extension AL_EXT_MCFORMATS not available, cannot enumerate > 2 channels\n");
322 return NULL;
325 if(format->wBitsPerSample == 8)
327 switch(format->nChannels)
329 case 1: return "AL_FORMAT_MONO8";
330 case 2: return "AL_FORMAT_STEREO8";
331 case 4: return "AL_FORMAT_QUAD8";
332 case 6: return "AL_FORMAT_51CHN8";
333 case 7: return "AL_FORMAT_61CHN8";
334 case 8: return "AL_FORMAT_71CHN8";
335 default: break;
338 else if(format->wBitsPerSample == 16)
340 switch(format->nChannels)
342 case 1: return "AL_FORMAT_MONO16";
343 case 2: return "AL_FORMAT_STEREO16";
344 case 4: return "AL_FORMAT_QUAD16";
345 case 6: return "AL_FORMAT_51CHN16";
346 case 7: return "AL_FORMAT_61CHN16";
347 case 8: return "AL_FORMAT_71CHN16";
348 default: break;
351 else
352 return NULL;
354 if((format->wBitsPerSample%8) == 0)
355 ERR("Could not get OpenAL format (%d-bit, %d channels)\n",
356 format->wBitsPerSample, format->nChannels);
357 return NULL;
360 /* Speaker configs */
361 #define MONO SPEAKER_FRONT_CENTER
362 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
363 #define REAR (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
364 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
365 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
366 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
367 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
369 static const char *get_fmtstr_EXT(const WAVEFORMATEX *format, WAVEFORMATEX **out)
371 WAVEFORMATEXTENSIBLE *wfe;
373 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
375 WARN("Invalid cbSize specified for WAVE_FORMAT_EXTENSIBLE (%d)\n", format->cbSize);
376 return NULL;
379 wfe = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
380 if(!wfe) return NULL;
382 *wfe = *(WAVEFORMATEXTENSIBLE*)format;
383 wfe->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
384 *out = &wfe->Format;
386 TRACE("Extensible values:\n"
387 " Samples = %d\n"
388 " ChannelMask = %#x\n"
389 " SubFormat = %s\n",
390 wfe->Samples.wReserved, wfe->dwChannelMask,
391 debugstr_guid(&wfe->SubFormat));
393 if(!wfe->Samples.wValidBitsPerSample)
394 wfe->Samples.wValidBitsPerSample = wfe->Format.wBitsPerSample;
396 if(wfe->dwChannelMask != MONO && wfe->dwChannelMask != STEREO &&
397 !palIsExtensionPresent("AL_EXT_MCFORMATS"))
399 /* QUAD PCM might still work, special case */
400 if(palIsExtensionPresent("AL_LOKI_quadriphonic") &&
401 IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
402 wfe->dwChannelMask == QUAD)
404 if(wfe->Samples.wValidBitsPerSample == 16)
405 return "AL_FORMAT_QUAD16_LOKI";
406 else if(wfe->Samples.wValidBitsPerSample == 8)
407 return "AL_FORMAT_QUAD8_LOKI";
409 WARN("Not all formats available\n");
410 return NULL;
413 if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
415 if(wfe->Samples.wValidBitsPerSample == 8)
417 switch(wfe->dwChannelMask)
419 case MONO: return "AL_FORMAT_MONO8";
420 case STEREO: return "AL_FORMAT_STEREO8";
421 case REAR: return "AL_FORMAT_REAR8";
422 case QUAD: return "AL_FORMAT_QUAD8";
423 case X5DOT1: return "AL_FORMAT_51CHN8";
424 case X6DOT1: return "AL_FORMAT_61CHN8";
425 case X7DOT1: return "AL_FORMAT_71CHN8";
426 default: break;
429 else if(wfe->Samples.wValidBitsPerSample == 16)
431 switch(wfe->dwChannelMask)
433 case MONO: return "AL_FORMAT_MONO16";
434 case STEREO: return "AL_FORMAT_STEREO16";
435 case REAR: return "AL_FORMAT_REAR16";
436 case QUAD: return "AL_FORMAT_QUAD16";
437 case X5DOT1: return "AL_FORMAT_51CHN16";
438 case X6DOT1: return "AL_FORMAT_61CHN16";
439 case X7DOT1: return "AL_FORMAT_71CHN16";
440 default: break;
443 #if 1 /* TODO: Ugly freaky hack just to pass tests */
444 else if(wfe->Samples.wValidBitsPerSample == 24 ||
445 wfe->Samples.wValidBitsPerSample == 32)
447 switch(wfe->dwChannelMask)
449 case MONO: return "AL_FORMAT_MONO_FLOAT32";
450 case STEREO: return "AL_FORMAT_STEREO_FLOAT32";
451 case REAR: return "AL_FORMAT_REAR32";
452 case QUAD: return "AL_FORMAT_QUAD32";
453 case X5DOT1: return "AL_FORMAT_51CHN32";
454 case X6DOT1: return "AL_FORMAT_61CHN32";
455 case X7DOT1: return "AL_FORMAT_71CHN32";
456 default: break;
459 #endif
460 else if((wfe->Samples.wValidBitsPerSample%8) == 0)
461 ERR("Could not get OpenAL PCM format (%d-bit, channelmask %#x)\n",
462 wfe->Samples.wValidBitsPerSample, wfe->dwChannelMask);
463 return NULL;
465 else if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
466 palIsExtensionPresent("AL_EXT_float32"))
468 if(wfe->Samples.wValidBitsPerSample != 32)
470 WARN("Invalid valid bits %u/32\n", wfe->Samples.wValidBitsPerSample);
471 return NULL;
473 switch(wfe->dwChannelMask)
475 case MONO: return "AL_FORMAT_MONO_FLOAT32";
476 case STEREO: return "AL_FORMAT_STEREO_FLOAT32";
477 case REAR: return "AL_FORMAT_REAR32";
478 case QUAD: return "AL_FORMAT_QUAD32";
479 case X5DOT1: return "AL_FORMAT_51CHN32";
480 case X6DOT1: return "AL_FORMAT_61CHN32";
481 case X7DOT1: return "AL_FORMAT_71CHN32";
482 default:
483 ERR("Could not get OpenAL float format (%d-bit, channelmask %#x)\n",
484 wfe->Samples.wValidBitsPerSample, wfe->dwChannelMask);
485 return NULL;
488 else if(!IsEqualGUID(&wfe->SubFormat, &GUID_NULL))
489 ERR("Unhandled extensible format: %s\n", debugstr_guid(&wfe->SubFormat));
490 return NULL;
493 static const char *get_fmtstr_FLOAT(const WAVEFORMATEX *format, WAVEFORMATEX **out)
495 if (!palIsExtensionPresent("AL_EXT_float32"))
496 return NULL;
498 *out = HeapAlloc(GetProcessHeap(), 0, sizeof(*format));
499 if(!*out) return NULL;
501 **out = *format;
502 (*out)->cbSize = 0;
504 switch(format->nChannels)
506 case 1: return "AL_FORMAT_MONO_FLOAT32";
507 case 2: return "AL_FORMAT_STEREO_FLOAT32";
508 default:
509 return NULL;
513 static HRESULT openal_format(const WAVEFORMATEX *format, WAVEFORMATEX **myformat, ALenum *enumformat)
515 const char *fmt_str;
516 HRESULT hr = DSERR_INVALIDPARAM;
517 *enumformat = 0;
518 *myformat = 0;
519 if(format->wFormatTag == WAVE_FORMAT_PCM)
520 fmt_str = get_fmtstr_PCM(format, myformat);
521 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
523 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)format;
525 hr = DSERR_CONTROLUNAVAIL;
526 if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
527 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
528 goto fail;
530 hr = DSERR_INVALIDPARAM;
531 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
532 !IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
533 goto fail;
535 fmt_str = get_fmtstr_EXT(format, myformat);
537 else if(format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
539 fmt_str = get_fmtstr_FLOAT(format, myformat);
541 else
543 ERR("Unhandled formattag 0x%04x\n", format->wFormatTag);
544 return DSERR_INVALIDPARAM;
547 if(!fmt_str)
548 goto fail;
550 *enumformat = palGetEnumValue(fmt_str);
551 if(palGetError() != AL_NO_ERROR || *enumformat == 0 || *enumformat == -1)
552 goto fail2;
553 return S_OK;
555 fail2:
556 WARN("Could not get OpenAL format from %s\n", fmt_str);
557 fail:
558 HeapFree(GetProcessHeap(), 0, *myformat);
559 *myformat = 0;
560 return hr;
563 static void DS8Data_Release(DS8Data *This);
564 static HRESULT DS8Data_Create(DS8Data **ppv, const DSBUFFERDESC *desc, DS8Primary *prim)
566 HRESULT hr = DSERR_INVALIDPARAM;
567 WAVEFORMATEX *format;
568 DS8Data *pBuffer;
570 format = desc->lpwfxFormat;
571 TRACE("Requested buffer format:\n"
572 " FormatTag = %#x\n"
573 " Channels = %d\n"
574 " SamplesPerSec = %u\n"
575 " AvgBytesPerSec = %u\n"
576 " BlockAlign = %d\n"
577 " BitsPerSample = %d\n",
578 format->wFormatTag, format->nChannels,
579 format->nSamplesPerSec, format->nAvgBytesPerSec,
580 format->nBlockAlign, format->wBitsPerSample);
582 if(format->nBlockAlign == 0)
584 WARN("Invalid BlockAlign specified\n");
585 return DSERR_INVALIDPARAM;
588 /* Generate a new buffer. Supporting the DSBCAPS_LOC* flags properly
589 * will need the EAX-RAM extension. Currently, we just tell the app it
590 * gets what it wanted. */
591 pBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pBuffer));
592 if(!pBuffer)
593 return E_OUTOFMEMORY;
594 pBuffer->ref = 1;
596 pBuffer->dsbflags = desc->dwFlags;
597 if((pBuffer->dsbflags&(DSBCAPS_LOCSOFTWARE|DSBCAPS_LOCHARDWARE)) == (DSBCAPS_LOCSOFTWARE|DSBCAPS_LOCHARDWARE))
599 WARN("Hardware and software location requested\n");
600 goto fail;
602 if(!(pBuffer->dsbflags&(DSBCAPS_LOCSOFTWARE|DSBCAPS_LOCHARDWARE|DSBCAPS_LOCDEFER)))
603 pBuffer->dsbflags |= DSBCAPS_LOCHARDWARE;
605 pBuffer->buf_size = desc->dwBufferBytes + format->nBlockAlign - 1;
606 pBuffer->buf_size -= pBuffer->buf_size%format->nBlockAlign;
608 hr = DSERR_BUFFERTOOSMALL;
609 if(pBuffer->buf_size < DSBSIZE_MIN)
610 goto fail;
612 hr = DSERR_INVALIDPARAM;
613 if(pBuffer->buf_size > DSBSIZE_MAX)
614 goto fail;
616 pBuffer->numsegs = 1;
617 pBuffer->segsize = pBuffer->buf_size;
618 pBuffer->lastsegsize = pBuffer->buf_size;
620 if(!(pBuffer->dsbflags & DSBCAPS_STATIC) && !prim->ExtAL.BufferSubData
621 && !prim->ExtAL.BufferDataStatic)
623 ALCint refresh = FAKE_REFRESH_COUNT;
624 ALuint newSize;
626 palcGetIntegerv(prim->parent->device, ALC_REFRESH, 1, &refresh);
627 getALCError(prim->parent->device);
629 newSize = format->nAvgBytesPerSec/refresh + format->nBlockAlign - 1;
630 newSize -= newSize%format->nBlockAlign;
632 /* Make sure enough buffers are available */
633 if(newSize > pBuffer->buf_size/(QBUFFERS+2))
634 ERR("Buffer segments too large to stream (%u for %u)!\n",
635 newSize, pBuffer->buf_size);
636 else
638 pBuffer->numsegs = pBuffer->buf_size/newSize;
639 pBuffer->segsize = newSize;
640 pBuffer->lastsegsize = pBuffer->buf_size - (newSize*(pBuffer->numsegs-1));
641 TRACE("New streaming buffer (%u chunks, %u : %u sizes)\n",
642 pBuffer->numsegs, pBuffer->segsize, pBuffer->lastsegsize);
646 hr = openal_format(format, &pBuffer->format, &pBuffer->buf_format);
647 if (FAILED(hr))
648 goto fail;
650 hr = E_OUTOFMEMORY;
651 pBuffer->buffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pBuffer->buffers)*pBuffer->numsegs);
652 pBuffer->data = HeapAlloc(GetProcessHeap(), 0, pBuffer->buf_size);
653 if(!pBuffer->buffers || !pBuffer->data)
654 goto fail;
656 palGenBuffers(pBuffer->numsegs, pBuffer->buffers);
657 getALError();
659 *ppv = pBuffer;
660 return S_OK;
662 fail:
663 DS8Data_Release(pBuffer);
664 return hr;
667 static void DS8Data_AddRef(DS8Data *data)
669 InterlockedIncrement(&data->ref);
672 /* This function is always called with the device lock held */
673 static void DS8Data_Release(DS8Data *This)
675 if(InterlockedDecrement(&This->ref)) return;
677 TRACE("Deleting %p\n", This);
678 if (This->buffers && This->data && This->buffers[0])
680 palDeleteBuffers(This->numsegs, This->buffers);
681 getALError();
683 HeapFree(GetProcessHeap(), 0, This->buffers);
684 HeapFree(GetProcessHeap(), 0, This->data);
685 HeapFree(GetProcessHeap(), 0, This->format);
686 HeapFree(GetProcessHeap(), 0, This);
689 HRESULT DS8Buffer_Create(DS8Buffer **ppv, DS8Primary *parent, DS8Buffer *orig)
691 HRESULT hr = DSERR_OUTOFMEMORY;
692 DS8Buffer *This;
693 DS8Buffer **bufs;
695 *ppv = NULL;
696 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
697 if(!This) return hr;
699 This->primary = parent;
700 This->IDirectSoundBuffer8_iface.lpVtbl = &DS8Buffer_Vtbl;
701 This->IDirectSound3DBuffer_iface.lpVtbl = &DS8Buffer3d_Vtbl;
702 This->IDirectSoundNotify_iface.lpVtbl = &DS8BufferNot_Vtbl;
703 This->IKsPropertySet_iface.lpVtbl = &DS8BufferProp_Vtbl;
704 This->ctx = parent->ctx;
705 This->ExtAL = &parent->ExtAL;
706 This->crst = &parent->crst;
707 This->ref = This->all_ref = 1;
709 if(orig)
711 This->buffer = orig->buffer;
712 DS8Data_AddRef(This->buffer);
715 /* Append to buffer list */
716 bufs = parent->buffers;
717 if(parent->nbuffers == parent->sizebuffers)
719 bufs = HeapReAlloc(GetProcessHeap(), 0, parent->buffers, sizeof(This)*(1+parent->nbuffers));
720 if(!bufs) goto fail;
721 parent->sizebuffers++;
723 parent->buffers = bufs;
724 bufs[parent->nbuffers++] = This;
726 /* Disable until initialized.. */
727 This->ds3dmode = DS3DMODE_DISABLE;
729 *ppv = This;
730 return S_OK;
732 fail:
733 DS8Buffer_Destroy(This);
734 return hr;
737 void DS8Buffer_Destroy(DS8Buffer *This)
739 DWORD idx;
740 TRACE("Destroying %p\n", This);
742 DS8Buffer_removenotify(This);
744 /* Remove from list, if in list */
745 for(idx = 0;idx < This->primary->nbuffers;++idx)
747 if(This->primary->buffers[idx] == This)
749 This->primary->buffers[idx] = This->primary->buffers[This->primary->nbuffers-1];
750 This->primary->nbuffers--;
751 break;
754 setALContext(This->ctx);
755 if(This->source)
757 ALuint *sources;
759 palSourceStop(This->source);
760 palSourcei(This->source, AL_BUFFER, 0);
761 getALError();
763 sources = This->primary->sources;
764 if(This->primary->nsources == This->primary->sizesources)
766 sources = HeapReAlloc(GetProcessHeap(), 0, This->primary->sources, sizeof(This->source)*(1+This->primary->nsources));
767 if(!sources)
768 palDeleteSources(1, &This->source);
769 else
770 This->primary->sizesources++;
772 if(sources)
774 sources[This->primary->nsources++] = This->source;
775 This->primary->sources = sources;
778 HeapFree(GetProcessHeap(), 0, This->notify);
779 This->source = 0;
780 if(This->buffer)
781 DS8Data_Release(This->buffer);
782 popALContext();
783 HeapFree(GetProcessHeap(), 0, This);
786 static inline DS8Buffer *impl_from_IDirectSoundBuffer8(IDirectSoundBuffer8 *iface)
788 return CONTAINING_RECORD(iface, DS8Buffer, IDirectSoundBuffer8_iface);
791 static HRESULT WINAPI DS8Buffer_QueryInterface(IDirectSoundBuffer8 *iface, REFIID riid, void **ppv)
793 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
795 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
797 *ppv = NULL;
798 if(IsEqualIID(riid, &IID_IUnknown) ||
799 IsEqualIID(riid, &IID_IDirectSoundBuffer))
800 *ppv = iface;
801 else if(IsEqualIID(riid, &IID_IDirectSoundBuffer8))
803 if(This->primary->parent->is_8)
804 *ppv = iface;
806 else if(IsEqualIID(riid, &IID_IDirectSound3DBuffer))
808 if((This->buffer->dsbflags&DSBCAPS_CTRL3D))
809 *ppv = &This->IDirectSound3DBuffer_iface;
811 else if(IsEqualIID(riid, &IID_IDirectSoundNotify))
813 if((This->buffer->dsbflags&DSBCAPS_CTRLPOSITIONNOTIFY))
814 *ppv = &This->IDirectSoundNotify_iface;
816 else if(IsEqualIID(riid, &IID_IKsPropertySet))
817 *ppv = &This->IKsPropertySet_iface;
818 else
819 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
821 if(*ppv)
823 IUnknown_AddRef((IUnknown*)*ppv);
824 return S_OK;
827 return E_NOINTERFACE;
830 static ULONG WINAPI DS8Buffer_AddRef(IDirectSoundBuffer8 *iface)
832 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
833 LONG ret;
835 InterlockedIncrement(&This->all_ref);
836 ret = InterlockedIncrement(&This->ref);
837 TRACE("new refcount %d\n", ret);
839 return ret;
842 static ULONG WINAPI DS8Buffer_Release(IDirectSoundBuffer8 *iface)
844 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
845 LONG ret;
847 ret = InterlockedDecrement(&This->ref);
848 TRACE("new refcount %d\n", ret);
849 if(InterlockedDecrement(&This->all_ref) == 0)
850 DS8Buffer_Destroy(This);
852 return ret;
855 static HRESULT WINAPI DS8Buffer_GetCaps(IDirectSoundBuffer8 *iface, DSBCAPS *caps)
857 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
859 TRACE("(%p)->(%p)\n", iface, caps);
861 if(!caps || caps->dwSize < sizeof(*caps))
863 WARN("Invalid DSBCAPS (%p, %u)\n", caps, (caps ? caps->dwSize : 0));
864 return DSERR_INVALIDPARAM;
867 caps->dwFlags = This->buffer->dsbflags;
868 caps->dwBufferBytes = This->buffer->buf_size;
869 caps->dwUnlockTransferRate = 4096;
870 caps->dwPlayCpuOverhead = 0;
871 return S_OK;
874 static HRESULT WINAPI DS8Buffer_GetCurrentPosition(IDirectSoundBuffer8 *iface, DWORD *playpos, DWORD *curpos)
876 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
877 WAVEFORMATEX *format = This->buffer->format;
878 UINT writecursor, pos;
880 TRACE("(%p)->(%p, %p)\n", iface, playpos, curpos);
882 EnterCriticalSection(This->crst);
883 setALContext(This->ctx);
885 if(This->buffer->numsegs > 1)
887 ALint queued = QBUFFERS;
888 palGetSourcei(This->source, AL_BUFFERS_QUEUED, &queued);
889 getALError();
891 pos = (This->curidx+This->buffer->numsegs-queued)%This->buffer->numsegs;
892 pos *= This->buffer->segsize;
893 writecursor = This->curidx * This->buffer->segsize;
895 else if(This->ExtAL->BufferSubData)
897 ALint rwpos[2] = { 0, 0 };
899 palGetSourceiv(This->source, AL_BYTE_RW_OFFSETS_SOFT, rwpos);
900 getALError();
902 pos = rwpos[0];
903 writecursor = rwpos[1];
905 else
907 ALint status = 0;
908 ALint ofs = 0;
910 palGetSourcei(This->source, AL_BYTE_OFFSET, &ofs);
911 palGetSourcei(This->source, AL_SOURCE_STATE, &status);
912 getALError();
914 pos = ofs;
915 if(status == AL_PLAYING)
917 writecursor = format->nSamplesPerSec / 100;
918 writecursor *= format->nBlockAlign;
920 else
921 writecursor = 0;
922 writecursor = (writecursor + pos) % This->buffer->buf_size;
924 TRACE("%p Play pos = %u, write pos = %u\n", This, pos, writecursor);
925 if(pos >= This->buffer->buf_size)
927 ERR("playpos >= buf_size\n");
928 pos %= This->buffer->buf_size;
930 if(writecursor >= This->buffer->buf_size)
932 ERR("writepos >= buf_size\n");
933 writecursor %= This->buffer->buf_size;
936 if(playpos) *playpos = pos;
937 if(curpos) *curpos = writecursor;
939 popALContext();
940 LeaveCriticalSection(This->crst);
942 return S_OK;
945 static HRESULT WINAPI DS8Buffer_GetFormat(IDirectSoundBuffer8 *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
947 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
948 HRESULT hr = S_OK;
949 UINT size;
951 TRACE("(%p)->(%p, %u, %p)\n", iface, wfx, allocated, written);
953 if(!wfx && !written)
955 WARN("Cannot report format or format size\n");
956 return DSERR_INVALIDPARAM;
959 EnterCriticalSection(This->crst);
960 size = sizeof(*This->buffer->format) + This->buffer->format->cbSize;
961 if(written)
962 *written = size;
963 if(wfx)
965 if(allocated < size)
966 hr = DSERR_INVALIDPARAM;
967 else
968 memcpy(wfx, This->buffer->format, size);
970 LeaveCriticalSection(This->crst);
972 return hr;
975 static HRESULT WINAPI DS8Buffer_GetVolume(IDirectSoundBuffer8 *iface, LONG *vol)
977 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
978 HRESULT hr = S_OK;
979 ALfloat gain;
981 TRACE("(%p)->(%p)\n", iface, vol);
983 if(!vol)
985 WARN("Invalid pointer\n");
986 return DSERR_INVALIDPARAM;
989 EnterCriticalSection(This->crst);
991 if(!(This->buffer->dsbflags&DSBCAPS_CTRLVOLUME))
993 WARN("Volume control not set\n");
994 hr = DSERR_CONTROLUNAVAIL;
995 goto out;
998 setALContext(This->ctx);
999 palGetSourcef(This->source, AL_GAIN, &gain);
1000 getALError();
1001 popALContext();
1003 *vol = gain_to_mB(gain);
1004 *vol = min(*vol, DSBVOLUME_MAX);
1005 *vol = max(*vol, DSBVOLUME_MIN);
1007 out:
1008 LeaveCriticalSection(This->crst);
1009 return hr;
1012 static HRESULT WINAPI DS8Buffer_GetPan(IDirectSoundBuffer8 *iface, LONG *pan)
1014 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1015 HRESULT hr = S_OK;
1016 ALfloat pos[3];
1018 TRACE("(%p)->(%p)\n", iface, pan);
1020 if(!pan)
1022 WARN("Invalid pointer\n");
1023 return DSERR_INVALIDPARAM;
1026 EnterCriticalSection(This->crst);
1027 if(!(This->buffer->dsbflags&DSBCAPS_CTRLPAN))
1029 WARN("Volume control not set\n");
1030 hr = DSERR_CONTROLUNAVAIL;
1031 goto out;
1034 setALContext(This->ctx);
1035 palGetSourcefv(This->source, AL_POSITION, pos);
1036 getALError();
1037 popALContext();
1039 *pan = (LONG)((pos[0]+1.0) * (DSBPAN_RIGHT-DSBPAN_LEFT) / 2.0 + 0.5) + DSBPAN_LEFT;
1040 *pan = min(*pan, DSBPAN_RIGHT);
1041 *pan = max(*pan, DSBPAN_LEFT);
1043 out:
1044 LeaveCriticalSection(This->crst);
1045 return hr;
1048 static HRESULT WINAPI DS8Buffer_GetFrequency(IDirectSoundBuffer8 *iface, DWORD *freq)
1050 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1051 HRESULT hr = S_OK;
1052 ALfloat pitch = 1.0f;
1054 TRACE("(%p)->(%p)\n", iface, freq);
1056 if(!freq)
1058 WARN("Invalid pointer\n");
1059 return DSERR_INVALIDPARAM;
1062 EnterCriticalSection(This->crst);
1063 if(!(This->buffer->dsbflags&DSBCAPS_CTRLFREQUENCY))
1065 WARN("Volume control not set\n");
1066 hr = DSERR_CONTROLUNAVAIL;
1067 goto out;
1070 setALContext(This->ctx);
1071 palGetSourcefv(This->source, AL_PITCH, &pitch);
1072 getALError();
1073 popALContext();
1075 *freq = (DWORD)(This->buffer->format->nSamplesPerSec * pitch);
1077 out:
1078 LeaveCriticalSection(This->crst);
1079 return hr;
1082 static HRESULT WINAPI DS8Buffer_GetStatus(IDirectSoundBuffer8 *iface, DWORD *status)
1084 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1085 ALint state, looping;
1087 TRACE("(%p)->(%p)\n", iface, status);
1089 if(!status)
1091 WARN("Invalid pointer\n");
1092 return DSERR_INVALIDPARAM;
1095 EnterCriticalSection(This->crst);
1097 setALContext(This->ctx);
1099 palGetSourcei(This->source, AL_SOURCE_STATE, &state);
1100 looping = This->islooping;
1101 if(This->buffer->numsegs == 1)
1102 palGetSourcei(This->source, AL_LOOPING, &looping);
1103 else if(state != AL_PLAYING)
1104 state = This->isplaying ? AL_PLAYING : AL_PAUSED;
1105 getALError();
1107 popALContext();
1109 *status = 0;
1110 if((This->buffer->dsbflags&DSBCAPS_LOCDEFER))
1112 if((This->buffer->dsbflags&DSBCAPS_LOCSOFTWARE))
1113 *status |= DSBSTATUS_LOCSOFTWARE;
1114 else if((This->buffer->dsbflags&DSBCAPS_LOCHARDWARE))
1115 *status |= DSBSTATUS_LOCHARDWARE;
1117 if(state == AL_PLAYING)
1118 *status |= DSBSTATUS_PLAYING | (looping ? DSBSTATUS_LOOPING : 0);
1120 LeaveCriticalSection(This->crst);
1122 return S_OK;
1125 static HRESULT WINAPI DS8Buffer_Initialize(IDirectSoundBuffer8 *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
1127 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1128 HRESULT hr = DSERR_ALREADYINITIALIZED;
1129 DS3DBUFFER *ds3dbuffer = &This->ds3dbuffer;
1131 TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
1133 EnterCriticalSection(This->crst);
1134 setALContext(This->ctx);
1136 if(This->source)
1137 goto out;
1139 if(!This->buffer)
1141 hr = DSERR_INVALIDPARAM;
1142 if(!desc)
1144 WARN("Missing DSound buffer description\n");
1145 goto out;
1147 if(!desc->lpwfxFormat)
1149 WARN("Missing buffer format (%p)\n", This);
1150 goto out;
1152 if((desc->dwFlags&DSBCAPS_CTRL3D) && desc->lpwfxFormat->nChannels != 1)
1154 if(This->primary->parent->is_8)
1156 /* DirectSoundBuffer8 objects aren't allowed non-mono 3D
1157 * buffers */
1158 WARN("Can't create multi-channel 3D buffers\n");
1159 goto out;
1161 ERR("Multi-channel 3D sounds are not spatialized\n");
1164 hr = DS8Data_Create(&This->buffer, desc, This->primary);
1165 if(FAILED(hr))
1166 goto out;
1167 else
1169 DS8Data *buf = This->buffer;
1171 if(buf->format->wBitsPerSample == 8)
1172 memset(buf->data, 0x80, buf->buf_size);
1173 else
1174 memset(buf->data, 0x00, buf->buf_size);
1176 if(This->ExtAL->BufferDataStatic)
1177 This->ExtAL->BufferDataStatic(buf->buffers[0], buf->buf_format,
1178 buf->data, buf->buf_size,
1179 buf->format->nSamplesPerSec);
1180 else if(This->ExtAL->BufferSubData)
1181 palBufferData(buf->buffers[0], buf->buf_format,
1182 buf->data, buf->buf_size,
1183 buf->format->nSamplesPerSec);
1185 getALError();
1188 hr = S_OK;
1189 if(This->primary->nsources)
1191 This->source = This->primary->sources[--This->primary->nsources];
1192 palSourcef(This->source, AL_GAIN, 1.0f);
1193 palSourcef(This->source, AL_PITCH, 1.0f);
1194 getALError();
1196 else
1198 palGenSources(1, &This->source);
1199 if(palGetError() != AL_NO_ERROR)
1201 hr = DSERR_GENERIC;
1202 goto out;
1206 ds3dbuffer->dwSize = sizeof(*ds3dbuffer);
1207 ds3dbuffer->vPosition.x = 0.0;
1208 ds3dbuffer->vPosition.y = 0.0;
1209 ds3dbuffer->vPosition.z = 0.0;
1210 ds3dbuffer->vVelocity.x = 0.0;
1211 ds3dbuffer->vVelocity.y = 0.0;
1212 ds3dbuffer->vVelocity.z = 0.0;
1213 ds3dbuffer->dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1214 ds3dbuffer->dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1215 ds3dbuffer->vConeOrientation.x = 0.0;
1216 ds3dbuffer->vConeOrientation.y = 0.0;
1217 ds3dbuffer->vConeOrientation.z = 1.0;
1218 ds3dbuffer->lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1219 ds3dbuffer->flMinDistance = DS3D_DEFAULTMINDISTANCE;
1220 ds3dbuffer->flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1221 ds3dbuffer->dwMode = DS3DMODE_NORMAL;
1223 if((This->buffer->dsbflags&DSBCAPS_CTRL3D))
1225 if(This->primary->auxslot != 0)
1227 palSource3i(This->source, AL_AUXILIARY_SEND_FILTER, This->primary->auxslot, 0, AL_FILTER_NULL);
1228 getALError();
1231 hr = IDirectSound3DBuffer_SetAllParameters(&This->IDirectSound3DBuffer_iface, ds3dbuffer, DS3D_IMMEDIATE);
1232 if(FAILED(hr))
1234 ERR("SetAllParameters failed\n");
1235 hr = S_OK;
1238 else
1240 ALuint source = This->source;
1242 if(This->primary->auxslot != 0)
1243 palSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL);
1245 /* Non-3D sources aren't distance attenuated */
1246 This->ds3dmode = DS3DMODE_DISABLE;
1247 palSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f);
1248 palSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
1249 palSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
1250 palSourcef(source, AL_CONE_OUTER_GAIN, 1.0f);
1251 palSourcef(source, AL_REFERENCE_DISTANCE, 1.0f);
1252 palSourcef(source, AL_MAX_DISTANCE, 1000.0f);
1253 palSourcef(source, AL_ROLLOFF_FACTOR, 0.0f);
1254 palSourcei(source, AL_CONE_INNER_ANGLE, 360);
1255 palSourcei(source, AL_CONE_OUTER_ANGLE, 360);
1256 palSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
1257 getALError();
1260 out:
1261 popALContext();
1262 LeaveCriticalSection(This->crst);
1264 return hr;
1267 static HRESULT WINAPI DS8Buffer_Lock(IDirectSoundBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
1269 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1270 HRESULT hr;
1271 DWORD remain;
1273 TRACE("(%p)->(%u, %u, %p, %p, %p, %p, 0x%x)\n", This, ofs, bytes, ptr1, len1, ptr2, len2, flags);
1275 EnterCriticalSection(This->crst);
1276 setALContext(This->ctx);
1278 if(ptr1) *ptr1 = NULL;
1279 if(len1) *len1 = 0;
1280 if(ptr2) *ptr2 = NULL;
1281 if(len2) *len2 = 0;
1283 hr = DSERR_INVALIDPARAM;
1284 if(ofs >= This->buffer->buf_size)
1286 WARN("Invalid ofs %u\n", ofs);
1287 goto out;
1289 if(!ptr1 || !len1)
1291 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
1292 goto out;
1294 if((flags&DSBLOCK_FROMWRITECURSOR))
1295 DS8Buffer_GetCurrentPosition(iface, NULL, &ofs);
1296 if((flags&DSBLOCK_ENTIREBUFFER))
1297 bytes = This->buffer->buf_size;
1298 if(bytes > This->buffer->buf_size)
1300 WARN("Invalid size %u\n", bytes);
1301 goto out;
1304 *ptr1 = This->buffer->data + ofs;
1305 if(ofs+bytes >= This->buffer->buf_size)
1307 *len1 = This->buffer->buf_size - ofs;
1308 remain = bytes - *len1;
1310 else
1312 *len1 = bytes;
1313 remain = 0;
1316 This->buffer->locked = TRUE;
1318 if(ptr2 && len2 && remain)
1320 *ptr2 = This->buffer->data;
1321 *len2 = remain;
1323 hr = S_OK;
1325 out:
1326 popALContext();
1327 LeaveCriticalSection(This->crst);
1328 return hr;
1331 static HRESULT WINAPI DS8Buffer_Play(IDirectSoundBuffer8 *iface, DWORD res1, DWORD prio, DWORD flags)
1333 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1334 ALint type, state = AL_STOPPED;
1335 HRESULT hr;
1337 TRACE("%p\n", This);
1339 EnterCriticalSection(This->crst);
1340 setALContext(This->ctx);
1342 hr = DSERR_BUFFERLOST;
1343 if(This->bufferlost)
1345 WARN("Buffer %p lost\n", This);
1346 goto out;
1349 if((This->buffer->dsbflags&DSBCAPS_LOCDEFER))
1351 if(!(This->buffer->dsbflags&(DSBCAPS_LOCHARDWARE|DSBCAPS_LOCSOFTWARE)))
1353 if(flags & DSBPLAY_LOCSOFTWARE)
1354 This->buffer->dsbflags |= DSBCAPS_LOCSOFTWARE;
1355 else
1356 This->buffer->dsbflags |= DSBCAPS_LOCHARDWARE;
1359 else if(prio)
1361 ERR("Invalid priority set for non-deferred buffer %p, %u!\n", This->buffer, prio);
1362 hr = DSERR_INVALIDPARAM;
1363 goto out;
1366 palGetSourcei(This->source, AL_SOURCE_STATE, &state);
1367 if(This->buffer->numsegs > 1)
1369 This->islooping = !!(flags&DSBPLAY_LOOPING);
1370 if(state != AL_PLAYING && This->isplaying)
1371 state = AL_PLAYING;
1373 else
1375 palGetSourcei(This->source, AL_SOURCE_TYPE, &type);
1376 palSourcei(This->source, AL_LOOPING, (flags&DSBPLAY_LOOPING) ? AL_TRUE : AL_FALSE);
1377 if (flags & DSBPLAY_LOOPING)
1378 This->buffer->buggy_app_detection = 1;
1380 getALError();
1382 hr = S_OK;
1383 if(state == AL_PLAYING)
1384 goto out;
1386 /* alSourceQueueBuffers will implicitly set type to streaming */
1387 if(This->buffer->numsegs == 1)
1389 if(type != AL_STATIC)
1390 palSourcei(This->source, AL_BUFFER, This->buffer->buffers[0]);
1391 palSourcePlay(This->source);
1393 if(palGetError() != AL_NO_ERROR)
1395 ERR("Couldn't start source\n");
1396 This->curidx = (This->buffer->numsegs-1+This->curidx)%This->buffer->numsegs;
1397 palSourcei(This->source, AL_BUFFER, 0);
1398 getALError();
1399 hr = DSERR_GENERIC;
1400 goto out;
1402 This->isplaying = TRUE;
1404 if(This->nnotify)
1405 DS8Buffer_addnotify(This);
1406 DS8Buffer_starttimer(This->primary);
1408 out:
1409 popALContext();
1410 LeaveCriticalSection(This->crst);
1411 return hr;
1414 static HRESULT WINAPI DS8Buffer_SetCurrentPosition(IDirectSoundBuffer8 *iface, DWORD pos)
1416 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1417 HRESULT hr;
1418 TRACE("%p\n", This);
1420 EnterCriticalSection(This->crst);
1421 setALContext(This->ctx);
1422 if(pos >= This->buffer->buf_size)
1424 hr = DSERR_INVALIDPARAM;
1425 goto out;
1427 if(This->buffer->numsegs > 1)
1429 DS8Data *buf = This->buffer;
1430 This->curidx = pos/buf->segsize;
1431 if(This->curidx >= buf->numsegs)
1432 This->curidx = buf->numsegs - 1;
1433 if(This->isplaying)
1435 palSourceStop(This->source);
1436 palSourcei(This->source, AL_BUFFER, 0);
1437 getALError();
1440 else
1441 palSourcei(This->source, AL_BYTE_OFFSET, pos);
1442 This->lastpos = pos;
1443 hr = S_OK;
1445 out:
1446 popALContext();
1447 LeaveCriticalSection(This->crst);
1448 return hr;
1451 static HRESULT WINAPI DS8Buffer_SetFormat(IDirectSoundBuffer8 *iface, const WAVEFORMATEX *wfx)
1453 /* This call only works on primary buffers */
1454 WARN("(%p)->(%p)\n", iface, wfx);
1455 return DSERR_INVALIDCALL;
1458 static HRESULT WINAPI DS8Buffer_SetVolume(IDirectSoundBuffer8 *iface, LONG vol)
1460 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1461 HRESULT hr = S_OK;
1463 TRACE("(%p)->(%d)\n", iface, vol);
1465 if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
1467 WARN("Invalid volume (%d)\n", vol);
1468 return DSERR_INVALIDPARAM;
1471 EnterCriticalSection(This->crst);
1472 if(!(This->buffer->dsbflags&DSBCAPS_CTRLVOLUME))
1473 hr = DSERR_CONTROLUNAVAIL;
1474 if(SUCCEEDED(hr))
1476 ALfloat fvol = mB_to_gain(vol);
1477 setALContext(This->ctx);
1478 palSourcef(This->source, AL_GAIN, fvol);
1479 popALContext();
1481 LeaveCriticalSection(This->crst);
1483 return hr;
1486 static HRESULT WINAPI DS8Buffer_SetPan(IDirectSoundBuffer8 *iface, LONG pan)
1488 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1489 HRESULT hr = S_OK;
1491 TRACE("(%p)->(%d)\n", iface, pan);
1493 if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
1495 WARN("invalid parameter: pan = %d\n", pan);
1496 return DSERR_INVALIDPARAM;
1499 EnterCriticalSection(This->crst);
1500 if(!(This->buffer->dsbflags&DSBCAPS_CTRLPAN))
1501 hr = DSERR_CONTROLUNAVAIL;
1502 else
1504 ALfloat pos[3];
1505 pos[0] = (pan-DSBPAN_LEFT) * 2.0 / (ALfloat)(DSBPAN_RIGHT-DSBPAN_LEFT) - 1.0;
1506 /* NOTE: Strict movement along the X plane can cause the sound to jump
1507 * between left and right sharply. Using a curved path helps smooth it
1508 * out */
1509 pos[1] = sqrt(1.0 - pos[0]*pos[0]);
1510 pos[2] = 0.0;
1512 setALContext(This->ctx);
1513 palSourcefv(This->source, AL_POSITION, pos);
1514 getALError();
1515 popALContext();
1517 if(This->buffer->format->nChannels > 1)
1518 WARN("Panning for >1 channels not supported!\n");
1519 LeaveCriticalSection(This->crst);
1521 return hr;
1524 static HRESULT WINAPI DS8Buffer_SetFrequency(IDirectSoundBuffer8 *iface, DWORD freq)
1526 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1527 HRESULT hr = S_OK;
1529 TRACE("(%p)->(%u)\n", iface, freq);
1531 if(freq < DSBFREQUENCY_MIN || freq > DSBFREQUENCY_MAX)
1533 WARN("invalid parameter: freq = %d\n", freq);
1534 return DSERR_INVALIDPARAM;
1537 EnterCriticalSection(This->crst);
1538 if(!(This->buffer->dsbflags&DSBCAPS_CTRLFREQUENCY))
1539 hr = DSERR_CONTROLUNAVAIL;
1540 else
1542 ALfloat pitch = 1.0f;
1543 if(freq != DSBFREQUENCY_ORIGINAL)
1544 pitch = freq / (ALfloat)This->buffer->format->nSamplesPerSec;
1546 setALContext(This->ctx);
1547 palSourcef(This->source, AL_PITCH, pitch);
1548 getALError();
1549 popALContext();
1551 LeaveCriticalSection(This->crst);
1552 return hr;
1555 static HRESULT WINAPI DS8Buffer_Stop(IDirectSoundBuffer8 *iface)
1557 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1558 ALint state;
1560 TRACE("(%p)->()\n", iface);
1562 EnterCriticalSection(This->crst);
1563 setALContext(This->ctx);
1565 palSourcePause(This->source);
1566 getALError();
1567 /* Mac OS X doesn't immediately report state change
1568 * if Play() is immediately called after Stop, this can be fatal,
1569 * the buffer would never be restarted
1571 do {
1572 state = AL_PAUSED;
1573 palGetSourcei(This->source, AL_SOURCE_STATE, &state);
1574 if(state != AL_PLAYING)
1575 break;
1576 Sleep(1);
1577 } while(1);
1579 /* Stopped, remove from notify list */
1580 if(This->nnotify)
1582 HRESULT hr;
1583 DWORD pos = This->lastpos;
1584 hr = IDirectSoundBuffer8_GetCurrentPosition(iface, &pos, NULL);
1585 if(FAILED(hr))
1586 ERR("Own getcurrentposition failed!\n");
1587 trigger_notifies(This, This->lastpos, pos, TRUE);
1588 This->lastpos = pos;
1589 DS8Buffer_removenotify(This);
1592 This->isplaying = FALSE;
1594 popALContext();
1595 LeaveCriticalSection(This->crst);
1597 return S_OK;
1600 static HRESULT WINAPI DS8Buffer_Unlock(IDirectSoundBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
1602 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1603 DS8Data *buf = This->buffer;
1604 DWORD bufsize = buf->buf_size;
1605 DWORD_PTR ofs1, ofs2;
1606 DWORD_PTR boundary = (DWORD_PTR)buf->data;
1607 HRESULT hr;
1609 TRACE("(%p)->(%p, %u, %p, %u)\n", iface, ptr1, len1, ptr2, len2);
1611 EnterCriticalSection(This->crst);
1612 setALContext(This->ctx);
1614 This->buffer->locked = 0;
1615 hr = DSERR_INVALIDPARAM;
1617 /* Make sure offset is between boundary and boundary + bufsize */
1618 ofs1 = (DWORD_PTR)ptr1;
1619 ofs2 = (DWORD_PTR)ptr2;
1620 if(ofs1 < boundary)
1621 goto out;
1622 if(ofs2 && ofs2 != boundary)
1623 goto out;
1624 ofs1 -= boundary;
1625 ofs2 = 0;
1626 if(bufsize-ofs1 < len1 || len2 > ofs1)
1627 goto out;
1628 if(!ptr2)
1629 len2 = 0;
1631 hr = S_OK;
1632 if(!len1 && !len2)
1633 goto out;
1635 if(This->ExtAL->BufferDataStatic)
1636 goto out;
1638 if(This->ExtAL->BufferSubData)
1640 WAVEFORMATEX *format = buf->format;
1642 len1 -= len1%format->nBlockAlign;
1643 if(len1 > 0)
1644 This->ExtAL->BufferSubData(buf->buffers[0], buf->buf_format, ptr1,
1645 ofs1, len1);
1646 len2 -= len2%format->nBlockAlign;
1647 if(len2 > 0)
1648 This->ExtAL->BufferSubData(buf->buffers[0], buf->buf_format, ptr2,
1649 ofs2, len2);
1650 getALError();
1652 else
1654 palBufferData(buf->buffers[0], buf->buf_format,
1655 buf->data, buf->buf_size,
1656 buf->format->nSamplesPerSec);
1657 getALError();
1660 This->buffer->buggy_app_detection = 0;
1661 out:
1662 if (hr != S_OK) {
1663 WARN("Invalid parameters (0x%lx,%u) (%p,%u,%p,%u)\n", boundary, bufsize, ptr1, len1, ptr2, len2);
1664 buf->buggy_app_detection = buf->buf_size;
1666 popALContext();
1667 LeaveCriticalSection(This->crst);
1668 return hr;
1671 static HRESULT WINAPI DS8Buffer_Restore(IDirectSoundBuffer8 *iface)
1673 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1674 HRESULT hr;
1676 TRACE("(%p)->()\n", iface);
1678 EnterCriticalSection(This->crst);
1679 if(This->primary->parent->prio_level < DSSCL_WRITEPRIMARY ||
1680 iface == This->primary->write_emu)
1682 This->bufferlost = 0;
1683 hr = S_OK;
1685 else
1686 hr = DSERR_BUFFERLOST;
1687 LeaveCriticalSection(This->crst);
1689 return hr;
1692 static HRESULT WINAPI DS8Buffer_SetFX(IDirectSoundBuffer8 *iface, DWORD fxcount, DSEFFECTDESC *desc, DWORD *rescodes)
1694 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1695 DWORD i;
1697 TRACE("(%p)->(%u, %p, %p)\n", This, fxcount, desc, rescodes);
1699 if(!(This->buffer->dsbflags & DSBCAPS_CTRLFX))
1701 WARN("FX control not set\n");
1702 return DSERR_CONTROLUNAVAIL;
1705 if(fxcount == 0)
1707 if(desc || rescodes)
1709 WARN("Non-NULL desc and/or result pointer specified with no effects.\n");
1710 return DSERR_INVALIDPARAM;
1713 /* No effects; we can handle that */
1714 return DS_OK;
1717 if(!desc || !rescodes)
1719 WARN("NULL desc and/or result pointer specified.\n");
1720 return DSERR_INVALIDPARAM;
1723 /* We don't (currently) handle DSound effects */
1724 for(i = 0;i < fxcount;++i)
1726 FIXME("Cannot handle effect: %s\n", debugstr_guid(&desc[i].guidDSFXClass));
1727 rescodes[i] = DSFXR_FAILED;
1730 return DSERR_INVALIDPARAM;
1733 static HRESULT WINAPI DS8Buffer_AcquireResources(IDirectSoundBuffer8 *iface, DWORD flags, DWORD fxcount, DWORD *rescodes)
1735 DS8Buffer *This = impl_from_IDirectSoundBuffer8(iface);
1737 TRACE("(%p)->(%u, %u, %p)\n", This, flags, fxcount, rescodes);
1739 /* effects aren't supported at the moment.. */
1740 if(fxcount != 0 || rescodes)
1742 WARN("Non-zero effect count and/or result pointer specified with no effects.\n");
1743 return DSERR_INVALIDPARAM;
1746 EnterCriticalSection(This->crst);
1747 if(This->buffer->dsbflags & DSBCAPS_LOCDEFER)
1749 This->buffer->dsbflags &= ~(DSBCAPS_LOCSOFTWARE|DSBCAPS_LOCHARDWARE);
1750 if((flags&DSBPLAY_LOCSOFTWARE))
1751 This->buffer->dsbflags |= DSBCAPS_LOCSOFTWARE;
1752 else
1753 This->buffer->dsbflags |= DSBCAPS_LOCHARDWARE;
1755 LeaveCriticalSection(This->crst);
1757 return S_OK;
1760 static HRESULT WINAPI DS8Buffer_GetObjectInPath(IDirectSoundBuffer8 *iface, REFGUID guid, DWORD idx, REFGUID rguidiface, void **ppv)
1762 FIXME("(%p)->(%s, %u, %s, %p) : stub!\n", iface, debugstr_guid(guid), idx, debugstr_guid(rguidiface), ppv);
1763 return E_NOTIMPL;
1766 static const IDirectSoundBuffer8Vtbl DS8Buffer_Vtbl =
1768 DS8Buffer_QueryInterface,
1769 DS8Buffer_AddRef,
1770 DS8Buffer_Release,
1771 DS8Buffer_GetCaps,
1772 DS8Buffer_GetCurrentPosition,
1773 DS8Buffer_GetFormat,
1774 DS8Buffer_GetVolume,
1775 DS8Buffer_GetPan,
1776 DS8Buffer_GetFrequency,
1777 DS8Buffer_GetStatus,
1778 DS8Buffer_Initialize,
1779 DS8Buffer_Lock,
1780 DS8Buffer_Play,
1781 DS8Buffer_SetCurrentPosition,
1782 DS8Buffer_SetFormat,
1783 DS8Buffer_SetVolume,
1784 DS8Buffer_SetPan,
1785 DS8Buffer_SetFrequency,
1786 DS8Buffer_Stop,
1787 DS8Buffer_Unlock,
1788 DS8Buffer_Restore,
1789 DS8Buffer_SetFX,
1790 DS8Buffer_AcquireResources,
1791 DS8Buffer_GetObjectInPath
1794 static inline DS8Buffer *impl_from_IDirectSound3DBuffer(IDirectSound3DBuffer *iface)
1796 return CONTAINING_RECORD(iface, DS8Buffer, IDirectSound3DBuffer_iface);
1799 static HRESULT WINAPI DS8Buffer3D_QueryInterface(IDirectSound3DBuffer *iface, REFIID riid, void **ppv)
1801 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1802 return IDirectSoundBuffer8_QueryInterface(&This->IDirectSoundBuffer8_iface, riid, ppv);
1805 static ULONG WINAPI DS8Buffer3D_AddRef(IDirectSound3DBuffer *iface)
1807 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1808 LONG ret;
1810 InterlockedIncrement(&This->all_ref);
1811 ret = InterlockedIncrement(&This->ds3d_ref);
1812 TRACE("new refcount %d\n", ret);
1814 return ret;
1817 static ULONG WINAPI DS8Buffer3D_Release(IDirectSound3DBuffer *iface)
1819 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1820 LONG ret;
1822 ret = InterlockedDecrement(&This->ds3d_ref);
1823 TRACE("new refcount %d\n", ret);
1824 if(InterlockedDecrement(&This->all_ref) == 0)
1825 DS8Buffer_Destroy(This);
1827 return ret;
1830 static HRESULT WINAPI DS8Buffer3D_GetAllParameters(IDirectSound3DBuffer *iface, DS3DBUFFER *ds3dbuffer)
1832 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1833 DS3DBUFFER ds3dbuf;
1834 HRESULT hr;
1836 TRACE("%p\n", This);
1838 if(!ds3dbuffer || ds3dbuffer->dwSize < sizeof(*ds3dbuffer))
1840 WARN("Invalid parameters %p %u\n", ds3dbuffer, ds3dbuffer ? ds3dbuffer->dwSize : 0);
1841 return DSERR_INVALIDPARAM;
1843 ds3dbuf.dwSize = sizeof(ds3dbuf);
1845 EnterCriticalSection(This->crst);
1846 setALContext(This->ctx);
1848 hr = IDirectSound3DBuffer_GetPosition(iface, &ds3dbuf.vPosition);
1849 if(SUCCEEDED(hr))
1850 hr = IDirectSound3DBuffer_GetVelocity(iface, &ds3dbuf.vVelocity);
1851 if(SUCCEEDED(hr))
1852 hr = IDirectSound3DBuffer_GetConeAngles(iface, &ds3dbuf.dwInsideConeAngle, &ds3dbuf.dwOutsideConeAngle);
1853 if(SUCCEEDED(hr))
1854 hr = IDirectSound3DBuffer_GetConeOrientation(iface, &ds3dbuf.vConeOrientation);
1855 if(SUCCEEDED(hr))
1856 hr = IDirectSound3DBuffer_GetConeOutsideVolume(iface, &ds3dbuf.lConeOutsideVolume);
1857 if(SUCCEEDED(hr))
1858 hr = IDirectSound3DBuffer_GetMinDistance(iface, &ds3dbuf.flMinDistance);
1859 if(SUCCEEDED(hr))
1860 hr = IDirectSound3DBuffer_GetMaxDistance(iface, &ds3dbuf.flMaxDistance);
1861 if(SUCCEEDED(hr))
1862 hr = IDirectSound3DBuffer_GetMode(iface, &ds3dbuf.dwMode);
1863 if(SUCCEEDED(hr))
1864 memcpy(ds3dbuffer, &ds3dbuf, sizeof(ds3dbuf));
1866 popALContext();
1867 LeaveCriticalSection(This->crst);
1869 return hr;
1872 static HRESULT WINAPI DS8Buffer3D_GetConeAngles(IDirectSound3DBuffer *iface, DWORD *pdwInsideConeAngle, DWORD *pdwOutsideConeAngle)
1874 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1875 ALint inangle, outangle;
1877 TRACE("(%p)->(%p, %p)\n", This, pdwInsideConeAngle, pdwOutsideConeAngle);
1878 if(!pdwInsideConeAngle || !pdwOutsideConeAngle)
1880 WARN("Invalid pointers (%p, %p)\n", pdwInsideConeAngle, pdwOutsideConeAngle);
1881 return DSERR_INVALIDPARAM;
1884 EnterCriticalSection(This->crst);
1885 setALContext(This->ctx);
1887 palGetSourcei(This->source, AL_CONE_INNER_ANGLE, &inangle);
1888 palGetSourcei(This->source, AL_CONE_OUTER_ANGLE, &outangle);
1889 getALError();
1890 *pdwInsideConeAngle = inangle;
1891 *pdwOutsideConeAngle = outangle;
1893 popALContext();
1894 LeaveCriticalSection(This->crst);
1896 return S_OK;
1899 static HRESULT WINAPI DS8Buffer3D_GetConeOrientation(IDirectSound3DBuffer *iface, D3DVECTOR *orient)
1901 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1902 ALfloat dir[3];
1904 TRACE("(%p)->(%p)\n", This, orient);
1905 if(!orient)
1907 WARN("Invalid pointer\n");
1908 return DSERR_INVALIDPARAM;
1911 EnterCriticalSection(This->crst);
1912 setALContext(This->ctx);
1914 palGetSourcefv(This->source, AL_DIRECTION, dir);
1915 getALError();
1916 orient->x = dir[0];
1917 orient->y = dir[1];
1918 orient->z = -dir[2];
1920 popALContext();
1921 LeaveCriticalSection(This->crst);
1923 return S_OK;
1926 static HRESULT WINAPI DS8Buffer3D_GetConeOutsideVolume(IDirectSound3DBuffer *iface, LONG *vol)
1928 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1929 ALfloat gain;
1931 TRACE("(%p)->(%p)\n", This, vol);
1932 if(!vol)
1934 WARN("Invalid pointer\n");
1935 return DSERR_INVALIDPARAM;
1938 EnterCriticalSection(This->crst);
1939 setALContext(This->ctx);
1941 palGetSourcef(This->source, AL_CONE_OUTER_GAIN, &gain);
1942 getALError();
1943 *vol = gain_to_mB(gain);
1944 *vol = max(*vol, DSBVOLUME_MIN);
1945 *vol = min(*vol, DSBVOLUME_MAX);
1947 popALContext();
1948 LeaveCriticalSection(This->crst);
1949 return S_OK;
1952 static HRESULT WINAPI DS8Buffer3D_GetMaxDistance(IDirectSound3DBuffer *iface, D3DVALUE *maxdist)
1954 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1955 ALfloat dist;
1957 TRACE("(%p)->(%p)\n", This, maxdist);
1958 if(!maxdist)
1960 WARN("Invalid pointer\n");
1961 return DSERR_INVALIDPARAM;
1964 EnterCriticalSection(This->crst);
1965 setALContext(This->ctx);
1967 palGetSourcef(This->source, AL_MAX_DISTANCE, &dist);
1968 getALError();
1969 *maxdist = dist;
1971 popALContext();
1972 LeaveCriticalSection(This->crst);
1974 return S_OK;
1977 static HRESULT WINAPI DS8Buffer3D_GetMinDistance(IDirectSound3DBuffer *iface, D3DVALUE *mindist)
1979 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
1980 ALfloat dist;
1982 TRACE("(%p)->(%p)\n", This, mindist);
1983 if (!mindist)
1985 WARN("Invalid pointer\n");
1986 return DSERR_INVALIDPARAM;
1989 EnterCriticalSection(This->crst);
1990 setALContext(This->ctx);
1992 palGetSourcef(This->source, AL_REFERENCE_DISTANCE, &dist);
1993 getALError();
1994 *mindist = dist;
1996 popALContext();
1997 LeaveCriticalSection(This->crst);
1999 return S_OK;
2002 static HRESULT WINAPI DS8Buffer3D_GetMode(IDirectSound3DBuffer *iface, DWORD *mode)
2004 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2006 TRACE("(%p)->(%p)\n", This, mode);
2007 if(!mode)
2009 WARN("Invalid pointer\n");
2010 return DSERR_INVALIDPARAM;
2013 EnterCriticalSection(This->crst);
2014 *mode = This->ds3dmode;
2015 LeaveCriticalSection(This->crst);
2017 return S_OK;
2020 static HRESULT WINAPI DS8Buffer3D_GetPosition(IDirectSound3DBuffer *iface, D3DVECTOR *pos)
2022 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2023 ALfloat alpos[3];
2025 TRACE("(%p)->(%p)\n", This, pos);
2026 if(!pos)
2028 WARN("Invalid pointer\n");
2029 return DSERR_INVALIDPARAM;
2032 EnterCriticalSection(This->crst);
2033 setALContext(This->ctx);
2035 palGetSourcefv(This->source, AL_POSITION, alpos);
2036 getALError();
2037 pos->x = alpos[0];
2038 pos->y = alpos[1];
2039 pos->z = -alpos[2];
2041 popALContext();
2042 LeaveCriticalSection(This->crst);
2044 return S_OK;
2047 static HRESULT WINAPI DS8Buffer3D_GetVelocity(IDirectSound3DBuffer *iface, D3DVECTOR *vel)
2049 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2050 ALfloat alvel[3];
2052 TRACE("(%p)->(%p)\n", This, vel);
2053 if(!vel)
2055 WARN("Invalid pointer\n");
2056 return DSERR_INVALIDPARAM;
2059 EnterCriticalSection(This->crst);
2060 setALContext(This->ctx);
2062 palGetSourcefv(This->source, AL_POSITION, alvel);
2063 getALError();
2064 vel->x = alvel[0];
2065 vel->y = alvel[1];
2066 vel->z = -alvel[2];
2068 popALContext();
2069 LeaveCriticalSection(This->crst);
2071 return S_OK;
2074 static HRESULT WINAPI DS8Buffer3D_SetAllParameters(IDirectSound3DBuffer *iface, const DS3DBUFFER *ds3dbuffer, DWORD apply)
2076 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2077 TRACE("(%p)->(%p, %u)\n", This, ds3dbuffer, apply);
2079 if(!ds3dbuffer || ds3dbuffer->dwSize < sizeof(*ds3dbuffer))
2081 WARN("Invalid DS3DBUFFER (%p, %u)\n", ds3dbuffer, ds3dbuffer ? ds3dbuffer->dwSize : 0);
2082 return DSERR_INVALIDPARAM;
2085 if(ds3dbuffer->dwInsideConeAngle > DS3D_MAXCONEANGLE ||
2086 ds3dbuffer->dwOutsideConeAngle > DS3D_MAXCONEANGLE)
2088 WARN("Invalid cone angles (%u, %u)\n", ds3dbuffer->dwInsideConeAngle,
2089 ds3dbuffer->dwOutsideConeAngle);
2090 return DSERR_INVALIDPARAM;
2093 if(ds3dbuffer->lConeOutsideVolume > DSBVOLUME_MAX ||
2094 ds3dbuffer->lConeOutsideVolume < DSBVOLUME_MIN)
2096 WARN("Invalid cone outside volume (%d)\n", ds3dbuffer->lConeOutsideVolume);
2097 return DSERR_INVALIDPARAM;
2100 if(ds3dbuffer->flMaxDistance < 0.0f)
2102 WARN("Invalid max distance (%f)\n", ds3dbuffer->flMaxDistance);
2103 return DSERR_INVALIDPARAM;
2106 if(ds3dbuffer->flMinDistance < 0.0f)
2108 WARN("Invalid min distance (%f)\n", ds3dbuffer->flMinDistance);
2109 return DSERR_INVALIDPARAM;
2112 if(ds3dbuffer->dwMode != DS3DMODE_NORMAL &&
2113 ds3dbuffer->dwMode != DS3DMODE_HEADRELATIVE &&
2114 ds3dbuffer->dwMode != DS3DMODE_DISABLE)
2116 WARN("Invalid mode (%u)\n", ds3dbuffer->dwMode);
2117 return DSERR_INVALIDPARAM;
2120 EnterCriticalSection(This->crst);
2121 setALContext(This->ctx);
2122 IDirectSound3DBuffer_SetPosition(iface, ds3dbuffer->vPosition.x, ds3dbuffer->vPosition.y, ds3dbuffer->vPosition.z, apply);
2123 IDirectSound3DBuffer_SetVelocity(iface, ds3dbuffer->vVelocity.x, ds3dbuffer->vVelocity.y, ds3dbuffer->vVelocity.z, apply);
2124 IDirectSound3DBuffer_SetConeAngles(iface, ds3dbuffer->dwInsideConeAngle, ds3dbuffer->dwOutsideConeAngle, apply);
2125 IDirectSound3DBuffer_SetConeOrientation(iface, ds3dbuffer->vConeOrientation.x, ds3dbuffer->vConeOrientation.y, ds3dbuffer->vConeOrientation.z, apply);
2126 IDirectSound3DBuffer_SetConeOutsideVolume(iface, ds3dbuffer->lConeOutsideVolume, apply);
2127 IDirectSound3DBuffer_SetMinDistance(iface, ds3dbuffer->flMinDistance, apply);
2128 IDirectSound3DBuffer_SetMaxDistance(iface, ds3dbuffer->flMaxDistance, apply);
2129 IDirectSound3DBuffer_SetMode(iface, ds3dbuffer->dwMode, apply);
2130 popALContext();
2131 LeaveCriticalSection(This->crst);
2133 return S_OK;
2136 static HRESULT WINAPI DS8Buffer3D_SetConeAngles(IDirectSound3DBuffer *iface, DWORD dwInsideConeAngle, DWORD dwOutsideConeAngle, DWORD apply)
2138 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2140 TRACE("(%p)->(%u, %u, %u)\n", This, dwInsideConeAngle, dwOutsideConeAngle, apply);
2141 if(dwInsideConeAngle > DS3D_MAXCONEANGLE ||
2142 dwOutsideConeAngle > DS3D_MAXCONEANGLE)
2144 WARN("Invalid cone angles (%u, %u)\n", dwInsideConeAngle, dwOutsideConeAngle);
2145 return DSERR_INVALIDPARAM;
2148 EnterCriticalSection(This->crst);
2149 if(apply == DS3D_DEFERRED)
2151 This->ds3dbuffer.dwInsideConeAngle = dwInsideConeAngle;
2152 This->ds3dbuffer.dwOutsideConeAngle = dwOutsideConeAngle;
2153 This->dirty.bit.cone_angles = 1;
2155 else
2157 setALContext(This->ctx);
2158 palSourcei(This->source, AL_CONE_INNER_ANGLE, dwInsideConeAngle);
2159 palSourcei(This->source, AL_CONE_OUTER_ANGLE, dwOutsideConeAngle);
2160 getALError();
2161 popALContext();
2163 LeaveCriticalSection(This->crst);
2165 return S_OK;
2168 static HRESULT WINAPI DS8Buffer3D_SetConeOrientation(IDirectSound3DBuffer *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
2170 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2172 TRACE("(%p)->(%f, %f, %f, %u)\n", This, x, y, z, apply);
2174 EnterCriticalSection(This->crst);
2175 if(apply == DS3D_DEFERRED)
2177 This->ds3dbuffer.vConeOrientation.x = x;
2178 This->ds3dbuffer.vConeOrientation.y = y;
2179 This->ds3dbuffer.vConeOrientation.z = z;
2180 This->dirty.bit.cone_orient = 1;
2182 else
2184 setALContext(This->ctx);
2185 palSource3f(This->source, AL_DIRECTION, x, y, -z);
2186 getALError();
2187 popALContext();
2189 LeaveCriticalSection(This->crst);
2191 return S_OK;
2194 static HRESULT WINAPI DS8Buffer3D_SetConeOutsideVolume(IDirectSound3DBuffer *iface, LONG vol, DWORD apply)
2196 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2198 TRACE("(%p)->(%u, %u)\n", This, vol, apply);
2199 if(vol < DSBVOLUME_MIN || vol > DSBVOLUME_MAX)
2201 WARN("Invalid volume (%u)\n", vol);
2202 return DSERR_INVALIDPARAM;
2205 EnterCriticalSection(This->crst);
2206 if(apply == DS3D_DEFERRED)
2208 This->ds3dbuffer.lConeOutsideVolume = vol;
2209 This->dirty.bit.cone_outsidevolume = 1;
2211 else
2213 setALContext(This->ctx);
2214 palSourcef(This->source, AL_CONE_OUTER_GAIN, mB_to_gain(vol));
2215 getALError();
2216 popALContext();
2218 LeaveCriticalSection(This->crst);
2220 return S_OK;
2223 static HRESULT WINAPI DS8Buffer3D_SetMaxDistance(IDirectSound3DBuffer *iface, D3DVALUE maxdist, DWORD apply)
2225 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2227 TRACE("(%p)->(%f, %u)\n", This, maxdist, apply);
2228 if(maxdist < 0.0f)
2230 WARN("Invalid max distance (%f)\n", maxdist);
2231 return DSERR_INVALIDPARAM;
2234 EnterCriticalSection(This->crst);
2235 if(apply == DS3D_DEFERRED)
2237 This->ds3dbuffer.flMaxDistance = maxdist;
2238 This->dirty.bit.max_distance = 1;
2240 else
2242 setALContext(This->ctx);
2243 palSourcef(This->source, AL_MAX_DISTANCE, maxdist);
2244 getALError();
2245 popALContext();
2247 LeaveCriticalSection(This->crst);
2249 return S_OK;
2252 static HRESULT WINAPI DS8Buffer3D_SetMinDistance(IDirectSound3DBuffer *iface, D3DVALUE mindist, DWORD apply)
2254 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2256 TRACE("(%p)->(%f, %u)\n", This, mindist, apply);
2257 if(mindist < 0.0f)
2259 WARN("Invalid min distance (%f)\n", mindist);
2260 return DSERR_INVALIDPARAM;
2263 EnterCriticalSection(This->crst);
2264 if(apply == DS3D_DEFERRED)
2266 This->ds3dbuffer.flMinDistance = mindist;
2267 This->dirty.bit.min_distance = 1;
2269 else
2271 setALContext(This->ctx);
2272 palSourcef(This->source, AL_REFERENCE_DISTANCE, mindist);
2273 getALError();
2274 popALContext();
2276 LeaveCriticalSection(This->crst);
2278 return S_OK;
2281 static HRESULT WINAPI DS8Buffer3D_SetMode(IDirectSound3DBuffer *iface, DWORD mode, DWORD apply)
2283 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2285 TRACE("(%p)->(%u, %u)\n", This, mode, apply);
2286 if(mode != DS3DMODE_NORMAL && mode != DS3DMODE_HEADRELATIVE &&
2287 mode != DS3DMODE_DISABLE)
2289 WARN("Invalid mode (%u)\n", mode);
2290 return DSERR_INVALIDPARAM;
2293 EnterCriticalSection(This->crst);
2294 if(apply == DS3D_DEFERRED)
2296 This->ds3dbuffer.dwMode = mode;
2297 This->dirty.bit.mode = 1;
2299 else
2301 setALContext(This->ctx);
2302 palSourcei(This->source, AL_SOURCE_RELATIVE,
2303 mode != DS3DMODE_NORMAL ? AL_TRUE : AL_FALSE);
2304 palSourcef(This->source, AL_ROLLOFF_FACTOR,
2305 mode == DS3DMODE_DISABLE ? 0.0f : This->primary->rollofffactor);
2306 This->ds3dmode = mode;
2307 getALError();
2308 popALContext();
2310 LeaveCriticalSection(This->crst);
2312 return S_OK;
2315 static HRESULT WINAPI DS8Buffer3D_SetPosition(IDirectSound3DBuffer *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
2317 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2319 TRACE("(%p)->(%f, %f, %f, %u)\n", This, x, y, z, apply);
2321 EnterCriticalSection(This->crst);
2322 if(apply == DS3D_DEFERRED)
2324 This->ds3dbuffer.vPosition.x = x;
2325 This->ds3dbuffer.vPosition.y = y;
2326 This->ds3dbuffer.vPosition.z = z;
2327 This->dirty.bit.pos = 1;
2329 else
2331 setALContext(This->ctx);
2332 palSource3f(This->source, AL_POSITION, x, y, -z);
2333 getALError();
2334 popALContext();
2336 LeaveCriticalSection(This->crst);
2338 return S_OK;
2341 static HRESULT WINAPI DS8Buffer3D_SetVelocity(IDirectSound3DBuffer *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
2343 DS8Buffer *This = impl_from_IDirectSound3DBuffer(iface);
2345 TRACE("(%p)->(%f, %f, %f, %u)\n", This, x, y, z, apply);
2347 EnterCriticalSection(This->crst);
2348 if(apply == DS3D_DEFERRED)
2350 This->ds3dbuffer.vVelocity.x = x;
2351 This->ds3dbuffer.vVelocity.y = y;
2352 This->ds3dbuffer.vVelocity.z = z;
2353 This->dirty.bit.vel = 1;
2355 else
2357 setALContext(This->ctx);
2358 palSource3f(This->source, AL_VELOCITY, x, y, -z);
2359 getALError();
2360 popALContext();
2362 LeaveCriticalSection(This->crst);
2364 return S_OK;
2367 static const IDirectSound3DBufferVtbl DS8Buffer3d_Vtbl =
2369 DS8Buffer3D_QueryInterface,
2370 DS8Buffer3D_AddRef,
2371 DS8Buffer3D_Release,
2372 DS8Buffer3D_GetAllParameters,
2373 DS8Buffer3D_GetConeAngles,
2374 DS8Buffer3D_GetConeOrientation,
2375 DS8Buffer3D_GetConeOutsideVolume,
2376 DS8Buffer3D_GetMaxDistance,
2377 DS8Buffer3D_GetMinDistance,
2378 DS8Buffer3D_GetMode,
2379 DS8Buffer3D_GetPosition,
2380 DS8Buffer3D_GetVelocity,
2381 DS8Buffer3D_SetAllParameters,
2382 DS8Buffer3D_SetConeAngles,
2383 DS8Buffer3D_SetConeOrientation,
2384 DS8Buffer3D_SetConeOutsideVolume,
2385 DS8Buffer3D_SetMaxDistance,
2386 DS8Buffer3D_SetMinDistance,
2387 DS8Buffer3D_SetMode,
2388 DS8Buffer3D_SetPosition,
2389 DS8Buffer3D_SetVelocity
2392 static inline DS8Buffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
2394 return CONTAINING_RECORD(iface, DS8Buffer, IDirectSoundNotify_iface);
2397 static HRESULT WINAPI DS8BufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
2399 DS8Buffer *This = impl_from_IDirectSoundNotify(iface);
2400 return IDirectSoundBuffer8_QueryInterface(&This->IDirectSoundBuffer8_iface, riid, ppv);
2403 static ULONG WINAPI DS8BufferNot_AddRef(IDirectSoundNotify *iface)
2405 DS8Buffer *This = impl_from_IDirectSoundNotify(iface);
2406 LONG ret;
2408 InterlockedIncrement(&This->all_ref);
2409 ret = InterlockedIncrement(&This->not_ref);
2410 TRACE("new refcount %d\n", ret);
2412 return ret;
2415 static ULONG WINAPI DS8BufferNot_Release(IDirectSoundNotify *iface)
2417 DS8Buffer *This = impl_from_IDirectSoundNotify(iface);
2418 LONG ret;
2420 ret = InterlockedDecrement(&This->not_ref);
2421 TRACE("new refcount %d\n", ret);
2422 if(InterlockedDecrement(&This->all_ref) == 0)
2423 DS8Buffer_Destroy(This);
2425 return ret;
2428 static HRESULT WINAPI DS8BufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
2430 DS8Buffer *This = impl_from_IDirectSoundNotify(iface);
2431 DSBPOSITIONNOTIFY *nots;
2432 HRESULT hr;
2433 DWORD state;
2435 EnterCriticalSection(This->crst);
2436 hr = DSERR_INVALIDPARAM;
2437 if (count && !notifications)
2438 goto out;
2440 hr = IDirectSoundBuffer8_GetStatus(&This->IDirectSoundBuffer8_iface, &state);
2441 if(FAILED(hr))
2442 goto out;
2444 hr = DSERR_INVALIDCALL;
2445 if((state&DSBSTATUS_PLAYING))
2446 goto out;
2448 hr = DSERR_INVALIDPARAM;
2449 if(!count)
2451 HeapFree(GetProcessHeap(), 0, This->notify);
2452 This->notify = 0;
2453 This->nnotify = 0;
2455 else
2457 DWORD i;
2459 for(i = 0;i < count;++i)
2461 if(notifications[i].dwOffset >= This->buffer->buf_size &&
2462 notifications[i].dwOffset != (DWORD)DSBPN_OFFSETSTOP)
2463 goto out;
2466 hr = E_OUTOFMEMORY;
2467 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
2468 if(!nots)
2469 goto out;
2470 memcpy(nots, notifications, count*sizeof(*nots));
2472 HeapFree(GetProcessHeap(), 0, This->notify);
2473 This->notify = nots;
2474 This->nnotify = count;
2476 hr = S_OK;
2479 out:
2480 LeaveCriticalSection(This->crst);
2481 return hr;
2484 static const IDirectSoundNotifyVtbl DS8BufferNot_Vtbl =
2486 DS8BufferNot_QueryInterface,
2487 DS8BufferNot_AddRef,
2488 DS8BufferNot_Release,
2489 DS8BufferNot_SetNotificationPositions
2492 /* NOTE: Due to some apparent quirks in DSound, the listener properties are
2493 handled through secondary buffers. */
2494 static inline DS8Buffer *impl_from_IKsPropertySet(IKsPropertySet *iface)
2496 return CONTAINING_RECORD(iface, DS8Buffer, IKsPropertySet_iface);
2499 static HRESULT WINAPI DS8BufferProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
2501 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2502 return IDirectSoundBuffer8_QueryInterface(&This->IDirectSoundBuffer8_iface, riid, ppv);
2505 static ULONG WINAPI DS8BufferProp_AddRef(IKsPropertySet *iface)
2507 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2508 LONG ret;
2510 InterlockedIncrement(&This->all_ref);
2511 ret = InterlockedIncrement(&This->prop_ref);
2512 TRACE("new refcount %d\n", ret);
2514 return ret;
2517 static ULONG WINAPI DS8BufferProp_Release(IKsPropertySet *iface)
2519 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2520 LONG ret;
2522 ret = InterlockedDecrement(&This->prop_ref);
2523 TRACE("new refcount %d\n", ret);
2524 if(InterlockedDecrement(&This->all_ref) == 0)
2525 DS8Buffer_Destroy(This);
2527 return ret;
2530 static HRESULT WINAPI DS8BufferProp_Get(IKsPropertySet *iface, REFGUID guidPropSet,
2531 ULONG dwPropID,
2532 LPVOID pInstanceData,
2533 ULONG cbInstanceData,
2534 LPVOID pPropData,
2535 ULONG cbPropData,
2536 PULONG pcbReturned)
2538 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2539 HRESULT hr = E_PROP_ID_UNSUPPORTED;
2541 WARN("(%p)->(%s, %u, %p, %u, %p, %u, %p) : semi-stub!\n", iface, debugstr_guid(guidPropSet),
2542 dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData, pcbReturned);
2544 if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
2546 if((This->buffer->dsbflags&DSBCAPS_CTRL3D))
2547 hr = IKsPropertySet_Get(&This->primary->IKsPropertySet_iface, guidPropSet,
2548 dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData,
2549 pcbReturned);
2551 else
2552 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
2554 return hr;
2557 static HRESULT WINAPI DS8BufferProp_Set(IKsPropertySet *iface, REFGUID guidPropSet,
2558 ULONG dwPropID,
2559 LPVOID pInstanceData,
2560 ULONG cbInstanceData,
2561 LPVOID pPropData,
2562 ULONG cbPropData)
2564 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2565 HRESULT hr = E_PROP_ID_UNSUPPORTED;
2567 WARN("(%p)->(%s, %u, %p, %u, %p, %u) : semi-stub!\n", iface, debugstr_guid(guidPropSet),
2568 dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData);
2570 if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
2572 if((This->buffer->dsbflags&DSBCAPS_CTRL3D))
2573 hr = IKsPropertySet_Set(&This->primary->IKsPropertySet_iface, guidPropSet,
2574 dwPropID, pInstanceData, cbInstanceData, pPropData,
2575 cbPropData);
2577 else
2578 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
2580 return hr;
2583 static HRESULT WINAPI DS8BufferProp_QuerySupport(IKsPropertySet *iface, REFGUID guidPropSet,
2584 ULONG dwPropID,
2585 PULONG pTypeSupport)
2587 DS8Buffer *This = impl_from_IKsPropertySet(iface);
2588 HRESULT hr = E_PROP_ID_UNSUPPORTED;
2590 WARN("(%p)->(%s, %u, %p) : semi-stub!\n", iface, debugstr_guid(guidPropSet), dwPropID, pTypeSupport);
2592 if(!pTypeSupport)
2593 return E_POINTER;
2594 *pTypeSupport = 0;
2596 if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
2598 if((This->buffer->dsbflags&DSBCAPS_CTRL3D))
2599 hr = IKsPropertySet_QuerySupport(&This->primary->IKsPropertySet_iface,
2600 guidPropSet, dwPropID, pTypeSupport);
2602 else
2603 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
2605 return hr;
2608 static const IKsPropertySetVtbl DS8BufferProp_Vtbl =
2610 DS8BufferProp_QueryInterface,
2611 DS8BufferProp_AddRef,
2612 DS8BufferProp_Release,
2613 DS8BufferProp_Get,
2614 DS8BufferProp_Set,
2615 DS8BufferProp_QuerySupport
2618 #endif /*HAVE_OPENAL*/