Tutorial 2 — Detect acoustic units

Take a continuous recording, segment it into discrete listenable units.

Bandpass first

The detector assumes the input is already restricted to the species' vocal range. Otherwise the noise floor estimate is dominated by out-of-band energy.

from decipher import load_audio, bandpass

src = load_audio("data/raw/excerpt.wav")
bp = bandpass(src.samples, src.sr, lo_hz=40, hi_hz=4000, order=4)
After bandpass
After bandpass

Detect

from decipher import detect_units

units = detect_units(
    bp, src.sr,
    min_dur_s=0.08,
    max_dur_s=8.0,
    merge_gap_s=0.05,
    envelope="hilbert",     # or "rms" for long recordings with drifting noise
    smooth_ms=25.0,
    threshold_db=6.0,
    noise_floor="global",   # or "local" for sliding-window low-percentile
)

print(len(units), "units")
for u in units[:3]:
    print(f"  unit {u.index}: {u.start_s:.2f}–{u.end_s:.2f} s, peak={u.peak_db:.1f} dB")
envelope and threshold
The detector estimates a noise floor from the smoothed dB envelope, then marks frames as "active" when they exceed it by threshold_db dB.

Listen to the units

Each unit carries its own audio. Write them out and play them in order:

from decipher import write_wav

for u in units[:5]:
    write_wav(f"audio_samples/02_units/unit_{u.index:04d}.wav",
              u.source_samples, u.sr)
Humpback unit 000 — 375 ms
Humpback unit 000 — 375 ms
Detected by detect_units on a 30 s humpback excerpt fetched via decipher.fetch. 44100 Hz 0.375 s
Humpback unit 001 — 154 ms
A shorter unit. Listen: it's one phrase element. 44100 Hz
Humpback unit 002 — 270 ms
44100 Hz
Humpback unit 003 — 131 ms
44100 Hz
Humpback unit 004 — 116 ms
44100 Hz
Older segmentation examples (different corpus)
unit 0
unit 1
unit 2

Tune the parameters

ParameterWhat it doesIf too low / too high
threshold_dbHow far above the noise floor a frame must rise.Low: noise becomes units. High: quiet calls are missed.
smooth_msSmoothing window applied to the envelope.Low: rapid amplitude modulation fragments a single call. High: nearby calls fuse.
merge_gap_sAbove-threshold runs separated by less than this gap are merged.Low: clicks within a click-train become separate units. High: distinct calls fuse.
min_dur_s / max_dur_sHard duration filter.Drops candidates outside this window.
noise_floor"global" = constant median; "local" = sliding-window low-percentile.Use "local" when background noise drifts across long recordings.
duration histogram
Plot the duration histogram first — it almost always tells you whether min_dur_s and max_dur_s are set correctly.

Next

Cluster units into types →