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