]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - snd_main.c
made waveOutWrite overrun a non-fatal (if spammy) warning, and only with
[xonotic/darkplaces.git] / snd_main.c
index 988f0109f81b7ee9b076695b980a232e27878f61..251a85909db41f09e07612ea7170991fb2f724c2 100644 (file)
@@ -140,10 +140,12 @@ channel_t channels[MAX_CHANNELS];
 unsigned int total_channels;
 
 snd_ringbuffer_t *snd_renderbuffer = NULL;
-unsigned int soundtime = 0;
+static unsigned int soundtime = 0;
 static unsigned int oldpaintedtime = 0;
 static unsigned int extrasoundtime = 0;
 static double snd_starttime = 0.0;
+qboolean snd_threaded = false;
+qboolean snd_usethreadedmixing = false;
 
 vec3_t listener_origin;
 matrix4x4_t listener_matrix[SND_LISTENERS];
@@ -894,7 +896,7 @@ void S_FreeSfx (sfx_t *sfx, qboolean force)
        // Stop all channels using this sfx
        for (i = 0; i < total_channels; i++)
                if (channels[i].sfx == sfx)
-                       S_StopChannel (i);
+                       S_StopChannel (i, true);
 
        // Free it
        if (sfx->fetcher != NULL && sfx->fetcher->free != NULL)
@@ -1080,7 +1082,7 @@ channel_t *SND_PickChannel(int entnum, int entchannel)
                        if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) )
                        {
                                // always override sound from same entity
-                               S_StopChannel (ch_idx);
+                               S_StopChannel (ch_idx, true);
                                return &channels[ch_idx];
                        }
                }
@@ -1194,9 +1196,10 @@ void SND_Spatialize(channel_t *ch, qboolean isstatic)
 void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic)
 {
        // Initialize the channel
+       // We MUST set sfx LAST because otherwise we could crash a threaded mixer
+       // (otherwise we'd have to call SndSys_LockRenderBuffer here)
        memset (target_chan, 0, sizeof (*target_chan));
        VectorCopy (origin, target_chan->origin);
-       target_chan->sfx = sfx;
        target_chan->flags = flags;
        target_chan->pos = 0; // start of the sound
 
@@ -1213,7 +1216,12 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags,
        // Lock the SFX during play
        S_LockSfx (sfx);
 
-       // and finally, apply the volume
+       // finally, set the sfx pointer, so the channel becomes valid for playback
+       // and will be noticed by the mixer
+       target_chan->sfx = sfx;
+
+       // we have to set the channel volume AFTER the sfx because the function
+       // needs it for replaygain support
        S_SetChannelVolume(target_chan - channels, fvol);
 }
 
@@ -1258,7 +1266,7 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f
        return (target_chan - channels);
 }
 
-void S_StopChannel (unsigned int channel_ind)
+void S_StopChannel (unsigned int channel_ind, qboolean lockmutex)
 {
        channel_t *ch;
 
@@ -1270,6 +1278,12 @@ void S_StopChannel (unsigned int channel_ind)
        {
                sfx_t *sfx = ch->sfx;
 
+               // we have to lock an audio mutex to prevent crashes if an audio mixer
+               // thread is currently mixing this channel
+               // the SndSys_LockRenderBuffer function uses such a mutex in
+               // threaded sound backends
+               if (lockmutex)
+                       SndSys_LockRenderBuffer();
                if (sfx->fetcher != NULL)
                {
                        snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb;
@@ -1282,6 +1296,8 @@ void S_StopChannel (unsigned int channel_ind)
 
                ch->fetcher_data = NULL;
                ch->sfx = NULL;
+               if (lockmutex)
+                       SndSys_UnlockRenderBuffer();
        }
 }
 
@@ -1311,7 +1327,7 @@ void S_StopSound(int entnum, int entchannel)
        for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++)
                if (channels[i].entnum == entnum && channels[i].entchannel == entchannel)
                {
-                       S_StopChannel (i);
+                       S_StopChannel (i, true);
                        return;
                }
 }
@@ -1329,7 +1345,7 @@ void S_StopAllSounds (void)
        CDAudio_Stop();
 
        for (i = 0; i < total_channels; i++)
-               S_StopChannel (i);
+               S_StopChannel (i, true);
 
        total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
        memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
@@ -1471,6 +1487,7 @@ static void S_PaintAndSubmit (void)
                return;
 
        // Update sound time
+       snd_usethreadedmixing = false;
        usesoundtimehack = true;
        if (cls.timedemo) // SUPER NASTY HACK to mix non-realtime sound for more reliable benchmarking
        {
@@ -1489,6 +1506,7 @@ static void S_PaintAndSubmit (void)
        }
        else
        {
+               snd_usethreadedmixing = snd_threaded && !cls.capturevideo.soundrate;
                usesoundtimehack = 0;
                newsoundtime = SndSys_GetSoundTime();
        }
@@ -1516,6 +1534,9 @@ static void S_PaintAndSubmit (void)
        if (!soundtimehack && snd_blocked > 0)
                return;
 
+       if (snd_usethreadedmixing)
+               return; // the audio thread will mix its own data
+
        newsoundtime += extrasoundtime;
        if (newsoundtime < soundtime)
        {
@@ -1543,6 +1564,14 @@ static void S_PaintAndSubmit (void)
        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)
@@ -1557,7 +1586,29 @@ static void S_PaintAndSubmit (void)
        maxtime = paintedtime + snd_renderbuffer->maxframes - usedframes;
        endtime = min(endtime, maxtime);
 
-       S_PaintChannels(snd_renderbuffer, paintedtime, endtime);
+       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;