Skip to main content

icad_tone_detection

PyPI version License: MIT

Detect Two-Tone (Quick Call), Pulsed Single-Tone (On/Off Beeps), Long Tones, Hi–Low warble tones, MDC1200 / FleetSync, and DTMF in scanner-audio recordings.
The heavy DSP is performed by a bundled native binary (icad_decode), while the Python wrapper handles audio I/O and STFT-based frequency extraction.


Features

  • Single function: tone_detect() or CLI tool icad-tone-detect
  • Tone types:
    • Pulsed single-tone
    • Two-Tone / Quick Call
    • Long tones
    • Hi–Low warble tones
    • MDC1200 / FleetSync
    • DTMF
  • Flexible inputs: File path, URL, bytes, BytesIO, or pydub.AudioSegment
  • Automatic resample to 16 kHz mono PCM via FFmpeg
  • Fully configurable: All thresholds & detection params exposed as keyword args or CLI flags
  • Binaries included for:
    • Linux (x86_64, arm64, armv7)
    • macOS (x86_64, arm64)
    • Windows (x86_64)

Installation

pip install icad_tone_detection

Requires: Python 3.9+ and ffmpeg in PATH.


Quick start — Python

from icad_tone_detection import tone_detect

result = tone_detect("my_scanner_recording.wav")
print(result.pulsed_result)
print(result.two_tone_result)

Defaults work for most recordings

The built-in defaults are intentionally neutral and should work well on typical scanner audio (8–16 kHz mono, moderate noise). In many cases you can just call tone detection with no extra knobs and get solid results. All detectors are enabled by default, and the frontend (STFT + grouping) aims to avoid over- or under-segmenting tones.

Use the simplest call first:

from icad_tone_detection import tone_detect

result = tone_detect("my_scanner_recording.wav")
print(result.pulsed_result)
print(result.two_tone_result)

Or via CLI:

icad-tone-detect my_scanner_recording.wav --debug

Only tweak parameters if you have a specific problem (e.g., very drifty tones, very brisk pulses, or unusually noisy/quiet recordings).

When to tweak (quick guide):

  • Very fast pulses → lower time_resolution_ms from 50 to 25.
  • Tone drift gets merged (e.g., 992/950 collapsing) → set fe_abs_cap_hz=20–24, and optionally fe_force_split_step_hz≈18 with fe_split_lookahead_frames=2.
  • Too many weak frames pass (noisy audio) → make gating stricter by raising fe_silence_below_global_db (e.g., -24 dB) or increasing fe_snr_above_noise_db (e.g., 8 dB).
  • Real tones get gated out (very quiet recording) → make gating looser by lowering fe_silence_below_global_db (e.g., -32 dB) or decreasing fe_snr_above_noise_db (e.g., 4 dB).
  • Two-tone pairs too wide/tight → adjust two_tone_bw_hz (typical 20–30 Hz) and ensure two_tone_min_pair_separation_hz (default 40 Hz) fits your system.

Quick start — CLI

# Show help
icad-tone-detect --help

# Analyze a file with MDC disabled
icad-tone-detect my.wav --detect_mdc false --debug

Boolean flags accepted: true/false, yes/no, or 1/0.


Full CLI example

icad-tone-detect my_scanner_recording.wav \
  --detect_pulsed true \
  --pulsed_bw_hz 25 \
  --pulsed_min_cycles 6 \
  --pulsed_min_on_ms 120  --pulsed_max_on_ms 900 \
  --pulsed_min_off_ms 25  --pulsed_max_off_ms 350 \
  --pulsed_auto_center_band 200,3000 \
  --pulsed_mode_bin_hz 5 \
  \
  --detect_two_tone true \
  --tone_a_min_length 0.85 --tone_b_min_length 2.6 \
  --two_tone_bw_hz 25 --two_tone_min_pair_separation_hz 40 \
  \
  --detect_hi_low true \
  --hi_low_interval 0.2 --hi_low_min_alternations 6 \
  --hi_low_tone_bw_hz 25 --hi_low_min_pair_separation_hz 40 \
  \
  --detect_long true \
  --long_tone_min_length 3.8 --long_tone_bw_hz 25 \
  \
  --detect_mdc false --detect_dtmf true \
  \
  --time_resolution_ms 50 --matching_threshold 2.5 \
  --fe_freq_band 200,3000 \
  --fe_merge_short_gaps_ms 0 \
  --fe_silence_below_global_db -28 --fe_snr_above_noise_db 6 \
  \
  # FE refinements to keep stepped pairs from merging:
  --fe_abs_cap_hz 30 \
  --fe_force_split_step_hz 18 \
  --fe_split_lookahead_frames 2 \
  \
  --debug

Example output (debug mode)

############################################################
ICAD Tone Detection: DEBUG - v2.8.3
------------------------------------------------------------
Decode binary path:        /opt/icad/bin/linux_x86_64/icad_decode
Analyzing audio at:        /captures/2025-08-09_call_173.wav

Matching Threshold:        2.5%
Time Resolution (ms):      50

