Skip to content

Kokoro Text-to-Speech - #112

Merged
Alex-Wengg merged 129 commits into
mainfrom
kokoro-2
Oct 6, 2025
Merged

Alex-Wengg merged 129 commits into
mainfrom
kokoro-2

Conversation

@Alex-Wengg

@Alex-Wengg Alex-Wengg commented Sep 19, 2025

Copy link
Copy Markdown
Member

This PR introduces text-to-speech functionality to FluidAudio using the Kokoro TTS model from Core ML.

  • Added TTSManager for speech synthesis with configurable speed and speaker selection
  • Implemented TTSModels for automatic model downloading from HuggingFace ( FluidInference/coreml-kokoro)
  • Created CLI command fluidaudio tts for command-line speech synthesis
  • two support, one for 15 seconds of audio another for 5 seconds audio. 15 seconds covers 99% of sentence structures. 5 seconds is needed due to frame count drifting and coreml static conversion limitation.
swift run fluidaudio tts "$(cat tts_test.txt)" --output tts_test.wav  --metrics metrics.json --chunk-dir chunks
  • Only the English Kokoro pipeline is fully wired; other languages remain limited.
  • iOS GitHub runners can’t unzip the eSpeak bundle, so TTS on iOS CI still expects pre-extracted resources. Unless we move the xframework to the repo

docs

Documentation/KokoroChunkerOverview.md # chunking algorithm priority details

Alex-Wengg and others added 30 commits September 14, 2025 11:21
The CI was failing because models weren't being downloaded. Added the --auto-download flag to ensure models are fetched before synthesis.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added support for --auto-download flag in the TTS CLI command to download required models before synthesis. This fixes CI failures where models weren't available.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
The frontend model was being skipped during download but required during load, causing "Model not found" errors. Removed the skip condition to ensure all 7 models are downloaded.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added verification step after downloading models to ensure they can be loaded. If loading fails, explicitly compile the model using MLModel.compileModel(). This fixes CI errors where downloaded models weren't in the correct compiled format.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed compilation error by adding await for the async compileModel call and replaced unused testModel variable with underscore.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Changed to download the complete CoreML model structure including:
- model.mil
- weights/weight.bin
- coremldata.bin
- metadata.json
- analytics/ directory

This matches the actual compiled model format on HuggingFace instead of trying to create our own Manifest.json.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
The kokoro_frontend model has metadata.json instead of Manifest.json. Added special handling to:
- Download metadata.json and save it as Manifest.json for frontend
- Download Manifest.json directly for other models
- Only download coremldata.bin and analytics for frontend

This fixes the "A valid manifest does not exist" error.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Changed from using current working directory to TTSModels.cacheDirectoryURL() for storing Kokoro models. This ensures models are cached in:
- macOS: ~/Library/Caches/fluidaudio/
- Linux: ~/.cache/fluidaudio/

This is consistent with all other models in FluidAudio and prevents models from being downloaded to random directories.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread Documentation/Benchmarks.md Outdated
### Kokoro-82M MLX Pipeline

```bash
uv run python benchmark_kokoro_mlx.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scripts are not here right

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are in the morbius internal

Comment thread Documentation/Benchmarks.md Outdated
### Kokoro-82M PyTorch Pipeline

```bash
uv run python benchmark_kokoro_pip.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh also, this is probably not a fair apples to apples comparison since the torch is using "cpu" as the backend. It might be a lot faster with "mps" as the backend. Might even be almost on par with MLX

EIther copy the scripts here or provide some documentation on what thtese scripts are doing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed, just leave this to me after you merge, I will run the scripts on my laptop

