Strojový překlad · Anglický originál
API a protokoly
Původní Apple runtime · PocketTTS
PocketTTS přidává nativní syntézu CoreML do speech-swift. Všechny fáze jsou výchozí pouze pro CPU- provedení. Anglické Alba preset streams 1,920-výběrové rámy na 24 kHz (80 ms na snímek).
import PocketTTS
let model = try PocketTTSModel.fromBundle(URL(fileURLWithPath: "/path/to/bundle"))
try await model.generateStreaming(text: "Hello world.") { chunk in
await consume(chunk.samples)
}V AudioCommon Modul definuje modelové agnostické protokoly a sdílené typy. Jakýkoli vyhovující model lze pomocí těchto rozhraní vzájemně měnit.
Přehled protokolu
┌─────────────────────────────────────────────────────────┐
│ AudioCommon │
│ │
│ AudioChunk SpeechGenerationModel (TTS) │
│ AlignedWord SpeechRecognitionModel (STT) │
│ SpeechSegment ForcedAlignmentModel │
│ SpeechToSpeechModel │
│ VoiceActivityDetectionModel (VAD) │
│ TurnCompletionProvider (EOT) │
│ SpeakerEmbeddingModel │
│ SpeakerDiarizationModel │
│ SpeakerExtractionCapable │
└─────────────────────────────────────────────────────────┘Model pro rozpoznávání slov
Protokol pro modely slov a textů.
public protocol SpeechRecognitionModel: AnyObject {
var inputSampleRate: Int { get }
func transcribe(audio: [Float], sampleRate: Int, language: String?) -> String
func transcribeWithLanguage(audio: [Float], sampleRate: Int, language: String?) -> TranscriptionResult
}
Typy tvarování: Qwen3ASRModel, WhisperASRModel, ParakeetASRModel, ParakeetStreamingASRModel, OmnilingualASRModel (CoreML), OmnilingualASRMLXModel (MLX)
Model výroby mluvčích
Protokol pro modely text- to- speak.
public protocol SpeechGenerationModel: AnyObject {
var sampleRate: Int { get }
func generate(text: String, language: String?) async throws -> [Float]
func generateStream(text: String, language: String?) -> AsyncThrowingStream<AudioChunk, Error> // has default impl
}
generateStream() má defaultní implementaci, která se uzavírá generate() jako jeden kus. Modely s pravým streamingem (např. Qwen3-TTS) jej přepínají.
Typy tvarování: Qwen3TTSModel, CosyVoiceTTSModel, VoxCPM2TTSModel, KokoroTTSModel, IndexTTS2TTSModel
IndexTTS2TTSModel adds a reference-audio generate přetížení pro nulové klonování hlasu a použití IndexTTS2SynthesisOptions pro ovládání hlasitosti a pauzy.
Qwen3-TTS místní zatížení
fromLocal ověřuje požadované soubory a úplnost úlomku, pak odvozuje velikost modelu, kvantifikaci a architekturu od config.json. Volitelné configuration hodnota je pouze záloha pro vynechání metadat; špatné nebo protichůdné házení metadat Qwen3TTSLoadingError.invalidConfiguration před přidělením modelu.
public static func fromLocal(
modelDirectory: URL,
tokenizerDirectory: URL,
configuration: Qwen3TTSConfig? = nil,
wiredMemoryPolicy: Qwen3TTSWiredMemoryPolicy = .none,
progressHandler: ((Double, String) -> Void)? = nil
) throws -> Qwen3TTSModel
public static func fromPretrained(
modelId: String = Qwen3TTSModel.defaultModelId,
tokenizerModelId: String = "Qwen/Qwen3-TTS-Tokenizer-12Hz",
cacheDir: URL? = nil,
tokenizerCacheDir: URL? = nil,
offlineMode: Bool = false,
wiredMemoryPolicy: Qwen3TTSWiredMemoryPolicy = .pin(fraction: 0.9),
progressHandler: ((Double, String) -> Void)? = nil
) async throws -> Qwen3TTSModel
fromLocal selhání .none, takže to nemění proces- wide Metal wired- memory limit. fromPretrained udržuje .pin(fraction: 0.9) výchozí. Oba předškolené nakladače akceptují samostatný tokenizerCacheDir.
ForcedAlignmentModel
Protokol pro sladění časových razítek na úrovni slova.
public protocol ForcedAlignmentModel: AnyObject {
func align(audio: [Float], text: String, sampleRate: Int, language: String?) -> [AlignedWord]
}
Proslov ToSpeechModel
Protokol pro modely dialogu o slovech.
public protocol SpeechToSpeechModel: AnyObject {
var sampleRate: Int { get }
func respond(userAudio: [Float]) -> [Float]
func respondStream(userAudio: [Float]) -> AsyncThrowingStream<AudioChunk, Error>
}
Typy tvarování: PersonaPlexModel
HibikiTranslateModel
Překladač řeči. Neodpovídá SpeechToSpeechModel protože smlouva je odlišná: vstup a výstup jsou různé jazyky, délka výstupu je proměnlivá (EOS- drived, capped at max(tSrc * 5/2, tSrc + 20) kroky - zhruba 1.0-1.6 × doba trvání zdroje na klipech ve stylu FLEURS-) a vedle zvuku je paralelní text- token stream. Kompletní průvodce →
public final class HibikiTranslateModel {
public let cfg: HibikiConfig
public static var defaultModelId: String { "aufklarer/Hibiki-Zero-3B-MLX-4bit" }
public static var modelId8bit: String { "aufklarer/Hibiki-Zero-3B-MLX-8bit" }
public static func fromPretrained(
modelId: String = defaultModelId,
cacheDir: URL? = nil,
offlineMode: Bool = false,
progressHandler: ((Double, String) -> Void)? = nil
) async throws -> HibikiTranslateModel
public func warmUp()
public func translate(
sourceAudio: [Float],
sourceLanguage: HibikiSourceLanguage = .fr,
verbose: Bool = false
) -> (audio: [Float], textTokens: [Int32])
public func translateStream(
sourceAudio: [Float],
sourceLanguage: HibikiSourceLanguage = .fr,
streaming: HibikiStreamingConfig = .default,
verbose: Bool = false
) -> AsyncThrowingStream<AudioChunk, Error>
}
public enum HibikiSourceLanguage: String, CaseIterable, Sendable, Codable {
case fr, es, pt, de
}
Rychlost vzorkování: 24 kHz mono, vstup i výstup. Rychlost snímku: 12.5 Hz (Kodek Mimi). Zdrojové jazyky: FR / ES / PT / DE. Cílový jazyk: EN. Set HIBIKI_GREEDY=1 při zahájení procesu dekódování deterministického argmaxu (odpovídá kanárům CI).
VoiceActivityDetectionModel
Protokol pro detekci hlasové aktivity.
public protocol VoiceActivityDetectionModel: AnyObject {
var inputSampleRate: Int { get }
func detectSpeech(audio: [Float], sampleRate: Int) -> [SpeechSegment]
}
Provider TurnCompletionProvider
Protokol pro klasifikátory koncového kola konzultovaný StreamingVADProcessor na každé potvrzené VAD pauze. VAD slyší pouze ticho; model turn-dokončování poslouchá prosťáček celého projevu, takže pauza uprostřed věty udržuje agenta čekat, zatímco dokončená věta dostane okamžitou odpověď. Mapy na speech-core sc_turn_completion_vtable_tPodívej se na to. Smart Turn průvodce.
public protocol TurnCompletionProvider: AnyObject {
/// Probability in [0, 1] that the turn is complete, given the audio of the
/// turn so far (Smart Turn looks at the last 8 s).
func turnCompleteProbability(audio: [Float], sampleRate: Int) throws -> Float
}
Typy tvarování: SmartTurnModel (Smart Turn v3.2)
Reproduktor EmbeddingModel
Protokol pro extrakci z reproduktoru.
public protocol SpeakerEmbeddingModel: AnyObject {
var inputSampleRate: Int { get }
var embeddingDimension: Int { get }
func embed(audio: [Float], sampleRate: Int) -> [Float]
}
Typy tvarování: WeSpeakerModel
Reproduktor DiarizationModel
Protokol pro modely digitalizace reproduktorů, které přiřazují štítky reproduktorů k audio segmentům.
public protocol SpeakerDiarizationModel: AnyObject {
var inputSampleRate: Int { get }
func diarize(audio: [Float], sampleRate: Int) -> [DiarizedSegment]
}
Typy tvarování: DiarizationPipeline (Pyannote), SortformerDiarizer
SpeakerExtractionCapable
Rozšířený diarizační protokol pro motory, které podporují extrahování segmentů cílového reproduktoru pomocí referenčního vkládání. Ne všechny motory podporují toto (Sortformer je end- to- end a neprodukuje reproduktory vkládání).
public protocol SpeakerExtractionCapable: SpeakerDiarizationModel {
func extractSpeaker(audio: [Float], sampleRate: Int, targetEmbedding: [Float]) -> [SpeechSegment]
}
Typy tvarování: DiarizationPipeline (pouze Pyannote)
Sdílené typy
Audiochunk
public struct AudioChunk {
public let samples: [Float] // PCM samples
public let sampleRate: Int // Sample rate (e.g. 24000)
}
SpeechSegment
public struct SpeechSegment {
public let startTime: Float // Start time in seconds
public let endTime: Float // End time in seconds
}
AlignedWord
public struct AlignedWord {
public let text: String // The word
public let startTime: Float // Start time in seconds
public let endTime: Float // End time in seconds
}
DiarizedSegment
public struct DiarizedSegment {
public let startTime: Float // Start time in seconds
public let endTime: Float // End time in seconds
public let speakerId: Int // Speaker identifier (0-based)
}
DialogueSegment
Parsed segment multireprodukčního dialogu text s volitelným reproduktor a emoční tagy. Používá se s DialogueParser a DialogueSynthesizer pro syntézu dialogu CosyVoice3.
public struct DialogueSegment: Sendable, Equatable {
public let speaker: String? // Speaker identifier ("S1", "S2"), nil for untagged
public let emotion: String? // Emotion tag ("happy", "whispers"), nil if none
public let text: String // Cleaned text to synthesize
}
DialogueParser
Zobrazuje multireproduktor dialog text s inline reproduktor tagy ([S1]) a emoční tagy ((happy)).
public enum DialogueParser {
static func parse(_ text: String) -> [DialogueSegment]
static func emotionToInstruction(_ emotion: String) -> String
}
Budování emocí: happy/excited, sad, angry, whispers/whispering, laughs/laughing, calm, surprised, serious. Neznámé značky projít jako freeform instrukce.
DialogueSynthesizer
Orchestráty multisegmentové dialogové syntézy s per- reproduktorem hlasového klonování, tiché mezery, a crosfade.
public enum DialogueSynthesizer {
static func synthesize(
segments: [DialogueSegment],
speakerEmbeddings: [String: [Float]],
model: CosyVoiceTTSModel,
language: String,
config: DialogueSynthesisConfig,
verbose: Bool
) -> [Float]
}
DialogueSynthesisConfig
public struct DialogueSynthesisConfig: Sendable {
public var turnGapSeconds: Float // Default: 0.2
public var crossfadeSeconds: Float // Default: 0.0
public var defaultInstruction: String // Default: "You are a helpful assistant."
public var maxTokensPerSegment: Int // Default: 500
}
PipelineLLM
Protokol pro integraci jazykového modelu s hlasovými potrubím. Mosty LLM na VoicePipeline je ASR → LLM Plaza TTS průtok.
public protocol PipelineLLM: AnyObject {
func chat(messages: [(role: MessageRole, content: String)],
onToken: @escaping (String, Bool) -> Void)
func cancel()
}
Adaptér built- in: Qwen3PipelineLLM mosty Qwen35MLXChat k tomuto protokolu s token čištění, zrušení, a čeká akumulace frází.
AudioIO
Opakovatelný audio I / O manažer, který eliminuje AVAudioEngine boilerplate. Manipuluje s mikrofonem zachycení, přeskupování, přehrávání a měření úrovně zvuku.
let audio = AudioIO()
try audio.startMicrophone(targetSampleRate: 16000) { samples in
pipeline.pushAudio(samples)
}
audio.player.scheduleChunk(ttsOutput)
audio.stopMicrophone()
AudioIO zahrnuje a StreamingAudioPlayer pro výstup TTS a AudioRingBuffer pro trolejový bezpečný přenos zvuku mezi zachycovacím a inference závitem.
SystemAudioTap
Zachycuje systémovou výstupní mix - to, co Mac v současné době hraje - jako mono Float32 při zvolené vzorkovací rychlosti (Core Audio process taps, macOS 14.4+). Mononukleární globální klepnutí vylučuje současný proces ve výchozím nastavení, takže vlastní přehrávání aplikace se nikdy znovu nezachycuje a je zabaleno v soukromém agregovaném zařízení, které obsahuje pouze kohoutek.
let tap = SystemAudioTap()
try tap.start(targetSampleRate: 16000) { samples in
pipeline.pushAudio(samples)
}
tap.stop()
Zachycení přežije default- output- zařízení přepínače - vnitřní resampler je přestavěn, když se změní rychlost mixu. NSAudioCaptureUsageDescription. Pokud je nahrávací povolení odepřeno, lze stále vytvořit kohoutek, ale dodat pouze ticho: hodinky framesCaptured růst, zatímco nonSilentFrames zůstává 0 a selhává.
Časově ověřené nezávislé zdroje
Obě třídy zachycení odhalují časové zatížení. CapturedAudioChunk nese mononukleózu samples v sampleRate plus volitelný Mach hostTime pro první vstupní rámec. Vzhledem k tomu, že oba zdroje používají stejné hodiny pro hostitele, může aplikace ponechat svůj PCM oddělený a objednat odvozené události přepisu bez promíchání zvuku.
try tap.startTimestamped(targetSampleRate: 16000) { systemChunk in
systemPipeline.pushAudio(systemChunk.samples)
recordSystemTime(systemChunk.hostTime)
}
let microphone = AudioIO(enableAEC: true, enablePlayback: false)
try microphone.startMicrophoneTimestamped(targetSampleRate: 16000) { micChunk in
microphonePipeline.pushAudio(micChunk.samples)
recordMicrophoneTime(micChunk.hostTime)
}
Pro poslech - pouze zachycení, enableAEC: true aktivuje Apple Voice Processing před přečtením formátu mikrofonu a enablePlayback: false vynechá nevyužitý přehrávač. Na macOS se odebere přehrávání zařízení ze vzorků mikrofonu a místo toho se hlásí selhání spuštění AEC.
Inkrementální vstup a rozpoznávání souborů
CapturedAudioChunk také nese monotónní frameIndex a isFinal Značka. AudioFileLoader.stream produkuje tyto kousky na vyžádání s ohraničenou pamětí, přetvoří jednou s perzistentním stavu převodníku, a průměr všechny vstupní kanály ve výchozím nastavení. .first nebo .select([indices]) pokud je znám směrování kanálů.
StreamingRecognitionModel a StreamingRecognitionSession poskytnout jeden doplňkový modul pro konzervování API přes Parakeet Streaming a Nemotron Core ML / MLX:
let source = AudioFileLoader.stream(
url: inputURL,
options: AudioFileStreamOptions(targetSampleRate: 16_000,
chunkDuration: 0.32,
channelSelection: .mixAll))
let session = try model.makeStreamingSession(language: "en-US")
for try await chunk in source {
for update in try session.push(chunk) {
render(update.text, final: update.isFinal)
}
}
for update in try session.finish() {
render(update.text, final: true)
}
DesentencePieceModel
Sdílená protobuf čtečka pro SentencePile .model soubory, žije v AudioCommon. Každý modul, který potřebuje dekódovat kousky SentencePile (PersonaPlex, OmnilingualaSR, budoucí ASR / TTS porty) staví svůj vlastní dekodér na vrcholu tohoto jediného čtečky namísto re- implementace protobuf drátu formátu.
public struct SentencePieceModel: Sendable {
public struct Piece: Sendable, Equatable {
public let text: String
public let score: Float
public let type: Int32
public var pieceType: PieceType? { get }
public var isControlOrUnknown: Bool { get }
}
public enum PieceType: Int32 {
case normal = 1, unknown = 2, control = 3,
userDefined = 4, unused = 5, byte = 6
}
public let pieces: [Piece]
public var count: Int { get }
public subscript(_ id: Int) -> Piece? { get }
public init(contentsOf url: URL) throws
public init(modelPath: String) throws
public init(data: Data) throws
}
Používá se: OmnilingualASR.OmnilingualVocabulary, PersonaPlex.SentencePieceDecoder. Pokryto jednotkovými testy 7 v Tests/AudioCommonTests/SentencePieceModelTests.
MLXCommon.SDPA
Vystupňované dot- produktové pomocníky sdílené v každém modulu pozornosti MLX (Qwen3-ASR / Qwen3-TTS _ Qwen3-Chat-CosyVoice, PersonaPlex a OmnilingualaSR). Každý modul si nechává své vlastní projekce - SDPA zpracovává pouze reshape → pozornost → sloučení boilerplate.
public enum SDPA {
// Flat [B, T, H*D] input: project/reshape happens inside
public static func multiHead(
q: MLXArray, k: MLXArray, v: MLXArray,
numHeads: Int, headDim: Int, scale: Float,
mask: MLXArray? = nil
) -> MLXArray
// GQA / MQA variant with separate query and KV head counts
public static func multiHead(
q: MLXArray, k: MLXArray, v: MLXArray,
numQueryHeads: Int, numKVHeads: Int, headDim: Int, scale: Float,
mask: MLXArray? = nil
) -> MLXArray
// Already-shaped [B, H, T, D] (RoPE / KV cache paths)
public static func attendAndMerge(
qHeads: MLXArray, kHeads: MLXArray, vHeads: MLXArray,
scale: Float,
mask: MLXArray? = nil
) -> MLXArray
// Same, with ScaledDotProductAttentionMaskMode enum (newer API)
public static func attendAndMerge(
qHeads: MLXArray, kHeads: MLXArray, vHeads: MLXArray,
scale: Float,
mask: MLXFast.ScaledDotProductAttentionMaskMode
) -> MLXArray
// Low-level head merge: [B, H, T, D] → [B, T, H*D]
public static func mergeHeads(_ attn: MLXArray) -> MLXArray
}
Všechny reshape hovory použít -1 pro dávkový rozměr, takže pomocníky komponovat s MLX.compile(shapeless:) grafy, které se v běhu mění (např. automaticky regresivní dekódování Qwen3-TTS Talker).
HTTP API Server
V speech-server binárně zobrazuje každý model v speech-swift jako HTTP REST koncové body plus a WebSocket endpoint, který implementuje OpenAI Realtime API. Modely jsou načteny lenivě na první žádost; projít --preload aby je všechny zahřáli při startu.
swift build -c release
.build/release/speech-server --port 8080
# Preload every model at startup
.build/release/speech-server --port 8080 --preload
REST Konečné body
| Koncový bod | Metoda | Žádost | Odpověď |
|---|---|---|---|
/transcribe | POST | audio/wav tělo | JSON { text } (Qwen3-ASR) |
/v1/audio/transcriptions | POST | Vícesložkové { file, model, response_format?, language?, prompt?, temperature? } | JSON, text, verbose JSON nebo SRT nebo VTT |
/v1/audio/speech | POST | JSON { model, input, voice, response_format?, speed? } | audio/wav nebo audio/pcm tělo |
/speak | POST | JSON { text, engine?, language?, voice? } | audio/wav tělo (Qwen3-TTS, CosyVoice a Kokoro) |
/respond | POST | audio/wav tělo | audio/wav tělo (PersonaPlex) |
/enhance | POST | audio/wav tělo | audio/wav tělo (DeepFilterNet3) |
/vad | POST | audio/wav tělo | Seznam segmentů JSON |
/diarize | POST | audio/wav tělo | JSON DiarizedSegment seznam |
/embed-speaker | POST | audio/wav tělo | JSON [Float] (256- dim) |
/v1/audio/speech standardní mapy TTS model a hlasová jména lokálních motorů. WAV; response_format: "pcm" vrací bezhlavé 24 kHz mono PCM16 littleendian audio. Nepodporované komprimované formáty jsou výslovně zamítnuty.
/v1/audio/transcriptions přijímá jeden WAV soubor jako multipart forma dat a selhání {"text":"..."}; response_format může také požádat o text, verbose JSON, SRT, nebo VTT. Na Apple, model aliasy vybrat místní ASR motor. Balený speech-core zapnutý server Linux a Windows používá lazivě naložené Parakeet, přijímá PCM16/24/32 nebo Float32 WAV, a uploads uploady 25 MiB a 10 Minuty.
# Transcribe a file
curl -X POST http://localhost:8080/transcribe \
--data-binary @recording.wav \
-H "Content-Type: audio/wav"
# Synthesize speech
curl -X POST http://localhost:8080/speak \
-H "Content-Type: application/json" \
-d '{"text": "Hello world", "engine": "cosyvoice"}' \
-o output.wav
# OpenAI-compatible transcription
curl http://localhost:8080/v1/audio/transcriptions \
-F "[email protected];type=audio/wav" \
-F "model=whisper-1"
# OpenAI-compatible speech synthesis
curl http://localhost:8080/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","voice":"alloy","input":"Hello world","response_format":"wav"}' \
-o openai-output.wav
# Full speech-to-speech round trip
curl -X POST http://localhost:8080/respond \
--data-binary @question.wav \
-o response.wav
OpenAI Realtime API (/v1/realtime)
Koncentrace WebSocket v ws://host:port/v1/realtime implementuje protokol OpenAI Realtime. Všechny zprávy jsou JSON s type Diskriminator; audio užitečné zatížení jsou base64-encoded PCM16 na 24 kHz mono.
Při zatížení studeného modelu nebo dlouhých generací server vysílá lehké realtime.keepalive Události JSON a ovládací rámy Websocket pong zhruba každé 15 sekundy, dokud nebude model připraven. Klienti mohou tyto události ignorovat nebo je považovat za ukazatele aktivity.
Klient → Události serveru
| Událost | Účel |
|---|---|
session.update | Nastavit motor, jazyk, hlas, audio formát a volitelný server VAD |
input_audio_buffer.append | Připojit úlomek base64 PCM16 na vstupní buffer |
input_audio_buffer.commit | Odeslat buffered audio pro přepis |
input_audio_buffer.clear | Zlikvidujte aktuální vstupní buffer |
response.create | Žádost o syntézu TTS pro dodaný text / instrukce |
Server → Události klienta
| Událost | Význam |
|---|---|
session.created | Ruční stisk kompletní, výchozí vyzařovaný konfigurační |
session.updated | Nejnovější session.update potvrzen |
input_audio_buffer.speech_started | Server VAD detekoval začátek otáčení řeči |
input_audio_buffer.speech_stopped | Server VAD ukončil zatáčku po tichu nebo limitu trvání |
input_audio_buffer.committed | Audio akceptováno a fronta pro transkripci |
conversation.item.input_audio_transcription.completed | ASR výsledek s konečným přepisem textu |
response.audio.delta | Base64 PCM16 kus syntetizovaného zvuku |
response.audio.done | Už žádné audio kousky pro tuto odpověď. |
response.done | Konečná odpověď (metadata + statistiky latence) |
error | Chyba v obálce s type a message |
Automatické otáčky se serverem VAD
Explicitní input_audio_buffer.commit Pokud chcete server nechat zavřít a přepsat automaticky se Silero VAD, nastavte turn_detection. Server udržuje pouze požadovaný předroll při volnoběhu, vysílá spuštění / zastavení řeči a odevzdání událostí v pořadí, a síla - uzavírá nepřetržitě aktivní otáčky na max_turn_duration_ms.
ws.send(JSON.stringify({
type: 'session.update',
session: {
turn_detection: {
type: 'server_vad',
threshold: 0.5,
prefix_padding_ms: 300,
silence_duration_ms: 500,
max_turn_duration_ms: 120000
}
}
}));
// Disable automatic turns and return to explicit commits:
// { type: 'session.update', session: { turn_detection: null } }
const ws = new WebSocket('ws://localhost:8080/v1/realtime');
// ASR: push audio, request transcription
ws.send(JSON.stringify({ type: 'input_audio_buffer.append', audio: base64PCM16 }));
ws.send(JSON.stringify({ type: 'input_audio_buffer.commit' }));
// → conversation.item.input_audio_transcription.completed
// TTS: request synthesis and stream audio deltas
ws.send(JSON.stringify({
type: 'response.create',
response: { modalities: ['audio', 'text'], instructions: 'Hello world' }
}));
// → response.audio.delta (repeated), response.audio.done, response.done
Server žije v AudioServer SPM produkt. Příklad prohlížeč klient je dodáván na Examples/websocket-client.html - jej otevřete vedle běžícího serveru pro jízdu plnou ASR + TTS kulatou cestu.
Stáhnout model
Všechny modely jsou staženy z HuggingFace při prvním použití a cachovány v ~/Library/Caches/qwen3-speech/. AudioCommon modul poskytuje sdílené HuggingFaceDownloader který zpracovává kontrolu download, caching a integrity.
Stáhnout retry
Modelové nakladače a stahování API akceptovat downloadOptions: DownloadOptions. onRetry obdrží DownloadRetryEvent před každým vzad, s modelId, attempt, maxAttempts, originál error, a retryInSeconds. nil znamená, že žádné opakování nezůstalo; kompletní cache může stále uspět.
Callbacky jsou synchronní a @Sendable, Bez záruky hlavního herce. Zrušení neprodukuje žádnou retry událost. Set retryDelaysSeconds přizpůsobit politiku ([] znamená jeden pokus); explicitní stahování argument API má přednost. Výchozí počet pokusů a byte pokrok jsou zachovány. Neúspěšné pokusy jsou také zaznamenány na úrovni oznámení prostřednictvím AudioLog.download.
Zrušení spolupráce
Pro přepis MLX, Qwen3ASRModel.transcribeCheckingCancellation(audio:sampleRate:options:) házení CancellationError při zrušení úkolu je pozorován před funkce extrakce, kódování, dekodér předvyplnění, nebo dekódování krok. GPU práce již odevzdána může skončit. Synchronní transcribe a transcribeBatch API si zachovají své non-házení chování a ignorovat zrušení úkolu. speech-server používá vstupní bod pro Qwen3-ASR.
MOSS- TTS- Nano
MossTTSNanoModel serializuje žádosti a vytváří čerstvé CoreML stav pro každé utce. syntetizovat a syntetizeStream zachovat stereo; SpeechGenerationModel vrací mono downmix. Pass jazyk: nula. Streams konec s prázdnou finální marker.
import AudioCommon
import MossTTSNano
let model = try await MossTTSNanoModel.fromPretrained()
let reference = try await model.encodeReference(url: referenceURL)
let result = try await model.synthesize(
text: "The weather is nice today.", reference: reference,
options: MossTTSNanoOptions(maxFrames: 300, seed: 1))
try WAVWriter.writeStereo(left: result.left, right: result.right,
sampleRate: 48000, to: outputURL)Místní rodný odvod
Pro 12B jsou s tímto voláním konzumovány opakované přilehlé uzavírací značky tool- call bezprostředně po úplném, ověřeném volání. Nevytvářejí další volání ani nemění své argumenty. Samostatné markery a vadné argumenty zůstávají chybami; E4B si zachovává přísné zacházení.
Chcete-li zachovat nezdařené rodné žádosti chatu, nastavte failureCaptureDirectory v inference manifestu. Zachycení je vypnuto ve výchozím nastavení. Adresář musí odepřít skupinový a jiný přístup; nové adresáře používají 0700 a použití souborů selhání 0600. Soubory zahrnují přesné tělo požadavku, ID požadavku a chybu. Dokončené generace 12B si také zachovají proklamované okamžité, token ID, surový výstup a konec důvod před rozchodem. E4B selhání nemají žádný záznam o surové generaci. Tyto lokální soubory obsahují obsah konverzace a nástroje; HTTP chyby a úspěšné požadavky je nezahrnují.
Gemma4UnifiedChat.generateResponse přijímá nepovinné onGeneration Zavolat zpět s Gemma4GenerationTrace. Zpětný hovor běží před rozdělením nástroje, včetně případů, kdy je toto dokončení zamítnuto. Nahrávání nemění výběr vzorku nebo přijatou syntaxi nástroje.
Volitelné agentDirectory načítá zinscenované Gemma 4 12B Jednotná INT4 vedle textového modelu E4B Model: ".gemma4-12b-it"pro chat s nativní nástroje, historie výsledků, a zrušení. E4B zůstává k dispozici pro další žádosti. Gemma4UnifiedChat používá přišpendlené MLX implementace textů a uvolní každý požadavek KV cache. Response parsing odstraňuje oddělovače kanálů z vráceného textu. Opuštěné tool- call terminátory jsou odmítnuty jako vadné odpovědi. Pro Gemma 4 12B, obsah tool- message uchovává JSON jako text, odpovídající šabloně checkpoint chat.
Zdroj buduje akceptovat --inference-manifest models.json nahrát zinscenované Gemma 4 E4B INT4, VoxCPM2, plné Whisper Large-v3, a Silero VAD. Tato služba začíná off-line a odmítá neslučitelné identity modelu.
VoxCPM2 podporuje váhový Přesnost: .float16 pro nekvantifikovaný kontrolní bod. Jeho hlavní parametry používají FP16; AudiovAE a některé výpočty zůstávají FP32. INT8 kontrolní body jsou pro tento režim zamítnuty.
Gemma4ConversationMessage, Gemma4Tool a generativní odpověď zachovejte původní prohlášení o nástrojích, argumenty a historii výsledků. InferenceCancellationToken ruší výrobu cooperativně. Silero makementStream izoluje stav a proces ChunkThrowing šíří inference chyby.
Whisper ASRModel.From Directory zatížení zinscenoval plné Large-v3 CoreML svazků. 32- dekodér může vytvořit svou předčíslí bez TextDecoderContextPrefill.mlmodelc a uvolní per- step temporaries. Výchozí Whisper CLI model zůstává Turbo.
Nativní služba serializuje chat, syntézu a transkripci; lehký VAD má samostatný stav proudu. Gemma Předplněná použití 256-token dávky, požadavek KV stav je propuštěn, a MLX cache je omezena na 128 MiB s 20 GiB limit pro alokátor.
Proslov s proudem: pravda vrací NDJSON audio události na 48 kHz, následováno dokončením nebo chybou. Zrušit requestID na /v1/ odvod / zrušit. GET /v1/ inference / schopnosti a /v1/ inference / provenance report model identity, parametr dtypes, spony aktiv, a paměťové čítače.
speech-server --inference-manifest models.json --port 8080POST /v1/inference
{"operation":"chat","model":"gemma4-e4b-it","requestID":"chat-1","messages":[{"role":"user","content":"Hello"}],"maxTokens":128,"temperature":0}
chat: gemma4-e4b-it
speech: voxcpm2
transcribe: whisper-large-v3
vad: silero-vad
Audio: audioBase64 (mono PCM16), sampleRate
VAD: streamID, reset, endStream; 512 samples/frame at 16000 Hz
Tools: name, description, parameters
Tool call: id, name, arguments
Tool result: role="tool", toolCallID, contentNepovinné vlastnictví modelu
SpeechRuntime poskytuje sdílené zatížení, zrušitelné FIFO přijetí a serializovaný výkon. Qwen3ASRRuntime vybere MLX nebo plný CoreML prostřednictvím explicitních deskriptorů; Nativní API a CLI vlajky zůstávají k dispozici.
Audio server má jedno povolení k provedení přes každý kompletní požadavek, včetně streamovaného výstupu, s až 32 požadavky čekání. ASR selhání produkují HTTP chyby nebo WebSocket chybové události; platné prázdné přepisy zůstávají úspěšné. Přehrávání uchovává přetečení vzorků. Cached modely a čeká přehrávání audio stále zabírá paměť.
import Qwen3ASRRuntime
let runtime = Qwen3ASRRuntime(descriptor: Qwen3ASRDescriptors.small)
let text = try await runtime.transcribe(audio: samples, sampleRate: 16000)
try await runtime.unload()Voxtral TTS
Voxtral 4B TTS 2603 běží lokálně na Apple Silicon přes modul Voxtral TTS: 20 přednastavené hlasy, devět jazyků a 24 KHz mono řeč.
import VoxtralTTS
let model = try await VoxtralTTSModel.fromPretrained()
for try await chunk in model.generateStream(text: "Hello, welcome to Voxtral.", voice: "casual_male") {
print(chunk.frameIndex, chunk.samples.count, chunk.isFinal)
}Pouze přednastavené hlasy; reference-audio klonování je nedostupné. Audio výstupy proudí přes SDK, CLI, WebSocket a HTTP PCM. Dodávejte kompletní vstupní text vpředu. Modelové váhy používají CC BY-NC 4.0.
import AudioCommon
let player = StreamingAudioPlayer()
player.skipInitialSilence = false