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. number of audio chunks available in the created buffer.
*/ */
static int static int
CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt) CreateSecondary(_THIS, HWND focus)
{ {
LPDIRECTSOUND sndObj = this->hidden->sound; LPDIRECTSOUND sndObj = this->hidden->sound;
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf; LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
@ -356,6 +356,24 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
DSBUFFERDESC format; DSBUFFERDESC format;
LPVOID pvAudioPtr1, pvAudioPtr2; LPVOID pvAudioPtr1, pvAudioPtr2;
DWORD dwAudioBytes1, dwAudioBytes2; 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 */ /* Try to set primary mixing privileges */
if (focus) { if (focus) {
@ -371,7 +389,7 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
} }
/* Try to create the secondary buffer */ /* Try to create the secondary buffer */
SDL_memset(&format, 0, sizeof(format)); SDL_zero(format);
format.dwSize = sizeof(format); format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
if (!focus) { if (!focus) {
@ -386,12 +404,12 @@ CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks); DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
} }
format.dwReserved = 0; format.dwReserved = 0;
format.lpwfxFormat = wavefmt; format.lpwfxFormat = &wfmt;
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
if (result != DS_OK) { if (result != DS_OK) {
return SetDSerror("DirectSound CreateSoundBuffer", result); return SetDSerror("DirectSound CreateSoundBuffer", result);
} }
IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt); IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);
/* Silence the initial audio buffer */ /* Silence the initial audio buffer */
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
@ -437,8 +455,8 @@ static int
DSOUND_OpenDevice(_THIS, const char *devname, int iscapture) DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
{ {
HRESULT result; HRESULT result;
WAVEFORMATEX waveformat; SDL_bool valid_format = SDL_FALSE;
int valid_format = 0; SDL_bool tried_format = SDL_FALSE;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
FindDevGUIDData devguid; FindDevGUIDData devguid;
LPGUID guid = NULL; LPGUID guid = NULL;
@ -465,42 +483,6 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
} }
SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 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 */ /* Open the audio device */
result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL); result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
if (result != DS_OK) { if (result != DS_OK) {
@ -508,11 +490,29 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
return SetDSerror("DirectSoundCreate", result); return SetDSerror("DirectSoundCreate", result);
} }
/* Create the audio buffer to which we write */ while ((!valid_format) && (test_format)) {
this->hidden->num_buffers = CreateSecondary(this, NULL, &waveformat); switch (test_format) {
if (this->hidden->num_buffers < 0) { 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); 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() */ /* 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 static int
WINMM_OpenDevice(_THIS, const char *devname, int iscapture) 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) for (i = 0; i < NUM_BUFFERS; ++i)
this->hidden->wavebuf[i].dwUser = 0xFFFF; 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)) { while ((!valid_datatype) && (test_format)) {
valid_datatype = 1;
this->spec.format = test_format;
switch (test_format) { switch (test_format) {
case AUDIO_U8: case AUDIO_U8:
case AUDIO_S16: case AUDIO_S16:
case AUDIO_S32: case AUDIO_S32:
case AUDIO_F32: 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: default:
valid_datatype = 0;
test_format = SDL_NextAudioFormat(); test_format = SDL_NextAudioFormat();
break; break;
} }
@ -276,30 +310,6 @@ WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
return SDL_SetError("Unsupported audio format"); 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 */ /* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec); SDL_CalculateAudioSpec(&this->spec);