# How to Switch ASR Providers in FluidVoice: A Complete Guide

> Easily switch ASR providers in FluidVoice, from OpenAI Whisper to Qwen 3. Learn how to manage speech-to-text engines with our complete guide.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: how-to-guide
- Published: 2026-08-14

---

**FluidVoice lets you switch between speech-to-text engines like OpenAI Whisper and Qwen 3 ASR through a settings-backed provider architecture centered on `ASRService`.**

The FluidVoice macOS app abstracts automatic speech recognition (ASR) behind a clean **provider pattern**. Instead of hardcoding a single transcription engine, the app stores your preference in `UserDefaults` and lazily instantiates the corresponding implementation of the `TranscriptionProvider` protocol. This guide walks through how to change ASR providers both in the UI and programmatically.

## Understanding the ASR Provider Architecture

FluidVoice's transcription system is built around three core components:

- **`TranscriptionProvider`** — the protocol that every ASR backend must implement
- **`ASRService`** — the singleton façade that routes transcription requests to the active provider
- **`VoiceEngineSettingsViewModel`** — the settings view model that persists user selection

When you request transcription via `ASRService.shared.transcribe()`, the service checks the `asrProviderSelection` key in `UserDefaults`, instantiates the matching provider (e.g., `WhisperProvider` or `Qwen3ASRProvider`), and delegates the actual audio processing.

## Built-in ASR Providers

| Provider | Class | Requirements | Best For |
|----------|-------|--------------|----------|
| **OpenAI Whisper** | `WhisperProvider` | macOS 13+, local model | Fast, offline transcription |
| **Qwen 3 ASR** | `Qwen3ASRProvider` | macOS 15+, more memory | Multilingual support, higher accuracy |

Both conform to `TranscriptionProvider` and are located in `Sources/Fluid/Services/`.

## Method 1: Switch ASR Providers Via Settings UI

The simplest way to change providers requires no code:

1. Open **FluidVoice** → click the **gear icon** → select **Voice Engine**
2. Choose your desired provider from the list (e.g., **Qwen 3 ASR**)
3. Close the settings panel — your selection auto-saves to `UserDefaults`

The next transcription request automatically uses the newly selected engine. `VoiceEngineSettingsViewModel` writes the selection to the `asrProviderSelection` key, and `ASRService` reads this on its next provider initialization.

## Method 2: Switch ASR Providers Programmatically

For testing, automation, or custom UI flows, modify the provider directly in Swift:

```swift
import Fluid

func switchToProvider(_ providerId: String) {
    // Update the stored preference
    UserDefaults.standard.set(providerId, forKey: "asrProviderSelection")
    
    // Invalidate current provider to force recreation
    ASRService.shared.invalidateCurrentProvider()
}

// Switch to Qwen 3 ASR
switchToProvider("qwen3")

// Or revert to Whisper
switchToProvider("whisper")

```

### Complete Transcription Example

```swift
import Fluid

func transcribeWithSelectedProvider(audioSamples: [Float]) async throws -> String {
    // ASRService automatically uses the provider from UserDefaults
    let result = try await ASRService.shared.transcribe(audioSamples)
    return result.text
}

// Usage after switching providers
Task {
    let samples: [Float] = captureAudio()  // your audio capture logic
    
    // This uses whichever provider is currently selected
    let text = try await transcribeWithSelectedProvider(audioSamples: samples)
    print("Transcribed: \(text)")
}

```

## Key Source Files for ASR Provider Switching

Understanding the codebase structure helps with custom modifications:

| File | Path | Purpose |
|------|------|---------|
| [`ASRService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/ASRService.swift) | [`Sources/Fluid/Services/ASRService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ASRService.swift) | Singleton service that manages provider lifecycle and routes transcription calls |
| [`TranscriptionProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/TranscriptionProvider.swift) | [`Sources/Fluid/Services/TranscriptionProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/TranscriptionProvider.swift) | Protocol defining the interface all ASR backends must implement |
| [`WhisperProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/WhisperProvider.swift) | [`Sources/Fluid/Services/WhisperProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/WhisperProvider.swift) | Local Whisper implementation, default provider |
| [`VoiceEngineSettingsViewModel.swift`](https://github.com/altic-dev/FluidVoice/blob/main/VoiceEngineSettingsViewModel.swift) | [`Sources/Fluid/UI/AISettings/VoiceEngineSettingsViewModel.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/UI/AISettings/VoiceEngineSettingsViewModel.swift) | Settings model that writes `asrProviderSelection` to `UserDefaults` |
| [`SettingsView.swift`](https://github.com/altic-dev/FluidVoice/blob/main/SettingsView.swift) | [`Sources/Fluid/UI/SettingsView.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/UI/SettingsView.swift) | UI entry point for the voice engine selection panel |

## Adding Custom ASR Providers

To integrate a new ASR engine:

1. Create a new class conforming to `TranscriptionProvider` in `Sources/Fluid/Services/`
2. Add your provider's identifier to `VoiceEngineSettingsViewModel`
3. Update `ASRService` to instantiate your class when your identifier is selected
4. The existing `UserDefaults` key and UI flow will automatically include your option

The protocol-based design means no changes are needed to transcription call sites — `ASRService.shared.transcribe()` works identically regardless of which provider is active.

## Performance Considerations When Switching ASR Providers

- **Memory pressure**: `Qwen3ASRProvider` requires significantly more RAM than `WhisperProvider`; macOS 15+ is enforced at the settings level
- **Cold start latency**: First transcription after switching providers incurs model loading time; subsequent calls reuse the cached instance until `invalidateCurrentProvider()` is called
- **Model downloads**: Some providers may trigger background model downloads on first selection; check `VoiceEngineSettingsViewModel` for download progress UI bindings

## Summary

- **FluidVoice uses `ASRService`** as a unified interface to multiple ASR backends
- **Provider selection persists** via `UserDefaults` key `asrProviderSelection`
- **Two switch methods**: Settings UI (no code) or programmatic `UserDefaults` updates with `invalidateCurrentProvider()`
- **Built-in providers**: `WhisperProvider` (default, efficient) and `Qwen3ASRProvider` (multilingual, macOS 15+)
- **Protocol-based architecture** enables adding custom ASR engines without modifying transcription call sites

## Frequently Asked Questions

### What happens to in-progress transcriptions when I switch ASR providers?

Active transcription tasks complete with their original provider. The switch only affects new requests. `ASRService` maintains the current provider instance until explicitly invalidated via `invalidateCurrentProvider()` or until the app restarts.

### Can I use multiple ASR providers simultaneously in FluidVoice?

Not through the standard API. `ASRService` is a singleton with one active provider at a time. For parallel transcription, instantiate providers directly (e.g., `WhisperProvider()` and `Qwen3ASRProvider()`) and call their `transcribe()` methods independently, bypassing `ASRService`.

### Where is my ASR provider preference stored?

The selection writes to standard `UserDefaults` under the key `asrProviderSelection` as a string identifier ("whisper", "qwen3", etc.). This persists across app launches and is readable/writeable from Swift code or command-line tools using `defaults write`.

### Why is Qwen 3 ASR unavailable on my Mac?

`VoiceEngineSettingsViewModel` gates this option to macOS 15+ due to memory and framework requirements. The settings UI hides unavailable providers based on `ProcessInfo.processInfo.operatingSystemVersion` checks. Running on older macOS versions shows only `WhisperProvider`.