Comment thread Documentation/EspeakFramework.md Outdated
Comment thread Sources/FluidAudio/TextToSpeech/Kokoro/Assets/Lexicon/EspeakG2P.swift Outdated
Comment thread Sources/FluidAudio/TextToSpeech/Kokoro/Assets/TtsResourceDownloader.swift Outdated
Comment thread Sources/FluidAudio/TextToSpeech/Kokoro/Pipeline/Preprocess/KokoroChunker.swift Outdated
Comment thread Sources/FluidAudio/TextToSpeech/Kokoro/Pipeline/Preprocess/KokoroChunker.swift Outdated
Comment thread Sources/FluidAudio/TextToSpeech/Kokoro/Pipeline/Preprocess/KokoroChunker.swift Outdated
Comment thread benchmark_kokoro_mlx.py Outdated
Comment thread Sources/FluidAudioCLI/main.swift Outdated
- .github/workflows/swift-format.yml

  2. Documentation/EspeakFramework.md
  - Reorganized documentation to clarify that both macOS and iOS use the same primary flow (packaged bundle first)
  - Separated fallback behavior - only macOS falls back to downloading zip if bundle missing
  - Removed confusion about platform differences in bundling approach

  3. sources/FluidAudio/Resources/.gitkeep & espeak-ng/.gitkeep ❌ DELETED
  - Removed placeholder files (no longer needed if committing actual bundle)

  4. Sources/FluidAudio/TextToSpeech/Kokoro/Assets/Lexicon/EspeakG2P.swift
  - Added configurable espeak voice parameter (defaults to en-us)
  - Tracks current voice and only calls espeak_SetVoiceByName() when it changes
  - Enables multi-language support (British English, Spanish, French, etc.)

  5. Sources/FluidAudio/TextToSpeech/Kokoro/Assets/TtsResourceDownloader.swift
  - Removed PyTorch .pt file download  logic (58 lines removed)
  - Simplified voice embedding download to only fetch usable JSON files
  - Better error messages when embeddings  unavailable

  6.  Sources/FluidAudio/TextToSpeech/Kokoro/Pipeline/Preprocess/KokoroChunker.swift
  - Removed #if canImport(ESpeakNG)  checks (96 lines removed)
  - Removed #available checks for macOS 10.14/iOS 12.0 (unnecessary with current deployment targets)
  - Removed NSLinguisticTagger fallback  code (always uses NaturalLanguage now)
  - Cleaner code without redundant platform checks
ESpeakNG xcframework supports iOS but CocoaPods fails to link during pob lib on iOS.
Comment thread FluidAudio.podspec
Comment on lines +22 to +37
# iOS Configuration
# Exclude TTS module from iOS builds to avoid ESpeakNG xcframework linking issues.
# CocoaPods has known limitations with vendored xcframeworks during pod lib lint on iOS:
# the framework symbols aren't properly linked in the temporary build environment,
# causing "Undefined symbols" linker errors even though the binary is valid.
# iOS builds include: ASR (speech recognition), Diarization, and VAD (voice activity detection).
spec.ios.exclude_files = "Sources/FluidAudio/TextToSpeech/**/*"
spec.ios.frameworks = "CoreML", "AVFoundation", "Accelerate", "UIKit"

# macOS Configuration
# ESpeakNG framework is only vendored for macOS in the podspec (not a framework limitation).
# The xcframework supports iOS, but CocoaPods fails to link it during iOS validation.
# This enables TTS (text-to-speech) functionality with G2P (grapheme-to-phoneme) conversion.
# macOS builds include: ASR, Diarization, VAD, and TTS with ESpeakNG support.
spec.osx.vendored_frameworks = "Sources/FluidAudio/Frameworks/ESpeakNG.xcframework"
spec.osx.frameworks = "CoreML", "AVFoundation", "Accelerate", "Cocoa"

@BrandonWeng BrandonWeng Oct 4, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So not supported on iOS when they use cocoa pods?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see.. Im okay with the limitation, it will jsut ahve to be something to call out. Can you link this issue in the comments?

@BrandonWeng BrandonWeng left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this and iterating with the open source folks. Lets just merge this in and iterate on it for now. Just make sure to mark it as beta and send any feedback our way

/// Non-English voices are present in the model but are not yet quality-assured for production use.
public static let availableVoices: [String] = [
// American English
// American English (supported, beta)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought you tested some of these

@Alex-Wengg
Alex-Wengg merged commit 93bd9cf into main Oct 6, 2025
10 checks passed
@Alex-Wengg
Alex-Wengg deleted the kokoro-2 branch October 6, 2025 21:53
@danieloi

Copy link
Copy Markdown

@Alex-Wengg
This looks really exciting! I've been trying to use your CoreML Kokoro models from HuggingFace without much success on my iOS device.

Is this tested to work only on the 10s and 15s versions? I downloaded versions of those models from huggingface that didn't specify any length but it looks like those have been taken down and replaced with only versions that have 10s or 15s in their names. Are these strict limitations of the model output? If so why can't I have the coreml version be more like the onnx version that generates for varying lengths of audio without issue?

@Alex-Wengg

Alex-Wengg commented Oct 22, 2025

Copy link
Copy Markdown
Member Author

@danieloi
coreml doesn't support dynammic length, this was one of the trade offs for performance . We also have a 5 second model if you would like to take .

as for ios development we do have some users in the discord server that were able to develop on their iOS apps i can point you to . just ping me as @bobasaur on discord and we can discuss more .

https://huggingface.co/FluidInference/kokoro-82m-coreml/tree/main/kokoro_21_5s.mlmodelc

Alex-Wengg added a commit that referenced this pull request Jan 1, 2026
SGD2718 pushed a commit that referenced this pull request Jan 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request text-to-speech Related to text to speech

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants