0N/A/*
3261N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#define USE_ERROR
0N/A#define USE_TRACE
0N/A
0N/A/* define this for the silencing/servicing code. Requires USE_TRACE */
0N/A//#define USE_DEBUG_SILENCING
0N/A
0N/A#ifndef WIN32_EXTRA_LEAN
0N/A#define WIN32_EXTRA_LEAN
0N/A#endif
0N/A#ifndef WIN32_LEAN_AND_MEAN
0N/A#define WIN32_LEAN_AND_MEAN
0N/A#endif
0N/A
0N/A#include <windows.h>
0N/A#include <mmsystem.h>
0N/A#include <string.h>
0N/A
0N/A/* include DirectSound headers */
0N/A#include <dsound.h>
0N/A
0N/A/* include Java Sound specific headers as C code */
0N/A#ifdef __cplusplus
0N/Aextern "C" {
0N/A#endif
0N/A #include "DirectAudio.h"
0N/A#ifdef __cplusplus
0N/A}
0N/A#endif
0N/A
0N/A#ifdef USE_DEBUG_SILENCING
0N/A#define DEBUG_SILENCING0(p) TRACE0(p)
0N/A#define DEBUG_SILENCING1(p1,p2) TRACE1(p1,p2)
0N/A#define DEBUG_SILENCING2(p1,p2,p3) TRACE2(p1,p2,p3)
0N/A#else
0N/A#define DEBUG_SILENCING0(p)
0N/A#define DEBUG_SILENCING1(p1,p2)
0N/A#define DEBUG_SILENCING2(p1,p2,p3)
0N/A#endif
0N/A
0N/A
0N/A#if USE_DAUDIO == TRUE
0N/A
0N/A/* half a minute to wait before device list is re-read */
0N/A#define WAIT_BETWEEN_CACHE_REFRESH_MILLIS 30000
0N/A
0N/A/* maximum number of supported devices, playback+capture */
0N/A#define MAX_DS_DEVICES 60
0N/A
0N/Atypedef struct {
0N/A INT32 mixerIndex;
0N/A BOOL isSource;
0N/A /* either LPDIRECTSOUND or LPDIRECTSOUNDCAPTURE */
0N/A void* dev;
0N/A /* how many instances use the dev */
0N/A INT32 refCount;
0N/A GUID guid;
0N/A} DS_AudioDeviceCache;
0N/A
0N/Astatic DS_AudioDeviceCache g_audioDeviceCache[MAX_DS_DEVICES];
0N/Astatic INT32 g_cacheCount = 0;
0N/Astatic UINT64 g_lastCacheRefreshTime = 0;
0N/Astatic INT32 g_mixerCount = 0;
0N/A
0N/ABOOL DS_lockCache() {
0N/A /* dummy implementation for now, Java does locking */
0N/A return TRUE;
0N/A}
0N/A
0N/Avoid DS_unlockCache() {
0N/A /* dummy implementation for now */
0N/A}
0N/A
0N/Astatic GUID CLSID_DAUDIO_Zero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
0N/A
0N/ABOOL isEqualGUID(LPGUID lpGuid1, LPGUID lpGuid2) {
0N/A if (lpGuid1 == NULL || lpGuid2 == NULL) {
0N/A if (lpGuid1 == lpGuid2) {
0N/A return TRUE;
0N/A }
0N/A if (lpGuid1 == NULL) {
0N/A lpGuid1 = (LPGUID) (&CLSID_DAUDIO_Zero);
0N/A } else {
0N/A lpGuid2 = (LPGUID) (&CLSID_DAUDIO_Zero);
0N/A }
0N/A }
0N/A return memcmp(lpGuid1, lpGuid2, sizeof(GUID)) == 0;
0N/A}
0N/A
0N/AINT32 findCacheItemByGUID(LPGUID lpGuid, BOOL isSource) {
0N/A int i;
0N/A for (i = 0; i < g_cacheCount; i++) {
0N/A if (isSource == g_audioDeviceCache[i].isSource
0N/A && isEqualGUID(lpGuid, &(g_audioDeviceCache[i].guid))) {
0N/A return i;
0N/A }
0N/A }
0N/A return -1;
0N/A}
0N/A
0N/AINT32 findCacheItemByMixerIndex(INT32 mixerIndex) {
0N/A int i;
0N/A for (i = 0; i < g_cacheCount; i++) {
0N/A if (g_audioDeviceCache[i].mixerIndex == mixerIndex) {
0N/A return i;
0N/A }
0N/A }
0N/A return -1;
0N/A}
0N/A
0N/Atypedef struct {
0N/A INT32 currMixerIndex;
0N/A BOOL isSource;
0N/A} DS_RefreshCacheStruct;
0N/A
0N/A
0N/ABOOL CALLBACK DS_RefreshCacheEnum(LPGUID lpGuid,
0N/A LPCSTR lpstrDescription,
0N/A LPCSTR lpstrModule,
0N/A DS_RefreshCacheStruct* rs) {
0N/A INT32 cacheIndex = findCacheItemByGUID(lpGuid, rs->isSource);
0N/A /*TRACE3("Enumerating %d: %s (%s)\n", cacheIndex, lpstrDescription, lpstrModule);*/
0N/A if (cacheIndex == -1) {
0N/A /* add this device */
0N/A if (g_cacheCount < MAX_DS_DEVICES-1) {
0N/A g_audioDeviceCache[g_cacheCount].mixerIndex = rs->currMixerIndex;
0N/A g_audioDeviceCache[g_cacheCount].isSource = rs->isSource;
0N/A g_audioDeviceCache[g_cacheCount].dev = NULL;
0N/A g_audioDeviceCache[g_cacheCount].refCount = 0;
0N/A if (lpGuid == NULL) {
0N/A memset(&(g_audioDeviceCache[g_cacheCount].guid), 0, sizeof(GUID));
0N/A } else {
0N/A memcpy(&(g_audioDeviceCache[g_cacheCount].guid), lpGuid, sizeof(GUID));
0N/A }
0N/A g_cacheCount++;
0N/A rs->currMixerIndex++;
0N/A } else {
0N/A /* failure case: more than MAX_DS_DEVICES available... */
0N/A }
0N/A } else {
0N/A /* device already exists in cache... update mixer number */
0N/A g_audioDeviceCache[cacheIndex].mixerIndex = rs->currMixerIndex;
0N/A rs->currMixerIndex++;
0N/A }
0N/A /* continue enumeration */
0N/A return TRUE;
0N/A}
0N/A
0N/A///// implemented functions of DirectAudio.h
0N/A
0N/AINT32 DAUDIO_GetDirectAudioDeviceCount() {
0N/A DS_RefreshCacheStruct rs;
0N/A INT32 oldCount;
0N/A INT32 cacheIndex;
0N/A
0N/A if (!DS_lockCache()) {
0N/A return 0;
0N/A }
0N/A
0N/A if (g_lastCacheRefreshTime == 0
0N/A || (UINT64) timeGetTime() > (UINT64) (g_lastCacheRefreshTime + WAIT_BETWEEN_CACHE_REFRESH_MILLIS)) {
0N/A /* first, initialize any old cache items */
0N/A for (cacheIndex = 0; cacheIndex < g_cacheCount; cacheIndex++) {
0N/A g_audioDeviceCache[cacheIndex].mixerIndex = -1;
0N/A }
0N/A
0N/A /* enumerate all devices and either add them to the device cache,
0N/A * or refresh the mixer number
0N/A */
0N/A rs.currMixerIndex = 0;
0N/A rs.isSource = TRUE;
0N/A DirectSoundEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
0N/A /* if we only got the Primary Sound Driver (GUID=NULL),
0N/A * then there aren't any playback devices installed */
0N/A if (rs.currMixerIndex == 1) {
0N/A cacheIndex = findCacheItemByGUID(NULL, TRUE);
0N/A if (cacheIndex == 0) {
0N/A rs.currMixerIndex = 0;
0N/A g_audioDeviceCache[0].mixerIndex = -1;
0N/A TRACE0("Removing stale Primary Sound Driver from list.\n");
0N/A }
0N/A }
0N/A oldCount = rs.currMixerIndex;
0N/A rs.isSource = FALSE;
0N/A DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
0N/A /* if we only got the Primary Sound Capture Driver (GUID=NULL),
0N/A * then there aren't any capture devices installed */
0N/A if ((rs.currMixerIndex - oldCount) == 1) {
0N/A cacheIndex = findCacheItemByGUID(NULL, FALSE);
0N/A if (cacheIndex != -1) {
0N/A rs.currMixerIndex = oldCount;
0N/A g_audioDeviceCache[cacheIndex].mixerIndex = -1;
0N/A TRACE0("Removing stale Primary Sound Capture Driver from list.\n");
0N/A }
0N/A }
0N/A g_mixerCount = rs.currMixerIndex;
0N/A
0N/A g_lastCacheRefreshTime = (UINT64) timeGetTime();
0N/A }
0N/A DS_unlockCache();
0N/A /*TRACE1("DirectSound: %d installed devices\n", g_mixerCount);*/
0N/A return g_mixerCount;
0N/A}
0N/A
0N/ABOOL CALLBACK DS_GetDescEnum(LPGUID lpGuid,
0N/A LPCSTR lpstrDescription,
0N/A LPCSTR lpstrModule,
0N/A DirectAudioDeviceDescription* desc) {
0N/A
0N/A INT32 cacheIndex = findCacheItemByGUID(lpGuid, g_audioDeviceCache[desc->deviceID].isSource);
0N/A if (cacheIndex == desc->deviceID) {
0N/A strncpy(desc->name, lpstrDescription, DAUDIO_STRING_LENGTH);
0N/A //strncpy(desc->description, lpstrModule, DAUDIO_STRING_LENGTH);
0N/A desc->maxSimulLines = -1;
0N/A /* do not continue enumeration */
0N/A return FALSE;
0N/A }
0N/A return TRUE;
0N/A}
0N/A
0N/A
0N/AINT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* desc) {
0N/A
0N/A if (!DS_lockCache()) {
0N/A return FALSE;
0N/A }
0N/A
0N/A /* set the deviceID field to the cache index */
0N/A desc->deviceID = findCacheItemByMixerIndex(mixerIndex);
0N/A if (desc->deviceID < 0) {
0N/A DS_unlockCache();
0N/A return FALSE;
0N/A }
0N/A desc->maxSimulLines = 0;
0N/A if (g_audioDeviceCache[desc->deviceID].isSource) {
0N/A DirectSoundEnumerate((LPDSENUMCALLBACK) DS_GetDescEnum, desc);
0N/A strncpy(desc->description, "DirectSound Playback", DAUDIO_STRING_LENGTH);
0N/A } else {
0N/A DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_GetDescEnum, desc);
0N/A strncpy(desc->description, "DirectSound Capture", DAUDIO_STRING_LENGTH);
0N/A }
0N/A
0N/A /*desc->vendor;
0N/A desc->version;*/
0N/A
0N/A DS_unlockCache();
0N/A return (desc->maxSimulLines == -1)?TRUE:FALSE;
0N/A}
0N/A
0N/A/* multi-channel info: http://www.microsoft.com/whdc/hwdev/tech/audio/multichaud.mspx */
0N/A
0N/A//static UINT32 sampleRateArray[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 56000, 88000, 96000, 172000, 192000 };
0N/Astatic INT32 sampleRateArray[] = { -1 };
0N/Astatic INT32 channelsArray[] = { 1, 2};
0N/Astatic INT32 bitsArray[] = { 8, 16};
0N/A
0N/A#define SAMPLERATE_COUNT sizeof(sampleRateArray)/sizeof(INT32)
0N/A#define CHANNELS_COUNT sizeof(channelsArray)/sizeof(INT32)
0N/A#define BITS_COUNT sizeof(bitsArray)/sizeof(INT32)
0N/A
0N/Avoid DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
0N/A
0N/A int rateIndex, channelIndex, bitIndex;
0N/A
0N/A /* no need to lock, since deviceID identifies the device sufficiently */
0N/A
0N/A /* sanity */
0N/A if (deviceID >= g_cacheCount) {
0N/A return;
0N/A }
0N/A if ((g_audioDeviceCache[deviceID].isSource && !isSource)
0N/A || (!g_audioDeviceCache[deviceID].isSource && isSource)) {
0N/A /* only support Playback or Capture */
0N/A return;
0N/A }
0N/A
0N/A for (rateIndex = 0; rateIndex < SAMPLERATE_COUNT; rateIndex++) {
0N/A for (channelIndex = 0; channelIndex < CHANNELS_COUNT; channelIndex++) {
0N/A for (bitIndex = 0; bitIndex < BITS_COUNT; bitIndex++) {
0N/A DAUDIO_AddAudioFormat(creator, bitsArray[bitIndex],
0N/A ((bitsArray[bitIndex] + 7) / 8) * channelsArray[channelIndex],
0N/A channelsArray[channelIndex],
0N/A (float) sampleRateArray[rateIndex],
0N/A DAUDIO_PCM,
0N/A (bitsArray[bitIndex]==8)?FALSE:TRUE, /* signed */
0N/A (bitsArray[bitIndex]==8)?FALSE:
0N/A#ifndef _LITTLE_ENDIAN
0N/A TRUE /* big endian */
0N/A#else
0N/A FALSE /* little endian */
0N/A#endif
0N/A );
0N/A }
0N/A }
0N/A }
0N/A}
0N/A
0N/Atypedef struct {
0N/A int deviceID;
0N/A /* for convenience */
0N/A BOOL isSource;
0N/A /* the secondary buffer (Playback) */
0N/A LPDIRECTSOUNDBUFFER playBuffer;
0N/A /* the secondary buffer (Capture) */
0N/A LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;
0N/A
0N/A /* size of the directsound buffer, usually 2 seconds */
0N/A int dsBufferSizeInBytes;
0N/A
0N/A /* size of the read/write-ahead, as specified by Java */
0N/A int bufferSizeInBytes;
0N/A int bitsPerSample;
0N/A int frameSize; // storage size in Bytes
0N/A
0N/A UINT64 framePos;
0N/A /* where to write into the buffer.
0N/A * -1 if at current position (Playback)
0N/A * For Capture, this is the read position
0N/A */
0N/A int writePos;
0N/A
0N/A /* if start() had been called */
0N/A BOOL started;
0N/A
0N/A /* how many bytes there is silence from current write position */
0N/A int silencedBytes;
0N/A
0N/A BOOL underrun;
0N/A
0N/A} DS_Info;
0N/A
0N/A
0N/ALPSTR TranslateDSError(HRESULT hr) {
0N/A switch(hr) {
0N/A case DSERR_ALLOCATED:
0N/A return "DSERR_ALLOCATED";
0N/A
0N/A case DSERR_CONTROLUNAVAIL:
0N/A return "DSERR_CONTROLUNAVAIL";
0N/A
0N/A case DSERR_INVALIDPARAM:
0N/A return "DSERR_INVALIDPARAM";
0N/A
0N/A case DSERR_INVALIDCALL:
0N/A return "DSERR_INVALIDCALL";
0N/A
0N/A case DSERR_GENERIC:
0N/A return "DSERR_GENERIC";
0N/A
0N/A case DSERR_PRIOLEVELNEEDED:
0N/A return "DSERR_PRIOLEVELNEEDED";
0N/A
0N/A case DSERR_OUTOFMEMORY:
0N/A return "DSERR_OUTOFMEMORY";
0N/A
0N/A case DSERR_BADFORMAT:
0N/A return "DSERR_BADFORMAT";
0N/A
0N/A case DSERR_UNSUPPORTED:
0N/A return "DSERR_UNSUPPORTED";
0N/A
0N/A case DSERR_NODRIVER:
0N/A return "DSERR_NODRIVER";
0N/A
0N/A case DSERR_ALREADYINITIALIZED:
0N/A return "DSERR_ALREADYINITIALIZED";
0N/A
0N/A case DSERR_NOAGGREGATION:
0N/A return "DSERR_NOAGGREGATION";
0N/A
0N/A case DSERR_BUFFERLOST:
0N/A return "DSERR_BUFFERLOST";
0N/A
0N/A case DSERR_OTHERAPPHASPRIO:
0N/A return "DSERR_OTHERAPPHASPRIO";
0N/A
0N/A case DSERR_UNINITIALIZED:
0N/A return "DSERR_UNINITIALIZED";
0N/A
0N/A default:
0N/A return "Unknown HRESULT";
0N/A }
0N/A}
0N/A
0N/A/*
0N/A** data/routines for starting DS buffers by separate thread
0N/A** (joint into DS_StartBufferHelper class)
0N/A** see cr6372428: playback fails after exiting from thread that has started it
0N/A** due IDirectSoundBuffer8::Play() description:
0N/A** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
0N/A** /directx/htm/idirectsoundbuffer8play.asp
0N/A** (remark section): If the application is multithreaded, the thread that plays
0N/A** the buffer must continue to exist as long as the buffer is playing.
0N/A** Buffers created on WDM drivers stop playing when the thread is terminated.
0N/A** IDirectSoundCaptureBuffer8::Start() has the same remark:
0N/A** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
0N/A** /directx/htm/idirectsoundcapturebuffer8start.asp
0N/A*/
0N/Aclass DS_StartBufferHelper {
0N/Apublic:
0N/A /* starts DirectSound buffer (playback or capture) */
0N/A static HRESULT StartBuffer(DS_Info* info);
0N/A /* checks for initialization success */
0N/A static inline BOOL isInitialized() { return data.threadHandle != NULL; }
0N/Aprotected:
0N/A DS_StartBufferHelper() {} // no need to create an instance
0N/A
0N/A /* data class */
0N/A class Data {
0N/A public:
0N/A Data();
0N/A ~Data();
0N/A // public data to access from parent class
0N/A CRITICAL_SECTION crit_sect;
0N/A volatile HANDLE threadHandle;
0N/A volatile HANDLE startEvent;
0N/A volatile HANDLE startedEvent;
0N/A volatile DS_Info* line2Start;
0N/A volatile HRESULT startResult;
0N/A } static data;
0N/A
0N/A /* StartThread function */
0N/A static DWORD WINAPI __stdcall ThreadProc(void *param);
0N/A};
0N/A
0N/A/* StartBufferHelper class implementation
0N/A*/
0N/ADS_StartBufferHelper::Data DS_StartBufferHelper::data;
0N/A
0N/ADS_StartBufferHelper::Data::Data() {
0N/A threadHandle = NULL;
0N/A ::InitializeCriticalSection(&crit_sect);
0N/A startEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
0N/A startedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
0N/A if (startEvent != NULL && startedEvent != NULL)
0N/A threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
0N/A}
0N/A
0N/ADS_StartBufferHelper::Data::~Data() {
0N/A ::EnterCriticalSection(&crit_sect);
0N/A if (threadHandle != NULL) {
0N/A // terminate thread
0N/A line2Start = NULL;
0N/A ::SetEvent(startEvent);
0N/A ::CloseHandle(threadHandle);
0N/A threadHandle = NULL;
0N/A }
0N/A ::LeaveCriticalSection(&crit_sect);
0N/A // won't delete startEvent/startedEvent/crit_sect
0N/A // - Windows will do during process shutdown
0N/A}
0N/A
0N/ADWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)
0N/A{
2974N/A ::CoInitialize(NULL);
0N/A while (1) {
0N/A // wait for something to do
0N/A ::WaitForSingleObject(data.startEvent, INFINITE);
0N/A if (data.line2Start == NULL) {
0N/A // (data.line2Start == NULL) is a signal to terminate thread
0N/A break;
0N/A }
0N/A if (data.line2Start->isSource) {
0N/A data.startResult =
1989N/A data.line2Start->playBuffer->Play(0, 0, DSBPLAY_LOOPING);
0N/A } else {
0N/A data.startResult =
0N/A data.line2Start->captureBuffer->Start(DSCBSTART_LOOPING);
0N/A }
0N/A ::SetEvent(data.startedEvent);
0N/A }
2974N/A ::CoUninitialize();
0N/A return 0;
0N/A}
0N/A
0N/AHRESULT DS_StartBufferHelper::StartBuffer(DS_Info* info) {
0N/A HRESULT hr;
0N/A ::EnterCriticalSection(&data.crit_sect);
0N/A if (!isInitialized()) {
0N/A ::LeaveCriticalSection(&data.crit_sect);
0N/A return E_FAIL;
0N/A }
0N/A data.line2Start = info;
0N/A ::SetEvent(data.startEvent);
0N/A ::WaitForSingleObject(data.startedEvent, INFINITE);
0N/A hr = data.startResult;
0N/A ::LeaveCriticalSection(&data.crit_sect);
0N/A return hr;
0N/A}
0N/A
0N/A
0N/A/* helper routines for DS buffer positions */
0N/A/* returns distance from pos1 to pos2
0N/A */
0N/Ainline int DS_getDistance(DS_Info* info, int pos1, int pos2) {
0N/A int distance = pos2 - pos1;
0N/A while (distance < 0)
0N/A distance += info->dsBufferSizeInBytes;
0N/A return distance;
0N/A}
0N/A
0N/A/* adds 2 positions
0N/A */
0N/Ainline int DS_addPos(DS_Info* info, int pos1, int pos2) {
0N/A int result = pos1 + pos2;
0N/A while (result >= info->dsBufferSizeInBytes)
0N/A result -= info->dsBufferSizeInBytes;
0N/A return result;
0N/A}
0N/A
0N/A
0N/ABOOL DS_addDeviceRef(INT32 deviceID) {
0N/A HWND ownerWindow;
0N/A HRESULT res = DS_OK;
0N/A LPDIRECTSOUND devPlay;
0N/A LPDIRECTSOUNDCAPTURE devCapture;
0N/A LPGUID lpGuid = NULL;
0N/A
0N/A
0N/A if (g_audioDeviceCache[deviceID].dev == NULL) {
0N/A /* Create DirectSound */
0N/A TRACE1("Creating DirectSound object for device %d\n", deviceID);
0N/A lpGuid = &(g_audioDeviceCache[deviceID].guid);
0N/A if (isEqualGUID(lpGuid, NULL)) {
0N/A lpGuid = NULL;
0N/A }
0N/A if (g_audioDeviceCache[deviceID].isSource) {
0N/A res = DirectSoundCreate(lpGuid, &devPlay, NULL);
0N/A g_audioDeviceCache[deviceID].dev = (void*) devPlay;
0N/A } else {
0N/A res = DirectSoundCaptureCreate(lpGuid, &devCapture, NULL);
0N/A g_audioDeviceCache[deviceID].dev = (void*) devCapture;
0N/A }
0N/A g_audioDeviceCache[deviceID].refCount = 0;
0N/A if (FAILED(res)) {
0N/A ERROR1("DAUDIO_Open: ERROR: Failed to create DirectSound: %s", TranslateDSError(res));
0N/A g_audioDeviceCache[deviceID].dev = NULL;
0N/A return FALSE;
0N/A }
0N/A if (g_audioDeviceCache[deviceID].isSource) {
0N/A ownerWindow = GetForegroundWindow();
0N/A if (ownerWindow == NULL) {
0N/A ownerWindow = GetDesktopWindow();
0N/A }
0N/A TRACE0("DAUDIO_Open: Setting cooperative level\n");
0N/A res = devPlay->SetCooperativeLevel(ownerWindow, DSSCL_NORMAL);
0N/A if (FAILED(res)) {
0N/A ERROR1("DAUDIO_Open: ERROR: Failed to set cooperative level: %s", TranslateDSError(res));
0N/A return FALSE;
0N/A }
0N/A }
0N/A }
0N/A g_audioDeviceCache[deviceID].refCount++;
0N/A return TRUE;
0N/A}
0N/A
0N/A#define DEV_PLAY(devID) ((LPDIRECTSOUND) g_audioDeviceCache[devID].dev)
0N/A#define DEV_CAPTURE(devID) ((LPDIRECTSOUNDCAPTURE) g_audioDeviceCache[devID].dev)
0N/A
0N/Avoid DS_removeDeviceRef(INT32 deviceID) {
0N/A
0N/A if (g_audioDeviceCache[deviceID].refCount) {
0N/A g_audioDeviceCache[deviceID].refCount--;
0N/A }
0N/A if (g_audioDeviceCache[deviceID].refCount == 0) {
0N/A if (g_audioDeviceCache[deviceID].dev != NULL) {
0N/A if (g_audioDeviceCache[deviceID].isSource) {
0N/A DEV_PLAY(deviceID)->Release();
0N/A } else {
0N/A DEV_CAPTURE(deviceID)->Release();
0N/A }
0N/A g_audioDeviceCache[deviceID].dev = NULL;
0N/A }
0N/A }
0N/A}
0N/A
0N/A#ifndef _WAVEFORMATEXTENSIBLE_
0N/A#define _WAVEFORMATEXTENSIBLE_
0N/Atypedef struct {
0N/A WAVEFORMATEX Format;
0N/A union {
0N/A WORD wValidBitsPerSample; /* bits of precision */
0N/A WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
0N/A WORD wReserved; /* If neither applies, set to zero. */
0N/A } Samples;
0N/A DWORD dwChannelMask; /* which channels are */
0N/A /* present in stream */
0N/A GUID SubFormat;
0N/A} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
0N/A#endif // !_WAVEFORMATEXTENSIBLE_
0N/A
0N/A#if !defined(WAVE_FORMAT_EXTENSIBLE)
0N/A#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
0N/A#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
0N/A
0N/A#if !defined(DEFINE_WAVEFORMATEX_GUID)
0N/A#define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
0N/A#endif
0N/A#ifndef STATIC_KSDATAFORMAT_SUBTYPE_PCM
0N/A#define STATIC_KSDATAFORMAT_SUBTYPE_PCM\
0N/A DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
0N/A#endif
0N/A
0N/A
0N/Avoid createWaveFormat(WAVEFORMATEXTENSIBLE* format,
0N/A int sampleRate,
0N/A int channels,
0N/A int bits,
0N/A int significantBits) {
0N/A GUID subtypePCM = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};
0N/A format->Format.nSamplesPerSec = (DWORD)sampleRate;
0N/A format->Format.nChannels = (WORD) channels;
0N/A /* do not support useless padding, like 24-bit samples stored in 32-bit containers */
0N/A format->Format.wBitsPerSample = (WORD) ((bits + 7) & 0xFFF8);
0N/A
0N/A if (channels <= 2 && bits <= 16) {
0N/A format->Format.wFormatTag = WAVE_FORMAT_PCM;
0N/A format->Format.cbSize = 0;
0N/A } else {
0N/A format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
0N/A format->Format.cbSize = 22;
0N/A format->Samples.wValidBitsPerSample = bits;
0N/A /* no way to specify speaker locations */
0N/A format->dwChannelMask = 0xFFFFFFFF;
0N/A format->SubFormat = subtypePCM;
0N/A }
0N/A format->Format.nBlockAlign = (WORD)((format->Format.wBitsPerSample * format->Format.nChannels) / 8);
0N/A format->Format.nAvgBytesPerSec = format->Format.nSamplesPerSec * format->Format.nBlockAlign;
0N/A}
0N/A
0N/A/* fill buffer with silence
0N/A */
0N/Avoid DS_clearBuffer(DS_Info* info, BOOL fromWritePos) {
0N/A UBYTE* pb1=NULL, *pb2=NULL;
0N/A DWORD cb1=0, cb2=0;
0N/A DWORD flags = 0;
0N/A int start, count;
0N/A TRACE1("> DS_clearBuffer for device %d\n", info->deviceID);
0N/A if (info->isSource) {
0N/A if (fromWritePos) {
0N/A DWORD playCursor, writeCursor;
0N/A int end;
0N/A if (FAILED(info->playBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
0N/A ERROR0(" DS_clearBuffer: ERROR: Failed to get current position.");
0N/A TRACE0("< DS_clearbuffer\n");
0N/A return;
0N/A }
0N/A DEBUG_SILENCING2(" DS_clearBuffer: DS playPos=%d myWritePos=%d", (int) playCursor, (int) info->writePos);
0N/A if (info->writePos >= 0) {
0N/A start = info->writePos + info->silencedBytes;
0N/A } else {
0N/A start = writeCursor + info->silencedBytes;
0N/A //flags |= DSBLOCK_FROMWRITECURSOR;
0N/A }
0N/A while (start >= info->dsBufferSizeInBytes) {
0N/A start -= info->dsBufferSizeInBytes;
0N/A }
0N/A
0N/A // fix for bug 6251460 (REGRESSION: short sounds do not play)
0N/A // for unknown reason with hardware DS buffer playCursor sometimes
0N/A // jumps back for little interval (mostly 2-8 bytes) (writeCursor moves forward as usual)
0N/A // The issue happens right after start playing and for short sounds only (less then DS buffer,
0N/A // when whole sound written into the buffer and remaining space filled by silence)
0N/A // the case doesn't produce any audible aftifacts so just catch it to prevent filling
0N/A // whole buffer by silence.
0N/A if (((int)playCursor <= start && start < (int)writeCursor)
0N/A || (writeCursor < playCursor // buffer bound is between playCursor & writeCursor
0N/A && (start < (int)writeCursor || (int)playCursor <= start))) {
0N/A return;
0N/A }
0N/A
0N/A count = info->dsBufferSizeInBytes - info->silencedBytes;
0N/A // why / 4?
0N/A //if (count > info->dsBufferSizeInBytes / 4) {
0N/A // count = info->dsBufferSizeInBytes / 4;
0N/A //}
0N/A end = start + count;
0N/A if ((int) playCursor < start) {
0N/A playCursor += (DWORD) info->dsBufferSizeInBytes;
0N/A }
0N/A if (start <= (int) playCursor && end > (int) playCursor) {
0N/A /* at maximum, silence until play cursor */
0N/A count = (int) playCursor - start;
0N/A#ifdef USE_TRACE
0N/A if ((int) playCursor >= info->dsBufferSizeInBytes) playCursor -= (DWORD) info->dsBufferSizeInBytes;
0N/A TRACE3("\n DS_clearBuffer: Start Writing from %d, "
0N/A "would overwrite playCursor=%d, so reduce count to %d\n",
0N/A start, playCursor, count);
0N/A#endif
0N/A }
0N/A DEBUG_SILENCING2(" clearing buffer from %d, count=%d. ", (int)start, (int) count);
0N/A if (count <= 0) {
0N/A DEBUG_SILENCING0("\n");
0N/A TRACE1("< DS_clearBuffer: no need to clear, silencedBytes=%d\n", info->silencedBytes);
0N/A return;
0N/A }
0N/A } else {
0N/A start = 0;
0N/A count = info->dsBufferSizeInBytes;
0N/A flags |= DSBLOCK_ENTIREBUFFER;
0N/A }
0N/A if (FAILED(info->playBuffer->Lock(start,
0N/A count,
0N/A (LPVOID*) &pb1, &cb1,
0N/A (LPVOID*) &pb2, &cb2, flags))) {
0N/A ERROR0("\n DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
0N/A TRACE0("< DS_clearbuffer\n");
0N/A return;
0N/A }
0N/A } else {
0N/A if (FAILED(info->captureBuffer->Lock(0,
0N/A info->dsBufferSizeInBytes,
0N/A (LPVOID*) &pb1, &cb1,
0N/A (LPVOID*) &pb2, &cb2, DSCBLOCK_ENTIREBUFFER))) {
0N/A ERROR0(" DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
0N/A TRACE0("< DS_clearbuffer\n");
0N/A return;
0N/A }
0N/A }
0N/A if (pb1!=NULL) {
0N/A memset(pb1, (info->bitsPerSample == 8)?128:0, cb1);
0N/A }
0N/A if (pb2!=NULL) {
0N/A memset(pb2, (info->bitsPerSample == 8)?128:0, cb2);
0N/A }
0N/A if (info->isSource) {
0N/A info->playBuffer->Unlock( pb1, cb1, pb2, cb2 );
0N/A if (!fromWritePos) {
0N/A /* doesn't matter where to start writing next time */
0N/A info->writePos = -1;
0N/A info->silencedBytes = info->dsBufferSizeInBytes;
0N/A } else {
0N/A info->silencedBytes += (cb1+cb2);
0N/A if (info->silencedBytes > info->dsBufferSizeInBytes) {
0N/A ERROR1(" DS_clearbuffer: ERROR: silencedBytes=%d exceeds buffer size!\n",
0N/A info->silencedBytes);
0N/A info->silencedBytes = info->dsBufferSizeInBytes;
0N/A }
0N/A }
0N/A DEBUG_SILENCING2(" silencedBytes=%d, my writePos=%d\n", (int)info->silencedBytes, (int)info->writePos);
0N/A } else {
0N/A info->captureBuffer->Unlock( pb1, cb1, pb2, cb2 );
0N/A }
0N/A TRACE0("< DS_clearbuffer\n");
0N/A}
0N/A
0N/A/* returns pointer to buffer */
0N/Avoid* DS_createSoundBuffer(DS_Info* info,
0N/A float sampleRate,
0N/A int sampleSizeInBits,
0N/A int channels,
0N/A int bufferSizeInBytes) {
0N/A DSBUFFERDESC dsbdesc;
0N/A DSCBUFFERDESC dscbdesc;
0N/A HRESULT res;
0N/A WAVEFORMATEXTENSIBLE format;
0N/A void* buffer;
0N/A
0N/A TRACE1("Creating secondary buffer for device %d\n", info->deviceID);
0N/A createWaveFormat(&format,
0N/A (int) sampleRate,
0N/A channels,
0N/A info->frameSize / channels * 8,
0N/A sampleSizeInBits);
0N/A
0N/A /* 2 second secondary buffer */
0N/A info->dsBufferSizeInBytes = 2 * ((int) sampleRate) * info->frameSize;
0N/A
0N/A if (bufferSizeInBytes > info->dsBufferSizeInBytes / 2) {
0N/A bufferSizeInBytes = info->dsBufferSizeInBytes / 2;
0N/A }
0N/A bufferSizeInBytes = (bufferSizeInBytes / info->frameSize) * info->frameSize;
0N/A info->bufferSizeInBytes = bufferSizeInBytes;
0N/A
0N/A if (info->isSource) {
0N/A memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
0N/A dsbdesc.dwSize = sizeof(DSBUFFERDESC);
0N/A dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
0N/A | DSBCAPS_GLOBALFOCUS;
0N/A
0N/A dsbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
0N/A dsbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
0N/A res = DEV_PLAY(info->deviceID)->CreateSoundBuffer
0N/A (&dsbdesc, (LPDIRECTSOUNDBUFFER*) &buffer, NULL);
0N/A } else {
0N/A memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
0N/A dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
0N/A dscbdesc.dwFlags = 0;
0N/A dscbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
0N/A dscbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
0N/A res = DEV_CAPTURE(info->deviceID)->CreateCaptureBuffer
0N/A (&dscbdesc, (LPDIRECTSOUNDCAPTUREBUFFER*) &buffer, NULL);
0N/A }
0N/A if (FAILED(res)) {
0N/A ERROR1("DS_createSoundBuffer: ERROR: Failed to create sound buffer: %s", TranslateDSError(res));
0N/A return NULL;
0N/A }
0N/A return buffer;
0N/A}
0N/A
0N/Avoid DS_destroySoundBuffer(DS_Info* info) {
0N/A if (info->playBuffer != NULL) {
0N/A info->playBuffer->Release();
0N/A info->playBuffer = NULL;
0N/A }
0N/A if (info->captureBuffer != NULL) {
0N/A info->captureBuffer->Release();
0N/A info->captureBuffer = NULL;
0N/A }
0N/A}
0N/A
0N/A
0N/Avoid* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
0N/A int encoding, float sampleRate, int sampleSizeInBits,
0N/A int frameSize, int channels,
0N/A int isSigned, int isBigEndian, int bufferSizeInBytes) {
0N/A
0N/A DS_Info* info;
0N/A void* buffer;
0N/A
0N/A TRACE0("> DAUDIO_Open\n");
0N/A
0N/A /* some sanity checks */
0N/A if (deviceID >= g_cacheCount) {
0N/A ERROR1("DAUDIO_Open: ERROR: cannot open the device with deviceID=%d!\n", deviceID);
0N/A return NULL;
0N/A }
0N/A if ((g_audioDeviceCache[deviceID].isSource && !isSource)
0N/A || (!g_audioDeviceCache[deviceID].isSource && isSource)) {
0N/A /* only support Playback or Capture */
0N/A ERROR0("DAUDIO_Open: ERROR: Cache is corrupt: cannot open the device in specified isSource mode!\n");
0N/A return NULL;
0N/A }
0N/A if (encoding != DAUDIO_PCM) {
0N/A ERROR1("DAUDIO_Open: ERROR: cannot open the device with encoding=%d!\n", encoding);
0N/A return NULL;
0N/A }
0N/A if (sampleSizeInBits > 8 &&
0N/A#ifdef _LITTLE_ENDIAN
0N/A isBigEndian
0N/A#else
0N/A !isBigEndian
0N/A#endif
0N/A ) {
0N/A ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian);
0N/A return NULL;
0N/A }
0N/A if (sampleSizeInBits == 8 && isSigned) {
0N/A ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n");
0N/A return NULL;
0N/A }
0N/A if (!DS_StartBufferHelper::isInitialized()) {
0N/A ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n");
0N/A return NULL;
0N/A }
0N/A
0N/A info = (DS_Info*) malloc(sizeof(DS_Info));
0N/A if (!info) {
0N/A ERROR0("DAUDIO_Open: ERROR: Out of memory\n");
0N/A return NULL;
0N/A }
0N/A memset(info, 0, sizeof(DS_Info));
0N/A
0N/A info->deviceID = deviceID;
0N/A info->isSource = isSource;
0N/A info->bitsPerSample = sampleSizeInBits;
0N/A info->frameSize = frameSize;
0N/A info->framePos = 0;
0N/A info->started = FALSE;
0N/A info->underrun = FALSE;
0N/A
0N/A if (!DS_addDeviceRef(deviceID)) {
0N/A DS_removeDeviceRef(deviceID);
0N/A free(info);
0N/A return NULL;
0N/A }
0N/A
0N/A buffer = DS_createSoundBuffer(info,
0N/A sampleRate,
0N/A sampleSizeInBits,
0N/A channels,
0N/A bufferSizeInBytes);
0N/A if (!buffer) {
0N/A DS_removeDeviceRef(deviceID);
0N/A free(info);
0N/A return NULL;
0N/A }
0N/A
0N/A if (info->isSource) {
0N/A info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer;
0N/A } else {
0N/A info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer;
0N/A }
0N/A DS_clearBuffer(info, FALSE /* entire buffer */);
0N/A
0N/A /* use writepos of device */
0N/A if (info->isSource) {
0N/A info->writePos = -1;
0N/A } else {
0N/A info->writePos = 0;
0N/A }
0N/A
0N/A TRACE0("< DAUDIO_Open: Opened device successfully.\n");
0N/A return (void*) info;
0N/A}
0N/A
0N/Aint DAUDIO_Start(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A HRESULT res = DS_OK;
0N/A DWORD status;
0N/A
0N/A TRACE0("> DAUDIO_Start\n");
0N/A
0N/A if (info->isSource) {
0N/A res = info->playBuffer->GetStatus(&status);
0N/A if (res == DS_OK) {
0N/A if (status & DSBSTATUS_LOOPING) {
0N/A ERROR0("DAUDIO_Start: ERROR: Already started!");
0N/A return TRUE;
0N/A }
0N/A
0N/A /* only start buffer if already something written to it */
0N/A if (info->writePos >= 0) {
0N/A res = DS_StartBufferHelper::StartBuffer(info);
0N/A if (res == DSERR_BUFFERLOST) {
0N/A res = info->playBuffer->Restore();
0N/A if (res == DS_OK) {
0N/A DS_clearBuffer(info, FALSE /* entire buffer */);
0N/A /* write() will trigger actual device start */
0N/A }
0N/A } else {
0N/A /* make sure that we will have silence after
0N/A the currently valid audio data */
0N/A DS_clearBuffer(info, TRUE /* from write position */);
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A if (info->captureBuffer->GetStatus(&status) == DS_OK) {
0N/A if (status & DSCBSTATUS_LOOPING) {
0N/A ERROR0("DAUDIO_Start: ERROR: Already started!");
0N/A return TRUE;
0N/A }
0N/A }
0N/A res = DS_StartBufferHelper::StartBuffer(info);
0N/A }
0N/A if (FAILED(res)) {
0N/A ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res));
0N/A return FALSE;
0N/A }
0N/A info->started = TRUE;
0N/A return TRUE;
0N/A}
0N/A
0N/Aint DAUDIO_Stop(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A
0N/A TRACE0("> DAUDIO_Stop\n");
0N/A
0N/A info->started = FALSE;
0N/A if (info->isSource) {
0N/A info->playBuffer->Stop();
0N/A } else {
0N/A info->captureBuffer->Stop();
0N/A }
0N/A
0N/A TRACE0("< DAUDIO_Stop\n");
0N/A return TRUE;
0N/A}
0N/A
0N/A
0N/Avoid DAUDIO_Close(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A
0N/A TRACE0("DAUDIO_Close\n");
0N/A
0N/A if (info != NULL) {
0N/A DS_destroySoundBuffer(info);
0N/A DS_removeDeviceRef(info->deviceID);
0N/A free(info);
0N/A }
0N/A}
0N/A
0N/A/* Check buffer for underrun
0N/A * This method is only meaningful for Output devices (write devices).
0N/A */
0N/Avoid DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) {
0N/A TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, "
0N/A "info->writePos=%d silencedBytes=%d dsBufferSizeInBytes=%d\n",
0N/A (int) playCursor, (int) writeCursor, (int) info->writePos,
0N/A (int) info->silencedBytes, (int) info->dsBufferSizeInBytes);
0N/A if (info->underrun || info->writePos < 0) return;
0N/A int writeAhead = DS_getDistance(info, writeCursor, info->writePos);
0N/A if (writeAhead > info->bufferSizeInBytes) {
0N/A // this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes)
0N/A // But the case can occur only when we have more then info->bufferSizeInBytes valid bytes
0N/A // (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes)
0N/A // If we already have a lot of silencedBytes after valid data (written by
0N/A // DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun
0N/A if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) {
0N/A // underrun!
0N/A ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n");
0N/A info->underrun = TRUE;
0N/A }
0N/A }
0N/A}
0N/A
0N/A/* For source (playback) line:
0N/A * (a) if (fromPlayCursor == FALSE), returns number of bytes available
0N/A * for writing: bufferSize - (info->writePos - writeCursor);
0N/A * (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor
0N/A * and returned value can be used for play position calculation (see also
0N/A * note about bufferSize)
0N/A * For destination (capture) line:
0N/A * (c) if (fromPlayCursor == FALSE), returns number of bytes available
0N/A * for reading from the buffer: readCursor - info->writePos;
0N/A * (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor
0N/A * and returned value can be used for capture position calculation (see
0N/A * note about bufferSize)
0N/A * bufferSize parameter are filled by "actual" buffer size:
0N/A * if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes
0N/A * otherwise it increase by number of bytes currently processed by DirectSound
0N/A * (writeCursor - playCursor) or (captureCursor - readCursor)
0N/A */
0N/Aint DS_GetAvailable(DS_Info* info,
0N/A DWORD* playCursor, DWORD* writeCursor,
0N/A int* bufferSize, BOOL fromPlayCursor) {
0N/A int available;
0N/A int newReadPos;
0N/A
0N/A TRACE2("DS_GetAvailable: fromPlayCursor=%d, deviceID=%d\n", fromPlayCursor, info->deviceID);
0N/A if (!info->playBuffer && !info->captureBuffer) {
0N/A ERROR0("DS_GetAvailable: ERROR: buffer not yet created");
0N/A return 0;
0N/A }
0N/A
0N/A if (info->isSource) {
0N/A if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) {
0N/A ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
0N/A return 0;
0N/A }
0N/A int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
0N/A // workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor
0N/A if (processing > info->dsBufferSizeInBytes / 2) {
0N/A *writeCursor = *playCursor;
0N/A processing = 0;
0N/A }
0N/A TRACE3(" playCursor=%d, writeCursor=%d, info->writePos=%d\n",
0N/A *playCursor, *writeCursor, info->writePos);
0N/A *bufferSize = info->bufferSizeInBytes;
0N/A if (fromPlayCursor) {
0N/A *bufferSize += processing;
0N/A }
0N/A DS_CheckUnderrun(info, *playCursor, *writeCursor);
0N/A if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) {
0N/A /* always full buffer if at beginning */
0N/A available = *bufferSize;
0N/A } else {
0N/A int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor, info->writePos);
0N/A if (currWriteAhead > *bufferSize) {
0N/A if (info->underrun) {
0N/A // playCursor surpassed writePos - no valid data, whole buffer available
0N/A available = *bufferSize;
0N/A } else {
0N/A // the case may occur after stop(), when writeCursor jumps back to playCursor
0N/A // so "actual" buffer size has grown
0N/A *bufferSize = currWriteAhead;
0N/A available = 0;
0N/A }
0N/A } else {
0N/A available = *bufferSize - currWriteAhead;
0N/A }
0N/A }
0N/A } else {
0N/A if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) {
0N/A ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
0N/A return 0;
0N/A }
0N/A *bufferSize = info->bufferSizeInBytes;
0N/A if (fromPlayCursor) {
0N/A *bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
0N/A }
0N/A TRACE4(" captureCursor=%d, readCursor=%d, info->readPos=%d refBufferSize=%d\n",
0N/A *playCursor, *writeCursor, info->writePos, *bufferSize);
0N/A if (info->writePos == -1) {
0N/A /* always empty buffer if at beginning */
0N/A info->writePos = (int) (*writeCursor);
0N/A }
0N/A if (fromPlayCursor) {
0N/A available = ((int) (*playCursor) - info->writePos);
0N/A } else {
0N/A available = ((int) (*writeCursor) - info->writePos);
0N/A }
0N/A if (available < 0) {
0N/A available += info->dsBufferSizeInBytes;
0N/A }
0N/A if (!fromPlayCursor && available > info->bufferSizeInBytes) {
0N/A /* overflow */
0N/A ERROR2("DS_GetAvailable: ERROR: overflow detected: "
0N/A "DirectSoundBufferSize=%d, bufferSize=%d, ",
0N/A info->dsBufferSizeInBytes, info->bufferSizeInBytes);
0N/A ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n",
0N/A *playCursor, *writeCursor, info->writePos);
0N/A /* advance read position, to allow exactly one buffer worth of data */
0N/A newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes;
0N/A if (newReadPos < 0) {
0N/A newReadPos += info->dsBufferSizeInBytes;
0N/A }
0N/A info->writePos = newReadPos;
0N/A available = info->bufferSizeInBytes;
0N/A }
0N/A }
0N/A available = (available / info->frameSize) * info->frameSize;
0N/A
0N/A TRACE1("DS_available: Returning %d available bytes\n", (int) available);
0N/A return available;
0N/A}
0N/A
0N/A// returns -1 on error, otherwise bytes written
0N/Aint DAUDIO_Write(void* id, char* data, int byteSize) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A int available;
0N/A int thisWritePos;
0N/A DWORD playCursor, writeCursor;
0N/A HRESULT res;
0N/A void* buffer1, *buffer2;
0N/A DWORD buffer1len, buffer2len;
0N/A BOOL needRestart = FALSE;
0N/A int bufferLostTrials = 2;
0N/A int bufferSize;
0N/A
0N/A TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
0N/A
0N/A while (--bufferLostTrials > 0) {
0N/A available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */);
0N/A if (byteSize > available) byteSize = available;
0N/A if (byteSize == 0) break;
0N/A thisWritePos = info->writePos;
0N/A if (thisWritePos == -1 || info->underrun) {
0N/A // play from current write cursor after flush, etc.
0N/A needRestart = TRUE;
0N/A thisWritePos = writeCursor;
0N/A info->underrun = FALSE;
0N/A }
0N/A DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize);
0N/A res = info->playBuffer->Lock(thisWritePos, byteSize,
0N/A (LPVOID *) &buffer1, &buffer1len,
0N/A (LPVOID *) &buffer2, &buffer2len,
0N/A 0);
0N/A if (res != DS_OK) {
0N/A /* some DS failure */
0N/A if (res == DSERR_BUFFERLOST) {
0N/A ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer.");
0N/A if (info->playBuffer->Restore() == DS_OK) {
0N/A DS_clearBuffer(info, FALSE /* entire buffer */);
0N/A info->writePos = -1;
0N/A /* try again */
0N/A continue;
0N/A }
0N/A }
0N/A /* can't recover from error */
0N/A byteSize = 0;
0N/A break;
0N/A }
0N/A /* buffer could be locked successfully */
0N/A /* first fill first buffer */
0N/A if (buffer1) {
0N/A memcpy(buffer1, data, buffer1len);
0N/A data = (char*) (((UINT_PTR) data) + buffer1len);
0N/A } else buffer1len = 0;
0N/A if (buffer2) {
0N/A memcpy(buffer2, data, buffer2len);
0N/A } else buffer2len = 0;
0N/A byteSize = buffer1len + buffer2len;
0N/A
0N/A /* update next write pos */
0N/A thisWritePos += byteSize;
0N/A while (thisWritePos >= info->dsBufferSizeInBytes) {
0N/A thisWritePos -= info->dsBufferSizeInBytes;
0N/A }
0N/A /* commit data to directsound */
0N/A info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
0N/A
0N/A info->writePos = thisWritePos;
0N/A
0N/A /* update position
0N/A * must be AFTER updating writePos,
0N/A * so that getSvailable doesn't return too little,
0N/A * so that getFramePos doesn't jump
0N/A */
0N/A info->framePos += (byteSize / info->frameSize);
0N/A
0N/A /* decrease silenced bytes */
0N/A if (info->silencedBytes > byteSize) {
0N/A info->silencedBytes -= byteSize;
0N/A } else {
0N/A info->silencedBytes = 0;
0N/A }
0N/A break;
0N/A } /* while */
0N/A
0N/A /* start the device, if necessary */
0N/A if (info->started && needRestart && (info->writePos >= 0)) {
0N/A DS_StartBufferHelper::StartBuffer(info);
0N/A }
0N/A
0N/A TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize);
0N/A return byteSize;
0N/A}
0N/A
0N/A// returns -1 on error
0N/Aint DAUDIO_Read(void* id, char* data, int byteSize) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A int available;
0N/A int thisReadPos;
0N/A DWORD captureCursor, readCursor;
0N/A HRESULT res;
0N/A void* buffer1, *buffer2;
0N/A DWORD buffer1len, buffer2len;
0N/A int bufferSize;
0N/A
0N/A TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
0N/A
0N/A available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */);
0N/A if (byteSize > available) byteSize = available;
0N/A if (byteSize > 0) {
0N/A thisReadPos = info->writePos;
0N/A if (thisReadPos == -1) {
0N/A /* from beginning */
0N/A thisReadPos = 0;
0N/A }
0N/A res = info->captureBuffer->Lock(thisReadPos, byteSize,
0N/A (LPVOID *) &buffer1, &buffer1len,
0N/A (LPVOID *) &buffer2, &buffer2len,
0N/A 0);
0N/A if (res != DS_OK) {
0N/A /* can't recover from error */
0N/A byteSize = 0;
0N/A } else {
0N/A /* buffer could be locked successfully */
0N/A /* first fill first buffer */
0N/A if (buffer1) {
0N/A memcpy(data, buffer1, buffer1len);
0N/A data = (char*) (((UINT_PTR) data) + buffer1len);
0N/A } else buffer1len = 0;
0N/A if (buffer2) {
0N/A memcpy(data, buffer2, buffer2len);
0N/A } else buffer2len = 0;
0N/A byteSize = buffer1len + buffer2len;
0N/A
0N/A /* update next read pos */
0N/A thisReadPos = DS_addPos(info, thisReadPos, byteSize);
0N/A /* commit data to directsound */
0N/A info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
0N/A
0N/A /* update position
0N/A * must be BEFORE updating readPos,
0N/A * so that getAvailable doesn't return too much,
0N/A * so that getFramePos doesn't jump
0N/A */
0N/A info->framePos += (byteSize / info->frameSize);
0N/A
0N/A info->writePos = thisReadPos;
0N/A }
0N/A }
0N/A
0N/A TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize);
0N/A return byteSize;
0N/A}
0N/A
0N/A
0N/Aint DAUDIO_GetBufferSize(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A return info->bufferSizeInBytes;
0N/A}
0N/A
0N/Aint DAUDIO_StillDraining(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A BOOL draining = FALSE;
0N/A int available, bufferSize;
0N/A DWORD playCursor, writeCursor;
0N/A
0N/A DS_clearBuffer(info, TRUE /* from write position */);
0N/A available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */);
0N/A draining = (available < bufferSize);
0N/A
0N/A TRACE3("DAUDIO_StillDraining: available=%d silencedBytes=%d Still draining: %s\n",
0N/A available, info->silencedBytes, draining?"TRUE":"FALSE");
0N/A return draining;
0N/A}
0N/A
0N/A
0N/Aint DAUDIO_Flush(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A
0N/A TRACE0("DAUDIO_Flush\n");
0N/A
0N/A if (info->isSource) {
0N/A info->playBuffer->Stop();
0N/A DS_clearBuffer(info, FALSE /* entire buffer */);
0N/A } else {
0N/A DWORD captureCursor, readCursor;
0N/A /* set the read pointer to the current read position */
0N/A if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) {
0N/A ERROR0("DAUDIO_Flush: ERROR: Failed to get current position.");
0N/A return FALSE;
0N/A }
0N/A DS_clearBuffer(info, FALSE /* entire buffer */);
0N/A /* SHOULD set to *captureCursor*,
0N/A * but that would be detected as overflow
0N/A * in a subsequent GetAvailable() call.
0N/A */
0N/A info->writePos = (int) readCursor;
0N/A }
0N/A return TRUE;
0N/A}
0N/A
0N/Aint DAUDIO_GetAvailable(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A DWORD playCursor, writeCursor;
0N/A int ret, bufferSize;
0N/A
0N/A ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE);
0N/A
0N/A TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
0N/A return ret;
0N/A}
0N/A
0N/AINT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) {
0N/A // estimate the current position with the buffer size and
0N/A // the available bytes to read or write in the buffer.
0N/A // not an elegant solution - bytePos will stop on xruns,
0N/A // and in race conditions it may jump backwards
0N/A // Advantage is that it is indeed based on the samples that go through
0N/A // the system (rather than time-based methods)
0N/A if (info->isSource) {
0N/A // javaBytePos is the position that is reached when the current
0N/A // buffer is played completely
0N/A return (INT64) (javaBytePos - bufferSize + availInBytes);
0N/A } else {
0N/A // javaBytePos is the position that was when the current buffer was empty
0N/A return (INT64) (javaBytePos + availInBytes);
0N/A }
0N/A}
0N/A
0N/AINT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A int available, bufferSize;
0N/A DWORD playCursor, writeCursor;
0N/A INT64 result = javaBytePos;
0N/A
0N/A available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE);
0N/A result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available);
0N/A return result;
0N/A}
0N/A
0N/A
0N/Avoid DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
0N/A /* save to ignore, since GetBytePosition
0N/A * takes the javaBytePos param into account
0N/A */
0N/A}
0N/A
0N/Aint DAUDIO_RequiresServicing(void* id, int isSource) {
0N/A // need servicing on for SourceDataLines
0N/A return isSource?TRUE:FALSE;
0N/A}
0N/A
0N/Avoid DAUDIO_Service(void* id, int isSource) {
0N/A DS_Info* info = (DS_Info*) id;
0N/A if (isSource) {
0N/A if (info->silencedBytes < info->dsBufferSizeInBytes) {
0N/A // clear buffer
0N/A TRACE0("DAUDIO_Service\n");
0N/A DS_clearBuffer(info, TRUE /* from write position */);
0N/A }
0N/A if (info->writePos >= 0
0N/A && info->started
0N/A && !info->underrun
0N/A && info->silencedBytes >= info->dsBufferSizeInBytes) {
0N/A // if we're currently playing, and the entire buffer is silenced...
0N/A // then we are underrunning!
0N/A info->underrun = TRUE;
0N/A ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n");
0N/A }
0N/A }
0N/A}
0N/A
0N/A
0N/A#endif // USE_DAUDIO