argmax-oss-swift

argmaxincMIT活跃维护

面向 Apple Silicon 的端侧语音 AI 工具包,用 Swift 在 iOS 和 macOS 上本地跑语音识别、说话人分离和语音合成,不用联网也能处理音频。

On-device Speech AI for Apple Silicon

v1.1.1发布于 2026-10-08 13:48 UTC6,449star +3 / 7 天625fork同步于 2026-11-10 17:34 UTC

怎么上手

由 README 提炼,执行前请确认
dependencies: [.package(url: "https://github.com/argmaxinc/argmax-oss-swift.git", from: "0.9.0")]
brew install whisperkit-cli
硬件要求按 README 说明

README 未说明具体显存或 GPU 型号要求;TTSKit 基于 Core ML 在 Apple silicon 上运行,0.6B 模型约 1 GB 下载、1.7B 模型约 2.2 GB 且仅限 macOS。

支持 Apple Silicon
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

在 Apple Silicon 上本地跑语音识别、说话人分离和语音合成的 Swift SDK

面向 Apple Silicon 的端侧语音 AI 工具包,用 Swift 在 iOS 和 macOS 上本地跑语音识别、说话人分离和语音合成,不用联网也能处理音频。

核心能力

  • WhisperKit 基于 OpenAI Whisper 做端侧语音转文字
  • SpeakerKit 用 Pyannote 做说话人分离
  • TTSKit 基于 Qwen3-TTS 做端侧语音合成
  • 提供 OpenAI 兼容的本地 HTTP 服务端点
  • 支持增量加载长音频以控制内存占用

容易踩的坑

