|
@@ -24,6 +24,7 @@
|
|
|
|
|
|
/* Allow access to a raw mixing buffer */
|
|
|
|
|
|
+#include "SDL_assert.h"
|
|
|
#include "SDL_timer.h"
|
|
|
#include "SDL_loadso.h"
|
|
|
#include "SDL_audio.h"
|
|
@@ -36,11 +37,13 @@
|
|
|
|
|
|
/* DirectX function pointers for audio */
|
|
|
static void* DSoundDLL = NULL;
|
|
|
-typedef HRESULT(WINAPI*fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
|
|
|
-typedef HRESULT(WINAPI*fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
|
|
-typedef HRESULT(WINAPI*fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
|
|
|
+typedef HRESULT (WINAPI *fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
|
|
|
+typedef HRESULT (WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
|
|
+typedef HRESULT (WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID,LPDIRECTSOUNDCAPTURE8 *,LPUNKNOWN);
|
|
|
+typedef HRESULT (WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
|
|
|
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
|
|
|
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
|
|
|
+static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
|
|
|
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
|
|
|
|
|
|
static void
|
|
@@ -48,6 +51,7 @@ DSOUND_Unload(void)
|
|
|
{
|
|
|
pDirectSoundCreate8 = NULL;
|
|
|
pDirectSoundEnumerateW = NULL;
|
|
|
+ pDirectSoundCaptureCreate8 = NULL;
|
|
|
pDirectSoundCaptureEnumerateW = NULL;
|
|
|
|
|
|
if (DSoundDLL != NULL) {
|
|
@@ -76,6 +80,7 @@ DSOUND_Load(void)
|
|
|
loaded = 1; /* will reset if necessary. */
|
|
|
DSOUNDLOAD(DirectSoundCreate8);
|
|
|
DSOUNDLOAD(DirectSoundEnumerateW);
|
|
|
+ DSOUNDLOAD(DirectSoundCaptureCreate8);
|
|
|
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
|
|
|
#undef DSOUNDLOAD
|
|
|
|
|
@@ -197,7 +202,7 @@ DSOUND_WaitDevice(_THIS)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- while ((cursor / this->hidden->mixlen) == this->hidden->lastchunk) {
|
|
|
+ while ((cursor / this->spec.size) == this->hidden->lastchunk) {
|
|
|
/* FIXME: find out how much time is left and sleep that long */
|
|
|
SDL_Delay(1);
|
|
|
|
|
@@ -239,9 +244,8 @@ DSOUND_PlayDevice(_THIS)
|
|
|
if (this->hidden->locked_buf) {
|
|
|
IDirectSoundBuffer_Unlock(this->hidden->mixbuf,
|
|
|
this->hidden->locked_buf,
|
|
|
- this->hidden->mixlen, NULL, 0);
|
|
|
+ this->spec.size, NULL, 0);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
static Uint8 *
|
|
@@ -265,7 +269,7 @@ DSOUND_GetDeviceBuf(_THIS)
|
|
|
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
|
return (NULL);
|
|
|
}
|
|
|
- cursor /= this->hidden->mixlen;
|
|
|
+ cursor /= this->spec.size;
|
|
|
#ifdef DEBUG_SOUND
|
|
|
/* Detect audio dropouts */
|
|
|
{
|
|
@@ -281,17 +285,17 @@ DSOUND_GetDeviceBuf(_THIS)
|
|
|
#endif
|
|
|
this->hidden->lastchunk = cursor;
|
|
|
cursor = (cursor + 1) % this->hidden->num_buffers;
|
|
|
- cursor *= this->hidden->mixlen;
|
|
|
+ cursor *= this->spec.size;
|
|
|
|
|
|
/* Lock the audio buffer */
|
|
|
result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
|
|
|
- this->hidden->mixlen,
|
|
|
+ this->spec.size,
|
|
|
(LPVOID *) & this->hidden->locked_buf,
|
|
|
&rawlen, NULL, &junk, 0);
|
|
|
if (result == DSERR_BUFFERLOST) {
|
|
|
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
|
|
result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
|
|
|
- this->hidden->mixlen,
|
|
|
+ this->spec.size,
|
|
|
(LPVOID *) & this->
|
|
|
hidden->locked_buf, &rawlen, NULL,
|
|
|
&junk, 0);
|
|
@@ -310,7 +314,7 @@ DSOUND_WaitDone(_THIS)
|
|
|
|
|
|
/* Wait for the playing chunk to finish */
|
|
|
if (stream != NULL) {
|
|
|
- SDL_memset(stream, this->spec.silence, this->hidden->mixlen);
|
|
|
+ SDL_memset(stream, this->spec.silence, this->spec.size);
|
|
|
DSOUND_PlayDevice(this);
|
|
|
}
|
|
|
DSOUND_WaitDevice(this);
|
|
@@ -319,83 +323,106 @@ DSOUND_WaitDone(_THIS)
|
|
|
IDirectSoundBuffer_Stop(this->hidden->mixbuf);
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
|
|
+{
|
|
|
+ struct SDL_PrivateAudioData *h = this->hidden;
|
|
|
+ DWORD junk, cursor, ptr1len, ptr2len;
|
|
|
+ VOID *ptr1, *ptr2;
|
|
|
+
|
|
|
+ SDL_assert(buflen == this->spec.size);
|
|
|
+
|
|
|
+ while (SDL_TRUE) {
|
|
|
+ if (SDL_AtomicGet(&this->shutdown)) { /* in case the buffer froze... */
|
|
|
+ SDL_memset(buffer, this->spec.silence, buflen);
|
|
|
+ return buflen;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if ((cursor / this->spec.size) == h->lastchunk) {
|
|
|
+ SDL_Delay(1); /* FIXME: find out how much time is left and sleep that long */
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * this->spec.size, this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ SDL_assert(ptr1len == this->spec.size);
|
|
|
+ SDL_assert(ptr2 == NULL);
|
|
|
+ SDL_assert(ptr2len == 0);
|
|
|
+
|
|
|
+ SDL_memcpy(buffer, ptr1, ptr1len);
|
|
|
+
|
|
|
+ if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
|
|
|
+
|
|
|
+ return ptr1len;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+DSOUND_FlushCapture(_THIS)
|
|
|
+{
|
|
|
+ struct SDL_PrivateAudioData *h = this->hidden;
|
|
|
+ DWORD junk, cursor;
|
|
|
+ if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
|
|
|
+ h->lastchunk = cursor / this->spec.size;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
DSOUND_CloseDevice(_THIS)
|
|
|
{
|
|
|
if (this->hidden->mixbuf != NULL) {
|
|
|
+ IDirectSoundBuffer_Stop(this->hidden->mixbuf);
|
|
|
IDirectSoundBuffer_Release(this->hidden->mixbuf);
|
|
|
}
|
|
|
if (this->hidden->sound != NULL) {
|
|
|
IDirectSound_Release(this->hidden->sound);
|
|
|
}
|
|
|
+ if (this->hidden->capturebuf != NULL) {
|
|
|
+ IDirectSoundCaptureBuffer_Stop(this->hidden->capturebuf);
|
|
|
+ IDirectSoundCaptureBuffer_Release(this->hidden->capturebuf);
|
|
|
+ }
|
|
|
+ if (this->hidden->capture != NULL) {
|
|
|
+ IDirectSoundCapture_Release(this->hidden->capture);
|
|
|
+ }
|
|
|
SDL_free(this->hidden);
|
|
|
}
|
|
|
|
|
|
/* This function tries to create a secondary audio buffer, and returns the
|
|
|
- number of audio chunks available in the created buffer.
|
|
|
+ number of audio chunks available in the created buffer. This is for
|
|
|
+ playback devices, not capture.
|
|
|
*/
|
|
|
static int
|
|
|
-CreateSecondary(_THIS, HWND focus)
|
|
|
+CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
|
|
{
|
|
|
LPDIRECTSOUND sndObj = this->hidden->sound;
|
|
|
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
|
|
|
- Uint32 chunksize = this->spec.size;
|
|
|
- const int numchunks = 8;
|
|
|
HRESULT result = DS_OK;
|
|
|
DSBUFFERDESC format;
|
|
|
LPVOID pvAudioPtr1, pvAudioPtr2;
|
|
|
DWORD dwAudioBytes1, dwAudioBytes2;
|
|
|
- WAVEFORMATEX wfmt;
|
|
|
-
|
|
|
- SDL_zero(wfmt);
|
|
|
-
|
|
|
- if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
|
|
|
- wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
|
|
- } else {
|
|
|
- wfmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
- }
|
|
|
-
|
|
|
- wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
|
|
|
- wfmt.nChannels = this->spec.channels;
|
|
|
- wfmt.nSamplesPerSec = this->spec.freq;
|
|
|
- wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
|
|
|
- wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
|
|
|
-
|
|
|
- /* Try to set primary mixing privileges */
|
|
|
- if (focus) {
|
|
|
- result = IDirectSound_SetCooperativeLevel(sndObj,
|
|
|
- focus, DSSCL_PRIORITY);
|
|
|
- } else {
|
|
|
- result = IDirectSound_SetCooperativeLevel(sndObj,
|
|
|
- GetDesktopWindow(),
|
|
|
- DSSCL_NORMAL);
|
|
|
- }
|
|
|
- if (result != DS_OK) {
|
|
|
- return SetDSerror("DirectSound SetCooperativeLevel", result);
|
|
|
- }
|
|
|
|
|
|
/* Try to create the secondary buffer */
|
|
|
SDL_zero(format);
|
|
|
format.dwSize = sizeof(format);
|
|
|
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
|
|
- if (!focus) {
|
|
|
- format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
|
|
- } else {
|
|
|
- format.dwFlags |= DSBCAPS_STICKYFOCUS;
|
|
|
- }
|
|
|
- format.dwBufferBytes = numchunks * chunksize;
|
|
|
- if ((format.dwBufferBytes < DSBSIZE_MIN) ||
|
|
|
- (format.dwBufferBytes > DSBSIZE_MAX)) {
|
|
|
- return SDL_SetError("Sound buffer size must be between %d and %d",
|
|
|
- DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
|
|
|
- }
|
|
|
- format.dwReserved = 0;
|
|
|
- format.lpwfxFormat = &wfmt;
|
|
|
+ format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
|
|
+ format.dwBufferBytes = bufsize;
|
|
|
+ format.lpwfxFormat = wfmt;
|
|
|
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
|
|
if (result != DS_OK) {
|
|
|
return SetDSerror("DirectSound CreateSoundBuffer", result);
|
|
|
}
|
|
|
- IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);
|
|
|
+ IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
|
|
|
|
|
|
/* Silence the initial audio buffer */
|
|
|
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
|
|
@@ -410,18 +437,65 @@ CreateSecondary(_THIS, HWND focus)
|
|
|
}
|
|
|
|
|
|
/* We're ready to go */
|
|
|
- return (numchunks);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* This function tries to create a capture buffer, and returns the
|
|
|
+ number of audio chunks available in the created buffer. This is for
|
|
|
+ capture devices, not playback.
|
|
|
+*/
|
|
|
+static int
|
|
|
+CreateCaptureBuffer(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
|
|
+{
|
|
|
+ LPDIRECTSOUNDCAPTURE capture = this->hidden->capture;
|
|
|
+ LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf;
|
|
|
+ DSCBUFFERDESC format;
|
|
|
+// DWORD junk, cursor;
|
|
|
+ HRESULT result;
|
|
|
+
|
|
|
+ SDL_zero(format);
|
|
|
+ format.dwSize = sizeof (format);
|
|
|
+ format.dwFlags = DSCBCAPS_WAVEMAPPED;
|
|
|
+ format.dwBufferBytes = bufsize;
|
|
|
+ format.lpwfxFormat = wfmt;
|
|
|
+
|
|
|
+ result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ return SetDSerror("DirectSound CreateCaptureBuffer", result);
|
|
|
+ }
|
|
|
+
|
|
|
+ result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ IDirectSoundCaptureBuffer_Release(*capturebuf);
|
|
|
+ return SetDSerror("DirectSound Start", result);
|
|
|
+ }
|
|
|
+
|
|
|
+#if 0
|
|
|
+ /* presumably this starts at zero, but just in case... */
|
|
|
+ result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ IDirectSoundCaptureBuffer_Stop(*capturebuf);
|
|
|
+ IDirectSoundCaptureBuffer_Release(*capturebuf);
|
|
|
+ return SetDSerror("DirectSound GetCurrentPosition", result);
|
|
|
+ }
|
|
|
+
|
|
|
+ this->hidden->lastchunk = cursor / this->spec.size;
|
|
|
+#endif
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
{
|
|
|
+ const DWORD numchunks = 8;
|
|
|
HRESULT result;
|
|
|
SDL_bool valid_format = SDL_FALSE;
|
|
|
SDL_bool tried_format = SDL_FALSE;
|
|
|
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
|
|
|
LPGUID guid = (LPGUID) handle;
|
|
|
-
|
|
|
+ DWORD bufsize;
|
|
|
+
|
|
|
/* Initialize all variables that we clean on shutdown */
|
|
|
this->hidden = (struct SDL_PrivateAudioData *)
|
|
|
SDL_malloc((sizeof *this->hidden));
|
|
@@ -431,9 +505,22 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
SDL_zerop(this->hidden);
|
|
|
|
|
|
/* Open the audio device */
|
|
|
- result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
|
|
|
- if (result != DS_OK) {
|
|
|
- return SetDSerror("DirectSoundCreate", result);
|
|
|
+ if (iscapture) {
|
|
|
+ result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ return SetDSerror("DirectSoundCaptureCreate8", result);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ return SetDSerror("DirectSoundCreate8", result);
|
|
|
+ }
|
|
|
+ result = IDirectSound_SetCooperativeLevel(this->hidden->sound,
|
|
|
+ GetDesktopWindow(),
|
|
|
+ DSSCL_NORMAL);
|
|
|
+ if (result != DS_OK) {
|
|
|
+ return SetDSerror("DirectSound SetCooperativeLevel", result);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
while ((!valid_format) && (test_format)) {
|
|
@@ -443,12 +530,38 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
case AUDIO_S32:
|
|
|
case AUDIO_F32:
|
|
|
tried_format = SDL_TRUE;
|
|
|
+
|
|
|
this->spec.format = test_format;
|
|
|
+
|
|
|
/* Update the fragment size as size in bytes */
|
|
|
SDL_CalculateAudioSpec(&this->spec);
|
|
|
- this->hidden->num_buffers = CreateSecondary(this, NULL);
|
|
|
- if (this->hidden->num_buffers > 0) {
|
|
|
- valid_format = SDL_TRUE;
|
|
|
+
|
|
|
+ bufsize = numchunks * this->spec.size;
|
|
|
+ if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
|
|
|
+ SDL_SetError("Sound buffer size must be between %d and %d",
|
|
|
+ (DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks,
|
|
|
+ DSBSIZE_MAX / numchunks);
|
|
|
+ } else {
|
|
|
+ int rc;
|
|
|
+ WAVEFORMATEX wfmt;
|
|
|
+ SDL_zero(wfmt);
|
|
|
+ if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
|
|
|
+ wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
|
|
+ } else {
|
|
|
+ wfmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
+ }
|
|
|
+
|
|
|
+ wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
|
|
|
+ wfmt.nChannels = this->spec.channels;
|
|
|
+ wfmt.nSamplesPerSec = this->spec.freq;
|
|
|
+ wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
|
|
|
+ wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
|
|
|
+
|
|
|
+ rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt);
|
|
|
+ if (rc == 0) {
|
|
|
+ this->hidden->num_buffers = numchunks;
|
|
|
+ valid_format = SDL_TRUE;
|
|
|
+ }
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
@@ -462,8 +575,7 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
return SDL_SetError("DirectSound: Unsupported audio format");
|
|
|
}
|
|
|
|
|
|
- /* The buffer will auto-start playing in DSOUND_WaitDevice() */
|
|
|
- this->hidden->mixlen = this->spec.size;
|
|
|
+ /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
|
|
|
|
|
|
return 0; /* good to go. */
|
|
|
}
|
|
@@ -490,11 +602,14 @@ DSOUND_Init(SDL_AudioDriverImpl * impl)
|
|
|
impl->WaitDevice = DSOUND_WaitDevice;
|
|
|
impl->WaitDone = DSOUND_WaitDone;
|
|
|
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
|
|
|
+ impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
|
|
|
+ impl->FlushCapture = DSOUND_FlushCapture;
|
|
|
impl->CloseDevice = DSOUND_CloseDevice;
|
|
|
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
|
|
|
-
|
|
|
impl->Deinitialize = DSOUND_Deinitialize;
|
|
|
|
|
|
+ impl->HasCaptureSupport = SDL_TRUE;
|
|
|
+
|
|
|
return 1; /* this audio target is available. */
|
|
|
}
|
|
|
|