1 /*
   2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #define USE_ERROR
  27 #define USE_TRACE
  28 
  29 /* define this for the silencing/servicing code. Requires USE_TRACE */
  30 //#define USE_DEBUG_SILENCING
  31 
  32 #ifndef WIN32_EXTRA_LEAN
  33 #define WIN32_EXTRA_LEAN
  34 #endif
  35 #ifndef WIN32_LEAN_AND_MEAN
  36 #define WIN32_LEAN_AND_MEAN
  37 #endif
  38 
  39 #include <windows.h>
  40 #include <mmsystem.h>
  41 #include <string.h>
  42 
  43 /* include DirectSound headers */
  44 #include <dsound.h>
  45 
  46 /* include Java Sound specific headers as C code */
  47 #ifdef __cplusplus
  48 extern "C" {
  49 #endif
  50  #include "DirectAudio.h"
  51 #ifdef __cplusplus
  52 }
  53 #endif
  54 
  55 /* include to prevent charset problem */
  56 #include "PLATFORM_API_WinOS_Charset_Util.h"
  57 
  58 #ifdef USE_DEBUG_SILENCING
  59 #define DEBUG_SILENCING0(p) TRACE0(p)
  60 #define DEBUG_SILENCING1(p1,p2) TRACE1(p1,p2)
  61 #define DEBUG_SILENCING2(p1,p2,p3) TRACE2(p1,p2,p3)
  62 #else
  63 #define DEBUG_SILENCING0(p)
  64 #define DEBUG_SILENCING1(p1,p2)
  65 #define DEBUG_SILENCING2(p1,p2,p3)
  66 #endif
  67 
  68 
  69 #if USE_DAUDIO == TRUE
  70 
  71 /* half a minute to wait before device list is re-read */
  72 #define WAIT_BETWEEN_CACHE_REFRESH_MILLIS 30000
  73 
  74 /* maximum number of supported devices, playback+capture */
  75 #define MAX_DS_DEVICES 60
  76 
  77 typedef struct {
  78     INT32 mixerIndex;
  79     BOOL isSource;
  80     /* either LPDIRECTSOUND or LPDIRECTSOUNDCAPTURE */
  81     void* dev;
  82     /* how many instances use the dev */
  83     INT32 refCount;
  84     GUID guid;
  85 } DS_AudioDeviceCache;
  86 
  87 static DS_AudioDeviceCache g_audioDeviceCache[MAX_DS_DEVICES];
  88 static INT32 g_cacheCount = 0;
  89 static UINT64 g_lastCacheRefreshTime = 0;
  90 static INT32 g_mixerCount = 0;
  91 
  92 BOOL DS_lockCache() {
  93     /* dummy implementation for now, Java does locking */
  94     return TRUE;
  95 }
  96 
  97 void DS_unlockCache() {
  98     /* dummy implementation for now */
  99 }
 100 
 101 static GUID CLSID_DAUDIO_Zero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 102 
 103 BOOL isEqualGUID(LPGUID lpGuid1, LPGUID lpGuid2) {
 104     if (lpGuid1 == NULL || lpGuid2 == NULL) {
 105         if (lpGuid1 == lpGuid2) {
 106             return TRUE;
 107         }
 108         if (lpGuid1 == NULL) {
 109             lpGuid1 = (LPGUID) (&CLSID_DAUDIO_Zero);
 110         } else {
 111             lpGuid2 = (LPGUID) (&CLSID_DAUDIO_Zero);
 112         }
 113     }
 114     return memcmp(lpGuid1, lpGuid2, sizeof(GUID)) == 0;
 115 }
 116 
 117 INT32 findCacheItemByGUID(LPGUID lpGuid, BOOL isSource) {
 118     int i;
 119     for (i = 0; i < g_cacheCount; i++) {
 120         if (isSource == g_audioDeviceCache[i].isSource
 121             && isEqualGUID(lpGuid, &(g_audioDeviceCache[i].guid))) {
 122             return i;
 123         }
 124     }
 125     return -1;
 126 }
 127 
 128 INT32 findCacheItemByMixerIndex(INT32 mixerIndex) {
 129     int i;
 130     for (i = 0; i < g_cacheCount; i++) {
 131         if (g_audioDeviceCache[i].mixerIndex == mixerIndex) {
 132             return i;
 133         }
 134     }
 135     return -1;
 136 }
 137 
 138 typedef struct {
 139     INT32 currMixerIndex;
 140     BOOL isSource;
 141 } DS_RefreshCacheStruct;
 142 
 143 
 144 BOOL CALLBACK DS_RefreshCacheEnum(LPGUID lpGuid,
 145                                   LPCSTR lpstrDescription,
 146                                   LPCSTR lpstrModule,
 147                                   DS_RefreshCacheStruct* rs) {
 148     INT32 cacheIndex = findCacheItemByGUID(lpGuid, rs->isSource);
 149     /*TRACE3("Enumerating %d: %s (%s)\n", cacheIndex, lpstrDescription, lpstrModule);*/
 150     if (cacheIndex == -1) {
 151         /* add this device */
 152         if (g_cacheCount < MAX_DS_DEVICES-1) {
 153             g_audioDeviceCache[g_cacheCount].mixerIndex = rs->currMixerIndex;
 154             g_audioDeviceCache[g_cacheCount].isSource = rs->isSource;
 155             g_audioDeviceCache[g_cacheCount].dev = NULL;
 156             g_audioDeviceCache[g_cacheCount].refCount = 0;
 157             if (lpGuid == NULL) {
 158                 memset(&(g_audioDeviceCache[g_cacheCount].guid), 0, sizeof(GUID));
 159             } else {
 160                 memcpy(&(g_audioDeviceCache[g_cacheCount].guid), lpGuid, sizeof(GUID));
 161             }
 162             g_cacheCount++;
 163             rs->currMixerIndex++;
 164         } else {
 165             /* failure case: more than MAX_DS_DEVICES available... */
 166         }
 167     } else {
 168         /* device already exists in cache... update mixer number */
 169         g_audioDeviceCache[cacheIndex].mixerIndex = rs->currMixerIndex;
 170         rs->currMixerIndex++;
 171     }
 172     /* continue enumeration */
 173     return TRUE;
 174 }
 175 
 176 ///// implemented functions of DirectAudio.h
 177 
 178 INT32 DAUDIO_GetDirectAudioDeviceCount() {
 179     DS_RefreshCacheStruct rs;
 180     INT32 oldCount;
 181     INT32 cacheIndex;
 182 
 183     if (!DS_lockCache()) {
 184         return 0;
 185     }
 186 
 187     if (g_lastCacheRefreshTime == 0
 188         || (UINT64) timeGetTime() > (UINT64) (g_lastCacheRefreshTime + WAIT_BETWEEN_CACHE_REFRESH_MILLIS)) {
 189         /* first, initialize any old cache items */
 190         for (cacheIndex = 0; cacheIndex < g_cacheCount; cacheIndex++) {
 191             g_audioDeviceCache[cacheIndex].mixerIndex = -1;
 192         }
 193 
 194         /* enumerate all devices and either add them to the device cache,
 195          * or refresh the mixer number
 196          */
 197         rs.currMixerIndex = 0;
 198         rs.isSource = TRUE;
 199         DirectSoundEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
 200         /* if we only got the Primary Sound Driver (GUID=NULL),
 201          * then there aren't any playback devices installed */
 202         if (rs.currMixerIndex == 1) {
 203             cacheIndex = findCacheItemByGUID(NULL, TRUE);
 204             if (cacheIndex == 0) {
 205                 rs.currMixerIndex = 0;
 206                 g_audioDeviceCache[0].mixerIndex = -1;
 207                 TRACE0("Removing stale Primary Sound Driver from list.\n");
 208             }
 209         }
 210         oldCount = rs.currMixerIndex;
 211         rs.isSource = FALSE;
 212         DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) DS_RefreshCacheEnum, &rs);
 213         /* if we only got the Primary Sound Capture Driver (GUID=NULL),
 214          * then there aren't any capture devices installed */
 215         if ((rs.currMixerIndex - oldCount) == 1) {
 216             cacheIndex = findCacheItemByGUID(NULL, FALSE);
 217             if (cacheIndex != -1) {
 218                 rs.currMixerIndex = oldCount;
 219                 g_audioDeviceCache[cacheIndex].mixerIndex = -1;
 220                 TRACE0("Removing stale Primary Sound Capture Driver from list.\n");
 221             }
 222         }
 223         g_mixerCount = rs.currMixerIndex;
 224 
 225         g_lastCacheRefreshTime = (UINT64) timeGetTime();
 226     }
 227     DS_unlockCache();
 228     /*TRACE1("DirectSound: %d installed devices\n", g_mixerCount);*/
 229     return g_mixerCount;
 230 }
 231 
 232 BOOL CALLBACK DS_GetDescEnum(LPGUID lpGuid,
 233                              LPCWSTR lpstrDescription,
 234                              LPCWSTR lpstrModule,
 235                              DirectAudioDeviceDescription* desc) {
 236 
 237     INT32 cacheIndex = findCacheItemByGUID(lpGuid, g_audioDeviceCache[desc->deviceID].isSource);
 238     if (cacheIndex == desc->deviceID) {
 239         UnicodeToUTF8AndCopy(desc->name, lpstrDescription, DAUDIO_STRING_LENGTH);
 240         //strncpy(desc->description, lpstrModule, DAUDIO_STRING_LENGTH);
 241         desc->maxSimulLines = -1;
 242         /* do not continue enumeration */
 243         return FALSE;
 244     }
 245     return TRUE;
 246 }
 247 
 248 
 249 INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* desc) {
 250 
 251     if (!DS_lockCache()) {
 252         return FALSE;
 253     }
 254 
 255     /* set the deviceID field to the cache index */
 256     desc->deviceID = findCacheItemByMixerIndex(mixerIndex);
 257     if (desc->deviceID < 0) {
 258         DS_unlockCache();
 259         return FALSE;
 260     }
 261     desc->maxSimulLines = 0;
 262     if (g_audioDeviceCache[desc->deviceID].isSource) {
 263         DirectSoundEnumerateW((LPDSENUMCALLBACKW) DS_GetDescEnum, desc);
 264         strncpy(desc->description, "DirectSound Playback", DAUDIO_STRING_LENGTH);
 265     } else {
 266         DirectSoundCaptureEnumerateW((LPDSENUMCALLBACKW) DS_GetDescEnum, desc);
 267         strncpy(desc->description, "DirectSound Capture", DAUDIO_STRING_LENGTH);
 268     }
 269 
 270     /*desc->vendor;
 271     desc->version;*/
 272 
 273     DS_unlockCache();
 274     return (desc->maxSimulLines == -1)?TRUE:FALSE;
 275 }
 276 
 277 /* multi-channel info: http://www.microsoft.com/whdc/hwdev/tech/audio/multichaud.mspx */
 278 
 279 //static UINT32 sampleRateArray[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 56000, 88000, 96000, 172000, 192000 };
 280 static INT32 sampleRateArray[] = { -1 };
 281 static INT32 channelsArray[] = { 1, 2};
 282 static INT32 bitsArray[] = { 8, 16};
 283 
 284 #define SAMPLERATE_COUNT sizeof(sampleRateArray)/sizeof(INT32)
 285 #define CHANNELS_COUNT sizeof(channelsArray)/sizeof(INT32)
 286 #define BITS_COUNT sizeof(bitsArray)/sizeof(INT32)
 287 
 288 void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
 289 
 290     int rateIndex, channelIndex, bitIndex;
 291 
 292     /* no need to lock, since deviceID identifies the device sufficiently */
 293 
 294     /* sanity */
 295     if (deviceID >= g_cacheCount) {
 296         return;
 297     }
 298     if ((g_audioDeviceCache[deviceID].isSource && !isSource)
 299         || (!g_audioDeviceCache[deviceID].isSource && isSource)) {
 300         /* only support Playback or Capture */
 301         return;
 302     }
 303 
 304     for (rateIndex = 0; rateIndex < SAMPLERATE_COUNT; rateIndex++) {
 305         for (channelIndex = 0; channelIndex < CHANNELS_COUNT; channelIndex++) {
 306             for (bitIndex = 0; bitIndex < BITS_COUNT; bitIndex++) {
 307                 DAUDIO_AddAudioFormat(creator, bitsArray[bitIndex],
 308                                       ((bitsArray[bitIndex] + 7) / 8) * channelsArray[channelIndex],
 309                                       channelsArray[channelIndex],
 310                                       (float) sampleRateArray[rateIndex],
 311                                       DAUDIO_PCM,
 312                                       (bitsArray[bitIndex]==8)?FALSE:TRUE,  /* signed */
 313                                       (bitsArray[bitIndex]==8)?FALSE:
 314 #ifndef _LITTLE_ENDIAN
 315                                       TRUE /* big endian */
 316 #else
 317                                       FALSE /* little endian */
 318 #endif
 319                                       );
 320             }
 321         }
 322     }
 323 }
 324 
 325 typedef struct {
 326     int deviceID;
 327     /* for convenience */
 328     BOOL isSource;
 329     /* the secondary buffer (Playback) */
 330     LPDIRECTSOUNDBUFFER playBuffer;
 331     /* the secondary buffer (Capture) */
 332     LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;
 333 
 334     /* size of the directsound buffer, usually 2 seconds */
 335     int dsBufferSizeInBytes;
 336 
 337     /* size of the read/write-ahead, as specified by Java */
 338     int bufferSizeInBytes;
 339     int bitsPerSample;
 340     int frameSize; // storage size in Bytes
 341 
 342     UINT64 framePos;
 343     /* where to write into the buffer.
 344      * -1 if at current position (Playback)
 345      * For Capture, this is the read position
 346      */
 347     int writePos;
 348 
 349     /* if start() had been called */
 350     BOOL started;
 351 
 352     /* how many bytes there is silence from current write position */
 353     int silencedBytes;
 354 
 355     BOOL underrun;
 356 
 357 } DS_Info;
 358 
 359 
 360 LPSTR TranslateDSError(HRESULT hr) {
 361     switch(hr) {
 362         case DSERR_ALLOCATED:
 363             return "DSERR_ALLOCATED";
 364 
 365         case DSERR_CONTROLUNAVAIL:
 366             return "DSERR_CONTROLUNAVAIL";
 367 
 368         case DSERR_INVALIDPARAM:
 369             return "DSERR_INVALIDPARAM";
 370 
 371         case DSERR_INVALIDCALL:
 372             return "DSERR_INVALIDCALL";
 373 
 374         case DSERR_GENERIC:
 375             return "DSERR_GENERIC";
 376 
 377         case DSERR_PRIOLEVELNEEDED:
 378             return "DSERR_PRIOLEVELNEEDED";
 379 
 380         case DSERR_OUTOFMEMORY:
 381             return "DSERR_OUTOFMEMORY";
 382 
 383         case DSERR_BADFORMAT:
 384             return "DSERR_BADFORMAT";
 385 
 386         case DSERR_UNSUPPORTED:
 387             return "DSERR_UNSUPPORTED";
 388 
 389         case DSERR_NODRIVER:
 390             return "DSERR_NODRIVER";
 391 
 392         case DSERR_ALREADYINITIALIZED:
 393             return "DSERR_ALREADYINITIALIZED";
 394 
 395         case DSERR_NOAGGREGATION:
 396             return "DSERR_NOAGGREGATION";
 397 
 398         case DSERR_BUFFERLOST:
 399             return "DSERR_BUFFERLOST";
 400 
 401         case DSERR_OTHERAPPHASPRIO:
 402             return "DSERR_OTHERAPPHASPRIO";
 403 
 404         case DSERR_UNINITIALIZED:
 405             return "DSERR_UNINITIALIZED";
 406 
 407         default:
 408             return "Unknown HRESULT";
 409         }
 410 }
 411 
 412 /*
 413 ** data/routines for starting DS buffers by separate thread
 414 ** (joint into DS_StartBufferHelper class)
 415 ** see cr6372428: playback fails after exiting from thread that has started it
 416 ** due IDirectSoundBuffer8::Play() description:
 417 ** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
 418 **       /directx/htm/idirectsoundbuffer8play.asp
 419 ** (remark section): If the application is multithreaded, the thread that plays
 420 ** the buffer must continue to exist as long as the buffer is playing.
 421 ** Buffers created on WDM drivers stop playing when the thread is terminated.
 422 ** IDirectSoundCaptureBuffer8::Start() has the same remark:
 423 ** http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c
 424 **       /directx/htm/idirectsoundcapturebuffer8start.asp
 425 */
 426 class DS_StartBufferHelper {
 427 public:
 428     /* starts DirectSound buffer (playback or capture) */
 429     static HRESULT StartBuffer(DS_Info* info);
 430     /* checks for initialization success */
 431     static inline BOOL isInitialized() { return data.threadHandle != NULL; }
 432 protected:
 433     DS_StartBufferHelper() {}  // no need to create an instance
 434 
 435     /* data class */
 436     class Data {
 437     public:
 438         Data();
 439         ~Data();
 440         // public data to access from parent class
 441         CRITICAL_SECTION crit_sect;
 442         volatile HANDLE threadHandle;
 443         volatile HANDLE startEvent;
 444         volatile HANDLE startedEvent;
 445         volatile DS_Info* line2Start;
 446         volatile HRESULT startResult;
 447     } static data;
 448 
 449     /* StartThread function */
 450     static DWORD WINAPI __stdcall ThreadProc(void *param);
 451 };
 452 
 453 /* StartBufferHelper class implementation
 454 */
 455 DS_StartBufferHelper::Data DS_StartBufferHelper::data;
 456 
 457 DS_StartBufferHelper::Data::Data() {
 458     threadHandle = NULL;
 459     ::InitializeCriticalSection(&crit_sect);
 460     startEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
 461     startedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
 462     if (startEvent != NULL && startedEvent != NULL)
 463         threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
 464 }
 465 
 466 DS_StartBufferHelper::Data::~Data() {
 467     ::EnterCriticalSection(&crit_sect);
 468     if (threadHandle != NULL) {
 469         // terminate thread
 470         line2Start = NULL;
 471         ::SetEvent(startEvent);
 472         ::CloseHandle(threadHandle);
 473         threadHandle = NULL;
 474     }
 475     ::LeaveCriticalSection(&crit_sect);
 476     // won't delete startEvent/startedEvent/crit_sect
 477     // - Windows will do during process shutdown
 478 }
 479 
 480 DWORD WINAPI __stdcall DS_StartBufferHelper::ThreadProc(void *param)
 481 {
 482     ::CoInitialize(NULL);
 483     while (1) {
 484         // wait for something to do
 485         ::WaitForSingleObject(data.startEvent, INFINITE);
 486         if (data.line2Start == NULL) {
 487             // (data.line2Start == NULL) is a signal to terminate thread
 488             break;
 489         }
 490         if (data.line2Start->isSource) {
 491             data.startResult =
 492                 data.line2Start->playBuffer->Play(0, 0, DSBPLAY_LOOPING);
 493         } else {
 494             data.startResult =
 495                 data.line2Start->captureBuffer->Start(DSCBSTART_LOOPING);
 496         }
 497         ::SetEvent(data.startedEvent);
 498     }
 499     ::CoUninitialize();
 500     return 0;
 501 }
 502 
 503 HRESULT DS_StartBufferHelper::StartBuffer(DS_Info* info) {
 504     HRESULT hr;
 505     ::EnterCriticalSection(&data.crit_sect);
 506     if (!isInitialized()) {
 507         ::LeaveCriticalSection(&data.crit_sect);
 508         return E_FAIL;
 509     }
 510     data.line2Start = info;
 511     ::SetEvent(data.startEvent);
 512     ::WaitForSingleObject(data.startedEvent, INFINITE);
 513     hr = data.startResult;
 514     ::LeaveCriticalSection(&data.crit_sect);
 515     return hr;
 516 }
 517 
 518 
 519 /* helper routines for DS buffer positions */
 520 /* returns distance from pos1 to pos2
 521  */
 522 inline int DS_getDistance(DS_Info* info, int pos1, int pos2) {
 523     int distance = pos2 - pos1;
 524     while (distance < 0)
 525         distance += info->dsBufferSizeInBytes;
 526     return distance;
 527 }
 528 
 529 /* adds 2 positions
 530  */
 531 inline int DS_addPos(DS_Info* info, int pos1, int pos2) {
 532     int result = pos1 + pos2;
 533     while (result >= info->dsBufferSizeInBytes)
 534         result -= info->dsBufferSizeInBytes;
 535     return result;
 536 }
 537 
 538 
 539 BOOL DS_addDeviceRef(INT32 deviceID) {
 540     HWND ownerWindow;
 541     HRESULT res = DS_OK;
 542     LPDIRECTSOUND devPlay;
 543     LPDIRECTSOUNDCAPTURE devCapture;
 544     LPGUID lpGuid = NULL;
 545 
 546 
 547     if (g_audioDeviceCache[deviceID].dev == NULL) {
 548         /* Create DirectSound */
 549         TRACE1("Creating DirectSound object for device %d\n", deviceID);
 550         lpGuid = &(g_audioDeviceCache[deviceID].guid);
 551         if (isEqualGUID(lpGuid, NULL)) {
 552             lpGuid = NULL;
 553         }
 554         if (g_audioDeviceCache[deviceID].isSource) {
 555             res = DirectSoundCreate(lpGuid, &devPlay, NULL);
 556             g_audioDeviceCache[deviceID].dev = (void*) devPlay;
 557         } else {
 558             res = DirectSoundCaptureCreate(lpGuid, &devCapture, NULL);
 559             g_audioDeviceCache[deviceID].dev = (void*) devCapture;
 560         }
 561         g_audioDeviceCache[deviceID].refCount = 0;
 562         if (FAILED(res)) {
 563             ERROR1("DAUDIO_Open: ERROR: Failed to create DirectSound: %s", TranslateDSError(res));
 564             g_audioDeviceCache[deviceID].dev = NULL;
 565             return FALSE;
 566         }
 567         if (g_audioDeviceCache[deviceID].isSource) {
 568             ownerWindow = GetForegroundWindow();
 569             if (ownerWindow == NULL) {
 570                 ownerWindow = GetDesktopWindow();
 571             }
 572             TRACE0("DAUDIO_Open: Setting cooperative level\n");
 573             res = devPlay->SetCooperativeLevel(ownerWindow, DSSCL_NORMAL);
 574             if (FAILED(res)) {
 575                 ERROR1("DAUDIO_Open: ERROR: Failed to set cooperative level: %s", TranslateDSError(res));
 576                 return FALSE;
 577             }
 578         }
 579     }
 580     g_audioDeviceCache[deviceID].refCount++;
 581     return TRUE;
 582 }
 583 
 584 #define DEV_PLAY(devID)    ((LPDIRECTSOUND) g_audioDeviceCache[devID].dev)
 585 #define DEV_CAPTURE(devID) ((LPDIRECTSOUNDCAPTURE) g_audioDeviceCache[devID].dev)
 586 
 587 void DS_removeDeviceRef(INT32 deviceID) {
 588 
 589     if (g_audioDeviceCache[deviceID].refCount) {
 590         g_audioDeviceCache[deviceID].refCount--;
 591     }
 592     if (g_audioDeviceCache[deviceID].refCount == 0) {
 593         if (g_audioDeviceCache[deviceID].dev != NULL) {
 594             if (g_audioDeviceCache[deviceID].isSource) {
 595                 DEV_PLAY(deviceID)->Release();
 596             } else {
 597                 DEV_CAPTURE(deviceID)->Release();
 598             }
 599             g_audioDeviceCache[deviceID].dev = NULL;
 600         }
 601     }
 602 }
 603 
 604 #ifndef _WAVEFORMATEXTENSIBLE_
 605 #define _WAVEFORMATEXTENSIBLE_
 606 typedef struct {
 607     WAVEFORMATEX    Format;
 608     union {
 609         WORD wValidBitsPerSample;       /* bits of precision  */
 610         WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
 611         WORD wReserved;                 /* If neither applies, set to zero. */
 612     } Samples;
 613     DWORD           dwChannelMask;      /* which channels are */
 614                                         /* present in stream  */
 615     GUID            SubFormat;
 616 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
 617 #endif // !_WAVEFORMATEXTENSIBLE_
 618 
 619 #if !defined(WAVE_FORMAT_EXTENSIBLE)
 620 #define  WAVE_FORMAT_EXTENSIBLE                 0xFFFE
 621 #endif // !defined(WAVE_FORMAT_EXTENSIBLE)
 622 
 623 #if !defined(DEFINE_WAVEFORMATEX_GUID)
 624 #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
 625 #endif
 626 #ifndef STATIC_KSDATAFORMAT_SUBTYPE_PCM
 627 #define STATIC_KSDATAFORMAT_SUBTYPE_PCM\
 628     DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
 629 #endif
 630 
 631 
 632 void createWaveFormat(WAVEFORMATEXTENSIBLE* format,
 633                       int sampleRate,
 634                       int channels,
 635                       int bits,
 636                       int significantBits) {
 637     GUID subtypePCM = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};
 638     format->Format.nSamplesPerSec = (DWORD)sampleRate;
 639     format->Format.nChannels = (WORD) channels;
 640     /* do not support useless padding, like 24-bit samples stored in 32-bit containers */
 641     format->Format.wBitsPerSample = (WORD) ((bits + 7) & 0xFFF8);
 642 
 643     if (channels <= 2 && bits <= 16) {
 644         format->Format.wFormatTag = WAVE_FORMAT_PCM;
 645         format->Format.cbSize = 0;
 646     } else {
 647         format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
 648         format->Format.cbSize = 22;
 649         format->Samples.wValidBitsPerSample = bits;
 650         /* no way to specify speaker locations */
 651         format->dwChannelMask = 0xFFFFFFFF;
 652         format->SubFormat = subtypePCM;
 653     }
 654     format->Format.nBlockAlign = (WORD)((format->Format.wBitsPerSample * format->Format.nChannels) / 8);
 655     format->Format.nAvgBytesPerSec = format->Format.nSamplesPerSec * format->Format.nBlockAlign;
 656 }
 657 
 658 /* fill buffer with silence
 659  */
 660 void DS_clearBuffer(DS_Info* info, BOOL fromWritePos) {
 661     UBYTE* pb1=NULL, *pb2=NULL;
 662     DWORD  cb1=0, cb2=0;
 663     DWORD flags = 0;
 664     int start, count;
 665     TRACE1("> DS_clearBuffer for device %d\n", info->deviceID);
 666     if (info->isSource)  {
 667         if (fromWritePos) {
 668                 DWORD playCursor, writeCursor;
 669                 int end;
 670                 if (FAILED(info->playBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
 671                     ERROR0("  DS_clearBuffer: ERROR: Failed to get current position.");
 672                     TRACE0("< DS_clearbuffer\n");
 673                     return;
 674                 }
 675                 DEBUG_SILENCING2("  DS_clearBuffer: DS playPos=%d  myWritePos=%d", (int) playCursor, (int) info->writePos);
 676                 if (info->writePos >= 0) {
 677                     start = info->writePos + info->silencedBytes;
 678                 } else {
 679                     start = writeCursor + info->silencedBytes;
 680                     //flags |= DSBLOCK_FROMWRITECURSOR;
 681                 }
 682                 while (start >= info->dsBufferSizeInBytes) {
 683                     start -= info->dsBufferSizeInBytes;
 684                 }
 685 
 686                 // fix for bug 6251460 (REGRESSION: short sounds do not play)
 687                 // for unknown reason with hardware DS buffer playCursor sometimes
 688                 // jumps back for little interval (mostly 2-8 bytes) (writeCursor moves forward as usual)
 689                 // The issue happens right after start playing and for short sounds only (less then DS buffer,
 690                 // when whole sound written into the buffer and remaining space filled by silence)
 691                 // the case doesn't produce any audible aftifacts so just catch it to prevent filling
 692                 // whole buffer by silence.
 693                 if (((int)playCursor <= start && start < (int)writeCursor)
 694                     || (writeCursor < playCursor    // buffer bound is between playCursor & writeCursor
 695                         && (start < (int)writeCursor || (int)playCursor <= start))) {
 696                     return;
 697                 }
 698 
 699                 count = info->dsBufferSizeInBytes - info->silencedBytes;
 700                 // why / 4?
 701                 //if (count > info->dsBufferSizeInBytes / 4) {
 702                 //    count = info->dsBufferSizeInBytes / 4;
 703                 //}
 704                 end = start + count;
 705                 if ((int) playCursor < start) {
 706                     playCursor += (DWORD) info->dsBufferSizeInBytes;
 707                 }
 708                 if (start <= (int) playCursor && end > (int) playCursor) {
 709                     /* at maximum, silence until play cursor */
 710                     count = (int) playCursor - start;
 711 #ifdef USE_TRACE
 712                     if ((int) playCursor >= info->dsBufferSizeInBytes) playCursor -= (DWORD) info->dsBufferSizeInBytes;
 713                     TRACE3("\n  DS_clearBuffer: Start Writing from %d, "
 714                            "would overwrite playCursor=%d, so reduce count to %d\n",
 715                            start, playCursor, count);
 716 #endif
 717                 }
 718                 DEBUG_SILENCING2("  clearing buffer from %d, count=%d. ", (int)start, (int) count);
 719                 if (count <= 0) {
 720                     DEBUG_SILENCING0("\n");
 721                     TRACE1("< DS_clearBuffer: no need to clear, silencedBytes=%d\n", info->silencedBytes);
 722                     return;
 723                 }
 724         } else {
 725                 start = 0;
 726                 count = info->dsBufferSizeInBytes;
 727                 flags |= DSBLOCK_ENTIREBUFFER;
 728         }
 729         if (FAILED(info->playBuffer->Lock(start,
 730                                           count,
 731                                           (LPVOID*) &pb1, &cb1,
 732                                           (LPVOID*) &pb2, &cb2, flags))) {
 733             ERROR0("\n  DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
 734             TRACE0("< DS_clearbuffer\n");
 735             return;
 736         }
 737     } else {
 738         if (FAILED(info->captureBuffer->Lock(0,
 739                                              info->dsBufferSizeInBytes,
 740                                              (LPVOID*) &pb1, &cb1,
 741                                              (LPVOID*) &pb2, &cb2, DSCBLOCK_ENTIREBUFFER))) {
 742             ERROR0("  DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
 743             TRACE0("< DS_clearbuffer\n");
 744             return;
 745         }
 746     }
 747     if (pb1!=NULL) {
 748         memset(pb1, (info->bitsPerSample == 8)?128:0, cb1);
 749     }
 750     if (pb2!=NULL) {
 751         memset(pb2, (info->bitsPerSample == 8)?128:0, cb2);
 752     }
 753     if (info->isSource)  {
 754         info->playBuffer->Unlock( pb1, cb1, pb2, cb2 );
 755         if (!fromWritePos) {
 756             /* doesn't matter where to start writing next time */
 757             info->writePos = -1;
 758             info->silencedBytes = info->dsBufferSizeInBytes;
 759         } else {
 760             info->silencedBytes += (cb1+cb2);
 761             if (info->silencedBytes > info->dsBufferSizeInBytes) {
 762                 ERROR1("  DS_clearbuffer: ERROR: silencedBytes=%d exceeds buffer size!\n",
 763                        info->silencedBytes);
 764                 info->silencedBytes = info->dsBufferSizeInBytes;
 765             }
 766         }
 767         DEBUG_SILENCING2("  silencedBytes=%d, my writePos=%d\n", (int)info->silencedBytes, (int)info->writePos);
 768     } else {
 769         info->captureBuffer->Unlock( pb1, cb1, pb2, cb2 );
 770     }
 771     TRACE0("< DS_clearbuffer\n");
 772 }
 773 
 774 /* returns pointer to buffer */
 775 void* DS_createSoundBuffer(DS_Info* info,
 776                           float sampleRate,
 777                           int sampleSizeInBits,
 778                           int channels,
 779                           int bufferSizeInBytes) {
 780     DSBUFFERDESC dsbdesc;
 781     DSCBUFFERDESC dscbdesc;
 782     HRESULT res;
 783     WAVEFORMATEXTENSIBLE format;
 784     void* buffer;
 785 
 786     TRACE1("Creating secondary buffer for device %d\n", info->deviceID);
 787     createWaveFormat(&format,
 788                      (int) sampleRate,
 789                      channels,
 790                      info->frameSize / channels * 8,
 791                      sampleSizeInBits);
 792 
 793     /* 2 second secondary buffer */
 794     info->dsBufferSizeInBytes = 2 * ((int) sampleRate) * info->frameSize;
 795 
 796     if (bufferSizeInBytes > info->dsBufferSizeInBytes / 2) {
 797         bufferSizeInBytes = info->dsBufferSizeInBytes / 2;
 798     }
 799     bufferSizeInBytes = (bufferSizeInBytes / info->frameSize) * info->frameSize;
 800     info->bufferSizeInBytes = bufferSizeInBytes;
 801 
 802     if (info->isSource) {
 803         memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
 804         dsbdesc.dwSize = sizeof(DSBUFFERDESC);
 805         dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
 806                     | DSBCAPS_GLOBALFOCUS;
 807 
 808         dsbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
 809         dsbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
 810         res = DEV_PLAY(info->deviceID)->CreateSoundBuffer
 811             (&dsbdesc, (LPDIRECTSOUNDBUFFER*) &buffer, NULL);
 812     } else {
 813         memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
 814         dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
 815         dscbdesc.dwFlags = 0;
 816         dscbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
 817         dscbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
 818         res = DEV_CAPTURE(info->deviceID)->CreateCaptureBuffer
 819             (&dscbdesc, (LPDIRECTSOUNDCAPTUREBUFFER*) &buffer, NULL);
 820     }
 821     if (FAILED(res)) {
 822         ERROR1("DS_createSoundBuffer: ERROR: Failed to create sound buffer: %s", TranslateDSError(res));
 823         return NULL;
 824     }
 825     return buffer;
 826 }
 827 
 828 void DS_destroySoundBuffer(DS_Info* info) {
 829     if (info->playBuffer != NULL) {
 830         info->playBuffer->Release();
 831         info->playBuffer = NULL;
 832     }
 833     if (info->captureBuffer != NULL) {
 834         info->captureBuffer->Release();
 835         info->captureBuffer = NULL;
 836     }
 837 }
 838 
 839 
 840 void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
 841                   int encoding, float sampleRate, int sampleSizeInBits,
 842                   int frameSize, int channels,
 843                   int isSigned, int isBigEndian, int bufferSizeInBytes) {
 844 
 845     DS_Info* info;
 846     void* buffer;
 847 
 848     TRACE0("> DAUDIO_Open\n");
 849 
 850     /* some sanity checks */
 851     if (deviceID >= g_cacheCount) {
 852         ERROR1("DAUDIO_Open: ERROR: cannot open the device with deviceID=%d!\n", deviceID);
 853         return NULL;
 854     }
 855     if ((g_audioDeviceCache[deviceID].isSource && !isSource)
 856         || (!g_audioDeviceCache[deviceID].isSource && isSource)) {
 857         /* only support Playback or Capture */
 858         ERROR0("DAUDIO_Open: ERROR: Cache is corrupt: cannot open the device in specified isSource mode!\n");
 859         return NULL;
 860     }
 861     if (encoding != DAUDIO_PCM) {
 862         ERROR1("DAUDIO_Open: ERROR: cannot open the device with encoding=%d!\n", encoding);
 863         return NULL;
 864     }
 865     if (channels <= 0) {
 866         ERROR1("DAUDIO_Open: ERROR: Invalid number of channels=%d!\n", channels);
 867         return NULL;
 868     }
 869     if (sampleSizeInBits > 8 &&
 870 #ifdef _LITTLE_ENDIAN
 871         isBigEndian
 872 #else
 873         !isBigEndian
 874 #endif
 875         ) {
 876         ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian);
 877         return NULL;
 878     }
 879     if (sampleSizeInBits == 8 && isSigned) {
 880         ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n");
 881         return NULL;
 882     }
 883     if (!DS_StartBufferHelper::isInitialized()) {
 884         ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n");
 885         return NULL;
 886     }
 887 
 888     info = (DS_Info*) malloc(sizeof(DS_Info));
 889     if (!info) {
 890         ERROR0("DAUDIO_Open: ERROR: Out of memory\n");
 891         return NULL;
 892     }
 893     memset(info, 0, sizeof(DS_Info));
 894 
 895     info->deviceID = deviceID;
 896     info->isSource = isSource;
 897     info->bitsPerSample = sampleSizeInBits;
 898     info->frameSize = frameSize;
 899     info->framePos = 0;
 900     info->started = FALSE;
 901     info->underrun = FALSE;
 902 
 903     if (!DS_addDeviceRef(deviceID)) {
 904         DS_removeDeviceRef(deviceID);
 905         free(info);
 906         return NULL;
 907     }
 908 
 909     buffer = DS_createSoundBuffer(info,
 910                                   sampleRate,
 911                                   sampleSizeInBits,
 912                                   channels,
 913                                   bufferSizeInBytes);
 914     if (!buffer) {
 915         DS_removeDeviceRef(deviceID);
 916         free(info);
 917         return NULL;
 918     }
 919 
 920     if (info->isSource) {
 921         info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer;
 922     } else {
 923         info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer;
 924     }
 925     DS_clearBuffer(info, FALSE /* entire buffer */);
 926 
 927     /* use writepos of device */
 928     if (info->isSource) {
 929         info->writePos = -1;
 930     } else {
 931         info->writePos = 0;
 932     }
 933 
 934     TRACE0("< DAUDIO_Open: Opened device successfully.\n");
 935     return (void*) info;
 936 }
 937 
 938 int DAUDIO_Start(void* id, int isSource) {
 939     DS_Info* info = (DS_Info*) id;
 940     HRESULT res = DS_OK;
 941     DWORD status;
 942 
 943     TRACE0("> DAUDIO_Start\n");
 944 
 945     if (info->isSource)  {
 946         res = info->playBuffer->GetStatus(&status);
 947         if (res == DS_OK) {
 948             if (status & DSBSTATUS_LOOPING) {
 949                 ERROR0("DAUDIO_Start: ERROR: Already started!");
 950                 return TRUE;
 951             }
 952 
 953             /* only start buffer if already something written to it */
 954             if (info->writePos >= 0) {
 955                 res = DS_StartBufferHelper::StartBuffer(info);
 956                 if (res == DSERR_BUFFERLOST) {
 957                     res = info->playBuffer->Restore();
 958                     if (res == DS_OK) {
 959                         DS_clearBuffer(info, FALSE /* entire buffer */);
 960                         /* write() will trigger actual device start */
 961                     }
 962                 } else {
 963                     /* make sure that we will have silence after
 964                        the currently valid audio data */
 965                     DS_clearBuffer(info, TRUE /* from write position */);
 966                 }
 967             }
 968         }
 969     } else {
 970         if (info->captureBuffer->GetStatus(&status) == DS_OK) {
 971             if (status & DSCBSTATUS_LOOPING) {
 972                 ERROR0("DAUDIO_Start: ERROR: Already started!");
 973                 return TRUE;
 974             }
 975         }
 976         res = DS_StartBufferHelper::StartBuffer(info);
 977     }
 978     if (FAILED(res)) {
 979         ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res));
 980         return FALSE;
 981     }
 982     info->started = TRUE;
 983     return TRUE;
 984 }
 985 
 986 int DAUDIO_Stop(void* id, int isSource) {
 987     DS_Info* info = (DS_Info*) id;
 988 
 989     TRACE0("> DAUDIO_Stop\n");
 990 
 991     info->started = FALSE;
 992     if (info->isSource)  {
 993         info->playBuffer->Stop();
 994     } else {
 995         info->captureBuffer->Stop();
 996     }
 997 
 998     TRACE0("< DAUDIO_Stop\n");
 999     return TRUE;
1000 }
1001 
1002 
1003 void DAUDIO_Close(void* id, int isSource) {
1004     DS_Info* info = (DS_Info*) id;
1005 
1006     TRACE0("DAUDIO_Close\n");
1007 
1008     if (info != NULL) {
1009         DS_destroySoundBuffer(info);
1010         DS_removeDeviceRef(info->deviceID);
1011         free(info);
1012     }
1013 }
1014 
1015 /* Check buffer for underrun
1016  * This method is only meaningful for Output devices (write devices).
1017  */
1018 void DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) {
1019     TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, "
1020            "info->writePos=%d  silencedBytes=%d  dsBufferSizeInBytes=%d\n",
1021            (int) playCursor, (int) writeCursor, (int) info->writePos,
1022            (int) info->silencedBytes, (int) info->dsBufferSizeInBytes);
1023     if (info->underrun || info->writePos < 0) return;
1024     int writeAhead = DS_getDistance(info, writeCursor, info->writePos);
1025     if (writeAhead > info->bufferSizeInBytes) {
1026         // this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes)
1027         // But the case can occur only when we have more then info->bufferSizeInBytes valid bytes
1028         // (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes)
1029         // If we already have a lot of silencedBytes after valid data (written by
1030         // DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun
1031         if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) {
1032             // underrun!
1033             ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n");
1034             info->underrun = TRUE;
1035         }
1036     }
1037 }
1038 
1039 /* For source (playback) line:
1040  *   (a) if (fromPlayCursor == FALSE), returns number of bytes available
1041  *     for writing: bufferSize - (info->writePos - writeCursor);
1042  *   (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor
1043  *     and returned value can be used for play position calculation (see also
1044  *     note about bufferSize)
1045  * For destination (capture) line:
1046  *   (c) if (fromPlayCursor == FALSE), returns number of bytes available
1047  *     for reading from the buffer: readCursor - info->writePos;
1048  *   (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor
1049  *     and returned value can be used for capture position calculation (see
1050  *     note about bufferSize)
1051  * bufferSize parameter are filled by "actual" buffer size:
1052  *   if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes
1053  *   otherwise it increase by number of bytes currently processed by DirectSound
1054  *     (writeCursor - playCursor) or (captureCursor - readCursor)
1055  */
1056 int DS_GetAvailable(DS_Info* info,
1057                     DWORD* playCursor, DWORD* writeCursor,
1058                     int* bufferSize, BOOL fromPlayCursor) {
1059     int available;
1060     int newReadPos;
1061 
1062     TRACE2("DS_GetAvailable: fromPlayCursor=%d,  deviceID=%d\n", fromPlayCursor, info->deviceID);
1063     if (!info->playBuffer && !info->captureBuffer) {
1064         ERROR0("DS_GetAvailable: ERROR: buffer not yet created");
1065         return 0;
1066     }
1067 
1068     if (info->isSource)  {
1069         if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) {
1070             ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
1071             return 0;
1072         }
1073         int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
1074         // workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor
1075         if (processing > info->dsBufferSizeInBytes / 2) {
1076             *writeCursor = *playCursor;
1077             processing = 0;
1078         }
1079         TRACE3("   playCursor=%d, writeCursor=%d, info->writePos=%d\n",
1080                *playCursor, *writeCursor, info->writePos);
1081         *bufferSize = info->bufferSizeInBytes;
1082         if (fromPlayCursor) {
1083             *bufferSize += processing;
1084         }
1085         DS_CheckUnderrun(info, *playCursor, *writeCursor);
1086         if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) {
1087                 /* always full buffer if at beginning */
1088                 available = *bufferSize;
1089         } else {
1090             int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor, info->writePos);
1091             if (currWriteAhead > *bufferSize) {
1092                 if (info->underrun) {
1093                     // playCursor surpassed writePos - no valid data, whole buffer available
1094                     available = *bufferSize;
1095                 } else {
1096                     // the case may occur after stop(), when writeCursor jumps back to playCursor
1097                     // so "actual" buffer size has grown
1098                     *bufferSize = currWriteAhead;
1099                     available = 0;
1100                 }
1101             } else {
1102                 available = *bufferSize - currWriteAhead;
1103             }
1104         }
1105     } else {
1106         if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) {
1107             ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
1108             return 0;
1109         }
1110         *bufferSize = info->bufferSizeInBytes;
1111         if (fromPlayCursor) {
1112             *bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
1113         }
1114         TRACE4("   captureCursor=%d, readCursor=%d, info->readPos=%d  refBufferSize=%d\n",
1115                *playCursor, *writeCursor, info->writePos, *bufferSize);
1116         if (info->writePos == -1) {
1117             /* always empty buffer if at beginning */
1118             info->writePos = (int) (*writeCursor);
1119         }
1120         if (fromPlayCursor) {
1121             available = ((int) (*playCursor) - info->writePos);
1122         } else {
1123             available = ((int) (*writeCursor) - info->writePos);
1124         }
1125         if (available < 0) {
1126             available += info->dsBufferSizeInBytes;
1127         }
1128         if (!fromPlayCursor && available > info->bufferSizeInBytes) {
1129             /* overflow */
1130             ERROR2("DS_GetAvailable: ERROR: overflow detected: "
1131                    "DirectSoundBufferSize=%d, bufferSize=%d, ",
1132                    info->dsBufferSizeInBytes, info->bufferSizeInBytes);
1133             ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n",
1134                    *playCursor, *writeCursor, info->writePos);
1135             /* advance read position, to allow exactly one buffer worth of data */
1136             newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes;
1137             if (newReadPos < 0) {
1138                 newReadPos += info->dsBufferSizeInBytes;
1139             }
1140             info->writePos = newReadPos;
1141             available = info->bufferSizeInBytes;
1142         }
1143     }
1144     available = (available / info->frameSize) * info->frameSize;
1145 
1146     TRACE1("DS_available: Returning %d available bytes\n", (int) available);
1147     return available;
1148 }
1149 
1150 // returns -1 on error, otherwise bytes written
1151 int DAUDIO_Write(void* id, char* data, int byteSize) {
1152     DS_Info* info = (DS_Info*) id;
1153     int available;
1154     int thisWritePos;
1155     DWORD playCursor, writeCursor;
1156     HRESULT res;
1157     void* buffer1, *buffer2;
1158     DWORD buffer1len, buffer2len;
1159     BOOL needRestart = FALSE;
1160     int bufferLostTrials = 2;
1161     int bufferSize;
1162 
1163     TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
1164 
1165     while (--bufferLostTrials > 0) {
1166         available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */);
1167         if (byteSize > available) byteSize = available;
1168         if (byteSize == 0) break;
1169         thisWritePos = info->writePos;
1170         if (thisWritePos == -1 || info->underrun) {
1171             // play from current write cursor after flush, etc.
1172             needRestart = TRUE;
1173             thisWritePos = writeCursor;
1174             info->underrun = FALSE;
1175         }
1176         DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize);
1177         res = info->playBuffer->Lock(thisWritePos, byteSize,
1178                                      (LPVOID *) &buffer1, &buffer1len,
1179                                      (LPVOID *) &buffer2, &buffer2len,
1180                                      0);
1181         if (res != DS_OK) {
1182             /* some DS failure */
1183             if (res == DSERR_BUFFERLOST) {
1184                 ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer.");
1185                 if (info->playBuffer->Restore() == DS_OK) {
1186                     DS_clearBuffer(info, FALSE /* entire buffer */);
1187                     info->writePos = -1;
1188                     /* try again */
1189                     continue;
1190                 }
1191             }
1192             /* can't recover from error */
1193             byteSize = 0;
1194             break;
1195         }
1196         /* buffer could be locked successfully */
1197         /* first fill first buffer */
1198         if (buffer1) {
1199             memcpy(buffer1, data, buffer1len);
1200             data = (char*) (((UINT_PTR) data) + buffer1len);
1201         } else buffer1len = 0;
1202         if (buffer2) {
1203             memcpy(buffer2, data, buffer2len);
1204         } else buffer2len = 0;
1205         byteSize = buffer1len + buffer2len;
1206 
1207         /* update next write pos */
1208         thisWritePos += byteSize;
1209         while (thisWritePos >= info->dsBufferSizeInBytes) {
1210             thisWritePos -= info->dsBufferSizeInBytes;
1211         }
1212         /* commit data to directsound */
1213         info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
1214 
1215         info->writePos = thisWritePos;
1216 
1217         /* update position
1218          * must be AFTER updating writePos,
1219          * so that getSvailable doesn't return too little,
1220          * so that getFramePos doesn't jump
1221          */
1222         info->framePos += (byteSize / info->frameSize);
1223 
1224         /* decrease silenced bytes */
1225         if (info->silencedBytes > byteSize) {
1226             info->silencedBytes -= byteSize;
1227         } else {
1228             info->silencedBytes = 0;
1229         }
1230         break;
1231     } /* while */
1232 
1233     /* start the device, if necessary */
1234     if (info->started && needRestart && (info->writePos >= 0)) {
1235         DS_StartBufferHelper::StartBuffer(info);
1236     }
1237 
1238     TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize);
1239     return byteSize;
1240 }
1241 
1242 // returns -1 on error
1243 int DAUDIO_Read(void* id, char* data, int byteSize) {
1244     DS_Info* info = (DS_Info*) id;
1245     int available;
1246     int thisReadPos;
1247     DWORD captureCursor, readCursor;
1248     HRESULT res;
1249     void* buffer1, *buffer2;
1250     DWORD buffer1len, buffer2len;
1251     int bufferSize;
1252 
1253     TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
1254 
1255     available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */);
1256     if (byteSize > available) byteSize = available;
1257     if (byteSize > 0) {
1258         thisReadPos = info->writePos;
1259         if (thisReadPos == -1) {
1260             /* from beginning */
1261             thisReadPos = 0;
1262         }
1263         res = info->captureBuffer->Lock(thisReadPos, byteSize,
1264                                         (LPVOID *) &buffer1, &buffer1len,
1265                                         (LPVOID *) &buffer2, &buffer2len,
1266                                         0);
1267         if (res != DS_OK) {
1268             /* can't recover from error */
1269             byteSize = 0;
1270         } else {
1271             /* buffer could be locked successfully */
1272             /* first fill first buffer */
1273             if (buffer1) {
1274                 memcpy(data, buffer1, buffer1len);
1275                 data = (char*) (((UINT_PTR) data) + buffer1len);
1276             } else buffer1len = 0;
1277             if (buffer2) {
1278                 memcpy(data, buffer2, buffer2len);
1279             } else buffer2len = 0;
1280             byteSize = buffer1len + buffer2len;
1281 
1282             /* update next read pos */
1283             thisReadPos = DS_addPos(info, thisReadPos, byteSize);
1284             /* commit data to directsound */
1285             info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
1286 
1287             /* update position
1288              * must be BEFORE updating readPos,
1289              * so that getAvailable doesn't return too much,
1290              * so that getFramePos doesn't jump
1291              */
1292             info->framePos += (byteSize / info->frameSize);
1293 
1294             info->writePos = thisReadPos;
1295         }
1296     }
1297 
1298     TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize);
1299     return byteSize;
1300 }
1301 
1302 
1303 int DAUDIO_GetBufferSize(void* id, int isSource) {
1304     DS_Info* info = (DS_Info*) id;
1305     return info->bufferSizeInBytes;
1306 }
1307 
1308 int DAUDIO_StillDraining(void* id, int isSource) {
1309     DS_Info* info = (DS_Info*) id;
1310     BOOL draining = FALSE;
1311     int available, bufferSize;
1312     DWORD playCursor, writeCursor;
1313 
1314     DS_clearBuffer(info, TRUE /* from write position */);
1315     available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */);
1316     draining = (available < bufferSize);
1317 
1318     TRACE3("DAUDIO_StillDraining: available=%d  silencedBytes=%d  Still draining: %s\n",
1319            available, info->silencedBytes, draining?"TRUE":"FALSE");
1320     return draining;
1321 }
1322 
1323 
1324 int DAUDIO_Flush(void* id, int isSource) {
1325     DS_Info* info = (DS_Info*) id;
1326 
1327     TRACE0("DAUDIO_Flush\n");
1328 
1329     if (info->isSource)  {
1330         info->playBuffer->Stop();
1331         DS_clearBuffer(info, FALSE /* entire buffer */);
1332     } else {
1333         DWORD captureCursor, readCursor;
1334         /* set the read pointer to the current read position */
1335         if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) {
1336             ERROR0("DAUDIO_Flush: ERROR: Failed to get current position.");
1337             return FALSE;
1338         }
1339         DS_clearBuffer(info, FALSE /* entire buffer */);
1340         /* SHOULD set to *captureCursor*,
1341          * but that would be detected as overflow
1342          * in a subsequent GetAvailable() call.
1343          */
1344         info->writePos = (int) readCursor;
1345     }
1346     return TRUE;
1347 }
1348 
1349 int DAUDIO_GetAvailable(void* id, int isSource) {
1350     DS_Info* info = (DS_Info*) id;
1351     DWORD playCursor, writeCursor;
1352     int ret, bufferSize;
1353 
1354     ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE);
1355 
1356     TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
1357     return ret;
1358 }
1359 
1360 INT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) {
1361     // estimate the current position with the buffer size and
1362     // the available bytes to read or write in the buffer.
1363     // not an elegant solution - bytePos will stop on xruns,
1364     // and in race conditions it may jump backwards
1365     // Advantage is that it is indeed based on the samples that go through
1366     // the system (rather than time-based methods)
1367     if (info->isSource) {
1368         // javaBytePos is the position that is reached when the current
1369         // buffer is played completely
1370         return (INT64) (javaBytePos - bufferSize + availInBytes);
1371     } else {
1372         // javaBytePos is the position that was when the current buffer was empty
1373         return (INT64) (javaBytePos + availInBytes);
1374     }
1375 }
1376 
1377 INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
1378     DS_Info* info = (DS_Info*) id;
1379     int available, bufferSize;
1380     DWORD playCursor, writeCursor;
1381     INT64 result = javaBytePos;
1382 
1383     available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE);
1384     result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available);
1385     return result;
1386 }
1387 
1388 
1389 void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
1390     /* save to ignore, since GetBytePosition
1391      * takes the javaBytePos param into account
1392      */
1393 }
1394 
1395 int DAUDIO_RequiresServicing(void* id, int isSource) {
1396     // need servicing on for SourceDataLines
1397     return isSource?TRUE:FALSE;
1398 }
1399 
1400 void DAUDIO_Service(void* id, int isSource) {
1401     DS_Info* info = (DS_Info*) id;
1402     if (isSource) {
1403         if (info->silencedBytes < info->dsBufferSizeInBytes) {
1404             // clear buffer
1405             TRACE0("DAUDIO_Service\n");
1406             DS_clearBuffer(info, TRUE /* from write position */);
1407         }
1408         if (info->writePos >= 0
1409             && info->started
1410             && !info->underrun
1411             && info->silencedBytes >= info->dsBufferSizeInBytes) {
1412             // if we're currently playing, and the entire buffer is silenced...
1413             // then we are underrunning!
1414             info->underrun = TRUE;
1415             ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n");
1416         }
1417     }
1418 }
1419 
1420 
1421 #endif // USE_DAUDIO