要使用 sherpa-onnx 存储库在 iOS 上结合 Swift 实现文本朗读(Text-to-Speech, TTS),需要完成以下步骤。sherpa-onnx 是一个基于 ONNX Runtime 的开源项目,支持离线语音识别和语音合成,适用于多种平台,包括 iOS。以下是具体实现指南:


1. 准备工作

(1) 环境要求

  • Xcode: 确保已安装最新版本的 Xcode(推荐 15.x 或更高)。
  • CMake: 用于构建 sherpa-onnx,需要安装 CMake(可以通过 Homebrew 安装:brew install cmake)。
  • iOS 设备或模拟器: iOS 版本建议 12.0 或更高。
  • 预训练模型: 需要下载适用于 TTS 的 ONNX 模型,例如 VITS 模型。

(2) 下载 sherpa-onnx 源码

从 GitHub 克隆存储库:

git clone https://github.com/k2-fsa/sherpa-onnx.git
cd sherpa-onnx

(3) 下载预训练 TTS 模型

访问 sherpa-onnx 预训练模型页面,选择一个支持你所需语言的 TTS 模型。例如:

  • 下载并解压 VITS 模型(比如vits-zh-aishell3.tar.bz2):
    tar xvf vits-zh-aishell3.tar.bz2
    这是一个支持中文的模型,解压后会得到 model.onnxtokens.txt 等文件。

2. 构建 sherpa-onnx for iOS

sherpa-onnx 提供了一个脚本 build-ios.sh 来为 iOS 编译静态库。你需要运行这个脚本生成适用于 iOS 的库文件。

(1) 运行构建脚本

sherpa-onnx 根目录下:

./build-ios.sh
  • 该脚本会下载 ONNX Runtime 的 iOS 版本并编译 sherpa-onnx
  • 编译完成后,生成的库文件会位于 build-ios 目录下,例如 lib/sherpa-onnx.a

(2) 注意事项

  • 如果遇到权限问题,确保脚本有执行权限:chmod +x build-ios.sh
  • 编译目标包括模拟器(x86_64)和真机(arm64),生成的 .a 文件是静态库,可以集成到 Xcode 项目中。

3. 创建 iOS 项目并集成

(1) 创建 Xcode 项目

  • 打开 Xcode,选择 “File > New > Project”,选择 “App” 模板,使用 Swift 作为语言。
  • 设置项目名称,例如 SherpaTTS,并选择保存路径。

(2) 集成 sherpa-onnx 库

  1. 添加静态库

    • build-ios/lib/sherpa-onnx.a 拖入 Xcode 项目导航栏的 “Frameworks, Libraries, and Embedded Content” 部分。
    • 确保在 “Build Phases” 中添加了该库。
  2. 添加头文件

    • sherpa-onnx/c-api/c-api.h 复制到项目中(位于 sherpa-onnx/c-api 目录)。
    • 这是一个 C API 文件,Swift 将通过桥接调用它。
  3. 添加 ONNX Runtime 依赖

    • build-ios.sh 已下载 ONNX Runtime(位于 build-ios/ios-onnxruntime),将其中的 onnxruntime.xcframework 添加到项目中。
  4. 添加模型文件

    • 将下载的 TTS 模型文件(例如 vits-zh-aishell3/model.onnxtokens.txt)拖入项目中,确保勾选 “Copy items if needed”。

(3) 创建桥接头文件

由于 sherpa-onnx 使用 C API,需要通过 Objective-C 桥接在 Swift 中调用:

  • 在 Xcode 中创建文件 “SherpaTTS-Bridging-Header.h”:
    #ifndef SherpaTTS_Bridging_Header_h
    #define SherpaTTS_Bridging_Header_h

    #include "c-api.h" // 指向 sherpa-onnx/c-api/c-api.h

    #endif
  • 在项目设置 “Build Settings > Swift Compiler - General > Objective-C Bridging Header” 中指定该文件路径(例如 SherpaTTS/SherpaTTS-Bridging-Header.h)。

