# MeetingTranscriptionService in FluidVoice: How It Handles Multi-Speaker Transcription

> Discover FluidVoice's MeetingTranscriptionService. Learn how this @MainActor ObservableObject handles multi-speaker transcription by orchestrating audio processing, speaker diarization, and result aggregation.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: deep-dive
- Published: 2026-07-01

---

**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`](https://github.com/altic-dev/FluidVoice/blob/main/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 state
- `progress`: Double representing completion percentage (0.0 to 1.0)
- `error`: Optional `TranscriptionError` for failure states
- `result`: Optional `TranscriptionResult` containing 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:

```swift
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`](https://github.com/altic-dev/FluidVoice/blob/main/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 `@MainActor` `ObservableObject` that 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.