Omi Swift SDK 接入实战:iOS/macOS 端设备连接与本地 Whisper / 流式 Deepgram 实时转写
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
本文围绕开源仓库 Friend(Omi)中 Swift SDK 官方文档 展开,系统讲解如何在 iOS/macOS 应用中通过 Swift Package 连接 Omi 智能穿戴设备:从零配置的本地 Whisper 转写快速上手,到OmiManager全部公开方法逐项剖析,再到基于 WebSocket 的 Deepgram / Parakeet 流式语音识别接入。读完本文,你将掌握该 SDK 的完整调用链、BLE 数据流底层原理与可复用的 Swift 代码模板,能够直接在 Xcode 工程中落地"设备连接 + 实时转写"功能。
一、SDK 概览:包定位与适用场景
Swift SDK 是一个开箱即用的 Swift Package(swift-tools-version支持 SPM 集成),专为 iOS/macOS 客户端连接 Omi 系列设备而设计。其核心卖点有三:
- 原生 iOS/macOS 支持:基于
CoreBluetooth实现 BLE 通信,无跨平台桥接层; - 本地转写:默认使用随包捆绑的 Whisper 模型(
ggml-tiny.en)在设备端完成语音转写,无需任何云 API 和网络请求; - 简洁 API:对外仅暴露一个
OmiManager门面类,几分钟即可完成连接与转写。
从仓库目录结构看,包内主要分为两大部分:
| 模块 | 目录 | 职责 |
|---|---|---|
| 设备通信 | sdks/swift/Sources/omi-lib/helpers | BLE 扫描/连接、Friend 设备协议、录音与编解码、数据包处理 |
| 语音识别 | sdks/swift/Sources/omi-lib/STT | Whisper(本地离线)、Deepgram(云端流式)、Parakeet(自托管流式)三种引擎 |
另外包内还随附了ggml-tiny.en.bin模型文件(Resources 目录)与 Deepgram 转写器的单元测试(DeepgramTranscriberTests.swift),便于开发者验证和二次开发。
二、快速开始:2 分钟跑通设备连接与本地转写
1. 替换 ViewController 完整代码
按官方文档指引,将你工程中的ViewController.swift整体替换为如下代码(该示例无 UI,转写结果直接打印到 Xcode 控制台):
import UIKit import omi_lib class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.lookForDevice() } func lookForDevice() { OmiManager.startScan { device, error in print("starting scan") if let device = device { print("got device ", device) self.connectToOmiDevice(device: device) OmiManager.endScan() } } } func connectToOmiDevice(device: Device) { OmiManager.connectToDevice(device: device) self.listenToLiveTranscript(device: device) self.reconnectIfDisconnects() } func reconnectIfDisconnects() { OmiManager.connectionUpdated { connected in if connected == false { self.lookForDevice() } } } func listenToLiveTranscript(device: Device) { OmiManager.getLiveTranscription(device: device) { transcription in print("transcription:", transcription ?? "no transcription") } } }这段代码覆盖了 SDK 使用的四个关键步骤:扫描发现设备(startScan)→建立连接(connectToDevice)→订阅实时转写(getLiveTranscription)→监听断线并自动重连(connectionUpdated)。其中connectionUpdated返回false时重新发起扫描,是实际部署中保证连接稳健性的推荐模式。
2. 构建运行前置条件
运行前必须满足以下三点,缺一不可:
- 选择你的开发团队(Signing & Capabilities 中设置 Team,真机运行必须有开发者签名);
- 使用真机连接 iPhone:官方文档明确说明模拟器不支持蓝牙(simulators don't support Bluetooth),因此 BLE 扫描、连接、音频传输都必须在物理设备上验证;
- Run 工程,将 App 安装到手机。
3. 验证:开机、自动连接、说话看日志
- 打开你的 Omi 设备电源;
- 启动 App,程序会自动扫描并连接设备;
- 对着设备说话,转写文本会实时出现在 Xcode 控制台(
print("transcription:", ...)的输出)。
注意:本示例没有任何 UI,转写结果只出现在 Xcode 日志中;如需界面展示,可在
getLiveTranscription回调中自行刷新 UILabel 或 SwiftUI 视图。
三、OmiManager 公共 API 一览与方法逐项说明
官方文档给出了OmiManager的六个核心方法,全部为static方法,可在任意位置直接调用:
| 方法 | 说明 |
|---|---|
startScan(callback) | 开始扫描 Omi 设备 |
endScan() | 停止扫描 |
connectToDevice(device) | 连接已发现的设备 |
connectionUpdated(callback) | 监听连接状态变化 |
getLiveTranscription(device, callback) | 接收实时转写文本 |
getLiveAudio(device, callback) | 接收音频文件 URL |
结合门面实现源码(omi_lib.swift),可以进一步看清每个方法的底层行为:
startScan(completion:)内部将回调透传给单例FriendManager,并在seen_devices数组中按设备 UUID 去重,避免重复回调同一个设备(Device结构体仅暴露id: String字段给上层);connectToDevice(device:)会先在上层seen_devices中按id找回内部Friend对象,再调用FriendManager.connectToDevice,因此连接前必须先完成扫描;getLiveAudio(device:)实际映射到内部getRawAudio,与转写共用同一条 BLE 音频链路,只是输出为音频文件 URL 而非文本。
底层实现:8 秒轮询、临时文件与 44 字节 WAV 头
从 FriendManager.swift 的实现看,"实时"转写实际是定时器驱动的准实时轮询:
getLiveTranscription用Timer.scheduledTimer(withTimeInterval: 8.0, repeats: true)每8 秒取出当前录音文件,调用resetRecording()重置录音,再交给本地 Whisper 转写;getRawAudio同样以 8 秒为周期产出音频块,但有一个关键细节:录音文件会先被复制到临时目录,然后才调用resetRecording()(因为resetRecording()会删除原文件)。复制得到的 WAV 文件头可能显示 0 字节音频数据(文件仍在写入中),但 44 字节头部之后实际存在完整的 PCM 数据——代码中正是通过fileSize > 44来判断音频块是否有效(见 FriendManager.swift)。
这意味着如果你的应用对延迟有更高要求,可以自行缩短轮询间隔或改用下面的流式 STT 方案。
四、设备发现与 BLE 链路解析(源码级)
扫描过滤:三种设备名
扫描器在centralManager(_:didDiscover:...)回调中按广播名过滤外设,仅接受以下名称(见 BLEManager 相关扩展):
Friend Friend DevKit 2 Omi DevKit 2也就是说,Omi 正式版与 DevKit 开发板都能被 SDK 识别。扫描使用CBCentralManagerScanOptionAllowDuplicatesKey: false避免重复回调,并在centralManagerDidUpdateState中等待蓝牙poweredOn后才开始扫描。
BLE 服务与特征 UUID
Friend设备的通信协议定义在 helpers/Friend.swift,核心 UUID 如下:
| 用途 | UUID |
|---|---|
| 音频服务 | 19B10000-E8F2-537E-4F6C-D104768A1214 |
| 音频数据特征 | 19B10001-E8F2-537E-4F6C-D104768A1214 |
| 音频编解码器特征 | 19B10002-E8F2-537E-4F6C-D104768A1214 |
| 灯效编解码器特征 | 19B10003-E8F2-537E-4F6C-D104768A1214 |
音频数据包的解析逻辑为:前 2 字节是小端UInt16包序号(配合PacketCounter做丢包检测),第 3 字节是分片索引,其后才是音频负载;只有index == 0的包才触发录音缓冲刷新,避免分片内容被拆散。
编解码与连接状态机
设备通过编解码器特征上报音频格式,FriendCodec枚举支持:
pcm16(16 kHz PCM)/pcm8(8 kHz PCM)µLaw16/µLaw8(16 kHz / 8 kHz μ-Law)opus16(16 kHz Opus,见 Friend.swift)
BLEManager维护了完整的连接状态机:off / on / scanning / connecting / connected / linked / disconnected,并在didDisconnectPeripheral时回调lostConnection()——这正是上层connectionUpdated(false)触发重连的链路来源(见 BLEManager.swift)。从源码结构看,connected → linked的转变发生在按服务 UUID 匹配到已注册的可穿戴设备类型之后,SDK 通过WearableDeviceRegistry实现设备类型注册与实例化。
五、流式 STT:Deepgram 实时转写
当需要真正的低延迟流式转写(而不是 8 秒轮询)时,SDK 提供了基于 WebSocket 的 Deepgram 引擎。
方式一:通过工厂方法 OmiSttFactory
import omi_lib // Via OmiSttFactory let transcriber = try OmiSttFactory.makeStreaming( engine: .deepgram, deepgramAPIKey: "YOUR_DEEPGRAM_API_KEY", deepgramModel: "nova-2", deepgramLanguage: "es", // e.g. "en-US", "es", "fr", "ja" onTranscript: { text in print("Transcript: \(text)") } )方式二:直接实例化 OmiDeepgramTranscriber
// Or directly with OmiDeepgramTranscriber let deepgram = OmiDeepgramTranscriber( apiKey: "YOUR_DEEPGRAM_API_KEY", sampleRate: 16000, model: "nova-2", language: "es", onTranscript: { text in print("Transcript: \(text)") } ) // Send PCM audio data deepgram.appendPcm(pcmData) // Disconnect when done deepgram.stop()工厂方法的默认参数为:deepgramModel = "nova"、deepgramLanguage = "en-US"、sampleRate = 16000(见 SttFactory.swift)。OmiStreamingTranscriber协议只要求两个方法——appendPcm(_ data: Data)(推送 PCM16 LE 单声道 16 kHz 数据)与stop(),统一了各引擎的调用方式(见 SttEngine.swift)。
WebSocket 协议细节与测试验证
从 DeepgramTranscriber.swift 的实现看,客户端会向wss://api.deepgram.com/v1/listen发起 WebSocket 连接,并携带以下查询参数:
| 参数 | 值 | 说明 |
|---|---|---|
punctuate | true | 自动加标点 |
model | 可配置,默认nova | Deepgram 模型版本 |
language | 可配置,默认en-US | 识别语言 |
encoding | linear16 | 线性 PCM 编码 |
sample_rate | 默认16000 | 采样率 |
channels | 1 | 单声道 |
认证方式为 HTTP 头Authorization: Token <API_KEY>。接收循环持续解析服务端返回的 JSON,从channel.alternatives[0].transcript中提取文本并回调onTranscript;appendPcm在私有串行队列中执行以保证数据顺序,stop()以goingAway关闭码断开连接。
这些行为均有单元测试背书:DeepgramTranscriberTests.swift 验证了默认参数(nova/en-US/16000/linear16/channels=1)、自定义参数(nova-2/de/ 8000 Hz)以及多语言场景(nova-2-general/ja),并断言了工厂方法在 API Key 缺失或为空时抛出omi.stt域、code 为 2 的错误。
六、其他转写引擎:Parakeet 与本地 Whisper
Parakeet(自托管 NVIDIA 引擎)
OmiParakeetTranscriber面向自托管的 Parakeet 推理服务。其内部会把传入的 HTTP(S) base URL 自动转换为 WebSocket 地址(https://→wss://,http://→ws://),并拼接/v3/stream?sample_rate=16000端点。握手阶段服务端返回{"type":"ready"}后客户端才开始推送音频;stop()时先发送finalize字符串再断开,以保证最终结果落盘(见 ParakeetTranscriber.swift)。其 API URL 可通过工厂方法的parakeetAPIURL参数传入,默认读取环境变量HOSTED_PARAKEET_API_URL;若该值为空,工厂会抛出omi.stt域、code 为 3 的错误。
Whisper(设备端离线)
除FriendManager内置的 8 秒轮询转写外,SDK 还提供了独立的OmiWhisperTranscriber(见 WhisperTranscriber.swift):它默认从包内加载ggml-tiny.en模型,接受[Float]PCM 帧数组,await异步返回拼接后的识别文本。注意工厂方法对engine: .whisper会直接抛错(code 4),提示"流式 Whisper 请走getLiveTranscription路径"——即Whisper 是离线、面向文件/帧的引擎,Deepgram / Parakeet 才是流式引擎。
引擎选择建议
| 场景 | 推荐引擎 | 理由 |
|---|---|---|
| 最快上手、零云成本、离线可用 | Whisper(getLiveTranscription) | 模型内置,无需网络与密钥 |
| 低延迟流式转写、多语言、可调模型 | Deepgram | WebSocket 流式,nova-2等模型可选 |
| 数据不出内网、自建推理服务 | Parakeet | 对接自托管/v3/stream服务 |
七、常见错误与排查
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 收不到转写文本 | 使用模拟器运行 | 改用真机(模拟器不支持蓝牙) |
startScan无回调 | 设备未开机或蓝牙未授权 | 打开 Omi 设备电源;检查系统蓝牙权限(Info.plist中NSBluetoothAlwaysUsageDescription) |
makeStreaming(.deepgram)抛错 code 2 | 未传或传空deepgramAPIKey | 配置有效密钥 |
makeStreaming(.parakeet)抛错 code 3 | HOSTED_PARAKEET_API_URL为空 | 设置环境变量或显式传parakeetAPIURL |
makeStreaming(.whisper)抛错 code 4 | 误用工厂创建流式 Whisper | 改用getLiveTranscription或OmiWhisperTranscriber |
| 转写中断、自动重连 | BLE 意外断开 | 确认已实现connectionUpdated回调中的重扫逻辑 |
| 音频文件异常 | 文件仍在写入、WAV 头为 0 字节 | 参考getRawAudio的 44 字节头部判断与临时文件复制策略 |
八、相关资源
- Swift SDK 官方文档:sdks/swift/README.md
- 公共 API 门面实现:sdks/swift/Sources/omi-lib/omi_lib.swift
- 内部连接与转写核心:sdks/swift/Sources/omi-lib/FriendManager.swift
- STT 引擎工厂与协议:sdks/swift/Sources/omi-lib/STT/SttFactory.swift、sdks/swift/Sources/omi-lib/STT/SttEngine.swift
- Deepgram / Parakeet / Whisper 转写器:DeepgramTranscriber.swift、ParakeetTranscriber.swift、WhisperTranscriber.swift
- BLE 协议与设备模型:helpers/Friend.swift、helpers/BLEManager.swift
- 单元测试示例:sdks/swift/Tests/omi-libTests/DeepgramTranscriberTests.swift
需要说明的是,本指南基于当前仓库中 Swift SDK 的既有实现整理而成,具体参数(如默认模型nova、轮询间隔 8 秒)以仓库源码为准;接入时若 SDK 版本更新,请以最新源码与文档为准。
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考