#include "quakedef.h"
#include "snd_main.h"
+extern cvar_t snd_softclip;
-typedef struct portable_samplepair_s
-{
- int sample[SND_LISTENERS];
-} portable_sampleframe_t;
-
-// LordHavoc: was 512, expanded to 2048
-#define PAINTBUFFER_SIZE 2048
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(unsigned char *bufstereo16le, size_t length, int rate);
-static void S_CaptureAVISound(size_t length)
+static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
{
size_t i;
- unsigned char out[PAINTBUFFER_SIZE * 4];
- unsigned char* out_ptr;
+ unsigned int j;
if (!cls.capturevideo.active)
return;
- // write the sound buffer as little endian 16bit interleaved stereo
- for(i = 0, out_ptr = out; i < length; i++, out_ptr += 4)
+ // undo whatever swapping the channel layout (swapstereo, ALSA) did
+ for(j = 0; j < snd_speakerlayout.channels; ++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];
+ }
+
+ SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
+}
+
+extern cvar_t snd_softclip;
+
+static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_ptr, int nbframes, int width, int channels)
+{
+ int i;
+
+ if((snd_softclip.integer == 1 && width <= 2) || snd_softclip.integer > 1)
{
- int n0, n1;
+ portable_sampleframe_t *p = painted_ptr;
- n0 = paintbuffer[i].sample[0];
- n0 = bound(-32768, n0, 32767);
- out_ptr[0] = (unsigned char)n0;
- out_ptr[1] = (unsigned char)(n0 >> 8);
+#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;
- n1 = paintbuffer[i].sample[1];
- n1 = bound(-32768, n1, 32767);
- out_ptr[2] = (unsigned char)n1;
- out_ptr[3] = (unsigned char)(n1 >> 8);
+ if (channels == 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 (channels == 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 (channels == 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 (channels == 2) // 2.0 stereo
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ }
+ }
+ else if (channels == 1) // 1.0 mono
+ {
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ }
+ }
+#undef SOFTCLIP
}
- SCR_CaptureVideo_SoundFrame(out, length, snd_renderbuffer->format.speed);
}
-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 channels)
{
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;
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[6] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[7] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 1) // 1.0 mono
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] + painted_ptr->sample[1]) >> 1;
- *snd_out++ = bound(-32768, val, 32767);
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 16384.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
+
+ // noise is really really annoying
+ if (cls.timedemo)
+ memset(rb_ptr, 0, nbframes * channels * width);
}
else // 8bit
{
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[6] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[7] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 1) // 1.0 mono
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
- val = ((painted_ptr->sample[0] + painted_ptr->sample[1]) >> 9) + 128;
- *snd_out++ = bound(0, val, 255);
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 64.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
+
+ // noise is really really annoying
+ if (cls.timedemo)
+ memset(rb_ptr, 128, nbframes * channels);
}
}
===============================================================================
*/
-static qboolean SND_PaintChannel (channel_t *ch, portable_sampleframe_t *paint, unsigned int count)
-{
- int snd_vol, vol[SND_LISTENERS];
- const snd_buffer_t *sb;
- unsigned int i, sb_offset;
-
- // If this channel manages its own volume
- if (ch->flags & CHANNELFLAG_FULLVOLUME)
- snd_vol = 256;
- else
- snd_vol = (int)(volume.value * 256);
-
- // calculate mixing volumes based on channel volumes and volume cvar
- // also limit the volumes to values that won't clip
- for (i = 0;i < SND_LISTENERS;i++)
- {
- vol[i] = ch->listener_volume[i] * snd_vol;
- vol[i] = bound(0, vol[i], 65536);
- }
-
- // if volumes are all zero, just return
- for (i = 0;i < SND_LISTENERS;i++)
- if (vol[i])
- break;
- if (i == SND_LISTENERS)
- return false;
-
- sb_offset = ch->pos;
- sb = ch->sfx->fetcher->getsb (ch->sfx->fetcher_data, &ch->fetcher_data, &sb_offset, count);
- if (sb == NULL)
- {
- Con_DPrintf("SND_PaintChannel: ERROR: can't get sound buffer from sfx \"%s\"\n",
- ch->sfx->name); // , count); // or add this? FIXME
- return false;
- }
- else
- {
-#if SND_LISTENERS != 8
-# error the following code only supports up to 8 channels, update it
-#endif
- if (sb->format.width == 1)
- {
- const signed char *samples = (signed char*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
-
- // Stereo sound support
- if (sb->format.channels == 2)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 8;
- paint[i].sample[7] += (samples[1] * vol[7]) >> 8;
- samples += 2;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
- samples += 2;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- samples += 2;
- }
- }
- }
- else if (sb->format.channels == 1)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 8;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 8;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 8;
- paint[i].sample[7] += (samples[0] * vol[7]) >> 8;
- samples += 1;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 8;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 8;
- samples += 1;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- samples += 1;
- }
- }
- }
- else
- return false; // unsupported number of channels in sound
- }
- else if (sb->format.width == 2)
- {
- const signed short *samples = (signed short*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
-
- // Stereo sound support
- if (sb->format.channels == 2)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 16;
- paint[i].sample[7] += (samples[1] * vol[7]) >> 16;
- samples += 2;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
- samples += 2;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- samples += 2;
- }
- }
- }
- else if (sb->format.channels == 1)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 16;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 16;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 16;
- paint[i].sample[7] += (samples[0] * vol[7]) >> 16;
- samples += 1;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 16;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 16;
- samples += 1;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- samples += 1;
- }
- }
- }
- else
- return false; // unsupported number of channels in sound
- }
- }
- return true;
-}
-
void S_MixToBuffer(void *stream, unsigned int bufferframes)
{
- unsigned int i;
+ int channelindex;
channel_t *ch;
- unsigned int frames;
+ int totalmixframes;
unsigned char *outbytes = (unsigned char *) stream;
+ sfx_t *sfx;
+ portable_sampleframe_t *paint;
+ int wantframes;
+ int i;
+ int count;
+ int fetched;
+ int fetch;
+ int istartframe;
+ int iendframe;
+ int ilengthframes;
+ int totallength;
+ int loopstart;
+ int indexfrac;
+ int indexfracstep;
+#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 maxvol;
+ qboolean looping;
+ qboolean silent;
// mix as many times as needed to fill the requested buffer
while (bufferframes)
{
// limit to the size of the paint buffer
- frames = min(bufferframes, PAINTBUFFER_SIZE);
+ totalmixframes = min(bufferframes, PAINTBUFFER_SIZE);
// clear the paint buffer
- memset (paintbuffer, 0, frames * sizeof (paintbuffer[0]));
+ memset(paintbuffer, 0, totalmixframes * sizeof(paintbuffer[0]));
// paint in the channels.
// channels with zero volumes still advance in time but don't paint.
- ch = channels;
- for (i = 0; i < total_channels ; i++, ch++)
+ 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_t *sfx;
- unsigned int ltime;
- unsigned int count;
-
sfx = ch->sfx;
if (sfx == NULL)
continue;
continue;
if (ch->flags & CHANNELFLAG_PAUSED)
continue;
+ if (!sfx->total_length)
+ continue;
- ltime = 0;
- if (ch->pos < 0)
+ // copy the channel information to the stack for reference, otherwise the
+ // values might change during a mix if the spatializer is updating them
+ // (note: this still may get some old and some new values!)
+ posd = ch->position;
+ speedd = ch->mixspeed * sfx->format.speed / snd_renderbuffer->format.speed;
+ for (i = 0;i < SND_LISTENERS;i++)
+ 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++)
+ if(vol[i] > maxvol)
+ maxvol = vol[i];
+ switch(snd_renderbuffer->format.width)
{
- count = -ch->pos;
- count = min(count, frames - ltime);
- ch->pos += count;
- ltime += count;
+ 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;
}
- while (ltime < frames)
+ // when doing prologic mixing, some channels invert one side
+ if (ch->prologic_invert == -1)
+ vol[1] *= -1.0f;
+
+ // get some sfx info in a consistent form
+ totallength = sfx->total_length;
+ loopstart = (int)sfx->loopstart < totallength ? (int)sfx->loopstart : ((ch->flags & CHANNELFLAG_FORCELOOP) ? 0 : totallength);
+ looping = loopstart < totallength;
+
+ // do the actual paint now (may skip work if silent)
+ paint = paintbuffer;
+ istartframe = 0;
+ for (wantframes = totalmixframes;wantframes > 0;posd += count * speedd, wantframes -= count)
{
- // paint up to end of buffer or of input, whichever is lower
- count = sfx->total_length - ch->pos;
- count = bound(0, count, frames - ltime);
- if (count)
+ // check if this is a delayed sound
+ if (posd < 0)
{
- SND_PaintChannel (ch, paintbuffer + ltime, count);
- ch->pos += count;
- ltime += count;
+ // for a delayed sound we have to eat into the delay first
+ count = (int)floor(-posd / speedd) + 1;
+ count = bound(1, count, wantframes);
+ // let the for loop iterator apply the skip
+ continue;
}
- // if at end of sfx, loop or stop the channel
- if (ch->pos >= (int)sfx->total_length)
+ // compute a fetch size that won't overflow our buffer
+ count = wantframes;
+ for (;;)
{
- if (sfx->loopstart < sfx->total_length)
- ch->pos = sfx->loopstart;
- else if (ch->flags & CHANNELFLAG_FORCELOOP)
- ch->pos = 0;
+ 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
+ // (floating point noise from uninitialized memory = HORRIBLE)
+ // otherwise we would only need to clear the excess
+ if (!silent)
+ memset(fetchsampleframes, 0, ilengthframes*sfx->format.channels*sizeof(fetchsampleframes[0]));
+
+ // if looping, do multiple fetches
+ fetched = 0;
+ for (;;)
+ {
+ fetch = min(ilengthframes - fetched, totallength - istartframe);
+ if (fetch > 0)
+ {
+ if (!silent)
+ sfx->fetcher->getsamplesfloat(ch, sfx, istartframe, fetch, fetchsampleframes + fetched*sfx->format.channels);
+ istartframe += fetch;
+ fetched += fetch;
+ }
+ if (istartframe == totallength && looping && fetched < ilengthframes)
+ {
+ // loop and fetch some more
+ posd += loopstart - totallength;
+ istartframe = loopstart;
+ }
else
{
- S_StopChannel (ch - channels, false);
break;
}
}
+
+ // set up our fixedpoint resampling variables (float to int conversions are expensive so do not do one per sampleframe)
+ fetchsampleframe = fetchsampleframes;
+ indexfrac = (int)floor((posd - floor(posd)) * 65536.0);
+ indexfracstep = (int)floor(speedd * 65536.0);
+ if (!silent)
+ {
+ if (sfx->format.channels == 2)
+ {
+ // music is stereo
+#if SND_LISTENERS != 8
+#error the following code only supports up to 8 channels, update it
+#endif
+ if (snd_speakerlayout.channels > 2)
+ {
+ // surround mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
+ sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
+ sample[2] = (sample[0] + sample[1]) * 0.5f;
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[1] * vol[1];
+ paint->sample[2] += sample[0] * vol[2];
+ paint->sample[3] += sample[1] * vol[3];
+ paint->sample[4] += sample[2] * vol[4];
+ paint->sample[5] += sample[2] * vol[5];
+ paint->sample[6] += sample[0] * vol[6];
+ paint->sample[7] += sample[1] * vol[7];
+ indexfrac += indexfracstep;
+ fetchsampleframe += 2 * (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ else
+ {
+ // stereo mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
+ sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[1] * vol[1];
+ indexfrac += indexfracstep;
+ fetchsampleframe += 2 * (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ }
+ else if (sfx->format.channels == 1)
+ {
+ // most sounds are mono
+#if SND_LISTENERS != 8
+#error the following code only supports up to 8 channels, update it
+#endif
+ if (snd_speakerlayout.channels > 2)
+ {
+ // surround mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[0] * vol[1];
+ paint->sample[2] += sample[0] * vol[2];
+ paint->sample[3] += sample[0] * vol[3];
+ paint->sample[4] += sample[0] * vol[4];
+ paint->sample[5] += sample[0] * vol[5];
+ paint->sample[6] += sample[0] * vol[6];
+ paint->sample[7] += sample[0] * vol[7];
+ indexfrac += indexfracstep;
+ fetchsampleframe += (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ else
+ {
+ // stereo mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[0] * vol[1];
+ indexfrac += indexfracstep;
+ fetchsampleframe += (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ }
+ }
}
+ 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);
+
if (!snd_usethreadedmixing)
- S_CaptureAVISound(frames);
+ S_CaptureAVISound(paintbuffer, totalmixframes);
- S_ConvertPaintBuffer(paintbuffer, outbytes, frames, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
+ S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
// advance the output pointer
- outbytes += frames * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
- bufferframes -= frames;
+ outbytes += totalmixframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ bufferframes -= totalmixframes;
}
}