DEV Community

Moisi Trungu
Moisi Trungu

Posted on • Originally published at flixzone.icu

Asterisk Music on Hold — Custom Audio & Per-Queue MOH

Asterisk Music on Hold — Custom Audio & Per-Queue MOH

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 asterisk or /etc/init.d/asterisk restart
  • Database credentials for ViciDial (root or dedicated asterisk user)
  • 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:

  1. MOH Classes — defined in musiconhold.conf, each with a directory or file
  2. MOH Selection — via dialplan (SetMusicOnHold()) or queue configuration
  3. MOH Playback — native playback to calling party via audio stream
  4. 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
Enter fullscreen mode Exit fullscreen mode

Key Parameters:

  • mode=files — use audio files from directory
  • mode=mp3 — legacy; use files instead
  • mode=silence — no audio, prevents dead-air
  • directory= — absolute path to MOH files
  • quiet=100 — reduce volume (0=loud, 100=silent); use 60–80 for balanced audio
  • application= — 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()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/*
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}/*
Enter fullscreen mode Exit fullscreen mode

Validate audio format:

sox /var/lib/asterisk/moh/sales/music.wav -n stat
# Output should show: Sample Rate 8000, Channels 1
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 5: Reload and Test MOH

After configuration changes, reload Asterisk:

asterisk -rx "reload musiconhold"
asterisk -rx "reload queues"
asterisk -rx "reload res_musiconhold"
Enter fullscreen mode Exit fullscreen mode

Verify MOH classes are loaded:

asterisk -rx "moh show classes"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Key fields:

  • queue_id — unique identifier
  • queue_name — queue name (matches Asterisk queue)
  • queue_maxlen — max queue depth
  • queue_timeout — agent ring timeout
  • queue_strategy — linear, ring, weighted_random
  • music_on_holdMOH 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;
Enter fullscreen mode Exit fullscreen mode

Verify via ViciDial web UI:
Navigate to /vicidial/admin.phpQueues → 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"
Enter fullscreen mode Exit fullscreen mode

Schedule with cron:

0 */4 * * * /usr/local/bin/sync-moh-to-dialplan.sh
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

Monitoring and Performance Tuning

Check MOH Playback in Real-Time

View active calls using MOH:

asterisk -rx "core show calls" | grep -i "music\|Queue"
Enter fullscreen mode Exit fullscreen mode

Sample output:

SIP/2001-00000001     Queue(sales-queue,t)    0:00:15
SIP/2002-00000002     Queue(support-queue,t)  0:00:42
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

MOH Not Playing When Call Enters Queue

Symptom: Dead silence on hold instead of music.

Diagnosis:

asterisk -rx "moh show classes"
Enter fullscreen mode Exit fullscreen mode

If MOH classes don't appear or are empty:

  1. Check musiconhold.conf syntax:
   asterisk -m -c "moh reload" 2>&1 | grep -i error
