Skip to main content

AMD Worker

The AMD Worker is a Java/Vert.x 5 microservice that performs real-time Answering Machine Detection (AMD). It receives live audio streams from Cloudonix via WebSocket, analyzes audio for voicemail beep tones, and posts detection results to the Laravel backend.

Purpose

The worker handles:

  • Receiving WebSocket audio streams from Cloudonix
  • Decoding mu-law audio to PCM and resampling to 16 kHz
  • Detecting voicemail beeps with ML and energy-based detectors
  • Posting results to Laravel's /api/voice/amd-action endpoint
  • Closing the WebSocket after a result or timeout

When It Is Triggered

The AMD worker is triggered when:

  1. An outbound auto-dialer campaign has AMD enabled
  2. A call is answered
  3. Laravel returns CXML with a <Start><Stream url="wss://.../ws/amd/detect"> verb
  4. Cloudonix opens a WebSocket to the AMD worker and begins streaming audio

It is also used for any other call flow that wraps CXML with the AMD stream verb.

Architecture

Build and Run

The worker starts automatically with the full OPBX stack:

docker compose up -d

Local Build

cd amd-worker

# Compile
mvn compile

# Run tests
mvn test

# Build shaded JAR
mvn package -DskipTests -B

# Run locally
java -jar target/amd-worker-1.0.0.jar

Docker Build

cd amd-worker
docker build -t amd-worker:latest .
docker run -p 8082:8082 -p 8083:8083 \
-e AMD_WORKER_API_TOKEN=your-token \
-e AMD_ACTION_CALLBACK_URL=http://host.docker.internal/api/voice/amd-action \
amd-worker:latest

Environment Variables

VariableDefaultRequiredDescription
AMD_WEBSOCKET_PORT8082NoWebSocket listener port
AMD_HTTP_PORT8083NoHealth/metrics HTTP port
AMD_MODEL_PATH./models/beep_detector.onnxNoONNX model file path
AMD_MAX_CONCURRENT_STREAMS100NoMax simultaneous streams
AMD_DEFAULT_TIMEOUT_SECONDS30NoDetection timeout per stream
AMD_DETECTORSbeep_ml,tone_energyNoEnabled detectors
AMD_LOG_LEVELinfoNoSLF4J log level
AMD_DUMP_AUDIOfalseNoDump streams to WAV for debugging
AMD_DUMP_AUDIO_PATH/tmp/amd-dumpsNoDump directory
AMD_WORKER_API_TOKENYesBearer token for callback auth
AMD_ACTION_CALLBACK_URLhttp://nginx/api/voice/amd-actionNoLaravel callback URL
danger

AMD_WORKER_API_TOKEN must match the value configured in OPBX (AMD_WORKER_API_TOKEN in .env). The callback uses constant-time comparison to prevent timing attacks.

Detection Pipeline

  1. Cloudonix sends a connected event
  2. Cloudonix sends a start event with custom parameters (actions)
  3. The worker stores action_human, action_voicemail, and action_unknown
  4. Cloudonix sends media events every 20 ms with Base64 mu-law audio
  5. The worker decodes to PCM16 and resamples to 16 kHz
  6. Energy VAD segments speech
  7. After ~11.5 seconds, a rolling buffer tone scan checks for beeps
  8. The detection pipeline resolves to voicemail, human, or unknown
  9. The worker POSTs the result to /api/voice/amd-action
  10. Laravel updates the session profile and executes the configured action

Action Options

Actions are passed via <Parameter> elements inside the CXML <Stream> verb:

<Stream url="wss://.../ws/amd/detect" track="outbound">
<Parameter name="action_voicemail" value="HANGUP"/>
<Parameter name="action_human" value="CONTINUE"/>
<Parameter name="action_unknown" value="CONTINUE"/>
</Stream>
Action ValueBehavior
URL (https://...)Switch the call to the given voice application URL
HANGUPDisconnect the session
CONTINUEClose the WebSocket and take no further action

Default behavior:

  • Voicemail → HANGUP
  • Human or Unknown → CONTINUE

Health Endpoint

curl http://localhost:8083/health

Returns JSON with status, model load state, active streams, detection counts, and uptime.

Troubleshooting

IssueSolution
AMD results not appearingVerify AMD_WORKER_API_TOKEN matches OPBX; check docker compose logs -f amd-worker
High false-positive rateReview dumped audio with OfflineToneTest and tune detector parameters
Model fails to loadEnsure AMD_MODEL_PATH points to a valid ONNX file; the worker falls back to energy detection
Streams rejectedCheck concurrent stream count against AMD_MAX_CONCURRENT_STREAMS