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.
Update: I have updated the script in hopes it will solve some of the problems that have been brought up. Details can be found in my update post. I have also posted it on GitHub for easier maintenance.
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:
Debian/Ubuntu: sudo apt-get install sox
Arch: pacman -S sox
sudo modprobe snd_aloop
#!/bin/bash time=5 workDir=$(mktemp -d) noiseFile="noise.wav" #record noise sample record() { read -p "Recording background noise. Keep quiet for $time seconds. Press Enter to start." sleep 0.5 parecord -d $input $noiseFile & PID=$! sleep $time kill $PID echo "Playing back noise sample" paplay $noiseFile } cd $workDir #detect if aloop module is loaded #allow the user to load the module here if [ $(lsmod | grep -c snd_aloop) -eq 0 ]; then echo "ALOOP module is not loaded." read -p "Attempt to load the module?[y/n]" yn case $yn in [Yy]* ) sudo modprobe snd_aloop;; [Nn]* ) exit;; * ) echo "Please answer yes or no.";; esac fi #get list of input and output devices (with details) inputs=$(pactl list short sources | grep -E -v '(monitor|aloop)') outputs=$(pactl list short sinks | grep aloop) #abort if devices aren't available if [ $(echo "$inputs" | wc -l) -lt 1 ]; then echo "No input devices detected. Aborting" exit fi if [ $(echo "$outputs" | wc -l) -lt 1 ]; then echo "No output devices detected. Aborting" exit fi inputIndex=0 #allow user selection when multiple devices are available if [ $(echo "$inputs" | wc -l) -gt 1 ]; then echo "Multiple input devices detected. Select from this list:" echo "$inputs" | awk -F '\\s' '{print NR-1," ",$2}' read -p "Enter an index:" inputIndex exit fi inputs=$(echo "$inputs" | awk -v idx=$inputIndex '{if (NR-1 == idx) print}') outputIndex=0 #allow user selection when multiple devices are available if [ $(echo "$outputs" | wc -l) -gt 1 ]; then echo "Multiple output devices detected. Select from this list:" echo "$outputs" | awk -F '\\s' '{print NR-1," ",$2}' read -p "Enter an index:" outputIndex exit fi outputs=$(echo "$outputs" | awk -v idx=$outputIndex '{if (NR-1 == idx) print}') #load device names input=$(echo "$inputs" | awk -F '\\s' '{print $2}') output=$(echo "$outputs" | awk -F '\\s' '{print $2}') #get input device specs format=$(echo "$inputs" | awk -F '\\s' '{print $4}') #number type tmp=$(echo $format | grep -o "^[fus]") case $tmp in f ) inputEncoding="floating-point";; s ) inputEncoding="signed-integer";; u ) inputEncoding="unsigned-integer";; esac #bit count inputBits=$(echo $format | grep -o "[0-9]*") #endianness tmp=$(echo $format | grep -o "[bl]e$") case $tmp in be ) inputEndian="-B";; le ) inputEndian="-L";; esac #channels inputChannels=$(echo "$inputs" | awk -F '\\s' '{print $5}' | grep -o "[0-9]*") #bitrate inputBitrate=$(echo "$inputs" | awk -F '\\s' '{print $6}' | grep -o "[0-9]*") #get output device specs format=$(echo "$outputs" | awk -F '\\s' '{print $4}') #number type tmp=$(echo $format | grep -o "^[fus]") case $tmp in f ) outputEncoding="floating-point";; s ) outputEncoding="signed-integer";; u ) outputEncoding="unsigned-integer";; esac #bit count outputBits=$(echo $format | grep -o "[0-9]*") #endianness tmp=$(echo $format | grep -o "[bl]e$") case $tmp in be ) outputEndian="-B";; le ) outputEndian="-L";; esac #channels outputChannels=$(echo "$outputs" | awk -F '\\s' '{print $5}' | grep -o "[0-9]*") #bitrate outputBitrate=$(echo "$outputs" | awk -F '\\s' '{print $6}' | grep -o "[0-9]*") #record noise sample record while true; do read -p "Do you wish to re-record the noise sample?[y/n]" yn case $yn in [Yy]* ) record;; [Nn]* ) break;; * ) echo "Please answer yes or no.";; esac done #create noise profile sox $noiseFile -n noiseprof noise.prof echo "Sending output to loopback device." echo "Change recording port to in PulseAudio to apply." echo "Ctrl+C to terminate." #filter audio from $input to $output pacat -r -d $input --latency=1msec | sox -b $inputBits -e $inputEncoding -c $inputChannels -r $inputBitrate $inputEndian -t raw - -b $outputBits -e $outputEncoding -c $outputChannels -r $outputBitrate $outputEndian -t raw - noisered noise.prof 0.2 | pacat -p -d $output --latency=1msec cd - rm -rf "$workdir"
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.
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.
LikeLike
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.
LikeLike
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.
LikeLike
THIS IS AWESOME! Really great!
LikeLike
Thanks! Glad you find it useful.
LikeLike
Fantastic! Thanks for this solution. Works very well.
Sadir
LikeLike
Now if I can just use this technique to cancel the sound of my snoring, my poor tired wife might get some sleep
LikeLike
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!
LikeLike
I don’t hear it anyway… Which I think, is probably for the best. Its more for others benefit.
LikeLike
I don’t understand your instructions. I own a pair of Noise Canceling Headphones. If I turn them on and don’t plug then into anything, they actively dampen ambient sounds. There’s no need to modify them; his wife just needs to wear a pair and turn them on.
I’m not sure how well they work against snoring; I’ve not tried this. And most noise canceling headphones wouldn’t be comfortable to sleep in.
LikeLike
Unless your comment is satirical (which I am assuming is not the case), I should clarify: I was being sarcastic. Those “instructions” are just a joke. If that were not the case, I would have made a tutorial (probably a YouTube video) explaining how it works.
LikeLike
Great, this is what I was looking for! I will try this soon.
LikeLike
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!
LikeLike
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.
LikeLike
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!
LikeLike
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.
LikeLike
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!
LikeLike
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.
LikeLike
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?
LikeLike
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.
LikeLike
I have found another good solution and I think worth sharing it:
https://askubuntu.com/a/765024/3798
it uses a PulseAudio module
LikeLike
Awesome! Thanks for the alternative.
LikeLike
[…] I also tried this but it didn’t work as expected. https://cubethethird.wordpress.com/2015/07/26/real-time-background-noise-cancellation-of-microphone-… […]
LikeLike
Hey man, when i run the script always say: “sudo modprobe snd_aloop” even i run the “sudo modprobe snd_aloop” and run the script after. You have any idea what’s the problem?
LikeLike
Do you get any errors executing the modprobe?
LikeLike
The error is that the script still claims no loopback device is created, even though there is one. It seems like something is wrong with the name detection, but I’m not sure… I’m sadly no Linux expert Would be awesome if you could look into this, because this solution really looks so appealing
LikeLike
I have updated the script and separated the detection of the module and the audio devices. This should allow for (hopefully) less problems to link everything up. I’ve also posted the script on my github here: https://github.com/CubeTheThird/LiveNoiseFilter
This way any issues can be more easily managed.
LikeLike
Thank you for the valuable resources…please can you help me,is t possible to use the above code for speech to text conversion in google speech recognizer?can you tell me how to write it in python code?while i am
giving my voice commands to microphone its capturing background noise can you please guide how to overcome it ?i am using it with rpi jessie
LikeLike
What’s up it’s me, I am also visiting this web site on a regular basis, this web page is genuinely good and the viewers
are in fact sharing pleasant thoughts.
LikeLike
Thanks for the feedback! It’s nice to hear that I sometimes get regular views, as I try to foster a place that is welcome to criticism and engagement.
LikeLike
[…] some time ago, I posted about how to perform live noise cancellation for audio inputs. I’ve finally got around to updating it, in hopes it will be better utilized […]
LikeLike
Anyone coming here in 2019 should note that, in addition to the solution shared by @voyeg3r (using ‘module-echo-cancel’, there’s also a way to do realtime input processing with `sox` without the alsa loopback module, by using pulseaudio’s ‘module-null-sink’. Example:
pactl load-module module-null-sink channels=1 rate=48000
# pacmd set-default-source null.monitor # (optional)
sox -b $inputBits -e $inputEncoding -c $inputChannels -r $inputBitrate $inputEndian -t pulseaudio $input – -b $outputBits -e $outputEncoding -c $outputChannels -r $outputBitrate $outputEndian -t pulseaudio null – noisered noise.prof 0.2
Then, recording applications should record from ‘null monitor’, aka “Monitor of Null Output”.
I first got this idea from https://askubuntu.com/q/421947/495331.
LikeLike
Awesome find! I may update the script to use this method instead, as it would alleviate the dependency of loading a kernel module just to achieve this.
LikeLike
Btw, the ‘Bitrate’ variables in the script should really be called ‘SampleRate’ 🙂
LikeLike
Thanks for pointing that out. I mainly named it that based on the input descriptions used by the applications.
LikeLike