feat: voice message distress analysis — paralinguistic features (closes #131) #144

Closed
Rockachopa wants to merge 1 commits from feat/131-voice-analysis into main
Owner

Voice Message Distress Analysis

Closes #131 (Epic #102 — Multimodal Crisis Detection)

What

Analyzes voice messages for distress signals using paralinguistic features — pure DSP, no neural model.

Signals Detected

Signal Normal Range Distress Indicator
Speech rate 100-180 wpm <80 slow (depression), >200 fast (panic)
Pitch variability 20-80 Hz std <20 Hz = monotone (depression)
Silence ratio 5-35% >35% = long pauses
Volume -30 to -10 dB <-30 dB = very quiet

API

from voice_analysis import analyze_voice_message

result = analyze_voice_message("voice_message.ogg")
# result.distress_score    → 0.0 - 1.0
# result.distress_level    → "low", "medium", "high"
# result.distress_signals  → ["monotone_voice", "long_pauses"]
# result.speech_rate_wpm   → 85.0
# result.transcript        → "I don't know what to do anymore..."

Thresholds

  • Low (< 0.3): Normal
  • Medium (0.3 - 0.7): Some distress signals present
  • High (> 0.7): Multiple strong indicators

Dependencies

  • whisper (OpenAI) — transcription + word timestamps
  • librosa — audio feature extraction (pitch, volume, silence)
  • numpy — numerical operations

Integration

Add to Telegram voice message handler:

if message.voice:
    audio_path = download_voice(message.voice)
    analysis = analyze_voice_message(audio_path)
    if analysis.distress_level in ("medium", "high"):
        trigger_crisis_protocol(analysis)
## Voice Message Distress Analysis Closes #131 (Epic #102 — Multimodal Crisis Detection) ### What Analyzes voice messages for distress signals using paralinguistic features — pure DSP, no neural model. ### Signals Detected | Signal | Normal Range | Distress Indicator | |--------|-------------|-------------------| | Speech rate | 100-180 wpm | <80 slow (depression), >200 fast (panic) | | Pitch variability | 20-80 Hz std | <20 Hz = monotone (depression) | | Silence ratio | 5-35% | >35% = long pauses | | Volume | -30 to -10 dB | <-30 dB = very quiet | ### API ```python from voice_analysis import analyze_voice_message result = analyze_voice_message("voice_message.ogg") # result.distress_score → 0.0 - 1.0 # result.distress_level → "low", "medium", "high" # result.distress_signals → ["monotone_voice", "long_pauses"] # result.speech_rate_wpm → 85.0 # result.transcript → "I don't know what to do anymore..." ``` ### Thresholds - **Low** (< 0.3): Normal - **Medium** (0.3 - 0.7): Some distress signals present - **High** (> 0.7): Multiple strong indicators ### Dependencies - `whisper` (OpenAI) — transcription + word timestamps - `librosa` — audio feature extraction (pitch, volume, silence) - `numpy` — numerical operations ### Integration Add to Telegram voice message handler: ```python if message.voice: audio_path = download_voice(message.voice) analysis = analyze_voice_message(audio_path) if analysis.distress_level in ("medium", "high"): trigger_crisis_protocol(analysis) ```
Rockachopa added 1 commit 2026-04-15 16:28:00 +00:00
feat: voice message distress analysis — paralinguistic features
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 8s
Smoke Test / smoke (pull_request) Successful in 17s
4dc6819079
Closes #131 (Epic #102 — Multimodal Crisis Detection)

Analyzes audio messages (OGG/MP3/WAV) for distress signals using
paralinguistic features — no neural model needed, pure DSP.

Signals detected:
- Speech rate: very slow (<80 wpm) or very fast (>200 wpm)
- Pitch variability: monotone voice (low F0 std = depression indicator)
- Silence ratio: long pauses (>35% silence)
- Volume: very quiet (<-30 dB)

Implementation:
- voice_analysis.py: Core module with analyze_voice_message()
- Whisper integration for transcription + word timestamps
- librosa for audio feature extraction (pitch, volume, silence)
- Composite distress score (0-1) from max of individual signals
- Thresholds: low (<0.3), medium (0.3-0.7), high (>0.7)

17 tests in tests/test_voice_analysis.py.
Timmy requested changes 2026-04-15 23:06:27 +00:00
Timmy left a comment
Owner

Good paralinguistic distress analysis. The feature set (speech rate, pitch variability, silence ratio, volume) covers the key indicators from clinical literature. Tests are solid.

Concerns:

  1. PRIVACY: Audio processing location: The analyze_voice_message function processes audio files. Confirm that audio data never leaves the local machine — no cloud transcription APIs. The transcribe_with_timestamps function is imported but not shown in this diff; verify it uses a local model (e.g., Whisper) not a cloud API.

  2. SAFETY: Distress threshold calibration: DISTRESS_THRESHOLDS high=1.0 means only a perfect distress score triggers 'high'. This is extremely conservative and could cause false negatives. A high threshold of 0.7-0.8 would be safer for a crisis detection system. The test test_high_is_10 asserts high==1.0 which confirms this concern.

  3. Good: max-based composite scoring — using max(individual_scores) not average means one severe signal is sufficient. This is the correct approach for safety.

  4. Good: Score capped at 1.0 preventing overflow.

  5. No audio retention — good for privacy, but verify the audio file is not cached by the transcription library.

Requesting changes for the high threshold issue (#2) — setting high=1.0 makes it nearly impossible to reach 'high' distress, creating a dangerous false negative gap.

Good paralinguistic distress analysis. The feature set (speech rate, pitch variability, silence ratio, volume) covers the key indicators from clinical literature. Tests are solid. Concerns: 1. **PRIVACY: Audio processing location**: The analyze_voice_message function processes audio files. Confirm that audio data never leaves the local machine — no cloud transcription APIs. The transcribe_with_timestamps function is imported but not shown in this diff; verify it uses a local model (e.g., Whisper) not a cloud API. 2. **SAFETY: Distress threshold calibration**: DISTRESS_THRESHOLDS high=1.0 means only a perfect distress score triggers 'high'. This is extremely conservative and could cause false negatives. A high threshold of 0.7-0.8 would be safer for a crisis detection system. The test test_high_is_10 asserts high==1.0 which confirms this concern. 3. **Good: max-based composite scoring** — using max(individual_scores) not average means one severe signal is sufficient. This is the correct approach for safety. 4. **Good: Score capped at 1.0** preventing overflow. 5. **No audio retention** — good for privacy, but verify the audio file is not cached by the transcription library. Requesting changes for the high threshold issue (#2) — setting high=1.0 makes it nearly impossible to reach 'high' distress, creating a dangerous false negative gap.
Timmy closed this pull request 2026-04-17 01:52:32 +00:00
Owner

Archived — branch unknown preserved for reference. Cherry-pick if still relevant.

Archived — branch `unknown` preserved for reference. Cherry-pick if still relevant.
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 8s
Smoke Test / smoke (pull_request) Successful in 17s

Pull request closed

Sign in to join this conversation.