Skip to content

Latest commit

 

History

History
380 lines (277 loc) · 21.9 KB

File metadata and controls

380 lines (277 loc) · 21.9 KB

Speech Swift

Modelos de IA de voz para Apple Silicon, impulsados por MLX Swift y CoreML.

📖 Read in: English · 中文 · 日本語 · 한국어 · Español · Deutsch · Français · हिन्दी · Português · Русский

Reconocimiento, síntesis y comprensión de voz en el dispositivo para Mac e iOS. Se ejecuta localmente en Apple Silicon — sin nube, sin claves de API, ningún dato sale del dispositivo.

📚 Documentación completa → · 🤗 Modelos en HuggingFace · 📝 Blog · 💬 Discord

  • Qwen3-ASR — Voz a texto (reconocimiento automático del habla, 52 idiomas, MLX + CoreML)
  • Parakeet TDT — Voz a texto vía CoreML (Neural Engine, NVIDIA FastConformer + decodificador TDT, 25 idiomas)
  • Omnilingual ASR — Voz a texto (Meta wav2vec2 + CTC, 1.672 idiomas en 32 escrituras, CoreML 300M + MLX 300M/1B/3B/7B)
  • Dictado en streaming — Dictado en tiempo real con resultados parciales y detección de fin de enunciado (Parakeet-EOU-120M)
  • Nemotron Streaming — ASR en streaming de baja latencia con puntuación y mayúsculas nativas (NVIDIA Nemotron-Speech-Streaming-0.6B, CoreML, inglés)
  • Qwen3-ForcedAligner — Alineación de marcas temporales a nivel de palabra (audio + texto → marcas temporales)
  • Qwen3-TTS — Síntesis de texto a voz (máxima calidad, streaming, hablantes personalizados, 10 idiomas)
  • CosyVoice TTS — TTS con streaming, clonación de voz, diálogo multi-hablante y etiquetas de emoción (9 idiomas)
  • Kokoro TTS — TTS en el dispositivo (82M, CoreML/Neural Engine, 54 voces, listo para iOS, 10 idiomas)
  • VibeVoice TTS — TTS de formato largo / múltiples hablantes (Microsoft VibeVoice Realtime-0.5B + 1.5B, MLX, síntesis de podcast/audiolibro de hasta 90 min, EN/ZH)
  • Qwen3.5-Chat — Chat LLM en el dispositivo (0.8B, MLX INT4 + CoreML INT8, DeltaNet híbrido, tokens en streaming)
  • MADLAD-400 — Traducción multidireccional entre 400+ idiomas (3B, MLX INT4 + INT8, T5 v1.1, Apache 2.0)
  • PersonaPlex — Voz a voz full-duplex (7B, audio de entrada → audio de salida, 18 presets de voz)
  • DeepFilterNet3 — Supresión de ruido en tiempo real (2.1M parámetros, 48 kHz)
  • Separación de fuentes — Separación de fuentes musicales con Open-Unmix (UMX-HQ / UMX-L, 4 stems: voces/batería/bajo/otros, 44,1 kHz estéreo)
  • Palabra de activación — Detección de palabras clave en el dispositivo (KWS Zipformer 3M, CoreML, 26× tiempo real, lista de palabras clave configurable)
  • VAD — Detección de actividad vocal (Silero streaming, Pyannote offline, FireRedVAD 100+ idiomas)
  • Diarización de hablantes — Quién habló cuándo (pipeline Pyannote, Sortformer de extremo a extremo en Neural Engine)
  • Embeddings de hablante — WeSpeaker ResNet34 (256 dim), CAM++ (192 dim)

Papers: Qwen3-ASR (Alibaba) · Qwen3-TTS (Alibaba) · Omnilingual ASR (Meta) · Parakeet TDT (NVIDIA) · CosyVoice 3 (Alibaba) · Kokoro (StyleTTS 2) · PersonaPlex (NVIDIA) · Mimi (Kyutai) · Sortformer (NVIDIA)

Novedades

Inicio rápido

Añade el paquete a tu Package.swift:

.package(url: "https://github.com/soniqo/speech-swift", branch: "main")

Importa solo los módulos que necesites — cada modelo es una librería SPM independiente, así no pagas por lo que no uses:

.product(name: "ParakeetStreamingASR", package: "speech-swift"),
.product(name: "SpeechUI",             package: "speech-swift"),  // vistas SwiftUI opcionales

