View Issue Details

IDProjectCategoryView StatusLast Update
0004191The Dark ModSoundpublic14.08.2015 17:48
ReporterNagaHuntress Assigned ToSteveL  
PrioritynormalSeveritynormalReproducibilityrandom
Status resolvedResolutionfixed 
OSLinux 
Product VersionTDM 2.03 
Target VersionTDM 2.04Fixed in VersionTDM 2.04 
Summary0004191: Popping/crackling sound when running on Linux
DescriptionWhen running the game, there's a sizable chance that the audio will start popping or crackling, as if the audio is being interrupt for a fraction of a second. This problem will manifest either immediately upon running the game, or a few seconds after.

When this problem does occur the following can be seen printed repeatedly in the terminal window used to run the game:
snd_pcm_writei 4096 frames failed: Broken pipe
preparing audio device for output


This problem has been experienced on the following distros:
Ubuntu 12.04 (32 bit)
Linux Mint 15 (64 bit)
Linux Mint 17.2 (64 bit)
TagsNo tags attached.
Attached Files
tdm-linux-sound-fix.diff (2,526 bytes)   
Index: sys/linux/sound_alsa.cpp
===================================================================
--- sys/linux/sound_alsa.cpp	(revision 6521)
+++ sys/linux/sound_alsa.cpp	(working copy)
@@ -26,6 +26,11 @@
 static idCVar s_alsa_pcm( "s_alsa_pcm", "default", CVAR_SYSTEM | CVAR_ARCHIVE, "which alsa pcm device to use. default, hwplug, hw.. see alsa docs" );
 static idCVar s_alsa_lib( "s_alsa_lib", "libasound.so.2", CVAR_SYSTEM | CVAR_ARCHIVE, "alsa client sound library" );
 
