February 10th, 2013 at 22:46 UTC by Steven J. Murdoch
I’m working on a security-related project with the Raspberry Pi and have encountered an annoying problem with the on-board sound output. I’ve managed to work around this, so thought it might be helpful the share my experiences with others in the same situation.
The problem manifests itself as a loud pop or click, just before sound is output and just after sound output is stopped. This is because a PWM output of the BCM2835 CPU is being used, rather than a standard DAC. When the PWM function is activated, there’s a jump in output voltage which results in the popping sound.
Until there’s a driver modification, the work-around suggested (other than using the HDMI sound output or an external USB sound card) is to run PulseAudio on top of ALSA and keep the driver active even when no sound is being output. This is achieved by disabling the
module-suspend-on-idle PulseAudio module, then configuring applications to use PulseAudio rather than ALSA. Daniel Bader describes this work-around and how to configure MPD, in a blog post. However, when I tried this approach, the work-around didn’t work.
module-suspend-on-idle was enabled, there was the popping sound but when
module-suspend-on-idle was disabled, there was no sound output. I investigated this, mainly though the debug logs in
/var/log/messages produced by PulseAudio after starting it with
pulseaudio --start --log-target=syslog --log-level=4. I also had to look at the source occasionally too.
The problem turned out to be because the audio files I was playing were mono with a sampling rate of 16,000 Hz or 48,000 Hz, and the default audio input format for the ALSA driver was stereo (with a 48,000 Hz sampling rate). When the audio was being played, PulseAudio had to change the input format to match, even if the only change was stereo to mono. This format change involves resetting the driver and so causes a pop as it is deactivated and re-activated even if
module-suspend-on-idle has not suspended the driver. Once audio playback is finished, the original audio format is restored, leading to a pop then too.
What is worse is that if
module-suspend-on-idle is disabled there’s no audio output. When PulseAudio switches the format, the driver is suspended but never resumes. There are some comments in the PulseAudio source which suggest that
module-suspend-on-idle is assumed to be available and is responsible for resuming drivers on a format change. Therefore the work-around for the popping, of disabling
module-suspend-on-idle, isn’t suitable when the audio input format is not stereo at 48,000 Hz.
Rather than disabling
module-suspend-on-idle, the work-around I arrived on was to set a very long idle timeout. This is done by appending
timeout=604800 to the line starting with
load-module module-suspend-on-idle in
/etc/pulse/default.pa. Now, when there was a format change, the driver is still resumed but the driver will not be suspended until a week (604,800 seconds) after the last audio has been played.
Doing this, we don’t get popping between tracks when they are all stereo at 48,000 Hz, and at least other audio input formats play back. However, this doesn’t fix the popping on format change. To deal with this, I converted the audio to 48,000 Hz stereo before sending it to PulseAudio. One program which can do this is mplayer. For example to play back an audio file using PulseAudio do:
mplayer -ao pulse -af channels=2,resample=48000:1 FILENAME
channels=2 converts mono to stereo and the
resample=48000:1 converts the sampling rate to 48,000 Hz. The mplayer manpage gives more detail. These options can also be combined with other uses of mplayer, for example as an audio output program for the Festival speech synthesis program, by adding the following to your
(Parameter.set 'Audio_Command "mplayer -really-quiet -noconsolecontrols -nojoystick -nolirc -nomouseinput -demuxer rawaudio -rawaudio channels=1:rate=$SR $FILE -ao pulse -af channels=2,resample=48000:1")
Update (2013-03-07): According to a comment on the Raspberry Pi bug tracker, this problem has now been fixed.