MeetingTranscriptionService in FluidVoice: How It Handles Multi-Speaker Transcription
MeetingTranscriptionService is an @MainActor-bound ObservableObject that orchestrates end-to-end transcription of audio and video meeting recordings, delegating speaker diarization to underlying ASR providers while handling file validation, chunked processing, and result aggregation.
In the FluidVoice app, the MeetingTranscriptionService serves as the primary bridge between raw media files and searchable transcripts. Located in Sources/Fluid/Services/MeetingTranscriptionService.swift, this high-level Swift service manages the entire pipeline—from file validation to final text output—while optionally supporting multi-speaker identification through pluggable ASR providers.
What Is MeetingTranscriptionService?
The MeetingTranscriptionService is a Swift class marked with @MainActor and conforming to ObservableObject, designed specifically for transcribing complete audio or video files such as recorded meetings. It publishes reactive state properties including isTranscribing, progress, error, and result, enabling SwiftUI views to display real-time transcription status.
Rather than implementing speech recognition directly, the service receives an ASRService instance during initialization (lines 94-99), reusing already-loaded ASR models to avoid redundant downloads. This dependency injection pattern allows the service to focus on orchestration while delegating actual recognition to specialized providers.
How Multi-Speaker Transcription Works
The service handles multi-speaker identification through a delegation model rather than internal logic. According to the source comments (lines 61-62), the service "supports optional speaker diarization" by relying on the underlying provider returned by ASRService.fileTranscriptionProvider.
When the provider supports diarization—such as the proprietary "Parakeet" model or enhanced Whisper implementations—it returns a TranscriptionResult containing speaker-tagged segments. The service aggregates these results without modification, concatenating texts and averaging confidence scores. If the provider lacks diarization support, the output remains a single flat transcript. This architecture ensures the service can support future diarization-enabled models without code changes.
Implementation Architecture
Service Declaration and State Publishing
The class declaration (lines 64-70) establishes the service as an @MainActor type, ensuring all published state updates occur on the main thread. Key published properties include:
isTranscribing: Boolean tracking active transcription stateprogress: Double representing completion percentage (0.0 to 1.0)error: OptionalTranscriptionErrorfor failure statesresult: OptionalTranscriptionResultcontaining the final output
File Validation and Type Detection
Before processing, the service validates file extensions against a dynamically constructed set of supportedFileExtensions (lines 73-89). This check prevents attempting transcription on incompatible formats, surfacing errors early in the pipeline.
Dual-Path Processing Strategy
The service implements two distinct processing paths based on provider capabilities:
Native File Fast Path (lines 110-126): When the provider can handle the file format directly (e.g., uncompressed WAV files), the service calls provider.transcribeFile(at:), streaming the entire file without modification.
Chunked Processing for Video and Long Audio (lines 154-210, 224-274, 278-311): For formats the provider cannot handle natively—most video containers and compressed audio—the service opens the file via AVAudioFile, resamples audio to 16 kHz mono, and processes it in approximately 20-minute chunks. Each chunk is sent to provider.transcribe(samples), with progress updates published between chunks.
Result Aggregation and Persistence
After processing all chunks, the service joins non-empty transcriptions using finalText = allTranscriptions.joined(separator: " ") (lines 330-354). It calculates average confidence scores across all segments and constructs a TranscriptionResult containing duration, processing time, and transcript text. The result is stored in self.result and appended to FileTranscriptionHistoryStore for persistent access.
Error handling and analytics complete the pipeline (lines 322-340, 361-382), categorizing failures and reporting metrics via AnalyticsService.
Usage Example
To transcribe a meeting recording in your own FluidVoice integration:
import Fluid
// Obtain the shared ASR service
let asr = ASRService.shared
// Initialize the meeting transcription service
let meetingService = MeetingTranscriptionService(asrService: asr)
// Transcribe a local meeting file
Task {
do {
let result = try await meetingService.transcribeFile(
URL(fileURLWithPath: "/path/to/meeting.m4a")
)
print("Transcript:", result.text)
print("Duration:", result.duration, "seconds")
print("Confidence:", result.confidence)
} catch {
print("Transcription failed:", error.localizedDescription)
}
}
For UI integration, MeetingTranscriptionView (located in Sources/Fluid/UI/MeetingTranscriptionView.swift) demonstrates reactive usage via @StateObject, automatically wiring progress bars and error banners to the service's published properties.
Summary
- MeetingTranscriptionService is an
@MainActorObservableObjectthat manages end-to-end transcription of meeting recordings in FluidVoice. - Multi-speaker support is delegated to underlying ASR providers (Whisper or Parakeet), which return speaker-tagged results when available.
- Dual processing paths handle native audio files directly while resampling and chunking video files into ~20-minute segments.
- State management publishes real-time progress, errors, and results for reactive UI updates.
- Result aggregation concatenates chunk transcripts and averages confidence scores before persisting to
FileTranscriptionHistoryStore.
Frequently Asked Questions
Does MeetingTranscriptionService perform speaker diarization internally?
No. The service delegates diarization to the underlying ASR provider returned by ASRService.fileTranscriptionProvider. If the provider supports speaker identification—such as the Parakeet model or enhanced Whisper implementations—the service aggregates those tagged results. Otherwise, it returns a flat transcript without speaker labels.
What audio formats does MeetingTranscriptionService support?
The service dynamically builds a set of supported extensions based on the underlying provider's capabilities. It validates file extensions against supportedFileExtensions (lines 73-89) before processing. Video files and unsupported audio formats are automatically converted to 16 kHz mono PCM audio and processed in chunks.
How does the service handle large video files?
For files the provider cannot handle natively, the service opens them via AVAudioFile, resamples to 16 kHz mono, and processes audio in approximately 20-minute chunks (lines 154-210). This prevents memory exhaustion while allowing progress updates between chunks.
Where does MeetingTranscriptionService store transcription results?
Completed transcriptions are stored in the result property and automatically appended to FileTranscriptionHistoryStore (lines 361-368), which persists results for later review and export within the FluidVoice app.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →