+static void S_PaintAndSubmit (void)
+{
+ unsigned int newsoundtime, paintedtime, endtime, maxtime, usedframes;
+ int usesoundtimehack;
+ static int soundtimehack = -1;
+ static int oldsoundtime = 0;
+
+ if (snd_renderbuffer == NULL || nosound.integer)
+ return;
+
+ // Update sound time
+ snd_usethreadedmixing = false;
+ usesoundtimehack = true;
+ if (cls.timedemo) // SUPER NASTY HACK to mix non-realtime sound for more reliable benchmarking
+ {
+ usesoundtimehack = 1;
+ newsoundtime = (unsigned int)((double)cl.mtime[0] * (double)snd_renderbuffer->format.speed);
+ }
+ else if (cls.capturevideo.soundrate && !cls.capturevideo.realtime) // SUPER NASTY HACK to record non-realtime sound
+ {
+ usesoundtimehack = 2;
+ newsoundtime = (unsigned int)((double)cls.capturevideo.frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo.framerate);
+ }
+ else if (simsound)
+ {
+ usesoundtimehack = 3;
+ newsoundtime = (unsigned int)((realtime - snd_starttime) * (double)snd_renderbuffer->format.speed);
+ }
+ else
+ {
+ snd_usethreadedmixing = snd_threaded && !cls.capturevideo.soundrate;
+ usesoundtimehack = 0;
+ newsoundtime = SndSys_GetSoundTime();
+ }
+ // if the soundtimehack state changes we need to reset the soundtime
+ if (soundtimehack != usesoundtimehack)
+ {
+ snd_renderbuffer->startframe = snd_renderbuffer->endframe = soundtime = newsoundtime;
+
+ // Mute the contents of the submission buffer
+ if (simsound || SndSys_LockRenderBuffer ())
+ {
+ int clear;
+ size_t memsize;
+
+ clear = (snd_renderbuffer->format.width == 1) ? 0x80 : 0;
+ memsize = snd_renderbuffer->maxframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ memset(snd_renderbuffer->ring, clear, memsize);
+
+ if (!simsound)
+ SndSys_UnlockRenderBuffer ();
+ }
+ }
+ soundtimehack = usesoundtimehack;
+
+ if (!soundtimehack && snd_blocked > 0)
+ return;
+
+ if (snd_usethreadedmixing)
+ return; // the audio thread will mix its own data
+
+ newsoundtime += extrasoundtime;
+ if (newsoundtime < soundtime)
+ {
+ if ((cls.capturevideo.soundrate != 0) != recording_sound)
+ {
+ unsigned int additionaltime;
+
+ // add some time to extrasoundtime make newsoundtime higher
+
+ // The extra time must be a multiple of the render buffer size
+ // to avoid modifying the current position in the buffer,
+ // some modules write directly to a shared (DMA) buffer
+ additionaltime = (soundtime - newsoundtime) + snd_renderbuffer->maxframes - 1;
+ additionaltime -= additionaltime % snd_renderbuffer->maxframes;
+
+ extrasoundtime += additionaltime;
+ newsoundtime += additionaltime;
+ Con_DPrintf("S_PaintAndSubmit: new extra sound time = %u\n",
+ extrasoundtime);
+ }
+ else if (!soundtimehack)
+ Con_Printf("S_PaintAndSubmit: WARNING: newsoundtime < soundtime (%u < %u)\n",
+ newsoundtime, soundtime);
+ }
+ soundtime = newsoundtime;
+ recording_sound = (cls.capturevideo.soundrate != 0);
+
+ // Lock submitbuffer
+ if (!simsound && !SndSys_LockRenderBuffer())
+ {
+ // If the lock failed, stop here
+ Con_DPrint(">> S_PaintAndSubmit: SndSys_LockRenderBuffer() failed\n");
+ return;
+ }
+
+ // Check to make sure that we haven't overshot
+ paintedtime = snd_renderbuffer->endframe;
+ if (paintedtime < soundtime)
+ paintedtime = soundtime;
+
+ // mix ahead of current position
+ if (soundtimehack)
+ endtime = soundtime + (unsigned int)(_snd_mixahead.value * (float)snd_renderbuffer->format.speed);
+ else
+ endtime = soundtime + (unsigned int)(max(_snd_mixahead.value * (float)snd_renderbuffer->format.speed, min(3 * (soundtime - oldsoundtime), 0.3 * (float)snd_renderbuffer->format.speed)));
+ usedframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+ maxtime = paintedtime + snd_renderbuffer->maxframes - usedframes;
+ endtime = min(endtime, maxtime);
+
+ while (paintedtime < endtime)
+ {
+ unsigned int startoffset;
+ unsigned int nbframes;
+
+ // see how much we can fit in the paint buffer
+ nbframes = endtime - paintedtime;
+ // limit to the end of the ring buffer (in case of wrapping)
+ startoffset = paintedtime % snd_renderbuffer->maxframes;
+ nbframes = min(nbframes, snd_renderbuffer->maxframes - startoffset);
+
+ // mix into the buffer
+ S_MixToBuffer(&snd_renderbuffer->ring[startoffset * snd_renderbuffer->format.width * snd_renderbuffer->format.channels], nbframes);
+
+ paintedtime += nbframes;
+ snd_renderbuffer->endframe = paintedtime;
+ }
+ if (!simsound)
+ SndSys_UnlockRenderBuffer();
+
+ // Remove outdated samples from the ring buffer, if any
+ if (snd_renderbuffer->startframe < soundtime)
+ snd_renderbuffer->startframe = soundtime;
+
+ if (simsound)
+ snd_renderbuffer->startframe = snd_renderbuffer->endframe;
+ else
+ SndSys_Submit();
+
+ oldsoundtime = soundtime;
+
+ cls.soundstats.latency_milliseconds = (snd_renderbuffer->endframe - snd_renderbuffer->startframe) * 1000 / snd_renderbuffer->format.speed;
+}