From 83411c047af692089772924ecbf80d7fd3920e8c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 14 Jul 2013 21:30:16 -0400 Subject: [PATCH] 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. --- src/audio/directsound/SDL_directsound.c | 78 ++++++++++++------------- src/audio/winmm/SDL_winmm.c | 66 ++++++++++++--------- 2 files changed, 77 insertions(+), 67 deletions(-) diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c index f08204494..686d46615 100644 --- a/src/audio/directsound/SDL_directsound.c +++ b/src/audio/directsound/SDL_directsound.c @@ -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,14 +483,25 @@ 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(); @@ -480,41 +509,12 @@ DSOUND_OpenDevice(_THIS, const char *devname, int iscapture) 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; diff --git a/src/audio/winmm/SDL_winmm.c b/src/audio/winmm/SDL_winmm.c index dc7336b2b..9c8da7ae2 100644 --- a/src/audio/winmm/SDL_winmm.c +++ b/src/audio/winmm/SDL_winmm.c @@ -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);