news 2026/9/17 8:22:58

Omi Swift SDK 接入实战:iOS/macOS 端设备连接与本地 Whisper / 流式 Deepgram 实时转写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Omi Swift SDK 接入实战:iOS/macOS 端设备连接与本地 Whisper / 流式 Deepgram 实时转写

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/helpersBLE 扫描/连接、Friend 设备协议、录音与编解码、数据包处理
语音识别sdks/swift/Sources/omi-lib/STTWhisper(本地离线)、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. 构建运行前置条件

运行前必须满足以下三点,缺一不可:

  1. 选择你的开发团队(Signing & Capabilities 中设置 Team,真机运行必须有开发者签名);
  2. 使用真机连接 iPhone:官方文档明确说明模拟器不支持蓝牙(simulators don't support Bluetooth),因此 BLE 扫描、连接、音频传输都必须在物理设备上验证;
  3. 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 的实现看,"实时"转写实际是定时器驱动的准实时轮询

  • getLiveTranscriptionTimer.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 连接,并携带以下查询参数:

参数说明
punctuatetrue自动加标点
model可配置,默认novaDeepgram 模型版本
language可配置,默认en-US识别语言
encodinglinear16线性 PCM 编码
sample_rate默认16000采样率
channels1单声道

认证方式为 HTTP 头Authorization: Token <API_KEY>。接收循环持续解析服务端返回的 JSON,从channel.alternatives[0].transcript中提取文本并回调onTranscriptappendPcm在私有串行队列中执行以保证数据顺序,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模型内置,无需网络与密钥
低延迟流式转写、多语言、可调模型DeepgramWebSocket 流式,nova-2等模型可选
数据不出内网、自建推理服务Parakeet对接自托管/v3/stream服务

七、常见错误与排查

现象可能原因处理方式
收不到转写文本使用模拟器运行改用真机(模拟器不支持蓝牙)
startScan无回调设备未开机或蓝牙未授权打开 Omi 设备电源;检查系统蓝牙权限(Info.plistNSBluetoothAlwaysUsageDescription
makeStreaming(.deepgram)抛错 code 2未传或传空deepgramAPIKey配置有效密钥
makeStreaming(.parakeet)抛错 code 3HOSTED_PARAKEET_API_URL为空设置环境变量或显式传parakeetAPIURL
makeStreaming(.whisper)抛错 code 4误用工厂创建流式 Whisper改用getLiveTranscriptionOmiWhisperTranscriber
转写中断、自动重连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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 8:22:05

SpringBoot+MySQL短视频网站开发:从建表到分页优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 8:21:27

DDR5 SPD本质是DRAM校准档案,非说明书

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 8:15:55

LLM双模型交叉评估实战框架:五类能力断层测试

1. 这不是“评测报告”&#xff0c;而是一份LLM裁判员的实操手记你有没有试过让两个大模型同时给你打分&#xff1f;不是那种“AI助手帮你写周报”的轻量级任务&#xff0c;而是真正把它们推上裁判席——给一段代码纠错、给一篇议论文打分、给一个数学证明判对错。标题里说的“…

作者头像 李华
网站建设 2026/9/17 8:15:32

LabVIEW中TOOMOSS CAN句柄管理VI设计原理与工业实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 8:13:11

光纤光栅测温:开关柜触头温升监测与变电运行实践

简介&#xff1a;这份PDF文献聚焦电力系统变电运行中的光纤光栅测温系统&#xff0c;面向变电运维人员、电力设备状态监测研究者以及电气工程相关专业师生&#xff0c;用于理解测温技术选型与热故障预防思路。全文围绕电力设备过热故障分类、现有测温手段对比展开&#xff0c;系…

作者头像 李华