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