blob: b60ff058641203592de3531bb4ba46fbd928d0af [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 while (1) {
480 // wait for something to do
481 ::WaitForSingleObject(data.startEvent, INFINITE);
482 if (data.line2Start == NULL) {
483 // (data.line2Start == NULL) is a signal to terminate thread
484 break;
485 }
486 if (data.line2Start->isSource) {
487 data.startResult =
488 data.line2Start->playBuffer->Play(0, 0, DSCBSTART_LOOPING);
489 } else {
490 data.startResult =
491 data.line2Start->captureBuffer->Start(DSCBSTART_LOOPING);
492 }
493 ::SetEvent(data.startedEvent);
494 }
495 return 0;
496}
497
498HRESULT DS_StartBufferHelper::StartBuffer(DS_Info* info) {
499 HRESULT hr;
500 ::EnterCriticalSection(&data.crit_sect);
501 if (!isInitialized()) {
502 ::LeaveCriticalSection(&data.crit_sect);
503 return E_FAIL;
504 }
505 data.line2Start = info;
506 ::SetEvent(data.startEvent);
507 ::WaitForSingleObject(data.startedEvent, INFINITE);
508 hr = data.startResult;
509 ::LeaveCriticalSection(&data.crit_sect);
510 return hr;
511}
512
513
514/* helper routines for DS buffer positions */
515/* returns distance from pos1 to pos2
516 */
517inline int DS_getDistance(DS_Info* info, int pos1, int pos2) {
518 int distance = pos2 - pos1;
519 while (distance < 0)
520 distance += info->dsBufferSizeInBytes;
521 return distance;
522}
523
524/* adds 2 positions
525 */
526inline int DS_addPos(DS_Info* info, int pos1, int pos2) {
527 int result = pos1 + pos2;
528 while (result >= info->dsBufferSizeInBytes)
529 result -= info->dsBufferSizeInBytes;
530 return result;
531}
532
533
534BOOL DS_addDeviceRef(INT32 deviceID) {
535 HWND ownerWindow;
536 HRESULT res = DS_OK;
537 LPDIRECTSOUND devPlay;
538 LPDIRECTSOUNDCAPTURE devCapture;
539 LPGUID lpGuid = NULL;
540
541
542 if (g_audioDeviceCache[deviceID].dev == NULL) {
543 /* Create DirectSound */
544 TRACE1("Creating DirectSound object for device %d\n", deviceID);
545 lpGuid = &(g_audioDeviceCache[deviceID].guid);
546 if (isEqualGUID(lpGuid, NULL)) {
547 lpGuid = NULL;
548 }
549 if (g_audioDeviceCache[deviceID].isSource) {
550 res = DirectSoundCreate(lpGuid, &devPlay, NULL);
551 g_audioDeviceCache[deviceID].dev = (void*) devPlay;
552 } else {
553 res = DirectSoundCaptureCreate(lpGuid, &devCapture, NULL);
554 g_audioDeviceCache[deviceID].dev = (void*) devCapture;
555 }
556 g_audioDeviceCache[deviceID].refCount = 0;
557 if (FAILED(res)) {
558 ERROR1("DAUDIO_Open: ERROR: Failed to create DirectSound: %s", TranslateDSError(res));
559 g_audioDeviceCache[deviceID].dev = NULL;
560 return FALSE;
561 }
562 if (g_audioDeviceCache[deviceID].isSource) {
563 ownerWindow = GetForegroundWindow();
564 if (ownerWindow == NULL) {
565 ownerWindow = GetDesktopWindow();
566 }
567 TRACE0("DAUDIO_Open: Setting cooperative level\n");
568 res = devPlay->SetCooperativeLevel(ownerWindow, DSSCL_NORMAL);
569 if (FAILED(res)) {
570 ERROR1("DAUDIO_Open: ERROR: Failed to set cooperative level: %s", TranslateDSError(res));
571 return FALSE;
572 }
573 }
574 }
575 g_audioDeviceCache[deviceID].refCount++;
576 return TRUE;
577}
578
579#define DEV_PLAY(devID) ((LPDIRECTSOUND) g_audioDeviceCache[devID].dev)
580#define DEV_CAPTURE(devID) ((LPDIRECTSOUNDCAPTURE) g_audioDeviceCache[devID].dev)
581
582void DS_removeDeviceRef(INT32 deviceID) {
583
584 if (g_audioDeviceCache[deviceID].refCount) {
585 g_audioDeviceCache[deviceID].refCount--;
586 }
587 if (g_audioDeviceCache[deviceID].refCount == 0) {
588 if (g_audioDeviceCache[deviceID].dev != NULL) {
589 if (g_audioDeviceCache[deviceID].isSource) {
590 DEV_PLAY(deviceID)->Release();
591 } else {
592 DEV_CAPTURE(deviceID)->Release();
593 }
594 g_audioDeviceCache[deviceID].dev = NULL;
595 }
596 }
597}
598
599#ifndef _WAVEFORMATEXTENSIBLE_
600#define _WAVEFORMATEXTENSIBLE_
601typedef struct {
602 WAVEFORMATEX Format;
603 union {
604 WORD wValidBitsPerSample; /* bits of precision */
605 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
606 WORD wReserved; /* If neither applies, set to zero. */
607 } Samples;
608 DWORD dwChannelMask; /* which channels are */
609 /* present in stream */
610 GUID SubFormat;
611} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
612#endif // !_WAVEFORMATEXTENSIBLE_
613
614#if !defined(WAVE_FORMAT_EXTENSIBLE)
615#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
616#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
617
618#if !defined(DEFINE_WAVEFORMATEX_GUID)
619#define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
620#endif
621#ifndef STATIC_KSDATAFORMAT_SUBTYPE_PCM
622#define STATIC_KSDATAFORMAT_SUBTYPE_PCM\
623 DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
624#endif
625
626
627void createWaveFormat(WAVEFORMATEXTENSIBLE* format,
628 int sampleRate,
629 int channels,
630 int bits,
631 int significantBits) {
632 GUID subtypePCM = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};
633 format->Format.nSamplesPerSec = (DWORD)sampleRate;
634 format->Format.nChannels = (WORD) channels;
635 /* do not support useless padding, like 24-bit samples stored in 32-bit containers */
636 format->Format.wBitsPerSample = (WORD) ((bits + 7) & 0xFFF8);
637
638 if (channels <= 2 && bits <= 16) {
639 format->Format.wFormatTag = WAVE_FORMAT_PCM;
640 format->Format.cbSize = 0;
641 } else {
642 format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
643 format->Format.cbSize = 22;
644 format->Samples.wValidBitsPerSample = bits;
645 /* no way to specify speaker locations */
646 format->dwChannelMask = 0xFFFFFFFF;
647 format->SubFormat = subtypePCM;
648 }
649 format->Format.nBlockAlign = (WORD)((format->Format.wBitsPerSample * format->Format.nChannels) / 8);
650 format->Format.nAvgBytesPerSec = format->Format.nSamplesPerSec * format->Format.nBlockAlign;
651}
652
653/* fill buffer with silence
654 */
655void DS_clearBuffer(DS_Info* info, BOOL fromWritePos) {
656 UBYTE* pb1=NULL, *pb2=NULL;
657 DWORD cb1=0, cb2=0;
658 DWORD flags = 0;
659 int start, count;
660 TRACE1("> DS_clearBuffer for device %d\n", info->deviceID);
661 if (info->isSource) {
662 if (fromWritePos) {
663 DWORD playCursor, writeCursor;
664 int end;
665 if (FAILED(info->playBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
666 ERROR0(" DS_clearBuffer: ERROR: Failed to get current position.");
667 TRACE0("< DS_clearbuffer\n");
668 return;
669 }
670 DEBUG_SILENCING2(" DS_clearBuffer: DS playPos=%d myWritePos=%d", (int) playCursor, (int) info->writePos);
671 if (info->writePos >= 0) {
672 start = info->writePos + info->silencedBytes;
673 } else {
674 start = writeCursor + info->silencedBytes;
675 //flags |= DSBLOCK_FROMWRITECURSOR;
676 }
677 while (start >= info->dsBufferSizeInBytes) {
678 start -= info->dsBufferSizeInBytes;
679 }
680
681 // fix for bug 6251460 (REGRESSION: short sounds do not play)
682 // for unknown reason with hardware DS buffer playCursor sometimes
683 // jumps back for little interval (mostly 2-8 bytes) (writeCursor moves forward as usual)
684 // The issue happens right after start playing and for short sounds only (less then DS buffer,
685 // when whole sound written into the buffer and remaining space filled by silence)
686 // the case doesn't produce any audible aftifacts so just catch it to prevent filling
687 // whole buffer by silence.
688 if (((int)playCursor <= start && start < (int)writeCursor)
689 || (writeCursor < playCursor // buffer bound is between playCursor & writeCursor
690 && (start < (int)writeCursor || (int)playCursor <= start))) {
691 return;
692 }
693
694 count = info->dsBufferSizeInBytes - info->silencedBytes;
695 // why / 4?
696 //if (count > info->dsBufferSizeInBytes / 4) {
697 // count = info->dsBufferSizeInBytes / 4;
698 //}
699 end = start + count;
700 if ((int) playCursor < start) {
701 playCursor += (DWORD) info->dsBufferSizeInBytes;
702 }
703 if (start <= (int) playCursor && end > (int) playCursor) {
704 /* at maximum, silence until play cursor */
705 count = (int) playCursor - start;
706#ifdef USE_TRACE
707 if ((int) playCursor >= info->dsBufferSizeInBytes) playCursor -= (DWORD) info->dsBufferSizeInBytes;
708 TRACE3("\n DS_clearBuffer: Start Writing from %d, "
709 "would overwrite playCursor=%d, so reduce count to %d\n",
710 start, playCursor, count);
711#endif
712 }
713 DEBUG_SILENCING2(" clearing buffer from %d, count=%d. ", (int)start, (int) count);
714 if (count <= 0) {
715 DEBUG_SILENCING0("\n");
716 TRACE1("< DS_clearBuffer: no need to clear, silencedBytes=%d\n", info->silencedBytes);
717 return;
718 }
719 } else {
720 start = 0;
721 count = info->dsBufferSizeInBytes;
722 flags |= DSBLOCK_ENTIREBUFFER;
723 }
724 if (FAILED(info->playBuffer->Lock(start,
725 count,
726 (LPVOID*) &pb1, &cb1,
727 (LPVOID*) &pb2, &cb2, flags))) {
728 ERROR0("\n DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
729 TRACE0("< DS_clearbuffer\n");
730 return;
731 }
732 } else {
733 if (FAILED(info->captureBuffer->Lock(0,
734 info->dsBufferSizeInBytes,
735 (LPVOID*) &pb1, &cb1,
736 (LPVOID*) &pb2, &cb2, DSCBLOCK_ENTIREBUFFER))) {
737 ERROR0(" DS_clearBuffer: ERROR: Failed to lock sound buffer.\n");
738 TRACE0("< DS_clearbuffer\n");
739 return;
740 }
741 }
742 if (pb1!=NULL) {
743 memset(pb1, (info->bitsPerSample == 8)?128:0, cb1);
744 }
745 if (pb2!=NULL) {
746 memset(pb2, (info->bitsPerSample == 8)?128:0, cb2);
747 }
748 if (info->isSource) {
749 info->playBuffer->Unlock( pb1, cb1, pb2, cb2 );
750 if (!fromWritePos) {
751 /* doesn't matter where to start writing next time */
752 info->writePos = -1;
753 info->silencedBytes = info->dsBufferSizeInBytes;
754 } else {
755 info->silencedBytes += (cb1+cb2);
756 if (info->silencedBytes > info->dsBufferSizeInBytes) {
757 ERROR1(" DS_clearbuffer: ERROR: silencedBytes=%d exceeds buffer size!\n",
758 info->silencedBytes);
759 info->silencedBytes = info->dsBufferSizeInBytes;
760 }
761 }
762 DEBUG_SILENCING2(" silencedBytes=%d, my writePos=%d\n", (int)info->silencedBytes, (int)info->writePos);
763 } else {
764 info->captureBuffer->Unlock( pb1, cb1, pb2, cb2 );
765 }
766 TRACE0("< DS_clearbuffer\n");
767}
768
769/* returns pointer to buffer */
770void* DS_createSoundBuffer(DS_Info* info,
771 float sampleRate,
772 int sampleSizeInBits,
773 int channels,
774 int bufferSizeInBytes) {
775 DSBUFFERDESC dsbdesc;
776 DSCBUFFERDESC dscbdesc;
777 HRESULT res;
778 WAVEFORMATEXTENSIBLE format;
779 void* buffer;
780
781 TRACE1("Creating secondary buffer for device %d\n", info->deviceID);
782 createWaveFormat(&format,
783 (int) sampleRate,
784 channels,
785 info->frameSize / channels * 8,
786 sampleSizeInBits);
787
788 /* 2 second secondary buffer */
789 info->dsBufferSizeInBytes = 2 * ((int) sampleRate) * info->frameSize;
790
791 if (bufferSizeInBytes > info->dsBufferSizeInBytes / 2) {
792 bufferSizeInBytes = info->dsBufferSizeInBytes / 2;
793 }
794 bufferSizeInBytes = (bufferSizeInBytes / info->frameSize) * info->frameSize;
795 info->bufferSizeInBytes = bufferSizeInBytes;
796
797 if (info->isSource) {
798 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
799 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
800 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
801 | DSBCAPS_GLOBALFOCUS;
802
803 dsbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
804 dsbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
805 res = DEV_PLAY(info->deviceID)->CreateSoundBuffer
806 (&dsbdesc, (LPDIRECTSOUNDBUFFER*) &buffer, NULL);
807 } else {
808 memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
809 dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
810 dscbdesc.dwFlags = 0;
811 dscbdesc.dwBufferBytes = info->dsBufferSizeInBytes;
812 dscbdesc.lpwfxFormat = (WAVEFORMATEX*) &format;
813 res = DEV_CAPTURE(info->deviceID)->CreateCaptureBuffer
814 (&dscbdesc, (LPDIRECTSOUNDCAPTUREBUFFER*) &buffer, NULL);
815 }
816 if (FAILED(res)) {
817 ERROR1("DS_createSoundBuffer: ERROR: Failed to create sound buffer: %s", TranslateDSError(res));
818 return NULL;
819 }
820 return buffer;
821}
822
823void DS_destroySoundBuffer(DS_Info* info) {
824 if (info->playBuffer != NULL) {
825 info->playBuffer->Release();
826 info->playBuffer = NULL;
827 }
828 if (info->captureBuffer != NULL) {
829 info->captureBuffer->Release();
830 info->captureBuffer = NULL;
831 }
832}
833
834
835void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
836 int encoding, float sampleRate, int sampleSizeInBits,
837 int frameSize, int channels,
838 int isSigned, int isBigEndian, int bufferSizeInBytes) {
839
840 DS_Info* info;
841 void* buffer;
842
843 TRACE0("> DAUDIO_Open\n");
844
845 /* some sanity checks */
846 if (deviceID >= g_cacheCount) {
847 ERROR1("DAUDIO_Open: ERROR: cannot open the device with deviceID=%d!\n", deviceID);
848 return NULL;
849 }
850 if ((g_audioDeviceCache[deviceID].isSource && !isSource)
851 || (!g_audioDeviceCache[deviceID].isSource && isSource)) {
852 /* only support Playback or Capture */
853 ERROR0("DAUDIO_Open: ERROR: Cache is corrupt: cannot open the device in specified isSource mode!\n");
854 return NULL;
855 }
856 if (encoding != DAUDIO_PCM) {
857 ERROR1("DAUDIO_Open: ERROR: cannot open the device with encoding=%d!\n", encoding);
858 return NULL;
859 }
860 if (sampleSizeInBits > 8 &&
861#ifdef _LITTLE_ENDIAN
862 isBigEndian
863#else
864 !isBigEndian
865#endif
866 ) {
867 ERROR1("DAUDIO_Open: ERROR: wrong endianness: isBigEndian==%d!\n", isBigEndian);
868 return NULL;
869 }
870 if (sampleSizeInBits == 8 && isSigned) {
871 ERROR0("DAUDIO_Open: ERROR: wrong signed'ness: with 8 bits, data must be unsigned!\n");
872 return NULL;
873 }
874 if (!DS_StartBufferHelper::isInitialized()) {
875 ERROR0("DAUDIO_Open: ERROR: StartBufferHelper initialization was failed!\n");
876 return NULL;
877 }
878
879 info = (DS_Info*) malloc(sizeof(DS_Info));
880 if (!info) {
881 ERROR0("DAUDIO_Open: ERROR: Out of memory\n");
882 return NULL;
883 }
884 memset(info, 0, sizeof(DS_Info));
885
886 info->deviceID = deviceID;
887 info->isSource = isSource;
888 info->bitsPerSample = sampleSizeInBits;
889 info->frameSize = frameSize;
890 info->framePos = 0;
891 info->started = FALSE;
892 info->underrun = FALSE;
893
894 if (!DS_addDeviceRef(deviceID)) {
895 DS_removeDeviceRef(deviceID);
896 free(info);
897 return NULL;
898 }
899
900 buffer = DS_createSoundBuffer(info,
901 sampleRate,
902 sampleSizeInBits,
903 channels,
904 bufferSizeInBytes);
905 if (!buffer) {
906 DS_removeDeviceRef(deviceID);
907 free(info);
908 return NULL;
909 }
910
911 if (info->isSource) {
912 info->playBuffer = (LPDIRECTSOUNDBUFFER) buffer;
913 } else {
914 info->captureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) buffer;
915 }
916 DS_clearBuffer(info, FALSE /* entire buffer */);
917
918 /* use writepos of device */
919 if (info->isSource) {
920 info->writePos = -1;
921 } else {
922 info->writePos = 0;
923 }
924
925 TRACE0("< DAUDIO_Open: Opened device successfully.\n");
926 return (void*) info;
927}
928
929int DAUDIO_Start(void* id, int isSource) {
930 DS_Info* info = (DS_Info*) id;
931 HRESULT res = DS_OK;
932 DWORD status;
933
934 TRACE0("> DAUDIO_Start\n");
935
936 if (info->isSource) {
937 res = info->playBuffer->GetStatus(&status);
938 if (res == DS_OK) {
939 if (status & DSBSTATUS_LOOPING) {
940 ERROR0("DAUDIO_Start: ERROR: Already started!");
941 return TRUE;
942 }
943
944 /* only start buffer if already something written to it */
945 if (info->writePos >= 0) {
946 res = DS_StartBufferHelper::StartBuffer(info);
947 if (res == DSERR_BUFFERLOST) {
948 res = info->playBuffer->Restore();
949 if (res == DS_OK) {
950 DS_clearBuffer(info, FALSE /* entire buffer */);
951 /* write() will trigger actual device start */
952 }
953 } else {
954 /* make sure that we will have silence after
955 the currently valid audio data */
956 DS_clearBuffer(info, TRUE /* from write position */);
957 }
958 }
959 }
960 } else {
961 if (info->captureBuffer->GetStatus(&status) == DS_OK) {
962 if (status & DSCBSTATUS_LOOPING) {
963 ERROR0("DAUDIO_Start: ERROR: Already started!");
964 return TRUE;
965 }
966 }
967 res = DS_StartBufferHelper::StartBuffer(info);
968 }
969 if (FAILED(res)) {
970 ERROR1("DAUDIO_Start: ERROR: Failed to start: %s", TranslateDSError(res));
971 return FALSE;
972 }
973 info->started = TRUE;
974 return TRUE;
975}
976
977int DAUDIO_Stop(void* id, int isSource) {
978 DS_Info* info = (DS_Info*) id;
979
980 TRACE0("> DAUDIO_Stop\n");
981
982 info->started = FALSE;
983 if (info->isSource) {
984 info->playBuffer->Stop();
985 } else {
986 info->captureBuffer->Stop();
987 }
988
989 TRACE0("< DAUDIO_Stop\n");
990 return TRUE;
991}
992
993
994void DAUDIO_Close(void* id, int isSource) {
995 DS_Info* info = (DS_Info*) id;
996
997 TRACE0("DAUDIO_Close\n");
998
999 if (info != NULL) {
1000 DS_destroySoundBuffer(info);
1001 DS_removeDeviceRef(info->deviceID);
1002 free(info);
1003 }
1004}
1005
1006/* Check buffer for underrun
1007 * This method is only meaningful for Output devices (write devices).
1008 */
1009void DS_CheckUnderrun(DS_Info* info, DWORD playCursor, DWORD writeCursor) {
1010 TRACE5("DS_CheckUnderrun: playCursor=%d, writeCursor=%d, "
1011 "info->writePos=%d silencedBytes=%d dsBufferSizeInBytes=%d\n",
1012 (int) playCursor, (int) writeCursor, (int) info->writePos,
1013 (int) info->silencedBytes, (int) info->dsBufferSizeInBytes);
1014 if (info->underrun || info->writePos < 0) return;
1015 int writeAhead = DS_getDistance(info, writeCursor, info->writePos);
1016 if (writeAhead > info->bufferSizeInBytes) {
1017 // this may occur after Stop(), when writeCursor decreases (real valid data size > bufferSizeInBytes)
1018 // But the case can occur only when we have more then info->bufferSizeInBytes valid bytes
1019 // (and less then (info->dsBufferSizeInBytes - info->bufferSizeInBytes) silenced bytes)
1020 // If we already have a lot of silencedBytes after valid data (written by
1021 // DAUDIO_StillDraining() or DAUDIO_Service()) then it's underrun
1022 if (info->silencedBytes >= info->dsBufferSizeInBytes - info->bufferSizeInBytes) {
1023 // underrun!
1024 ERROR0("DS_CheckUnderrun: ERROR: underrun detected!\n");
1025 info->underrun = TRUE;
1026 }
1027 }
1028}
1029
1030/* For source (playback) line:
1031 * (a) if (fromPlayCursor == FALSE), returns number of bytes available
1032 * for writing: bufferSize - (info->writePos - writeCursor);
1033 * (b) if (fromPlayCursor == TRUE), playCursor is used instead writeCursor
1034 * and returned value can be used for play position calculation (see also
1035 * note about bufferSize)
1036 * For destination (capture) line:
1037 * (c) if (fromPlayCursor == FALSE), returns number of bytes available
1038 * for reading from the buffer: readCursor - info->writePos;
1039 * (d) if (fromPlayCursor == TRUE), captureCursor is used instead readCursor
1040 * and returned value can be used for capture position calculation (see
1041 * note about bufferSize)
1042 * bufferSize parameter are filled by "actual" buffer size:
1043 * if (fromPlayCursor == FALSE), bufferSize = info->bufferSizeInBytes
1044 * otherwise it increase by number of bytes currently processed by DirectSound
1045 * (writeCursor - playCursor) or (captureCursor - readCursor)
1046 */
1047int DS_GetAvailable(DS_Info* info,
1048 DWORD* playCursor, DWORD* writeCursor,
1049 int* bufferSize, BOOL fromPlayCursor) {
1050 int available;
1051 int newReadPos;
1052
1053 TRACE2("DS_GetAvailable: fromPlayCursor=%d, deviceID=%d\n", fromPlayCursor, info->deviceID);
1054 if (!info->playBuffer && !info->captureBuffer) {
1055 ERROR0("DS_GetAvailable: ERROR: buffer not yet created");
1056 return 0;
1057 }
1058
1059 if (info->isSource) {
1060 if (FAILED(info->playBuffer->GetCurrentPosition(playCursor, writeCursor))) {
1061 ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
1062 return 0;
1063 }
1064 int processing = DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
1065 // workaround: sometimes DirectSound report writeCursor is less (for several bytes) then playCursor
1066 if (processing > info->dsBufferSizeInBytes / 2) {
1067 *writeCursor = *playCursor;
1068 processing = 0;
1069 }
1070 TRACE3(" playCursor=%d, writeCursor=%d, info->writePos=%d\n",
1071 *playCursor, *writeCursor, info->writePos);
1072 *bufferSize = info->bufferSizeInBytes;
1073 if (fromPlayCursor) {
1074 *bufferSize += processing;
1075 }
1076 DS_CheckUnderrun(info, *playCursor, *writeCursor);
1077 if (info->writePos == -1 || (info->underrun && !fromPlayCursor)) {
1078 /* always full buffer if at beginning */
1079 available = *bufferSize;
1080 } else {
1081 int currWriteAhead = DS_getDistance(info, fromPlayCursor ? (int)*playCursor : (int)*writeCursor, info->writePos);
1082 if (currWriteAhead > *bufferSize) {
1083 if (info->underrun) {
1084 // playCursor surpassed writePos - no valid data, whole buffer available
1085 available = *bufferSize;
1086 } else {
1087 // the case may occur after stop(), when writeCursor jumps back to playCursor
1088 // so "actual" buffer size has grown
1089 *bufferSize = currWriteAhead;
1090 available = 0;
1091 }
1092 } else {
1093 available = *bufferSize - currWriteAhead;
1094 }
1095 }
1096 } else {
1097 if (FAILED(info->captureBuffer->GetCurrentPosition(playCursor, writeCursor))) {
1098 ERROR0("DS_GetAvailable: ERROR: Failed to get current position.\n");
1099 return 0;
1100 }
1101 *bufferSize = info->bufferSizeInBytes;
1102 if (fromPlayCursor) {
1103 *bufferSize += DS_getDistance(info, (int)*playCursor, (int)*writeCursor);
1104 }
1105 TRACE4(" captureCursor=%d, readCursor=%d, info->readPos=%d refBufferSize=%d\n",
1106 *playCursor, *writeCursor, info->writePos, *bufferSize);
1107 if (info->writePos == -1) {
1108 /* always empty buffer if at beginning */
1109 info->writePos = (int) (*writeCursor);
1110 }
1111 if (fromPlayCursor) {
1112 available = ((int) (*playCursor) - info->writePos);
1113 } else {
1114 available = ((int) (*writeCursor) - info->writePos);
1115 }
1116 if (available < 0) {
1117 available += info->dsBufferSizeInBytes;
1118 }
1119 if (!fromPlayCursor && available > info->bufferSizeInBytes) {
1120 /* overflow */
1121 ERROR2("DS_GetAvailable: ERROR: overflow detected: "
1122 "DirectSoundBufferSize=%d, bufferSize=%d, ",
1123 info->dsBufferSizeInBytes, info->bufferSizeInBytes);
1124 ERROR3("captureCursor=%d, readCursor=%d, info->readPos=%d\n",
1125 *playCursor, *writeCursor, info->writePos);
1126 /* advance read position, to allow exactly one buffer worth of data */
1127 newReadPos = (int) (*writeCursor) - info->bufferSizeInBytes;
1128 if (newReadPos < 0) {
1129 newReadPos += info->dsBufferSizeInBytes;
1130 }
1131 info->writePos = newReadPos;
1132 available = info->bufferSizeInBytes;
1133 }
1134 }
1135 available = (available / info->frameSize) * info->frameSize;
1136
1137 TRACE1("DS_available: Returning %d available bytes\n", (int) available);
1138 return available;
1139}
1140
1141// returns -1 on error, otherwise bytes written
1142int DAUDIO_Write(void* id, char* data, int byteSize) {
1143 DS_Info* info = (DS_Info*) id;
1144 int available;
1145 int thisWritePos;
1146 DWORD playCursor, writeCursor;
1147 HRESULT res;
1148 void* buffer1, *buffer2;
1149 DWORD buffer1len, buffer2len;
1150 BOOL needRestart = FALSE;
1151 int bufferLostTrials = 2;
1152 int bufferSize;
1153
1154 TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
1155
1156 while (--bufferLostTrials > 0) {
1157 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, FALSE /* fromPlayCursor */);
1158 if (byteSize > available) byteSize = available;
1159 if (byteSize == 0) break;
1160 thisWritePos = info->writePos;
1161 if (thisWritePos == -1 || info->underrun) {
1162 // play from current write cursor after flush, etc.
1163 needRestart = TRUE;
1164 thisWritePos = writeCursor;
1165 info->underrun = FALSE;
1166 }
1167 DEBUG_SILENCING2("DAUDIO_Write: writing from %d, count=%d\n", (int) thisWritePos, (int) byteSize);
1168 res = info->playBuffer->Lock(thisWritePos, byteSize,
1169 (LPVOID *) &buffer1, &buffer1len,
1170 (LPVOID *) &buffer2, &buffer2len,
1171 0);
1172 if (res != DS_OK) {
1173 /* some DS failure */
1174 if (res == DSERR_BUFFERLOST) {
1175 ERROR0("DAUDIO_write: ERROR: Restoring lost Buffer.");
1176 if (info->playBuffer->Restore() == DS_OK) {
1177 DS_clearBuffer(info, FALSE /* entire buffer */);
1178 info->writePos = -1;
1179 /* try again */
1180 continue;
1181 }
1182 }
1183 /* can't recover from error */
1184 byteSize = 0;
1185 break;
1186 }
1187 /* buffer could be locked successfully */
1188 /* first fill first buffer */
1189 if (buffer1) {
1190 memcpy(buffer1, data, buffer1len);
1191 data = (char*) (((UINT_PTR) data) + buffer1len);
1192 } else buffer1len = 0;
1193 if (buffer2) {
1194 memcpy(buffer2, data, buffer2len);
1195 } else buffer2len = 0;
1196 byteSize = buffer1len + buffer2len;
1197
1198 /* update next write pos */
1199 thisWritePos += byteSize;
1200 while (thisWritePos >= info->dsBufferSizeInBytes) {
1201 thisWritePos -= info->dsBufferSizeInBytes;
1202 }
1203 /* commit data to directsound */
1204 info->playBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
1205
1206 info->writePos = thisWritePos;
1207
1208 /* update position
1209 * must be AFTER updating writePos,
1210 * so that getSvailable doesn't return too little,
1211 * so that getFramePos doesn't jump
1212 */
1213 info->framePos += (byteSize / info->frameSize);
1214
1215 /* decrease silenced bytes */
1216 if (info->silencedBytes > byteSize) {
1217 info->silencedBytes -= byteSize;
1218 } else {
1219 info->silencedBytes = 0;
1220 }
1221 break;
1222 } /* while */
1223
1224 /* start the device, if necessary */
1225 if (info->started && needRestart && (info->writePos >= 0)) {
1226 DS_StartBufferHelper::StartBuffer(info);
1227 }
1228
1229 TRACE1("< DAUDIO_Write: returning %d bytes.\n", byteSize);
1230 return byteSize;
1231}
1232
1233// returns -1 on error
1234int DAUDIO_Read(void* id, char* data, int byteSize) {
1235 DS_Info* info = (DS_Info*) id;
1236 int available;
1237 int thisReadPos;
1238 DWORD captureCursor, readCursor;
1239 HRESULT res;
1240 void* buffer1, *buffer2;
1241 DWORD buffer1len, buffer2len;
1242 int bufferSize;
1243
1244 TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
1245
1246 available = DS_GetAvailable(info, &captureCursor, &readCursor, &bufferSize, FALSE /* fromCaptureCursor? */);
1247 if (byteSize > available) byteSize = available;
1248 if (byteSize > 0) {
1249 thisReadPos = info->writePos;
1250 if (thisReadPos == -1) {
1251 /* from beginning */
1252 thisReadPos = 0;
1253 }
1254 res = info->captureBuffer->Lock(thisReadPos, byteSize,
1255 (LPVOID *) &buffer1, &buffer1len,
1256 (LPVOID *) &buffer2, &buffer2len,
1257 0);
1258 if (res != DS_OK) {
1259 /* can't recover from error */
1260 byteSize = 0;
1261 } else {
1262 /* buffer could be locked successfully */
1263 /* first fill first buffer */
1264 if (buffer1) {
1265 memcpy(data, buffer1, buffer1len);
1266 data = (char*) (((UINT_PTR) data) + buffer1len);
1267 } else buffer1len = 0;
1268 if (buffer2) {
1269 memcpy(data, buffer2, buffer2len);
1270 } else buffer2len = 0;
1271 byteSize = buffer1len + buffer2len;
1272
1273 /* update next read pos */
1274 thisReadPos = DS_addPos(info, thisReadPos, byteSize);
1275 /* commit data to directsound */
1276 info->captureBuffer->Unlock(buffer1, buffer1len, buffer2, buffer2len);
1277
1278 /* update position
1279 * must be BEFORE updating readPos,
1280 * so that getAvailable doesn't return too much,
1281 * so that getFramePos doesn't jump
1282 */
1283 info->framePos += (byteSize / info->frameSize);
1284
1285 info->writePos = thisReadPos;
1286 }
1287 }
1288
1289 TRACE1("< DAUDIO_Read: returning %d bytes.\n", byteSize);
1290 return byteSize;
1291}
1292
1293
1294int DAUDIO_GetBufferSize(void* id, int isSource) {
1295 DS_Info* info = (DS_Info*) id;
1296 return info->bufferSizeInBytes;
1297}
1298
1299int DAUDIO_StillDraining(void* id, int isSource) {
1300 DS_Info* info = (DS_Info*) id;
1301 BOOL draining = FALSE;
1302 int available, bufferSize;
1303 DWORD playCursor, writeCursor;
1304
1305 DS_clearBuffer(info, TRUE /* from write position */);
1306 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, TRUE /* fromPlayCursor */);
1307 draining = (available < bufferSize);
1308
1309 TRACE3("DAUDIO_StillDraining: available=%d silencedBytes=%d Still draining: %s\n",
1310 available, info->silencedBytes, draining?"TRUE":"FALSE");
1311 return draining;
1312}
1313
1314
1315int DAUDIO_Flush(void* id, int isSource) {
1316 DS_Info* info = (DS_Info*) id;
1317
1318 TRACE0("DAUDIO_Flush\n");
1319
1320 if (info->isSource) {
1321 info->playBuffer->Stop();
1322 DS_clearBuffer(info, FALSE /* entire buffer */);
1323 } else {
1324 DWORD captureCursor, readCursor;
1325 /* set the read pointer to the current read position */
1326 if (FAILED(info->captureBuffer->GetCurrentPosition(&captureCursor, &readCursor))) {
1327 ERROR0("DAUDIO_Flush: ERROR: Failed to get current position.");
1328 return FALSE;
1329 }
1330 DS_clearBuffer(info, FALSE /* entire buffer */);
1331 /* SHOULD set to *captureCursor*,
1332 * but that would be detected as overflow
1333 * in a subsequent GetAvailable() call.
1334 */
1335 info->writePos = (int) readCursor;
1336 }
1337 return TRUE;
1338}
1339
1340int DAUDIO_GetAvailable(void* id, int isSource) {
1341 DS_Info* info = (DS_Info*) id;
1342 DWORD playCursor, writeCursor;
1343 int ret, bufferSize;
1344
1345 ret = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ FALSE);
1346
1347 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
1348 return ret;
1349}
1350
1351INT64 estimatePositionFromAvail(DS_Info* info, INT64 javaBytePos, int bufferSize, int availInBytes) {
1352 // estimate the current position with the buffer size and
1353 // the available bytes to read or write in the buffer.
1354 // not an elegant solution - bytePos will stop on xruns,
1355 // and in race conditions it may jump backwards
1356 // Advantage is that it is indeed based on the samples that go through
1357 // the system (rather than time-based methods)
1358 if (info->isSource) {
1359 // javaBytePos is the position that is reached when the current
1360 // buffer is played completely
1361 return (INT64) (javaBytePos - bufferSize + availInBytes);
1362 } else {
1363 // javaBytePos is the position that was when the current buffer was empty
1364 return (INT64) (javaBytePos + availInBytes);
1365 }
1366}
1367
1368INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
1369 DS_Info* info = (DS_Info*) id;
1370 int available, bufferSize;
1371 DWORD playCursor, writeCursor;
1372 INT64 result = javaBytePos;
1373
1374 available = DS_GetAvailable(info, &playCursor, &writeCursor, &bufferSize, /*fromPlayCursor?*/ TRUE);
1375 result = estimatePositionFromAvail(info, javaBytePos, bufferSize, available);
1376 return result;
1377}
1378
1379
1380void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
1381 /* save to ignore, since GetBytePosition
1382 * takes the javaBytePos param into account
1383 */
1384}
1385
1386int DAUDIO_RequiresServicing(void* id, int isSource) {
1387 // need servicing on for SourceDataLines
1388 return isSource?TRUE:FALSE;
1389}
1390
1391void DAUDIO_Service(void* id, int isSource) {
1392 DS_Info* info = (DS_Info*) id;
1393 if (isSource) {
1394 if (info->silencedBytes < info->dsBufferSizeInBytes) {
1395 // clear buffer
1396 TRACE0("DAUDIO_Service\n");
1397 DS_clearBuffer(info, TRUE /* from write position */);
1398 }
1399 if (info->writePos >= 0
1400 && info->started
1401 && !info->underrun
1402 && info->silencedBytes >= info->dsBufferSizeInBytes) {
1403 // if we're currently playing, and the entire buffer is silenced...
1404 // then we are underrunning!
1405 info->underrun = TRUE;
1406 ERROR0("DAUDIO_Service: ERROR: DirectSound: underrun detected!\n");
1407 }
1408 }
1409}
1410
1411
1412#endif // USE_DAUDIO