Supertonic 2
This commit is contained in:
@@ -12,7 +12,7 @@ struct ContentView: View {
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 12) {
|
||||
Text("SupertonicTTS iOS Demo")
|
||||
Text("Supertonic 2 iOS Demo")
|
||||
.font(.title2.weight(.semibold))
|
||||
.foregroundColor(.primary)
|
||||
|
||||
@@ -44,6 +44,19 @@ struct ContentView: View {
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.padding(.horizontal)
|
||||
|
||||
HStack(spacing: 12) {
|
||||
Text("Language")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Picker("Language", selection: $vm.language) {
|
||||
ForEach(TTSService.Language.allCases, id: \.self) { lang in
|
||||
Text(lang.displayName).tag(lang)
|
||||
}
|
||||
}
|
||||
.pickerStyle(MenuPickerStyle())
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
HStack(spacing: 16) {
|
||||
|
||||
@@ -3,6 +3,23 @@ import OnnxRuntimeBindings
|
||||
|
||||
final class TTSService {
|
||||
enum Voice { case male, female }
|
||||
enum Language: String, CaseIterable {
|
||||
case en = "en"
|
||||
case ko = "ko"
|
||||
case es = "es"
|
||||
case pt = "pt"
|
||||
case fr = "fr"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .en: return "English"
|
||||
case .ko: return "한국어"
|
||||
case .es: return "Español"
|
||||
case .pt: return "Português"
|
||||
case .fr: return "Français"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let env: ORTEnv
|
||||
private let textToSpeech: TextToSpeech
|
||||
@@ -16,13 +33,13 @@ final class TTSService {
|
||||
sampleRate = textToSpeech.sampleRate
|
||||
}
|
||||
|
||||
func synthesize(text: String, nfe: Int, voice: Voice) async throws -> URL {
|
||||
func synthesize(text: String, nfe: Int, voice: Voice, language: Language) async throws -> URL {
|
||||
// Load style for the selected voice
|
||||
let styleURL = try Self.locateVoiceStyleURL(voice: voice)
|
||||
let style = try loadVoiceStyle([styleURL.path], verbose: false)
|
||||
|
||||
// 2) Synthesize via packed TextToSpeech component
|
||||
let (wav, duration) = try textToSpeech.call(text, style, nfe)
|
||||
let (wav, duration) = try textToSpeech.call(text, language.rawValue, style, nfe)
|
||||
let audioSeconds = Double(duration)
|
||||
let wavLenSample = min(Int(Double(sampleRate) * audioSeconds), wav.count)
|
||||
let wavOut = Array(wav[0..<wavLenSample])
|
||||
|
||||
@@ -6,6 +6,7 @@ final class TTSViewModel: ObservableObject {
|
||||
@Published var text: String = "This morning, I took a walk in the park, and the sound of the birds and the breeze was so pleasant that I stopped for a long time just to listen."
|
||||
@Published var nfe: Double = 5
|
||||
@Published var voice: TTSService.Voice = .male
|
||||
@Published var language: TTSService.Language = .en
|
||||
@Published var isGenerating: Bool = false
|
||||
@Published var isPlaying: Bool = false
|
||||
@Published var errorMessage: String?
|
||||
@@ -39,7 +40,7 @@ final class TTSViewModel: ObservableObject {
|
||||
Task {
|
||||
let tic = Date()
|
||||
do {
|
||||
let url = try await service.synthesize(text: text, nfe: Int(nfe), voice: voice)
|
||||
let url = try await service.synthesize(text: text, nfe: Int(nfe), voice: voice, language: language)
|
||||
let elapsed = Date().timeIntervalSince(tic)
|
||||
let audio = audioDuration(at: url)
|
||||
await MainActor.run {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Supertonic iOS Example App
|
||||
|
||||
A minimal iOS demo that runs Supertonic (ONNX Runtime) on-device. The app shows:
|
||||
A minimal iOS demo that runs Supertonic 2 (ONNX Runtime) on-device. The app shows:
|
||||
- Multiline text input
|
||||
- NFE (denoising steps) slider
|
||||
- Voice toggle (M/F)
|
||||
- Language selector (en, ko, es, pt, fr)
|
||||
- Generate & Play buttons
|
||||
- RTF display (Elapsed / Audio seconds)
|
||||
|
||||
@@ -11,6 +12,8 @@ All ONNX models/configs are reused from `Supertonic/assets/onnx`, and voice styl
|
||||
|
||||
## 📰 Update News
|
||||
|
||||
**2026.01.06** - 🎉 **Supertonic 2** released with multilingual support! Now supports English (`en`), Korean (`ko`), Spanish (`es`), Portuguese (`pt`), and French (`fr`). [Demo](https://huggingface.co/spaces/Supertone/supertonic-2) | [Models](https://huggingface.co/Supertone/supertonic-2)
|
||||
|
||||
**2025.12.10** - Added [6 new voice styles](https://huggingface.co/Supertone/supertonic/tree/b10dbaf18b316159be75b34d24f740008fddd381) (M3, M4, M5, F3, F4, F5). See [Voices](https://supertone-inc.github.io/supertonic-py/voices/) for details
|
||||
|
||||
**2025.12.08** - Optimized ONNX models via [OnnxSlim](https://github.com/inisis/OnnxSlim) now available on [Hugging Face Models](https://huggingface.co/Supertone/supertonic)
|
||||
@@ -59,7 +62,17 @@ These references are defined in `project.yml` and added to the app bundle by Xco
|
||||
## App Controls
|
||||
- **Text**: Multiline `TextEditor`
|
||||
- **NFE**: Denoising steps (default 5)
|
||||
- **Voice**: M1/M2/F1/F2 voice style selector (4 pre-extracted styles)
|
||||
- **Voice**: M/F voice style selector
|
||||
- **Language**: Language selector (English, 한국어, Español, Português, Français)
|
||||
- **Generate**: Runs end-to-end synthesis
|
||||
- **Play/Stop**: Controls playback of the last output
|
||||
- **RTF**: Shows Elapsed / Audio seconds for quick performance intuition
|
||||
- **RTF**: Shows Elapsed / Audio seconds for quick performance intuition
|
||||
|
||||
## Multilingual Support
|
||||
|
||||
Supertonic 2 supports multiple languages. Select the appropriate language for your input text:
|
||||
- **English (en)**: Default language
|
||||
- **한국어 (ko)**: Korean
|
||||
- **Español (es)**: Spanish
|
||||
- **Português (pt)**: Portuguese
|
||||
- **Français (fr)**: French
|
||||
Reference in New Issue
Block a user