Master custom music on hold setup, per-queue MOH routing, and troubleshooting for high-volume ViciDial production systems
Prerequisites
Before implementing custom MOH in Asterisk/ViciDial, verify:
- Asterisk version 11+ (tested on 13.x, 16.x, 18.x)
- ViciDial 2.14+ running on CentOS 7/8 or Ubuntu 18.04+
- Root or sudo access to the Asterisk server
- Audio files in WAV or MP3 format (MOH files must be 8 kHz, 16-bit mono for optimal performance)
- Sufficient disk space in
/var/lib/asterisk/moh/(typically 500 MB–2 GB depending on queue count) - Asterisk service control:
systemctl restart asteriskor/etc/init.d/asterisk restart - Database credentials for ViciDial (
rootor dedicatedasteriskuser) - Understanding of Asterisk dialplan basics and conf file syntax
Run asterisk -rx "core show version" to confirm Asterisk is running and accessible.
Understanding Asterisk Music on Hold Architecture
How MOH Works in Asterisk
Music on Hold is triggered when a call enters a queue, is placed on hold, or meets a Wait() application. Asterisk uses:
- MOH Classes — defined in
musiconhold.conf, each with a directory or file - MOH Selection — via dialplan (
SetMusicOnHold()) or queue configuration - MOH Playback — native playback to calling party via audio stream
- MOH Generators — optional generator for on-the-fly tone/silence generation
Default MOH directory: /var/lib/asterisk/moh/
ViciDial-Specific MOH Integration
ViciDial uses MOH at these points:
- Queue hold — when agent is unavailable or queue is full
- Callback queues — while customer waits for agent callback
- IVR prompts — background music during menu selection
- Transfer hold — during blind/attended transfers
Core MOH Configuration Files
Primary Configuration: musiconhold.conf
Located at /etc/asterisk/musiconhold.conf, this is the master MOH configuration.
[general]
; Default class if none specified
default=default
; Directory scanning mode - rescan disk for new files automatically
; Useful for dynamic queue changes
moh_quiet=yes
[default]
; Standard MP3 playback with quieting
mode=files
directory=/var/lib/asterisk/moh/default
; Quieting level (0-100)
quiet=100
[on-hold]
mode=files
directory=/var/lib/asterisk/moh/on-hold
quiet=100
[queue-sales]
mode=files
directory=/var/lib/asterisk/moh/sales
quiet=100
[queue-support]
mode=files
directory=/var/lib/asterisk/moh/support
quiet=100
[queue-billing]
mode=files
directory=/var/lib/asterisk/moh/billing
quiet=100
[silence]
; Silent MOH - useful for internal transfers
mode=silence
[tone]
; Audible ringback tone
mode=files
directory=/var/lib/asterisk/moh/tone
quiet=0
Key Parameters:
mode=files— use audio files from directorymode=mp3— legacy; usefilesinsteadmode=silence— no audio, prevents dead-airdirectory=— absolute path to MOH filesquiet=100— reduce volume (0=loud, 100=silent); use 60–80 for balanced audioapplication=— for generator-based MOH (DAHDI, SIP)
Extensions Dialplan: extensions-vicidial.conf
Add MOH selection logic to your dialplan:
[from-internal]
; Example: set MOH before queue
exten => 100,1,NoOp(Incoming call from ${CALLERID(num)})
exten => 100,n,SetMusicOnHold(queue-sales)
exten => 100,n,Queue(sales-queue,t)
exten => 100,n,Hangup()
exten => 101,1,NoOp(Support queue)
exten => 101,n,SetMusicOnHold(queue-support)
exten => 101,n,Queue(support-queue,t)
exten => 101,n,Hangup()
Queue Configuration: queues.conf
ViciDial typically manages queues via database, but direct queues.conf configuration is still used:
[sales-queue]
strategy=linear
timeout=30
retry=5
maxlen=0
; Music on hold class for this queue
musiconhold=queue-sales
; Announce position every 60 seconds
announce-frequency=60
announce-round-seconds=10
setinterfacevar=yes
setqueuevar=yes
[support-queue]
strategy=linear
timeout=30
retry=5
maxlen=0
musiconhold=queue-support
announce-frequency=120
[callback-queue]
strategy=linear
timeout=300
retry=10
maxlen=100
musiconhold=on-hold
Setting Up Per-Queue Music on Hold
Step 1: Create Per-Queue MOH Directories
Establish separate directories for each queue:
mkdir -p /var/lib/asterisk/moh/{default,sales,support,billing,vip}
chown -R asterisk:asterisk /var/lib/asterisk/moh/
chmod 755 /var/lib/asterisk/moh/*
Step 2: Prepare and Validate Audio Files
Audio files must meet specific requirements:
For WAV files:
ffmpeg -i original-music.mp3 -acodec pcm_s16le -ar 8000 -ac 1 /var/lib/asterisk/moh/sales/music.wav
For MP3 files (if using mp3 mode):
ffmpeg -i original-music.mp3 -acodec libmp3lame -ar 8000 -ac 1 -b:a 32k /var/lib/asterisk/moh/sales/music.mp3
Batch conversion for directory:
#!/bin/bash
SOURCE_DIR="/tmp/moh-source"
DEST_DIR="/var/lib/asterisk/moh/sales"
for file in ${SOURCE_DIR}/*; do
filename=$(basename "$file" | cut -d. -f1)
ffmpeg -i "$file" -acodec pcm_s16le -ar 8000 -ac 1 "${DEST_DIR}/${filename}.wav"
done
chown -R asterisk:asterisk ${DEST_DIR}
chmod 644 ${DEST_DIR}/*
Validate audio format:
sox /var/lib/asterisk/moh/sales/music.wav -n stat
# Output should show: Sample Rate 8000, Channels 1
Step 3: Update musiconhold.conf with Queue-Specific Entries
[queue-sales]
mode=files
directory=/var/lib/asterisk/moh/sales
quiet=70
[queue-support]
mode=files
directory=/var/lib/asterisk/moh/support
quiet=70
[queue-billing]
mode=files
directory=/var/lib/asterisk/moh/billing
quiet=70
[queue-vip]
mode=files
directory=/var/lib/asterisk/moh/vip
quiet=60
Step 4: Configure Queues with MOH Class Assignment
Edit or create /etc/asterisk/queues.conf:
[general]
shared_lastcall=yes
monitor-type=MixMonitor
[sales-queue]
strategy=linear
timeout=30
retry=5
musiconhold=queue-sales
autofill=yes
setinterfacevar=yes
setqueuevar=yes
announce-frequency=60
announce-round-seconds=10
[support-queue]
strategy=linear
timeout=30
retry=5
musiconhold=queue-support
autofill=yes
setinterfacevar=yes
setqueuevar=yes
[billing-queue]
strategy=ring
timeout=45
retry=10
musiconhold=queue-billing
maxlen=50
[callback-queue]
strategy=linear
timeout=300
retry=15
musiconhold=queue-sales
maxlen=100
joined-sound=queue-youare
Step 5: Reload and Test MOH
After configuration changes, reload Asterisk:
asterisk -rx "reload musiconhold"
asterisk -rx "reload queues"
asterisk -rx "reload res_musiconhold"
Verify MOH classes are loaded:
asterisk -rx "moh show classes"
Expected output:
Class: queue-sales
Mode: files
Directory: /var/lib/asterisk/moh/sales
Quieting: 70
Class: queue-support
Mode: files
Directory: /var/lib/asterisk/moh/support
Quieting: 70
Class: queue-billing
Mode: files
Directory: /var/lib/asterisk/moh/billing
Quieting: 70
ViciDial Database Configuration
Integrating MOH with ViciDial Queue Tables
ViciDial stores queue configuration in the asterisk database. Use these tables:
Table: vicidial_queues
SELECT * FROM vicidial_queues WHERE queue_name='sales-queue'\G
Key fields:
queue_id— unique identifierqueue_name— queue name (matches Asterisk queue)queue_maxlen— max queue depthqueue_timeout— agent ring timeoutqueue_strategy— linear, ring, weighted_randommusic_on_hold— MOH class name
Update ViciDial Queue MOH via SQL:
UPDATE vicidial_queues SET music_on_hold='queue-sales' WHERE queue_name='sales-queue';
UPDATE vicidial_queues SET music_on_hold='queue-support' WHERE queue_name='support-queue';
UPDATE vicidial_queues SET music_on_hold='queue-billing' WHERE queue_name='billing-queue';
COMMIT;
Verify via ViciDial web UI:
Navigate to /vicidial/admin.php → Queues → Edit each queue → Set "Music on Hold" dropdown to desired class.
Database Sync Script
Create a script to sync MOH settings from database to dialplan:
#!/bin/bash
# /usr/local/bin/sync-moh-to-dialplan.sh
MYSQL_USER="root"
MYSQL_PASS="your_password"
MYSQL_DB="asterisk"
OUTFILE="/etc/asterisk/queues-vicidial.conf"
cat > ${OUTFILE} << 'EOF'
[general]
shared_lastcall=yes
monitor-type=MixMonitor
EOF
mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${MYSQL_DB} << 'SQLEOF' | while read queue_name moh_class; do
SELECT queue_name, COALESCE(music_on_hold, 'default') FROM vicidial_queues WHERE active='Y' ORDER BY queue_name;
SQLEOF
cat >> ${OUTFILE} << QEOF
[${queue_name}]
strategy=linear
timeout=30
retry=5
musiconhold=${moh_class}
autofill=yes
setinterfacevar=yes
setqueuevar=yes
QEOF
done
chown asterisk:asterisk ${OUTFILE}
asterisk -rx "reload queues"
Schedule with cron:
0 */4 * * * /usr/local/bin/sync-moh-to-dialplan.sh
Advanced MOH Techniques
Dynamic MOH Selection via Channel Variables
Use SetMusicOnHold() in dialplan to override queue-level MOH:
[from-vicidial]
exten => _X.,1,NoOp(Dynamic MOH selection)
exten => _X.,n,Set(CALLER_LANG=${CALLERID(name):0:2})
exten => _X.,n,GotoIf($["${CALLER_LANG}"="ES"]?spanish:english)
exten => _X.,n(english),SetMusicOnHold(queue-sales)
exten => _X.,n,Goto(queue-main)
exten => _X.,n(spanish),SetMusicOnHold(queue-sales-spanish)
exten => _X.,n(queue-main),Queue(sales-queue,t)
exten => _X.,n,Hangup()
Custom Voicemail Greeting Integration
Combine MOH with IVR greetings:
[from-queue-announcement]
exten => s,1,NoOp(Queue announcement with MOH)
exten => s,n,SetMusicOnHold(queue-sales)
exten => s,n,Playback(queue/position-in-queue)
exten => s,n,Playback(queue/est-wait-time)
exten => s,n,Queue(sales-queue,t)
exten => s,n,Hangup()
Silence on Hold for Internal Transfers
For agent-to-agent transfers, use silent MOH:
[agent-transfer]
exten => *1,1,NoOp(Blind transfer to ${ARG1})
exten => *1,n,SetMusicOnHold(silence)
exten => *1,n,Blind Transfer(${ARG1})
exten => *1,n,Hangup()
Conditional MOH Based on Queue Depth
[from-vicidial]
exten => 100,1,NoOp(Queue ${QUEUENAME} check)
exten => 100,n,Queue(sales-queue,t)
exten => 100,n,GotoIf($["${QUEUE_MEMBER_COUNT}">"5"]?busy:available)
exten => 100,n(available),SetMusicOnHold(queue-sales)
exten => 100,n,Goto(end)
exten => 100,n(busy),SetMusicOnHold(queue-vip)
exten => 100,n(end),Hangup()
Looped MOH with Periodic Announcements
Create a custom application that plays MOH, then an announcement, repeatedly:
[queue-with-announcements]
exten => s,1,NoOp(MOH + periodic announcement)
exten => s,n,SetMusicOnHold(queue-sales)
exten => s,n,WaitExten(30)
exten => s,n,Playback(queue/estimated-wait)
exten => s,n,WaitExten(30)
exten => s,n,Queue(sales-queue,t)
exten => s,n,Hangup()
Monitoring and Performance Tuning
Check MOH Playback in Real-Time
View active calls using MOH:
asterisk -rx "core show calls" | grep -i "music\|Queue"
Sample output:
SIP/2001-00000001 Queue(sales-queue,t) 0:00:15
SIP/2002-00000002 Queue(support-queue,t) 0:00:42
Monitor MOH File Usage
Track which MOH classes are actively used:
asterisk -rx "moh show classes"
asterisk -rx "core show channels concise" | awk -F'!' '{print $10}' | sort | uniq -c
Audio Quality Diagnostics
Verify audio playback quality:
sox /var/lib/asterisk/moh/sales/music.wav -n remix - | sox - -n stat
# Check: RMS amplitude, Peak amplitude, Crest factor
Optimize quiet Parameter
Test MOH with different quieting levels:
# Lower quiet value = louder
# Higher quiet value = quieter
# Typical range: 60-80
[queue-sales]
quiet=70 # Balanced
quiet=40 # Louder
quiet=100 # Quieter/silence preservation
CPU Usage Monitoring
MOH playback CPU impact is minimal, but monitor with:
top -b -n 1 | grep asterisk
ps aux | grep asterisk | grep -v grep
Troubleshooting
MOH Not Playing When Call Enters Queue
Symptom: Dead silence on hold instead of music.
Diagnosis:
asterisk -rx "moh show classes"
If MOH classes don't appear or are empty:
Check musiconhold.conf syntax:
asterisk -m -c "moh reload" 2>&1 | grep -i errorVerify audio files exist and are readable:
ls -la /var/lib/asterisk/moh/queue-sales/ chown -R asterisk:asterisk /var/lib/asterisk/moh/ chmod 755 /var/lib/asterisk/moh/* chmod 644 /var/lib/asterisk/moh/*/*Reload MOH module:
asterisk -rx "module reload res_musiconhold.so" asterisk -rx "reload musiconhold"
MOH Class Not Found for Queue
Symptom: Asterisk CLI shows error: Unable to locate MOH class queue-sales
Solution:
Verify queue name matches musiconhold.conf:
SELECT queue_name, music_on_hold FROM vicidial_queues;Check queues.conf has matching musiconhold entry:
grep -A 5 "^\[sales-queue\]" /etc/asterisk/queues.conf | grep musiconholdReload both configs:
asterisk -rx "reload musiconhold" asterisk -rx "reload queues"
Audio Distortion or Quality Issues
Symptom: MOH playback sounds garbled, clipped, or distorted.
Solutions:
Check audio file format:
ffprobe -v error -show_entries stream=sample_rate,channels,codec_name \ -of default=noprint_wrappers=1 /var/lib/asterisk/moh/sales/music.wav # Should output: sample_rate=8000, channels=1Re-encode to correct format:
ffmpeg -i /var/lib/asterisk/moh/sales/music.wav -acodec pcm_s16le \ -ar 8000 -ac 1 /var/lib/asterisk/moh/sales/music-fixed.wav mv /var/lib/asterisk/moh/sales/music-fixed.wav \ /var/lib/asterisk/moh/sales/music.wavAdjust quiet parameter (reduce to increase volume):
[queue-sales] quiet=50 # Increase volume (was 70)Check Asterisk log for codec errors:
tail -100 /var/log/asterisk/messages | grep -i "moh\|music\|codec"
Wrong MOH Playing for Queue
Symptom: Queue plays default MOH instead of queue-specific MOH.
Diagnosis:
Verify queue configuration in database:
SELECT queue_name, music_on_hold FROM vicidial_queues WHERE queue_name='sales-queue';Check dialplan override:
grep -n "SetMusicOnHold" /etc/asterisk/extensions-vicidial.confVerify queues.conf musiconhold assignment:
grep -A 10 "^\[sales-queue\]" /etc/asterisk/queues.confForce reload:
asterisk -rx "reload musiconhold" asterisk -rx "reload queues" asterisk -rx "reload res_musiconhold.so"
MOH Stops After Few Seconds
Symptom: Music plays briefly, then cuts out; silence follows.
Causes:
Audio file too short or corrupt:
sox /var/lib/asterisk/moh/sales/music.wav -n stat # Check duration; should be 2+ minutesDirectory permission issue:
ls -la /var/lib/asterisk/moh/sales/ # Should show: drwxr-xr-x asterisk:asteriskAsterisk process permissions:
ps aux | grep asterisk | grep -v grep # Should run as asterisk user
Fix:
chown -R asterisk:asterisk /var/lib/asterisk/moh/
chmod -R 755 /var/lib/asterisk/moh/
systemctl restart asterisk
High CPU Usage with MOH
Symptom: Asterisk CPU usage spikes when MOH is active.
Solutions:
Use MP3 or WAV, not generator-based MOH:
[queue-sales] mode=files # Not mode=mp3 or mode=DAHDIVerify audio codec is efficient:
ffprobe /var/lib/asterisk/moh/sales/music.wav # Should show PCM 8-bit or 16-bit, 8000 HzReduce number of concurrent MOH streams if necessary:
asterisk -rx "moh show files"
Logs Not Showing MOH Activity
Enable MOH debug logging:
asterisk -rx "core set debug 3"
asterisk -rx "core set verbose 3"
tail -f /var/log/asterisk/messages | grep -i moh
Check for specific errors:
grep -i "moh\|music\|hold" /var/log/asterisk/messages | tail -50
Summary
Implementing custom per-queue Music on Hold in Asterisk/ViciDial requires:
- Audio Preparation: Convert media to 8 kHz, 16-bit mono WAV files for optimal quality and compatibility
- Directory Structure: Organize MOH files in
/var/lib/asterisk/moh/with separate subdirectories per queue - Configuration: Define MOH classes in
musiconhold.confand assign to queues viaqueues.confor ViciDial database - Dialplan Integration: Use
SetMusicOnHold()for dynamic selection and combine with queue applications - Database Sync: Link ViciDial
vicidial_queuestablemusic_on_holdfield to Asterisk configurations - Validation & Testing: Reload modules, verify with CLI commands, test with live calls
- Monitoring: Use
asterisk -rxcommands to monitor active MOH playback and troubleshoot issues - Performance: Ensure proper file permissions, audio quality, and system resource allocation
Key commands for day-to-day operations:
# Reload after configuration changes
asterisk -rx "reload musiconhold"
asterisk -rx "reload queues"
# Verify MOH is loaded
asterisk -rx "moh show classes"
# Monitor active queues with MOH
asterisk -rx "core show calls" | grep Queue
# Restart Asterisk if issues persist
systemctl restart asterisk
With proper setup, ViciDial agents and customers will experience professional, branded music on hold tailored to each queue, improving perceived call quality and reducing perceived wait times.