Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-// snd_mix.c -- portable code to mix sounds for snd_dma.c
#include "quakedef.h"
+#include "snd_main.h"
-#ifdef _WIN32
-#include "winquake.h"
-#else
-#define DWORD unsigned long
+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
+
+#ifdef CONFIG_VIDEO_CAPTURE
+static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
+{
+ size_t i;
+ unsigned int j;
+
+ if (!cls.capturevideo.active)
+ return;
+
+ // 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);
+}
#endif
-// LordHavoc: was 512, expanded to 2048
-#define PAINTBUFFER_SIZE 2048
-portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
-int snd_scaletable[32][256];
-int *snd_p, snd_linear_count, snd_vol;
-short *snd_out;
+extern cvar_t snd_softclip;
-/*
-// LordHavoc: disabled this because it desyncs with the video too easily
-extern cvar_t cl_avidemo;
-static FILE *cl_avidemo_soundfile = NULL;
-void S_CaptureAVISound(portable_samplepair_t *buf, int length)
+static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_ptr, int nbframes, int width, int nchannels)
{
- int i, n;
- qbyte out[PAINTBUFFER_SIZE * 4];
- char filename[MAX_OSPATH];
+ int i;
- if (cl_avidemo.value >= 0.1f)
+ if((snd_softclip.integer == 1 && width <= 2) || snd_softclip.integer > 1)
{
- if (cl_avidemo_soundfile == NULL)
+ 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
{
- sprintf (filename, "%s/dpavi.wav", com_gamedir);
- cl_avidemo_soundfile = fopen(filename, "wb");
- memset(out, 0, 44);
- fwrite(out, 1, 44, cl_avidemo_soundfile);
- // header will be filled out when file is closed
+ 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]);
+ }
}
- fseek(cl_avidemo_soundfile, 0, SEEK_END);
- // write the sound buffer as little endian 16bit interleaved stereo
- for(i = 0;i < length;i++)
+ else if (nchannels == 6) // 5.1 surround
{
- n = buf[i].left >> 2; // quiet enough to prevent clipping most of the time
- n = bound(-32768, n, 32767);
- out[i*4+0] = n & 0xFF;
- out[i*4+1] = (n >> 8) & 0xFF;
- n = buf[i].right >> 2; // quiet enough to prevent clipping most of the time
- n = bound(-32768, n, 32767);
- out[i*4+2] = n & 0xFF;
- out[i*4+3] = (n >> 8) & 0xFF;
+ 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]);
+ }
}
- if (fwrite(out, 4, length, cl_avidemo_soundfile) < length)
+ else if (nchannels == 4) // 4.0 surround
{
- Cvar_SetValueQuick(&cl_avidemo, 0);
- Con_Printf("avi saving sound failed, out of disk space? stopping avi demo capture.\n");
+ 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 (cl_avidemo_soundfile)
- {
- // file has not been closed yet, close it
- fseek(cl_avidemo_soundfile, 0, SEEK_END);
- i = ftell(cl_avidemo_soundfile);
-
- //"RIFF", (int) unknown (chunk size), "WAVE",
- //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
- //"data", (int) unknown (chunk size)
- memcpy(out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\x00data****", 44);
- // the length of the whole RIFF chunk
- n = i - 8;
- out[4] = (n) & 0xFF;
- out[5] = (n >> 8) & 0xFF;
- out[6] = (n >> 16) & 0xFF;
- out[7] = (n >> 24) & 0xFF;
- // rate
- n = shm->speed;
- out[24] = (n) & 0xFF;
- out[25] = (n >> 8) & 0xFF;
- out[26] = (n >> 16) & 0xFF;
- out[27] = (n >> 24) & 0xFF;
- // bytes per second (rate * channels * bytes per channel)
- n = shm->speed * 4;
- out[28] = (n) & 0xFF;
- out[29] = (n >> 8) & 0xFF;
- out[30] = (n >> 16) & 0xFF;
- out[31] = (n >> 24) & 0xFF;
- // the length of the data chunk
- n = i - 44;
- out[40] = (n) & 0xFF;
- out[41] = (n >> 8) & 0xFF;
- out[42] = (n >> 16) & 0xFF;
- out[43] = (n >> 24) & 0xFF;
-
- fseek(cl_avidemo_soundfile, 0, SEEK_SET);
- fwrite(out, 1, 44, cl_avidemo_soundfile);
- fclose(cl_avidemo_soundfile);
- cl_avidemo_soundfile = NULL;
- }
-}
-*/
-
-void Snd_WriteLinearBlastStereo16 (void)
-{
- int i;
- int val;
-
- if (snd_swapstereo.value)
- {
- for (i=0 ; i<snd_linear_count ; i+=2)
+ else if (nchannels == 2) // 2.0 stereo
{
- val = (snd_p[i+1]*snd_vol)>>8;
- snd_out[i ] = bound(-32768, val, 32767);
- val = (snd_p[i ]*snd_vol)>>8;
- snd_out[i+1] = bound(-32768, val, 32767);
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ SOFTCLIP(p->sample[1]);
+ }
}
- }
- else
- {
- for (i=0 ; i<snd_linear_count ; i+=2)
+ else if (nchannels == 1) // 1.0 mono
{
- val = (snd_p[i]*snd_vol)>>8;
- snd_out[i] = bound(-32768, val, 32767);
- val = (snd_p[i+1]*snd_vol)>>8;
- snd_out[i+1] = bound(-32768, val, 32767);
+ for (i = 0; i < nbframes; i++, p++)
+ {
+ SOFTCLIP(p->sample[0]);
+ }
}
+#undef SOFTCLIP
}
}
-void S_TransferStereo16 (int endtime)
+static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int nchannels)
{
- int lpos;
- int lpaintedtime;
- DWORD *pbuf;
-#ifdef _WIN32
- int reps;
- DWORD dwSize,dwSize2;
- DWORD *pbuf2;
- HRESULT hresult;
-#endif
-
- snd_vol = volume.value*256;
-
- snd_p = (int *) paintbuffer;
- lpaintedtime = paintedtime;
+ int i, val;
-#ifdef _WIN32
- if (pDSBuf)
+ // FIXME: add 24bit and 32bit float formats
+ // FIXME: optimize with SSE intrinsics?
+ if (width == 2) // 16bit
{
- reps = 0;
-
- while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, &pbuf2, &dwSize2, 0)) != DS_OK)
+ short *snd_out = (short*)rb_ptr;
+ if (nchannels == 8) // 7.1 surround
{
- if (hresult != DSERR_BUFFERLOST)
+ for (i = 0;i < nbframes;i++, painted_ptr++)
{
- Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
- S_Shutdown ();
- S_Startup ();
- return;
+ 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);
}
-
- if (++reps > 10000)
+ }
+ else if (nchannels == 6) // 5.1 surround
+ {
+ for (i = 0; i < nbframes; i++, painted_ptr++)
{
- Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n");
- S_Shutdown ();
- S_Startup ();
- return;
+ 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 (nchannels == 4) // 4.0 surround
+ {
+ for (i = 0; i < nbframes; i++, painted_ptr++)
+ {
+ 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 (nchannels == 2) // 2.0 stereo
+ {
+ for (i = 0; i < nbframes; i++, painted_ptr++)
+ {
+ 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 (nchannels == 1) // 1.0 mono
+ {
+ for (i = 0; i < nbframes; i++, painted_ptr++)
+ {
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 16384.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
- }
- else
-#endif
- {
- pbuf = (DWORD *)shm->buffer;
- }
-
- while (lpaintedtime < endtime)
- {
- // handle recirculating buffer issues
- lpos = lpaintedtime & ((shm->samples>>1)-1);
-
- snd_out = (short *) pbuf + (lpos<<1);
-
- snd_linear_count = (shm->samples>>1) - lpos;
- if (lpaintedtime + snd_linear_count > endtime)
- snd_linear_count = endtime - lpaintedtime;
-
- snd_linear_count <<= 1;
-
- // write a linear blast of samples
- Snd_WriteLinearBlastStereo16 ();
-
- snd_p += snd_linear_count;
- lpaintedtime += (snd_linear_count>>1);
- }
-
-#ifdef _WIN32
- if (pDSBuf)
- pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
-#endif
-}
-
-void S_TransferPaintBuffer(int endtime)
-{
- int out_idx;
- int count;
- int out_mask;
- int *p;
- int step;
- int val;
- int snd_vol;
- DWORD *pbuf;
-#ifdef _WIN32
- int reps;
- DWORD dwSize,dwSize2;
- DWORD *pbuf2;
- HRESULT hresult;
-#endif
- if (shm->samplebits == 16 && shm->channels == 2)
- {
- S_TransferStereo16 (endtime);
- return;
+ // noise is really really annoying
+ if (cls.timedemo)
+ memset(rb_ptr, 0, nbframes * nchannels * width);
}
-
- p = (int *) paintbuffer;
- count = (endtime - paintedtime) * shm->channels;
- out_mask = shm->samples - 1;
- out_idx = paintedtime * shm->channels & out_mask;
- step = 3 - shm->channels;
- snd_vol = volume.value*256;
-
-#ifdef _WIN32
- if (pDSBuf)
+ else // 8bit
{
- reps = 0;
-
- while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, &pbuf2,&dwSize2, 0)) != DS_OK)
+ unsigned char *snd_out = (unsigned char*)rb_ptr;
+ if (nchannels == 8) // 7.1 surround
{
- if (hresult != DSERR_BUFFERLOST)
+ for (i = 0; i < nbframes; i++, painted_ptr++)
{
- Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
- S_Shutdown ();
- S_Startup ();
- return;
+ 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);
}
-
- if (++reps > 10000)
+ }
+ else if (nchannels == 6) // 5.1 surround
+ {
+ for (i = 0; i < nbframes; i++, painted_ptr++)
{
- Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
- S_Shutdown ();
- S_Startup ();
- return;
+ 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
-#endif
- {
- pbuf = (DWORD *)shm->buffer;
- }
-
- if (shm->samplebits == 16)
- {
- short *out = (short *) pbuf;
- while (count--)
+ else if (nchannels == 4) // 4.0 surround
{
- val = (*p * snd_vol) >> 8;
- out[out_idx] = bound(-32768, val, 32767);
- p+= step;
- out_idx = (out_idx + 1) & out_mask;
+ for (i = 0; i < nbframes; i++, painted_ptr++)
+ {
+ 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 (shm->samplebits == 8)
- {
- unsigned char *out = (unsigned char *) pbuf;
- while (count--)
+ else if (nchannels == 2) // 2.0 stereo
{
- val = ((*p * snd_vol) >> 16) + 128;
- out[out_idx] = bound(0, val, 255);
- p+= step;
- out_idx = (out_idx + 1) & out_mask;
+ for (i = 0; i < nbframes; i++, painted_ptr++)
+ {
+ 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 (nchannels == 1) // 1.0 mono
+ {
+ for (i = 0;i < nbframes;i++, painted_ptr++)
+ {
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 64.0f) + 128; *snd_out++ = bound(0, val, 255);
+ }
}
- }
-
-#ifdef _WIN32
- if (pDSBuf)
- {
- DWORD dwNewpos, dwWrite;
- int il = paintedtime;
- int ir = endtime - paintedtime;
-
- ir += il;
-
- pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
-
- pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
-// if ((dwNewpos >= il) && (dwNewpos <= ir))
-// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
+ // noise is really really annoying
+ if (cls.timedemo)
+ memset(rb_ptr, 128, nbframes * nchannels);
}
-#endif
}
===============================================================================
*/
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
-
-void S_PaintChannels(int endtime)
+void S_MixToBuffer(void *stream, unsigned int bufferframes)
{
- int i;
- int end;
+ int channelindex;
channel_t *ch;
- sfxcache_t *sc;
- int ltime, count;
-
- while (paintedtime < endtime)
+ 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)
{
- // if paintbuffer is smaller than DMA buffer
- end = endtime;
- if (endtime - paintedtime > PAINTBUFFER_SIZE)
- end = paintedtime + PAINTBUFFER_SIZE;
+ // limit to the size of the paint buffer
+ totalmixframes = min(bufferframes, PAINTBUFFER_SIZE);
// clear the paint buffer
- memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
+ memset(paintbuffer, 0, totalmixframes * sizeof(paintbuffer[0]));
// paint in the channels.
- ch = channels;
- for (i=0; i<total_channels ; i++, ch++)
+ // channels with zero volumes still advance in time but don't paint.
+ 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++)
{
- if (!ch->sfx)
+ sfx = ch->sfx;
+ if (sfx == NULL)
continue;
- if (!ch->leftvol && !ch->rightvol)
+ if (!S_LoadSound (sfx, true))
continue;
- sc = S_LoadSound (ch->sfx);
- if (!sc)
+ if (ch->flags & CHANNELFLAG_PAUSED)
+ continue;
+ if (!sfx->total_length)
continue;
- ltime = paintedtime;
-
- while (ltime < end)
+ // 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)
{
- // paint up to end
- if (ch->end < end)
- count = ch->end - ltime;
- else
- count = end - ltime;
+ 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)
+ 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;
- if (count > 0)
+ // do the actual paint now (may skip work if silent)
+ paint = paintbuffer;
+ istartframe = 0;
+ for (wantframes = totalmixframes;wantframes > 0;posd += count * speedd, wantframes -= count)
+ {
+ // check if this is a delayed sound
+ if (posd < 0)
{
- if (sc->width == 1)
- SND_PaintChannelFrom8(ch, sc, count);
- else
- SND_PaintChannelFrom16(ch, sc, 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;
+ }
- ltime += count;
+ // compute a fetch size that won't overflow our buffer
+ count = wantframes;
+ for (;;)
+ {
+ 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;
}
- // if at end of loop, restart
- if (ltime >= ch->end)
+ // 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 (;;)
{
- if (sc->loopstart >= 0)
+ 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)
{
- ch->pos = sc->loopstart;
- ch->end = ltime + sc->length - ch->pos;
+ // loop and fetch some more
+ posd += loopstart - totallength;
+ istartframe = loopstart;
}
else
{
- // channel just stopped
- ch->sfx = NULL;
break;
}
}
- }
-
- }
-
- // transfer out according to DMA format
- //S_CaptureAVISound(paintbuffer, end - paintedtime);
- S_TransferPaintBuffer(end);
- paintedtime = end;
- }
-}
-
-void SND_InitScaletable (void)
-{
- int i, j;
-
- for (i=0 ; i<32 ; i++)
- for (j=0 ; j<256 ; j++)
- snd_scaletable[i][j] = ((signed char)j) * i * 8;
-}
-
-
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
-{
-// int data;
- int *lscale, *rscale;
- unsigned char *sfx;
- int i;
-
- if (ch->leftvol > 255)
- ch->leftvol = 255;
- if (ch->rightvol > 255)
- ch->rightvol = 255;
-
- lscale = snd_scaletable[ch->leftvol >> 3];
- rscale = snd_scaletable[ch->rightvol >> 3];
- if (sc->stereo)
- {
- // LordHavoc: stereo sound support, and optimizations
- sfx = (unsigned char *)sc->data + ch->pos * 2;
-
- for (i=0 ; i<count ; i++)
- {
- paintbuffer[i].left += lscale[*sfx++];
- paintbuffer[i].right += rscale[*sfx++];
- }
-
- }
- else
- {
- sfx = (unsigned char *)sc->data + ch->pos;
- for (i=0 ; i<count ; i++)
- {
- paintbuffer[i].left += lscale[*sfx];
- paintbuffer[i].right += rscale[*sfx++];
+ // 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);
}
- }
- ch->pos += count;
-}
+ S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
-{
-// int data;
-// int left, right;
- int leftvol, rightvol;
- signed short *sfx;
- int i;
-
- leftvol = ch->leftvol;
- rightvol = ch->rightvol;
- if (sc->stereo)
- {
- // LordHavoc: stereo sound support, and optimizations
- sfx = (signed short *)sc->data + ch->pos * 2;
+#ifdef CONFIG_VIDEO_CAPTURE
+ if (!snd_usethreadedmixing)
+ S_CaptureAVISound(paintbuffer, totalmixframes);
+#endif
- for (i=0 ; i<count ; i++)
- {
- paintbuffer[i].left += (*sfx++ * leftvol) >> 8;
- paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
- }
- }
- else
- {
- sfx = (signed short *)sc->data + ch->pos;
+ S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
- for (i=0 ; i<count ; i++)
- {
- paintbuffer[i].left += (*sfx * leftvol) >> 8;
- paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
- }
+ // advance the output pointer
+ outbytes += totalmixframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ bufferframes -= totalmixframes;
}
-
- ch->pos += count;
}
-