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