Speaker Diarization Guide
October 5, 2025 · View on GitHub
HushNote now supports speaker diarization - identifying "who spoke when" in your meeting recordings.
Overview
Speaker diarization adds speaker labels to transcriptions, making it easy to see who said what. This is particularly useful for:
- Multi-person meetings and interviews
- Panel discussions and group conversations
- Podcasts with multiple hosts/guests
- Customer calls and consultations
Features
✅ Automatic Speaker Detection - Uses AI to identify different speakers ✅ Interactive Labeling - Shows quotes to help you identify each speaker ✅ Privacy-First - All processing happens locally on your machine ✅ Future-Ready - Designed for upcoming Zoom/Teams API integration ✅ Multiple Formats - Export with speaker labels in TXT, MD, JSON, SRT, VTT
Quick Start
Full Workflow with Diarization
# Record, diarize, transcribe, label, and summarize
./hushnote full --diarize --speakers 3 -m small -o mistral:7b
# The workflow will:
# 1. Record audio
# 2. Identify 3 speakers
# 3. Transcribe the audio
# 4. Show you quotes and ask "Who is SPEAKER_00?"
# 5. You type names: "Alice", "Bob", "Charlie"
# 6. Create final transcript with [Alice], [Bob], [Charlie] labels
# 7. Generate AI summary with speaker context
Step-by-Step Workflow
# 1. Record a meeting
./hushnote record -d 1800 # 30 minutes
# 2. Identify speakers (diarization)
./hushnote diarize recordings/meeting_20251005_143022.wav --speakers 3
# 3. Transcribe the audio
./hushnote transcribe recordings/meeting_20251005_143022.wav -m small
# 4. Merge diarization + transcription (done automatically by hushnote)
./merge_diarization.py recordings/meeting_20251005_143022_speakers.json \
recordings/meeting_20251005_143022.json
# 5. Label speakers interactively
./hushnote label recordings/meeting_20251005_143022_diarized.json
# 6. Apply labels to create final transcript
./hushnote apply-labels recordings/meeting_20251005_143022_speakers_labeled.json
# 7. Summarize (optional)
./hushnote summarize recordings/meeting_20251005_143022.txt
Setup
1. Install Dependencies
# Install pyannote.audio and dependencies
./venv/bin/pip install pyannote.audio torch huggingface-hub
# Or use requirements.txt
./venv/bin/pip install -r requirements.txt
2. Get HuggingFace Token
Pyannote models require a free HuggingFace account:
- Create account: https://huggingface.co/join
- Accept conditions: https://huggingface.co/pyannote/speaker-diarization-3.1
- Create token: https://huggingface.co/settings/tokens
- Set environment variable:
export HF_TOKEN="your_token_here"
# Or add to ~/.bashrc for persistence
echo 'export HF_TOKEN="your_token_here"' >> ~/.bashrc
Note: Token is only needed for first-time model download. Models are cached locally at ~/.cache/huggingface/ and work offline afterward.
Usage Examples
Example 1: Two-Person Interview
# Record interview
./hushnote record -d 3600
# Diarize with 2 speakers
./hushnote full --diarize --speakers 2 -m medium
# Interactive labeling shows:
# SPEAKER_00: "Thanks for joining me today..."
# → You type: "Host"
#
# SPEAKER_01: "Happy to be here..."
# → You type: "Guest"
# Output: transcript with [Host] and [Guest] labels
Example 2: Multi-Person Meeting
# Record team meeting
./hushnote record
# Auto-detect number of speakers
./hushnote full --diarize -m small
# Label each speaker:
# SPEAKER_00 → "Alice" (Product Manager)
# SPEAKER_01 → "Bob" (Engineer)
# SPEAKER_02 → "Charlie" (Designer)
# SPEAKER_03 → "Dana" (QA Lead)
Example 3: Existing Recording
# You already have a recording
./hushnote diarize old_meeting.wav --speakers 4
./hushnote transcribe old_meeting.wav -m base
./merge_diarization.py old_meeting_speakers.json old_meeting.json
./hushnote label old_meeting_diarized.json
./hushnote apply-labels old_meeting_speakers_labeled.json -f md
Interactive Labeling
When you run ./hushnote label, you'll see:
HushNote Speaker Labeling
══════════════════════════════════════════════════════════════════
Found 3 speakers in meeting_20251005_143022.wav
──────────────────────────────────────────────────────────────────
SPEAKER_00 (7:30, 45 segments)
Sample quotes:
[00:12] "Good morning everyone, let's get started."
[03:45] "I think we need to prioritize the backend."
[12:30] "Let me summarize the action items."
Who is SPEAKER_00? Alice Johnson
✓ Labeled as "Alice Johnson"
──────────────────────────────────────────────────────────────────
SPEAKER_01 (11:20, 67 segments)
Sample quotes:
[01:15] "Thanks Alice. I wanted to discuss the API timeline."
[05:22] "We should allocate more resources to frontend."
[15:40] "I'll take ownership of the deployment pipeline."
Who is SPEAKER_01? Bob Smith
✓ Labeled as "Bob Smith"
──────────────────────────────────────────────────────────────────
✓ All speakers labeled!
Output Formats
Text Format (TXT)
[Alice Johnson] Good morning everyone, let's get started with today's meeting.
[Bob Smith] Thanks Alice. I wanted to discuss the timeline for the API release.
[Charlie Davis] I agree with Bob's approach on the architecture.
Markdown Format (MD)
**Alice Johnson**: Good morning everyone, let's get started.
**Bob Smith**: Thanks Alice. I wanted to discuss the API timeline.
**Charlie Davis**: I agree with Bob's approach.
JSON Format
{
"segments": [
{
"speaker": "Alice Johnson",
"speaker_id": "SPEAKER_00",
"start": 0.5,
"end": 5.2,
"text": "Good morning everyone..."
}
],
"labels": {
"SPEAKER_00": {
"name": "Alice Johnson",
"source": "manual"
}
}
}
SRT Format (Subtitles)
1
00:00:00,500 --> 00:00:05,200
[Alice Johnson] Good morning everyone, let's get started.
2
00:00:05,300 --> 00:00:12,100
[Bob Smith] Thanks Alice. I wanted to discuss the timeline.
File Organization
After running ./hushnote full --diarize, you'll have:
recordings/
├── meeting_20251005_143022.wav # Original audio
├── meeting_20251005_143022_speakers.json # Raw diarization
├── meeting_20251005_143022.json # Raw transcription
├── meeting_20251005_143022_diarized.json # Merged data
├── meeting_20251005_143022_speakers_labeled.json # After labeling
├── meeting_20251005_143022.txt # Final transcript
└── meeting_20251005_143022_summary.md # AI summary
Commands Reference
./hushnote diarize FILE [OPTIONS]
Identify speakers in an audio file.
Options:
-s, --speakers NUM- Expected number of speakers--min-speakers NUM- Minimum speakers (auto-detect)--max-speakers NUM- Maximum speakers (auto-detect)--hf-token TOKEN- HuggingFace API token
Examples:
# Auto-detect speakers
./hushnote diarize meeting.wav
# Expect 3 speakers
./hushnote diarize meeting.wav --speakers 3
# Detect 2-5 speakers
./hushnote diarize meeting.wav --min-speakers 2 --max-speakers 5
./hushnote label FILE
Interactively label speakers with their names.
Examples:
./hushnote label recordings/meeting_20251005_143022_diarized.json
./hushnote apply-labels FILE [OPTIONS]
Apply speaker labels to create final transcript.
Options:
-f, --format FORMAT- Output format (txt, md, json, srt, vtt)-o, --output FILE- Output file path
Examples:
# Create text transcript
./hushnote apply-labels meeting_speakers_labeled.json
# Create markdown
./hushnote apply-labels meeting_speakers_labeled.json -f md
# Create SRT subtitles
./hushnote apply-labels meeting_speakers_labeled.json -f srt
./hushnote full --diarize [OPTIONS]
Complete workflow with speaker diarization.
Options:
--diarize- Enable speaker diarization-s, --speakers NUM- Number of speakers-m, --model MODEL- Whisper model (tiny, base, small, medium, large-v3)-o, --ollama MODEL- Ollama summarization model-d, --duration SEC- Recording duration
Examples:
# Full workflow with diarization
./hushnote full --diarize --speakers 3 -m small -o mistral:7b
# Auto-detect speakers
./hushnote full --diarize -m base
# 1-hour recording with diarization
./hushnote full --diarize -d 3600 -m medium
Performance
Diarization adds processing time:
| Task | Without Diarization | With Diarization |
|---|---|---|
| 1 hour audio (CPU) | ~10-15 min | ~20-30 min |
| 1 hour audio (GPU) | ~3-5 min | ~8-12 min |
GPU acceleration recommended for faster processing:
# Install GPU support (NVIDIA/AMD)
./venv/bin/pip install torch torchvision torchaudio
# Diarization automatically uses GPU if available
Future API Integration
The speaker label format is designed for future integration with meeting platforms:
Planned: Zoom Integration
# Future feature (not yet implemented)
./hushnote full --diarize --zoom-meeting-id 123456789
# Will automatically:
# - Fetch participant names from Zoom API
# - Match voices to participants
# - Label speakers automatically (no manual labeling needed)
Planned: Teams Integration
# Future feature (not yet implemented)
./hushnote full --diarize --teams-meeting-url "https://..."
# Will automatically label speakers from Teams participant list
Speaker Label Format (Future-Ready)
The JSON format includes fields for API integration:
{
"labels": {
"SPEAKER_00": {
"name": "Alice Johnson",
"email": "alice@company.com", // Future: from Zoom/Teams
"role": "Product Manager", // Future: from meeting metadata
"source": "zoom_api", // Future: "zoom_api", "teams_api"
"zoom_participant_id": "abc123" // Future: platform-specific ID
}
}
}
Troubleshooting
"Error loading model" or "HuggingFace token required"
Solution:
- Get a free HuggingFace account: https://huggingface.co/join
- Accept pyannote conditions: https://huggingface.co/pyannote/speaker-diarization-3.1
- Create access token: https://huggingface.co/settings/tokens
- Set environment variable:
export HF_TOKEN="your_token"
"pyannote.audio not installed"
Solution:
./venv/bin/pip install pyannote.audio torch huggingface-hub
Poor speaker separation
Tips:
- Use higher quality audio (good microphone)
- Reduce background noise
- Specify expected number of speakers with
--speakers - Try different speaker count ranges with
--min-speakersand--max-speakers
Speakers merged or split incorrectly
Solution:
- Adjust
--speakersparameter - For 2 people, use
--speakers 2(don't let it auto-detect) - For uncertain count, use
--min-speakers 2 --max-speakers 5
GPU out of memory
Solution:
# Use CPU instead
# (GPU is automatically used if available, no flag needed)
# To force CPU, you can uninstall GPU PyTorch or set device manually in code
Privacy & Security
✅ No Cloud Services - All diarization happens locally
✅ No Audio Upload - Audio never leaves your machine
✅ Local Model Cache - Models stored at ~/.cache/huggingface/
✅ Offline Capable - Works without internet after first model download
✅ Open Source - Fully auditable code
The HuggingFace token is only used to download models once. After that, everything runs offline.
Technical Details
How It Works
-
Diarization (pyannote.audio)
- Analyzes audio waveform
- Detects voice activity
- Identifies speaker changes
- Outputs segments: "SPEAKER_00 spoke from 0:05 to 0:12"
-
Transcription (faster-whisper)
- Converts speech to text
- Outputs segments with timestamps
- Independent of speaker info
-
Merging
- Matches transcription timestamps to speaker segments
- Combines: "At 0:05, SPEAKER_00 said 'Hello'"
-
Labeling
- Shows user sample quotes from each speaker
- User identifies speakers by voice/content
- Maps SPEAKER_00 → "Alice Johnson"
-
Final Output
- Replaces speaker IDs with names
- Formats as readable transcript
Models Used
-
Diarization:
pyannote/speaker-diarization-3.1- Size: ~50-200MB
- Accuracy: ~10% DER (Diarization Error Rate)
- License: MIT
-
Transcription:
faster-whisper(OpenAI Whisper)- Models: tiny (75MB) to large-v3 (3GB)
- See main README for Whisper details
Examples
See examples/ directory for sample outputs:
example_diarized.txt- Two-person interview transcriptexample_meeting.md- Four-person team meetingexample_podcast.srt- Podcast with subtitles
Contributing
Interested in improving diarization? Areas for contribution:
- Speaker voice profiling (remember voices across meetings)
- Real-time diarization for streaming
- Zoom/Teams API integration
- Improved speaker labeling UX
- Multi-language speaker detection
See CONTRIBUTING.md for details.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
HushNote - Privacy-first meeting transcription with speaker diarization. 🤫