4. 编写 Swift 代码实现 TTS

以下是一个简单的 Swift 示例,用于调用 sherpa-onnx 的 TTS 功能并播放生成的音频。

(1) 示例代码

ViewController.swift 中:

import UIKit
import AVFoundation

class ViewController: UIViewController {
var audioPlayer: AVAudioPlayer?

override func viewDidLoad() {
super.viewDidLoad()

// 配置 TTS
let modelPath = Bundle.main.path(forResource: "model", ofType: "onnx")!
let tokensPath = Bundle.main.path(forResource: "tokens", ofType: "txt")!

var config = SherpaOnnxOfflineTtsConfig(
model: SherpaOnnxOfflineTtsModelConfig(
vits: SherpaOnnxOfflineTtsVitsModelConfig(
model: modelPath,
lexicon: nil,
tokens: tokensPath,
dataDir: nil,
dictDir: nil,
noiseScale: 0.667,
noiseScaleW: 0.8,
lengthScale: 1.0
),
numThreads: 1,
debug: 0,
provider: "cpu"
),
maxNumSentences: 1
)

// 创建 TTS 实例
let tts = SherpaOnnxCreateOfflineTts(&config)

// 输入文本
let text = "你好,这是一个测试。"

// 生成音频
let audio = SherpaOnnxOfflineTtsGenerate(
tts,
text,
0, // speaker ID(根据模型支持调整)
1.0 // 语速
)

// 保存音频到临时文件并播放
if let samples = audio?.pointee.samples, audio?.pointee.numSamples > 0 {
let sampleRate = Int(audio!.pointee.sampleRate)
let data = Data(bytes: samples, count: Int(audio!.pointee.numSamples) * MemoryLayout<Float>.size)
playAudio(data: data, sampleRate: sampleRate)
}

// 清理资源
SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio)
SherpaOnnxDestroyOfflineTts(tts)
}

func playAudio(data: Data, sampleRate: Int) {
do {
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + "output.wav")
try writeWavFile(data: data, sampleRate: sampleRate, to: tempURL)
audioPlayer = try AVAudioPlayer(contentsOf: tempURL)
audioPlayer?.play()
} catch {
print("播放音频失败: \(error)")
}
}

func writeWavFile(data: Data, sampleRate: Int, to url: URL) throws {
let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: Double(sampleRate), channels: 1, interleaved: false)!
let audioFile = try AVAudioFile(forWriting: url, settings: audioFormat.settings)
let audioBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount(data.count / MemoryLayout<Float>.size))!
audioBuffer.frameLength = audioBuffer.frameCapacity
memcpy(audioBuffer.floatChannelData![0], (data as NSData).bytes, data.count)
try audioFile.write(from: audioBuffer)
}
}

(2) 代码说明

  • 配置 TTS: 使用 SherpaOnnxOfflineTtsConfig 设置模型路径和参数。
  • 生成音频: 调用 SherpaOnnxOfflineTtsGenerate 将文本转换为音频样本。
  • 播放音频: 使用 AVAudioPlayer 播放生成的音频(需先将浮点样本保存为 WAV 文件)。
  • 资源清理: 确保调用销毁函数释放内存。

5. 运行和测试

  1. 连接 iOS 设备或选择模拟器。
  2. 在 Xcode 中点击 “Run” 构建并运行项目。
  3. 应用启动后,应听到文本 “你好,这是一个测试。” 被朗读出来。

6. 注意事项

  • 模型选择: 确保使用的 TTS 模型支持目标语言(例如中文用 vits-zh-aishell3)。
  • 性能优化: 对于实时应用,可调整 numThreads 或使用更轻量模型。
  • 错误排查: 如果遇到链接错误,检查库和头文件是否正确添加;如果音频无声,确认样本数据有效。

通过以上步骤,你可以在 iOS 上使用 sherpa-onnx 和 Swift 实现文本朗读功能。如需进一步帮助,可参考 sherpa-onnx官方文档 或 GitHub Issues。