Make winmm and directsound audio targets robust against unsupported formats.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 14 Jul 2013 21:30:16 -0400
changeset 7461 489d2bbcf4aa
parent 7460 1aee27b573d8
child 7462 66b536503733
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
src/audio/winmm/SDL_winmm.c
--- a/src/audio/directsound/SDL_directsound.c	Sun Jul 14 15:55:34 2013 -0700
+++ b/src/audio/directsound/SDL_directsound.c	Sun Jul 14 21:30:16 2013 -0400
@@ -346,7 +346,7 @@
    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 @@
     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 @@
     }
 
     /* 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 @@
                             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 @@
 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 @@
     }
     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 @@
 
     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;
 
--- a/src/audio/winmm/SDL_winmm.c	Sun Jul 14 15:55:34 2013 -0700
+++ b/src/audio/winmm/SDL_winmm.c	Sun Jul 14 21:30:16 2013 -0400
@@ -197,6 +197,30 @@
     }
 }
 
+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 @@
     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 @@
         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);