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).

Swift ja CLI-käyttö

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
Muistipolitiikka

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
/transcribePOSTaudio/wav runkoJSON { text } (Qwen3-ASR)
/v1/audio/transcriptionsPOST Moniosainen { file, model, response_format?, language?, prompt?, temperature? }JSON, teksti, verbose JSON, SRT tai VTT
/v1/audio/speechPOSTJSON { model, input, voice, response_format?, speed? }audio/wav tai audio/pcm runko
/speakPOSTJSON { text, engine?, language?, voice? }audio/wav runko (Qwen3-TTS, CosyVoice ja Kokoro)
/respondPOSTaudio/wav runkoaudio/wav keho (PersonaPlex)
/enhancePOSTaudio/wav runkoaudio/wav runko (DeepFilterNet3)
/vadPOSTaudio/wav runkoJSON-segmenttiluettelo
/diarizePOSTaudio/wav runkoJSON DiarizedSegment list
/embed-speakerPOSTaudio/wav runkoJSON [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.createdKättely valmis, oletuskonfig lähetetty
session.updated Viimeisin session.update tunnustettu
input_audio_buffer.speech_startedPalvelin VAD havaitsi puheen käännön alun
input_audio_buffer.speech_stoppedPalvelin 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.completedASR tulos lopullisen transkriitin kanssa
response.audio.deltaBase64 PCM16 pala syntetisoitua ääntä
response.audio.done Ei enää äänipaloja tähän vastaukseen
response.doneVaste 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)

MOSS-TSS-Nano

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 8080
POST /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, content

Valinnainen 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

Lue Voxtral TTS Guide