]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
UNMERGE! Implement SDL2 AUDIO_F32 format and use it as the default, since it has... 29/merge
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 26 May 2019 03:04:36 +0000 (03:04 +0000)
committerRudolf Polzer <divVerent@gmail.com>
Sun, 11 Apr 2021 23:14:17 +0000 (01:14 +0200)
Improved the fallback mechanism on SDL2 audio setup - by removing it; SDL2 will tell us what changes it makes for the format, and it will convert the rest automatically.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12476 d7cf8633-e32d-0410-b094-e92efae38249
::stable-branch::unmerge=925dc3b341a087249ca6fa0be7c227e49d01b213

snd_main.c
snd_main.h
snd_mem.c
snd_mix.c
snd_sdl.c

index bc874db62936e3afac55ba06a630c60364e77cbf..62807eb0983a79cb7529913aea7aaf29b46b5599 100644 (file)
@@ -239,10 +239,10 @@ static cvar_t ambient_fade = {0, "ambient_fade", "100", "rate of volume fading w
 static cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0", "disables extra sound mixer calls that are meant to reduce the chance of sound breakup at very low framerates"};
 static cvar_t snd_show = {0, "snd_show", "0", "shows some statistics about sound mixing"};
 
-// Default sound format is 48KHz, 32bit float, stereo
+// Default sound format is 48KHz, 16-bit, stereo
 // (48KHz because a lot of onboard sound cards sucks at any other speed)
 static cvar_t snd_speed = {CVAR_SAVE, "snd_speed", "48000", "sound output frequency, in hertz"};
-static cvar_t snd_width = {CVAR_SAVE, "snd_width", "4", "sound output precision, in bytes - 1 = 8bit, 2 = 16bit, 4 = 32bit float"};
+static cvar_t snd_width = {CVAR_SAVE, "snd_width", "2", "sound output precision, in bytes (1 and 2 supported)"};
 static cvar_t snd_channels = {CVAR_SAVE, "snd_channels", "2", "number of channels for the sound output (2 for stereo; up to 8 supported for 3D sound)"};
 
 static cvar_t snd_startloopingsounds = {0, "snd_startloopingsounds", "1", "whether to start sounds that would loop (you want this to be 1); existing sounds are not affected"};
@@ -373,6 +373,77 @@ int S_GetSoundChannels(void)
 }
 
 