需要 macOS 14.0+ 与 Xcode 16.0+,TTSKit 还要求 macOS 15.0+/iOS 18.0+;首次运行会自动下载模型(TTSKit 默认 0.6B 约 1 GB);本地服务默认端口为 50060,且需用 --model 指定模型。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.1.1
  1. v1.1.1最新正式版2026-10-08 · 2026-10-08 13:48 UTC

    Highlights

    v1.1.1 is a patch release fixing some crashes identified by the community and a broader set of bug fixes, including removing several force unwraps, TTS text chunking, and improved download efficiency and error handling.

    HF Download Changes

    • Hugging Face tokens are no longer automatically read from the file system, meaning tokens need to be either passed in to the config or set as an HF_TOKEN environment variable (macOS or CLI implementations).
    • Metadata checks on an existing model download no longer re-fetch small config files.
    • Authentication errors now include the HTTP status code, for example Authentication failed with status code: 403.
    • Canceling a Hub snapshot download now throws CancellationError. Previously, it could return the partially downloaded folder, which looked like a successful download but was missing files.

    Other API Updates

    • AudioStreamTranscriber takes an optional inputDeviceID to stream from a specific input device instead of the system default.
    • WhisperKit.recommendedRemoteModels and WhisperKit.fetchModelSupportConfig take an optional useBackgroundSession, and WhisperKit now passes useBackgroundDownloadSession through when it fetches the support config.
    • TTSKitConfig and AudioOutput.startPlayback take an optional preserveExistingAudioSession to reuse the app's current iOS AVAudioSession setup instead of reconfiguring it.

    All optional with defaults for additional customizability.

    Thanks to everyone who sent fixes and reproductions for this release. 🙏

    What's Changed

    New Contributors

    Full Changelog: https://github.com/argmaxinc/argmax-oss-swift/compare/v1.1.0...v1.1.1

  2. v1.1.02026-08-06 · 2026-08-06 17:54 UTC
    查看更新说明

    Highlights

    • WhisperKit: Significant peak memory reduction via incremental audio file loading: 70%+ savings for 3-hour audio input.
    • TTSKit: ~40% end-to-end speedup with improved inference for Qwen3-TTS.
    • SpeakerKit: speaker centroid embeddings are now available from a public interface, useful for matching speakers across separate diarization runs.
    • Bug fixes for promptTokens, Chinese word timestamps, transcribeWithOptions, TTS chunk boundaries, and the model load/cache path.

    [!WARNING] TTSKit's default model variants changed (a one-time download on upgrade) and speech generation now requires macOS 15 / iOS 18. The previous assets remain supported - see Model Assets.

    WhisperKit: Incremental File Loading

    By default, WhisperKit decodes an entire audio file into memory before transcribing, which can lead to OOM situations for multi-hour audio files. .incremental streams it from disk in bounded-memory chunks instead:

    let pipe = try await WhisperKit()
    
    let results = try await pipe.transcribe(
        audioPath: "path/to/large-audio.wav",
        audioInputOptions: AudioInputOptions(audioLoadingMode: .incremental)
    )
    

    Chunks are cut at silence (VAD) boundaries, so the transcript matches a full-file run with chunkingStrategy: .vad - only peak memory differs. Tune with .incremental(chunkDuration:chunkBufferSize:) (defaults: 120s chunks, 2 chunks buffered at a time).

    Also available via CLI:

    swift run argmax-cli transcribe \
      --model large-v3-v20240930_626MB \
      --audio-path "path/to/large-audio.wav" \
      --incremental-loading
    

    This will become a default option in a future release, so let us know how it works for you!

    TTSKit: Faster Models

    Both Qwen3-TTS decoders now ship as multifunction Core ML assets, which lets us load slightly different model forward pass configurations using the same set of weights.

    SpeechDecoder (TTSKitConfig.speechDecoderMode): .latencyOptimized (default, 1 audio frame/call, ~80ms in length) for lowest time-to-first-audio, or .throughputOptimized (4 frames/call, ~320ms) to speed up throughput overall at the cost of a slower initial buffer.

    MultiCodeDecoder (TTSKitConfig.multiCodeDecoderMode): a talker frame expands into 15 residual codes. .stepped (default) does that in 16 Core ML calls with host-side sampling; .fused does the whole frame in one call with sampling and lookups inside the graph.

    Benchmarks in #494 and #513.

    // Defaults: latency-optimized SpeechDecoder, stepped MultiCodeDecoder
    let tts = try await TTSKit()
    
    // Opt in
    let config = TTSKitConfig(
        speechDecoderMode: .throughputOptimized,
        multiCodeDecoderMode: .fused
    )
    let fasterTTS = try await TTSKit(config)
    

    Via CLI:

    swift run -c release argmax-cli tts --text "Hello there." \
      --speech-decoder-mode throughputOptimized \
      --multi-code-decoder-mode fused --play
    

    The mode is read once at model load since it's considered an entirely separate model to the OS. Set it before constructing TTSKit, or reload to switch at runtime. The two modes don't produce byte-identical audio, but it's audibly indistinguishable - fused sampling draws a different but equally valid sequence. The TTSKitExample app gains sidebar pickers for both modes.

    The layout is detected from the asset at load time, so the legacy single-function W8A16 variants keep working (#520) - see Model Assets.

    SpeakerKit: Speaker Centroid Embeddings

    DiarizationResult now carries speakerCentroidEmbeddings: [Int: [Float]] which can be used to link speakers across separate diarization runs without re-running th

  3. v1.0.02026-05-01 · 2026-05-01 21:42 UTC
    查看更新说明

    Highlights

    v1.0.0 marks the graduation of the project from WhisperKit into the Argmax Open-Source SDK, a Swift package that ships WhisperKit, SpeakerKit, TTSKit, and ArgmaxCore together. Alongside the new name, this release brings full Swift 6 concurrency support, vendors the swift-transformers Hub and Tokenizers sources directly into ArgmaxCore under their original Apache 2.0 license (see NOTICES), and adds a new dynamic library product for binary distribution.

    ⚠️ This is a breaking release. Deprecated APIs have been removed, and a small number of APIs changed; see API changes below.

    New name: Argmax Open-Source SDK

    The new name reflects the project's evolution from a single kit (WhisperKit) into a multi-kit open-source SDK, and gives us room to add more models and capabilities in the future. This open-source SDK is the foundation that the Argmax Pro SDK is built on. The Pro SDK extends what's here with additional models and advanced features.

    The package is now argmax-oss-swift and the CLI is argmax-cli. A new umbrella product, ArgmaxOSS, re-exports all core modules so a single import gives you everything:

    .package(url: "https://github.com/argmaxinc/argmax-oss-swift", from: "1.0.0")
    
    import ArgmaxOSS  // WhisperKit + SpeakerKit + TTSKit + ArgmaxCore
    

    Swift 6 Concurrency

    The package is now Swift 6 compatible under Swift 6's approachable concurrency model, with Sendable adopted where appropriate and async boundaries made explicit. CI builds against the latest Swift 6 toolchains alongside Swift 5.10.

    [!NOTE] The top-level kit classes (WhisperKit, SpeakerKit, TTSKit) are not yet Sendable. If you store an instance of these in a Sendable type or pass it through a @Sendable closure, you may see warnings. Full Sendable conformance for the top-level kit classes is planned for a future release.

    Vendored Hub and Tokenizers

    Huge thanks to the Hugging Face team and the swift-transformers community. The project has been a foundational dependency for WhisperKit since day one.

    To give the OSS SDK a self-contained build and avoid version-resolution conflicts for downstream apps that already depend on swift-transformers, we've vendored the Hub and Tokenizers source files directly into Sources/ArgmaxCore/External/ under their original Apache 2.0 license (see NOTICES for full attribution). Every file carries the upstream attribution header, and all Argmax modifications are marked by // Argmax-modification: comments for easy auditing against upstream.

    The public API surface is exposed through opaque wrappers (HubApiWrapper, TokenizerWrapper, and AutoTokenizerWrapper), so the embedded Hub and Tokenizer types stay internal to ArgmaxCore and won't collide if you import swift-transformers separately in the same app.

    ArgmaxOSS Dynamic Library

    A new ArgmaxOSSDynamic product (in [email protected]) builds the SDK as a dynamic library.

    CLI

    The renamed argmax-cli bundles transcription, diarization, TTS, and an OpenAI-compatible local server in one binary.

    Run the local server with streaming transcription support:

    BUILD_ALL=1 swift run argmax-cli serve --model large-v3-v20240930_626MB --host 0.0.0.0 --port 50060
    

    The server responds to POST /v1/audio/transcriptions and POST /v1/audio/translations, so existing OpenAI SDK clients work out of the box. See the README for client examples.

    API Changes

    Removed deprecated APIs

    All APIs previously marked `@a

  4. v0.18.02026-04-01 · 2026-04-01 03:25 UTC
    查看更新说明

    Highlights

    This release refactors SpeakerKit around a new shared ModelManager base class, unifying the download → load → unload lifecycle across kits and simplifying the SpeakerKit public API.

    The top-level entry point is now just:

    let speakerKit = try await SpeakerKit()
    

    No config object required for the default case.

    Architecture Changes

    ModelManager (new, ArgmaxCore)

    A reusable base class for managing the full model lifecycle that all kits can now inherit from. It handles state transitions, error recovery, and concurrent load coalescing via an internal LoadModelsCoordinator — concurrent callers to ensureModelsLoaded() coalesce onto a single in-flight task rather than racing.

    Backend-specific I/O is delegated to a new ModelLoader protocol:

    public protocol ModelLoader: AnyObject, Sendable {
        var modelFolder: String? { get }
        func resolveModels(downloader: ModelDownloader, progressCallback: ((Progress) -> Void)?) async throws -> String
        func load(from modelPath: String, prewarm: Bool) async throws
        func unload() async
    }
    

    SpeakerKitDiarizer (replaces SpeakerKitModelManager)

    SpeakerKitModelManager has been replaced by SpeakerKitDiarizer, which inherits from ModelManager and conforms to the new Diarizer protocol. Create it via the static factory:

    let diarizer = SpeakerKitDiarizer.pyannote(config: config)
    

    SpeakerKitModelManager is still available as a deprecated typealias pointing to SpeakerKitDiarizer so existing code compiles with a warning rather than breaking.

    Diarizer protocol (new)

    A clean protocol for plugging in diarization backends:

    public protocol Diarizer: Sendable {
        var modelState: ModelState { get }
        func downloadModels() async throws
        func loadModels() async throws
        func unloadModels() async
        func diarize(audioArray: [Float], options: (any DiarizationOptions)?, progressCallback: ...) async throws -> DiarizationResult
    }
    

    ModelDownloadConfig (new, ArgmaxCore)

    All download parameters (endpoint, repo, token, revision, background session flag) are now bundled in a ModelDownloadConfig struct rather than passed individually to ModelDownloader. The existing convenience init still works unchanged.

    SpeakerKitConfig (new base class)

    PyannoteConfig now inherits from SpeakerKitConfig, which adds a load: Bool flag. When false (the default), models are loaded lazily on the first diarize() call. Set load: true to eager-load during SpeakerKit init.

    API Changes

    SpeakerKit initialization simplified

    // Before
    let config = PyannoteConfig()
    let speakerKit = try await SpeakerKit(config)
    
    // After
    let speakerKit = try await SpeakerKit()
    

    Local models path is now a String instead of URL:

    // Before
    let config = PyannoteConfig(modelFolder: URL(filePath: "/path/to/models"))
    
    // After
    let config = PyannoteConfig(modelFolder: "/path/to/models")
    

    Lazy loading by default

    Models are now loaded on the first diarize() call unless load: true is set in config.

    CLI

    diarize and transcribe --diarization now use the simplified SpeakerKit(config) API internally. No functional changes to CLI flags.

    What's Changed

    Full Changelog: https://github.com/argmaxinc/WhisperKit/compare/v0.17.0...v0.18.0

  5. v0.17.02026-03-13 · 2026-03-13 15:38 UTC
    查看更新说明

    Highlights

    We're excited to bring our commercial-grade speaker diarization framework, SpeakerKit, to open-source!

    With NVIDIA Sortformer now powering real-time speaker diarization in the Argmax Pro SDK, we're open-sourcing our implementation of Pyannote 4 (community-1). Pyannote is well-known for solving the "who spoke when" problem and has shown strong results on datasets such as AMI, DIHARD, and VoxConverse. Read the blog post for architecture details and benchmarks.

    Quickstart

    Download, load, diarize, and generate RTTM (Rich Transcription Time Marked) output in just a few lines of code:

    import SpeakerKit
    
    let speakerKit = try await SpeakerKit()
    let result = try await speakerKit.diarize(audioPath: "audio.wav")
    let rttm = speakerKit.generateRTTM(result: result)
    

    Key features

    • End-to-end Pyannote-style diarization pipeline
    • Automatically estimate the number of speakers or set it manually
    • Utilities to add speaker information to WhisperKit outputs
    • Standard RTTM export

    Explore the new SpeakerKit README section for API documentation, configuration details, and optimization tips.

    CLI

    whisperkit-cli now includes a dedicated diarize subcommand:

    swift run -c release whisperkit-cli diarize --audio-path audio.wav --rttm-path output.rttm
    

    With Homebrew:

    brew install whisperkit-cli
    whisperkit-cli diarize --audio-path audio.wav --rttm-path output.rttm
    

    You can also run transcription and diarization together using the new --diarization flag:

    whisperkit-cli transcribe --audio-path audio.wav --diarization
    

    Example output:

    ---- Speaker Diarization Results ----
    SPEAKER audio 1 0.220 7.360 What is RLHF? reinforcement learning with human feedback. What was that little magic ingredient to the dish that made it so much more delicious? <NA> A <NA> <NA>
    SPEAKER audio 1 7.610 14.850 - So we train these models on a lot of text data. And in that process, they learn something about the underlying representations of what's in here or in there. <NA> B <NA> <NA>
    

    Additional flags are available for speaker counts, model variants, clustering algorithms tuning, and more.

    WhisperAX example updates

    The WhisperAX example app has been updated with SpeakerKit support. It now includes diarization toggles, a flexible pipeline selector, and a Speakers tab for browsing labeled segments.

    What's Changed

    Full Changelog: https://github.com/argmaxinc/WhisperKit/compare/v0.16.0...v0.17.0

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Argmax Open-Source SDK

    Tests Supported Swift Version Supported Platforms License Discord Hugging Face

    Argmax Open-Source SDK Swift is a collection of turn-key on-device inference frameworks:

    • WhisperKit for speech-to-text with OpenAI Whisper
    • SpeakerKit for speaker diarization with Pyannote
    • TTSKit for text-to-speech with Qwen-TTS

    [!IMPORTANT] Argmax Pro SDK supports additional models and advanced features such as:

    • Real-time transcription with speakers
    • Frontier accuracy for your use case with custom vocabulary
    • Argmax Local Server for non-native apps
    • Android support with Argmax Pro SDK Kotlin

    Further resources:

    Table of Contents

    Installation

    Swift Package Manager

    WhisperKit, TTSKit, and SpeakerKit are separate library products in the same Swift package. Add the package once and pick the products you need. You can also use the ArgmaxOSS umbrella product to import everything at once.

    Prerequisites

    • macOS 14.0 or later.
    • Xcode 16.0 or later.

    Xcode Steps

    1. Open your Swift project in Xcode.
    2. Navigate to File > Add Package Dependencies....
    3. Enter the package repository URL: https://github.com/argmaxinc/argmax-oss-swift.
    4. Choose the version range or specific version.
    5. When prompted to choose library products, select ArgmaxOSS (all kits), or individual kits: WhisperKit, TTSKit, SpeakerKit.

    Package.swift

    Add the package dependency:

    dependencies: [
        .package(url: "https://github.com/argmaxinc/argmax-oss-swift.git", from: "0.9.0"),
    ],
    

    Then add the products you need as target dependencies:

    .target(
        name: "YourApp",
        dependencies: [
            // Import everything at once:
            .product(name: "ArgmaxOSS", package: "argmax-oss-swift"),
    
            // Or pick individual kits:
            // .product(name: "WhisperKit", package: "argmax-oss-swift"),   // speech-to-text
            // .product(name: "TTSKit", package: "argmax-oss-swift"),       // text-to-speech
            // .product(name: "SpeakerKit", package: "argmax-oss-swift"),   // speaker diarization
        ]
    ),
    

    Homebrew

    You can install the command line app using Homebrew by running the following command:

    brew install whisperkit-cli
    

    WhisperKit

    To get started with WhisperKit, you need to initialize it in your project.

    Quick Example

    This example demonstrates how to transcribe a local audio file:

    import WhisperKit
    
    // Initialize WhisperKit with default settings
    Task {
        let pipe = try? await WhisperKit()
        let results = try? await pipe?.transcribe(audioPath: "path/to/your/audio.{wav,mp3,m4a,flac}")
        let transcription = results?.map(\.text).joined(separator: " ")
        print(transcription ?? "")
    }
    

    Memory-Efficient Loading for Large Files

    By default WhisperKit loads the whole audio file into memory before transcribing. For long recordings, .incremental streams it from disk in bounded-memory chunks instead:

    import WhisperKit
    
    let pipe = try await WhisperKit()
    
    let options = AudioInputOptions(audioLoadingMode: .incremental)
    let results = try await pipe.transcribe(
        audioPath: "path/to/large-audio.wav",
        audioInputOptions: options
    )
    print(results.map(\.text).joined(separator: " "))
    

    It splits the audio at silence (VAD) boundaries, so the result matches a full-file transcription run with chunkingStrategy: .vad — only peak memory differs. Tune the chunking with .incremental(chunkDuration:chunkBufferSize:), or pick channels with AudioInputOptions(channelMode:).

    From the CLI, add --incremental-loading (optionally --incremental-chunk-duration / --incremental-chunk-buffer-size):

    swift run argmax-cli transcribe --model large-v3-v20240930_626MB --audio-path "path/to/large-audio.wav" --incremental-loading
    

    Model Selection

    [!NOTE] Argmax recommends large-v3-v20240930_626MB for maximum multilingual accuracy and tiny for the fastest debugging workflow.

    Whisper VersionWhisperKit VariantDescription
    Large v3 Turbo (compressed)large-v3-v20240930_626MBRecommended across iOS and macOS for maximum accuracy
    Large v3 Turbolarge-v3-v20240930_turboRecommended on macOS for maximum speed and accuracy
    Base (multilingual)base
    Base (English-only)base.en
    Small (Multilingual)small
    Small (English-only)small.en
    Tiny (Multilingual)tiny
    Tiny (English-only)tiny.enSmallest size, lowest accuracy. Only recommended for development & debugging.

    WhisperKit automatically downloads the recommended model for the device if not specified. You can also select a specific model by passing in the model name:

    let pipe = try? await WhisperKit(WhisperKitConfig(model: "large-v3-v20240930_626MB"))
    

    This method also supports glob search, so you can use wildcards to select a model:

    let pipe = try? await WhisperKit(WhisperKitConfig(model: "large-v3-v20240930_626MB"))
    

    Note that the model search must return a single model from the source repo, otherwise an error will be thrown.

    For a list of available models, see our HuggingFace repo.

    Generating Models

    WhisperKit also comes with the supporting repo whisperkittools which lets you create and deploy your own fine tuned versions of Whisper in CoreML format to HuggingFace. Once generated, they can be loaded by simply changing the repo name to the one used to upload the model:

    let config = WhisperKitConfig(model: "large-v3-v20240930_626MB", modelRepo: "username/your-model-repo")
    let pipe = try? await WhisperKit(config)
    

    Swift CLI

    The Swift CLI allows for quick testing and debugging outside of an Xcode project. To install it, run the following:

    git clone https://github.com/argmaxinc/argmax-oss-swift.git
    cd argmax-oss-swift
    

    Then, setup the environment and download your desired model.

    make setup
    make download-model MODEL=large-v3-v20240930_626MB
    

    Note:

    1. This will download only the model specified by MODEL (see what's available in our HuggingFace repo, where we use the prefix openai_whisper-{MODEL})
    2. Before running download-model, make sure git-lfs is installed

    If you would like download all available models to your local folder, use this command instead:

    make download-models
    

    You can then run them via the CLI with:

    swift run argmax-cli transcribe --model-path "Models/whisperkit-coreml/openai_whisper-large-v3-v20240930_626MB" --audio-path "path/to/your/audio.{wav,mp3,m4a,flac}"
    

    Which should print a transcription of the audio file. If you would like to stream the audio directly from a microphone, use:

    swift run argmax-cli transcribe --model-path "Models/whisperkit-coreml/openai_whisper-large-v3-v20240930_626MB" --stream
    

    Local Server

    The Argmax CLI includes a local server that implements the OpenAI Audio API, allowing you to use existing OpenAI SDK clients or generate new ones. The server supports transcription and translation with output streaming capabilities (real-time transcription results as they're generated).

    [!NOTE] Argmax Pro Local Server provides a real-time streaming transcription with a WebSocket local server that is API-compatible with cloud-based providers such as Deepgram.

    Building the Server
    # Build with server support
    make build-local-server
    
    # Or manually with the build flag
    BUILD_ALL=1 swift build --product argmax-cli
    
    Starting the Server
    # Start server with default settings
    BUILD_ALL=1 swift run argmax-cli serve
    
    # Custom host and port
    BUILD_ALL=1 swift run argmax-cli serve --host 0.0.0.0 --port 8080
    
    # With specific model and verbose logging
    BUILD_ALL=1 swift run argmax-cli serve --model tiny --verbose
    
    # See all configurable parameters
    BUILD_ALL=1 swift run argmax-cli serve --help
    
    API Endpoints
    • POST /v1/audio/transcriptions - Transcribe audio to text
    • POST /v1/audio/translations - Translate audio to English
    Supported Parameters
    ParameterDescriptionDefault
    fileAudio file (wav, mp3, m4a, flac)Required
    modelModel identifierServer default
    languageSource language codeAuto-detect
    promptText to guide transcriptionNone
    response_formatOutput format (json, verbose_json)verbose_json
    temperatureSampling temperature (0.0-1.0)0.0
    timestamp_granularities[]Timing detail (word, segment)segment
    streamEnable streamingfalse
    Client Examples

    Python Client (OpenAI SDK)

    cd Examples/ServeCLIClient/Python
    uv sync
    python whisperkit_client.py transcribe --file audio.wav --language en
    python whisperkit_client.py translate --file audio.wav
    

    Quick Python example:

    from openai import OpenAI
    client = OpenAI(base_url="http://localhost:50060/v1")
    result = client.audio.transcriptions.create(
        file=open("audio.wav", "rb"),
        model="tiny"  # Model parameter is required
    )
    print(result.text)
    

    Swift Client (Generated from OpenAPI Spec, see ServeCLIClient/Swift/updateClient.sh)

    cd Examples/ServeCLIClient/Swift
    swift run whisperkit-client transcribe audio.wav --language en
    swift run whisperkit-client translate audio.wav
    

    CurlClient (Shell Scripts)

    cd Examples/ServeCLIClient/Curl
    chmod +x *.sh
    ./transcribe.sh audio.wav --language en
    ./translate.sh audio.wav --language es
    ./test.sh  # Run comprehensive test suite
    
    Generating the API Specification

    The server's OpenAPI specification and code are generated from the official OpenAI API:

    # Generate latest spec and server code
    make generate-server
    
    Client Generation

    You can generate clients for any language using the OpenAPI specification, for example:

    # Generate Python client
    swift run swift-openapi-generator generate scripts/specs/localserver_openapi.yaml \
      --output-directory python-client \
      --mode client \
      --mode types
    
    # Generate TypeScript client
    npx @openapitools/openapi-generator-cli generate \
      -i scripts/specs/localserver_openapi.yaml \
      -g typescript-fetch \
      -o typescript-client
    
    API Limitations

    Compared to the official OpenAI API, the local server has these limitations:

    • Response formats: Only json and verbose_json supported (no plain text, SRT, VTT formats)
    • Model selection: Client must launch server with desired model via --model flag
    Fully Supported Features

    The local server fully supports these OpenAI API features:

    • Include parameters: logprobs parameter for detailed token-level log probabilities
    • Streaming responses: Server-Sent Events (SSE) for real-time transcription
    • Timestamp granularities: Both word and segment level timing
    • Language detection: Automatic language detection or manual specification
    • Temperature control: Sampling temperature for transcription randomness
    • Prompt text: Text guidance for transcription style and context

    TTSKit

    TTSKit is an on-device text-to-speech framework built on Core ML. It runs Qwen3-TTS models entirely on Apple silicon with real-time streaming playback, no server required.

    • macOS 15.0 or later.
    • iOS 18.0 or later.

    Quick Example

    This example demonstrates how to generate speech from text:

    import TTSKit
    
    Task {
        let tts = try await TTSKit()
        let result = try await tts.generate(text: "Hello from TTSKit!")
        print("Generated \(result.audioDuration)s of audio at \(result.sampleRate)Hz")
    }
    

    TTSKit() automatically downloads the default 0.6B model on first run. The tokenizer and CoreML models are loaded lazily on the first generate() call.

    Model Selection

    TTSKit ships two model sizes. You can select the model by passing a variant to TTSKitConfig:

    // Fast, runs on all platforms (~1 GB download)
    let tts = try await TTSKit(TTSKitConfig(model: .qwen3TTS_0_6b))
    
    // Higher quality, macOS only (~2.2 GB download, supports style instructions)
    let tts = try await TTSKit(TTSKitConfig(model: .qwen3TTS_1_7b))
    

    Models are hosted on HuggingFace and cached locally after the first download.

    Custom Voices

    You can choose from 9 built-in voices and 10 languages:

    let result = try await tts.generate(
        text: "こんにちは世界",
        speaker: .onoAnna,
        language: .japanese
    )
    

    Voices: .ryan, .aiden, .onoAnna, .sohee, .eric, .dylan, .serena, .vivian, .uncleFu

    Languages: .english, .chinese, .japanese, .korean, .german, .french, .russian, .portuguese, .spanish, .italian

    Real-Time Streaming Playback

    play streams audio to the device speakers frame-by-frame as it is generated:

    try await tts.play(text: "This starts playing before generation finishes.")
    

    You can control how much audio is buffered before playback begins. The default .auto strategy measures the first generation step and pre-buffers just enough to avoid underruns:

    try await tts.play(
        text: "Long passage...",
        playbackStrategy: .auto
    )
    

    Other strategies include .stream (immediate, no buffer), .buffered(seconds:) (fixed pre-buffer), and .generateFirst (generate all audio first, then play).

    Speech Decoder Mode

    TTSKit's default speech decoder bundles two functions, selectable via TTSKitConfig.speechDecoderMode:

    ModeRVQ frames / callAudio / callUse case
    .latencyOptimized (default)1~80 msLowest time-to-first-audio for streaming.
    .throughputOptimized4~320 msAmortizes decoder overhead for higher throughput, at the cost of a ~4× larger first-buffer latency.
    // Default: latency-optimized (lowest time-to-first-audio)
    let tts = try await TTSKit()
    
    // Opt into throughput-optimized generation
    let config = TTSKitConfig(speechDecoderMode: .throughputOptimized)
    let throughputTTS = try await TTSKit(config)
    

    The mode is read once when models are loaded; set it before constructing TTSKit (or reload the model to switch at runtime).

    Generation Options

    You can customize sampling, chunking, and concurrency via GenerationOptions:

    // Defaults recommended by Qwen
    var options = GenerationOptions()
    options.temperature = 0.9
    options.topK = 50
    options.repetitionPenalty = 1.05
    options.maxNewTokens = 245
    
    // Long text is automatically split at sentence boundaries
    options.chunkingStrategy = .sentence
    options.concurrentWorkerCount = nil  // nil = all chunks run concurrently with a good default for the device
    
    let result = try await tts.generate(text: longArticle, options: options)
    
    Style Instructions (1.7B only)

    The 1.7B model accepts a natural-language style instruction that controls prosody:

    var options = GenerationOptions()
    options.instruction = "Speak slowly and warmly, like a storyteller."
    
    let result = try await tts.generate(
        text: "Once upon a time...",
        speaker: .ryan,
        options: options
    )
    

    Saving Audio

    Generated audio can be saved to WAV or M4A:

    let result = try await tts.generate(text: "Save me!")
    let outputDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    
    // Save as .wav or .m4a (AAC)
    try await AudioOutput.saveAudio(result.audio, toFolder: outputDir, filename: "output", format: .m4a)
    

    Progress Callbacks

    You can receive per-step audio during generation. Return false from the callback to cancel early:

    let result = try await tts.generate(text: "Hello!") { progress in
        print("Audio chunk: \(progress.audio.count) samples")
        if let stepTime = progress.stepTime {
            print("First step took \(stepTime)s")
        }
        return true  // return false to cancel
    }
    

    Swift CLI

    The TTS command is available through the argmax-cli tool. You can generate speech and optionally play it back in real time:

    swift run argmax-cli tts --text "Hello from the command line" --play
    swift run argmax-cli tts --text "Save to file" --output-path output.wav
    swift run argmax-cli tts --text "日本語テスト" --speaker ono-anna --language japanese
    swift run argmax-cli tts --text-file article.txt --model 1.7b --instruction "Read cheerfully"
    swift run argmax-cli tts --help
    

    Demo App

    The TTSKitExample example app showcases real-time streaming, model management, waveform visualization, and generation history on macOS and iOS. See the TTSKitExample README for build instructions.

    SpeakerKit

    SpeakerKit is an on-device speaker diarization framework built on Core ML. It runs Pyannote v4 (community-1) on Apple silicon to label speakers in audio. Read the blog post for architecture details and benchmarks.

    • macOS 13.0 or later.
    • iOS 16.0 or later.

    Quick Example

    This example demonstrates how to diarize an audio file:

    import SpeakerKit
    
    Task {
        let speakerKit = try await SpeakerKit()
    
        let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
        let result = try await speakerKit.diarize(audioArray: audioArray)
    
        print("Detected \(result.speakerCount) speakers")
        for segment in result.segments {
            print(segment)
        }
    }
    

    SpeakerKit() uses PyannoteConfig() defaults, automatically downloading models from HuggingFace on first run. The segmenter and embedder CoreML models are loaded lazily (unless load is set on config) on the first diarize() call.

    Diarization Options

    You can control speaker detection via PyannoteDiarizationOptions:

    let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
    let options = PyannoteDiarizationOptions(
        numberOfSpeakers: 2,               // nil = automatic detection
        clusterDistanceThreshold: 0.6,     // clustering threshold
        useExclusiveReconciliation: false   // exclusive speaker assignment per frame
    )
    let result = try await speakerKit.diarize(audioArray: audioArray, options: options)
    

    For local models, skip the download step:

    let config = PyannoteConfig(modelFolder: "/path/to/models")
    let speakerKit = try await SpeakerKit(config)
    

    Combining with Transcription

    SpeakerKit can merge diarization results with WhisperKit transcriptions to produce speaker-attributed segments:

    import WhisperKit
    import SpeakerKit
    
    let whisperKit = try await WhisperKit()
    let speakerKit = try await SpeakerKit()
    
    let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
    let transcription = try await whisperKit.transcribe(audioArray: audioArray)
    let diarization = try await speakerKit.diarize(audioArray: audioArray)
    
    let speakerSegments = diarization.addSpeakerInfo(to: transcription)
    
    for group in speakerSegments {
        for segment in group {
            print("\(segment.speaker): \(segment.text)")
        }
    }
    

    Two strategies are available for matching speakers to transcription:

    • .subsegment (default) -- splits segments at word gaps, then assigns speakers
    • .segment -- assigns a speaker to each transcription segment as a whole

    RTTM Output

    Generate RTTM output:

    let speakerKit = try await SpeakerKit()
    
    let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "meeting.wav")
    let diarization = try await speakerKit.diarize(audioArray: audioArray)
    
    let rttmLines = SpeakerKit.generateRTTM(from: diarization, fileName: "meeting")
    for line in rttmLines {
        print(line)
    }
    

    Swift CLI

    The diarization commands are available through the argmax-cli tool:

    # Standalone diarization
    swift run argmax-cli diarize --audio-path audio.wav --verbose
    
    # Save RTTM output
    swift run argmax-cli diarize --audio-path audio.wav --rttm-path output.rttm
    
    # Specify number of speakers
    swift run argmax-cli diarize --audio-path audio.wav --num-speakers 3
    
    # Transcription with diarization
    swift run argmax-cli transcribe --audio-path audio.wav --diarization
    
    # See all options
    swift run argmax-cli diarize --help
    

    Contributing & Roadmap

    Our goal is to make this SDK better and better over time and we'd love your help! Just search the code for "TODO" for a variety of features that are yet to be built. Please refer to our contribution guidelines for submitting issues, pull requests, and coding standards, where we also have a public roadmap of features we are looking forward to building in the future.

    External dependencies: Sources/ArgmaxCore/External/ contains a copy of swift-transformers (Hub and Tokenizers modules, v1.1.6) with Jinja-dependent code removed. When updating to a newer version, copy the fresh sources over that directory and re-apply the patches marked with // Argmax-modification: (grep -r "Argmax-modification:" Sources/ArgmaxCore/External/). The matching upstream tests are vendored under Tests/ArgmaxCoreTests/External/ using the same convention.

    License

    Argmax OSS is released under the MIT License. See LICENSE for more details.

    This project incorporates third-party software under their own license terms. See NOTICES for attributions.

    Citation

    If you use this SDK for something cool or just find it useful, please drop us a note at [email protected]!

    If you use WhisperKit, SpeakerKit or TTSKit for academic work, please cite the project using the following BibTeX:

    @misc{whisperkit-argmax,
       title = {Argmax OSS: On-device Speech AI with WhisperKit, SpeakerKit and TTSKit},
       author = {Argmax, Inc.},
       year = {2024},
       URL = {https://github.com/argmaxinc/argmax-oss-swift}
    }