Transcribe un buffer de audio en 3 líneas:

import ParakeetStreamingASR

let model = try await ParakeetStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000)

Streaming en vivo con resultados parciales:

for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
    print(partial.isFinal ? "FINAL: \(partial.text)" : "... \(partial.text)")
}

Vista de dictado SwiftUI en ~10 líneas:

import SwiftUI
import ParakeetStreamingASR
import SpeechUI

@MainActor
struct DictateView: View {
    @State private var store = TranscriptionStore()

    var body: some View {
        TranscriptionView(finals: store.finalLines, currentPartial: store.currentPartial)
            .task {
                let model = try? await ParakeetStreamingASRModel.fromPretrained()
                guard let model else { return }
                for await p in model.transcribeStream(audio: samples, sampleRate: 16000) {
                    store.apply(text: p.text, isFinal: p.isFinal)
                }
            }
    }
}

SpeechUI solo incluye TranscriptionView (finales + parciales) y TranscriptionStore (adaptador de ASR en streaming). Usa AVFoundation para la visualización y reproducción de audio.

Productos SPM disponibles: Qwen3ASR, Qwen3TTS, Qwen3TTSCoreML, ParakeetASR, ParakeetStreamingASR, NemotronStreamingASR, OmnilingualASR, KokoroTTS, VibeVoiceTTS, CosyVoiceTTS, PersonaPlex, SpeechVAD, SpeechEnhancement, SourceSeparation, Qwen3Chat, SpeechCore, SpeechUI, AudioCommon.

Modelos

Vista compacta a continuación. Catálogo completo de modelos con tamaños, cuantizaciones, URLs de descarga y tablas de memoria → soniqo.audio/architecture.

Modelo Tarea Backends Tamaños Idiomas
Qwen3-ASR Voz → Texto MLX, CoreML (híbrido) 0.6B, 1.7B 52
Parakeet TDT Voz → Texto CoreML (ANE) 0.6B 25 europeos
Parakeet EOU Voz → Texto (streaming) CoreML (ANE) 120M 25 europeos
Nemotron Streaming Voz → Texto (streaming, con puntuación) CoreML (ANE) 0.6B Inglés
Omnilingual ASR Voz → Texto CoreML (ANE), MLX 300M / 1B / 3B / 7B 1.672
Qwen3-ForcedAligner Audio + Texto → Marcas temp. MLX, CoreML 0.6B Multi
Qwen3-TTS Texto → Voz MLX, CoreML 0.6B, 1.7B 10
CosyVoice3 Texto → Voz MLX 0.5B 9
Kokoro-82M Texto → Voz CoreML (ANE) 82M 10
VibeVoice Realtime-0.5B Texto → Voz (formato largo, múltiples hablantes) MLX 0.5B EN/ZH
VibeVoice 1.5B Texto → Voz (podcast de hasta 90 min) MLX 1.5B EN/ZH
Qwen3.5-Chat Texto → Texto (LLM) MLX, CoreML 0.8B Multi
MADLAD-400 Texto → Texto (Traducción) MLX 3B 400+
PersonaPlex Voz → Voz MLX 7B EN
Silero VAD Detección de actividad vocal MLX, CoreML 309K Agnóstico
Pyannote VAD + Diarización MLX 1.5M Agnóstico
Sortformer Diarización (E2E) CoreML (ANE) Agnóstico
DeepFilterNet3 Mejora de voz CoreML 2.1M Agnóstico
Open-Unmix Separación de fuentes MLX 8.6M Agnostic
WeSpeaker Embedding de hablante MLX, CoreML 6.6M Agnóstico

Instalación

Homebrew

Requiere Homebrew ARM nativo (/opt/homebrew). Homebrew Rosetta/x86_64 no está soportado.

brew tap soniqo/speech https://github.com/soniqo/speech-swift
brew install speech

Luego:

audio transcribe recording.wav
audio speak "Hello world"
audio translate "Hello, how are you?" --to es
audio respond --input question.wav --transcript
audio-server --port 8080            # servidor HTTP / WebSocket local (OpenAI-compatible /v1/realtime)

Referencia completa de la CLI →

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/soniqo/speech-swift", branch: "main")
]

Importa solo lo que necesites — cada modelo es su propio target SPM:

