Rename SetThreadName to althrd_setname
[openal-soft.git] / Alc / backends / dsound.c
blobb09208ae2bb228a71181fdcc56696f36b7ed1c79
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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 "threads.h"
38 #include "compat.h"
39 #include "alstring.h"
41 #ifndef DSSPEAKER_5POINT1
42 # define DSSPEAKER_5POINT1 0x00000006
43 #endif
44 #ifndef DSSPEAKER_7POINT1
45 # define DSSPEAKER_7POINT1 0x00000007
46 #endif
47 #ifndef DSSPEAKER_7POINT1_SURROUND
48 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
49 #endif
50 #ifndef DSSPEAKER_5POINT1_SURROUND
51 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
52 #endif
55 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
56 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
59 static void *ds_handle;
60 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
61 static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
62 static HRESULT (WINAPI *pDirectSoundCaptureCreate)(LPCGUID pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
63 static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
65 #define DirectSoundCreate pDirectSoundCreate
66 #define DirectSoundEnumerateW pDirectSoundEnumerateW
67 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
68 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
71 typedef struct {
72 // DirectSound Playback Device
73 IDirectSound *DS;
74 IDirectSoundBuffer *PrimaryBuffer;
75 IDirectSoundBuffer *Buffer;
76 IDirectSoundNotify *Notifies;
77 HANDLE NotifyEvent;
79 volatile int killNow;
80 althrd_t thread;
81 } DSoundPlaybackData;
83 typedef struct {
84 // DirectSound Capture Device
85 IDirectSoundCapture *DSC;
86 IDirectSoundCaptureBuffer *DSCbuffer;
87 DWORD BufferBytes;
88 DWORD Cursor;
89 RingBuffer *Ring;
90 } DSoundCaptureData;
93 typedef struct {
94 al_string name;
95 GUID guid;
96 } DevMap;
97 DECL_VECTOR(DevMap)
99 vector_DevMap PlaybackDevices;
100 vector_DevMap CaptureDevices;
103 #define MAX_UPDATES 128
106 static ALCboolean DSoundLoad(void)
108 if(!ds_handle)
110 ds_handle = LoadLib("dsound.dll");
111 if(ds_handle == NULL)
113 ERR("Failed to load dsound.dll\n");
114 return ALC_FALSE;
117 #define LOAD_FUNC(f) do { \
118 p##f = GetSymbol(ds_handle, #f); \
119 if(p##f == NULL) { \
120 CloseLib(ds_handle); \
121 ds_handle = NULL; \
122 return ALC_FALSE; \
124 } while(0)
125 LOAD_FUNC(DirectSoundCreate);
126 LOAD_FUNC(DirectSoundEnumerateW);
127 LOAD_FUNC(DirectSoundCaptureCreate);
128 LOAD_FUNC(DirectSoundCaptureEnumerateW);
129 #undef LOAD_FUNC
131 return ALC_TRUE;
135 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCWSTR desc, LPCWSTR UNUSED(drvname), LPVOID data)
137 vector_DevMap *devices = data;
138 LPOLESTR guidstr = NULL;
139 DevMap *iter, *end;
140 DevMap entry;
141 HRESULT hr;
142 int count;
144 if(!guid)
145 return TRUE;
147 AL_STRING_INIT(entry.name);
149 count = 0;
150 do {
151 al_string_copy_wcstr(&entry.name, desc);
152 if(count != 0)
154 char str[64];
155 snprintf(str, sizeof(str), " #%d", count+1);
156 al_string_append_cstr(&entry.name, str);
158 count++;
160 iter = VECTOR_ITER_BEGIN(*devices);
161 end = VECTOR_ITER_END(*devices);
162 for(;iter != end;++iter)
164 if(al_string_cmp(entry.name, iter->name) == 0)
165 break;
167 } while(iter != end);
168 entry.guid = *guid;
170 hr = StringFromCLSID(guid, &guidstr);
171 if(SUCCEEDED(hr))
173 TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
174 CoTaskMemFree(guidstr);
177 VECTOR_PUSH_BACK(*devices, entry);
179 return TRUE;
183 FORCE_ALIGN static int DSoundPlaybackProc(void *ptr)
185 ALCdevice *Device = (ALCdevice*)ptr;
186 DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData;
187 DSBCAPS DSBCaps;
188 DWORD LastCursor = 0;
189 DWORD PlayCursor;
190 VOID *WritePtr1, *WritePtr2;
191 DWORD WriteCnt1, WriteCnt2;
192 BOOL Playing = FALSE;
193 DWORD FrameSize;
194 DWORD FragSize;
195 DWORD avail;
196 HRESULT err;
198 SetRTPriority();
199 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
201 memset(&DSBCaps, 0, sizeof(DSBCaps));
202 DSBCaps.dwSize = sizeof(DSBCaps);
203 err = IDirectSoundBuffer_GetCaps(data->Buffer, &DSBCaps);
204 if(FAILED(err))
206 ERR("Failed to get buffer caps: 0x%lx\n", err);
207 ALCdevice_Lock(Device);
208 aluHandleDisconnect(Device);
209 ALCdevice_Unlock(Device);
210 return 1;
213 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
214 FragSize = Device->UpdateSize * FrameSize;
216 IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &LastCursor, NULL);
217 while(!data->killNow)
219 // Get current play cursor
220 IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &PlayCursor, NULL);
221 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
223 if(avail < FragSize)
225 if(!Playing)
227 err = IDirectSoundBuffer_Play(data->Buffer, 0, 0, DSBPLAY_LOOPING);
228 if(FAILED(err))
230 ERR("Failed to play buffer: 0x%lx\n", err);
231 ALCdevice_Lock(Device);
232 aluHandleDisconnect(Device);
233 ALCdevice_Unlock(Device);
234 return 1;
236 Playing = TRUE;
239 avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
240 if(avail != WAIT_OBJECT_0)
241 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
242 continue;
244 avail -= avail%FragSize;
246 // Lock output buffer
247 WriteCnt1 = 0;
248 WriteCnt2 = 0;
249 err = IDirectSoundBuffer_Lock(data->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
251 // If the buffer is lost, restore it and lock
252 if(err == DSERR_BUFFERLOST)
254 WARN("Buffer lost, restoring...\n");
255 err = IDirectSoundBuffer_Restore(data->Buffer);
256 if(SUCCEEDED(err))
258 Playing = FALSE;
259 LastCursor = 0;
260 err = IDirectSoundBuffer_Lock(data->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
264 // Successfully locked the output buffer
265 if(SUCCEEDED(err))
267 // If we have an active context, mix data directly into output buffer otherwise fill with silence
268 aluMixData(Device, WritePtr1, WriteCnt1/FrameSize);
269 aluMixData(Device, WritePtr2, WriteCnt2/FrameSize);
271 // Unlock output buffer only when successfully locked
272 IDirectSoundBuffer_Unlock(data->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
274 else
276 ERR("Buffer lock error: %#lx\n", err);
277 ALCdevice_Lock(Device);
278 aluHandleDisconnect(Device);
279 ALCdevice_Unlock(Device);
280 return 1;
283 // Update old write cursor location
284 LastCursor += WriteCnt1+WriteCnt2;
285 LastCursor %= DSBCaps.dwBufferBytes;
288 return 0;
291 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
293 DSoundPlaybackData *data = NULL;
294 LPGUID guid = NULL;
295 HRESULT hr, hrcom;
297 if(VECTOR_SIZE(PlaybackDevices) == 0)
299 /* Initialize COM to prevent name truncation */
300 hrcom = CoInitialize(NULL);
301 hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
302 if(FAILED(hr))
303 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
304 if(SUCCEEDED(hrcom))
305 CoUninitialize();
308 if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
310 deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
311 guid = &VECTOR_FRONT(PlaybackDevices).guid;
313 else
315 DevMap *iter, *end;
317 iter = VECTOR_ITER_BEGIN(PlaybackDevices);
318 end = VECTOR_ITER_END(PlaybackDevices);
319 for(;iter != end;++iter)
321 if(al_string_cmp_cstr(iter->name, deviceName) == 0)
323 guid = &iter->guid;
324 break;
327 if(iter == end)
328 return ALC_INVALID_VALUE;
331 //Initialise requested device
332 data = calloc(1, sizeof(DSoundPlaybackData));
333 if(!data)
334 return ALC_OUT_OF_MEMORY;
336 hr = DS_OK;
337 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
338 if(data->NotifyEvent == NULL)
339 hr = E_FAIL;
341 //DirectSound Init code
342 if(SUCCEEDED(hr))
343 hr = DirectSoundCreate(guid, &data->DS, NULL);
344 if(SUCCEEDED(hr))
345 hr = IDirectSound_SetCooperativeLevel(data->DS, GetForegroundWindow(), DSSCL_PRIORITY);
346 if(FAILED(hr))
348 if(data->DS)
349 IDirectSound_Release(data->DS);
350 if(data->NotifyEvent)
351 CloseHandle(data->NotifyEvent);
352 free(data);
353 ERR("Device init failed: 0x%08lx\n", hr);
354 return ALC_INVALID_VALUE;
357 al_string_copy_cstr(&device->DeviceName, deviceName);
358 device->ExtraData = data;
359 return ALC_NO_ERROR;
362 static void DSoundClosePlayback(ALCdevice *device)
364 DSoundPlaybackData *data = device->ExtraData;
366 if(data->Notifies)
367 IDirectSoundNotify_Release(data->Notifies);
368 data->Notifies = NULL;
369 if(data->Buffer)
370 IDirectSoundBuffer_Release(data->Buffer);
371 data->Buffer = NULL;
372 if(data->PrimaryBuffer != NULL)
373 IDirectSoundBuffer_Release(data->PrimaryBuffer);
374 data->PrimaryBuffer = NULL;
376 IDirectSound_Release(data->DS);
377 CloseHandle(data->NotifyEvent);
378 free(data);
379 device->ExtraData = NULL;
382 static ALCboolean DSoundResetPlayback(ALCdevice *device)
384 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
385 DSBUFFERDESC DSBDescription;
386 WAVEFORMATEXTENSIBLE OutputType;
387 DWORD speakers;
388 HRESULT hr;
390 memset(&OutputType, 0, sizeof(OutputType));
392 if(data->Notifies)
393 IDirectSoundNotify_Release(data->Notifies);
394 data->Notifies = NULL;
395 if(data->Buffer)
396 IDirectSoundBuffer_Release(data->Buffer);
397 data->Buffer = NULL;
398 if(data->PrimaryBuffer != NULL)
399 IDirectSoundBuffer_Release(data->PrimaryBuffer);
400 data->PrimaryBuffer = NULL;
402 switch(device->FmtType)
404 case DevFmtByte:
405 device->FmtType = DevFmtUByte;
406 break;
407 case DevFmtFloat:
408 if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
409 break;
410 /* fall-through */
411 case DevFmtUShort:
412 device->FmtType = DevFmtShort;
413 break;
414 case DevFmtUInt:
415 device->FmtType = DevFmtInt;
416 break;
417 case DevFmtUByte:
418 case DevFmtShort:
419 case DevFmtInt:
420 break;
423 hr = IDirectSound_GetSpeakerConfig(data->DS, &speakers);
424 if(SUCCEEDED(hr))
426 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
428 speakers = DSSPEAKER_CONFIG(speakers);
429 if(speakers == DSSPEAKER_MONO)
430 device->FmtChans = DevFmtMono;
431 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
432 device->FmtChans = DevFmtStereo;
433 else if(speakers == DSSPEAKER_QUAD)
434 device->FmtChans = DevFmtQuad;
435 else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND)
436 device->FmtChans = DevFmtX51;
437 else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
438 device->FmtChans = DevFmtX71;
439 else
440 ERR("Unknown system speaker config: 0x%lx\n", speakers);
443 switch(device->FmtChans)
445 case DevFmtMono:
446 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
447 break;
448 case DevFmtStereo:
449 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
450 SPEAKER_FRONT_RIGHT;
451 break;
452 case DevFmtQuad:
453 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
454 SPEAKER_FRONT_RIGHT |
455 SPEAKER_BACK_LEFT |
456 SPEAKER_BACK_RIGHT;
457 break;
458 case DevFmtX51:
459 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
460 SPEAKER_FRONT_RIGHT |
461 SPEAKER_FRONT_CENTER |
462 SPEAKER_LOW_FREQUENCY |
463 SPEAKER_BACK_LEFT |
464 SPEAKER_BACK_RIGHT;
465 break;
466 case DevFmtX51Side:
467 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
468 SPEAKER_FRONT_RIGHT |
469 SPEAKER_FRONT_CENTER |
470 SPEAKER_LOW_FREQUENCY |
471 SPEAKER_SIDE_LEFT |
472 SPEAKER_SIDE_RIGHT;
473 break;
474 case DevFmtX61:
475 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
476 SPEAKER_FRONT_RIGHT |
477 SPEAKER_FRONT_CENTER |
478 SPEAKER_LOW_FREQUENCY |
479 SPEAKER_BACK_CENTER |
480 SPEAKER_SIDE_LEFT |
481 SPEAKER_SIDE_RIGHT;
482 break;
483 case DevFmtX71:
484 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
485 SPEAKER_FRONT_RIGHT |
486 SPEAKER_FRONT_CENTER |
487 SPEAKER_LOW_FREQUENCY |
488 SPEAKER_BACK_LEFT |
489 SPEAKER_BACK_RIGHT |
490 SPEAKER_SIDE_LEFT |
491 SPEAKER_SIDE_RIGHT;
492 break;
495 retry_open:
496 hr = S_OK;
497 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
498 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
499 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
500 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
501 OutputType.Format.nSamplesPerSec = device->Frequency;
502 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
503 OutputType.Format.cbSize = 0;
506 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
508 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
509 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
510 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
511 if(device->FmtType == DevFmtFloat)
512 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
513 else
514 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
516 if(data->PrimaryBuffer)
517 IDirectSoundBuffer_Release(data->PrimaryBuffer);
518 data->PrimaryBuffer = NULL;
520 else
522 if(SUCCEEDED(hr) && !data->PrimaryBuffer)
524 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
525 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
526 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
527 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->PrimaryBuffer, NULL);
529 if(SUCCEEDED(hr))
530 hr = IDirectSoundBuffer_SetFormat(data->PrimaryBuffer,&OutputType.Format);
533 if(SUCCEEDED(hr))
535 if(device->NumUpdates > MAX_UPDATES)
537 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
538 MAX_UPDATES-1) / MAX_UPDATES;
539 device->NumUpdates = MAX_UPDATES;
542 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
543 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
544 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
545 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
546 OutputType.Format.nBlockAlign;
547 DSBDescription.lpwfxFormat=&OutputType.Format;
548 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->Buffer, NULL);
549 if(FAILED(hr) && device->FmtType == DevFmtFloat)
551 device->FmtType = DevFmtShort;
552 goto retry_open;
556 if(SUCCEEDED(hr))
558 hr = IDirectSoundBuffer_QueryInterface(data->Buffer, &IID_IDirectSoundNotify, (LPVOID *)&data->Notifies);
559 if(SUCCEEDED(hr))
561 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
562 ALuint i;
564 for(i = 0;i < device->NumUpdates;++i)
566 notifies[i].dwOffset = i * device->UpdateSize *
567 OutputType.Format.nBlockAlign;
568 notifies[i].hEventNotify = data->NotifyEvent;
570 if(IDirectSoundNotify_SetNotificationPositions(data->Notifies, device->NumUpdates, notifies) != DS_OK)
571 hr = E_FAIL;
575 if(FAILED(hr))
577 if(data->Notifies != NULL)
578 IDirectSoundNotify_Release(data->Notifies);
579 data->Notifies = NULL;
580 if(data->Buffer != NULL)
581 IDirectSoundBuffer_Release(data->Buffer);
582 data->Buffer = NULL;
583 if(data->PrimaryBuffer != NULL)
584 IDirectSoundBuffer_Release(data->PrimaryBuffer);
585 data->PrimaryBuffer = NULL;
586 return ALC_FALSE;
589 ResetEvent(data->NotifyEvent);
590 SetDefaultWFXChannelOrder(device);
592 return ALC_TRUE;
595 static ALCboolean DSoundStartPlayback(ALCdevice *device)
597 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
599 data->killNow = 0;
600 if(althrd_create(&data->thread, DSoundPlaybackProc, device) != althrd_success)
601 return ALC_FALSE;
603 return ALC_TRUE;
606 static void DSoundStopPlayback(ALCdevice *device)
608 DSoundPlaybackData *data = device->ExtraData;
609 int res;
611 if(data->killNow)
612 return;
614 data->killNow = 1;
615 althrd_join(data->thread, &res);
617 IDirectSoundBuffer_Stop(data->Buffer);
621 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
623 DSoundCaptureData *data = NULL;
624 WAVEFORMATEXTENSIBLE InputType;
625 DSCBUFFERDESC DSCBDescription;
626 LPGUID guid = NULL;
627 HRESULT hr, hrcom;
628 ALuint samples;
630 if(VECTOR_SIZE(CaptureDevices) == 0)
632 /* Initialize COM to prevent name truncation */
633 hrcom = CoInitialize(NULL);
634 hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
635 if(FAILED(hr))
636 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
637 if(SUCCEEDED(hrcom))
638 CoUninitialize();
641 if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
643 deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
644 guid = &VECTOR_FRONT(CaptureDevices).guid;
646 else
648 DevMap *iter, *end;
650 iter = VECTOR_ITER_BEGIN(CaptureDevices);
651 end = VECTOR_ITER_END(CaptureDevices);
652 for(;iter != end;++iter)
654 if(al_string_cmp_cstr(iter->name, deviceName) == 0)
656 guid = &iter->guid;
657 break;
660 if(iter == end)
661 return ALC_INVALID_VALUE;
664 switch(device->FmtType)
666 case DevFmtByte:
667 case DevFmtUShort:
668 case DevFmtUInt:
669 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
670 return ALC_INVALID_ENUM;
672 case DevFmtUByte:
673 case DevFmtShort:
674 case DevFmtInt:
675 case DevFmtFloat:
676 break;
679 //Initialise requested device
680 data = calloc(1, sizeof(DSoundCaptureData));
681 if(!data)
682 return ALC_OUT_OF_MEMORY;
684 hr = DS_OK;
686 //DirectSoundCapture Init code
687 if(SUCCEEDED(hr))
688 hr = DirectSoundCaptureCreate(guid, &data->DSC, NULL);
689 if(SUCCEEDED(hr))
691 memset(&InputType, 0, sizeof(InputType));
693 switch(device->FmtChans)
695 case DevFmtMono:
696 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
697 break;
698 case DevFmtStereo:
699 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
700 SPEAKER_FRONT_RIGHT;
701 break;
702 case DevFmtQuad:
703 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
704 SPEAKER_FRONT_RIGHT |
705 SPEAKER_BACK_LEFT |
706 SPEAKER_BACK_RIGHT;
707 break;
708 case DevFmtX51:
709 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
710 SPEAKER_FRONT_RIGHT |
711 SPEAKER_FRONT_CENTER |
712 SPEAKER_LOW_FREQUENCY |
713 SPEAKER_BACK_LEFT |
714 SPEAKER_BACK_RIGHT;
715 break;
716 case DevFmtX51Side:
717 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
718 SPEAKER_FRONT_RIGHT |
719 SPEAKER_FRONT_CENTER |
720 SPEAKER_LOW_FREQUENCY |
721 SPEAKER_SIDE_LEFT |
722 SPEAKER_SIDE_RIGHT;
723 break;
724 case DevFmtX61:
725 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
726 SPEAKER_FRONT_RIGHT |
727 SPEAKER_FRONT_CENTER |
728 SPEAKER_LOW_FREQUENCY |
729 SPEAKER_BACK_CENTER |
730 SPEAKER_SIDE_LEFT |
731 SPEAKER_SIDE_RIGHT;
732 break;
733 case DevFmtX71:
734 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
735 SPEAKER_FRONT_RIGHT |
736 SPEAKER_FRONT_CENTER |
737 SPEAKER_LOW_FREQUENCY |
738 SPEAKER_BACK_LEFT |
739 SPEAKER_BACK_RIGHT |
740 SPEAKER_SIDE_LEFT |
741 SPEAKER_SIDE_RIGHT;
742 break;
745 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
746 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
747 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
748 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
749 InputType.Format.nSamplesPerSec = device->Frequency;
750 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
751 InputType.Format.cbSize = 0;
753 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
755 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
756 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
757 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
758 if(device->FmtType == DevFmtFloat)
759 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
760 else
761 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
764 samples = device->UpdateSize * device->NumUpdates;
765 samples = maxu(samples, 100 * device->Frequency / 1000);
767 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
768 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
769 DSCBDescription.dwFlags = 0;
770 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
771 DSCBDescription.lpwfxFormat = &InputType.Format;
773 hr = IDirectSoundCapture_CreateCaptureBuffer(data->DSC, &DSCBDescription, &data->DSCbuffer, NULL);
775 if(SUCCEEDED(hr))
777 data->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
778 if(data->Ring == NULL)
779 hr = DSERR_OUTOFMEMORY;
782 if(FAILED(hr))
784 ERR("Device init failed: 0x%08lx\n", hr);
786 DestroyRingBuffer(data->Ring);
787 data->Ring = NULL;
788 if(data->DSCbuffer != NULL)
789 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
790 data->DSCbuffer = NULL;
791 if(data->DSC)
792 IDirectSoundCapture_Release(data->DSC);
793 data->DSC = NULL;
795 free(data);
796 return ALC_INVALID_VALUE;
799 data->BufferBytes = DSCBDescription.dwBufferBytes;
800 SetDefaultWFXChannelOrder(device);
802 al_string_copy_cstr(&device->DeviceName, deviceName);
803 device->ExtraData = data;
805 return ALC_NO_ERROR;
808 static void DSoundCloseCapture(ALCdevice *device)
810 DSoundCaptureData *data = device->ExtraData;
812 DestroyRingBuffer(data->Ring);
813 data->Ring = NULL;
815 if(data->DSCbuffer != NULL)
817 IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
818 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
819 data->DSCbuffer = NULL;
822 IDirectSoundCapture_Release(data->DSC);
823 data->DSC = NULL;
825 free(data);
826 device->ExtraData = NULL;
829 static void DSoundStartCapture(ALCdevice *device)
831 DSoundCaptureData *data = device->ExtraData;
832 HRESULT hr;
834 hr = IDirectSoundCaptureBuffer_Start(data->DSCbuffer, DSCBSTART_LOOPING);
835 if(FAILED(hr))
837 ERR("start failed: 0x%08lx\n", hr);
838 aluHandleDisconnect(device);
842 static void DSoundStopCapture(ALCdevice *device)
844 DSoundCaptureData *data = device->ExtraData;
845 HRESULT hr;
847 hr = IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
848 if(FAILED(hr))
850 ERR("stop failed: 0x%08lx\n", hr);
851 aluHandleDisconnect(device);
855 static ALCenum DSoundCaptureSamples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
857 DSoundCaptureData *data = Device->ExtraData;
858 ReadRingBuffer(data->Ring, pBuffer, lSamples);
859 return ALC_NO_ERROR;
862 static ALCuint DSoundAvailableSamples(ALCdevice *Device)
864 DSoundCaptureData *data = Device->ExtraData;
865 DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
866 VOID *ReadPtr1, *ReadPtr2;
867 DWORD ReadCnt1, ReadCnt2;
868 DWORD FrameSize;
869 HRESULT hr;
871 if(!Device->Connected)
872 goto done;
874 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
875 BufferBytes = data->BufferBytes;
876 LastCursor = data->Cursor;
878 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(data->DSCbuffer, NULL, &ReadCursor);
879 if(SUCCEEDED(hr))
881 NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
882 if(NumBytes == 0)
883 goto done;
884 hr = IDirectSoundCaptureBuffer_Lock(data->DSCbuffer, LastCursor, NumBytes,
885 &ReadPtr1, &ReadCnt1,
886 &ReadPtr2, &ReadCnt2, 0);
888 if(SUCCEEDED(hr))
890 WriteRingBuffer(data->Ring, ReadPtr1, ReadCnt1/FrameSize);
891 if(ReadPtr2 != NULL)
892 WriteRingBuffer(data->Ring, ReadPtr2, ReadCnt2/FrameSize);
893 hr = IDirectSoundCaptureBuffer_Unlock(data->DSCbuffer,
894 ReadPtr1, ReadCnt1,
895 ReadPtr2, ReadCnt2);
896 data->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
899 if(FAILED(hr))
901 ERR("update failed: 0x%08lx\n", hr);
902 aluHandleDisconnect(Device);
905 done:
906 return RingBufferSize(data->Ring);
910 static const BackendFuncs DSoundFuncs = {
911 DSoundOpenPlayback,
912 DSoundClosePlayback,
913 DSoundResetPlayback,
914 DSoundStartPlayback,
915 DSoundStopPlayback,
916 DSoundOpenCapture,
917 DSoundCloseCapture,
918 DSoundStartCapture,
919 DSoundStopCapture,
920 DSoundCaptureSamples,
921 DSoundAvailableSamples,
922 ALCdevice_GetLatencyDefault
926 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
928 VECTOR_INIT(PlaybackDevices);
929 VECTOR_INIT(CaptureDevices);
931 if(!DSoundLoad())
932 return ALC_FALSE;
933 *FuncList = DSoundFuncs;
934 return ALC_TRUE;
937 void alcDSoundDeinit(void)
939 DevMap *iter, *end;
941 iter = VECTOR_ITER_BEGIN(PlaybackDevices);
942 end = VECTOR_ITER_END(PlaybackDevices);
943 for(;iter != end;++iter)
944 AL_STRING_DEINIT(iter->name);
945 VECTOR_DEINIT(PlaybackDevices);
947 iter = VECTOR_ITER_BEGIN(CaptureDevices);
948 end = VECTOR_ITER_END(CaptureDevices);
949 for(;iter != end;++iter)
950 AL_STRING_DEINIT(iter->name);
951 VECTOR_DEINIT(CaptureDevices);
953 if(ds_handle)
954 CloseLib(ds_handle);
955 ds_handle = NULL;
958 void alcDSoundProbe(enum DevProbe type)
960 DevMap *iter, *end;
961 HRESULT hr, hrcom;
963 /* Initialize COM to prevent name truncation */
964 hrcom = CoInitialize(NULL);
965 switch(type)
967 case ALL_DEVICE_PROBE:
968 iter = VECTOR_ITER_BEGIN(PlaybackDevices);
969 end = VECTOR_ITER_END(PlaybackDevices);
970 for(;iter != end;++iter)
971 AL_STRING_DEINIT(iter->name);
972 VECTOR_RESIZE(PlaybackDevices, 0);
974 hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
975 if(FAILED(hr))
976 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
977 else
979 iter = VECTOR_ITER_BEGIN(PlaybackDevices);
980 end = VECTOR_ITER_END(PlaybackDevices);
981 for(;iter != end;++iter)
982 AppendAllDevicesList(al_string_get_cstr(iter->name));
984 break;
986 case CAPTURE_DEVICE_PROBE:
987 iter = VECTOR_ITER_BEGIN(CaptureDevices);
988 end = VECTOR_ITER_END(CaptureDevices);
989 for(;iter != end;++iter)
990 AL_STRING_DEINIT(iter->name);
991 VECTOR_RESIZE(CaptureDevices, 0);
993 hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
994 if(FAILED(hr))
995 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
996 else
998 iter = VECTOR_ITER_BEGIN(CaptureDevices);
999 end = VECTOR_ITER_END(CaptureDevices);
1000 for(;iter != end;++iter)
1001 AppendCaptureDeviceList(al_string_get_cstr(iter->name));
1003 break;
1005 if(SUCCEEDED(hrcom))
1006 CoUninitialize();