X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_mix.c;h=736d6ea1964f54ab7d11485daafde3d5d498f039;hp=e01ca0ee7d5b5b705ae17653bc79f7967cd353cc;hb=849c92ba2b16d3af63f04b3c2fab755f737693a9;hpb=ff0e0fd9ea6ce001e9c2c6f9e18ecda6aef15d10 diff --git a/snd_mix.c b/snd_mix.c index e01ca0ee..736d6ea1 100644 --- a/snd_mix.c +++ b/snd_mix.c @@ -21,14 +21,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #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); -static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length) +#ifdef CONFIG_VIDEO_CAPTURE +static portable_sampleframe_t paintbuffer_unswapped[PAINTBUFFER_SIZE]; + +static void S_CaptureAVISound(const portable_sampleframe_t *sampleframes, size_t length) { size_t i; unsigned int j; @@ -41,21 +43,156 @@ static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t { 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); } +#endif + +extern cvar_t snd_softclip; -static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int channels) +static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_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 + 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(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int nchannels) +{ + int i; + float val; + 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]; + } + } + + // 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 (channels == 8) // 7.1 surround + if (nchannels == 8) // 7.1 surround { for (i = 0;i < nbframes;i++, painted_ptr++) { @@ -69,7 +206,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -81,7 +218,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -91,7 +228,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -99,7 +236,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -109,12 +246,12 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void // 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++) { @@ -128,7 +265,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -140,7 +277,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -150,7 +287,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -158,7 +295,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void 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++) { @@ -168,7 +305,7 @@ static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void // noise is really really annoying if (cls.timedemo) - memset(rb_ptr, 128, nbframes * channels); + memset(rb_ptr, 128, nbframes * nchannels); } } @@ -201,17 +338,17 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) 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; - qboolean looping; - qboolean silent; + float maxvol; + qbool looping; + qbool silent; // mix as many times as needed to fill the requested buffer while (bufferframes) @@ -224,7 +361,7 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) // 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; @@ -246,9 +383,31 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) 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) @@ -261,34 +420,30 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) // 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 @@ -301,7 +456,7 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) fetched = 0; for (;;) { - fetch = min(ilengthframes, totallength - istartframe); + fetch = min(ilengthframes - fetched, totallength - istartframe); if (fetch > 0) { if (!silent) @@ -316,7 +471,9 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) istartframe = loopstart; } else + { break; + } } // set up our fixedpoint resampling variables (float to int conversions are expensive so do not do one per sampleframe) @@ -415,16 +572,18 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) } } } - 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);