import Qwen3ASR             // Reconocimiento de voz (MLX)
import ParakeetASR          // Reconocimiento de voz (CoreML, batch)
import ParakeetStreamingASR // Dictado en streaming con parciales + EOU
import NemotronStreamingASR // ASR streaming en inglés con puntuación nativa (0.6B)
import OmnilingualASR       // 1.672 idiomas (CoreML + MLX)
import Qwen3TTS             // Síntesis de voz
import CosyVoiceTTS         // Síntesis de voz con clonación
import KokoroTTS            // Síntesis de voz (listo para iOS)
import VibeVoiceTTS         // TTS de formato largo / múltiples hablantes (EN/ZH)
import Qwen3Chat            // Chat LLM en el dispositivo
import MADLADTranslation    // Traducción multidireccional entre 400+ idiomas
import PersonaPlex          // Voz a voz full-duplex
import SpeechVAD            // VAD + diarización + embeddings
import SpeechEnhancement    // Supresión de ruido
import SourceSeparation     // Separación de fuentes musicales (Open-Unmix, 4 stems)
import SpeechUI             // Componentes SwiftUI para transcripciones en streaming
import AudioCommon          // Protocolos y utilidades compartidas

Requisitos

  • Swift 6+, Xcode 16+ (con Metal Toolchain)
  • macOS 15+ (Sequoia) o iOS 18+, Apple Silicon (M1/M2/M3/M4)

El mínimo de macOS 15 / iOS 18 proviene de MLState —— la API de estado persistente de ANE de Apple —— que los pipelines CoreML (Qwen3-ASR, Qwen3-Chat, Qwen3-TTS) usan para mantener las cachés KV residentes en el Neural Engine entre pasos de token.

Compilar desde el código fuente

git clone https://github.com/soniqo/speech-swift
cd speech-swift
make build

make build compila el paquete Swift y la librería de shaders MLX Metal. La librería Metal es necesaria para la inferencia en GPU — sin ella verás Failed to load the default metallib en tiempo de ejecución. make debug para builds de depuración, make test para la suite de pruebas.

Guía completa de compilación e instalación →

Aplicaciones de demostración

  • DictateDemo (documentación) — Dictado en streaming en la barra de menús de macOS con parciales en vivo, detección de fin de enunciado basada en VAD y copia con un clic. Se ejecuta como agent en segundo plano (Parakeet-EOU-120M + Silero VAD).
  • iOSEchoDemo — Demo de eco iOS (Parakeet ASR + Kokoro TTS). Dispositivo y simulador.
  • PersonaPlexDemo — Asistente de voz conversacional con entrada de micrófono, VAD y contexto multi-turno. macOS. RTF ~0.94 en M2 Max (más rápido que tiempo real).
  • SpeechDemo — Dictado y síntesis TTS en una interfaz de pestañas. macOS.

El README de cada demo tiene instrucciones de compilación.

Ejemplos de código

Los fragmentos siguientes muestran el camino mínimo para cada dominio. Cada sección enlaza a una guía completa en soniqo.audio con opciones de configuración, múltiples backends, patrones de streaming y recetas de CLI.

Voz a texto — guía completa →

import Qwen3ASR

let model = try await Qwen3ASRModel.fromPretrained()
let text = model.transcribe(audio: audioSamples, sampleRate: 16000)

Backends alternativos: Parakeet TDT (CoreML, 32× tiempo real), Omnilingual ASR (1.672 idiomas, CoreML o MLX), Dictado en streaming (parciales en vivo).

Alineación forzada — guía completa →

import Qwen3ASR

let aligner = try await Qwen3ForcedAligner.fromPretrained()
let aligned = aligner.align(
    audio: audioSamples,
    text: "Can you guarantee that the replacement part will be shipped tomorrow?",
    sampleRate: 24000
)
for word in aligned {
    print("[\(word.startTime)s - \(word.endTime)s] \(word.text)")
}

Texto a voz — guía completa →

import Qwen3TTS
import AudioCommon

let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesize(text: "Hello world", language: "english")
try WAVWriter.write(samples: audio, sampleRate: 24000, to: outputURL)

Motores TTS alternativos: CosyVoice3 (streaming + clonación + etiquetas de emoción), Kokoro-82M (listo para iOS, 54 voces), VibeVoice (podcast de formato largo / múltiples hablantes, EN/ZH), Clonación de voz.

Voz a voz — guía completa →

import PersonaPlex

