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 7461489d2bbcf4aa
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
     1.1 --- a/src/audio/directsound/SDL_directsound.c	Sun Jul 14 15:55:34 2013 -0700
     1.2 +++ b/src/audio/directsound/SDL_directsound.c	Sun Jul 14 21:30:16 2013 -0400
     1.3 @@ -346,7 +346,7 @@
     1.4     number of audio chunks available in the created buffer.
     1.5  */
     1.6  static int
     1.7 -CreateSecondary(_THIS, HWND focus, WAVEFORMATEX * wavefmt)
     1.8 +CreateSecondary(_THIS, HWND focus)
     1.9  {
    1.10      LPDIRECTSOUND sndObj = this->hidden->sound;
    1.11      LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
    1.12 @@ -356,6 +356,24 @@
    1.13      DSBUFFERDESC format;
    1.14      LPVOID pvAudioPtr1, pvAudioPtr2;
    1.15      DWORD dwAudioBytes1, dwAudioBytes2;
    1.16 +    WAVEFORMATEX wfmt;
    1.17 +
    1.18 +    SDL_zero(wfmt);
    1.19 +
    1.20 +    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
    1.21 +        wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    1.22 +    } else {
    1.23 +        wfmt.wFormatTag = WAVE_FORMAT_PCM;
    1.24 +    }
    1.25 +
    1.26 +    wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    1.27 +    wfmt.nChannels = this->spec.channels;
    1.28 +    wfmt.nSamplesPerSec = this->spec.freq;
    1.29 +    wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
    1.30 +    wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
    1.31 +
    1.32 +    /* Update the fragment size as size in bytes */
    1.33 +    SDL_CalculateAudioSpec(&this->spec);
    1.34  
    1.35      /* Try to set primary mixing privileges */
    1.36      if (focus) {
    1.37 @@ -371,7 +389,7 @@
    1.38      }
    1.39  
    1.40      /* Try to create the secondary buffer */
    1.41 -    SDL_memset(&format, 0, sizeof(format));
    1.42 +    SDL_zero(format);
    1.43      format.dwSize = sizeof(format);
    1.44      format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
    1.45      if (!focus) {
    1.46 @@ -386,12 +404,12 @@
    1.47                              DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
    1.48      }
    1.49      format.dwReserved = 0;
    1.50 -    format.lpwfxFormat = wavefmt;
    1.51 +    format.lpwfxFormat = &wfmt;
    1.52      result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
    1.53      if (result != DS_OK) {
    1.54          return SetDSerror("DirectSound CreateSoundBuffer", result);
    1.55      }
    1.56 -    IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt);
    1.57 +    IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);
    1.58  
    1.59      /* Silence the initial audio buffer */
    1.60      result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
    1.61 @@ -437,8 +455,8 @@
    1.62  DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
    1.63  {
    1.64      HRESULT result;
    1.65 -    WAVEFORMATEX waveformat;
    1.66 -    int valid_format = 0;
    1.67 +    SDL_bool valid_format = SDL_FALSE;
    1.68 +    SDL_bool tried_format = SDL_FALSE;
    1.69      SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
    1.70      FindDevGUIDData devguid;
    1.71      LPGUID guid = NULL;
    1.72 @@ -465,14 +483,25 @@
    1.73      }
    1.74      SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    1.75  
    1.76 +    /* Open the audio device */
    1.77 +    result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
    1.78 +    if (result != DS_OK) {
    1.79 +        DSOUND_CloseDevice(this);
    1.80 +        return SetDSerror("DirectSoundCreate", result);
    1.81 +    }
    1.82 +
    1.83      while ((!valid_format) && (test_format)) {
    1.84          switch (test_format) {
    1.85          case AUDIO_U8:
    1.86          case AUDIO_S16:
    1.87          case AUDIO_S32:
    1.88          case AUDIO_F32:
    1.89 +            tried_format = SDL_TRUE;
    1.90              this->spec.format = test_format;
    1.91 -            valid_format = 1;
    1.92 +            this->hidden->num_buffers = CreateSecondary(this, NULL);
    1.93 +            if (this->hidden->num_buffers > 0) {
    1.94 +                valid_format = SDL_TRUE;
    1.95 +            }
    1.96              break;
    1.97          }
    1.98          test_format = SDL_NextAudioFormat();
    1.99 @@ -480,41 +509,12 @@
   1.100  
   1.101      if (!valid_format) {
   1.102          DSOUND_CloseDevice(this);
   1.103 +        if (tried_format) {
   1.104 +            return -1;  // CreateSecondary() should have called SDL_SetError().
   1.105 +        }
   1.106          return SDL_SetError("DirectSound: Unsupported audio format");
   1.107      }
   1.108  
   1.109 -    SDL_memset(&waveformat, 0, sizeof(waveformat));
   1.110 -
   1.111 -    if (SDL_AUDIO_ISFLOAT(this->spec.format))
   1.112 -        waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   1.113 -    else
   1.114 -        waveformat.wFormatTag = WAVE_FORMAT_PCM;
   1.115 -
   1.116 -    waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   1.117 -    waveformat.nChannels = this->spec.channels;
   1.118 -    waveformat.nSamplesPerSec = this->spec.freq;
   1.119 -    waveformat.nBlockAlign =
   1.120 -        waveformat.nChannels * (waveformat.wBitsPerSample / 8);
   1.121 -    waveformat.nAvgBytesPerSec =
   1.122 -        waveformat.nSamplesPerSec * waveformat.nBlockAlign;
   1.123 -
   1.124 -    /* Update the fragment size as size in bytes */
   1.125 -    SDL_CalculateAudioSpec(&this->spec);
   1.126 -
   1.127 -    /* Open the audio device */
   1.128 -    result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
   1.129 -    if (result != DS_OK) {
   1.130 -        DSOUND_CloseDevice(this);
   1.131 -        return SetDSerror("DirectSoundCreate", result);
   1.132 -    }
   1.133 -
   1.134 -    /* Create the audio buffer to which we write */
   1.135 -    this->hidden->num_buffers = CreateSecondary(this, NULL, &waveformat);
   1.136 -    if (this->hidden->num_buffers < 0) {
   1.137 -        DSOUND_CloseDevice(this);
   1.138 -        return -1;
   1.139 -    }
   1.140 -
   1.141      /* The buffer will auto-start playing in DSOUND_WaitDevice() */
   1.142      this->hidden->mixlen = this->spec.size;
   1.143  
     2.1 --- a/src/audio/winmm/SDL_winmm.c	Sun Jul 14 15:55:34 2013 -0700
     2.2 +++ b/src/audio/winmm/SDL_winmm.c	Sun Jul 14 21:30:16 2013 -0400
     2.3 @@ -197,6 +197,30 @@
     2.4      }
     2.5  }
     2.6  
     2.7 +static SDL_bool
     2.8 +PrepWaveFormat(_THIS, UINT_PTR devId, WAVEFORMATEX *pfmt, const int iscapture)
     2.9 +{
    2.10 +    SDL_zerop(pfmt);
    2.11 +
    2.12 +    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
    2.13 +        pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    2.14 +    } else {
    2.15 +        pfmt->wFormatTag = WAVE_FORMAT_PCM;
    2.16 +    }
    2.17 +    pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    2.18 +
    2.19 +    pfmt->nChannels = this->spec.channels;
    2.20 +    pfmt->nSamplesPerSec = this->spec.freq;
    2.21 +    pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
    2.22 +    pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
    2.23 +
    2.24 +    if (iscapture) {
    2.25 +        return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    2.26 +    } else {
    2.27 +        return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    2.28 +    }
    2.29 +}
    2.30 +
    2.31  static int
    2.32  WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
    2.33  {
    2.34 @@ -254,18 +278,28 @@
    2.35      for (i = 0; i < NUM_BUFFERS; ++i)
    2.36          this->hidden->wavebuf[i].dwUser = 0xFFFF;
    2.37  
    2.38 +    if (this->spec.channels > 2)
    2.39 +        this->spec.channels = 2;        /* !!! FIXME: is this right? */
    2.40 +
    2.41 +    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
    2.42 +    if (this->spec.samples < (this->spec.freq / 4))
    2.43 +        this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
    2.44 +
    2.45      while ((!valid_datatype) && (test_format)) {
    2.46 -        valid_datatype = 1;
    2.47 -        this->spec.format = test_format;
    2.48          switch (test_format) {
    2.49          case AUDIO_U8:
    2.50          case AUDIO_S16:
    2.51          case AUDIO_S32:
    2.52          case AUDIO_F32:
    2.53 -            break;              /* valid. */
    2.54 +            this->spec.format = test_format;
    2.55 +            if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
    2.56 +                valid_datatype = 1;
    2.57 +            } else {
    2.58 +                test_format = SDL_NextAudioFormat();
    2.59 +            }
    2.60 +            break;
    2.61  
    2.62          default:
    2.63 -            valid_datatype = 0;
    2.64              test_format = SDL_NextAudioFormat();
    2.65              break;
    2.66          }
    2.67 @@ -276,30 +310,6 @@
    2.68          return SDL_SetError("Unsupported audio format");
    2.69      }
    2.70  
    2.71 -    /* Set basic WAVE format parameters */
    2.72 -    SDL_memset(&waveformat, '\0', sizeof(waveformat));
    2.73 -
    2.74 -    if (SDL_AUDIO_ISFLOAT(this->spec.format))
    2.75 -        waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    2.76 -    else
    2.77 -        waveformat.wFormatTag = WAVE_FORMAT_PCM;
    2.78 -
    2.79 -    waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    2.80 -
    2.81 -    if (this->spec.channels > 2)
    2.82 -        this->spec.channels = 2;        /* !!! FIXME: is this right? */
    2.83 -
    2.84 -    waveformat.nChannels = this->spec.channels;
    2.85 -    waveformat.nSamplesPerSec = this->spec.freq;
    2.86 -    waveformat.nBlockAlign =
    2.87 -        waveformat.nChannels * (waveformat.wBitsPerSample / 8);
    2.88 -    waveformat.nAvgBytesPerSec =
    2.89 -        waveformat.nSamplesPerSec * waveformat.nBlockAlign;
    2.90 -
    2.91 -    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
    2.92 -    if (this->spec.samples < (this->spec.freq / 4))
    2.93 -        this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
    2.94 -
    2.95      /* Update the fragment size as size in bytes */
    2.96      SDL_CalculateAudioSpec(&this->spec);
    2.97