Skip to content

feat: add CAM++ speaker diarization with sentence-level speaker labels#26

Open
yanhuicsdn wants to merge 2 commits into
Mininglamp-AI:mainfrom
yanhuicsdn:feature/speaker-diarization
Open

feat: add CAM++ speaker diarization with sentence-level speaker labels#26
yanhuicsdn wants to merge 2 commits into
Mininglamp-AI:mainfrom
yanhuicsdn:feature/speaker-diarization

Conversation

@yanhuicsdn

Copy link
Copy Markdown

Summary

Adds a speaker diarization module that uses FunASR CAM++ (iic/speech_campplus_sv_zh-cn_16k-common) to assign speaker labels to transcribed segments. Works at the sentence level by splitting VAD segments into small sliding windows (3s / 1.5s shift) before clustering.

Changes

New: core/diarization.py

  • SpeakerDiarizer class with two-phase embedding extraction:
    • Phase 1: Split VAD segments into short (3s) overlapping windows
    • Phase 2: Extract CAM++ speaker embeddings per window, then cluster via ClusterBackend
  • Majority voting assigns each original VAD segment the label of its majority sub-windows
  • Graceful fallback: if funasr is not installed, silently degrades to single-speaker output
  • Configurable: merge_thr, window_s, shift_s

Modified: core/auto_model.py

  • New AutoModel.transcribe_with_diarization() method that returns:
    • "text": full concatenated transcript with [讲话人N] markers
    • "segments": list of per-segment dicts with start_s, end_s, text, speaker
    • "num_speakers": number of distinct speakers found
  • Lazy-initialised SpeakerDiarizer instance; failures caught gracefully

Usage

from core.auto_model import AutoModel

model = AutoModel(model=..., vad_model=...)
result = model.transcribe_with_diarization("meeting.wav")

print(f"Detected {result[\"num_speakers\"]} speakers")
for seg in result["segments"]:
    print(f"[Speaker {seg[\"speaker\"]}] {seg[\"text\"]}")

Dependencies (optional)

  • funasr (for CAM++ model)
  • soundfile (for audio I/O)

Performance

On a 7-minute podcast (8 VAD segments → 108 sub-windows):

  • Transcription: ~50s (MLX)
  • Diarization overhead: ~15s (CAM++ sliding window)
  • Total: ~65s vs ~50s without diarization (+30%)

The overhead is proportional to the number of sub-windows, not audio duration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant