Path: blob/master/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
41149 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#define USE_ERROR26#define USE_TRACE2728/* define this for the silencing/servicing code. Requires USE_TRACE */29//#define USE_DEBUG_SILENCING3031#ifndef WIN32_EXTRA_LEAN32#define WIN32_EXTRA_LEAN33#endif34#ifndef WIN32_LEAN_AND_MEAN35#define WIN32_LEAN_AND_MEAN36#endif3738#include <windows.h>39#include <mmsystem.h>40#include <string.h>4142/* include DirectSound headers */43#include <dsound.h>4445/* include Java Sound specific headers as C code */46#ifdef __cplusplus47extern "C" {48#endif49#include "DirectAudio.h"50#ifdef __cplusplus51}52#endif5354/* include to prevent charset problem */55#include "PLATFORM_API_WinOS_Charset_Util.h"5657#ifdef USE_DEBUG_SILENCING58#define DEBUG_SILENCING0(p) TRACE0(p)59#define DEBUG_SILENCING1(p1,p2) TRACE1(p1,p2)60#define DEBUG_SILENCING2(p1,p2,p3) TRACE2(p1,p2,p3)61#else62#define DEBUG_SILENCING0(p)63#define DEBUG_SILENCING1(p1,p2)64#define DEBUG_SILENCING2(p1,p2,p3)65#endif666768#if USE_DAUDIO == TRUE6970/* 3 seconds to wait before device list is re-read */71#define WAIT_BETWEEN_CACHE_REFRESH_MILLIS 30007273/* maximum number of supported devices, playback+capture */74#define MAX_DS_DEVICES 607576typedef struct {77INT32 mixerIndex;78BOOL isSource;79/* either LPDIRECTSOUND or LPDIRECTSOUNDCAPTURE */80void* dev;81/* how many instances use the dev */82INT32 refCount;83GUID guid;84} DS_AudioDeviceCache;8586static DS_AudioDeviceCache g_audioDeviceCache[MAX_DS_DEVICES];87static INT32 g_cacheCount = 0;88static UINT64 g_lastCacheRefreshTime = 0;89static INT32 g_mixerCount = 0;9091BOOL DS_lockCache() {92/* dummy implementation for now, Java does locking */93return TRUE;94}9596void DS_unlockCache() {97/* dummy implementation for now */98}99100static GUID CLSID_DAUDIO_Zero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};101102BOOL isEqualGUID(LPGUID lpGuid1, LPGUID lpGuid2) {103if (lpGuid1 == NULL || lpGuid2 == NULL) {104if (lpGuid1 == lpGuid2) {105return TRUE;106}107if (lpGuid1 == NULL) {108lpGuid1 = (LPGUID) (&CLSID_DAUDIO_Zero);109} else {110lpGuid2 = (LPGUID) (&CLSID_DAUDIO_Zero);111}112}113return memcmp(lpGuid1, lpGuid2, sizeof(GUID)) == 0;114}115116INT32 findCacheItemByGUID(LPGUID lpGuid, BOOL isSource) {117int i;118for (i = 0; i < g_cacheCount; i++) {119if (isSource == g_audioDeviceCache[i].isSource120&& isEqualGUID(lpGuid, &(g_audioDeviceCache[i].guid))) {121return i;122}123}124return -1;125}126127INT32 findCacheItemByMixerIndex(INT32 mixerIndex) {128int i;129for (i = 0; i < g_cacheCount; i++) {130if (g_audioDeviceCache[i].mixerIndex == mixerIndex) {131return i;132}133}134return -1;135}136137typedef struct {138INT32 currMixerIndex;139BOOL isSource;140} DS_RefreshCacheStruct;141142143BOOL CALLBACK DS_RefreshCacheEnum(LPGUID lpGuid,144LPCSTR lpstrDescription,145LPCSTR lpstrModule,146DS_RefreshCacheStruct* rs) {147INT32 cacheIndex = findCacheItemByGUID(lpGuid, rs->isSource);148/*TRACE3("Enumerating %d: %s (%s)\n", cacheIndex, lpstrDescription, lpstrModule);*/149if (cacheIndex == -1) {150/* add this device */151if (g_cacheCount < MAX_DS_DEVICES-1) {152g_audioDeviceCache[g_cacheCount].mixerIndex = rs->currMixerIndex;153g_audioDeviceCache[g_cacheCount].isSource = rs->isSource;154g_audioDeviceCache[g_cacheCount].dev = NULL;155g_audioDeviceCache[g_cacheCount].refCount = 0;156if (lpGuid == NULL) {157memset(&(g_audioDeviceCache[g_cacheCount].guid), 0, sizeof(GUID));158} else {159memcpy(&(g_audioDeviceCache[g_cacheCount].guid), lpGuid, sizeof(GUID));160}161g_cacheCount++;162rs->currMixerIndex++;163} else {164/* failure case: more than MAX_DS_DEVICES available... */165}166} else {167/* device already exists in cache... update mixer number */168g_audioDeviceCache[cacheIndex].mixerIndex = rs->currMixerIndex;169rs->currMixerIndex++;170}171/* continue enumeration */172return TRUE;173}174175///// implemented functions of DirectAudio.h176177INT32 DAUDIO_GetDirectAudioDeviceCount() {178DS_RefreshCacheStruct rs;179INT32 oldCount;180INT32 cacheIndex;181182if (!DS_lockCache()) {183return 0;184}185186if (g_lastCacheRefreshTime == 0187|| (UINT64) timeGetTime() > (UINT64) (g_lastCacheRefreshTime + WAIT_BETWEEN_CACHE_REFRESH_MILLIS)) {188/* first, initialize any old cache items */189for (cacheIndex = 0; cacheIndex < g_cacheCount; cacheIndex++) {190g_audioDeviceCache[cacheIndex].mixerIndex = -1;191}192193/* enumerate all devices and either add them to the device cache,194* or refresh the mixer number195*/196rs.currMixerIndex = 0;197rs.isSource = TRUE;198DirectSoundEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);199/* if we only got the Primary Sound Driver (GUID=NULL),200* then there aren't any playback devices installed */201if (rs.currMixerIndex == 1) {202cacheIndex = findCacheItemByGUID(NULL, TRUE);203if (cacheIndex == 0) {204rs.currMixerIndex = 0;205g_audioDeviceCache[0].mixerIndex = -1;206TRACE0("Removing stale Primary Sound Driver from list.\n");207}208}209oldCount = rs.currMixerIndex;210rs.isSource = FALSE;211DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);212/* if we only got the Primary Sound Capture Driver (GUID=NULL),213* then there aren't any capture devices installed */214if ((rs.currMixerIndex - oldCount) == 1) {215cacheIndex = findCacheItemByGUID(NULL, FALSE);216if (cacheIndex != -1) {217rs.currMixerIndex = oldCount;218g_audioDeviceCache[cacheIndex].mixerIndex = -1;219TRACE0("Removing stale Primary Sound Capture Driver from list.\n");220}221}222g_mixerCount = rs.currMixerIndex;223224g_lastCacheRefreshTime = (UINT64) timeGetTime();225}226DS_unlockCache();227/*TRACE1("DirectSound: %d installed devices\n", g_mixerCount);*/228return g_mixerCount;229}230231BOOL CALLBACK DS_GetDescEnum(LPGUID lpGuid,232LPCWSTR lpstrDescription,233LPCWSTR lpstrModule,234DirectAudioDeviceDescription* desc) {235236INT32 cacheIndex = findCacheItemByGUID(lpGuid, g_audioDeviceCache[desc->deviceID].isSource);237if (cacheIndex == desc->deviceID) {238UnicodeToUTF8AndCopy(desc->name, lpstrDescription, DAUDIO_STRING_LENGTH);239//strncpy(desc->description, lpstrModule, DAUDIO_STRING_LENGTH);240desc->maxSimulLines = -1;241/* do not continue enumeration */242return FALSE;243}244return TRUE;245}246247248INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* desc) {249250if (!DS_lockCache()) {251return FALSE;252}253254/* set the deviceID field to the cache index */255desc->deviceID = findCacheItemByMixerIndex(mixerIndex);256if (desc->deviceID < 0) {257DS_unlockCache();258return FALSE;259}260desc->maxSimulLines = 0;261if (g_audioDeviceCache[desc->deviceID].isSource) {262DirectSoundEnumerateW((LPDSENUMCALLBACKW) DS_GetDescEnum, desc);263strncpy(desc->description, "DirectSound Playback", DAUDIO_STRING_LENGTH);264} else {265DirectSoundCaptureEnumerateW((LPDSENUMCALLBACKW) DS_GetDescEnum, desc);266strncpy(desc->description, "DirectSound Capture", DAUDIO_STRING_LENGTH);267}268269/*desc->vendor;270desc->version;*/271272DS_unlockCache();273return (desc->maxSimulLines == -1)?TRUE:FALSE;274}275276/* multi-channel info: http://www.microsoft.com/whdc/hwdev/tech/audio/multichaud.mspx */277278//static UINT32 sampleRateArray[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 56000, 88000, 96000, 172000, 192000 };279static INT32 sampleRateArray[] = { -1 };280static INT32 channelsArray[] = { 1, 2};281static INT32 bitsArray[] = { 8, 16};282283#define SAMPLERATE_COUNT sizeof(sampleRateArray)/sizeof(INT32)284#define CHANNELS_COUNT sizeof(channelsArray)/sizeof(INT32)285#define BITS_COUNT sizeof(bitsArray)/sizeof(INT32)286287void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {288289int rateIndex, channelIndex, bitIndex;290291/* no need to lock, since deviceID identifies the device sufficiently */292293/* sanity */294if (deviceID >= g_cacheCount) {295return;296}297if ((g_audioDeviceCache[deviceID].isSource && !isSource)298|| (!g_audioDeviceCache[deviceID].isSource && isSource)) {299/* only support Playback or Capture */300return;301}302303for (rateIndex = 0; rateIndex < SAMPLERATE_COUNT; rateIndex++) {304for (channelIndex = 0; channelIndex < CHANNELS_COUNT; channelIndex++) {305for (bitIndex = 0; bitIndex < BITS_COUNT; bitIndex++) {306DAUDIO_AddAudioFormat(creator, bitsArray[bitIndex],307((bitsArray[bitIndex] + 7) / 8) * channelsArray[channelIndex],308channelsArray[channelIndex],309(float) sampleRateArray[rateIndex],310DAUDIO_PCM,311(bitsArray[bitIndex]==8)?FALSE:TRUE, /* signed */312(bitsArray[bitIndex]==8)?FALSE:313#ifndef _LITTLE_ENDIAN314TRUE /* big endian */315#else316FALSE /* little endian */317#endif318);319}320}321}322}323324typedef struct {325int deviceID;326/* for convenience */327BOOL isSource;328/* the secondary buffer (Playback) */329LPDIRECTSOUNDBUFFER playBuffer;330/* the secondary buffer (Capture) */331LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;332333/* size of the directsound buffer, usually 2 seconds */334int dsBufferSizeInBytes;335336/* size of the read/write-ahead, as specified by Java */337int bufferSizeInBytes;338int bitsPerSample;339int frameSize; // storage size in Bytes340341UINT64 framePos;342/* where to write into the buffer.343* -1 if at current position (Playback)344* For Capture, this is the read position345*/346int writePos;347348/* if start() had been called */349BOOL started;350351/* how many bytes there is silence from current write position */352int silencedBytes;353354BOOL underrun;355356} DS_Info;357358359LPCSTR TranslateDSError(HRESULT hr) {360switch(hr) {361case DSERR_ALLOCATED:362return "DSERR_ALLOCATED";363364case DSERR_CONTROLUNAVAIL:365return "DSERR_CONTROLUNAVAIL";366367case DSERR_INVALIDPARAM:368return "DSERR_INVALIDPARAM";369370case DSERR_INVALIDCALL:371return "DSERR_INVALIDCALL";372373case DSERR_GENERIC:374return "DSERR_GENERIC";375376case DSERR_PRIOLEVELNEEDED:377return "DSERR_PRIOLEVELNEEDED";378379case DSERR_OUTOFMEMORY:380return "DSERR_OUTOFMEMORY";381382case DSERR_BADFORMAT:383return "DSERR_BADFORMAT";384385case DSERR_UNSUPPORTED:386return "DSERR_UNSUPPORTED";387388case DSERR_NODRIVER:389return "DSERR_NODRIVER";390391case DSERR_ALREADYINITIALIZED:392return "DSERR_ALREADYINITIALIZED";393394case DSERR_NOAGGREGATION:395return "DSERR_NOAGGREGATION";396397case DSERR_BUFFERLOST:398return "DSERR_BUFFERLOST";399400case DSERR_OTHERAPPHASPRIO:401return "DSERR_OTHERAPPHASPRIO";402403case DSERR_UNINITIALIZED:404return "DSERR_UNINITIALIZED";405406default:407return "Unknown HRESULT";408}409}410411/*412** data/routines for starting DS buffers by separate thread413** (joint into DS_StartBufferHelper class)414** see cr6372428: playback fails after exiting from thread that has started it415** due IDirectSoundBuffer8::Play() description:416** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c417** /directx/htm/idirectsoundbuffer8play.asp418** (remark section): If the application is multithreaded, the thread that plays419** the buffer must continue to exist as long as the buffer is playing.420** Buffers created on WDM drivers stop playing when the thread is terminated.421** IDirectSoundCaptureBuffer8::Start() has the same remark:422** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c423** /directx/htm/idirectsoundcapturebuffer8start.asp424*/425class DS_StartBufferHelper {426public:427/* starts DirectSound buffer (playback or capture) */428static HRESULT StartBuffer(DS_Info* info);429/* checks for initialization success */430static inline BOOL isInitialized() { return data.threadHandle != NULL; }431protected:432DS_StartBufferHelper() {} // no need to create an instance433434/* data class */435class Data {436public:437Data();438~Data();439// public data to access from parent class440CRITICAL_SECTION crit_sect;441volatile HANDLE threadHandle;442volatile HANDLE startEvent;443volatile HANDLE startedEvent;444volatile DS_Info* line2Start;445volatile HRESULT startResult;446} static data;447448/* StartThread function */449static DWORD WINAPI __stdcall ThreadProc(void *param);450};451452/* StartBufferHelper class implementation453*/454DS_StartBufferHelper::Data DS_StartBufferHelper::data;455456DS_StartBufferHelper::Data::Data() {457threadHandle = NULL;458::InitializeCriticalSection(&crit_sect);459startEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);460startedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);461if (startEvent != NULL && startedEvent != NULL)462threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);463}464465DS_StartBufferHelper::Data::~Data() {466::EnterCriticalSection(&crit_sect);467if (threadHandle != NULL) {468// terminate thread469line2Start = NULL;470::SetEvent(startEvent);471::CloseHandle(threadHandle);472threadHandle = NULL;473}474::LeaveCriticalSection(&crit_sect);475// won't delete startEvent/startedEvent/crit_sect476// - Windows will do during process shutdown477}478479DWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)480{481::CoInitialize(NULL);482while (1) {483// wait for something to do484::WaitForSingleObject(data.startEvent, INFINITE);485if (data.line2Start == NULL) {486// (data.line2Start == NULL) is a signal to terminate thread487break;488}489if (data.line2Start->isSource) {490data.startResult =491data.line2Start->playBuffer->Play(0, 0, DSBPLAY_LOOPING);492} else {493data.startResult =494data.line2Start->captureBuffer->Start(DSCBSTART_LOOPING);495}496::SetEvent(data.startedEvent);497}498::CoUninitialize();499return 0;500}501502HRESULT DS_StartBufferHelper::StartBuffer(DS_Info* info) {503HRESULT hr;504::EnterCriticalSection(&data.crit_sect);505if (!isInitialized()) {506::LeaveCriticalSection(&data.crit_sect);507return E_FAIL;508}509data.line2Start = info;510::SetEvent(data.startEvent);511::WaitForSingleObject(data.startedEvent, INFINITE);512hr = data.startResult;513::LeaveCriticalSection(&data.crit_sect);514return hr;515}516517518/* helper routines for DS buffer positions */519/* returns distance from pos1 to pos2520*/521inline int DS_getDistance(DS_Info* info, int pos1, int pos2) {522int distance = pos2 - pos1;523while (distance < 0)524distance += info->dsBufferSizeInBytes;525return distance;526}527528/* adds 2 positions529*/530inline int DS_addPos(DS_Info* info, int pos1, int pos2) {531int result = pos1 + pos2;532while (result >= info->dsBufferSizeInBytes)533result -= info->dsBufferSizeInBytes;534return result;535}536537538BOOL DS_addDeviceRef(INT32 deviceID) {539HWND ownerWindow;540HRESULT res = DS_OK;541LPDIRECTSOUND devPlay;542LPDIRECTSOUNDCAPTURE devCapture;543LPGUID lpGuid = NULL;544545546if (g_audioDeviceCache[deviceID].dev == NULL) {547/* Create DirectSound */548TRACE1("Creating DirectSound object for device %d\n", deviceID);549lpGuid = &(g_audioDeviceCache[deviceID].guid);550if (isEqualGUID(lpGuid, NULL)) {551lpGuid = NULL;552}553if (g_audioDeviceCache[deviceID].isSource) {554res = DirectSoundCreate(lpGuid, &devPlay, NULL);555g_audioDeviceCache[deviceID].dev = (void*) devPlay;556} else {557res = DirectSoundCaptureCreate(lpGuid, &devCapture, NULL);558g_audioDeviceCache[deviceID].dev = (void*) devCapture;559}560g_audioDeviceCache[deviceID].refCount = 0;561if (FAILED(res)) {562ERROR1("DAUDIO_Open: ERROR: Failed to create DirectSound: %s", TranslateDSError(res));563g_audioDeviceCache[deviceID].dev = NULL;564return FALSE;565}566if (g_audioDeviceCache[deviceID].isSource) {567ownerWindow = GetForegroundWindow();568if (ownerWindow == NULL) {569ownerWindow = GetDesktopWindow();570}571TRACE0("DAUDIO_Open: Setting cooperative level\n");572res = devPlay->SetCooperativeLevel(ownerWindow, DSSCL_NORMAL);573if (FAILED(res)) {574ERROR1("DAUDIO_Open: ERROR: Failed to set cooperative level: %s", TranslateDSError(res));575return FALSE;576}577}578}579g_audioDeviceCache[deviceID].refCount++;580return TRUE;581}582583#define DEV_PLAY(devID) ((LPDIRECTSOUND) g_audioDeviceCache[devID].dev)584#define DEV_CAPTURE(devID) ((LPDIRECTSOUNDCAPTURE) g_audioDeviceCache[devID].dev)585586void DS_removeDeviceRef(INT32 deviceID) {587588if (g_audioDeviceCache[deviceID].refCount) {589g_audioDeviceCache[deviceID].refCount--;590}591if (g_audioDeviceCache[deviceID].refCount == 0) {592if (g_audioDeviceCache[deviceID].dev != NULL) {593if (g_audioDeviceCache[deviceID].isSource) {594DEV_PLAY(deviceID)->Release();595} else {596DEV_CAPTURE(deviceID)->Release();597}598g_audioDeviceCache[deviceID].dev = NULL;599}600}601}602603#ifndef _WAVEFORMATEXTENSIBLE_604#define _WAVEFORMATEXTENSIBLE_605typedef struct {606WAVEFORMATEX Format;607union {608WORD wValidBitsPerSample; /* bits of precision */609WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */610WORD wReserved; /* If neither applies, set to zero. */611} Samples;612DWORD dwChannelMask; /* which channels are */613/* present in stream */614GUID SubFormat;615} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;616#endif // !_WAVEFORMATEXTENSIBLE_617618#if !defined(WAVE_FORMAT_EXTENSIBLE)619#define WAVE_FORMAT_EXTENSIBLE 0xFFFE620#endif // !defined(WAVE_FORMAT_EXTENSIBLE)621622#if !defined(DEFINE_WAVEFORMATEX_GUID)623#define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71624#endif625#ifndef STATIC_KSDATAFORMAT_SUBTYPE_PCM626#define STATIC_KSDATAFORMAT_SUBTYPE_PCM\627DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)628#endif629630631void createWaveFormat(WAVEFORMATEXTENSIBLE* format,632int sampleRate,633int channels,634int bits,635int significantBits) {636GUID subtypePCM = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};637format->Format.nSamplesPerSec = (DWORD)sampleRate;638format->Format.nChannels = (WORD) channels;639/* do not support useless padding, like 24-bit samples stored in 32-bit containers */640format->Format.wBitsPerSample = (WORD) ((bits + 7) & 0xFFF8);641642if (channels <= 2 && bits <= 16) {643format->Format.wFormatTag = WAVE_FORMAT_PCM;644format->Format.cbSize = 0;645} else {646format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;647format->Format.cbSize = 22;648format->Samples.wValidBitsPerSample = bits;649/* no way to specify speaker locations */650format->dwChannelMask = 0xFFFFFFFF;651format->SubFormat = subtypePCM;652}653format->Format.nBlockAlign = (WORD)((format->Format.wBitsPerSample * format->Format.nChannels) / 8);654format->Format.nAvgBytesPerSec = format->Format.nSamplesPerSec * format->Format.nBlockAlign;655}656657/* fill buffer with silence658*/659void DS_clearBuffer(DS_Info* info, BOOL fromWritePos) {660UBYTE* pb1=NULL, *pb2=NULL;661DWORD cb1=0, cb2=0;662DWORD flags = 0;663int start, count;664TRACE1("> DS_clearBuffer for device %d\n", info->deviceID);665if (info->isSource) {666if (fromWritePos) {667DWORD playCursor, writeCursor;668int end;669if (FAILED(info->playBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {670ERROR0(" DS_clearBuffer: ERROR: Failed to get current position.");671TRACE0("< DS_clearbuffer\n");672return;673}674DEBUG_SILENCING2(" DS_clearBuffer: DS playPos=%d myWritePos=%d", (int) playCursor, (int) info->writePos);675if (info->writePos >= 0) {676start = info->writePos + info->silencedBytes;677} else {678start = writeCursor + info->silencedBytes;679//flags |= DSBLOCK_FROMWRITECURSOR;680}681while (start >= info->dsBufferSizeInBytes) {682start -= info->dsBufferSizeInBytes;683}684685// fix for bug 6251460 (REGRESSION: short sounds do not play)686// for unknown reason with hardware DS buffer playCursor sometimes687// jumps back for little interval (mostly 2-8 bytes) (writeCursor moves forward as usual)688// The issue happens right after start playing and for short sounds only (less then DS buffer,689// when whole sound written into the buffer and remaining space filled by silence)690// the case doesn't produce any audible aftifacts so just catch it to prevent filling691// whole buffer by silence.692if (((int)playCursor <= start && start < (int)writeCursor)693|| (writeCursor < playCursor // buffer bound is between playCursor & writeCursor694&& (start < (int)writeCursor || (int)playCursor <= start))) {695return;696}697698count = info->dsBufferSizeInBytes - info->silencedBytes;699// why / 4?700//if (count > info->dsBufferSizeInBytes / 4) {701// count = info->dsBufferSizeInBytes / 4;702//}703end = start + count;704if ((int) playCursor < start) {705playCursor += (DWORD) info->dsBufferSizeInBytes;706}707if (start <= (int) playCursor && end > (int) playCursor) {708/* at maximum, silence until play cursor */709count = (int) playCursor - start;710#ifdef USE_TRACE711if ((int) playCursor >= info->dsBufferSizeInBytes) playCursor -= (DWORD) info->dsBufferSizeInBytes;712TRACE3("\n DS_clearBuffer: Start Writing from %d, "713"would overwrite playCursor=%d, so reduce count to %d\n",714start, playCursor, count);715#endif716}717DEBUG_SILENCING2(" clearing buffer from %d, count=%d. ", (int)start, (int) count);718if (count <= 0) {719DEBUG_SILENCING0("\n");720TRACE1("< DS_clearBuffer: no need to clear, silencedBytes=%d\n", info->silencedBytes);721return;722}723} else {724start = 0;725count = info->dsBufferSizeInBytes;726flags |= DSBLOCK_ENTIREBUFFER;727}728if (FAILED(info->playBuffer->Lock(start,729count,730(LPVOID*) &pb1, &cb1,731(LPVOID*) &pb2, &cb2, flags))) {732ERROR0("\n DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");733TRACE0("< DS_clearbuffer\n");734return;735}736} else {737if (FAILED(info->captureBuffer->Lock(0,738info->dsBufferSizeInBytes,739(LPVOID*) &pb1, &cb1,740(LPVOID*) &pb2, &cb2, DSCBLOCK_ENTIREBUFFER))) {741ERROR0(" DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");742TRACE0("< DS_clearbuffer\n");743return;744}745}746if (pb1!=NULL) {747memset(pb1, (info->bitsPerSample == 8)?128:0, cb1);748}749if (pb2!=NULL) {750memset(pb2, (info->bitsPerSample == 8)?128:0, cb2);751}752if (info->isSource) {753info->playBuffer->Unlock( pb1, cb1, pb2, cb2 );754if (!fromWritePos) {755/* doesn't matter where to start writing next time */756info->writePos = -1;757info->silencedBytes = info->dsBufferSizeInBytes;758} else {759info->silencedBytes += (cb1+cb2);760if (info->silencedBytes > info->dsBufferSizeInBytes) {761ERROR1(" DS_clearbuffer: ERROR: silencedBytes=%d exceeds buffer size!\n",762info->silencedBytes);763info->silencedBytes = info->dsBufferSizeInBytes;764}765}766DEBUG_SILENCING2(" silencedBytes=%d, my writePos=%d\n", (int)info->silencedBytes, (int)info->writePos);767} else {768info->captureBuffer->Unlock( pb1, cb1, pb2, cb2 );769}770TRACE0("< DS_clearbuffer\n");771}772773/* returns pointer to buffer */774void* DS_createSoundBuffer(DS_Info* info,775float sampleRate,776int sampleSizeInBits,777int channels,778int bufferSizeInBytes) {779DSBUFFERDESC dsbdesc;780DSCBUFFERDESC dscbdesc;781HRESULT res;782WAVEFORMATEXTENSIBLE format;783void* buffer;784785TRACE1("Creating secondary buffer for device %d\n", info->deviceID);786createWaveFormat(&format,787(int) sampleRate,788channels,789info->frameSize / channels * 8,790sampleSizeInBits);791792/* 2 second secondary buffer */793info->dsBufferSizeInBytes = 2 * ((int) sampleRate) * info->frameSize;794795if (bufferSizeInBytes > info->dsBufferSizeInBytes / 2) {796bufferSizeInBytes = info->dsBufferSizeInBytes / 2;797}798bufferSizeInBytes = (bufferSizeInBytes / info->frameSize) * info->frameSize;799info->bufferSizeInBytes = bufferSizeInBytes;800801if (info->isSource) {802memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));803dsbdesc.dwSize = sizeof(DSBUFFERDESC);804dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2805| DSBCAPS_GLOBALFOCUS;806807dsbdesc.dwBufferBytes = info->dsBufferSizeInBytes;808dsbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;809res = DEV_PLAY(info->deviceID)->CreateSoundBuffer810(&dsbdesc, (LPDIRECTSOUNDBUFFER*) &buffer, NULL);811} else {812memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));813dscbdesc.dwSize = sizeof(DSCBUFFERDESC);814dscbdesc.dwFlags = 0;815dscbdesc.dwBufferBytes = info->dsBufferSizeInBytes;816dscbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;817res = DEV_CAPTURE(info->deviceID)->CreateCaptureBuffer818(&dscbdesc, (LPDIRECTSOUNDCAPTUREBUFFER*) &buffer, NULL);819}820if (FAILED(res)) {821ERROR1("DS_createSoundBuffer: ERROR: Failed to create sound buffer: %s", TranslateDSError(res));822return NULL;823}824return buffer;825}826827void DS_destroySoundBuffer(DS_Info* info) {828if (info->playBuffer != NULL) {829info->playBuffer->Release();830info->playBuffer = NULL;831}832if (info->captureBuffer != NULL) {833info->captureBuffer->Release();834info->captureBuffer = NULL;835}836}837838839void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,840int encoding, float sampleRate, int sampleSizeInBits,841int frameSize, int channels,842int isSigned, int isBigEndian, int bufferSizeInBytes) {843844DS_Info* info;845void* buffer;846847TRACE0("> DAUDIO_Open\n");848849/* some sanity checks */850if (deviceID >= g_cacheCount) {851ERROR1("DAUDIO_Open: ERROR: cannot open the device with deviceID=%d!\n", deviceID);852return NULL;853}854if ((g_audioDeviceCache[deviceID].isSource && !isSource)855|| (!g_audioDeviceCache[deviceID].isSource && isSource)) {856/* only support Playback or Capture */857ERROR0("DAUDIO_Open: ERROR: Cache is corrupt: cannot open the device in specified isSource mode!\n");858return NULL;859}860if (encoding != DAUDIO_PCM) {861ERROR1("DAUDIO_Open: ERROR: cannot open the device with encoding=%d!\n", encoding);862return NULL;863}864if (channels <= 0) {865ERROR1("DAUDIO_Open: ERROR: Invalid number of channels=%d!\n", channels);866return NULL;867}868if (sampleSizeInBits > 8 &&869#ifdef _LITTLE_ENDIAN870isBigEndian871#else872!isBigEndian873#endif874) {875ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian);876return NULL;877}878if (sampleSizeInBits == 8 && isSigned) {879ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n");880return NULL;881}882if (!DS_StartBufferHelper::isInitialized()) {883ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n");884return NULL;885}886887info = (DS_Info*) malloc(sizeof(DS_Info));888if (!info) {889ERROR0("DAUDIO_Open: ERROR: Out of memory\n");890return NULL;891}892memset(info, 0, sizeof(DS_Info));893894info->deviceID = deviceID;895info->isSource = isSource;896info->bitsPerSample = sampleSizeInBits;897info->frameSize = frameSize;898info->framePos = 0;899info->started = FALSE;900info->underrun = FALSE;901902if (!DS_addDeviceRef(deviceID)) {903DS_removeDeviceRef(deviceID);904free(info);905return NULL;906}907908buffer = DS_createSoundBuffer(info,909sampleRate,910sampleSizeInBits,911channels,912bufferSizeInBytes);913if (!buffer) {914DS_removeDeviceRef(deviceID);915free(info);916return NULL;917}918919if (info->isSource) {920info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer;921} else {922info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer;923}924DS_clearBuffer(info, FALSE /* entire buffer */);925926/* use writepos of device */927if (info->isSource) {928info->writePos = -1;929} else {930info->writePos = 0;931}932933TRACE0("< DAUDIO_Open: Opened device successfully.\n");934return (void*) info;935}936937int DAUDIO_Start(void* id, int isSource) {938DS_Info* info = (DS_Info*) id;939HRESULT res = DS_OK;940DWORD status;941942TRACE0("> DAUDIO_Start\n");943944if (info->isSource) {945res = info->playBuffer->GetStatus(&status);946if (res == DS_OK) {947if (status & DSBSTATUS_LOOPING) {948ERROR0("DAUDIO_Start: ERROR: Already started!");949return TRUE;950}951952/* only start buffer if already something written to it */953if (info->writePos >= 0) {954res = DS_StartBufferHelper::StartBuffer(info);955if (res == DSERR_BUFFERLOST) {956res = info->playBuffer->Restore();957if (res == DS_OK) {958DS_clearBuffer(info, FALSE /* entire buffer */);959/* write() will trigger actual device start */960}961} else {962/* make sure that we will have silence after963the currently valid audio data */964DS_clearBuffer(info, TRUE /* from write position */);965}966}967}968} else {969if (info->captureBuffer->GetStatus(&status) == DS_OK) {970if (status & DSCBSTATUS_LOOPING) {971ERROR0("DAUDIO_Start: ERROR: Already started!");972return TRUE;973}974}975res = DS_StartBufferHelper::StartBuffer(info);976}977if (FAILED(res)) {978ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res));979return FALSE;980}981info->started = TRUE;982return TRUE;983}984985int DAUDIO_Stop(void* id, int isSource) {986DS_Info* info = (DS_Info*) id;987988TRACE0("> DAUDIO_Stop\n");989990info->started = FALSE;991if (info->isSource) {992info->playBuffer->Stop();993} else {994info->captureBuffer->Stop();995}996997TRACE0("< DAUDIO_Stop\n");998return TRUE;999}100010011002void DAUDIO_Close(void* id, int isSource) {1003DS_Info* info = (DS_Info*) id;10041005TRACE0("DAUDIO_Close\n");10061007if (info != NULL) {1008DS_destroySoundBuffer(info);1009DS_removeDeviceRef(info->deviceID);1010free(info);1011}1012}10131014/* Check buffer for underrun1015* This method is only meaningful for Output devices (write devices).1016*/1017void DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) {1018TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, "1019"info->writePos=%d silencedBytes=%d dsBufferSizeInBytes=%d\n",1020(int) playCursor, (int) writeCursor, (int) info->writePos,1021(int) info->silencedBytes, (int) info->dsBufferSizeInBytes);1022if (info->underrun || info->writePos < 0) return;1023int writeAhead = DS_getDistance(info, writeCursor, info->writePos);1024if (writeAhead > info->bufferSizeInBytes) {1025// this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes)1026// But the case can occur only when we have more then info->bufferSizeInBytes valid bytes1027// (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes)1028// If we already have a lot of silencedBytes after valid data (written by1029// DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun1030if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) {1031// underrun!1032ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n");1033info->underrun = TRUE;1034}1035}1036}10371038/* For source (playback) line:1039* (a) if (fromPlayCursor == FALSE), returns number of bytes available1040* for writing: bufferSize - (info->writePos - writeCursor);1041* (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor1042* and returned value can be used for play position calculation (see also1043* note about bufferSize)1044* For destination (capture) line:1045* (c) if (fromPlayCursor == FALSE), returns number of bytes available1046* for reading from the buffer: readCursor - info->writePos;1047* (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor1048* and returned value can be used for capture position calculation (see1049* note about bufferSize)1050* bufferSize parameter are filled by "actual" buffer size:1051* if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes1052* otherwise it increase by number of bytes currently processed by DirectSound1053* (writeCursor - playCursor) or (captureCursor - readCursor)1054*/1055int DS_GetAvailable(DS_Info* info,1056DWORD* playCursor, DWORD* writeCursor,1057int* bufferSize, BOOL fromPlayCursor) {1058int available;1059int newReadPos;10601061TRACE2("DS_GetAvailable: fromPlayCursor=%d, deviceID=%d\n", fromPlayCursor, info->deviceID);1062if (!info->playBuffer && !info->captureBuffer) {1063ERROR0("DS_GetAvailable: ERROR: buffer not yet created");1064return 0;1065}10661067if (info->isSource) {1068if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) {1069ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");1070return 0;1071}1072int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor);1073// workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor1074if (processing > info->dsBufferSizeInBytes / 2) {1075*writeCursor = *playCursor;1076processing = 0;1077}1078TRACE3(" playCursor=%d, writeCursor=%d, info->writePos=%d\n",1079*playCursor, *writeCursor, info->writePos);1080*bufferSize = info->bufferSizeInBytes;1081if (fromPlayCursor) {1082*bufferSize += processing;1083}1084DS_CheckUnderrun(info, *playCursor, *writeCursor);1085if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) {1086/* always full buffer if at beginning */1087available = *bufferSize;1088} else {1089int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor, info->writePos);1090if (currWriteAhead > *bufferSize) {1091if (info->underrun) {1092// playCursor surpassed writePos - no valid data, whole buffer available1093available = *bufferSize;1094} else {1095// the case may occur after stop(), when writeCursor jumps back to playCursor1096// so "actual" buffer size has grown1097*bufferSize = currWriteAhead;1098available = 0;1099}1100} else {1101available = *bufferSize - currWriteAhead;1102}1103}1104} else {1105if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) {1106ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");1107return 0;1108}1109*bufferSize = info->bufferSizeInBytes;1110if (fromPlayCursor) {1111*bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor);1112}1113TRACE4(" captureCursor=%d, readCursor=%d, info->readPos=%d refBufferSize=%d\n",1114*playCursor, *writeCursor, info->writePos, *bufferSize);1115if (info->writePos == -1) {1116/* always empty buffer if at beginning */1117info->writePos = (int) (*writeCursor);1118}1119if (fromPlayCursor) {1120available = ((int) (*playCursor) - info->writePos);1121} else {1122available = ((int) (*writeCursor) - info->writePos);1123}1124if (available < 0) {1125available += info->dsBufferSizeInBytes;1126}1127if (!fromPlayCursor && available > info->bufferSizeInBytes) {1128/* overflow */1129ERROR2("DS_GetAvailable: ERROR: overflow detected: "1130"DirectSoundBufferSize=%d, bufferSize=%d, ",1131info->dsBufferSizeInBytes, info->bufferSizeInBytes);1132ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n",1133*playCursor, *writeCursor, info->writePos);1134/* advance read position, to allow exactly one buffer worth of data */1135newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes;1136if (newReadPos < 0) {1137newReadPos += info->dsBufferSizeInBytes;1138}1139info->writePos = newReadPos;1140available = info->bufferSizeInBytes;1141}1142}1143available = (available / info->frameSize) * info->frameSize;11441145TRACE1("DS_available: Returning %d available bytes\n", (int) available);1146return available;1147}11481149// returns -1 on error, otherwise bytes written1150int DAUDIO_Write(void* id, char* data, int byteSize) {1151DS_Info* info = (DS_Info*) id;1152int available;1153int thisWritePos;1154DWORD playCursor, writeCursor;1155HRESULT res;1156void* buffer1, *buffer2;1157DWORD buffer1len, buffer2len;1158BOOL needRestart = FALSE;1159int bufferLostTrials = 2;1160int bufferSize;11611162TRACE1("> DAUDIO_Write %d bytes\n", byteSize);11631164while (--bufferLostTrials > 0) {1165available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */);1166if (byteSize > available) byteSize = available;1167if (byteSize == 0) break;1168thisWritePos = info->writePos;1169if (thisWritePos == -1 || info->underrun) {1170// play from current write cursor after flush, etc.1171needRestart = TRUE;1172thisWritePos = writeCursor;1173info->underrun = FALSE;1174}1175DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize);1176res = info->playBuffer->Lock(thisWritePos, byteSize,1177(LPVOID *) &buffer1, &buffer1len,1178(LPVOID *) &buffer2, &buffer2len,11790);1180if (res != DS_OK) {1181/* some DS failure */1182if (res == DSERR_BUFFERLOST) {1183ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer.");1184if (info->playBuffer->Restore() == DS_OK) {1185DS_clearBuffer(info, FALSE /* entire buffer */);1186info->writePos = -1;1187/* try again */1188continue;1189}1190}1191/* can't recover from error */1192byteSize = 0;1193break;1194}1195/* buffer could be locked successfully */1196/* first fill first buffer */1197if (buffer1) {1198memcpy(buffer1, data, buffer1len);1199data = (char*) (((UINT_PTR) data) + buffer1len);1200} else buffer1len = 0;1201if (buffer2) {1202memcpy(buffer2, data, buffer2len);1203} else buffer2len = 0;1204byteSize = buffer1len + buffer2len;12051206/* update next write pos */1207thisWritePos += byteSize;1208while (thisWritePos >= info->dsBufferSizeInBytes) {1209thisWritePos -= info->dsBufferSizeInBytes;1210}1211/* commit data to directsound */1212info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);12131214info->writePos = thisWritePos;12151216/* update position1217* must be AFTER updating writePos,1218* so that getSvailable doesn't return too little,1219* so that getFramePos doesn't jump1220*/1221info->framePos += (byteSize / info->frameSize);12221223/* decrease silenced bytes */1224if (info->silencedBytes > byteSize) {1225info->silencedBytes -= byteSize;1226} else {1227info->silencedBytes = 0;1228}1229break;1230} /* while */12311232/* start the device, if necessary */1233if (info->started && needRestart && (info->writePos >= 0)) {1234DS_StartBufferHelper::StartBuffer(info);1235}12361237TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize);1238return byteSize;1239}12401241// returns -1 on error1242int DAUDIO_Read(void* id, char* data, int byteSize) {1243DS_Info* info = (DS_Info*) id;1244int available;1245int thisReadPos;1246DWORD captureCursor, readCursor;1247HRESULT res;1248void* buffer1, *buffer2;1249DWORD buffer1len, buffer2len;1250int bufferSize;12511252TRACE1("> DAUDIO_Read %d bytes\n", byteSize);12531254available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */);1255if (byteSize > available) byteSize = available;1256if (byteSize > 0) {1257thisReadPos = info->writePos;1258if (thisReadPos == -1) {1259/* from beginning */1260thisReadPos = 0;1261}1262res = info->captureBuffer->Lock(thisReadPos, byteSize,1263(LPVOID *) &buffer1, &buffer1len,1264(LPVOID *) &buffer2, &buffer2len,12650);1266if (res != DS_OK) {1267/* can't recover from error */1268byteSize = 0;1269} else {1270/* buffer could be locked successfully */1271/* first fill first buffer */1272if (buffer1) {1273memcpy(data, buffer1, buffer1len);1274data = (char*) (((UINT_PTR) data) + buffer1len);1275} else buffer1len = 0;1276if (buffer2) {1277memcpy(data, buffer2, buffer2len);1278} else buffer2len = 0;1279byteSize = buffer1len + buffer2len;12801281/* update next read pos */1282thisReadPos = DS_addPos(info, thisReadPos, byteSize);1283/* commit data to directsound */1284info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);12851286/* update position1287* must be BEFORE updating readPos,1288* so that getAvailable doesn't return too much,1289* so that getFramePos doesn't jump1290*/1291info->framePos += (byteSize / info->frameSize);12921293info->writePos = thisReadPos;1294}1295}12961297TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize);1298return byteSize;1299}130013011302int DAUDIO_GetBufferSize(void* id, int isSource) {1303DS_Info* info = (DS_Info*) id;1304return info->bufferSizeInBytes;1305}13061307int DAUDIO_StillDraining(void* id, int isSource) {1308DS_Info* info = (DS_Info*) id;1309BOOL draining = FALSE;1310int available, bufferSize;1311DWORD playCursor, writeCursor;13121313DS_clearBuffer(info, TRUE /* from write position */);1314available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */);1315draining = (available < bufferSize);13161317TRACE3("DAUDIO_StillDraining: available=%d silencedBytes=%d Still draining: %s\n",1318available, info->silencedBytes, draining?"TRUE":"FALSE");1319return draining;1320}132113221323int DAUDIO_Flush(void* id, int isSource) {1324DS_Info* info = (DS_Info*) id;13251326TRACE0("DAUDIO_Flush\n");13271328if (info->isSource) {1329info->playBuffer->Stop();1330DS_clearBuffer(info, FALSE /* entire buffer */);1331} else {1332DWORD captureCursor, readCursor;1333/* set the read pointer to the current read position */1334if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) {1335ERROR0("DAUDIO_Flush: ERROR: Failed to get current position.");1336return FALSE;1337}1338DS_clearBuffer(info, FALSE /* entire buffer */);1339/* SHOULD set to *captureCursor*,1340* but that would be detected as overflow1341* in a subsequent GetAvailable() call.1342*/1343info->writePos = (int) readCursor;1344}1345return TRUE;1346}13471348int DAUDIO_GetAvailable(void* id, int isSource) {1349DS_Info* info = (DS_Info*) id;1350DWORD playCursor, writeCursor;1351int ret, bufferSize;13521353ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE);13541355TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);1356return ret;1357}13581359INT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) {1360// estimate the current position with the buffer size and1361// the available bytes to read or write in the buffer.1362// not an elegant solution - bytePos will stop on xruns,1363// and in race conditions it may jump backwards1364// Advantage is that it is indeed based on the samples that go through1365// the system (rather than time-based methods)1366if (info->isSource) {1367// javaBytePos is the position that is reached when the current1368// buffer is played completely1369return (INT64) (javaBytePos - bufferSize + availInBytes);1370} else {1371// javaBytePos is the position that was when the current buffer was empty1372return (INT64) (javaBytePos + availInBytes);1373}1374}13751376INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {1377DS_Info* info = (DS_Info*) id;1378int available, bufferSize;1379DWORD playCursor, writeCursor;1380INT64 result = javaBytePos;13811382available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE);1383result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available);1384return result;1385}138613871388void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {1389/* save to ignore, since GetBytePosition1390* takes the javaBytePos param into account1391*/1392}13931394int DAUDIO_RequiresServicing(void* id, int isSource) {1395// need servicing on for SourceDataLines1396return isSource?TRUE:FALSE;1397}13981399void DAUDIO_Service(void* id, int isSource) {1400DS_Info* info = (DS_Info*) id;1401if (isSource) {1402if (info->silencedBytes < info->dsBufferSizeInBytes) {1403// clear buffer1404TRACE0("DAUDIO_Service\n");1405DS_clearBuffer(info, TRUE /* from write position */);1406}1407if (info->writePos >= 01408&& info->started1409&& !info->underrun1410&& info->silencedBytes >= info->dsBufferSizeInBytes) {1411// if we're currently playing, and the entire buffer is silenced...1412// then we are underrunning!1413info->underrun = TRUE;1414ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n");1415}1416}1417}141814191420#endif // USE_DAUDIO142114221423