b0a8ed2a5a
Measured six recordings off a live P25 node and found two independent defects causing lost audio at the end of calls: - stop_recording() sliced the ring buffer immediately, so if the MP3 muxer had not yet delivered the tail the file was silently short. Now waits (bounded, 2s) until buffered audio covers the end epoch. - TGID-change closes used the new grant's epoch as the end with no pad at all, guaranteeing truncation on every split. Tail pad is now a setting, default raised 0.5s -> 1.0s. The ring buffer also capped maximum call length: a call longer than the buffer had its front silently clamped. The ring now serves the pre-roll only, with a per-call accumulator for the rest, bounded at 4.8MB. Clamping is loudly warned rather than silent. Uploads averaged 63% silence, which inflates STT cost and is a known Whisper hallucination trigger. Leading/trailing silence is now trimmed conservatively (-40dB, 0.25s guard, internal pauses untouched). started_at/ended_at still describe the call; new audio_* fields carry the trimmed audio bounds so playback can map back to wall clock. All-silence recordings are skipped and logged instead of uploaded. Also: log measured control-channel idle on idle-timeout closes so CALL_IDLE_TIMEOUT can be tuned from data, and quiet the httpx logger which emitted ~170k lines/day of poll noise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.5 KiB
Bash
65 lines
2.5 KiB
Bash
# Node Identity
|
|
NODE_ID=node-001
|
|
NODE_NAME="My Radio Node"
|
|
NODE_LAT=0.0
|
|
NODE_LON=0.0
|
|
|
|
# MQTT — point to your C2 server
|
|
MQTT_BROKER=localhost
|
|
MQTT_PORT=1883
|
|
# Must match MQTT_NODE_USER/MQTT_NODE_PASS in the server's top-level .env
|
|
MQTT_USER=drb-node
|
|
MQTT_PASS=change-me-node
|
|
|
|
# C2 server for audio upload (leave blank to disable upload)
|
|
C2_URL=http://localhost:8888
|
|
# API key is provisioned automatically via MQTT after admin approves the node
|
|
|
|
# Icecast (local container — usually no need to change)
|
|
# Live listening only. Call recording and Discord voice use PulseAudio instead.
|
|
ICECAST_SOURCE_PASSWORD=hackme
|
|
ICECAST_ADMIN_PASSWORD=admin
|
|
ICECAST_HOST=localhost
|
|
ICECAST_PORT=8000
|
|
ICECAST_MOUNT=/radio
|
|
|
|
# PulseAudio capture (usually no need to change)
|
|
# Monitor of the drb_sink null sink that Liquidsoap writes into.
|
|
PULSE_SOURCE=drb_sink.monitor
|
|
# Seconds to wait for the shared PulseAudio socket before giving up and retrying.
|
|
PULSE_WAIT_TIMEOUT=30
|
|
|
|
# Call segmentation: seconds of radio silence before the current recording is
|
|
# closed. Grants on the same talkgroup within this window stay in ONE recording.
|
|
# Tune ONLY from the "measured control-channel idle" line the edge node logs on
|
|
# every idle-timeout close — silence measured in the audio is a different clock
|
|
# (it also contains the ~1.9s P25 grant-to-speech delay).
|
|
CALL_IDLE_TIMEOUT=3
|
|
|
|
# Seconds of audio kept after the last transmission ends. This is the only
|
|
# headroom protecting the final word of a transmission — usually the disposition
|
|
# or the address. Measured at 0.5s it left ~0.3s of real margin and one recording
|
|
# ended mid-word, hence 1.0.
|
|
CALL_TAIL_PAD_SECONDS=1.0
|
|
|
|
# Strip leading/trailing dead air before upload. ~63% of an untrimmed recording
|
|
# is silence, which costs Whisper spend and makes it hallucinate text that was
|
|
# never spoken. Only the head and tail are touched, with a guard margin so no
|
|
# syllable is clipped. Set to false to upload raw audio.
|
|
TRIM_SILENCE=true
|
|
# dBFS below which audio counts as silence for detection.
|
|
TRIM_SILENCE_THRESHOLD_DB=-40
|
|
# Seconds of audio kept either side of detected speech.
|
|
TRIM_SILENCE_GUARD_SECONDS=0.25
|
|
|
|
# OP25 container (usually no need to change)
|
|
OP25_API_URL=http://localhost:8001
|
|
OP25_TERMINAL_URL=http://localhost:8081
|
|
|
|
# Container registry — set these to pull pre-built images instead of building locally.
|
|
# Must match the DOCKER_ORG variable and repo name configured in Gitea.
|
|
# Leave blank to always build locally.
|
|
IMAGE_REGISTRY=git.vpn.cusano.net
|
|
DOCKER_ORG=
|
|
DOCKER_REPO=
|