Konekäännös · Englanninkielinen alkuperäinen
API ja protokollat
Native Applen ajoaika · PocketTTS
PocketTTS lisää natiivin CoreML puheen synteesin speech-swift. Kaikki vaiheet oletuksena CPU-yksinomaan suoritukseen. Englanti Alba esiasetetut virrat 1,920-0 näytekehys 24 kHz (80 ms per runko).
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)
}AudioCommon -moduuli määrittelee mallin agnostiset protokollia ja jaettuja tyyppejä. Mitä tahansa vastaavaa mallia voidaan käyttää näiden rajapintojen kautta.
Protokollan yleiskatsaus
┌─────────────────────────────────────────────────────────┐
│ AudioCommon │
│ │
│ AudioChunk SpeechGenerationModel (TTS) │
│ AlignedWord SpeechRecognitionModel (STT) │
│ SpeechSegment ForcedAlignmentModel │
│ SpeechToSpeechModel │
│ VoiceActivityDetectionModel (VAD) │
│ TurnCompletionProvider (EOT) │
│ SpeakerEmbeddingModel │
│ SpeakerDiarizationModel │
│ SpeakerExtractionCapable │
└─────────────────────────────────────────────────────────┘Puheentunnistusmalli
Protokolla puhe-to-text malleja.
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
}
Muodostustyypit: Qwen3ASRModel, WhisperASRModel, ParakeetASRModel, ParakeetStreamingASRModel, OmnilingualASRModel (CoreML), OmnilingualASRMLXModel (MLX)
PuheGenerationMalli
Protokolla teksti-puhe-malleja varten.
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() on oletustoteutus, joka käärii generate() Mallit, joissa on todellinen streaming (esim. Qwen3-TTS) ohittavat sen.
Muodostustyypit: Qwen3TTSModel, CosyVoiceTTSModel, VoxCPM2TTSModel, KokoroTTSModel, IndexTTS2TTSModel
IndexTTS2TTSModel lisää viite-audio generate ylikuormituksen äänikloonauksessa ja käyttää IndexTTS2SynthesisOptions puhe- ja taukokontrolleissa.
Qwen3-TTS paikallinen kuormitus
fromLocal validoi vaaditut tiedostot ja tarkistuspisteen sirpale täydellisyys, sitten saadaan mallin koko, kvantitalisaatio ja arkkitehtuuri config.json. Valinnainen configuration on vain varaisku pois jätettyihin metatietoihin; epämuodostuneet tai ristiriitaiset metadataheitot Qwen3TTSLoadingError.invalidConfiguration ennen mallin jakamista.
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 oletusarvolta .none, joten se ei muuta prosessinlaajuista Metal langallinen muistiraja. fromPretrained pitää .pin(fraction: 0.9) oletus. Molemmat ennalta koulutetut kuormaajat hyväksyvät erillisen tokenizerCacheDir.
Pakotettu työharjoittelumalli
Sanatason aikaleiman yhdenmukaistaminen.
public protocol ForcedAlignmentModel: AnyObject {
func align(audio: [Float], text: String, sampleRate: Int, language: String?) -> [AlignedWord]
}
PuheToSpeechModel
Pöytäkirja puhe-puhe-dialogimalleja varten.
public protocol SpeechToSpeechModel: AnyObject {
var sampleRate: Int { get }
func respond(userAudio: [Float]) -> [Float]
func respondStream(userAudio: [Float]) -> AsyncThrowingStream<AudioChunk, Error>
}
Yhdistämistyypit: PersonaPlexModel
HibikiKäännäModel
Puhe-puhe-käännös-ajuri. Ei vastaa SpeechToSpeechModel koska sopimus on erilainen: tulo ja lähtö ovat eri kieliä, ulostulon pituus on vaihteleva (EOS-ohjattu, yläraja max(tSrc * 5/2, tSrc + 20) vaiheet ... noin 1,0...1,6× lähdekesto FLEURS-tyylisillä leikkeillä) ja samanaikaisesti on teksti-token stream vieressä. Täydellinen opas →
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
}
Näytteenottonopeus: 24:n mono, sekä sisään- että ulostulo. Kehysnopeus: 12.5 Hz (Mimi-koodekki). Lähdekielet: FR / ES / PT / DE. Kohdekieli: FI. HIBIKI_GREEDY=1 prosessin käynnistämisessä determinististä argmax-purkua varten (vastaa CI:n kanarialintuja).
Ääniaktiviteetintunnistusmalli
-Pöytäkirja äänentunnistusta varten.
public protocol VoiceActivityDetectionModel: AnyObject {
var inputSampleRate: Int { get }
func detectSpeech(audio: [Float], sampleRate: Int) -> [SpeechSegment]
}
TurnCentlementProvider
Kääntyvien luokkien luokittajia koskeva pöytäkirja, jota ovat kuulleet StreamingVADProcessor jokaisesta vahvistetusta VAD Pysähdy. VAD Vain kuulee hiljaisuuden; kääntymis-täydennys malli kuuntelee prosodia koko lause, joten puolivälissä-lause tauko pitää agentti odottaa, kun valmis lause saa välittömän vastauksen. Kartat speech-core's sc_turn_completion_vtable_t. Katso Smart Turn-opas.
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
}
Yhdistämistyypit: SmartTurnModel (Smart Turn v3.2)
PuhemiesEmbeddingMalli
Protokolla kaiuttimeen.
public protocol SpeakerEmbeddingModel: AnyObject {
var inputSampleRate: Int { get }
var embeddingDimension: Int { get }
func embed(audio: [Float], sampleRate: Int) -> [Float]
}
Muodostustyypit: WeSpeakerModel
KaiutinDiarizationMalli
Protokolla kaiuttimen diarisointi malleja, jotka antavat kaiutin tarroja audio-segmenteille.
public protocol SpeakerDiarizationModel: AnyObject {
var inputSampleRate: Int { get }
func diarize(audio: [Float], sampleRate: Int) -> [DiarizedSegment]
}
Yhdistämistyypit: DiarizationPipeline (Pyannote), SortformerDiarizer
KaiutinExtractionCapable
Laajennettu diarisointiprotokolla moottoreille, jotka tukevat kohdekaiuttimen segmenttien poistoa käyttäen referenssipohjaa. Kaikki moottorit eivät tue tätä (Sortformer on pää-päästä-päähän eikä tuota kaiuttimen upotuksia).
public protocol SpeakerExtractionCapable: SpeakerDiarizationModel {
func extractSpeaker(audio: [Float], sampleRate: Int, targetEmbedding: [Float]) -> [SpeechSegment]
}
Yhdistystyypit: DiarizationPipeline (Pyannote ainoastaan)
Jaetut tyypit
AudioChunkName
public struct AudioChunk {
public let samples: [Float] // PCM samples
public let sampleRate: Int // Sample rate (e.g. 24000)
}
Puhe
public struct SpeechSegment {
public let startTime: Float // Start time in seconds
public let endTime: Float // End time in seconds
}
Tasattuword
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)
}
Vuoropuhelu
Monikaiuttimen dialogitekstin osa, jossa on valinnaisia puhuja- ja tunnetunnisteita. Käytetään DialogueParser ja DialogueSynthesizer CosyVoice3 dialogin synteesiä.
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
Parses multi-kaiuttimet dialogi tekstiä inline kaiuttimen tunnisteet ([S1]) ja emotion tagit ((happy)).
public enum DialogueParser {
static func parse(_ text: String) -> [DialogueSegment]
static func emotionToInstruction(_ emotion: String) -> String
}
Sisäänrakennetut tunteet happy/excited, sad, angry, whispers/whispering, laughs/laughing, calm, surprised, serious. Tuntematon tagit kulkevat vapaamuotoisena ohjeena.
VuoropuheluYhteystiedot
Orkesterissa on monen segmentin vuoropuhelusynteesiä, joka koostuu puheäänen kloonauksesta, hiljaisuuden aukoista ja ristiinnaulitsemisesta.
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
Sillat LLM puhelimen ASR → LLM →TTS
public protocol PipelineLLM: AnyObject {
func chat(messages: [(role: MessageRole, content: String)],
onToken: @escaping (String, Bool) -> Void)
func cancel()
}
Rakennettu adapteri: Qwen3PipelineLLM sillat Qwen35MLXChat tähän protokollaan kuuluva kuponkipesu, peruutus ja odottava lauseiden kertyminen.
Audio
Uudelleenkäytettävä audio I/O manager, joka poistaa AVaudioEngine kattilalevy. Kahvat mikki kaappaus, resampling, toisto, ja äänitason mittaus.
let audio = AudioIO()
try audio.startMicrophone(targetSampleRate: 16000) { samples in
pipeline.pushAudio(samples)
}
audio.player.scheduleChunk(ttsOutput)
audio.stopMicrophone()
AudioIO sisältää StreamingAudioPlayer TTS -lähdön ja AudioRingBuffer -äänensiirron nauhan ja päättelysäikeiden välillä.
SystemAudioTap
Kaappaa järjestelmän tulostusyhdistelmän ... mitä Mac tällä hetkellä pelaa - mono Float32:nä soittamalla soittajan valitsemalla näytteenottonopeudella (Core Audio process hanat, macOS 14.4+). Monoglobal hana sulkee pois nykyisen prosessin oletusarvoisesti, joten sovelluksen omaa toistoa ei koskaan kaapata uudelleen, ja se on kääritty yksityiseen kokoamislaitteeseen, joka sisältää vain hanan.
let tap = SystemAudioTap()
try tap.start(targetSampleRate: 16000) { samples in
pipeline.pushAudio(samples)
}
tap.stop()
Capture selviytyy oletus-tuotos-laite kytkimet ... sisäinen uudelleennäytin rakennetaan uudelleen, kun sekoitusnopeus muuttuu. NSAudioCaptureUsageDescription. Jos nauhoituslupa evätään, voidaan vielä luoda napa, mutta toimittaa vain hiljaisuus: katsella framesCaptured kasvaa kun taas nonSilentFrames pysyy 0 ja epäonnistuu.
Aikaleimatut riippumattomat lähteet
Molemmat talteenottoluokat paljastavat aikaleimatut ylikuormitusluvut. CapturedAudioChunk kuljettaa mono samples at sampleRate plus valinnaisen Mach hostTime ensimmäisen tulokelloa varten. Koska molemmat lähteet käyttävät samaa isäntäkelloa, sovellus voi pitää PCM:nsä erillään ja tilaustuotetut transkriptiotapahtumat sekoittamatta audiota.
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)
}
Kuuntele vain tallennus, enableAEC: true aktivoi Apple Voice Processing ennen mikrofoniformaatin lukemista ja enablePlayback: false jättää käyttämättä käyttämättömän pelaajan. macOS:ssa laitteen toisto poistetaan mikrofonin näytteistä ja ilmoitetaan AEC:n käynnistymisen epäonnistuminen sen sijaan, että palattaisiin raakaan syötteeseen.
Incremental tiedoston syöte ja tunnistaminen
CapturedAudioChunk sisältää myös monotonin frameIndex ja isFinal markkeri. AudioFileLoader.stream tuottaa nämä palaset pyynnöstä, jolloin muistia rajoitetaan, otetaan uudelleennäytteitä kerran, jossa on jatkuva muunnintila, ja keskiarvot kaikki tulokanavat oletusarvoisesti.Käytä .first tai .select([indices]), kun kanavareitit tunnetaan.
StreamingRecognitionModel ja StreamingRecognitionSession tarjoavat yhden välimuistia ylläpitävän invertiumin API koko Parakeet Streaming and 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)
}
TuomioPieceModel
SentencePiece .model -tiedostojen jakaja, asuu AudioCommon. Jokainen moduuli, joka tarvitsee koodinpurkajaa Sentence Piece-kappaleet (PersonaPlex, OmnilingualASR, future ASR / TTS portit), rakentaa oman dekooderinsa tämän yksittäisen lukijan päälle sen sijaan, että se käyttäisi uudelleen protobuf-lankamuotoa.
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
}
Käyttäjä: OmnilingualASR.OmnilingualVocabulary, PersonaPlex.SentencePieceDecoder. Kattaa 7 yksikön testit Tests/AudioCommonTests/SentencePieceModelTests.
MLXCommon. SDPA
Skaalattu piste-tuote-auttajat jaettu MLX Huomiomoduuli (Qwen3-ASR / Qwen3-TTS / Qwen3-Chat / CosyVoice / PersonaPlex / OmnilingualASR). Jokainen moduuli pitää omat ennusteet ... SDPA käsittelee vain uudelleenmuotoilua → huomiota → sulauttamalla kattilalevy.
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
}
Kaikki muokkautumispuhelut käyttävät -1 erän mittojen osalta, joten apulaitteet säveltävät MLX.compile(shapeless:) kaaviot, jotka vaihtelevat erän runtimella (esim. Qwen3-TTS Talker autoregressiivinen decode).
HTTP API Server
speech-server binääripaljastaa kaikki speech-swift HTTP REST -päätetapahtumat sekä WebSocket -päätteen, joka toteuttaa OpenAI Reaaliaikainen API. Mallit lastataan laiskasti ensimmäisestä pyynnöstä; läpäisee --preload -lämpenee ne kaikki käynnistettäessä.
swift build -c release
.build/release/speech-server --port 8080
# Preload every model at startup
.build/release/speech-server --port 8080 --preload
REST päätepisteet
| Päätetapahtuma | Menetelmä | Pyyntö | Vaste |
|---|---|---|---|
/transcribe | POST | audio/wav runko | JSON { text } (Qwen3-ASR) |
/v1/audio/transcriptions | POST | Moniosainen { file, model, response_format?, language?, prompt?, temperature? } | JSON, teksti, verbose JSON, SRT tai VTT |
/v1/audio/speech | POST | JSON { model, input, voice, response_format?, speed? } | audio/wav tai audio/pcm runko |
/speak | POST | JSON { text, engine?, language?, voice? } | audio/wav runko (Qwen3-TTS, CosyVoice ja Kokoro) |
/respond | POST | audio/wav runko | audio/wav keho (PersonaPlex) |
/enhance | POST | audio/wav runko | audio/wav runko (DeepFilterNet3) |
/vad | POST | audio/wav runko | JSON-segmenttiluettelo |
/diarize | POST | audio/wav runko | JSON DiarizedSegment list |
/embed-speaker | POST | audio/wav runko | JSON [Float] (256-dim) |
/v1/audio/speech kartat standardi TTS malli- ja ääninimet paikallisille moottoreille. Sen oletusarvo on WAV; response_format: "pcm" palauttaa otsikottoman 24 kHz mono PCM16 pienen endiaaniäänen.
/v1/audio/transcriptions hyväksyy yhden WAV-tiedoston moniosaisena muototietona ja oletusarvoina {"text":"..."}; response_format voi myös pyytää tekstiä, verbose JSON, SRT, tai VTT. Apple, malli peitenimet valita paikallisen ASR moottori. Pakattu speech-core palvelin päällä Linux sekä Windows käyttää laiskasti ladattua Parakeet, hyväksyy PCM16/24/32 tai Float32 WAV, ja caps lataukset klo 25 MiB ja 10 minuuttia.
# 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 Reaaliaikainen API (/v1/realtime)
WebSocket päätepiste ws://host:port/v1/realtime toteuttaa OpenAI Reaaliaikainen protokolla. Kaikki viestit ovat JSON, jonka tyypillä type diskriminator; audio hyötykuormat ovat base64-encoded PCM16 24 kHz monossa.
Kylmän mallin kuormien tai pitkien sukupolvien aikana palvelin lähettää kevyitä realtime.keepalive JSON-tapahtumia ja websocket-ping-ohjauskehyksiä noin joka 15. sekunti, kunnes mallilähtö on valmis. Asiakkaat voivat jättää nämä tapahtumat huomiotta tai käsitellä niitä toimintoindikaattoreina.
Asiakas → Palvelintapahtumat
| Tapahtuma | Aihe |
|---|---|
session.update | Määrittele moottori, kieli, ääni, äänimuoto ja valinnainen palvelin VAD |
input_audio_buffer.append | Lisätään base64 PCM16 pala syötepuskuriin |
input_audio_buffer.commit | Toimita puskuroitu ääni transkriptiota varten |
input_audio_buffer.clear | Hävitä nykyinen syöttöpuskuri |
response.create | Pyyntö TTS synteesi toimitetuille teksteille/ohjeille |
Palvelin → Asiakastapahtumat
| Tapahtuma | Merkitys |
|---|---|
session.created | Kättely valmis, oletuskonfig lähetetty |
session.updated | Viimeisin session.update tunnustettu |
input_audio_buffer.speech_started | Palvelin VAD havaitsi puheen käännön alun |
input_audio_buffer.speech_stopped | Palvelin VAD päätti käännöksen hiljaisuuden tai kestorajan jälkeen |
input_audio_buffer.committed | Ääni hyväksytty ja jonotettu transkriptiota varten |
conversation.item.input_audio_transcription.completed | ASR tulos lopullisen transkriitin kanssa |
response.audio.delta | Base64 PCM16 pala syntetisoitua ääntä |
response.audio.done | Ei enää äänipaloja tähän vastaukseen |
response.done | Vaste viimeistelty (metadata + latenssitilastot) |
error | Virhekuori type ja message |
Automaattiset käännökset, joissa palvelimen VAD
Eksplicit input_audio_buffer.commit jää oletuksena. Jotta palvelin sulkeutuu ja muunnos kääntyy automaattisesti Silero VAD, configurate turn_detection. Palvelin säilyttää vain pyydetyn esiroll-ohjelman ollessa joutokäynnissä, lähettää puheen käynnistyksen/pysähdyksen ja tekee tapahtumat järjestyksessä ja sulkee jatkuvasti aktiivisen käännöksen 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
Palvelin asuu AudioServer SPM-tuotteessa. Esimerkkiselainasiakas lähetetään osoitteeseen Examples/websocket-client.html Avaa se juoksupalvelimen vieressä ajaaksesi koko ASR + TTS kierrosmatka.
Mallilataukset
Kaikki mallit ladataan HuggingFace-osiosta ja välimuistista ~/Library/Caches/qwen3-speech/. AudioCommon -moduuli tarjoaa jaetun HuggingFaceDownloader-muistin, joka käsittelee latausta, välimuistia ja eheyden todentamista.
Lataa retries
Mallikuormaajat ja lataa sovellusrajapinnat hyväksytään downloadOptions: DownloadOptions. onRetry saa a DownloadRetryEvent ennen jokaista takaiskua, modelId, attempt, maxAttempts, alkuperäinen error, retryInSeconds. nil tarkoittaa, että ei retries jäljellä; täydellinen-välimuisti varalla voi silti onnistua.
Callbacks ovat synkronoitu ja @Sendable, ilman päänäyttelijän takuuta. Peruutus ei tuota uutta tilaisuutta. Aseta retryDelaysSeconds mukauttaa politiikkaa ([] tarkoittaa yhtä yritystä); nimenomainen lataus API argumentti on etusijalla. Oletusyritykset ja tavujen kehitys säilyvät. Epäonnistuneet yritykset kirjataan myös ilmoitustasolla kautta AudioLog.download.
Yhteistyön lopettaminen
MLX:n transkriptio Qwen3ASRModel.transcribeCheckingCancellation(audio:sampleRate:options:) heitot CancellationError kun tehtävän peruuntuminen havaitaan ennen ominaisuuden poistoa, koodausta, dekooderien esitäyttöä tai dekoodausvaihetta. GPU:n työ voi valmistua. transcribe sekä transcribeBatch API säilyttää ei-heitto käytöstä ja sivuuttaa tehtävän peruuntuminen. speech-server käyttää Qwen3-ASR:n peruutustietoista tulopistettä.
MOSS-TTS-Nano
MossTTSNanoModel serialisoi pyyntöjä ja luo tuore CoreML tila kunkin lauseen. syntetisoi ja syntetisoidaStream state stereot; SpeechGenerationModel palauttaa mono downmix. Pass language: noil. Streams päättyy tyhjä lopullinen merkki.
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)Paikallinen syntyperäinen päätelmä
12B:n osalta toistuvat vierekkäiset työkalujen puhelun sulkeutumismerkit välittömästi täydellisen, validoidun puhelun jälkeen kulutetaan tällä puhelulla. Ne eivät luo toista puhelua tai muuta sen argumentteja. Erilliset merkkiaineet ja epämuodostuneet argumentit pysyvät virheinä; E4B säilyttää tiukan käsittelyn.
Jos haluat säilyttää epäonnistuneet chat-pyynnöt, valitse failureCaptureDirectory inferenssissä. Capture on pois päältä oletusarvoisesti. Hakemiston on evättävä ryhmä- ja muu pääsy; uusien hakemistojen käyttö 0700 ja epäonnistumistiedostojen käyttö 0600. Tiedostot sisältävät tarkan pyyntörungon, pyyntötunnuksen ja virheen. Valmistuneet 12B-sukupolvet säilyttävät myös ripeät, tunnistetut tunnukset, raaka-tuotoksen ja pysäytä syyn ennen jäsentelyä. E4B viat eivät sisällä raaka sukupolven tietuetta. Nämä paikalliset tiedostot sisältävät keskustelu- ja työkalusisällön; HTTP-virheet ja onnistuneet pyynnöt eivät sisällä niitä.
Gemma4UnifiedChat.generateResponse hyväksyy valinnaisen onGeneration-kutsun, jossa on Gemma4GenerationTrace.Kannatus tapahtuu ennen työkalun jäsennystä, myös silloin, kun tämä täydennys hylätään. Tallentaminen ei muuta näytteenottoa tai hyväksyttyä työkalusynaksia.
Valinnainen agenttiHallinto kuormittaa vaiheistettua Gemma 4 12B Yhdistetty INT4 tekstimalli rinnakkain E4B. Käytä mallia: "gemma4-12b-it" keskustella natiivi työkaluja, tulos historia, ja peruutus. E4B on edelleen käytettävissä muita pyyntöjä varten. Gemma4UnifiedChat käyttää pinted MLX tekstien toteuttaminen ja julkaisut kunkin pyynnön. KV- välimuisti. Vastausten jäsentäminen poistaa kanavan erottimia palautetusta tekstistä. Orvot työkalu- puhelu terminaattorit hylätään epämuodostuneita vastauksia. Gemma 4 12B, työkalu-viesti sisältö säilyttää JSON tekstin, joka vastaa tarkistuspiste chat malli.
Lähteen rakenne hyväksyy --inference-manifest mallit.json lataamiseen Gemma 4 E4B INT4, VoxCPM2, täysi Whisper Large-v3 ja Silero VAD. Tämä palvelu alkaa offline-verkosta ja hylkää yhteensopimattomat mallitunnisteet.
VoxCPM2 tukee painoaPuhtaus: .float16 kvantitatiivisen tarkistuspisteen osalta. Sen pääparametrit käyttävät FP16; AudioVAE ja jotkin laskelmat pysyvät FP32. INT8 tarkastuspisteet hylätään tässä tilassa.
Gemma4ConversationMessage, Gemma4Tool ja luoVapaaehtoinen säilyttää natiivi työkaluilmoituksia, argumentteja ja tuloshistoriaa.InferenceCancellationToken peruuttaa sukupolven yhteistyössä. Silero makeIndependentStream isolaatit tilassa, ja prosessiChunkThewing adverces inference virheet.
Whisper ASRModel. fromDirectory loads standed full Large-v3 CoreML nippuja. 32-kerroksinen dekooderi voi rakentaa etuliitteensä ilman TextDecoderContextPrefill.mlmallic ja vapauttaa per-asteittaiset temporaries. Oletus Whisper CLI-malli pysyy Turbo.
Natiivipalvelu serialisoi chatin, synteesin ja transkription; kevyt VAD on erillinen stream-tila. Gemma esitäyttö käyttää 256-varmoja eriä, pyyntö KV-tila vapautuu ja MLX välimuistin yläraja on 128 MiB 20 Gib allocator-rajalla.
Puhe virralla: tosi palauttaa NDJSONin äänitapahtumat 48 kHz:ssä, minkä jälkeen on tehty tai virhe. Peruuta pyynnöstäID osoitteessa /v1/ference/cancel. GET /v1/ferenssi/voimakkuus ja/v1
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, contentValinnainen malliomistus
PuheRuntime tarjoaa yhteisen latauksen, peruutettavissa olevan FIFO:n sisäänpääsyn ja sarjatoteutuksen. Qwen3ASRRuntime selecks MLX or full CoreML identifikaatioiden avulla; alkuperäisiä API- ja CLI-lippuja on edelleen saatavilla.
Äänipalvelimella on yksi suorituslupa jokaisen täydellisen pyynnön kautta, myös streamed-ulostulo, jossa on enintään 32 pyyntöä odottamassa. ASR viat tuottavat HTTP-virheitä tai WebSocket virhetapahtumia; voimassa olevat tyhjät transkriptiot pysyvät onnistuneina.
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 toimii paikallisesti Apple Silicon kautta VoextralTTS-moduuli: 20 esiasetettua ääntä, yhdeksän kieltä ja 24 kHz monopuhe.
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)
}Vain esiasetetut äänet; viite-audio kloonaaminen ei ole saatavilla. Äänitehovirta SDK, CLI, WebSocket ja HTTP PCM. Toimita täydellinen tuloteksti eteen. Mallipainot käyttävät CC BY-NC 4.0:a.
import AudioCommon
let player = StreamingAudioPlayer()
player.skipInitialSilence = false