An end-to-end pipeline for automated transcription and summarization of Vietnamese meeting recordings. The system processes long-duration audio (1–3 hours), identifies individual speakers, generates a time-stamped transcript, and produces structured meeting minutes using a large language model.
This project was built to address a practical problem: long Vietnamese meetings produce hours of audio that are difficult to review manually. The pipeline automates the full workflow — from raw audio file to a polished Markdown meeting report — without requiring manual intervention.
The system is designed to handle real-world conditions such as overlapping silence, multiple speakers, and domain-specific terminology.
The pipeline executes five sequential stages:
Audio File
|
v
[Stage 1] Voice Activity Detection + Smart Chunking (Silero VAD)
|
v
[Stage 2] Speaker Diarization (pyannote.audio 3.1)
|
v
[Stage 3] Speech-to-Text Transcription (PhoWhisper-large / Whisper large-v3)
|
v
[Stage 4] Transcript Alignment (merge diarization + transcription)
|
v
[Stage 5] Map-Reduce Summarization (Gemini 2.0 Flash / GPT-4o mini)
|
v
Output: transcript.json, transcript.txt, meeting_minutes.md
Uses Silero VAD to detect speech segments and remove silence. Audio is then split into overlapping chunks of approximately 10–15 minutes at natural silence boundaries, ensuring no sentence is cut mid-way. This approach prevents issues with Whisper's fixed 30-second context window on long recordings.
Uses pyannote/speaker-diarization-3.1 to identify who is speaking at each point in time. The number of speakers can be set manually or detected automatically. Requires a Hugging Face token with access to the pyannote model.
Two backends are supported:
| Mode | Model | Use Case |
|---|---|---|
| Default | vinai/PhoWhisper-large | Optimized for Vietnamese, higher accuracy |
Fast (--fast) | openai/whisper-large-v3 | Multilingual, faster on CPU |
The transcriber processes each audio chunk in 30-second sub-windows aligned to Whisper's input format. Absolute timestamps are preserved across chunks.
Merges diarization segments (who spoke) with transcription segments (what was said) using a time-overlap heuristic. The output is a sequence of AlignedSegment objects, each containing a speaker label, start/end timestamps, and the corresponding text.
Exports:
transcript.json — machine-readable formattranscript.txt — human-readable formatFor long meetings, the full transcript exceeds the context limit of most LLMs. The pipeline uses a map-reduce strategy:
Two LLM backends are supported: Google Gemini (gemini-2.0-flash) and OpenAI (gpt-4o-mini). The active backend is set in config.py.
asr_meeting_summarization/
|
|-- main.py # Pipeline entry point
|-- config.py # Centralized configuration
|-- requirements.txt
|-- speech-to-text.py # Standalone transcription script
|
|-- preprocessing/
| |-- vad_chunker.py # Stage 1: VAD + smart chunking
|
|-- diarization/
| |-- speaker_diarizer.py # Stage 2: Speaker diarization
|
|-- transcription/
| |-- transcriber.py # Stage 3: Whisper-based ASR
|
|-- utils/
| |-- alignment.py # Stage 4: Transcript + diarization alignment
|
|-- summarization/
| |-- map_reduce.py # Stage 5: Map-reduce summarization
|
|-- output/ # Generated output files (gitignored)
|-- audio_records/ # Input audio files (gitignored)
int8_float16 compute type with faster-whisper)librosa>=0.10.0
soundfile>=0.12.0
faster-whisper>=1.0.0
pyannote.audio>=3.1.0
torch>=2.0.0
torchaudio>=2.0.0
google-generativeai>=0.8.0
python-dotenv>=1.0.0
numpy>=1.24.0
Install all dependencies:
pip install -r requirements.txt
All settings are managed in config.py. Sensitive credentials are loaded from a .env file and are never committed to the repository.
Create a .env file in the project root:
HF_TOKEN=your_huggingface_token
GEMINI_API_KEY=your_gemini_api_key
OPENAI_API_KEY=your_openai_api_key
Note: A Hugging Face token with accepted terms for
pyannote/speaker-diarization-3.1is required for speaker diarization. Visit the model page on Hugging Face to request access.
Key configuration values in config.py:
| Parameter | Default | Description |
|---|---|---|
WHISPER_MODEL_ID | vinai/PhoWhisper-large | Primary ASR model |
WHISPER_LANGUAGE | vi | Target transcription language |
DIARIZATION_MODEL | pyannote/speaker-diarization-3.1 | Speaker diarization model |
LLM_BACKEND | openai | LLM backend (openai or gemini) |
GEMINI_MODEL | gemini-2.0-flash | Gemini model variant |
OPENAI_MODEL | gpt-4o-mini | OpenAI model variant |
TARGET_CHUNK_DURATION | 600 (10 min) | Target audio chunk size in seconds |
SUMMARY_CHUNK_MINUTES | 15 | Transcript chunk size for map phase |
VAD_THRESHOLD | 0.5 | Silero VAD speech probability threshold |
python main.py --input path/to/meeting.mp3
python main.py --input path/to/meeting.mp3 --num-speakers 4
python main.py --input path/to/meeting.mp3 --fast
python main.py --input path/to/meeting.mp3 --skip-diarization
python main.py --input path/to/meeting.mp3 --skip-summarization
python main.py --input path/to/meeting.mp3 --output-dir path/to/output/
usage: main.py [-h] --input INPUT [--num-speakers NUM_SPEAKERS]
[--output-dir OUTPUT_DIR] [--skip-diarization]
[--skip-summarization] [--fast] [--lang LANG]
Options:
--input, -i Path to audio file (mp3, wav, flac, etc.)
--num-speakers, -n Number of speakers (auto-detect if not set)
--output-dir, -o Output directory (default: ./output)
--skip-diarization Skip speaker diarization
--skip-summarization Skip LLM summarization
--fast Use openai/whisper-large-v3 instead of PhoWhisper
--lang Language code override (e.g. vi, en)
After a successful run, the following files are written to the output directory:
| File | Format | Description |
|---|---|---|
transcript.json | JSON | Structured transcript with speaker labels and timestamps |
transcript.txt | Plain text | Human-readable transcript with speaker labels |
meeting_minutes.md | Markdown | Structured meeting minutes generated by the LLM |
transcript.txt)[00:00:12] SPEAKER_00: Xin chào tất cả mọi người, hôm nay chúng ta sẽ thảo luận về kế hoạch quý 2.
[00:00:28] SPEAKER_01: Vâng, tôi đã chuẩn bị báo cáo tình hình dự án.
meeting_minutes.md)# BIEN BAN CUOC HOP
## Thong tin chung
| Thong tin | Chi tiet |
|---|---|
| Thoi luong | ~1 gio 45 phut |
| So nguoi | 4 |
## Tom tat tong quan
...
## Action Items
| STT | Nhiem vu | Nguoi phu trach | Ghi chu |
|---|---|---|---|
...
For cases where only transcription is needed (without diarization or summarization), a lightweight standalone script is available:
python speech-to-text.py
This script loads an audio file and outputs raw transcriptions using PhoWhisper-large, without the full pipeline overhead.
--fast flag uses whisper-large-v3 which is faster on CPU but slightly less accurate for Vietnamese.This project is intended for academic and research purposes. Third-party models used in this pipeline (PhoWhisper, pyannote.audio) are subject to their respective licenses. Refer to their respective Hugging Face model pages for terms of use.
An end-to-end pipeline for automated transcription and summarization of Vietnamese meeting recordings. The system processes long-duration audio (1–3 hours), identifies individual speakers, generates a time-stamped transcript, and produces structured meeting minutes using a large language model.
This project was built to address a practical problem: long Vietnamese meetings produce hours of audio that are difficult to review manually. The pipeline automates the full workflow — from raw audio file to a polished Markdown meeting report — without requiring manual intervention.
The system is designed to handle real-world conditions such as overlapping silence, multiple speakers, and domain-specific terminology.
The pipeline executes five sequential stages:
Audio File
|
v
[Stage 1] Voice Activity Detection + Smart Chunking (Silero VAD)
|
v
[Stage 2] Speaker Diarization (pyannote.audio 3.1)
|
v
[Stage 3] Speech-to-Text Transcription (PhoWhisper-large / Whisper large-v3)
|
v
[Stage 4] Transcript Alignment (merge diarization + transcription)
|
v
[Stage 5] Map-Reduce Summarization (Gemini 2.0 Flash / GPT-4o mini)
|
v
Output: transcript.json, transcript.txt, meeting_minutes.md
Uses Silero VAD to detect speech segments and remove silence. Audio is then split into overlapping chunks of approximately 10–15 minutes at natural silence boundaries, ensuring no sentence is cut mid-way. This approach prevents issues with Whisper's fixed 30-second context window on long recordings.
Uses pyannote/speaker-diarization-3.1 to identify who is speaking at each point in time. The number of speakers can be set manually or detected automatically. Requires a Hugging Face token with access to the pyannote model.
Two backends are supported:
| Mode | Model | Use Case |
|---|---|---|
| Default | vinai/PhoWhisper-large | Optimized for Vietnamese, higher accuracy |
Fast (--fast) | openai/whisper-large-v3 | Multilingual, faster on CPU |
The transcriber processes each audio chunk in 30-second sub-windows aligned to Whisper's input format. Absolute timestamps are preserved across chunks.
Merges diarization segments (who spoke) with transcription segments (what was said) using a time-overlap heuristic. The output is a sequence of AlignedSegment objects, each containing a speaker label, start/end timestamps, and the corresponding text.
Exports:
transcript.json — machine-readable formattranscript.txt — human-readable formatFor long meetings, the full transcript exceeds the context limit of most LLMs. The pipeline uses a map-reduce strategy:
Two LLM backends are supported: Google Gemini (gemini-2.0-flash) and OpenAI (gpt-4o-mini). The active backend is set in config.py.
asr_meeting_summarization/
|
|-- main.py # Pipeline entry point
|-- config.py # Centralized configuration
|-- requirements.txt
|-- speech-to-text.py # Standalone transcription script
|
|-- preprocessing/
| |-- vad_chunker.py # Stage 1: VAD + smart chunking
|
|-- diarization/
| |-- speaker_diarizer.py # Stage 2: Speaker diarization
|
|-- transcription/
| |-- transcriber.py # Stage 3: Whisper-based ASR
|
|-- utils/
| |-- alignment.py # Stage 4: Transcript + diarization alignment
|
|-- summarization/
| |-- map_reduce.py # Stage 5: Map-reduce summarization
|
|-- output/ # Generated output files (gitignored)
|-- audio_records/ # Input audio files (gitignored)
int8_float16 compute type with faster-whisper)librosa>=0.10.0
soundfile>=0.12.0
faster-whisper>=1.0.0
pyannote.audio>=3.1.0
torch>=2.0.0
torchaudio>=2.0.0
google-generativeai>=0.8.0
python-dotenv>=1.0.0
numpy>=1.24.0
Install all dependencies:
pip install -r requirements.txt
All settings are managed in config.py. Sensitive credentials are loaded from a .env file and are never committed to the repository.
Create a .env file in the project root:
HF_TOKEN=your_huggingface_token
GEMINI_API_KEY=your_gemini_api_key
OPENAI_API_KEY=your_openai_api_key
Note: A Hugging Face token with accepted terms for
pyannote/speaker-diarization-3.1is required for speaker diarization. Visit the model page on Hugging Face to request access.
Key configuration values in config.py:
| Parameter | Default | Description |
|---|---|---|
WHISPER_MODEL_ID | vinai/PhoWhisper-large | Primary ASR model |
WHISPER_LANGUAGE | vi | Target transcription language |
DIARIZATION_MODEL | pyannote/speaker-diarization-3.1 | Speaker diarization model |
LLM_BACKEND | openai | LLM backend (openai or gemini) |
GEMINI_MODEL | gemini-2.0-flash | Gemini model variant |
OPENAI_MODEL | gpt-4o-mini | OpenAI model variant |
TARGET_CHUNK_DURATION | 600 (10 min) | Target audio chunk size in seconds |
SUMMARY_CHUNK_MINUTES | 15 | Transcript chunk size for map phase |
VAD_THRESHOLD | 0.5 | Silero VAD speech probability threshold |
python main.py --input path/to/meeting.mp3
python main.py --input path/to/meeting.mp3 --num-speakers 4
python main.py --input path/to/meeting.mp3 --fast
python main.py --input path/to/meeting.mp3 --skip-diarization
python main.py --input path/to/meeting.mp3 --skip-summarization
python main.py --input path/to/meeting.mp3 --output-dir path/to/output/
usage: main.py [-h] --input INPUT [--num-speakers NUM_SPEAKERS]
[--output-dir OUTPUT_DIR] [--skip-diarization]
[--skip-summarization] [--fast] [--lang LANG]
Options:
--input, -i Path to audio file (mp3, wav, flac, etc.)
--num-speakers, -n Number of speakers (auto-detect if not set)
--output-dir, -o Output directory (default: ./output)
--skip-diarization Skip speaker diarization
--skip-summarization Skip LLM summarization
--fast Use openai/whisper-large-v3 instead of PhoWhisper
--lang Language code override (e.g. vi, en)
After a successful run, the following files are written to the output directory:
| File | Format | Description |
|---|---|---|
transcript.json | JSON | Structured transcript with speaker labels and timestamps |
transcript.txt | Plain text | Human-readable transcript with speaker labels |
meeting_minutes.md | Markdown | Structured meeting minutes generated by the LLM |
transcript.txt)[00:00:12] SPEAKER_00: Xin chào tất cả mọi người, hôm nay chúng ta sẽ thảo luận về kế hoạch quý 2.
[00:00:28] SPEAKER_01: Vâng, tôi đã chuẩn bị báo cáo tình hình dự án.
meeting_minutes.md)# BIEN BAN CUOC HOP
## Thong tin chung
| Thong tin | Chi tiet |
|---|---|
| Thoi luong | ~1 gio 45 phut |
| So nguoi | 4 |
## Tom tat tong quan
...
## Action Items
| STT | Nhiem vu | Nguoi phu trach | Ghi chu |
|---|---|---|---|
...
For cases where only transcription is needed (without diarization or summarization), a lightweight standalone script is available:
python speech-to-text.py
This script loads an audio file and outputs raw transcriptions using PhoWhisper-large, without the full pipeline overhead.
--fast flag uses whisper-large-v3 which is faster on CPU but slightly less accurate for Vietnamese.This project is intended for academic and research purposes. Third-party models used in this pipeline (PhoWhisper, pyannote.audio) are subject to their respective licenses. Refer to their respective Hugging Face model pages for terms of use.