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