Enter fullscreen mode Exit fullscreen mode
  1. Verify 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/*/*
Enter fullscreen mode Exit fullscreen mode
  1. Reload MOH module:
   asterisk -rx "module reload res_musiconhold.so"
   asterisk -rx "reload musiconhold"
Enter fullscreen mode Exit fullscreen mode

MOH Class Not Found for Queue

Symptom: Asterisk CLI shows error: Unable to locate MOH class queue-sales

Solution:

  1. Verify queue name matches musiconhold.conf:
   SELECT queue_name, music_on_hold FROM vicidial_queues;
Enter fullscreen mode Exit fullscreen mode
  1. Check queues.conf has matching musiconhold entry:
   grep -A 5 "^\[sales-queue\]" /etc/asterisk/queues.conf | grep musiconhold
Enter fullscreen mode Exit fullscreen mode
  1. Reload both configs:
   asterisk -rx "reload musiconhold"
   asterisk -rx "reload queues"
Enter fullscreen mode Exit fullscreen mode

Audio Distortion or Quality Issues

Symptom: MOH playback sounds garbled, clipped, or distorted.

Solutions:

  1. 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=1
Enter fullscreen mode Exit fullscreen mode
  1. Re-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.wav
Enter fullscreen mode Exit fullscreen mode
  1. Adjust quiet parameter (reduce to increase volume):
   [queue-sales]
   quiet=50  # Increase volume (was 70)
Enter fullscreen mode Exit fullscreen mode
  1. Check Asterisk log for codec errors:
   tail -100 /var/log/asterisk/messages | grep -i "moh\|music\|codec"
Enter fullscreen mode Exit fullscreen mode

Wrong MOH Playing for Queue

Symptom: Queue plays default MOH instead of queue-specific MOH.

Diagnosis:

  1. Verify queue configuration in database:
   SELECT queue_name, music_on_hold FROM vicidial_queues WHERE queue_name='sales-queue';
Enter fullscreen mode Exit fullscreen mode
  1. Check dialplan override:
   grep -n "SetMusicOnHold" /etc/asterisk/extensions-vicidial.conf
Enter fullscreen mode Exit fullscreen mode
  1. Verify queues.conf musiconhold assignment:
   grep -A 10 "^\[sales-queue\]" /etc/asterisk/queues.conf
Enter fullscreen mode Exit fullscreen mode
  1. Force reload:
   asterisk -rx "reload musiconhold"
   asterisk -rx "reload queues"
   asterisk -rx "reload res_musiconhold.so"
Enter fullscreen mode Exit fullscreen mode

MOH Stops After Few Seconds

Symptom: Music plays briefly, then cuts out; silence follows.

Causes:

  1. Audio file too short or corrupt:
   sox /var/lib/asterisk/moh/sales/music.wav -n stat
   # Check duration; should be 2+ minutes
Enter fullscreen mode Exit fullscreen mode
  1. Directory permission issue:
   ls -la /var/lib/asterisk/moh/sales/
   # Should show: drwxr-xr-x asterisk:asterisk
Enter fullscreen mode Exit fullscreen mode
  1. Asterisk process permissions:
   ps aux | grep asterisk | grep -v grep
   # Should run as asterisk user
Enter fullscreen mode Exit fullscreen mode

Fix:

chown -R asterisk:asterisk /var/lib/asterisk/moh/
chmod -R 755 /var/lib/asterisk/moh/
systemctl restart asterisk
Enter fullscreen mode Exit fullscreen mode

High CPU Usage with MOH

Symptom: Asterisk CPU usage spikes when MOH is active.

Solutions:

  1. Use MP3 or WAV, not generator-based MOH:
   [queue-sales]
   mode=files  # Not mode=mp3 or mode=DAHDI
Enter fullscreen mode Exit fullscreen mode
  1. Verify audio codec is efficient:
   ffprobe /var/lib/asterisk/moh/sales/music.wav
   # Should show PCM 8-bit or 16-bit, 8000 Hz
Enter fullscreen mode Exit fullscreen mode
  1. Reduce number of concurrent MOH streams if necessary:
   asterisk -rx "moh show files"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Check for specific errors:

grep -i "moh\|music\|hold" /var/log/asterisk/messages | tail -50
Enter fullscreen mode Exit fullscreen mode

Summary

Implementing custom per-queue Music on Hold in Asterisk/ViciDial requires:

  1. Audio Preparation: Convert media to 8 kHz, 16-bit mono WAV files for optimal quality and compatibility
  2. Directory Structure: Organize MOH files in /var/lib/asterisk/moh/ with separate subdirectories per queue
  3. Configuration: Define MOH classes in musiconhold.conf and assign to queues via queues.conf or ViciDial database
  4. Dialplan Integration: Use SetMusicOnHold() for dynamic selection and combine with queue applications
  5. Database Sync: Link ViciDial vicidial_queues table music_on_hold field to Asterisk configurations
  6. Validation & Testing: Reload modules, verify with CLI commands, test with live calls
  7. Monitoring: Use asterisk -rx commands to monitor active MOH playback and troubleshoot issues
  8. 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
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)