Make winmm and directsound audio targets robust against unsupported formats.

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.

--HG--
extra : rebase_source : 7b4e61c8030d1e1ce4c926bc0abc9b4d4af11dd9
This commit is contained in:
Ryan C. Gordon 2013-07-14 21:30:16 -04:00
parent 901d874a2b
commit 8f4fb24e2d
2 changed files with 84 additions and 74 deletions

View file

@ -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;
@ -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) {
@ -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) {
@ -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,
@ -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;
@ -465,42 +483,6 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
while ((!valid_format) && (test_format)) {
switch (test_format) {
case AUDIO_U8:
case AUDIO_S16:
case AUDIO_S32:
case AUDIO_F32:
this->spec.format = test_format;
valid_format = 1;
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
DSOUND_CloseDevice(this);
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) {
@ -508,11 +490,29 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
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) {
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;
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);
return -1;
if (tried_format) {
return -1; // CreateSecondary() should have called SDL_SetError().
}
return SDL_SetError("DirectSound: Unsupported audio format");
}
/* The buffer will auto-start playing in DSOUND_WaitDevice() */

View file

@ -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)
{
@ -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;
}
@ -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);