#include "quakedef.h"
#include "snd_main.h"
+extern cvar_t snd_softclip;
static portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE];
static portable_sampleframe_t paintbuffer_unswapped[PAINTBUFFER_SIZE];
extern speakerlayout_t snd_speakerlayout; // for querying the listeners
-extern void SCR_CaptureVideo_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length);
+#ifdef CONFIG_VIDEO_CAPTURE
static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
{
size_t i;
SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
}
+#endif
+
+extern cvar_t snd_softclip;
+
+static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_ptr, int nbframes, int width, int nchannels)
+{
+ int i;
+
+ if((snd_softclip.integer == 1 && width <= 2) || snd_softclip.integer > 1)
+ {
+ portable_sampleframe_t *p = painted_ptr;
+
+#if 0
+/* Soft clipping, the sound of a dream, thanks to Jon Wattes
+ post to Musicdsp.org */
+#define SOFTCLIP(x) (x) = sin(bound(-M_PI/2, (x), M_PI/2)) * 0.25
+#endif
+
+ // let's do a simple limiter instead, seems to sound better
+ static float maxvol = 0;
+ maxvol = max(1.0f, maxvol * (1.0f - nbframes / (0.4f * snd_renderbuffer->format.speed)));
+#define SOFTCLIP(x) if(fabs(x)>maxvol) maxvol=fabs(x); (x) /= maxvol;
+
+ if (nchannels == 8) // 7.1 surround
+ {
+ for (i = 0;i < nbframes;i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ SOFTCLIP(p->sample[2]);
+ SOFTCLIP(p->sample[3]);
+ SOFTCLIP(p->sample[4]);
+ SOFTCLIP(p->sample[5]);
+ SOFTCLIP(p->sample[6]);
+ SOFTCLIP(p->sample[7]);
+ }
+ }
+ else if (nchannels == 6) // 5.1 surround
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ SOFTCLIP(p->sample[2]);
+ SOFTCLIP(p->sample[3]);
+ SOFTCLIP(p->sample[4]);
+ SOFTCLIP(p->sample[5]);
+ }
+ }
+ else if (nchannels == 4) // 4.0 surround
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ SOFTCLIP(p->sample[2]);
+ SOFTCLIP(p->sample[3]);
+ }
+ }
+ else if (nchannels == 2) // 2.0 stereo
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ }
+ }
+ else if (nchannels == 1) // 1.0 mono
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ }
+ }
+#undef SOFTCLIP
+ }
+}
-static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int channels)
+static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int nchannels)
{
int i, val;
+
// FIXME: add 24bit and 32bit float formats
// FIXME: optimize with SSE intrinsics?
if (width == 2) // 16bit
{
short *snd_out = (short*)rb_ptr;
- if (channels == 8) // 7.1 surround
+ if (nchannels == 8) // 7.1 surround
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[7] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
- else if (channels == 6) // 5.1 surround
+ else if (nchannels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
- else if (channels == 4) // 4.0 surround
+ else if (nchannels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
- else if (channels == 2) // 2.0 stereo
+ else if (nchannels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
- else if (channels == 1) // 1.0 mono
+ else if (nchannels == 1) // 1.0 mono
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
// noise is really really annoying
if (cls.timedemo)
- memset(rb_ptr, 0, nbframes * channels * width);
+ memset(rb_ptr, 0, nbframes * nchannels * width);
}
else // 8bit
{
unsigned char *snd_out = (unsigned char*)rb_ptr;
- if (channels == 8) // 7.1 surround
+ if (nchannels == 8) // 7.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[7] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
- else if (channels == 6) // 5.1 surround
+ else if (nchannels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
- else if (channels == 4) // 4.0 surround
+ else if (nchannels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
- else if (channels == 2) // 2.0 stereo
+ else if (nchannels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
- else if (channels == 1) // 1.0 mono
+ else if (nchannels == 1) // 1.0 mono
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
// noise is really really annoying
if (cls.timedemo)
- memset(rb_ptr, 128, nbframes * channels);
+ memset(rb_ptr, 128, nbframes * nchannels);
}
}
int loopstart;
int indexfrac;
int indexfracstep;
- const int fetchsampleframesmax = 1024;
- float fetchsampleframes[1024*2];
+#define S_FETCHBUFFERSIZE 4096
+ float fetchsampleframes[S_FETCHBUFFERSIZE*2];
const float *fetchsampleframe;
float vol[SND_LISTENERS];
float lerp[2];
float sample[3];
double posd;
double speedd;
- float sum;
+ float maxvol;
qboolean looping;
qboolean silent;
// paint in the channels.
// channels with zero volumes still advance in time but don't paint.
- ch = channels;
+ ch = channels; // cppcheck complains here but it is wrong, channels is a channel_t[MAX_CHANNELS] and not an int
for (channelindex = 0;channelindex < (int)total_channels;channelindex++, ch++)
{
sfx = ch->sfx;
vol[i] = ch->volume[i];
// check total volume level, because we can skip some code on silent sounds but other code must still run (position updates mainly)
+ maxvol = 0;
for (i = 0;i < SND_LISTENERS;i++)
- sum += vol[i]*vol[i];
- silent = sum < 0.001f;
+ if(vol[i] > maxvol)
+ maxvol = vol[i];
+ switch(snd_renderbuffer->format.width)
+ {
+ case 1: // 8bpp
+ silent = maxvol < (1.0f / (256.0f));
+ // so silent it has zero effect
+ break;
+ case 2: // 16bpp
+ silent = maxvol < (1.0f / (65536.0f));
+ // so silent it has zero effect
+ break;
+ default: // floating point
+ silent = maxvol < 1.0e-13f;
+ // 130 dB is difference between hearing
+ // threshold and a jackhammer from
+ // working distance.
+ // therefore, anyone who turns up
+ // volume so much they notice this
+ // cutoff, likely already has their
+ // ear-drums blown out anyway.
+ break;
+ }
// when doing prologic mixing, some channels invert one side
if (ch->prologic_invert == -1)
// do the actual paint now (may skip work if silent)
paint = paintbuffer;
- wantframes = totalmixframes;
- while (wantframes > 0)
+ istartframe = 0;
+ for (wantframes = totalmixframes;wantframes > 0;posd += count * speedd, wantframes -= count)
{
- // mix full output length (if possible)
- count = wantframes;
+ // check if this is a delayed sound
if (posd < 0)
{
// for a delayed sound we have to eat into the delay first
- count = (int)-posd;
- if (count > wantframes)
- count = wantframes;
- posd += count;
- wantframes -= count;
+ count = (int)floor(-posd / speedd) + 1;
+ count = bound(1, count, wantframes);
+ // let the for loop iterator apply the skip
continue;
}
- // get fetch size
- istartframe = (int)floor(posd);
- iendframe = (int)floor(posd + count * speedd);
- ilengthframes = iendframe + 2 - istartframe;
- // don't overflow fetch buffer
- while (ilengthframes > fetchsampleframesmax)
+ // compute a fetch size that won't overflow our buffer
+ count = wantframes;
+ for (;;)
{
- count /= 2;
- iendframe = (int)floor(posd + count * speedd);
- ilengthframes = iendframe + 2 - istartframe;
- if (count < 2)
- ilengthframes = 2;
+ istartframe = (int)floor(posd);
+ iendframe = (int)floor(posd + (count-1) * speedd);
+ ilengthframes = count > 1 ? (iendframe - istartframe + 2) : 2;
+ if (ilengthframes <= S_FETCHBUFFERSIZE)
+ break;
+ // reduce count by 25% and try again
+ count -= count >> 2;
}
// zero whole fetch buffer for safety
fetched = 0;
for (;;)
{
- fetch = min(ilengthframes, totallength - istartframe);
+ fetch = min(ilengthframes - fetched, totallength - istartframe);
if (fetch > 0)
{
if (!silent)
istartframe = loopstart;
}
else
+ {
break;
+ }
}
// set up our fixedpoint resampling variables (float to int conversions are expensive so do not do one per sampleframe)
}
}
}
- posd += count * speedd;
- wantframes -= count;
}
ch->position = posd;
if (!looping && istartframe == totallength)
S_StopChannel(ch - channels, false, false);
}
+ S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
+
+#ifdef CONFIG_VIDEO_CAPTURE
if (!snd_usethreadedmixing)
S_CaptureAVISound(paintbuffer, totalmixframes);
+#endif
S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);