aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/backends/dsound.c
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2011-08-20 00:41:47 -0700
committerChris Robinson <[email protected]>2011-08-20 00:41:47 -0700
commit9989f33fc24042578bf4bf2c50a06a6366d07c1d (patch)
treecdf87e14c0333069c3ba791109c753c3ebf9784e /Alc/backends/dsound.c
parentba4456d24aa013118e70fa1158def7ad18e8d66e (diff)
Move backend sources to a separate sub-directory
Diffstat (limited to 'Alc/backends/dsound.c')
-rw-r--r--Alc/backends/dsound.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c
new file mode 100644
index 00000000..08a1d13a
--- /dev/null
+++ b/Alc/backends/dsound.c
@@ -0,0 +1,627 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#define _WIN32_WINNT 0x0500
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <dsound.h>
+#include <cguid.h>
+#include <mmreg.h>
+#ifndef _WAVEFORMATEXTENSIBLE_
+#include <ks.h>
+#include <ksmedia.h>
+#endif
+
+#include "alMain.h"
+#include "AL/al.h"
+#include "AL/alc.h"
+
+#ifndef DSSPEAKER_5POINT1
+#define DSSPEAKER_5POINT1 6
+#endif
+#ifndef DSSPEAKER_7POINT1
+#define DSSPEAKER_7POINT1 7
+#endif
+
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+
+static HMODULE ds_handle;
+static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
+static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
+
+#define DirectSoundCreate pDirectSoundCreate
+#define DirectSoundEnumerateA pDirectSoundEnumerateA
+
+
+typedef struct {
+ // DirectSound Playback Device
+ IDirectSound *lpDS;
+ IDirectSoundBuffer *DSpbuffer;
+ IDirectSoundBuffer *DSsbuffer;
+ IDirectSoundNotify *DSnotify;
+ HANDLE hNotifyEvent;
+
+ volatile int killNow;
+ ALvoid *thread;
+} DSoundData;
+
+
+typedef struct {
+ ALCchar *name;
+ GUID guid;
+} DevMap;
+
+static const ALCchar dsDevice[] = "DirectSound Default";
+static DevMap *DeviceList;
+static ALuint NumDevices;
+
+#define MAX_UPDATES 128
+
+static ALCboolean DSoundLoad(void)
+{
+ ALCboolean ok = ALC_TRUE;
+ if(!ds_handle)
+ {
+ ds_handle = LoadLibraryA("dsound.dll");
+ if(ds_handle == NULL)
+ {
+ ERR("Failed to load dsound.dll\n");
+ return ALC_FALSE;
+ }
+
+#define LOAD_FUNC(x) do { \
+ if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \
+ ERR("Could not load %s from dsound.dll\n", #x); \
+ ok = ALC_FALSE; \
+ } \
+} while(0)
+ LOAD_FUNC(DirectSoundCreate);
+ LOAD_FUNC(DirectSoundEnumerateA);
+#undef LOAD_FUNC
+
+ if(!ok)
+ {
+ FreeLibrary(ds_handle);
+ ds_handle = NULL;
+ }
+ }
+ return ok;
+}
+
+
+static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
+{
+ char str[1024];
+ void *temp;
+ int count;
+ ALuint i;
+
+ (void)data;
+ (void)drvname;
+
+ if(NumDevices == 0)
+ {
+ temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
+ if(temp)
+ {
+ DeviceList = temp;
+ DeviceList[NumDevices].name = strdup(dsDevice);
+ DeviceList[NumDevices].guid = GUID_NULL;
+ NumDevices++;
+ }
+ }
+
+ if(!guid)
+ return TRUE;
+
+ count = 0;
+ do {
+ if(count == 0)
+ snprintf(str, sizeof(str), "%s", desc);
+ else
+ snprintf(str, sizeof(str), "%s #%d", desc, count+1);
+ count++;
+
+ for(i = 0;i < NumDevices;i++)
+ {
+ if(strcmp(str, DeviceList[i].name) == 0)
+ break;
+ }
+ } while(i != NumDevices);
+
+ temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
+ if(temp)
+ {
+ DeviceList = temp;
+ DeviceList[NumDevices].name = strdup(str);
+ DeviceList[NumDevices].guid = *guid;
+ NumDevices++;
+ }
+
+ return TRUE;
+}
+
+
+static ALuint DSoundProc(ALvoid *ptr)
+{
+ ALCdevice *pDevice = (ALCdevice*)ptr;
+ DSoundData *pData = (DSoundData*)pDevice->ExtraData;
+ DSBCAPS DSBCaps;
+ DWORD LastCursor = 0;
+ DWORD PlayCursor;
+ VOID *WritePtr1, *WritePtr2;
+ DWORD WriteCnt1, WriteCnt2;
+ BOOL Playing = FALSE;
+ DWORD FrameSize;
+ DWORD FragSize;
+ DWORD avail;
+ HRESULT err;
+
+ SetRTPriority();
+
+ memset(&DSBCaps, 0, sizeof(DSBCaps));
+ DSBCaps.dwSize = sizeof(DSBCaps);
+ err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
+ if(FAILED(err))
+ {
+ ERR("Failed to get buffer caps: 0x%lx\n", err);
+ aluHandleDisconnect(pDevice);
+ return 1;
+ }
+
+ FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
+ FragSize = pDevice->UpdateSize * FrameSize;
+
+ IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
+ while(!pData->killNow)
+ {
+ // Get current play cursor
+ IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
+ avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
+
+ if(avail < FragSize)
+ {
+ if(!Playing)
+ {
+ err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
+ if(FAILED(err))
+ {
+ ERR("Failed to play buffer: 0x%lx\n", err);
+ aluHandleDisconnect(pDevice);
+ return 1;
+ }
+ Playing = TRUE;
+ }
+
+ avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);
+ if(avail != WAIT_OBJECT_0)
+ ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
+ continue;
+ }
+ avail -= avail%FragSize;
+
+ // Lock output buffer
+ WriteCnt1 = 0;
+ WriteCnt2 = 0;
+ err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
+
+ // If the buffer is lost, restore it and lock
+ if(err == DSERR_BUFFERLOST)
+ {
+ WARN("Buffer lost, restoring...\n");
+ err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
+ if(SUCCEEDED(err))
+ {
+ Playing = FALSE;
+ LastCursor = 0;
+ err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
+ }
+ }
+
+ // Successfully locked the output buffer
+ if(SUCCEEDED(err))
+ {
+ // If we have an active context, mix data directly into output buffer otherwise fill with silence
+ aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
+ aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
+
+ // Unlock output buffer only when successfully locked
+ IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
+ }
+ else
+ {
+ ERR("Buffer lock error: %#lx\n", err);
+ aluHandleDisconnect(pDevice);
+ return 1;
+ }
+
+ // Update old write cursor location
+ LastCursor += WriteCnt1+WriteCnt2;
+ LastCursor %= DSBCaps.dwBufferBytes;
+ }
+
+ return 0;
+}
+
+static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
+{
+ DSoundData *pData = NULL;
+ LPGUID guid = NULL;
+ HRESULT hr;
+
+ if(!deviceName)
+ deviceName = dsDevice;
+ else if(strcmp(deviceName, dsDevice) != 0)
+ {
+ ALuint i;
+
+ if(!DeviceList)
+ {
+ hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
+ if(FAILED(hr))
+ ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
+ }
+
+ for(i = 0;i < NumDevices;i++)
+ {
+ if(strcmp(deviceName, DeviceList[i].name) == 0)
+ {
+ if(i > 0)
+ guid = &DeviceList[i].guid;
+ break;
+ }
+ }
+ if(i == NumDevices)
+ return ALC_FALSE;
+ }
+
+ //Initialise requested device
+ pData = calloc(1, sizeof(DSoundData));
+ if(!pData)
+ {
+ alcSetError(device, ALC_OUT_OF_MEMORY);
+ return ALC_FALSE;
+ }
+
+ hr = DS_OK;
+ pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if(pData->hNotifyEvent == NULL)
+ hr = E_FAIL;
+
+ //DirectSound Init code
+ if(SUCCEEDED(hr))
+ hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
+ if(SUCCEEDED(hr))
+ hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
+ if(FAILED(hr))
+ {
+ if(pData->lpDS)
+ IDirectSound_Release(pData->lpDS);
+ if(pData->hNotifyEvent)
+ CloseHandle(pData->hNotifyEvent);
+ free(pData);
+ ERR("Device init failed: 0x%08lx\n", hr);
+ return ALC_FALSE;
+ }
+
+ device->szDeviceName = strdup(deviceName);
+ device->ExtraData = pData;
+ return ALC_TRUE;
+}
+
+static void DSoundClosePlayback(ALCdevice *device)
+{
+ DSoundData *pData = device->ExtraData;
+
+ IDirectSound_Release(pData->lpDS);
+ CloseHandle(pData->hNotifyEvent);
+ free(pData);
+ device->ExtraData = NULL;
+}
+
+static ALCboolean DSoundResetPlayback(ALCdevice *device)
+{
+ DSoundData *pData = (DSoundData*)device->ExtraData;
+ DSBUFFERDESC DSBDescription;
+ WAVEFORMATEXTENSIBLE OutputType;
+ DWORD speakers;
+ HRESULT hr;
+
+ memset(&OutputType, 0, sizeof(OutputType));
+
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ device->FmtType = DevFmtUByte;
+ break;
+ case DevFmtUShort:
+ device->FmtType = DevFmtShort;
+ break;
+ case DevFmtUByte:
+ case DevFmtShort:
+ case DevFmtFloat:
+ break;
+ }
+
+ hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
+ if(SUCCEEDED(hr))
+ {
+ if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
+ {
+ speakers = DSSPEAKER_CONFIG(speakers);
+ if(speakers == DSSPEAKER_MONO)
+ device->FmtChans = DevFmtMono;
+ else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
+ device->FmtChans = DevFmtStereo;
+ else if(speakers == DSSPEAKER_QUAD)
+ device->FmtChans = DevFmtQuad;
+ else if(speakers == DSSPEAKER_5POINT1)
+ device->FmtChans = DevFmtX51;
+ else if(speakers == DSSPEAKER_7POINT1)
+ device->FmtChans = DevFmtX71;
+ else
+ ERR("Unknown system speaker config: 0x%lx\n", speakers);
+ }
+
+ switch(device->FmtChans)
+ {
+ case DevFmtMono:
+ OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+ break;
+ case DevFmtStereo:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT;
+ break;
+ case DevFmtQuad:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX51:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX51Side:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX61:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_CENTER |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX71:
+ OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ }
+
+ OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+ OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+ OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
+ OutputType.Format.nSamplesPerSec = device->Frequency;
+ OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
+ OutputType.Format.cbSize = 0;
+ }
+
+ if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+ {
+ OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+ OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ if(device->FmtType == DevFmtFloat)
+ OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ else
+ OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+ else
+ {
+ if(SUCCEEDED(hr))
+ {
+ memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
+ DSBDescription.dwSize=sizeof(DSBUFFERDESC);
+ DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
+ hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
+ }
+ if(SUCCEEDED(hr))
+ hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ if(device->NumUpdates > MAX_UPDATES)
+ {
+ device->UpdateSize = (device->UpdateSize*device->NumUpdates +
+ MAX_UPDATES-1) / MAX_UPDATES;
+ device->NumUpdates = MAX_UPDATES;
+ }
+
+ memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
+ DSBDescription.dwSize=sizeof(DSBUFFERDESC);
+ DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
+ DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
+ OutputType.Format.nBlockAlign;
+ DSBDescription.lpwfxFormat=&OutputType.Format;
+ hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
+ if(SUCCEEDED(hr))
+ {
+ DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
+ ALuint i;
+
+ for(i = 0;i < device->NumUpdates;++i)
+ {
+ notifies[i].dwOffset = i * device->UpdateSize *
+ OutputType.Format.nBlockAlign;
+ notifies[i].hEventNotify = pData->hNotifyEvent;
+ }
+ if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
+ hr = E_FAIL;
+ }
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ ResetEvent(pData->hNotifyEvent);
+ SetDefaultWFXChannelOrder(device);
+ pData->thread = StartThread(DSoundProc, device);
+ if(pData->thread == NULL)
+ hr = E_FAIL;
+ }
+
+ if(FAILED(hr))
+ {
+ if(pData->DSnotify != NULL)
+ IDirectSoundNotify_Release(pData->DSnotify);
+ pData->DSnotify = NULL;
+ if(pData->DSsbuffer != NULL)
+ IDirectSoundBuffer_Release(pData->DSsbuffer);
+ pData->DSsbuffer = NULL;
+ if(pData->DSpbuffer != NULL)
+ IDirectSoundBuffer_Release(pData->DSpbuffer);
+ pData->DSpbuffer = NULL;
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static void DSoundStopPlayback(ALCdevice *device)
+{
+ DSoundData *pData = device->ExtraData;
+
+ if(!pData->thread)
+ return;
+
+ pData->killNow = 1;
+ StopThread(pData->thread);
+ pData->thread = NULL;
+
+ pData->killNow = 0;
+
+ IDirectSoundNotify_Release(pData->DSnotify);
+ pData->DSnotify = NULL;
+ IDirectSoundBuffer_Release(pData->DSsbuffer);
+ pData->DSsbuffer = NULL;
+ if(pData->DSpbuffer != NULL)
+ IDirectSoundBuffer_Release(pData->DSpbuffer);
+ pData->DSpbuffer = NULL;
+}
+
+
+static const BackendFuncs DSoundFuncs = {
+ DSoundOpenPlayback,
+ DSoundClosePlayback,
+ DSoundResetPlayback,
+ DSoundStopPlayback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+ALCboolean alcDSoundInit(BackendFuncs *FuncList)
+{
+ if(!DSoundLoad())
+ return ALC_FALSE;
+ *FuncList = DSoundFuncs;
+ return ALC_TRUE;
+}
+
+void alcDSoundDeinit(void)
+{
+ ALuint i;
+
+ for(i = 0;i < NumDevices;++i)
+ free(DeviceList[i].name);
+ free(DeviceList);
+ DeviceList = NULL;
+ NumDevices = 0;
+
+ if(ds_handle)
+ FreeLibrary(ds_handle);
+ ds_handle = NULL;
+}
+
+void alcDSoundProbe(enum DevProbe type)
+{
+ HRESULT hr;
+ ALuint i;
+
+ switch(type)
+ {
+ case DEVICE_PROBE:
+ AppendDeviceList(dsDevice);
+ break;
+
+ case ALL_DEVICE_PROBE:
+ for(i = 0;i < NumDevices;++i)
+ free(DeviceList[i].name);
+ free(DeviceList);
+ DeviceList = NULL;
+ NumDevices = 0;
+
+ hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
+ if(FAILED(hr))
+ ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
+ else
+ {
+ for(i = 0;i < NumDevices;i++)
+ AppendAllDeviceList(DeviceList[i].name);
+ }
+ break;
+
+ case CAPTURE_DEVICE_PROBE:
+ break;
+ }
+}