let model = try await PersonaPlexModel.fromPretrained()
let responseAudio = model.respond(userAudio: userSamples)
// Salida Float32 mono 24 kHz lista para reproducir

Chat LLM — guía completa →

import Qwen3Chat

let chat = try await Qwen35MLXChat.fromPretrained()
chat.chat(messages: [(.user, "Explain MLX in one sentence")]) { token, isFinal in
    print(token, terminator: "")
}

Traducción — guía completa →

import MADLADTranslation

let translator = try await MADLADTranslator.fromPretrained()
let es = try translator.translate("Hello, how are you?", to: "es")
// → "Hola, ¿cómo estás?"

Detección de actividad vocal — guía completa →

import SpeechVAD

let vad = try await SileroVADModel.fromPretrained()
let segments = vad.detectSpeech(audio: samples, sampleRate: 16000)
for s in segments { print("\(s.startTime)s → \(s.endTime)s") }

Diarización de hablantes — guía completa →

import SpeechVAD

let diarizer = try await DiarizationPipeline.fromPretrained()
let segments = diarizer.diarize(audio: samples, sampleRate: 16000)
for s in segments { print("Speaker \(s.speakerId): \(s.startTime)s - \(s.endTime)s") }

Mejora de voz — guía completa →

import SpeechEnhancement

let denoiser = try await DeepFilterNet3Model.fromPretrained()
let clean = try denoiser.enhance(audio: noisySamples, sampleRate: 48000)

Pipeline de voz (ASR → LLM → TTS) — guía completa →

import SpeechCore

let pipeline = VoicePipeline(
    stt: parakeetASR,
    tts: qwen3TTS,
    vad: sileroVAD,
    config: .init(mode: .voicePipeline),
    onEvent: { event in print(event) }
)
pipeline.start()
pipeline.pushAudio(micSamples)

VoicePipeline es la máquina de estados de agent de voz en tiempo real (impulsada por speech-core) con detección de turnos basada en VAD, manejo de interrupciones y STT eager. Conecta cualquier SpeechRecognitionModel + SpeechGenerationModel + StreamingVADProvider.

Servidor API HTTP

audio-server --port 8080

Expone cada modelo a través de endpoints HTTP REST + WebSocket, incluyendo un WebSocket compatible con OpenAI Realtime API en /v1/realtime. Ver Sources/AudioServer/.

Arquitectura

speech-swift está dividido en un target SPM por modelo para que los consumidores solo paguen por lo que importan. La infraestructura compartida vive en AudioCommon (protocolos, E/S de audio, descargador de HuggingFace, SentencePieceModel) y MLXCommon (carga de pesos, helpers QuantizedLinear, helper de atención multi-head SDPA).

Diagrama completo de arquitectura con backends, tablas de memoria y mapa de módulos → soniqo.audio/architecture · Referencia de API → soniqo.audio/api · Benchmarks → soniqo.audio/benchmarks

Docs locales (repositorio):

Configuración de caché

Los pesos del modelo se descargan desde HuggingFace en el primer uso y se almacenan en ~/Library/Caches/qwen3-speech/. Puedes sobrescribir con QWEN3_CACHE_DIR (CLI) o cacheDir: (API Swift). Todos los puntos de entrada fromPretrained() aceptan offlineMode: true para omitir la red cuando los pesos ya están en caché.

Consulta docs/inference/cache-and-offline.md para los detalles completos, incluyendo rutas de contenedor iOS sandboxed.

Librería MLX Metal

Si ves Failed to load the default metallib en tiempo de ejecución, falta la librería de shaders Metal. Ejecuta make build o ./scripts/build_mlx_metallib.sh release después de un swift build manual. Si falta el Metal Toolchain, instálalo primero:

xcodebuild -downloadComponent MetalToolchain

Pruebas

make test                            # suite completa (unidad + E2E con descargas de modelos)
swift test --skip E2E                # solo unidad (seguro para CI, sin descargas)
swift test --filter Qwen3ASRTests    # módulo específico

Las clases de test E2E usan el prefijo E2E para que CI pueda filtrarlas con --skip E2E. Consulta CLAUDE.md para la convención completa de pruebas.

Contribuir

PRs bienvenidos — correcciones de bugs, integraciones de nuevos modelos, documentación. Fork, crea una rama de feature, make build && make test, abre un PR contra main.

Licencia

Apache 2.0