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 48extern "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 74typedef 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 84static DS_AudioDeviceCache g_audioDeviceCache[MAX_DS_DEVICES]; 85static INT32 g_cacheCount = 0; 86static UINT64 g_lastCacheRefreshTime = 0; 87static INT32 g_mixerCount = 0; 88 89BOOL DS_lockCache() { 90 /* dummy implementation for now, Java does locking */ 91 return TRUE; 92} 93 94void DS_unlockCache() { 95 /* dummy implementation for now */ 96} 97 98static GUID CLSID_DAUDIO_Zero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 99 100BOOL 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 114INT32 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 125INT32 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 135typedef struct { 136 INT32 currMixerIndex; 137 BOOL isSource; 138} DS_RefreshCacheStruct; 139 140 141BOOL 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 175INT32 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 229BOOL 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 246INT32 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 }; 277static INT32 sampleRateArray[] = { -1 }; 278static INT32 channelsArray[] = { 1, 2}; 279static 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 285void 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 322typedef 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 357LPSTR 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*/ 423class DS_StartBufferHelper { 424public: 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; } 429protected: 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*/ 452DS_StartBufferHelper::Data DS_StartBufferHelper::data; 453 454DS_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 463DS_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 477DWORD 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 500HRESULT 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 */ 519inline 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 */ 528inline 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 536BOOL 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 584void 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_ 603typedef 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 629void 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 */ 657void 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 */ 772void* 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 825void 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 837void* 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 (channels <= 0) { 863 ERROR1("DAUDIO_Open: ERROR: Invalid number of channels=%d!\n", channels); 864 return NULL; 865 } 866 if (sampleSizeInBits > 8 && 867#ifdef _LITTLE_ENDIAN 868 isBigEndian 869#else 870 !isBigEndian 871#endif 872 ) { 873 ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian); 874 return NULL; 875 } 876 if (sampleSizeInBits == 8 && isSigned) { 877 ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n"); 878 return NULL; 879 } 880 if (!DS_StartBufferHelper::isInitialized()) { 881 ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n"); 882 return NULL; 883 } 884 885 info = (DS_Info*) malloc(sizeof(DS_Info)); 886 if (!info) { 887 ERROR0("DAUDIO_Open: ERROR: Out of memory\n"); 888 return NULL; 889 } 890 memset(info, 0, sizeof(DS_Info)); 891 892 info->deviceID = deviceID; 893 info->isSource = isSource; 894 info->bitsPerSample = sampleSizeInBits; 895 info->frameSize = frameSize; 896 info->framePos = 0; 897 info->started = FALSE; 898 info->underrun = FALSE; 899 900 if (!DS_addDeviceRef(deviceID)) { 901 DS_removeDeviceRef(deviceID); 902 free(info); 903 return NULL; 904 } 905 906 buffer = DS_createSoundBuffer(info, 907 sampleRate, 908 sampleSizeInBits, 909 channels, 910 bufferSizeInBytes); 911 if (!buffer) { 912 DS_removeDeviceRef(deviceID); 913 free(info); 914 return NULL; 915 } 916 917 if (info->isSource) { 918 info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer; 919 } else { 920 info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer; 921 } 922 DS_clearBuffer(info, FALSE /* entire buffer */); 923 924 /* use writepos of device */ 925 if (info->isSource) { 926 info->writePos = -1; 927 } else { 928 info->writePos = 0; 929 } 930 931 TRACE0("< DAUDIO_Open: Opened device successfully.\n"); 932 return (void*) info; 933} 934 935int DAUDIO_Start(void* id, int isSource) { 936 DS_Info* info = (DS_Info*) id; 937 HRESULT res = DS_OK; 938 DWORD status; 939 940 TRACE0("> DAUDIO_Start\n"); 941 942 if (info->isSource) { 943 res = info->playBuffer->GetStatus(&status); 944 if (res == DS_OK) { 945 if (status & DSBSTATUS_LOOPING) { 946 ERROR0("DAUDIO_Start: ERROR: Already started!"); 947 return TRUE; 948 } 949 950 /* only start buffer if already something written to it */ 951 if (info->writePos >= 0) { 952 res = DS_StartBufferHelper::StartBuffer(info); 953 if (res == DSERR_BUFFERLOST) { 954 res = info->playBuffer->Restore(); 955 if (res == DS_OK) { 956 DS_clearBuffer(info, FALSE /* entire buffer */); 957 /* write() will trigger actual device start */ 958 } 959 } else { 960 /* make sure that we will have silence after 961 the currently valid audio data */ 962 DS_clearBuffer(info, TRUE /* from write position */); 963 } 964 } 965 } 966 } else { 967 if (info->captureBuffer->GetStatus(&status) == DS_OK) { 968 if (status & DSCBSTATUS_LOOPING) { 969 ERROR0("DAUDIO_Start: ERROR: Already started!"); 970 return TRUE; 971 } 972 } 973 res = DS_StartBufferHelper::StartBuffer(info); 974 } 975 if (FAILED(res)) { 976 ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res)); 977 return FALSE; 978 } 979 info->started = TRUE; 980 return TRUE; 981} 982 983int DAUDIO_Stop(void* id, int isSource) { 984 DS_Info* info = (DS_Info*) id; 985 986 TRACE0("> DAUDIO_Stop\n"); 987 988 info->started = FALSE; 989 if (info->isSource) { 990 info->playBuffer->Stop(); 991 } else { 992 info->captureBuffer->Stop(); 993 } 994 995 TRACE0("< DAUDIO_Stop\n"); 996 return TRUE; 997} 998 999 1000void DAUDIO_Close(void* id, int isSource) { 1001 DS_Info* info = (DS_Info*) id; 1002 1003 TRACE0("DAUDIO_Close\n"); 1004 1005 if (info != NULL) { 1006 DS_destroySoundBuffer(info); 1007 DS_removeDeviceRef(info->deviceID); 1008 free(info); 1009 } 1010} 1011 1012/* Check buffer for underrun 1013 * This method is only meaningful for Output devices (write devices). 1014 */ 1015void DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) { 1016 TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, " 1017 "info->writePos=%d silencedBytes=%d dsBufferSizeInBytes=%d\n", 1018 (int) playCursor, (int) writeCursor, (int) info->writePos, 1019 (int) info->silencedBytes, (int) info->dsBufferSizeInBytes); 1020 if (info->underrun || info->writePos < 0) return; 1021 int writeAhead = DS_getDistance(info, writeCursor, info->writePos); 1022 if (writeAhead > info->bufferSizeInBytes) { 1023 // this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes) 1024 // But the case can occur only when we have more then info->bufferSizeInBytes valid bytes 1025 // (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes) 1026 // If we already have a lot of silencedBytes after valid data (written by 1027 // DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun 1028 if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) { 1029 // underrun! 1030 ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n"); 1031 info->underrun = TRUE; 1032 } 1033 } 1034} 1035 1036/* For source (playback) line: 1037 * (a) if (fromPlayCursor == FALSE), returns number of bytes available 1038 * for writing: bufferSize - (info->writePos - writeCursor); 1039 * (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor 1040 * and returned value can be used for play position calculation (see also 1041 * note about bufferSize) 1042 * For destination (capture) line: 1043 * (c) if (fromPlayCursor == FALSE), returns number of bytes available 1044 * for reading from the buffer: readCursor - info->writePos; 1045 * (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor 1046 * and returned value can be used for capture position calculation (see 1047 * note about bufferSize) 1048 * bufferSize parameter are filled by "actual" buffer size: 1049 * if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes 1050 * otherwise it increase by number of bytes currently processed by DirectSound 1051 * (writeCursor - playCursor) or (captureCursor - readCursor) 1052 */ 1053int DS_GetAvailable(DS_Info* info, 1054 DWORD* playCursor, DWORD* writeCursor, 1055 int* bufferSize, BOOL fromPlayCursor) { 1056 int available; 1057 int newReadPos; 1058 1059 TRACE2("DS_GetAvailable: fromPlayCursor=%d, deviceID=%d\n", fromPlayCursor, info->deviceID); 1060 if (!info->playBuffer && !info->captureBuffer) { 1061 ERROR0("DS_GetAvailable: ERROR: buffer not yet created"); 1062 return 0; 1063 } 1064 1065 if (info->isSource) { 1066 if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) { 1067 ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n"); 1068 return 0; 1069 } 1070 int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor); 1071 // workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor 1072 if (processing > info->dsBufferSizeInBytes / 2) { 1073 *writeCursor = *playCursor; 1074 processing = 0; 1075 } 1076 TRACE3(" playCursor=%d, writeCursor=%d, info->writePos=%d\n", 1077 *playCursor, *writeCursor, info->writePos); 1078 *bufferSize = info->bufferSizeInBytes; 1079 if (fromPlayCursor) { 1080 *bufferSize += processing; 1081 } 1082 DS_CheckUnderrun(info, *playCursor, *writeCursor); 1083 if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) { 1084 /* always full buffer if at beginning */ 1085 available = *bufferSize; 1086 } else { 1087 int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor, info->writePos); 1088 if (currWriteAhead > *bufferSize) { 1089 if (info->underrun) { 1090 // playCursor surpassed writePos - no valid data, whole buffer available 1091 available = *bufferSize; 1092 } else { 1093 // the case may occur after stop(), when writeCursor jumps back to playCursor 1094 // so "actual" buffer size has grown 1095 *bufferSize = currWriteAhead; 1096 available = 0; 1097 } 1098 } else { 1099 available = *bufferSize - currWriteAhead; 1100 } 1101 } 1102 } else { 1103 if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) { 1104 ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n"); 1105 return 0; 1106 } 1107 *bufferSize = info->bufferSizeInBytes; 1108 if (fromPlayCursor) { 1109 *bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor); 1110 } 1111 TRACE4(" captureCursor=%d, readCursor=%d, info->readPos=%d refBufferSize=%d\n", 1112 *playCursor, *writeCursor, info->writePos, *bufferSize); 1113 if (info->writePos == -1) { 1114 /* always empty buffer if at beginning */ 1115 info->writePos = (int) (*writeCursor); 1116 } 1117 if (fromPlayCursor) { 1118 available = ((int) (*playCursor) - info->writePos); 1119 } else { 1120 available = ((int) (*writeCursor) - info->writePos); 1121 } 1122 if (available < 0) { 1123 available += info->dsBufferSizeInBytes; 1124 } 1125 if (!fromPlayCursor && available > info->bufferSizeInBytes) { 1126 /* overflow */ 1127 ERROR2("DS_GetAvailable: ERROR: overflow detected: " 1128 "DirectSoundBufferSize=%d, bufferSize=%d, ", 1129 info->dsBufferSizeInBytes, info->bufferSizeInBytes); 1130 ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n", 1131 *playCursor, *writeCursor, info->writePos); 1132 /* advance read position, to allow exactly one buffer worth of data */ 1133 newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes; 1134 if (newReadPos < 0) { 1135 newReadPos += info->dsBufferSizeInBytes; 1136 } 1137 info->writePos = newReadPos; 1138 available = info->bufferSizeInBytes; 1139 } 1140 } 1141 available = (available / info->frameSize) * info->frameSize; 1142 1143 TRACE1("DS_available: Returning %d available bytes\n", (int) available); 1144 return available; 1145} 1146 1147// returns -1 on error, otherwise bytes written 1148int DAUDIO_Write(void* id, char* data, int byteSize) { 1149 DS_Info* info = (DS_Info*) id; 1150 int available; 1151 int thisWritePos; 1152 DWORD playCursor, writeCursor; 1153 HRESULT res; 1154 void* buffer1, *buffer2; 1155 DWORD buffer1len, buffer2len; 1156 BOOL needRestart = FALSE; 1157 int bufferLostTrials = 2; 1158 int bufferSize; 1159 1160 TRACE1("> DAUDIO_Write %d bytes\n", byteSize); 1161 1162 while (--bufferLostTrials > 0) { 1163 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */); 1164 if (byteSize > available) byteSize = available; 1165 if (byteSize == 0) break; 1166 thisWritePos = info->writePos; 1167 if (thisWritePos == -1 || info->underrun) { 1168 // play from current write cursor after flush, etc. 1169 needRestart = TRUE; 1170 thisWritePos = writeCursor; 1171 info->underrun = FALSE; 1172 } 1173 DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize); 1174 res = info->playBuffer->Lock(thisWritePos, byteSize, 1175 (LPVOID *) &buffer1, &buffer1len, 1176 (LPVOID *) &buffer2, &buffer2len, 1177 0); 1178 if (res != DS_OK) { 1179 /* some DS failure */ 1180 if (res == DSERR_BUFFERLOST) { 1181 ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer."); 1182 if (info->playBuffer->Restore() == DS_OK) { 1183 DS_clearBuffer(info, FALSE /* entire buffer */); 1184 info->writePos = -1; 1185 /* try again */ 1186 continue; 1187 } 1188 } 1189 /* can't recover from error */ 1190 byteSize = 0; 1191 break; 1192 } 1193 /* buffer could be locked successfully */ 1194 /* first fill first buffer */ 1195 if (buffer1) { 1196 memcpy(buffer1, data, buffer1len); 1197 data = (char*) (((UINT_PTR) data) + buffer1len); 1198 } else buffer1len = 0; 1199 if (buffer2) { 1200 memcpy(buffer2, data, buffer2len); 1201 } else buffer2len = 0; 1202 byteSize = buffer1len + buffer2len; 1203 1204 /* update next write pos */ 1205 thisWritePos += byteSize; 1206 while (thisWritePos >= info->dsBufferSizeInBytes) { 1207 thisWritePos -= info->dsBufferSizeInBytes; 1208 } 1209 /* commit data to directsound */ 1210 info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len); 1211 1212 info->writePos = thisWritePos; 1213 1214 /* update position 1215 * must be AFTER updating writePos, 1216 * so that getSvailable doesn't return too little, 1217 * so that getFramePos doesn't jump 1218 */ 1219 info->framePos += (byteSize / info->frameSize); 1220 1221 /* decrease silenced bytes */ 1222 if (info->silencedBytes > byteSize) { 1223 info->silencedBytes -= byteSize; 1224 } else { 1225 info->silencedBytes = 0; 1226 } 1227 break; 1228 } /* while */ 1229 1230 /* start the device, if necessary */ 1231 if (info->started && needRestart && (info->writePos >= 0)) { 1232 DS_StartBufferHelper::StartBuffer(info); 1233 } 1234 1235 TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize); 1236 return byteSize; 1237} 1238 1239// returns -1 on error 1240int DAUDIO_Read(void* id, char* data, int byteSize) { 1241 DS_Info* info = (DS_Info*) id; 1242 int available; 1243 int thisReadPos; 1244 DWORD captureCursor, readCursor; 1245 HRESULT res; 1246 void* buffer1, *buffer2; 1247 DWORD buffer1len, buffer2len; 1248 int bufferSize; 1249 1250 TRACE1("> DAUDIO_Read %d bytes\n", byteSize); 1251 1252 available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */); 1253 if (byteSize > available) byteSize = available; 1254 if (byteSize > 0) { 1255 thisReadPos = info->writePos; 1256 if (thisReadPos == -1) { 1257 /* from beginning */ 1258 thisReadPos = 0; 1259 } 1260 res = info->captureBuffer->Lock(thisReadPos, byteSize, 1261 (LPVOID *) &buffer1, &buffer1len, 1262 (LPVOID *) &buffer2, &buffer2len, 1263 0); 1264 if (res != DS_OK) { 1265 /* can't recover from error */ 1266 byteSize = 0; 1267 } else { 1268 /* buffer could be locked successfully */ 1269 /* first fill first buffer */ 1270 if (buffer1) { 1271 memcpy(data, buffer1, buffer1len); 1272 data = (char*) (((UINT_PTR) data) + buffer1len); 1273 } else buffer1len = 0; 1274 if (buffer2) { 1275 memcpy(data, buffer2, buffer2len); 1276 } else buffer2len = 0; 1277 byteSize = buffer1len + buffer2len; 1278 1279 /* update next read pos */ 1280 thisReadPos = DS_addPos(info, thisReadPos, byteSize); 1281 /* commit data to directsound */ 1282 info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len); 1283 1284 /* update position 1285 * must be BEFORE updating readPos, 1286 * so that getAvailable doesn't return too much, 1287 * so that getFramePos doesn't jump 1288 */ 1289 info->framePos += (byteSize / info->frameSize); 1290 1291 info->writePos = thisReadPos; 1292 } 1293 } 1294 1295 TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize); 1296 return byteSize; 1297} 1298 1299 1300int DAUDIO_GetBufferSize(void* id, int isSource) { 1301 DS_Info* info = (DS_Info*) id; 1302 return info->bufferSizeInBytes; 1303} 1304 1305int DAUDIO_StillDraining(void* id, int isSource) { 1306 DS_Info* info = (DS_Info*) id; 1307 BOOL draining = FALSE; 1308 int available, bufferSize; 1309 DWORD playCursor, writeCursor; 1310 1311 DS_clearBuffer(info, TRUE /* from write position */); 1312 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */); 1313 draining = (available < bufferSize); 1314 1315 TRACE3("DAUDIO_StillDraining: available=%d silencedBytes=%d Still draining: %s\n", 1316 available, info->silencedBytes, draining?"TRUE":"FALSE"); 1317 return draining; 1318} 1319 1320 1321int DAUDIO_Flush(void* id, int isSource) { 1322 DS_Info* info = (DS_Info*) id; 1323 1324 TRACE0("DAUDIO_Flush\n"); 1325 1326 if (info->isSource) { 1327 info->playBuffer->Stop(); 1328 DS_clearBuffer(info, FALSE /* entire buffer */); 1329 } else { 1330 DWORD captureCursor, readCursor; 1331 /* set the read pointer to the current read position */ 1332 if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) { 1333 ERROR0("DAUDIO_Flush: ERROR: Failed to get current position."); 1334 return FALSE; 1335 } 1336 DS_clearBuffer(info, FALSE /* entire buffer */); 1337 /* SHOULD set to *captureCursor*, 1338 * but that would be detected as overflow 1339 * in a subsequent GetAvailable() call. 1340 */ 1341 info->writePos = (int) readCursor; 1342 } 1343 return TRUE; 1344} 1345 1346int DAUDIO_GetAvailable(void* id, int isSource) { 1347 DS_Info* info = (DS_Info*) id; 1348 DWORD playCursor, writeCursor; 1349 int ret, bufferSize; 1350 1351 ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE); 1352 1353 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret); 1354 return ret; 1355} 1356 1357INT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) { 1358 // estimate the current position with the buffer size and 1359 // the available bytes to read or write in the buffer. 1360 // not an elegant solution - bytePos will stop on xruns, 1361 // and in race conditions it may jump backwards 1362 // Advantage is that it is indeed based on the samples that go through 1363 // the system (rather than time-based methods) 1364 if (info->isSource) { 1365 // javaBytePos is the position that is reached when the current 1366 // buffer is played completely 1367 return (INT64) (javaBytePos - bufferSize + availInBytes); 1368 } else { 1369 // javaBytePos is the position that was when the current buffer was empty 1370 return (INT64) (javaBytePos + availInBytes); 1371 } 1372} 1373 1374INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) { 1375 DS_Info* info = (DS_Info*) id; 1376 int available, bufferSize; 1377 DWORD playCursor, writeCursor; 1378 INT64 result = javaBytePos; 1379 1380 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE); 1381 result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available); 1382 return result; 1383} 1384 1385 1386void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) { 1387 /* save to ignore, since GetBytePosition 1388 * takes the javaBytePos param into account 1389 */ 1390} 1391 1392int DAUDIO_RequiresServicing(void* id, int isSource) { 1393 // need servicing on for SourceDataLines 1394 return isSource?TRUE:FALSE; 1395} 1396 1397void DAUDIO_Service(void* id, int isSource) { 1398 DS_Info* info = (DS_Info*) id; 1399 if (isSource) { 1400 if (info->silencedBytes < info->dsBufferSizeInBytes) { 1401 // clear buffer 1402 TRACE0("DAUDIO_Service\n"); 1403 DS_clearBuffer(info, TRUE /* from write position */); 1404 } 1405 if (info->writePos >= 0 1406 && info->started 1407 && !info->underrun 1408 && info->silencedBytes >= info->dsBufferSizeInBytes) { 1409 // if we're currently playing, and the entire buffer is silenced... 1410 // then we are underrunning! 1411 info->underrun = TRUE; 1412 ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n"); 1413 } 1414 } 1415} 1416 1417 1418#endif // USE_DAUDIO 1419