+static qboolean S_ChooseCheaperFormat (snd_format_t* format, qboolean fixed_speed, qboolean fixed_width, qboolean fixed_channels)
+{
+       static const snd_format_t thresholds [] =
+       {
+               // speed                        width                   channels
+               { SND_MIN_SPEED,        SND_MIN_WIDTH,  SND_MIN_CHANNELS },
+               { 11025,                        1,                              2 },
+               { 22050,                        2,                              2 },
+               { 44100,                        2,                              2 },
+               { 48000,                        2,                              6 },
+               { 96000,                        2,                              6 },
+               { SND_MAX_SPEED,        SND_MAX_WIDTH,  SND_MAX_CHANNELS },
+       };
+       const unsigned int nb_thresholds = sizeof(thresholds) / sizeof(thresholds[0]);
+       unsigned int speed_level, width_level, channels_level;
+
+       // If we have reached the minimum values, there's nothing more we can do
+       if ((format->speed == thresholds[0].speed || fixed_speed) &&
+               (format->width == thresholds[0].width || fixed_width) &&
+               (format->channels == thresholds[0].channels || fixed_channels))
+               return false;
+
+       // Check the min and max values
+       #define CHECK_BOUNDARIES(param)                                                         \
+       if (format->param < thresholds[0].param)                                        \
+       {                                                                                                                       \
+               format->param = thresholds[0].param;                                    \
+               return true;                                                                                    \
+       }                                                                                                                       \
+       if (format->param > thresholds[nb_thresholds - 1].param)        \
+       {                                                                                                                       \
+               format->param = thresholds[nb_thresholds - 1].param;    \
+               return true;                                                                                    \
+       }
+       CHECK_BOUNDARIES(speed);
+       CHECK_BOUNDARIES(width);
+       CHECK_BOUNDARIES(channels);
+       #undef CHECK_BOUNDARIES
+
+       // Find the level of each parameter
+       #define FIND_LEVEL(param)                                                                       \
+       param##_level = 0;                                                                                      \
+       while (param##_level < nb_thresholds - 1)                                       \
+       {                                                                                                                       \
+               if (format->param <= thresholds[param##_level].param)   \
+                       break;                                                                                          \
+                                                                                                                               \
+               param##_level++;                                                                                \
+       }
+       FIND_LEVEL(speed);
+       FIND_LEVEL(width);
+       FIND_LEVEL(channels);
+       #undef FIND_LEVEL
+
+       // Decrease the parameter with the highest level to the previous level
+       if (channels_level >= speed_level && channels_level >= width_level && !fixed_channels)
+       {
+               format->channels = thresholds[channels_level - 1].channels;
+               return true;
+       }
+       if (speed_level >= width_level && !fixed_speed)
+       {
+               format->speed = thresholds[speed_level - 1].speed;
+               return true;
+       }
+
+       format->width = thresholds[width_level - 1].width;
+       return true;
+}
+
+
 #define SWAP_LISTENERS(l1, l2, tmpl) { tmpl = (l1); (l1) = (l2); (l2) = tmpl; }
 
 static void S_SetChannelLayout (void)
@@ -546,7 +617,7 @@ void S_Startup (void)
                chosen_fmt.speed = atoi (com_argv[i + 1]);
                fixed_speed = true;
        }
-// COMMANDLINEOPTION: Sound: -sndbits <bits> chooses 8 bit or 16 bit or 32bit float sound output
+// COMMANDLINEOPTION: Sound: -sndbits <bits> chooses 8 bit or 16 bit sound output
        i = COM_CheckParm ("-sndbits");
        if (0 < i && i < com_argc - 1)
        {
@@ -586,11 +657,6 @@ void S_Startup (void)
                chosen_fmt.width = SND_MIN_WIDTH;
                fixed_width = false;
        }
-    else if (chosen_fmt.width == 3)
-       {
-               chosen_fmt.width = 4;
-               fixed_width = false;
-       }
        else if (chosen_fmt.width > SND_MAX_WIDTH)
        {
                chosen_fmt.width = SND_MAX_WIDTH;
@@ -611,12 +677,39 @@ void S_Startup (void)
        // create the sound buffer used for sumitting the samples to the plaform-dependent module
        if (!simsound)
        {
-               Con_Printf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n",
-                                       chosen_fmt.speed,
-                                       chosen_fmt.width,
-                                       chosen_fmt.channels);
+               snd_format_t suggest_fmt;
+               qboolean accepted;
+
+               accepted = false;
+               do
+               {
+                       Con_Printf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n",
+                                               chosen_fmt.speed, chosen_fmt.width * 8,
+                                               chosen_fmt.channels);
+
+                       memset(&suggest_fmt, 0, sizeof(suggest_fmt));
+                       accepted = SndSys_Init(&chosen_fmt, &suggest_fmt);
+
+                       if (!accepted)
+                       {
+                               Con_Printf("S_Startup: sound output initialization FAILED\n");
+
+                               // If the module is suggesting another one
+                               if (suggest_fmt.speed != 0)
+                               {
+                                       memcpy(&chosen_fmt, &suggest_fmt, sizeof(chosen_fmt));
+                                       Con_Printf ("           Driver has suggested %dHz, %d bit, %d channels. Retrying...\n",
+                                                               suggest_fmt.speed, suggest_fmt.width * 8,
+                                                               suggest_fmt.channels);
+                               }
+                               // Else, try to find a less resource-demanding format
+                               else if (!S_ChooseCheaperFormat (&chosen_fmt, fixed_speed, fixed_width, fixed_channels))
+                                       break;
+                       }
+               } while (!accepted);
 
-               if (!SndSys_Init(&chosen_fmt))
+               // If we haven't found a suitable format
+               if (!accepted)
                {
                        Con_Print("S_Startup: SndSys_Init failed.\n");
                        sound_spatialized = false;
index 5a07d72b8483dbc3ca0be8e3d4b6411806b97997..527ce49fc4241b5c6b1343dd1ac247af56a56fcf 100644 (file)
@@ -153,6 +153,9 @@ void S_MixToBuffer(void *stream, unsigned int frames);
 
 qboolean S_LoadSound (sfx_t *sfx, qboolean complain);
 
+snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed);
+qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format);
+
 // If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself
 // (if "sampleframes" is 0, the function chooses the size).
 snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer);