-- Frequency Extraction (frontend) --
  Search band (Hz):        200.0..3000.0
  Merge short gaps ≤ ms:   0
  Silence below global:    -28.0 dB
  SNR above noise floor:   +6.0 dB
  Abs cap (Hz):            30.0
  Force-split step (Hz):   18.0
  Split lookahead frames:  2

-- Two-Tone (Quick Call) --
  A min length (s):        0.85
  B min length (s):        2.60
  Max A→B gap (s):         0.35
  Intra-band width (Hz):   25.0
  Min A/B separation (Hz): 40.0

-- Long Tone --
  Min length (s):          3.80
  Intra-band width (Hz):   25.0

-- Hi–Low Warble --
  Interval (s):            0.20
  Min alternations:        6
  Intra-band width (Hz):   25.0
  Min pair separation (Hz):40.0

-- Pulsed Single Tone (auto-centered) --
  Band (Hz):               200.0..3000.0
  Mode bin width (Hz):     5.0
  BW around center (Hz):   ±25.0
  Min cycles:              6
  ON range (ms):           120..900
  OFF range (ms):          25..350

-- External Decoders --
  MDC/FleetSync:           disabled (hp=200 Hz, lp=4000 Hz)
  DTMF:                    enabled

Total Duration (s):        39.42
Sample Rate (Hz):          16000

Matched Frequencies (8 groups):
  1) Start=2.31s  | End=5.24s  | Dur=2.93s
       Freqs: [1009.9, 1010.4, 1010.2, 1010.1, 1010.1, 1010.0, 1010.1, 1009.8, 1010.3, 1010.2]
  2) Start=5.24s  | End=5.31s  | Dur=0.07s
       Freqs: [0.0, 0.0]                       # OFF
  3) Start=5.31s  | End=8.18s  | Dur=2.87s
       Freqs: [473.3, 473.2, 473.2, 473.4, 473.2, 473.3, 473.2, 473.3, 473.2]
  4) Start=8.18s  | End=8.22s  | Dur=0.04s
       Freqs: [0.0]                            # OFF
  5) Start=8.22s  | End=11.10s | Dur=2.88s
       Freqs: [809.9, 810.0, 810.1, 809.9, 810.0, 810.0, 810.1, 810.0, 810.0]
  6) Start=12.00s | End=12.08s | Dur=0.08s
       Freqs: [770.1, 770.0, 770.1, 769.9]
  7) Start=12.35s | End=12.43s | Dur=0.08s
       Freqs: [1206.9, 1207.1, 1207.0, 1206.8]
  8) Start=20.52s | End=23.34s | Dur=2.82s
       Freqs: [954.7, 954.9, 955.1, 954.8, 955.0, 954.9, 954.8, 954.9, 954.9]

############################################################
Masked intervals
  Pulsed windows:          [2.31–5.24], [20.52–23.34]
  Two-tone B windows:      [8.22–11.10]
  Long-tone windows:       (none)
############################################################

------------------------------------------------------------
DETECTION SUMMARY
------------------------------------------------------------
Two-Tone (Quick Call): 1
Long Tones:            0
Hi-Low Warble:         0
Pulsed Single Tone:    2
MDC1200/FleetSync:     0   (disabled)
DTMF:                  2
------------------------------------------------------------

{
  "pulsed": [
    {
      "tone_id": "pl_1",
      "detected": 1010.1,
      "start": 2.31, "end": 5.24, "length": 2.93,
      "cycles": 8,
      "on_ms_median": 180, "off_ms_median": 92
    },
    {
      "tone_id": "pl_2",
      "detected": 955.0,
      "start": 20.52, "end": 23.34, "length": 2.82,
      "cycles": 7,
      "on_ms_median": 160, "off_ms_median": 85
    }
  ],
  "two_tone": [
    {
      "tone_id": "qc_1",
      "detected": [473.3, 810.0],
      "tone_a_length": 0.91, "tone_b_length": 2.88,
      "start": 5.31, "end": 11.10
    }
  ],
  "long_tone": [],
  "hi_low": [],
  "mdc": [],
  "dtmf": [
    { "digit": "5", "start": 12.00, "end": 12.08 },
    { "digit": "9", "start": 12.35, "end": 12.43 }
  ]
}

Python API — tone_detect() signature

