Mercurial > audio-send
view Alc/backends/dsound.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
line wrap: on
line source
1 /**2 * OpenAL cross platform audio library3 * Copyright (C) 1999-2007 by authors.4 * This library is free software; you can redistribute it and/or5 * modify it under the terms of the GNU Library General Public6 * License as published by the Free Software Foundation; either7 * version 2 of the License, or (at your option) any later version.8 *9 * This library is distributed in the hope that it will be useful,10 * but WITHOUT ANY WARRANTY; without even the implied warranty of11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12 * Library General Public License for more details.13 *14 * You should have received a copy of the GNU Library General Public15 * License along with this library; if not, write to the16 * 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.html19 */21 #include "config.h"23 #define _WIN32_WINNT 0x050024 #include <stdlib.h>25 #include <stdio.h>26 #include <memory.h>28 #include <dsound.h>29 #include <cguid.h>30 #include <mmreg.h>31 #ifndef _WAVEFORMATEXTENSIBLE_32 #include <ks.h>33 #include <ksmedia.h>34 #endif36 #include "alMain.h"37 #include "AL/al.h"38 #include "AL/alc.h"40 #ifndef DSSPEAKER_5POINT141 #define DSSPEAKER_5POINT1 642 #endif43 #ifndef DSSPEAKER_7POINT144 #define DSSPEAKER_7POINT1 745 #endif47 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);48 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);51 static HMODULE ds_handle;52 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);53 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);55 #define DirectSoundCreate pDirectSoundCreate56 #define DirectSoundEnumerateA pDirectSoundEnumerateA59 typedef struct {60 // DirectSound Playback Device61 IDirectSound *lpDS;62 IDirectSoundBuffer *DSpbuffer;63 IDirectSoundBuffer *DSsbuffer;64 IDirectSoundNotify *DSnotify;65 HANDLE hNotifyEvent;67 volatile int killNow;68 ALvoid *thread;69 } DSoundData;72 typedef struct {73 ALCchar *name;74 GUID guid;75 } DevMap;77 static const ALCchar dsDevice[] = "DirectSound Default";78 static DevMap *DeviceList;79 static ALuint NumDevices;81 #define MAX_UPDATES 12883 static ALCboolean DSoundLoad(void)84 {85 ALCboolean ok = ALC_TRUE;86 if(!ds_handle)87 {88 ds_handle = LoadLibraryA("dsound.dll");89 if(ds_handle == NULL)90 {91 ERR("Failed to load dsound.dll\n");92 return ALC_FALSE;93 }95 #define LOAD_FUNC(x) do { \96 if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \97 ERR("Could not load %s from dsound.dll\n", #x); \98 ok = ALC_FALSE; \99 } \100 } while(0)101 LOAD_FUNC(DirectSoundCreate);102 LOAD_FUNC(DirectSoundEnumerateA);103 #undef LOAD_FUNC105 if(!ok)106 {107 FreeLibrary(ds_handle);108 ds_handle = NULL;109 }110 }111 return ok;112 }115 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)116 {117 char str[1024];118 void *temp;119 int count;120 ALuint i;122 (void)data;123 (void)drvname;125 if(!guid)126 return TRUE;128 count = 0;129 do {130 if(count == 0)131 snprintf(str, sizeof(str), "%s", desc);132 else133 snprintf(str, sizeof(str), "%s #%d", desc, count+1);134 count++;136 for(i = 0;i < NumDevices;i++)137 {138 if(strcmp(str, DeviceList[i].name) == 0)139 break;140 }141 } while(i != NumDevices);143 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));144 if(temp)145 {146 DeviceList = temp;147 DeviceList[NumDevices].name = strdup(str);148 DeviceList[NumDevices].guid = *guid;149 NumDevices++;150 }152 return TRUE;153 }156 static ALuint DSoundProc(ALvoid *ptr)157 {158 ALCdevice *pDevice = (ALCdevice*)ptr;159 DSoundData *pData = (DSoundData*)pDevice->ExtraData;160 DSBCAPS DSBCaps;161 DWORD LastCursor = 0;162 DWORD PlayCursor;163 VOID *WritePtr1, *WritePtr2;164 DWORD WriteCnt1, WriteCnt2;165 BOOL Playing = FALSE;166 DWORD FrameSize;167 DWORD FragSize;168 DWORD avail;169 HRESULT err;171 SetRTPriority();173 memset(&DSBCaps, 0, sizeof(DSBCaps));174 DSBCaps.dwSize = sizeof(DSBCaps);175 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);176 if(FAILED(err))177 {178 ERR("Failed to get buffer caps: 0x%lx\n", err);179 aluHandleDisconnect(pDevice);180 return 1;181 }183 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);184 FragSize = pDevice->UpdateSize * FrameSize;186 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);187 while(!pData->killNow)188 {189 // Get current play cursor190 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);191 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;193 if(avail < FragSize)194 {195 if(!Playing)196 {197 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);198 if(FAILED(err))199 {200 ERR("Failed to play buffer: 0x%lx\n", err);201 aluHandleDisconnect(pDevice);202 return 1;203 }204 Playing = TRUE;205 }207 avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);208 if(avail != WAIT_OBJECT_0)209 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);210 continue;211 }212 avail -= avail%FragSize;214 // Lock output buffer215 WriteCnt1 = 0;216 WriteCnt2 = 0;217 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);219 // If the buffer is lost, restore it and lock220 if(err == DSERR_BUFFERLOST)221 {222 WARN("Buffer lost, restoring...\n");223 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);224 if(SUCCEEDED(err))225 {226 Playing = FALSE;227 LastCursor = 0;228 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);229 }230 }232 // Successfully locked the output buffer233 if(SUCCEEDED(err))234 {235 // If we have an active context, mix data directly into output buffer otherwise fill with silence236 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);237 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);239 // Unlock output buffer only when successfully locked240 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);241 }242 else243 {244 ERR("Buffer lock error: %#lx\n", err);245 aluHandleDisconnect(pDevice);246 return 1;247 }249 // Update old write cursor location250 LastCursor += WriteCnt1+WriteCnt2;251 LastCursor %= DSBCaps.dwBufferBytes;252 }254 return 0;255 }257 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)258 {259 DSoundData *pData = NULL;260 LPGUID guid = NULL;261 HRESULT hr;263 if(!deviceName)264 deviceName = dsDevice;265 else if(strcmp(deviceName, dsDevice) != 0)266 {267 ALuint i;269 if(!DeviceList)270 {271 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);272 if(FAILED(hr))273 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);274 }276 for(i = 0;i < NumDevices;i++)277 {278 if(strcmp(deviceName, DeviceList[i].name) == 0)279 {280 guid = &DeviceList[i].guid;281 break;282 }283 }284 if(i == NumDevices)285 return ALC_FALSE;286 }288 //Initialise requested device289 pData = calloc(1, sizeof(DSoundData));290 if(!pData)291 {292 alcSetError(device, ALC_OUT_OF_MEMORY);293 return ALC_FALSE;294 }296 hr = DS_OK;297 pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);298 if(pData->hNotifyEvent == NULL)299 hr = E_FAIL;301 //DirectSound Init code302 if(SUCCEEDED(hr))303 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);304 if(SUCCEEDED(hr))305 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);306 if(FAILED(hr))307 {308 if(pData->lpDS)309 IDirectSound_Release(pData->lpDS);310 if(pData->hNotifyEvent)311 CloseHandle(pData->hNotifyEvent);312 free(pData);313 ERR("Device init failed: 0x%08lx\n", hr);314 return ALC_FALSE;315 }317 device->szDeviceName = strdup(deviceName);318 device->ExtraData = pData;319 return ALC_TRUE;320 }322 static void DSoundClosePlayback(ALCdevice *device)323 {324 DSoundData *pData = device->ExtraData;326 IDirectSound_Release(pData->lpDS);327 CloseHandle(pData->hNotifyEvent);328 free(pData);329 device->ExtraData = NULL;330 }332 static ALCboolean DSoundResetPlayback(ALCdevice *device)333 {334 DSoundData *pData = (DSoundData*)device->ExtraData;335 DSBUFFERDESC DSBDescription;336 WAVEFORMATEXTENSIBLE OutputType;337 DWORD speakers;338 HRESULT hr;340 memset(&OutputType, 0, sizeof(OutputType));342 switch(device->FmtType)343 {344 case DevFmtByte:345 device->FmtType = DevFmtUByte;346 break;347 case DevFmtUShort:348 device->FmtType = DevFmtShort;349 break;350 case DevFmtUByte:351 case DevFmtShort:352 case DevFmtFloat:353 break;354 }356 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);357 if(SUCCEEDED(hr))358 {359 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))360 {361 speakers = DSSPEAKER_CONFIG(speakers);362 if(speakers == DSSPEAKER_MONO)363 device->FmtChans = DevFmtMono;364 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)365 device->FmtChans = DevFmtStereo;366 else if(speakers == DSSPEAKER_QUAD)367 device->FmtChans = DevFmtQuad;368 else if(speakers == DSSPEAKER_5POINT1)369 device->FmtChans = DevFmtX51;370 else if(speakers == DSSPEAKER_7POINT1)371 device->FmtChans = DevFmtX71;372 else373 ERR("Unknown system speaker config: 0x%lx\n", speakers);374 }376 switch(device->FmtChans)377 {378 case DevFmtMono:379 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;380 break;381 case DevFmtStereo:382 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |383 SPEAKER_FRONT_RIGHT;384 break;385 case DevFmtQuad:386 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |387 SPEAKER_FRONT_RIGHT |388 SPEAKER_BACK_LEFT |389 SPEAKER_BACK_RIGHT;390 break;391 case DevFmtX51:392 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |393 SPEAKER_FRONT_RIGHT |394 SPEAKER_FRONT_CENTER |395 SPEAKER_LOW_FREQUENCY |396 SPEAKER_BACK_LEFT |397 SPEAKER_BACK_RIGHT;398 break;399 case DevFmtX51Side:400 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |401 SPEAKER_FRONT_RIGHT |402 SPEAKER_FRONT_CENTER |403 SPEAKER_LOW_FREQUENCY |404 SPEAKER_SIDE_LEFT |405 SPEAKER_SIDE_RIGHT;406 break;407 case DevFmtX61:408 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |409 SPEAKER_FRONT_RIGHT |410 SPEAKER_FRONT_CENTER |411 SPEAKER_LOW_FREQUENCY |412 SPEAKER_BACK_CENTER |413 SPEAKER_SIDE_LEFT |414 SPEAKER_SIDE_RIGHT;415 break;416 case DevFmtX71:417 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |418 SPEAKER_FRONT_RIGHT |419 SPEAKER_FRONT_CENTER |420 SPEAKER_LOW_FREQUENCY |421 SPEAKER_BACK_LEFT |422 SPEAKER_BACK_RIGHT |423 SPEAKER_SIDE_LEFT |424 SPEAKER_SIDE_RIGHT;425 break;426 }428 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;429 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);430 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;431 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;432 OutputType.Format.nSamplesPerSec = device->Frequency;433 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;434 OutputType.Format.cbSize = 0;435 }437 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)438 {439 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;440 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;441 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);442 if(device->FmtType == DevFmtFloat)443 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;444 else445 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;446 }447 else448 {449 if(SUCCEEDED(hr))450 {451 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));452 DSBDescription.dwSize=sizeof(DSBUFFERDESC);453 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;454 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);455 }456 if(SUCCEEDED(hr))457 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);458 }460 if(SUCCEEDED(hr))461 {462 if(device->NumUpdates > MAX_UPDATES)463 {464 device->UpdateSize = (device->UpdateSize*device->NumUpdates +465 MAX_UPDATES-1) / MAX_UPDATES;466 device->NumUpdates = MAX_UPDATES;467 }469 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));470 DSBDescription.dwSize=sizeof(DSBUFFERDESC);471 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;472 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *473 OutputType.Format.nBlockAlign;474 DSBDescription.lpwfxFormat=&OutputType.Format;475 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);476 }478 if(SUCCEEDED(hr))479 {480 hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);481 if(SUCCEEDED(hr))482 {483 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];484 ALuint i;486 for(i = 0;i < device->NumUpdates;++i)487 {488 notifies[i].dwOffset = i * device->UpdateSize *489 OutputType.Format.nBlockAlign;490 notifies[i].hEventNotify = pData->hNotifyEvent;491 }492 if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)493 hr = E_FAIL;494 }495 }497 if(SUCCEEDED(hr))498 {499 ResetEvent(pData->hNotifyEvent);500 SetDefaultWFXChannelOrder(device);501 pData->thread = StartThread(DSoundProc, device);502 if(pData->thread == NULL)503 hr = E_FAIL;504 }506 if(FAILED(hr))507 {508 if(pData->DSnotify != NULL)509 IDirectSoundNotify_Release(pData->DSnotify);510 pData->DSnotify = NULL;511 if(pData->DSsbuffer != NULL)512 IDirectSoundBuffer_Release(pData->DSsbuffer);513 pData->DSsbuffer = NULL;514 if(pData->DSpbuffer != NULL)515 IDirectSoundBuffer_Release(pData->DSpbuffer);516 pData->DSpbuffer = NULL;517 return ALC_FALSE;518 }520 return ALC_TRUE;521 }523 static void DSoundStopPlayback(ALCdevice *device)524 {525 DSoundData *pData = device->ExtraData;527 if(!pData->thread)528 return;530 pData->killNow = 1;531 StopThread(pData->thread);532 pData->thread = NULL;534 pData->killNow = 0;536 IDirectSoundNotify_Release(pData->DSnotify);537 pData->DSnotify = NULL;538 IDirectSoundBuffer_Release(pData->DSsbuffer);539 pData->DSsbuffer = NULL;540 if(pData->DSpbuffer != NULL)541 IDirectSoundBuffer_Release(pData->DSpbuffer);542 pData->DSpbuffer = NULL;543 }546 static const BackendFuncs DSoundFuncs = {547 DSoundOpenPlayback,548 DSoundClosePlayback,549 DSoundResetPlayback,550 DSoundStopPlayback,551 NULL,552 NULL,553 NULL,554 NULL,555 NULL,556 NULL557 };560 ALCboolean alcDSoundInit(BackendFuncs *FuncList)561 {562 if(!DSoundLoad())563 return ALC_FALSE;564 *FuncList = DSoundFuncs;565 return ALC_TRUE;566 }568 void alcDSoundDeinit(void)569 {570 ALuint i;572 for(i = 0;i < NumDevices;++i)573 free(DeviceList[i].name);574 free(DeviceList);575 DeviceList = NULL;576 NumDevices = 0;578 if(ds_handle)579 FreeLibrary(ds_handle);580 ds_handle = NULL;581 }583 void alcDSoundProbe(enum DevProbe type)584 {585 HRESULT hr;586 ALuint i;588 switch(type)589 {590 case DEVICE_PROBE:591 AppendDeviceList(dsDevice);592 break;594 case ALL_DEVICE_PROBE:595 for(i = 0;i < NumDevices;++i)596 free(DeviceList[i].name);597 free(DeviceList);598 DeviceList = NULL;599 NumDevices = 0;601 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);602 if(FAILED(hr))603 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);604 else605 {606 for(i = 0;i < NumDevices;i++)607 AppendAllDeviceList(DeviceList[i].name);608 }609 break;611 case CAPTURE_DEVICE_PROBE:612 break;613 }614 }