@@ -162,9 +165,9 @@ snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int
 //         Architecture-dependent functions
 // ====================================================================
 
-// Create "snd_renderbuffer", attempting to use the chosen sound format, but accepting if the driver wants to change it (e.g. 7.1 to stereo or lowering the speed)
-// Note: SDL automatically converts all formats, so this only fails if there is no audio
-qboolean SndSys_Init (snd_format_t* fmt);
+// Create "snd_renderbuffer" with the proper sound format if the call is successful
+// May return a suggested format if the requested format isn't available
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested);
 
 // Stop the sound card, delete "snd_renderbuffer" and free its other resources
 void SndSys_Shutdown (void);
index 1098c2b67a817b0ec09c139b636ad42449b7590e..f6f9c145d85c073cf9aa2421f5cdab6d3dc17068 100644 (file)
--- a/snd_mem.c
+++ b/snd_mem.c
@@ -71,6 +71,229 @@ snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int
 }
 
 
+/*
+====================
+Snd_CreateSndBuffer
+====================
+*/
+snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed)
+{
+       size_t newsampleframes, memsize;
+       snd_buffer_t* sb;
+
+       newsampleframes = (size_t) ceil((double)sampleframes * (double)sb_speed / (double)in_format->speed);
+
+       memsize = newsampleframes * in_format->channels * in_format->width;
+       memsize += sizeof (*sb) - sizeof (sb->samples);
+
+       sb = (snd_buffer_t*)Mem_Alloc (snd_mempool, memsize);
+       sb->format.channels = in_format->channels;
+       sb->format.width = in_format->width;
+       sb->format.speed = sb_speed;
+       sb->maxframes = (unsigned int)newsampleframes;
+       sb->nbframes = 0;
+
+       if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format))
+       {
+               Mem_Free (sb);
+               return NULL;
+       }
+
+       return sb;
+}
+
+
+/*
+====================
+Snd_AppendToSndBuffer
+====================
+*/
+qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format)
+{
+       size_t srclength, outcount;
+       unsigned char *out_data;
+
+       //Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n",
+       //                      sampleframes, format->speed, outcount, sb->format.speed);
+
+       // If the formats are incompatible
+       if (sb->format.channels != format->channels || sb->format.width != format->width)
+       {
+               Con_Print("AppendToSndBuffer: incompatible sound formats!\n");
+               return false;
+       }
+
+       outcount = (size_t) ((double)sampleframes * (double)sb->format.speed / (double)format->speed);
+
+       // If the sound buffer is too short
+       if (outcount > sb->maxframes - sb->nbframes)
+       {
+               Con_Print("AppendToSndBuffer: sound buffer too short!\n");
+               return false;
+       }
+
+       out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels];
+       srclength = sampleframes * format->channels;
+
+       // Trivial case (direct transfer)
+       if (format->speed == sb->format.speed)
+       {
+               if (format->width == 1)
+               {
+                       size_t i;
+
+                       for (i = 0; i < srclength; i++)
+                               ((signed char*)out_data)[i] = samples[i] - 128;
+               }
+               else  // if (format->width == 2)
+                       memcpy (out_data, samples, srclength * format->width);
+       }
+
+       // General case (linear interpolation with a fixed-point fractional
+       // step, 18-bit integer part and 14-bit fractional part)
+       // Can handle up to 2^18 (262144) samples per second (> 96KHz stereo)
+#      define FRACTIONAL_BITS 14
+#      define FRACTIONAL_MASK ((1 << FRACTIONAL_BITS) - 1)
+#      define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS)
+       else
+       {
+               const unsigned int fracstep = (unsigned int)((double)format->speed / sb->format.speed * (1 << FRACTIONAL_BITS));
+               size_t remain_in = srclength, total_out = 0;
+               unsigned int samplefrac;
+               const unsigned char *in_ptr = samples;
+               unsigned char *out_ptr = out_data;
+
+               // Check that we can handle one second of that sound
+               if (format->speed * format->channels > (1 << INTEGER_BITS))
+               {
+                       Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n",
+                                          format->speed, format->channels);
+                       return 0;
+               }
+
+               // We work 1 sec at a time to make sure we don't accumulate any
+               // significant error when adding "fracstep" over several seconds, and
+               // also to be able to handle very long sounds.
+               while (total_out < outcount)
+               {
+                       size_t tmpcount, interpolation_limit, i, j;
+                       unsigned int srcsample;
+
+                       samplefrac = 0;
+
+                       // If more than 1 sec of sound remains to be converted
+                       if (outcount - total_out > sb->format.speed)
+                       {
+                               tmpcount = sb->format.speed;
+                               interpolation_limit = tmpcount;  // all samples can be interpolated
+                       }
+                       else
+                       {
+                               tmpcount = outcount - total_out;
+                               interpolation_limit = (int)ceil((double)(((remain_in / format->channels) - 1) << FRACTIONAL_BITS) / fracstep);
+                               if (interpolation_limit > tmpcount)
+                                       interpolation_limit = tmpcount;
+                       }
+
+                       // 16 bit samples
+                       if (format->width == 2)
+                       {
+                               const short* in_ptr_short;
+
+                               // Interpolated part
+                               for (i = 0; i < interpolation_limit; i++)
+                               {
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
+                                       in_ptr_short = &((const short*)in_ptr)[srcsample];
+
+                                       for (j = 0; j < format->channels; j++)
+                                       {
+                                               int a, b;
+
+                                               a = *in_ptr_short;
+                                               b = *(in_ptr_short + format->channels);
+                                               *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
+
+                                               in_ptr_short++;
+                                               out_ptr += sizeof (short);
+                                       }
+
+                                       samplefrac += fracstep;
+                               }
+
+                               // Non-interpolated part
+                               for (/* nothing */; i < tmpcount; i++)
+                               {
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
+                                       in_ptr_short = &((const short*)in_ptr)[srcsample];
+
+                                       for (j = 0; j < format->channels; j++)
+                                       {
+                                               *((short*)out_ptr) = *in_ptr_short;
+
+                                               in_ptr_short++;
+                                               out_ptr += sizeof (short);
+                                       }
+
+                                       samplefrac += fracstep;
+                               }
+                       }
+                       // 8 bit samples
+                       else  // if (format->width == 1)
+                       {
+                               const unsigned char* in_ptr_byte;
+
+                               // Convert up to 1 sec of sound
+                               for (i = 0; i < interpolation_limit; i++)
+                               {
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
+                                       in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
+
+                                       for (j = 0; j < format->channels; j++)
+                                       {
+                                               int a, b;
+
+                                               a = *in_ptr_byte - 128;
+                                               b = *(in_ptr_byte + format->channels) - 128;
+                                               *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
+
+                                               in_ptr_byte++;
+                                               out_ptr += sizeof (signed char);
+                                       }
+
+                                       samplefrac += fracstep;
+                               }
+
+                               // Non-interpolated part
+                               for (/* nothing */; i < tmpcount; i++)
+                               {
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
+                                       in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
+
+                                       for (j = 0; j < format->channels; j++)
+                                       {
+                                               *((signed char*)out_ptr) = *in_ptr_byte - 128;
+
+                                               in_ptr_byte++;
+                                               out_ptr += sizeof (signed char);
+                                       }
+
+                                       samplefrac += fracstep;
+                               }
+                       }
+
+                       // Update the counters and the buffer position
+                       remain_in -= format->speed * format->channels;
+                       in_ptr += format->speed * format->channels * format->width;
+                       total_out += tmpcount;
+               }
+       }
+
+       sb->nbframes += (unsigned int)outcount;
+       return true;
+}
+
+
 //=============================================================================
 
 /*
index 4ed38a88b633fde65875389fb622340cc3d4b12a..10da40047ec05355f7ccf85eff87d7b1a115344f 100644 (file)
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -29,7 +29,7 @@ static portable_sampleframe_t paintbuffer_unswapped[PAINTBUFFER_SIZE];
 extern speakerlayout_t snd_speakerlayout; // for querying the listeners
 
 #ifdef CONFIG_VIDEO_CAPTURE
-static void S_CaptureAVISound(const portable_sampleframe_t *sampleframes, size_t length)
+static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
 {
        size_t i;
        unsigned int j;
@@ -42,7 +42,7 @@ static void S_CaptureAVISound(const portable_sampleframe_t *sampleframes, size_t
        {
                unsigned int j0 = snd_speakerlayout.listeners[j].channel_unswapped;
                for(i = 0; i < length; ++i)
-                       paintbuffer_unswapped[i].sample[j0] = sampleframes[i].sample[j];
+                       paintbuffer_unswapped[i].sample[j0] = paintbuffer[i].sample[j];
        }
 
        SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
@@ -127,67 +127,11 @@ static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_ptr, int nbfra
 
 static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int nchannels)
 {
-       int i;
-       if (width == 4)  // 32bit float
-       {
-               float *snd_out = (float*)rb_ptr;
-               if (nchannels == 8)  // 7.1 surround
-               {
-                       for (i = 0; i < nbframes; i++, painted_ptr++)
-                       {
-                               *snd_out++ = painted_ptr->sample[0];
-                               *snd_out++ = painted_ptr->sample[1];
-                               *snd_out++ = painted_ptr->sample[2];
-                               *snd_out++ = painted_ptr->sample[3];
-                               *snd_out++ = painted_ptr->sample[4];
-                               *snd_out++ = painted_ptr->sample[5];
-                               *snd_out++ = painted_ptr->sample[6];
-                               *snd_out++ = painted_ptr->sample[7];
-                       }
-               }
-               else if (nchannels == 6)  // 5.1 surround
-               {
-                       for (i = 0; i < nbframes; i++, painted_ptr++)
-                       {
-                               *snd_out++ = painted_ptr->sample[0];
-                               *snd_out++ = painted_ptr->sample[1];
-                               *snd_out++ = painted_ptr->sample[2];
-                               *snd_out++ = painted_ptr->sample[3];
-                               *snd_out++ = painted_ptr->sample[4];
-                               *snd_out++ = painted_ptr->sample[5];
-                       }
-               }
-               else if (nchannels == 4)  // 4.0 surround
-               {
-                       for (i = 0; i < nbframes; i++, painted_ptr++)
-                       {
-                               *snd_out++ = painted_ptr->sample[0];
-                               *snd_out++ = painted_ptr->sample[1];
-                               *snd_out++ = painted_ptr->sample[2];
-                               *snd_out++ = painted_ptr->sample[3];
-                       }
-               }
-               else if (nchannels == 2)  // 2.0 stereo
-               {
-                       for (i = 0; i < nbframes; i++, painted_ptr++)
-                       {
-                               *snd_out++ = painted_ptr->sample[0];
-                               *snd_out++ = painted_ptr->sample[1];
-                       }
-               }
-               else if (nchannels == 1)  // 1.0 mono
-               {
-                       for (i = 0; i < nbframes; i++, painted_ptr++)
-                       {
-                               *snd_out++ = painted_ptr->sample[0];
-                       }
-               }
+       int i, val;
 
-               // noise is really really annoying
-               if (cls.timedemo)
-                       memset(rb_ptr, 0, nbframes * nchannels * width);
-       }
-       else if (width == 2)  // 16bit
+       // FIXME: add 24bit and 32bit float formats
+       // FIXME: optimize with SSE intrinsics?
+       if (width == 2)  // 16bit
        {
                short *snd_out = (short*)rb_ptr;
                if (nchannels == 8)  // 7.1 surround
index f7e0f4723065c73c8b73856257afe8aeaa42a005..d5cca85668d0c39bb61d7c73a6df2a334aaba34b 100644 (file)
--- a/snd_sdl.c
+++ b/snd_sdl.c
@@ -102,7 +102,7 @@ Create "snd_renderbuffer" with the proper sound format if the call is successful
 May return a suggested format if the requested format isn't available
 ====================
 */