result = tone_detect(
  audio_path="my.wav",          # path / URL / BytesIO / AudioSegment

  # STFT & grouping
  matching_threshold=2.5,       # % tolerance for grouping frames
  time_resolution_ms=50,        # STFT hop size in ms

  # Frequency-extraction (frontend)
  fe_freq_band=(200.0, 3000.0),         # Hz band to search for peaks
  fe_merge_short_gaps_ms=0,             # merge groups separated by ≤ this gap (ms)
  fe_silence_below_global_db=-28.0,     # OFF if frame is this many dB below global peak
  fe_snr_above_noise_db=6.0,            # require SNR above simple noise floor
  fe_abs_cap_hz=None,                   # cap dynamic tolerance (e.g., 30.0) or None
  fe_force_split_step_hz=None,          # force split if step > Hz (e.g., 18.0) or None
  fe_split_lookahead_frames=0,          # confirm forced split with lookahead

  # Quick Call (two-tone)
  tone_a_min_length=0.85,               # sec – min A-tone
  tone_b_min_length=2.6,                # sec – min B-tone
  two_tone_max_gap_between_a_b=0.35,    # sec – max gap A→B
  two_tone_bw_hz=25.0,                  # Hz – intra-group stability
  two_tone_min_pair_separation_hz=40.0, # Hz – min A/B separation

  # Hi/Low warble
  hi_low_interval=0.2,                  # sec – max gap between alternations
  hi_low_min_alternations=6,            # min alternations
  hi_low_tone_bw_hz=25.0,               # Hz – intra-group stability
  hi_low_min_pair_separation_hz=40.0,   # Hz – min separation between tones

  # Long tone
  long_tone_min_length=3.8,             # sec – min duration
  long_tone_bw_hz=25.0,                 # Hz – intra-group stability

  # Pulsed single tone (auto-centered)
  pulsed_bw_hz=25.0,                    # Hz ± deviation counted as ON
  pulsed_min_cycles=6,                  # min ON→OFF cycles
  pulsed_min_on_ms=120,                 # ms – ON min
  pulsed_max_on_ms=900,                 # ms – ON max
  pulsed_min_off_ms=25,                 # ms – OFF min
  pulsed_max_off_ms=350,                # ms – OFF max
  pulsed_auto_center_band=(200.0, 3000.0),  # Hz band to auto-estimate center
  pulsed_mode_bin_hz=5.0,               # Hz bin width for robust center mode

  # Detector toggles
  detect_pulsed=True,
  detect_two_tone=True,
  detect_long=True,
  detect_hi_low=True,

  # External decoders
  detect_mdc=True,                      # MDC1200 / FleetSync
  mdc_high_pass=200,                    # Hz
  mdc_low_pass=4000,                    # Hz
  detect_dtmf=True,

  debug=False,
)

Result object

tone_detect() returns a ToneDetectionResult dataclass with:

  • pulsed_result — Pulsed single-tone hits: tone_id, detected, start, end, length, cycles, on_ms_median, off_ms_median
  • two_tone_result — Quick-Call matches: tone_id, detected [A,B], tone_a_length, tone_b_length, start, end
  • long_result — Long tone hits: tone_id, detected, length, start, end
  • hi_low_result — Warble sequences: tone_id, detected [low,high], alternations, length, start, end
  • mdc_result — Decoded frames from external MDC/FleetSync decoder (if enabled)
  • dtmf_result — Decoded DTMF presses (if enabled)

Platforms & binaries

OS Architectures Wheel folder name
Linux x86_64, arm64, armv7 linux_x86_64, linux_arm64, linux_armv7
macOS x86_64, arm64 macos_x86_64, macos_arm64
Windows x86_64 windows_x86_64

Example audio

Sample WAV files are in examples/example_audio/.


Contributing

Issues and pull requests welcome: GitHub repo


License

MIT © TheGreatCodeholio • Version 2.8.6 • Python 3.9+

Metadata

Release files for icad-tone-detection 2.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for icad-tone-detection 2.9.0
File Size Uploaded
icad_tone_detection-2.9.0.tar.gz 3.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for icad-tone-detection 2.9.0
File Interpreter ABI Platform
icad_tone_detection-2.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 7.0 MB

Release files / icad_tone_detection-2.9.0.tar.gz

Download URL icad_tone_detection-2.9.0.tar.gz
Size 3.5 MB
Tags Source
SHA-256 checksum
How to use checksums
ade0c3be7f3b68258426fb36b82981aa9cb43f6fa51240bf78bc0ccf82a0dc8e
BLAKE2b-256 checksum
How to use checksums
0babf359945d2e843aa257fee42f895ab498f8173572963e19aa0730289548cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 3, 2026.

Transparency log

Release files / icad_tone_detection-2.9.0-py3-none-any.whl

Download URL icad_tone_detection-2.9.0-py3-none-any.whl
Size 3.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
619c7f14815bfdf6b689220453d2f64bdde60ba31e8a60f04a7617f6549abb01
BLAKE2b-256 checksum
How to use checksums
4cc2eec123a935e1ff43ca2932becb747166d7e711eded1e2cf4aa25121a5012
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.9.0 This release

2 release files

2.8.7

2 release files

2.8.6

2 release files

2.8.5

2 release files

2.8.4

2 release files

2.8.3

2 release files

2.8.2

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.2

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6

2 release files

2.5

2 release files

2.4

2 release files

2.3

2 release files

2.2

2 release files

2.1

2 release files

2.0

2 release files

1.5

2 release files

1.4

2 release files

1.3

2 release files

1.2

2 release files

1.1

2 release files

1.0

2 release files

0.9

2 release files

0.8

2 release files

0.7

2 release files

0.6

2 release files

0.5

2 release files

0.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page