+/**
+This idCVar configures how many extra frames of audio data it sends when the alsa driver experiences an underrun. This is more of a work around then a fix, as the root cause of the issue is likely elsewhere.
+ */
+static idCVar s_alsa_underrun_extrafill( "s_alsa_underrun_extrafill", "1024", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "If an underrun error occurs while outputing ausio, it will retry and refill the audio stream with extra data to try and clear the underrun condition. This specifies the number of extra frames." );
+
 /*
 ===============
 idAudioHardwareALSA::DLOpen
@@ -299,6 +304,20 @@
 	// write the max frames you can in one shot - we need to write it all out in Flush() calls before the next Write() happens
 	int pos = (int)m_buffer + ( MIXBUFFER_SAMPLES - m_remainingFrames ) * m_channels * 2;
 	snd_pcm_sframes_t frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, m_remainingFrames );
+	/*
+	  A kludge to handle buffer underruns (reported as broken pipes). 
+	  To the user this condition manifests as popping or crackling audio.
+	  This will re-ready the sound out and send the pending data twice.
+	  The root cause of this error is likely to lie else where in the audio pipeline, but this work around plugs the problem for now. 
+	*/
+	if ( frames == -EPIPE ) {
+		snd_pcm_sframes_t nextframes = s_alsa_underrun_extrafill.GetInteger();
+		nextframes = (m_remainingFrames > nextframes) ? nextframes : m_remainingFrames;
+		Sys_Printf( "snd_pcm_writei() reports broken pipe (underrun) while sending %d frames. Retrying but also sending %d duplicate frames first. Try increasing 's_alsa_underrun_extrafill' if this persists.\n", m_remainingFrames, nextframes);
+		id_snd_pcm_prepare( m_pcm_handle );
+		frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, nextframes );
+		frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, m_remainingFrames );
+	}
 	if ( frames < 0 ) {
 		if ( frames != -EAGAIN ) {
 			Sys_Printf( "snd_pcm_writei %d frames failed: %s\n", m_remainingFrames, id_snd_strerror( frames ) );
tdm-linux-sound-fix.diff (2,526 bytes)   
tdm-linux-sound-fix-v2.diff (2,764 bytes)   
Index: sys/linux/sound_alsa.cpp
===================================================================
--- sys/linux/sound_alsa.cpp	(revision 6527)
+++ sys/linux/sound_alsa.cpp	(working copy)
@@ -26,6 +26,11 @@
 static idCVar s_alsa_pcm( "s_alsa_pcm", "default", CVAR_SYSTEM | CVAR_ARCHIVE, "which alsa pcm device to use. default, hwplug, hw.. see alsa docs" );
 static idCVar s_alsa_lib( "s_alsa_lib", "libasound.so.2", CVAR_SYSTEM | CVAR_ARCHIVE, "alsa client sound library" );
 
+/**
+This idCVar configures how many extra frames of audio data it sends when the alsa driver experiences an underrun. This is more of a work around then a fix, as the root cause of the issue is likely elsewhere.
+ */
+static idCVar s_alsa_underrun_extrafill( "s_alsa_underrun_extrafill", "1024", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_INTEGER, "If an underrun error occurs while outputing ausio, it will retry and refill the audio stream with extra data to try and clear the underrun condition. This specifies the number of extra frames.", 0, 8192 );
+
 /*
 ===============
 idAudioHardwareALSA::DLOpen
@@ -297,8 +302,25 @@
 		return;
 	}
 	// write the max frames you can in one shot - we need to write it all out in Flush() calls before the next Write() happens
-	int pos = (int)m_buffer + ( MIXBUFFER_SAMPLES - m_remainingFrames ) * m_channels * 2;
+	size_t pos = (size_t)m_buffer + ( MIXBUFFER_SAMPLES - m_remainingFrames ) * m_channels * 2;
 	snd_pcm_sframes_t frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, m_remainingFrames );
+	/*
+	  A kludge to handle buffer underruns (reported as broken pipes). 
+	  To the user this condition manifests as popping or crackling audio.
+	  This will re-ready the sound out and send the pending data twice.
+	  The root cause of this error is likely to lie else where in the audio pipeline, but this work around plugs the problem for now. 
+	*/
+	if ( frames == -EPIPE ) {
+		snd_pcm_sframes_t nextframes = s_alsa_underrun_extrafill.GetInteger();
+		nextframes = (nextframes < 0) ? 0 : nextframes;
+		nextframes = (m_remainingFrames > nextframes) ? nextframes : m_remainingFrames;
+		Sys_Printf( "snd_pcm_writei() reports broken pipe (underrun) while sending %u frames. Retrying but also sending %u duplicate frames first. Try increasing 's_alsa_underrun_extrafill' if this persists.\n", (unsigned int)m_remainingFrames, (unsigned int)nextframes);
+		id_snd_pcm_prepare( m_pcm_handle );
+		if(nextframes) {
+			frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, nextframes );
+		}
+		frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, m_remainingFrames );
+	}
 	if ( frames < 0 ) {
 		if ( frames != -EAGAIN ) {
 			Sys_Printf( "snd_pcm_writei %d frames failed: %s\n", m_remainingFrames, id_snd_strerror( frames ) );
tdm-linux-sound-fix-v2.diff (2,764 bytes)   

Activities

NagaHuntress

NagaHuntress

31.07.2015 01:11

reporter   ~0007679

The attached diff file fixes the issue of buffer underrun (broken pipes) on the audio output by re-preparing the audio connection and sending extra audio data in the retry. The extra data in the retry pads the output buffer so that it won't run out of audio data too soon.

This patch is probably more a work around rather than a fix that addresses the root cause of the issue.
NagaHuntress

NagaHuntress

14.08.2015 17:20

reporter   ~0007715

Made a second version of the patch which adds these changes:
 - Sets the CVAR to an integer and limits the range from 0 to 8192.
 - If it's set to 0, it will skip sending the extra block of data.
SteveL

SteveL

14.08.2015 17:48

reporter   ~0007716

At revision 6529

/trunk/sys/linux/sound_alsa.cpp

Issue History

Date Modified Username Field Change
31.07.2015 01:01 NagaHuntress New Issue
31.07.2015 01:02 NagaHuntress File Added: tdm-linux-sound-fix.diff
31.07.2015 01:11 NagaHuntress Note Added: 0007679
13.08.2015 18:51 SteveL Assigned To => SteveL
13.08.2015 18:51 SteveL Status new => assigned
13.08.2015 18:52 SteveL Product Version => TDM 2.03
13.08.2015 18:52 SteveL Target Version => TDM 2.04
14.08.2015 17:16 NagaHuntress File Added: tdm-linux-sound-fix-v2.diff
14.08.2015 17:20 NagaHuntress Note Added: 0007715
14.08.2015 17:48 SteveL Note Added: 0007716
14.08.2015 17:48 SteveL Status assigned => resolved
14.08.2015 17:48 SteveL Fixed in Version => TDM 2.04
14.08.2015 17:48 SteveL Resolution open => fixed