Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Make winmm and directsound audio targets robust against unsupported f…
Browse files Browse the repository at this point in the history
…ormats.

It now tries to make sure the hardware can support a given format, and if it
 can't, it carries on to the next best format instead of failing completely.
  • Loading branch information
icculus committed Jul 15, 2013
1 parent 0e5e52f commit 83411c0
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 67 deletions.
78 changes: 39 additions & 39 deletions src/audio/directsound/SDL_directsound.c
Expand Up @@ -346,7 +346,7 @@ DSOUND_CloseDevice(_THIS)
number of audio chunks available in the created buffer.
*/
static int
CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
CreateSecondary(_THIS, HWND focus)
{
LPDIRECTSOUND sndObj = this->hidden->sound;
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
Expand All @@ -356,6 +356,24 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
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;

/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);

/* Try to set primary mixing privileges */
if (focus) {
Expand All @@ -371,7 +389,7 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
}

/* Try to create the secondary buffer */
SDL_memset(&format, 0, sizeof(format));
SDL_zero(format);
format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
if (!focus) {
Expand All @@ -386,12 +404,12 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
}
format.dwReserved = 0;
format.lpwfxFormat = wavefmt;
format.lpwfxFormat = &wfmt;
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
if (result != DS_OK) {
return SetDSerror("DirectSound CreateSoundBuffer", result);
}
IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt);
IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);

/* Silence the initial audio buffer */
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
Expand Down Expand Up @@ -437,8 +455,8 @@ static int
DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
{
HRESULT result;
WAVEFORMATEX waveformat;
int valid_format = 0;
SDL_bool valid_format = SDL_FALSE;
SDL_bool tried_format = SDL_FALSE;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
FindDevGUIDData devguid;
LPGUID guid = NULL;
Expand All @@ -465,56 +483,38 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));

/* Open the audio device */
result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
if (result != DS_OK) {
DSOUND_CloseDevice(this);
return SetDSerror("DirectSoundCreate", result);
}

while ((!valid_format) && (test_format)) {
switch (test_format) {
case AUDIO_U8:
case AUDIO_S16:
case AUDIO_S32:
case AUDIO_F32:
tried_format = SDL_TRUE;
this->spec.format = test_format;
valid_format = 1;
this->hidden->num_buffers = CreateSecondary(this, NULL);
if (this->hidden->num_buffers > 0) {
valid_format = SDL_TRUE;
}
break;
}
test_format = SDL_NextAudioFormat();
}

if (!valid_format) {
DSOUND_CloseDevice(this);
if (tried_format) {
return -1; // CreateSecondary() should have called SDL_SetError().
}
return SDL_SetError("DirectSound: Unsupported audio format");
}

SDL_memset(&waveformat, 0, sizeof(waveformat));

if (SDL_AUDIO_ISFLOAT(this->spec.format))
waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
else
waveformat.wFormatTag = WAVE_FORMAT_PCM;

waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
waveformat.nChannels = this->spec.channels;
waveformat.nSamplesPerSec = this->spec.freq;
waveformat.nBlockAlign =
waveformat.nChannels * (waveformat.wBitsPerSample / 8);
waveformat.nAvgBytesPerSec =
waveformat.nSamplesPerSec * waveformat.nBlockAlign;

/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);

/* Open the audio device */
result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
if (result != DS_OK) {
DSOUND_CloseDevice(this);
return SetDSerror("DirectSoundCreate", result);
}

/* Create the audio buffer to which we write */
this->hidden->num_buffers = CreateSecondary(this, NULL, &waveformat);
if (this->hidden->num_buffers < 0) {
DSOUND_CloseDevice(this);
return -1;
}

/* The buffer will auto-start playing in DSOUND_WaitDevice() */
this->hidden->mixlen = this->spec.size;

Expand Down
66 changes: 38 additions & 28 deletions src/audio/winmm/SDL_winmm.c
Expand Up @@ -197,6 +197,30 @@ WINMM_CloseDevice(_THIS)
}
}

static SDL_bool
PrepWaveFormat(_THIS, UINT_PTR devId, WAVEFORMATEX *pfmt, const int iscapture)
{
SDL_zerop(pfmt);

if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else {
pfmt->wFormatTag = WAVE_FORMAT_PCM;
}
pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);

pfmt->nChannels = this->spec.channels;
pfmt->nSamplesPerSec = this->spec.freq;
pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;

if (iscapture) {
return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
} else {
return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
}
}

static int
WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
{
Expand Down Expand Up @@ -254,18 +278,28 @@ WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
for (i = 0; i < NUM_BUFFERS; ++i)
this->hidden->wavebuf[i].dwUser = 0xFFFF;

if (this->spec.channels > 2)
this->spec.channels = 2; /* !!! FIXME: is this right? */

/* Check the buffer size -- minimum of 1/4 second (word aligned) */
if (this->spec.samples < (this->spec.freq / 4))
this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;

while ((!valid_datatype) && (test_format)) {
valid_datatype = 1;
this->spec.format = test_format;
switch (test_format) {
case AUDIO_U8:
case AUDIO_S16:
case AUDIO_S32:
case AUDIO_F32:
break; /* valid. */
this->spec.format = test_format;
if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
valid_datatype = 1;
} else {
test_format = SDL_NextAudioFormat();
}
break;

default:
valid_datatype = 0;
test_format = SDL_NextAudioFormat();
break;
}
Expand All @@ -276,30 +310,6 @@ WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
return SDL_SetError("Unsupported audio format");
}

/* Set basic WAVE format parameters */
SDL_memset(&waveformat, '\0', sizeof(waveformat));

if (SDL_AUDIO_ISFLOAT(this->spec.format))
waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
else
waveformat.wFormatTag = WAVE_FORMAT_PCM;

waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);

if (this->spec.channels > 2)
this->spec.channels = 2; /* !!! FIXME: is this right? */

waveformat.nChannels = this->spec.channels;
waveformat.nSamplesPerSec = this->spec.freq;
waveformat.nBlockAlign =
waveformat.nChannels * (waveformat.wBitsPerSample / 8);
waveformat.nAvgBytesPerSec =
waveformat.nSamplesPerSec * waveformat.nBlockAlign;

/* Check the buffer size -- minimum of 1/4 second (word aligned) */
if (this->spec.samples < (this->spec.freq / 4))
this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;

/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);

Expand Down

0 comments on commit 83411c0

Please sign in to comment.