-qboolean SndSys_Init (snd_format_t* fmt)
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
        unsigned int buffersize;
        SDL_AudioSpec wantspec;
@@ -118,15 +118,14 @@ qboolean SndSys_Init (snd_format_t* fmt)
                return false;
        }
 
-       buffersize = (unsigned int)ceil((double)fmt->speed / 25.0); // 2048 bytes on 24kHz to 48kHz
+       buffersize = (unsigned int)ceil((double)requested->speed / 25.0); // 2048 bytes on 24kHz to 48kHz
 
        // Init the SDL Audio subsystem
-       memset(&wantspec, 0, sizeof(wantspec));
        wantspec.callback = Buffer_Callback;
        wantspec.userdata = NULL;
-       wantspec.freq = fmt->speed;
-       wantspec.format = fmt->width == 1 ? AUDIO_U8 : (fmt->width == 2 ? AUDIO_S16SYS : AUDIO_F32);
-       wantspec.channels = fmt->channels;
+       wantspec.freq = requested->speed;
+       wantspec.format = ((requested->width == 1) ? AUDIO_U8 : AUDIO_S16SYS);
+       wantspec.channels = requested->channels;
        wantspec.samples = CeilPowerOf2(buffersize);  // needs to be a power of 2 on some platforms.
 
        Con_Printf("Wanted audio Specification:\n"
@@ -136,7 +135,7 @@ qboolean SndSys_Init (snd_format_t* fmt)
                                "\tSamples   : %i\n",
                                wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples);
 
