Merge pull request #204 from jhasse/android-byte-order
[openal-soft.git] / Alc / backends / dsound.c
blob6bab641c7c5f3b7ba88db446ac2fab2adbf1fb47
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
27 #include <dsound.h>
28 #include <cguid.h>
29 #include <mmreg.h>
30 #ifndef _WAVEFORMATEXTENSIBLE_
31 #include <ks.h>
32 #include <ksmedia.h>
33 #endif
35 #include "alMain.h"
36 #include "alu.h"
37 #include "ringbuffer.h"
38 #include "threads.h"
39 #include "compat.h"
40 #include "alstring.h"
42 #include "backends/base.h"
44 #ifndef DSSPEAKER_5POINT1
45 # define DSSPEAKER_5POINT1 0x00000006
46 #endif
47 #ifndef DSSPEAKER_5POINT1_BACK
48 # define DSSPEAKER_5POINT1_BACK 0x00000006
49 #endif
50 #ifndef DSSPEAKER_7POINT1
51 # define DSSPEAKER_7POINT1 0x00000007
52 #endif
53 #ifndef DSSPEAKER_7POINT1_SURROUND
54 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
55 #endif
56 #ifndef DSSPEAKER_5POINT1_SURROUND
57 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
58 #endif
61 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
62 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
64 #define DEVNAME_HEAD "OpenAL Soft on "
67 #ifdef HAVE_DYNLOAD
68 static void *ds_handle;
69 static HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
70 static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
71 static HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
72 static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
74 #define DirectSoundCreate pDirectSoundCreate
75 #define DirectSoundEnumerateW pDirectSoundEnumerateW
76 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
77 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
78 #endif
81 static ALCboolean DSoundLoad(void)
83 #ifdef HAVE_DYNLOAD
84 if(!ds_handle)
86 ds_handle = LoadLib("dsound.dll");
87 if(ds_handle == NULL)
89 ERR("Failed to load dsound.dll\n");
90 return ALC_FALSE;
93 #define LOAD_FUNC(f) do { \
94 p##f = GetSymbol(ds_handle, #f); \
95 if(p##f == NULL) { \
96 CloseLib(ds_handle); \
97 ds_handle = NULL; \
98 return ALC_FALSE; \
99 } \
100 } while(0)
101 LOAD_FUNC(DirectSoundCreate);
102 LOAD_FUNC(DirectSoundEnumerateW);
103 LOAD_FUNC(DirectSoundCaptureCreate);
104 LOAD_FUNC(DirectSoundCaptureEnumerateW);
105 #undef LOAD_FUNC
107 #endif
108 return ALC_TRUE;
112 #define MAX_UPDATES 128
114 typedef struct {
115 al_string name;
116 GUID guid;
117 } DevMap;
118 TYPEDEF_VECTOR(DevMap, vector_DevMap)
120 static vector_DevMap PlaybackDevices;
121 static vector_DevMap CaptureDevices;
123 static void clear_devlist(vector_DevMap *list)
125 #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
126 VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
127 VECTOR_RESIZE(*list, 0, 0);
128 #undef DEINIT_STR
131 static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
133 vector_DevMap *devices = data;
134 OLECHAR *guidstr = NULL;
135 DevMap entry;
136 HRESULT hr;
137 int count;
139 if(!guid)
140 return TRUE;
142 AL_STRING_INIT(entry.name);
144 count = 0;
145 while(1)
147 const DevMap *iter;
149 alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
150 alstr_append_wcstr(&entry.name, desc);
151 if(count != 0)
153 char str[64];
154 snprintf(str, sizeof(str), " #%d", count+1);
155 alstr_append_cstr(&entry.name, str);
158 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
159 VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
160 if(iter == VECTOR_END(*devices)) break;
161 #undef MATCH_ENTRY
162 count++;
164 entry.guid = *guid;
166 hr = StringFromCLSID(guid, &guidstr);
167 if(SUCCEEDED(hr))
169 TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
170 CoTaskMemFree(guidstr);
173 VECTOR_PUSH_BACK(*devices, entry);
175 return TRUE;
179 typedef struct ALCdsoundPlayback {
180 DERIVE_FROM_TYPE(ALCbackend);
182 IDirectSound *DS;
183 IDirectSoundBuffer *PrimaryBuffer;
184 IDirectSoundBuffer *Buffer;
185 IDirectSoundNotify *Notifies;
186 HANDLE NotifyEvent;
188 ATOMIC(ALenum) killNow;
189 althrd_t thread;
190 } ALCdsoundPlayback;
192 static int ALCdsoundPlayback_mixerProc(void *ptr);
194 static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
195 static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self);
196 static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
197 static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
198 static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
199 static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
200 static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
201 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
202 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
203 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
204 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
205 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
207 DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback);
210 static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device)
212 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
213 SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
215 self->DS = NULL;
216 self->PrimaryBuffer = NULL;
217 self->Buffer = NULL;
218 self->Notifies = NULL;
219 self->NotifyEvent = NULL;
220 ATOMIC_INIT(&self->killNow, AL_TRUE);
223 static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self)
225 if(self->Notifies)
226 IDirectSoundNotify_Release(self->Notifies);
227 self->Notifies = NULL;
228 if(self->Buffer)
229 IDirectSoundBuffer_Release(self->Buffer);
230 self->Buffer = NULL;
231 if(self->PrimaryBuffer != NULL)
232 IDirectSoundBuffer_Release(self->PrimaryBuffer);
233 self->PrimaryBuffer = NULL;
235 if(self->DS)
236 IDirectSound_Release(self->DS);
237 self->DS = NULL;
238 if(self->NotifyEvent)
239 CloseHandle(self->NotifyEvent);
240 self->NotifyEvent = NULL;
242 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
246 FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
248 ALCdsoundPlayback *self = ptr;
249 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
250 DSBCAPS DSBCaps;
251 DWORD LastCursor = 0;
252 DWORD PlayCursor;
253 void *WritePtr1, *WritePtr2;
254 DWORD WriteCnt1, WriteCnt2;
255 BOOL Playing = FALSE;
256 DWORD FrameSize;
257 DWORD FragSize;
258 DWORD avail;
259 HRESULT err;
261 SetRTPriority();
262 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
264 memset(&DSBCaps, 0, sizeof(DSBCaps));
265 DSBCaps.dwSize = sizeof(DSBCaps);
266 err = IDirectSoundBuffer_GetCaps(self->Buffer, &DSBCaps);
267 if(FAILED(err))
269 ERR("Failed to get buffer caps: 0x%lx\n", err);
270 ALCdevice_Lock(device);
271 aluHandleDisconnect(device, "Failure retrieving playback buffer info: 0x%lx", err);
272 ALCdevice_Unlock(device);
273 return 1;
276 FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
277 FragSize = device->UpdateSize * FrameSize;
279 IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
280 while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
281 ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
283 // Get current play cursor
284 IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
285 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
287 if(avail < FragSize)
289 if(!Playing)
291 err = IDirectSoundBuffer_Play(self->Buffer, 0, 0, DSBPLAY_LOOPING);
292 if(FAILED(err))
294 ERR("Failed to play buffer: 0x%lx\n", err);
295 ALCdevice_Lock(device);
296 aluHandleDisconnect(device, "Failure starting playback: 0x%lx", err);
297 ALCdevice_Unlock(device);
298 return 1;
300 Playing = TRUE;
303 avail = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
304 if(avail != WAIT_OBJECT_0)
305 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
306 continue;
308 avail -= avail%FragSize;
310 // Lock output buffer
311 WriteCnt1 = 0;
312 WriteCnt2 = 0;
313 err = IDirectSoundBuffer_Lock(self->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
315 // If the buffer is lost, restore it and lock
316 if(err == DSERR_BUFFERLOST)
318 WARN("Buffer lost, restoring...\n");
319 err = IDirectSoundBuffer_Restore(self->Buffer);
320 if(SUCCEEDED(err))
322 Playing = FALSE;
323 LastCursor = 0;
324 err = IDirectSoundBuffer_Lock(self->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
328 // Successfully locked the output buffer
329 if(SUCCEEDED(err))
331 // If we have an active context, mix data directly into output buffer otherwise fill with silence
332 ALCdevice_Lock(device);
333 aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
334 aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
335 ALCdevice_Unlock(device);
337 // Unlock output buffer only when successfully locked
338 IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
340 else
342 ERR("Buffer lock error: %#lx\n", err);
343 ALCdevice_Lock(device);
344 aluHandleDisconnect(device, "Failed to lock output buffer: 0x%lx", err);
345 ALCdevice_Unlock(device);
346 return 1;
349 // Update old write cursor location
350 LastCursor += WriteCnt1+WriteCnt2;
351 LastCursor %= DSBCaps.dwBufferBytes;
354 return 0;
357 static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName)
359 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
360 const GUID *guid = NULL;
361 HRESULT hr, hrcom;
363 if(VECTOR_SIZE(PlaybackDevices) == 0)
365 /* Initialize COM to prevent name truncation */
366 hrcom = CoInitialize(NULL);
367 hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
368 if(FAILED(hr))
369 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
370 if(SUCCEEDED(hrcom))
371 CoUninitialize();
374 if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
376 deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
377 guid = &VECTOR_FRONT(PlaybackDevices).guid;
379 else
381 const DevMap *iter;
383 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
384 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
385 #undef MATCH_NAME
386 if(iter == VECTOR_END(PlaybackDevices))
387 return ALC_INVALID_VALUE;
388 guid = &iter->guid;
391 hr = DS_OK;
392 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
393 if(self->NotifyEvent == NULL)
394 hr = E_FAIL;
396 //DirectSound Init code
397 if(SUCCEEDED(hr))
398 hr = DirectSoundCreate(guid, &self->DS, NULL);
399 if(SUCCEEDED(hr))
400 hr = IDirectSound_SetCooperativeLevel(self->DS, GetForegroundWindow(), DSSCL_PRIORITY);
401 if(FAILED(hr))
403 if(self->DS)
404 IDirectSound_Release(self->DS);
405 self->DS = NULL;
406 if(self->NotifyEvent)
407 CloseHandle(self->NotifyEvent);
408 self->NotifyEvent = NULL;
410 ERR("Device init failed: 0x%08lx\n", hr);
411 return ALC_INVALID_VALUE;
414 alstr_copy_cstr(&device->DeviceName, deviceName);
416 return ALC_NO_ERROR;
419 static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
421 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
422 DSBUFFERDESC DSBDescription;
423 WAVEFORMATEXTENSIBLE OutputType;
424 DWORD speakers;
425 HRESULT hr;
427 memset(&OutputType, 0, sizeof(OutputType));
429 if(self->Notifies)
430 IDirectSoundNotify_Release(self->Notifies);
431 self->Notifies = NULL;
432 if(self->Buffer)
433 IDirectSoundBuffer_Release(self->Buffer);
434 self->Buffer = NULL;
435 if(self->PrimaryBuffer != NULL)
436 IDirectSoundBuffer_Release(self->PrimaryBuffer);
437 self->PrimaryBuffer = NULL;
439 switch(device->FmtType)
441 case DevFmtByte:
442 device->FmtType = DevFmtUByte;
443 break;
444 case DevFmtFloat:
445 if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
446 break;
447 /* fall-through */
448 case DevFmtUShort:
449 device->FmtType = DevFmtShort;
450 break;
451 case DevFmtUInt:
452 device->FmtType = DevFmtInt;
453 break;
454 case DevFmtUByte:
455 case DevFmtShort:
456 case DevFmtInt:
457 break;
460 hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
461 if(SUCCEEDED(hr))
463 speakers = DSSPEAKER_CONFIG(speakers);
464 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
466 if(speakers == DSSPEAKER_MONO)
467 device->FmtChans = DevFmtMono;
468 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
469 device->FmtChans = DevFmtStereo;
470 else if(speakers == DSSPEAKER_QUAD)
471 device->FmtChans = DevFmtQuad;
472 else if(speakers == DSSPEAKER_5POINT1_SURROUND)
473 device->FmtChans = DevFmtX51;
474 else if(speakers == DSSPEAKER_5POINT1_BACK)
475 device->FmtChans = DevFmtX51Rear;
476 else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
477 device->FmtChans = DevFmtX71;
478 else
479 ERR("Unknown system speaker config: 0x%lx\n", speakers);
481 device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
482 speakers == DSSPEAKER_HEADPHONE);
484 switch(device->FmtChans)
486 case DevFmtMono:
487 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
488 break;
489 case DevFmtAmbi3D:
490 device->FmtChans = DevFmtStereo;
491 /*fall-through*/
492 case DevFmtStereo:
493 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
494 SPEAKER_FRONT_RIGHT;
495 break;
496 case DevFmtQuad:
497 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
498 SPEAKER_FRONT_RIGHT |
499 SPEAKER_BACK_LEFT |
500 SPEAKER_BACK_RIGHT;
501 break;
502 case DevFmtX51:
503 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
504 SPEAKER_FRONT_RIGHT |
505 SPEAKER_FRONT_CENTER |
506 SPEAKER_LOW_FREQUENCY |
507 SPEAKER_SIDE_LEFT |
508 SPEAKER_SIDE_RIGHT;
509 break;
510 case DevFmtX51Rear:
511 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
512 SPEAKER_FRONT_RIGHT |
513 SPEAKER_FRONT_CENTER |
514 SPEAKER_LOW_FREQUENCY |
515 SPEAKER_BACK_LEFT |
516 SPEAKER_BACK_RIGHT;
517 break;
518 case DevFmtX61:
519 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
520 SPEAKER_FRONT_RIGHT |
521 SPEAKER_FRONT_CENTER |
522 SPEAKER_LOW_FREQUENCY |
523 SPEAKER_BACK_CENTER |
524 SPEAKER_SIDE_LEFT |
525 SPEAKER_SIDE_RIGHT;
526 break;
527 case DevFmtX71:
528 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
529 SPEAKER_FRONT_RIGHT |
530 SPEAKER_FRONT_CENTER |
531 SPEAKER_LOW_FREQUENCY |
532 SPEAKER_BACK_LEFT |
533 SPEAKER_BACK_RIGHT |
534 SPEAKER_SIDE_LEFT |
535 SPEAKER_SIDE_RIGHT;
536 break;
539 retry_open:
540 hr = S_OK;
541 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
542 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
543 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
544 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
545 OutputType.Format.nSamplesPerSec = device->Frequency;
546 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
547 OutputType.Format.cbSize = 0;
550 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
552 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
553 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
554 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
555 if(device->FmtType == DevFmtFloat)
556 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
557 else
558 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
560 if(self->PrimaryBuffer)
561 IDirectSoundBuffer_Release(self->PrimaryBuffer);
562 self->PrimaryBuffer = NULL;
564 else
566 if(SUCCEEDED(hr) && !self->PrimaryBuffer)
568 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
569 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
570 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
571 hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->PrimaryBuffer, NULL);
573 if(SUCCEEDED(hr))
574 hr = IDirectSoundBuffer_SetFormat(self->PrimaryBuffer,&OutputType.Format);
577 if(SUCCEEDED(hr))
579 if(device->NumUpdates > MAX_UPDATES)
581 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
582 MAX_UPDATES-1) / MAX_UPDATES;
583 device->NumUpdates = MAX_UPDATES;
586 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
587 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
588 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
589 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
590 OutputType.Format.nBlockAlign;
591 DSBDescription.lpwfxFormat=&OutputType.Format;
592 hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->Buffer, NULL);
593 if(FAILED(hr) && device->FmtType == DevFmtFloat)
595 device->FmtType = DevFmtShort;
596 goto retry_open;
600 if(SUCCEEDED(hr))
602 hr = IDirectSoundBuffer_QueryInterface(self->Buffer, &IID_IDirectSoundNotify, (void**)&self->Notifies);
603 if(SUCCEEDED(hr))
605 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
606 ALuint i;
608 for(i = 0;i < device->NumUpdates;++i)
610 notifies[i].dwOffset = i * device->UpdateSize *
611 OutputType.Format.nBlockAlign;
612 notifies[i].hEventNotify = self->NotifyEvent;
614 if(IDirectSoundNotify_SetNotificationPositions(self->Notifies, device->NumUpdates, notifies) != DS_OK)
615 hr = E_FAIL;
619 if(FAILED(hr))
621 if(self->Notifies != NULL)
622 IDirectSoundNotify_Release(self->Notifies);
623 self->Notifies = NULL;
624 if(self->Buffer != NULL)
625 IDirectSoundBuffer_Release(self->Buffer);
626 self->Buffer = NULL;
627 if(self->PrimaryBuffer != NULL)
628 IDirectSoundBuffer_Release(self->PrimaryBuffer);
629 self->PrimaryBuffer = NULL;
630 return ALC_FALSE;
633 ResetEvent(self->NotifyEvent);
634 SetDefaultWFXChannelOrder(device);
636 return ALC_TRUE;
639 static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
641 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
642 if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
643 return ALC_FALSE;
645 return ALC_TRUE;
648 static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
650 int res;
652 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
653 return;
654 althrd_join(self->thread, &res);
656 IDirectSoundBuffer_Stop(self->Buffer);
661 typedef struct ALCdsoundCapture {
662 DERIVE_FROM_TYPE(ALCbackend);
664 IDirectSoundCapture *DSC;
665 IDirectSoundCaptureBuffer *DSCbuffer;
666 DWORD BufferBytes;
667 DWORD Cursor;
669 ll_ringbuffer_t *Ring;
670 } ALCdsoundCapture;
672 static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
673 static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self);
674 static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
675 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
676 static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
677 static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
678 static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
679 static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
680 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
681 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
682 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
683 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
685 DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture);
687 static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device)
689 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
690 SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
692 self->DSC = NULL;
693 self->DSCbuffer = NULL;
694 self->Ring = NULL;
697 static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self)
699 ll_ringbuffer_free(self->Ring);
700 self->Ring = NULL;
702 if(self->DSCbuffer != NULL)
704 IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
705 IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
706 self->DSCbuffer = NULL;
709 if(self->DSC)
710 IDirectSoundCapture_Release(self->DSC);
711 self->DSC = NULL;
713 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
717 static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
719 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
720 WAVEFORMATEXTENSIBLE InputType;
721 DSCBUFFERDESC DSCBDescription;
722 const GUID *guid = NULL;
723 HRESULT hr, hrcom;
724 ALuint samples;
726 if(VECTOR_SIZE(CaptureDevices) == 0)
728 /* Initialize COM to prevent name truncation */
729 hrcom = CoInitialize(NULL);
730 hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
731 if(FAILED(hr))
732 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
733 if(SUCCEEDED(hrcom))
734 CoUninitialize();
737 if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
739 deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
740 guid = &VECTOR_FRONT(CaptureDevices).guid;
742 else
744 const DevMap *iter;
746 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
747 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
748 #undef MATCH_NAME
749 if(iter == VECTOR_END(CaptureDevices))
750 return ALC_INVALID_VALUE;
751 guid = &iter->guid;
754 switch(device->FmtType)
756 case DevFmtByte:
757 case DevFmtUShort:
758 case DevFmtUInt:
759 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
760 return ALC_INVALID_ENUM;
762 case DevFmtUByte:
763 case DevFmtShort:
764 case DevFmtInt:
765 case DevFmtFloat:
766 break;
769 memset(&InputType, 0, sizeof(InputType));
770 switch(device->FmtChans)
772 case DevFmtMono:
773 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
774 break;
775 case DevFmtStereo:
776 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
777 SPEAKER_FRONT_RIGHT;
778 break;
779 case DevFmtQuad:
780 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
781 SPEAKER_FRONT_RIGHT |
782 SPEAKER_BACK_LEFT |
783 SPEAKER_BACK_RIGHT;
784 break;
785 case DevFmtX51:
786 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
787 SPEAKER_FRONT_RIGHT |
788 SPEAKER_FRONT_CENTER |
789 SPEAKER_LOW_FREQUENCY |
790 SPEAKER_SIDE_LEFT |
791 SPEAKER_SIDE_RIGHT;
792 break;
793 case DevFmtX51Rear:
794 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
795 SPEAKER_FRONT_RIGHT |
796 SPEAKER_FRONT_CENTER |
797 SPEAKER_LOW_FREQUENCY |
798 SPEAKER_BACK_LEFT |
799 SPEAKER_BACK_RIGHT;
800 break;
801 case DevFmtX61:
802 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
803 SPEAKER_FRONT_RIGHT |
804 SPEAKER_FRONT_CENTER |
805 SPEAKER_LOW_FREQUENCY |
806 SPEAKER_BACK_CENTER |
807 SPEAKER_SIDE_LEFT |
808 SPEAKER_SIDE_RIGHT;
809 break;
810 case DevFmtX71:
811 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
812 SPEAKER_FRONT_RIGHT |
813 SPEAKER_FRONT_CENTER |
814 SPEAKER_LOW_FREQUENCY |
815 SPEAKER_BACK_LEFT |
816 SPEAKER_BACK_RIGHT |
817 SPEAKER_SIDE_LEFT |
818 SPEAKER_SIDE_RIGHT;
819 break;
820 case DevFmtAmbi3D:
821 WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
822 return ALC_INVALID_ENUM;
825 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
826 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
827 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
828 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
829 InputType.Format.nSamplesPerSec = device->Frequency;
830 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
831 InputType.Format.cbSize = 0;
832 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
833 if(device->FmtType == DevFmtFloat)
834 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
835 else
836 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
838 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
840 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
841 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
844 samples = device->UpdateSize * device->NumUpdates;
845 samples = maxu(samples, 100 * device->Frequency / 1000);
847 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
848 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
849 DSCBDescription.dwFlags = 0;
850 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
851 DSCBDescription.lpwfxFormat = &InputType.Format;
853 //DirectSoundCapture Init code
854 hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
855 if(SUCCEEDED(hr))
856 hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
857 if(SUCCEEDED(hr))
859 self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates,
860 InputType.Format.nBlockAlign, false);
861 if(self->Ring == NULL)
862 hr = DSERR_OUTOFMEMORY;
865 if(FAILED(hr))
867 ERR("Device init failed: 0x%08lx\n", hr);
869 ll_ringbuffer_free(self->Ring);
870 self->Ring = NULL;
871 if(self->DSCbuffer != NULL)
872 IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
873 self->DSCbuffer = NULL;
874 if(self->DSC)
875 IDirectSoundCapture_Release(self->DSC);
876 self->DSC = NULL;
878 return ALC_INVALID_VALUE;
881 self->BufferBytes = DSCBDescription.dwBufferBytes;
882 SetDefaultWFXChannelOrder(device);
884 alstr_copy_cstr(&device->DeviceName, deviceName);
886 return ALC_NO_ERROR;
889 static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
891 HRESULT hr;
893 hr = IDirectSoundCaptureBuffer_Start(self->DSCbuffer, DSCBSTART_LOOPING);
894 if(FAILED(hr))
896 ERR("start failed: 0x%08lx\n", hr);
897 aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
898 "Failure starting capture: 0x%lx", hr);
899 return ALC_FALSE;
902 return ALC_TRUE;
905 static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
907 HRESULT hr;
909 hr = IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
910 if(FAILED(hr))
912 ERR("stop failed: 0x%08lx\n", hr);
913 aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
914 "Failure stopping capture: 0x%lx", hr);
918 static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
920 ll_ringbuffer_read(self->Ring, buffer, samples);
921 return ALC_NO_ERROR;
924 static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
926 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
927 DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
928 void *ReadPtr1, *ReadPtr2;
929 DWORD ReadCnt1, ReadCnt2;
930 DWORD FrameSize;
931 HRESULT hr;
933 if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
934 goto done;
936 FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
937 BufferBytes = self->BufferBytes;
938 LastCursor = self->Cursor;
940 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(self->DSCbuffer, NULL, &ReadCursor);
941 if(SUCCEEDED(hr))
943 NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
944 if(NumBytes == 0)
945 goto done;
946 hr = IDirectSoundCaptureBuffer_Lock(self->DSCbuffer, LastCursor, NumBytes,
947 &ReadPtr1, &ReadCnt1,
948 &ReadPtr2, &ReadCnt2, 0);
950 if(SUCCEEDED(hr))
952 ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
953 if(ReadPtr2 != NULL)
954 ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
955 hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
956 ReadPtr1, ReadCnt1,
957 ReadPtr2, ReadCnt2);
958 self->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
961 if(FAILED(hr))
963 ERR("update failed: 0x%08lx\n", hr);
964 aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr);
967 done:
968 return (ALCuint)ll_ringbuffer_read_space(self->Ring);
972 static inline void AppendAllDevicesList2(const DevMap *entry)
973 { AppendAllDevicesList(alstr_get_cstr(entry->name)); }
974 static inline void AppendCaptureDeviceList2(const DevMap *entry)
975 { AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
977 typedef struct ALCdsoundBackendFactory {
978 DERIVE_FROM_TYPE(ALCbackendFactory);
979 } ALCdsoundBackendFactory;
980 #define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } }
982 ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
984 static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
985 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
986 static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
987 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
988 static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
989 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
992 ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void)
994 static ALCdsoundBackendFactory factory = ALCDSOUNDBACKENDFACTORY_INITIALIZER;
995 return STATIC_CAST(ALCbackendFactory, &factory);
999 static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory* UNUSED(self))
1001 VECTOR_INIT(PlaybackDevices);
1002 VECTOR_INIT(CaptureDevices);
1004 if(!DSoundLoad())
1005 return ALC_FALSE;
1006 return ALC_TRUE;
1009 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory* UNUSED(self))
1011 clear_devlist(&PlaybackDevices);
1012 VECTOR_DEINIT(PlaybackDevices);
1014 clear_devlist(&CaptureDevices);
1015 VECTOR_DEINIT(CaptureDevices);
1017 #ifdef HAVE_DYNLOAD
1018 if(ds_handle)
1019 CloseLib(ds_handle);
1020 ds_handle = NULL;
1021 #endif
1024 static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory* UNUSED(self), ALCbackend_Type type)
1026 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
1027 return ALC_TRUE;
1028 return ALC_FALSE;
1031 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
1033 HRESULT hr, hrcom;
1035 /* Initialize COM to prevent name truncation */
1036 hrcom = CoInitialize(NULL);
1037 switch(type)
1039 case ALL_DEVICE_PROBE:
1040 clear_devlist(&PlaybackDevices);
1041 hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
1042 if(FAILED(hr))
1043 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
1044 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
1045 break;
1047 case CAPTURE_DEVICE_PROBE:
1048 clear_devlist(&CaptureDevices);
1049 hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
1050 if(FAILED(hr))
1051 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
1052 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
1053 break;
1055 if(SUCCEEDED(hrcom))
1056 CoUninitialize();
1059 static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1061 if(type == ALCbackend_Playback)
1063 ALCdsoundPlayback *backend;
1064 NEW_OBJ(backend, ALCdsoundPlayback)(device);
1065 if(!backend) return NULL;
1066 return STATIC_CAST(ALCbackend, backend);
1069 if(type == ALCbackend_Capture)
1071 ALCdsoundCapture *backend;
1072 NEW_OBJ(backend, ALCdsoundCapture)(device);
1073 if(!backend) return NULL;
1074 return STATIC_CAST(ALCbackend, backend);
1077 return NULL;