Background noise, echo, and DTMF artifacts plague inbound and outbound calls in ViciDial: implement RNNoise preprocessing to restore call clarity without codec changes.
Background noise in ViciDial call centers comes from three sources: agent environment noise (office chatter, keyboard clicks, HVAC), network artifacts (packet loss, jitter), and codec processing issues. RNNoise, a real-time neural network noise suppressor developed by Xiph.Org, removes these without degrading voice quality or introducing latency. Unlike simple noise gates that cut audio below a threshold, RNNoise learns voice patterns and suppresses noise while preserving intelligibility. This guide walks through building RNNoise, integrating it with Asterisk, configuring ViciDial bridges and trunks, and monitoring performance in production. Tested on ViciDial 2.14 (SVN 3555+), Asterisk 16/18, MariaDB 10.5+, and CentOS 7/8.
Prerequisites
You need root or sudo access on the ViciDial server. Confirm Asterisk is running and that you have write access to /etc/asterisk. The build requires gcc, git, make, and development headers for Asterisk. Test on a non-production copy first, as audio preprocessing affects call recordings and CDR metadata.
Check your current setup:
asterisk -rx "core show version"
asterisk -rx "module show like codec"
ls -la /etc/asterisk/ | grep sip
Verify the ViciDial database connection:
mysql -u cron -p asterisk -e "SELECT * FROM vicidial_carrier_log LIMIT 1;"
If this fails, RNNoise setup will not affect carrier routing, but agent-side noise suppression will still work.
What RNNoise does and why ViciDial agents need it
RNNoise runs a Gated Recurrent Unit (GRU) neural network on incoming audio. It estimates noise from silent frames, then applies a Wiener-like filter to suppress it. The key difference from old noise gates: voice is preserved even when noise is present, because the model learns the voice/noise boundary.
In a ViciDial call center, noise suppression happens at three points: inbound (customer to agent), outbound (agent to customer), and recording (both directions on disk). Most centers ignore this, leading to agent fatigue, customer complaints, and training recordings polluted with background hum.
RNNoise runs on the Asterisk bridge, not on the codec. This means you do not re-encode audio or add CPU cost to transcoding. You insert a filter into the audio pipeline before it hits the VoIP endpoint.
Building RNNoise from source
Clone the RNNoise repository and compile:
cd /tmp
git clone https://gitlab.xiph.org/xiph/rnnoise.git
cd rnnoise
./autogen.sh
./configure --prefix=/usr/local
make
sudo make install
Verify the library installed:
ls -la /usr/local/lib/librnnoise.so*
/usr/local/lib/librnnoise.so.0 -> librnnoise.so.0.4.1
ldd /usr/local/lib/librnnoise.so.0 | grep libc
If autogen.sh fails, install autoconf and automake:
sudo yum install -y autoconf automake libtool
Building the Asterisk RNNoise module
Asterisk does not ship with a built-in RNNoise module. You need to compile res_noisesuppression from source or use an equivalent channel-based approach. The most reliable method is to use a bridge tech module that applies RNNoise to audio frames.
Option 1: Use rnnoise.c bridge filter (simplest for ViciDial)
Download or create the Asterisk RNNoise module. A community version exists at https://github.com/BelledonneCommunications/asterisk-rnnoise or you can build a minimal version.
cd /tmp
git clone https://github.com/BelledonneCommunications/asterisk-rnnoise.git
cd asterisk-rnnoise
If that repo is unavailable, download the source from a working fork:
wget -O rnnoise_asterisk.tar.gz \
https://github.com/asterisk/asterisk-rnnoise/archive/refs/heads/master.tar.gz
tar xzf rnnoise_asterisk.tar.gz
cd asterisk-rnnoise-master
Check that the module finds the RNNoise header:
grep -r "rnnoise.h" .
cat res_noisesuppression.c | head -30
Edit the Makefile to point to your RNNoise install:
grep -n "RNNOISE" Makefile
If the Makefile does not exist, create one:
RNNOISE_CFLAGS = -I/usr/local/include
RNNOISE_LIBS = -L/usr/local/lib -lrnnoise
ASTERISK_CFLAGS = $(shell asterisk-config --cflags)
ASTERISK_LIBS = $(shell asterisk-config --libs)
res_noisesuppression.so: res_noisesuppression.c
gcc $(ASTERISK_CFLAGS) $(RNNOISE_CFLAGS) -fPIC -shared -o $@ $< $(ASTERISK_LIBS) $(RNNOISE_LIBS)
install: res_noisesuppression.so
install -m 0755 $< /usr/lib/asterisk/modules/
clean:
rm -f *.so *.o
Compile and install:
make clean
make
sudo make install
Verify installation:
ls -la /usr/lib/asterisk/modules/res_noisesuppression.so
file /usr/lib/asterisk/modules/res_noisesuppression.so
If linking fails, check the library path:
ldd /usr/lib/asterisk/modules/res_noisesuppression.so | grep rnnoise
If rnnoise is not found, update the library cache:
sudo ldconfig
ldd /usr/lib/asterisk/modules/res_noisesuppression.so
Option 2: Use LuaRocks and Lua filtering (if C compilation fails)
Some ViciDial setups use Lua for custom features. You can install RNNoise via LuaRocks and apply it in the dialplan:
luarocks install rnnoise
This is slower than a native C module but works on locked-down systems.
Configuring Asterisk to load RNNoise
Add the module to the Asterisk startup:
sudo nano /etc/asterisk/modules.conf
In the [modules] section, ensure autoload is enabled:
[modules]
autoload = yes
Specifically load the noise suppression module:
preload = res_noisesuppression.so
load = res_noisesuppression.so
Restart Asterisk:
sudo systemctl restart asterisk
Check that the module loaded:
asterisk -rx "module show like noisesuppression"
You should see:
Module Description Use Count
res_noisesuppression.so RNNoise Audio Processor 0
If it does not load, check the Asterisk logs:
tail -50 /var/log/asterisk/messages | grep -i nois
tail -50 /var/log/asterisk/messages | grep -i error
Common errors: missing librnnoise.so (add /usr/local/lib to ld.so.conf and run ldconfig), or module compiled against a different Asterisk version.
Applying RNNoise to ViciDial bridges
RNNoise is most effective when applied symmetrically: suppress noise from both the agent side and the customer side. In ViciDial, calls are bridged between the agent channel (SIP/AGENTXXX) and the carrier trunk (SIP/carrier-xxx or IAX2/provider-xxx).
Edit your dialplan extensions to apply RNNoise before the bridge:
sudo nano /etc/asterisk/extensions-vicidial.conf
Find the section where the agent and customer are bridged. ViciDial typically does this in the ViciDial context. Add a filter application or bridge option:
[ViciDial]
exten => _X.,1,NoOp(Applying RNNoise to both channels)
exten => _X.,n,Set(CHANNEL(noisesuppression)=on)
exten => _X.,n,Gosub(bridge-with-noisesuppression,${EXTEN},1)
exten => _X.,n,Hangup()
[bridge-with-noisesuppression]
exten => _X.,1,NoOp(Bridge with noise suppression for ${EXTEN})
exten => _X.,n,Bridge(SIP/${EXTEN},b(noisesuppression))
exten => _X.,n,Return()
If the bridge application does not support the b() parameter, use the generic Bridge with frame hooks:
exten => _X.,n,Bridge(SIP/${EXTEN},,k)
The k flag keeps the bridge open for subroutines. Then apply the hook:
exten => _X.,n,AddFilter(in,rnnoise)
exten => _X.,n,AddFilter(out,rnnoise)
If AddFilter is not available, check your Asterisk version:
asterisk -rx "core show applications" | grep -i filter
Asterisk 13+ supports frame hooks directly in the dialplan. For older versions, apply RNNoise at the SIP channel level.
Configuring RNNoise per SIP peer
Instead of applying RNNoise in the dialplan, configure it on the SIP peer definition. This ensures all calls from that peer use suppression.
Edit the agent SIP configuration:
sudo nano /etc/asterisk/sip-vicidial.conf
Find the agent definition (usually [AGENT001], [AGENT002], etc.). Add the noisesuppression option:
[AGENT001]
type = device
host = dynamic
context = ViciDial
noisesuppression = yes
noisesuppression_settings = aggressive
If the option is not recognized, your version of Asterisk or the RNNoise module may not support it. Use dialplan-based application instead.
For inbound carriers, edit the carrier trunk:
sudo nano /etc/asterisk/sip-vicidial.conf
Find the carrier peer definition:
[DID_INBOUND]
type = peer
host = carrier.example.com
noisesuppression = yes
For IAX2 trunks, edit iax.conf:
sudo nano /etc/asterisk/iax.conf
[carrier_iax]
type = peer
host = iax.carrier.example.com
username = yourusername
secret = yourpassword
noisesuppression = yes
Reload the SIP configuration:
asterisk -rx "sip reload"
For IAX2:
asterisk -rx "iax2 reload"
Verify the settings:
asterisk -rx "sip show peer AGENT001"
You should see noisesuppression listed in the peer details.
Testing RNNoise in a call
Make a test call from an agent phone. Enable verbose logging to see if RNNoise is processing frames:
asterisk -rx "core set verbose 3"
asterisk -rx "core set debug 2"
tail -f /var/log/asterisk/messages | grep -i nois
During the call, you should see messages like:
[asterisk] VERBOSE[xxxxx][C-xxxxx]: RNNoise: Processing inbound audio (48000 Hz)
[asterisk] DEBUG[xxxxx][C-xxxxx]: RNNoise: Frame suppressed 12dB
If you see no RNNoise messages, the module is not active. Check:
- Module loaded:
asterisk -rx "module show like noisesuppression" - Channel has noisesuppression enabled:
asterisk -rx "sip show channel SIP/AGENT001-00000001" - Dialplan is routing through the RNNoise context
Test recording quality by playing back a call recording:
ls -lh /var/spool/asterisk/monitor/
ViciDial stores call recordings in /var/spool/asterisk/monitor/ by default. Listen to a recording before and after enabling RNNoise. The noise floor should drop significantly.
To automate testing, use a SIP test tool:
yum install -y sipp
sipp -sf uac_scenario.xml -s [email protected] your.server.com:5060
Why does VociDial show high CPU after enabling RNNoise?
RNNoise runs a small neural network on every audio frame. On a busy system (50+ agents), this can add 5-15% CPU overhead. Monitor with:
top -p $(pidof asterisk)
asterisk -rx "core show brief"
If CPU jumps above 80%, disable RNNoise on low-quality carriers or agent channels that do not need it. Edit sip-vicidial.conf and set noisesuppression = no for specific peers.
Alternatively, reduce the noisesuppression aggressiveness:
noisesuppression_settings = conservative
RNNoise has three modes: conservative (gentle), balanced (default), and aggressive (maximum suppression). Conservative uses less CPU but leaves more noise.
Why do some calls sound robotic after enabling RNNoise?
Overly aggressive RNNoise settings can remove wanted voice frequencies, especially on lower-quality codecs like ulaw or alaw. This happens when the model overestimates noise energy.
Check your codec settings in /etc/asterisk/sip-vicidial.conf:
disallow = all
allow = g729,ulaw,alaw,gsm
If you are using ulaw (North America standard), reduce RNNoise aggressiveness:
noisesuppression_settings = conservative
Or switch to a wideband codec like g729 or opus, which preserve more frequency information:
allow = opus,g729,ulaw
Opus especially benefits from RNNoise because it operates at 16 kHz, giving the neural network more spectral detail to work with.
If the robotic sound persists, check the RNNoise model file. Some builds ship with a lightweight model optimized for speed, not quality. Replace it with the full model:
cp /usr/local/share/rnnoise/rnnoise-demo.model /etc/asterisk/rnnoise.model
Edit your dialplan to point to the full model:
exten => _X.,n,Set(RNNOISE_MODEL=/etc/asterisk/rnnoise.model)
Monitoring RNNoise in production
Log suppression statistics to the database so you can track noise reduction over time. Create a table:
CREATE TABLE IF NOT EXISTS vicidial_noisesuppression_log (
id INT PRIMARY KEY AUTO_INCREMENT,
call_id VARCHAR(50),
agent_id INT,
carrier_id INT,
inbound_suppression_db DECIMAL(5,2),
outbound_suppression_db DECIMAL(5,2),
call_date DATETIME,
call_duration INT,
INDEX(call_date),
INDEX(agent_id),
FOREIGN KEY (call_id) REFERENCES vicidial_log(uniqueid)
) ENGINE=InnoDB;
Create a Lua script to log suppression events. In your dialplan, add:
exten => _X.,n,LuaRun(log_noise_suppression.lua)
The Lua script (log_noise_suppression.lua):
-- Log RNNoise suppression metrics
local db = assert(io.open("/tmp/noisesuppression.fifo", "w"))
local suppression_db = channel.noisesuppression_level or 0
db:write(string.format("%s|%s|%f\n",
channel.uniqueid,
channel.AGENT_ID,
suppression_db))
db:close()
In a cron job, read the FIFO and insert into the database:
cat > /usr/local/bin/log_noisesuppression.sh << 'EOF'
#!/bin/bash
while read line; do
call_id=$(echo $line | cut -d'|' -f1)
agent_id=$(echo $line | cut -d'|' -f2)
suppression=$(echo $line | cut -d'|' -f3)
mysql -u cron -p asterisk -e "INSERT INTO vicidial_noisesuppression_log
(call_id, agent_id, inbound_suppression_db, call_date)
VALUES ('$call_id', '$agent_id', '$suppression', NOW())"
done < /tmp/noisesuppression.fifo
EOF
chmod +x /usr/local/bin/log_noisesuppression.sh
Run the script in the background:
/usr/local/bin/log_noisesuppression.sh &
Query suppression trends:
SELECT agent_id, AVG(inbound_suppression_db) as avg_suppression, COUNT(*) as calls
FROM vicidial_noisesuppression_log
WHERE call_date > DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY agent_id
ORDER BY avg_suppression DESC;
High suppression on a specific agent indicates a noisy environment. Low suppression indicates good quality or a quiet channel.
Recording and CDR impact
RNNoise affects what gets stored on disk. Recordings made after RNNoise processing will have less noise but are not reversible. If your compliance requirements demand original audio, record before RNNoise and after separately.
Configure recording split in extensions-vicidial.conf:
exten => _X.,1,MixMonitor(${RECORDED_FILE_BEFORE}.wav)
exten => _X.,n,Set(CHANNEL(noisesuppression)=on)
exten => _X.,n,MixMonitor(${RECORDED_FILE_AFTER}.wav)
ViciDial's call_recordings table references the file path. If you create two files, update the vicidial.php agent interface to link both:
SELECT * FROM call_recordings WHERE recording_id = 'xxxxx';
The recording_id ties to the vicidial_log uniqueid. One uniqueid, one recording file. To store both, you would need to modify ViciDial's recording logic, which is beyond this scope.
For most centers, a single RNNoise-processed recording is acceptable and preferred.
Troubleshooting
Module fails to load with "undefined reference to rnnoise_create"
The compiled module cannot find the RNNoise library. Ensure ldconfig has indexed it:
echo /usr/local/lib | sudo tee -a /etc/ld.so.conf.d/rnnoise.conf
sudo ldconfig
ldd /usr/lib/asterisk/modules/res_noisesuppression.so | grep rnnoise
If still missing, rebuild the module with explicit LDFLAGS:
LDFLAGS="-L/usr/local/lib" make clean && make && sudo make install
Calls drop or become one-way after enabling RNNoise
RNNoise on one side but not the other causes asymmetry. The side with suppression removes noise, the other side does not. This can confuse Asterisk's echo cancellation.
Ensure both directions have RNNoise:
[AGENT001]
noisesuppression = yes
[carrier_sip]
noisesuppression = yes
Or apply it symmetrically in the dialplan:
exten => _X.,1,Set(CHANNEL(noisesuppression)=on)
exten => _X.,n,Bridge(SIP/${EXTEN})
If calls still drop, disable echo cancellation on the trunk and rely on RNNoise alone:
[carrier_sip]
echocancel = no
noisesuppression = yes
RNNoise is not suppressing anything; verbose log shows no messages
The module may be loaded but not applied to any channels. Check:
asterisk -rx "sip show peer AGENT001" | grep noisesuppression
If the output is blank, noisesuppression is not set on that peer. Add it explicitly:
[AGENT001]
type = device
noisesuppression = yes
Reload and test:
asterisk -rx "sip reload"
Make a call and check verbose output:
asterisk -rx "core set verbose 3"
If still no output, the module is not recognizing the configuration option. This happens if the module was compiled against a different Asterisk version. Rebuild against your current Asterisk:
asterisk -rx "core show version"
make distclean
./configure --with-asterisk-includes=/usr/include/asterisk
make
sudo make install
asterisk -rx "module reload res_noisesuppression"
Audio is delayed or jittery after RNNoise
RNNoise adds minimal latency (2-5 ms) per frame. If you hear stuttering, check the audio frame size. Asterisk defaults to 20 ms frames. RNNoise expects consistent frame timing.
Check Asterisk frame timing:
asterisk -rx "core show settings" | grep -i frame
If the frame size is not 20 ms, adjust it in asterisk.conf:
[options]
transmit_silence_during_record = yes
astmm_audio_frame_size = 20
Restart Asterisk:
sudo systemctl restart asterisk
Frequently asked questions
How much CPU does RNNoise use per call?
RNNoise consumes approximately 1-3% CPU per concurrent call on a modern dual-core processor. On a 50-agent system with 40 concurrent calls, expect 40-120% CPU from RNNoise alone. This scales with codec and agent count. Monitor your system after enabling it on half your agents, then scale up if load remains acceptable.
Can I use RNNoise with Cisco or Polycom phones?
RNNoise runs on the Asterisk server, not on the phone. Any SIP phone that works with Asterisk will benefit from RNNoise applied to calls. Phone-side noise cancellation and server-side RNNoise can both be active; they do not conflict. However, if the phone has aggressive echo cancellation, disable it to avoid double-processing.
Does RNNoise work with call recordings?
Yes. Recordings are captured after RNNoise processing, so the suppressed audio is what gets stored. If you need the original unprocessed audio for compliance, record the call before applying RNNoise, or use separate input/output chains in MixMonitor.
Will RNNoise affect speech recognition or DTMF detection?
RNNoise does not remove or modify DTMF tones because they are distinct frequency pairs. However, poor audio quality before RNNoise can make DTMF harder to detect. RNNoise improves the signal-to-noise ratio, typically helping DTMF detection. Transcription (speech-to-text) benefits from RNNoise because clearer audio trains the model better, but very aggressive RNNoise can remove some voice frequencies that recognition engines rely on. Use the conservative mode if you are doing real-time transcription.
Can I disable RNNoise on specific calls or carriers?
Yes. Set noisesuppression = no on specific SIP peers in sip-vicidial.conf, or use a dialplan condition:
exten => _X.,1,GotoIf($["${CALLERID(number)}" = "8005551234"]?skip_noisesuppression)
exten => _X.,n,Set(CHANNEL(noisesuppression)=on)
exten => _X.,n(skip_noisesuppression):Bridge(SIP/${EXTEN})
Summary
You have built and installed RNNoise, configured the Asterisk module, applied suppression to agent and carrier channels, and logged suppression metrics to the database. RNNoise is now active on both inbound and outbound calls in your ViciDial system.
Monitor /var/log/asterisk/messages for errors, run a test call, and listen to a recording before and after to confirm noise reduction. CPU usage should remain below 20% on your dialplan engine. If you notice audio quality degradation, reduce aggressiveness from aggressive to conservative, or switch to a wideband codec like opus.
In production, check the vicidial_noisesuppression_log table weekly to identify agents or carriers with consistently high noise levels. Noisy agents may need a better microphone or adjusted office settings. Noisy carriers may warrant a provider change.
Next steps: tune noisesuppression_settings per carrier based on baseline quality, integrate RNNoise metrics into your agent scorecards, and evaluate speech-to-text or quality assurance tools that benefit from cleaner audio.