-       if ((audio_device = SDL_OpenAudioDevice(NULL, 0, &wantspec, &obtainspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE)) == 0)
+       if ((audio_device = SDL_OpenAudioDevice(NULL, 0, &wantspec, &obtainspec, 0)) == 0)
        {
                Con_Printf( "Failed to open the audio device! (%s)\n", SDL_GetError() );
                return false;
@@ -149,12 +148,28 @@ qboolean SndSys_Init (snd_format_t* fmt)
                                "\tSamples   : %i\n",
                                obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples);
 
-       fmt->speed = obtainspec.freq;
-       fmt->channels = obtainspec.channels;
+       // If we haven't obtained what we wanted
+       if (wantspec.freq != obtainspec.freq ||
+               wantspec.format != obtainspec.format ||
+               wantspec.channels != obtainspec.channels)
+       {
+               SDL_CloseAudioDevice(audio_device);
+
+               // Pass the obtained format as a suggested format
+               if (suggested != NULL)
+               {
+                       suggested->speed = obtainspec.freq;
+                       // FIXME: check the format more carefully. There are plenty of unsupported cases
+                       suggested->width = ((obtainspec.format == AUDIO_U8) ? 1 : 2);
+                       suggested->channels = obtainspec.channels;
+               }
+
+               return false;
+       }
 
        snd_threaded = true;
 
-       snd_renderbuffer = Snd_CreateRingBuffer(fmt, 0, NULL);
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
        if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
                Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD);