Real-time background noise cancellation of Microphone input on linux

I’m surprised at how little information there is about this topic, short of the majority of forums indicating it cannot be done. Having a poor quality mic, both integrated into a laptop as well as a cheap external microphone, I know my sound quality is not the best, and a fair bit of background noise can be picked up. When I discovered that audio mixing software, such as Audacity, can do a very good job of removing background noise based on a sound sample, I figured there would be a way of doing this in real-time. Although I have not found any information on doing this with Audacity (nor did I expect to), I only really found a single Ask Ubuntu forum answer with an explanation of how to accomplish this, and to my surprise (despite it not being the ‘best answer’ of that thread), it works, and quite well to boot.

By utilizing SoX audio software, the microphone input is redirected through it, and output into a loopback device, which can be chosen in PulseAudio’s volume mixer application. I have composed a bash script to essentially automate the process. Before using this, SoX must be installed, and the alsa loopback device must be loaded into the kernel:

sudo apt-get install sox

sudo modprobe snd_aloop


#!/bin/bash
time=5
workDir='/tmp'
record()
{
	echo "Recording background noise. Keep quiet for $time seconds."
	sleep 3
	arecord -f cd noise.wav &
	PID=$!
	sleep $time
	kill $PID
	aplay noise.wav
}

#get pulse audio devices
devices=`pactl list | grep -E -A2 '(Source|Sink) #' | grep 'Name: ' | grep -v monitor | cut -d" " -f2`
if [ `echo "$devices" | grep -c aloop` -lt 1 ]; then
	echo "No loopback device created. Run 'sudo modprobe snd_aloop' first."
	exit
fi

cd $workDir

#record noise sample
record
while true; do
    read -p "Do you wish to re-record the noise sample?" yn
    case $yn in
        [Yy]* ) record;;
        [Nn]* ) break;;
        * ) echo "Please answer yes or no.";;
    esac
done

#create noise profile
sox noise.wav -n noiseprof noise.prof

input=`echo "$devices" | grep input.*pci`
output=`echo "$devices" | grep output.*aloop`

echo "Sending output to loopback device. Change recording port to <Loopback Analog Stereo Monitor> in PulseAudio to apply. Ctrl+C to terminate."

#filter audio from $input to $output
pacat -r -d $input --latency=1msec | sox -b 16 -e signed -c 2 -r 44100 -t raw - -b 16 -e signed -c 2 -r 44100 -t raw - noisered noise.prof 0.2 | pacat -p -d $output --latency=1msec

This script first tests to make sure the loopback devices are loaded, then records a 5 second noise sample. The sample is then played back to the user. If there should be a problem with it, such as a non-background sound leaked into it, it can be re-recorded. Once done, the input and output devices are determined, and an audio stream is directed from PulseAudio into SoX, then back into Pulse through the loopback device. From PulseAudio Volume Control, the input of recording devices can be changed to Loopback Analog Stereo Monitor, where the filtered audio stream is being directed.

Advertisements

21 thoughts on “Real-time background noise cancellation of Microphone input on linux

  1. It works well, and I’ve been looking for something like this for a while, thanks for that. I create videos and hate having to rip the audio using WinFF, edit it in audacity to remove the noise, then combine the audio with a silence version of the video in kdenlive in order to have decent audio quality.

    The only issue with this, however, is that it tends to control the volume on it’s own which creates some hell of an audio spike here and there.

    Like

    • Thanks for the feedback! While I posted this a while ago, I haven’t had much need to use it, though I’ve been thinking about investigating modifications to the approach. Mostly this would be to test the effects of using different sound samples, though it seems looking into volume manipulation would also be worth while.

      Like

      • It’s exceptionally beneficial for me as I’m a video content creator for the likes of Vidme (previously YouTube) and do podcasts and various livestreams regularly as well. When recording, I can later edit the audio as mentioned above so that I can fix certain things post production, but in the cases of podcasts and other forms of livestreaming, this becomes a real issue requiring me to make sure I save a copy of it to disc as it’s streamed, then after the livestream is finished, delete the saved livestream and go about using winff to extract the audio, import to audacity, fix the audio, blah blah then reupload.

        Funny enough, I know I’m not the only one on Linux that does this. I’ve spoken with others in the community, some of which simply state that they have to use Windows in order to do their recordings and then switch to Linux for the editing. This is due to Windows having a noise cancelling feature within the sound settings that is as simple as just checking a box.

        Like

    • Hmm for something like that I’d recommend a more robust solution. Get a pair of noise-cancelling headphones, and cross the wires to reverse the polarity. That should cause it to project “cancelled” noise outward, taken from the sound produced inside. Wear them while you sleep and even you won’t hear your own snoring!

      Like

  2. The script errors for me on what seems to be the last line. I record my backround noise fine, and it plays back as expected, then the process says “Sending output to loopback device. Change recording port to <Loopback Analog Stereo Monitor> in PulseAudio to apply. Ctrl+C to terminate.” as expected. But a split second after it errors with “Stream error: Invalid argument” and the process terminates.

    I’m not sure if maybe I’m missing a dependency or if some arguments on the last line of the script need to be changed or removed.

    I’d really like to get this to work!

    Like

    • Thanks for the feedback. I’ll have to look into this at some point. It sounds like one of the arguments being passed (most likely) to sox isn’t valid. It’s possible the specifications for one of the applications used changed with updates. I haven’t actually used this in quite a while now, so it doesn’t entirely come as a surprise to me that there would be issues now. That being said, I’m sure it’s not much to work around said problems.

      Like

      • Gosh. I almost jump of happiness by this post. But I also have this same issue.

        Stream error: Invalid argument
        pa_stream_drain(): Bad state

        Any idea?

        thanks!

        Like

      • Thanks for the feedback. Are you running the script as-is, or doing something different? I’ll have to do some more testing (and probably some updates to this post) since I haven’t touched it in quite a while.

        Like

  3. Hi cubethethird, your script looks awesome! but I don’t understand what I have to do next.

    After the message: “Sending output to loopback device. Change recording port to <Loopback Analog Stereo Monitor> in PulseAudio to apply. Ctrl+C to terminate.” , I run this command:

    `pacmd “set-default-source alsa_input.platform-snd_aloop.0.analog-stereo”` to change the recording port, is it right? and then what I supposed to do?

    thank you!

    Like

    • That’s a good question! I’ve never actually used the command line to change the ports. I really need to do some updates to this at some point. I don’t think it will work if you set the default source to the loopback device, since that may also change it for the command that is running. I’d recommend (not sure how to do this via command line) applying the change only to the application that is actively capturing/recording the input. This will allow the pacat command to continue to read the direct input, while the other application(s) can read the filtered input from the loopback device.

      Like

      • I finally managed to do what I wanted! I wanted to do a noise cancelling (like some new headphones).This is recording the noise, inverting its waves and reproducing it, so the inverted waves plus the original waves (the external noise) makes the external noise cancelling. I did it with your script changing the last line with:
        `pacat -r -d $input –latency=1nsec | sox -v -1 -b 16 -e signed -c 1 -r 44100 -t raw – -b 16 -e signed -c 1 -r 44100 -t raw – | pacat -p -d $output –latency=1nsec` and listening the output
        I’m still having problems with the delay and the volume, so I didn’t get the desired result yet.
        Do you know if there is any possibility to process the input with 0 delay?

        Like

    • I don’t think its possible to do software audio processing with 0 latency. Any amount of processing is going to add some delay. While I like the idea of doing cancellation that way, I doubt it could be effective for a real-time application.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s