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, 16-bit, stereo
+// Default sound format is 48KHz, 32bit float, 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", "2", "sound output precision, in bytes (1 and 2 supported)"};
+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_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"};
}
-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)
chosen_fmt.speed = atoi (com_argv[i + 1]);
fixed_speed = true;
}
-// COMMANDLINEOPTION: Sound: -sndbits <bits> chooses 8 bit or 16 bit sound output
+// COMMANDLINEOPTION: Sound: -sndbits <bits> chooses 8 bit or 16 bit or 32bit float sound output
i = COM_CheckParm ("-sndbits");
if (0 < i && i < com_argc - 1)
{
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;
// create the sound buffer used for sumitting the samples to the plaform-dependent module
if (!simsound)
{
- 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);
+ Con_Printf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n",
+ chosen_fmt.speed,
+ chosen_fmt.width,
+ chosen_fmt.channels);
- // If we haven't found a suitable format
- if (!accepted)
+ if (!SndSys_Init(&chosen_fmt))
{
Con_Print("S_Startup: SndSys_Init failed.\n");
sound_spatialized = false;
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);
// Architecture-dependent functions
// ====================================================================
-// 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);
+// 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);
// Stop the sound card, delete "snd_renderbuffer" and free its other resources
void SndSys_Shutdown (void);
}
-/*
-====================
-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;
-}
-
-
//=============================================================================
/*
extern speakerlayout_t snd_speakerlayout; // for querying the listeners
#ifdef CONFIG_VIDEO_CAPTURE
-static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
+static void S_CaptureAVISound(const portable_sampleframe_t *sampleframes, size_t length)
{
size_t i;
unsigned int j;
{
unsigned int j0 = snd_speakerlayout.listeners[j].channel_unswapped;
for(i = 0; i < length; ++i)
- paintbuffer_unswapped[i].sample[j0] = paintbuffer[i].sample[j];
+ paintbuffer_unswapped[i].sample[j0] = sampleframes[i].sample[j];
}
SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int nchannels)
{
- int i, val;
+ 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];
+ }
+ }
- // FIXME: add 24bit and 32bit float formats
- // FIXME: optimize with SSE intrinsics?
- if (width == 2) // 16bit
+ // noise is really really annoying
+ if (cls.timedemo)
+ memset(rb_ptr, 0, nbframes * nchannels * width);
+ }
+ else if (width == 2) // 16bit
{
short *snd_out = (short*)rb_ptr;
if (nchannels == 8) // 7.1 surround
May return a suggested format if the requested format isn't available
====================
*/
-qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
+qboolean SndSys_Init (snd_format_t* fmt)
{
unsigned int buffersize;
SDL_AudioSpec wantspec;
return false;
}
- buffersize = (unsigned int)ceil((double)requested->speed / 25.0); // 2048 bytes on 24kHz to 48kHz
+ buffersize = (unsigned int)ceil((double)fmt->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 = requested->speed;
- wantspec.format = ((requested->width == 1) ? AUDIO_U8 : AUDIO_S16SYS);
- wantspec.channels = requested->channels;
+ wantspec.freq = fmt->speed;
+ wantspec.format = fmt->width == 1 ? AUDIO_U8 : (fmt->width == 2 ? AUDIO_S16SYS : AUDIO_F32);
+ wantspec.channels = fmt->channels;
wantspec.samples = CeilPowerOf2(buffersize); // needs to be a power of 2 on some platforms.
Con_Printf("Wanted audio Specification:\n"
"\tSamples : %i\n",
wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples);
- if ((audio_device = SDL_OpenAudioDevice(NULL, 0, &wantspec, &obtainspec, 0)) == 0)
+ if ((audio_device = SDL_OpenAudioDevice(NULL, 0, &wantspec, &obtainspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE)) == 0)
{
Con_Printf( "Failed to open the audio device! (%s)\n", SDL_GetError() );
return false;
"\tSamples : %i\n",
obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples);
- // 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;
- }
+ fmt->speed = obtainspec.freq;
+ fmt->channels = obtainspec.channels;
snd_threaded = true;
- snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
+ snd_renderbuffer = Snd_CreateRingBuffer(fmt, 0, NULL);
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD);