Text-to-Speech (TTS)
Relevant source files
The following files were used as context for generating this wiki page:
The Text-to-Speech (TTS) system provides native voice synthesis capabilities for the Conduit application on both Android and iOS platforms. It allows the application to convert text strings into audible speech using system-level engines and offers features for both live playback and file-based synthesis.
The system interfaces with Flutter via method channels and event channels, enabling the frontend to control voice selection, speech rate, pitch, and volume. It supports advanced features like voice-call audio routing to ensure speech remains audible during active communication sessions.
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:25-50, ios/Runner/NativeIosTtsBridge.swift:85-110
Core Architecture
The TTS implementation uses a bridge pattern to connect the Flutter framework with native platform APIs. Each platform implements a bridge class that handles method calls and streams events back to the Dart environment.
Platform Components
- NativeTtsBridge (Android): Utilizes
android.speech.tts.TextToSpeechto manage speech synthesis. It handles initialization states and implements aSilentCallAudioKeepAliveto maintain audio routing during voice calls. - NativeIosTtsBridge (iOS): Uses
AVFoundation'sAVSpeechSynthesizer. It manages session activation and tracks pending starts to synchronize Flutter results with native delegate callbacks.
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:25-60, ios/Runner/NativeIosTtsBridge.swift:85-95
Communication Flow
The following sequence diagram illustrates how a speech request is processed from Flutter to the native platform.
Functional Capabilities
Live Speech Control
The system supports several standard control methods to manage the lifecycle of a speech utterance.
| Method | Description | Platform Implementation |
|---|---|---|
speak | Synthesizes and plays text immediately. | engine.speak (Android) / synthesizer.speak (iOS) |
stop | Cancels all current and pending speech. | engine.stop (Android) / stopSpeaking (iOS) |
pause | Pauses speech at the current word or frame. | engine.stop (Android) / pauseSpeaking (iOS) |
resume | Continues speech from the paused offset. | speakRequest (Android) / continueSpeaking (iOS) |
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:80-140, ios/Runner/NativeIosTtsBridge.swift:100-145
File Synthesis
The synthesize method generates an audio file (WAV on Android, CAF on iOS) from text instead of playing it through speakers. This is used for preparing speech audio in advance.
- Android: Uses
synthesizeToFileand returns adurationMsextracted viaMediaMetadataRetriever. - iOS: Uses
AVSpeechSynthesizer.writeto stream PCM buffers into anAVAudioFileon the main queue.
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:100-125, ios/Runner/NativeIosTtsBridge.swift:310-340
Audio Routing and Keep-Alive
Voice Call Integration
When the voiceCall parameter is true, the system optimizes audio for communication. On Android, this sets the AudioAttributes usage to USAGE_VOICE_COMMUNICATION. On iOS, it activates the AVAudioSession with the notifyOthersOnDeactivation option.
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:345-360, ios/Runner/NativeIosTtsBridge.swift:115-125
Android Silent Keep-Alive
The SilentCallAudioKeepAlive class addresses a specific Android 12+ behavior where the system drops MODE_IN_COMMUNICATION if no audio is playing from the app's process.
- It creates a static
AudioTrackcontaining silence. - It loops this track while the TTS engine (which runs in a separate process) is speaking.
- It maintains a 5-second grace period (
CALL_AUDIO_KEEP_ALIVE_GRACE_MS) between utterances to prevent the call route from dropping during brief pauses in a conversation.
Voice Management
The system allows querying available system voices and selecting them by identifier.
Voice Attributes
The getVoices method returns a list of maps containing:
id/identifier: Unique name or ID of the voice.name: Human-readable name.locale/language: The BCP-47 language tag.quality: Numeric value (e.g., Default, Enhanced, Premium).
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:400-410, ios/Runner/NativeIosTtsBridge.swift:180-210
Parameter Scaling
Native bridges normalize input parameters from Flutter to fit system constraints:
- Rate: Android scales 0.1 to 3.0; iOS uses system minimum/maximum speech rates.
- Pitch: Typically clamped between 0.5 and 2.0.
- Volume: Clamped between 0.0 and 1.0.
Sources: android/app/src/main/kotlin/app/cogwheel/conduit/NativeTtsBridge.kt:435-450, ios/Runner/NativeIosTtsBridge.swift:230-260
Conclusion
The TTS system in Conduit ensures high-quality speech synthesis by leveraging platform-specific frameworks while abstracting the complexity of audio routing and hardware-specific behaviors like Android's communication mode ownership. This architecture supports both real-time interaction and pre-synthesized audio workflows required for a robust assistant interface.