Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_DirectSound.cpp
41149 views
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
92
BOOL DS_lockCache() {
93
/* dummy implementation for now, Java does locking */
94
return TRUE;
95
}
96
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
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
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
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
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
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
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
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
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
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 */
431
static inline BOOL isInitialized() { return data.threadHandle != NULL; }
432
protected:
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
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
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
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
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
*/
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
*/
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
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
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
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
*/
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 */
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
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
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
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
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
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
*/
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
*/
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
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
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
1303
int DAUDIO_GetBufferSize(void* id, int isSource) {
1304
DS_Info* info = (DS_Info*) id;
1305
return info->bufferSizeInBytes;
1306
}
1307
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
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
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
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
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
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
1395
int DAUDIO_RequiresServicing(void* id, int isSource) {
1396
// need servicing on for SourceDataLines
1397
return isSource?TRUE:FALSE;
1398
}
1399
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
1423