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