2015-06-21 15:33:46 +00:00
|
|
|
/*
|
|
|
|
Simple DirectMedia Layer
|
2016-01-02 18:10:34 +00:00
|
|
|
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
2015-06-21 15:33:46 +00:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
#include "../../SDL_internal.h"
|
|
|
|
|
|
|
|
#if SDL_AUDIO_DRIVER_DSOUND
|
|
|
|
|
|
|
|
/* Allow access to a raw mixing buffer */
|
|
|
|
|
2016-08-10 20:00:16 +00:00
|
|
|
#include "SDL_assert.h"
|
2015-06-21 15:33:46 +00:00
|
|
|
#include "SDL_timer.h"
|
|
|
|
#include "SDL_loadso.h"
|
|
|
|
#include "SDL_audio.h"
|
|
|
|
#include "../SDL_audio_c.h"
|
|
|
|
#include "SDL_directsound.h"
|
|
|
|
|
|
|
|
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
|
|
|
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* DirectX function pointers for audio */
|
|
|
|
static void* DSoundDLL = NULL;
|
2016-08-10 20:00:16 +00:00
|
|
|
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);
|
2015-06-21 15:33:46 +00:00
|
|
|
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
|
|
|
|
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
|
2016-08-10 20:00:16 +00:00
|
|
|
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
|
2015-06-21 15:33:46 +00:00
|
|
|
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_Unload(void)
|
|
|
|
{
|
|
|
|
pDirectSoundCreate8 = NULL;
|
|
|
|
pDirectSoundEnumerateW = NULL;
|
2016-08-10 20:00:16 +00:00
|
|
|
pDirectSoundCaptureCreate8 = NULL;
|
2015-06-21 15:33:46 +00:00
|
|
|
pDirectSoundCaptureEnumerateW = NULL;
|
|
|
|
|
|
|
|
if (DSoundDLL != NULL) {
|
|
|
|
SDL_UnloadObject(DSoundDLL);
|
|
|
|
DSoundDLL = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
DSOUND_Load(void)
|
|
|
|
{
|
|
|
|
int loaded = 0;
|
|
|
|
|
|
|
|
DSOUND_Unload();
|
|
|
|
|
|
|
|
DSoundDLL = SDL_LoadObject("DSOUND.DLL");
|
|
|
|
if (DSoundDLL == NULL) {
|
|
|
|
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
|
|
|
|
} else {
|
|
|
|
/* Now make sure we have DirectX 8 or better... */
|
|
|
|
#define DSOUNDLOAD(f) { \
|
|
|
|
p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \
|
|
|
|
if (!p##f) loaded = 0; \
|
|
|
|
}
|
|
|
|
loaded = 1; /* will reset if necessary. */
|
|
|
|
DSOUNDLOAD(DirectSoundCreate8);
|
|
|
|
DSOUNDLOAD(DirectSoundEnumerateW);
|
2016-08-10 20:00:16 +00:00
|
|
|
DSOUNDLOAD(DirectSoundCaptureCreate8);
|
2015-06-21 15:33:46 +00:00
|
|
|
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
|
|
|
|
#undef DSOUNDLOAD
|
|
|
|
|
|
|
|
if (!loaded) {
|
|
|
|
SDL_SetError("DirectSound: System doesn't appear to have DX8.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!loaded) {
|
|
|
|
DSOUND_Unload();
|
|
|
|
}
|
|
|
|
|
|
|
|
return loaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
SetDSerror(const char *function, int code)
|
|
|
|
{
|
|
|
|
static const char *error;
|
|
|
|
static char errbuf[1024];
|
|
|
|
|
|
|
|
errbuf[0] = 0;
|
|
|
|
switch (code) {
|
|
|
|
case E_NOINTERFACE:
|
|
|
|
error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
|
|
|
|
break;
|
|
|
|
case DSERR_ALLOCATED:
|
|
|
|
error = "Audio device in use";
|
|
|
|
break;
|
|
|
|
case DSERR_BADFORMAT:
|
|
|
|
error = "Unsupported audio format";
|
|
|
|
break;
|
|
|
|
case DSERR_BUFFERLOST:
|
|
|
|
error = "Mixing buffer was lost";
|
|
|
|
break;
|
|
|
|
case DSERR_CONTROLUNAVAIL:
|
|
|
|
error = "Control requested is not available";
|
|
|
|
break;
|
|
|
|
case DSERR_INVALIDCALL:
|
|
|
|
error = "Invalid call for the current state";
|
|
|
|
break;
|
|
|
|
case DSERR_INVALIDPARAM:
|
|
|
|
error = "Invalid parameter";
|
|
|
|
break;
|
|
|
|
case DSERR_NODRIVER:
|
|
|
|
error = "No audio device found";
|
|
|
|
break;
|
|
|
|
case DSERR_OUTOFMEMORY:
|
|
|
|
error = "Out of memory";
|
|
|
|
break;
|
|
|
|
case DSERR_PRIOLEVELNEEDED:
|
|
|
|
error = "Caller doesn't have priority";
|
|
|
|
break;
|
|
|
|
case DSERR_UNSUPPORTED:
|
|
|
|
error = "Function not supported";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SDL_snprintf(errbuf, SDL_arraysize(errbuf),
|
|
|
|
"%s: Unknown DirectSound error: 0x%x", function, code);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!errbuf[0]) {
|
|
|
|
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function,
|
|
|
|
error);
|
|
|
|
}
|
|
|
|
return SDL_SetError("%s", errbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_FreeDeviceHandle(void *handle)
|
|
|
|
{
|
|
|
|
SDL_free(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL CALLBACK
|
|
|
|
FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
|
|
|
|
{
|
|
|
|
const int iscapture = (int) ((size_t) data);
|
|
|
|
if (guid != NULL) { /* skip default device */
|
2016-08-10 19:34:24 +00:00
|
|
|
char *str = WIN_LookupAudioDeviceName(desc, guid);
|
2015-06-21 15:33:46 +00:00
|
|
|
if (str != NULL) {
|
|
|
|
LPGUID cpyguid = (LPGUID) SDL_malloc(sizeof (GUID));
|
|
|
|
SDL_memcpy(cpyguid, guid, sizeof (GUID));
|
|
|
|
SDL_AddAudioDevice(iscapture, str, cpyguid);
|
|
|
|
SDL_free(str); /* addfn() makes a copy of this string. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE; /* keep enumerating. */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_DetectDevices(void)
|
|
|
|
{
|
|
|
|
pDirectSoundCaptureEnumerateW(FindAllDevs, (void *) ((size_t) 1));
|
|
|
|
pDirectSoundEnumerateW(FindAllDevs, (void *) ((size_t) 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_WaitDevice(_THIS)
|
|
|
|
{
|
|
|
|
DWORD status = 0;
|
|
|
|
DWORD cursor = 0;
|
|
|
|
DWORD junk = 0;
|
|
|
|
HRESULT result = DS_OK;
|
|
|
|
|
|
|
|
/* Semi-busy wait, since we have no way of getting play notification
|
|
|
|
on a primary mixing buffer located in hardware (DirectX 5.0)
|
|
|
|
*/
|
|
|
|
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
|
|
|
&junk, &cursor);
|
|
|
|
if (result != DS_OK) {
|
|
|
|
if (result == DSERR_BUFFERLOST) {
|
|
|
|
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_SOUND
|
|
|
|
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-10 20:00:16 +00:00
|
|
|
while ((cursor / this->spec.size) == this->hidden->lastchunk) {
|
2015-06-21 15:33:46 +00:00
|
|
|
/* FIXME: find out how much time is left and sleep that long */
|
|
|
|
SDL_Delay(1);
|
|
|
|
|
|
|
|
/* Try to restore a lost sound buffer */
|
|
|
|
IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
|
|
|
|
if ((status & DSBSTATUS_BUFFERLOST)) {
|
|
|
|
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
|
|
|
IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
|
|
|
|
if ((status & DSBSTATUS_BUFFERLOST)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!(status & DSBSTATUS_PLAYING)) {
|
|
|
|
result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0,
|
|
|
|
DSBPLAY_LOOPING);
|
|
|
|
if (result == DS_OK) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_SOUND
|
|
|
|
SetDSerror("DirectSound Play", result);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find out where we are playing */
|
|
|
|
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
|
|
|
&junk, &cursor);
|
|
|
|
if (result != DS_OK) {
|
|
|
|
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_PlayDevice(_THIS)
|
|
|
|
{
|
|
|
|
/* Unlock the buffer, allowing it to play */
|
|
|
|
if (this->hidden->locked_buf) {
|
|
|
|
IDirectSoundBuffer_Unlock(this->hidden->mixbuf,
|
|
|
|
this->hidden->locked_buf,
|
2016-08-10 20:00:16 +00:00
|
|
|
this->spec.size, NULL, 0);
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Uint8 *
|
|
|
|
DSOUND_GetDeviceBuf(_THIS)
|
|
|
|
{
|
|
|
|
DWORD cursor = 0;
|
|
|
|
DWORD junk = 0;
|
|
|
|
HRESULT result = DS_OK;
|
|
|
|
DWORD rawlen = 0;
|
|
|
|
|
|
|
|
/* Figure out which blocks to fill next */
|
|
|
|
this->hidden->locked_buf = NULL;
|
|
|
|
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
|
|
|
&junk, &cursor);
|
|
|
|
if (result == DSERR_BUFFERLOST) {
|
|
|
|
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
|
|
|
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
|
|
|
&junk, &cursor);
|
|
|
|
}
|
|
|
|
if (result != DS_OK) {
|
|
|
|
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
|
|
return (NULL);
|
|
|
|
}
|
2016-08-10 20:00:16 +00:00
|
|
|
cursor /= this->spec.size;
|
2015-06-21 15:33:46 +00:00
|
|
|
#ifdef DEBUG_SOUND
|
|
|
|
/* Detect audio dropouts */
|
|
|
|
{
|
|
|
|
DWORD spot = cursor;
|
|
|
|
if (spot < this->hidden->lastchunk) {
|
|
|
|
spot += this->hidden->num_buffers;
|
|
|
|
}
|
|
|
|
if (spot > this->hidden->lastchunk + 1) {
|
|
|
|
fprintf(stderr, "Audio dropout, missed %d fragments\n",
|
|
|
|
(spot - (this->hidden->lastchunk + 1)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
this->hidden->lastchunk = cursor;
|
|
|
|
cursor = (cursor + 1) % this->hidden->num_buffers;
|
2016-08-10 20:00:16 +00:00
|
|
|
cursor *= this->spec.size;
|
2015-06-21 15:33:46 +00:00
|
|
|
|
|
|
|
/* Lock the audio buffer */
|
|
|
|
result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
|
2016-08-10 20:00:16 +00:00
|
|
|
this->spec.size,
|
2015-06-21 15:33:46 +00:00
|
|
|
(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,
|
2016-08-10 20:00:16 +00:00
|
|
|
this->spec.size,
|
2015-06-21 15:33:46 +00:00
|
|
|
(LPVOID *) & this->
|
|
|
|
hidden->locked_buf, &rawlen, NULL,
|
|
|
|
&junk, 0);
|
|
|
|
}
|
|
|
|
if (result != DS_OK) {
|
|
|
|
SetDSerror("DirectSound Lock", result);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return (this->hidden->locked_buf);
|
|
|
|
}
|
|
|
|
|
2016-08-10 20:00:16 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-21 15:33:46 +00:00
|
|
|
static void
|
|
|
|
DSOUND_CloseDevice(_THIS)
|
|
|
|
{
|
2016-08-05 05:44:41 +00:00
|
|
|
if (this->hidden->mixbuf != NULL) {
|
2016-08-10 20:00:16 +00:00
|
|
|
IDirectSoundBuffer_Stop(this->hidden->mixbuf);
|
2016-08-05 05:44:41 +00:00
|
|
|
IDirectSoundBuffer_Release(this->hidden->mixbuf);
|
|
|
|
}
|
|
|
|
if (this->hidden->sound != NULL) {
|
|
|
|
IDirectSound_Release(this->hidden->sound);
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
2016-08-10 20:00:16 +00:00
|
|
|
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);
|
|
|
|
}
|
2016-08-05 05:44:41 +00:00
|
|
|
SDL_free(this->hidden);
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This function tries to create a secondary audio buffer, and returns the
|
2016-08-10 20:00:16 +00:00
|
|
|
number of audio chunks available in the created buffer. This is for
|
|
|
|
playback devices, not capture.
|
2015-06-21 15:33:46 +00:00
|
|
|
*/
|
|
|
|
static int
|
2016-08-10 20:00:16 +00:00
|
|
|
CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
2015-06-21 15:33:46 +00:00
|
|
|
{
|
|
|
|
LPDIRECTSOUND sndObj = this->hidden->sound;
|
|
|
|
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
|
|
|
|
HRESULT result = DS_OK;
|
|
|
|
DSBUFFERDESC format;
|
|
|
|
LPVOID pvAudioPtr1, pvAudioPtr2;
|
|
|
|
DWORD dwAudioBytes1, dwAudioBytes2;
|
|
|
|
|
|
|
|
/* Try to create the secondary buffer */
|
|
|
|
SDL_zero(format);
|
|
|
|
format.dwSize = sizeof(format);
|
|
|
|
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
2016-08-10 20:00:16 +00:00
|
|
|
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
|
|
|
format.dwBufferBytes = bufsize;
|
|
|
|
format.lpwfxFormat = wfmt;
|
2015-06-21 15:33:46 +00:00
|
|
|
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
|
|
|
if (result != DS_OK) {
|
|
|
|
return SetDSerror("DirectSound CreateSoundBuffer", result);
|
|
|
|
}
|
2016-08-10 20:00:16 +00:00
|
|
|
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
|
2015-06-21 15:33:46 +00:00
|
|
|
|
|
|
|
/* Silence the initial audio buffer */
|
|
|
|
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
|
|
|
|
(LPVOID *) & pvAudioPtr1, &dwAudioBytes1,
|
|
|
|
(LPVOID *) & pvAudioPtr2, &dwAudioBytes2,
|
|
|
|
DSBLOCK_ENTIREBUFFER);
|
|
|
|
if (result == DS_OK) {
|
|
|
|
SDL_memset(pvAudioPtr1, this->spec.silence, dwAudioBytes1);
|
|
|
|
IDirectSoundBuffer_Unlock(*sndbuf,
|
|
|
|
(LPVOID) pvAudioPtr1, dwAudioBytes1,
|
|
|
|
(LPVOID) pvAudioPtr2, dwAudioBytes2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're ready to go */
|
2016-08-10 20:00:16 +00:00
|
|
|
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;
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
|
{
|
2016-08-10 20:00:16 +00:00
|
|
|
const DWORD numchunks = 8;
|
2015-06-21 15:33:46 +00:00
|
|
|
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;
|
2016-08-10 20:00:16 +00:00
|
|
|
DWORD bufsize;
|
|
|
|
|
2015-06-21 15:33:46 +00:00
|
|
|
/* Initialize all variables that we clean on shutdown */
|
|
|
|
this->hidden = (struct SDL_PrivateAudioData *)
|
|
|
|
SDL_malloc((sizeof *this->hidden));
|
|
|
|
if (this->hidden == NULL) {
|
|
|
|
return SDL_OutOfMemory();
|
|
|
|
}
|
2016-08-05 05:59:06 +00:00
|
|
|
SDL_zerop(this->hidden);
|
2015-06-21 15:33:46 +00:00
|
|
|
|
|
|
|
/* Open the audio device */
|
2016-08-10 20:00:16 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while ((!valid_format) && (test_format)) {
|
|
|
|
switch (test_format) {
|
|
|
|
case AUDIO_U8:
|
|
|
|
case AUDIO_S16:
|
|
|
|
case AUDIO_S32:
|
|
|
|
case AUDIO_F32:
|
|
|
|
tried_format = SDL_TRUE;
|
2016-08-10 20:00:16 +00:00
|
|
|
|
2015-06-21 15:33:46 +00:00
|
|
|
this->spec.format = test_format;
|
2016-08-10 20:00:16 +00:00
|
|
|
|
2016-08-09 23:35:46 +00:00
|
|
|
/* Update the fragment size as size in bytes */
|
|
|
|
SDL_CalculateAudioSpec(&this->spec);
|
2016-08-10 20:00:16 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-06-21 15:33:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
test_format = SDL_NextAudioFormat();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!valid_format) {
|
|
|
|
if (tried_format) {
|
|
|
|
return -1; /* CreateSecondary() should have called SDL_SetError(). */
|
|
|
|
}
|
|
|
|
return SDL_SetError("DirectSound: Unsupported audio format");
|
|
|
|
}
|
|
|
|
|
2016-08-10 20:00:16 +00:00
|
|
|
/* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
|
2015-06-21 15:33:46 +00:00
|
|
|
|
|
|
|
return 0; /* good to go. */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
DSOUND_Deinitialize(void)
|
|
|
|
{
|
|
|
|
DSOUND_Unload();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
DSOUND_Init(SDL_AudioDriverImpl * impl)
|
|
|
|
{
|
|
|
|
if (!DSOUND_Load()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the function pointers */
|
|
|
|
impl->DetectDevices = DSOUND_DetectDevices;
|
|
|
|
impl->OpenDevice = DSOUND_OpenDevice;
|
|
|
|
impl->PlayDevice = DSOUND_PlayDevice;
|
|
|
|
impl->WaitDevice = DSOUND_WaitDevice;
|
|
|
|
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
|
2016-08-10 20:00:16 +00:00
|
|
|
impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
|
|
|
|
impl->FlushCapture = DSOUND_FlushCapture;
|
2015-06-21 15:33:46 +00:00
|
|
|
impl->CloseDevice = DSOUND_CloseDevice;
|
|
|
|
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
|
|
|
|
impl->Deinitialize = DSOUND_Deinitialize;
|
|
|
|
|
2016-08-10 20:00:16 +00:00
|
|
|
impl->HasCaptureSupport = SDL_TRUE;
|
|
|
|
|
2015-06-21 15:33:46 +00:00
|
|
|
return 1; /* this audio target is available. */
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioBootStrap DSOUND_bootstrap = {
|
|
|
|
"directsound", "DirectSound", DSOUND_Init, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* SDL_AUDIO_DRIVER_DSOUND */
|
|
|
|
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|