一、文字转语音的实现
应用 AVFoundation
中 Speech Kit
的 AVSpeechSynthesizer
能利用系统自带的引擎,无需网络请求,高效的实现(语音合成)文字实时转语音
使用比较简单,直接看实现步骤
-
导入 AVFoundation 框架
swiftimport AVFoundation
-
创建
AVSpeechSynthesizer
实例AVSpeechSynthesizer
是AVFoundation
框架中用于文本转语音的类,需要用实例来进行文本到语音的转换。swiftlet speechSynthesizer = AVSpeechSynthesizer()
-
配置 AVSpeechUtterance 对象
AVSpeechUtterance 是 AVFoundation 中用于表示文本转语音过程的类。它用于接收需要转语音的文本内容,语音,音调,语速,声音性别等属性。
AVSpeechSynthesisVoice
是设置语音语音相关的类AVSpeechSynthesisVoice.speechVoices()
是返回所有可用语音的数组swiftlet speechUtterance = AVSpeechUtterance(string: text) speechUtterance.rate = 0.5 // 语速 speechUtterance.pitchMultiplier = 1.2 // 音调 // 设置语音类型和性别 let availableVoices = AVSpeechSynthesisVoice.speechVoices() if let englishVoice = availableVoices.first(where: { $0.language == "en-US" && $0.gender == .female }) { speechUtterance.voice = englishVoice } else { print("No suitable voice found") }
-
播放语音
配置好 AVSpeechUtterance 对象,你可以使用 AVSpeechSynthesizer 实例的
speak(_:)
方法来播放语音swiftspeechSynthesizer.speak(speechUtterance)
-
语音播放控制和状态判断
状态判断:是否在语音播放中、暂停
swiftopen var isSpeaking: Bool { get } open var isPaused: Bool { get }
暂停和停止
swiftopen func stopSpeaking(at boundary: AVSpeechBoundary) -> Bool open func pauseSpeaking(at boundary: AVSpeechBoundary) -> Bool
停止方式的选择枚举
swiftpublic enum AVSpeechBoundary : Int, @unchecked Sendable { // 立即停止 case immediate = 0 // 播放完当前的词 case word = 1 }
-
AVSpeechSynthesizerDelegate 监听语音播放的情况
例如:暂停,取消,完成,开始 这4种状态的变化
swift// 暂停 func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) { } // 完成 func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { } didCance, didStart ......
注意点:
- 调用
stopSpeaking
和语音播放完成都会触发didFinish
代理方法(需要自己区分) AVSpeechSynthesizer
本身不能自动区分输入的文字是哪种语音,当于设置的不一致时,或含emoji 转语音后播放的内容会异常- 设备设置了静音,不单独设置
AVAudioSession
,AVSpeechSynthesizer
语音会没声音
二、录音的实现
系统实现录音的方式有多种,常见的3种各有特点,简单介绍如下:
AVFAudio Framework
的 AVAudioRecorder
单纯的录音,并将录音的文件写入指定沙盒目录,不能实时获取。
swift
// 创建 AVAudioRecorder 实例
var audioRecorder: AVAudioRecorder
func setupAudioRecorder() {
let audioFilename = getDocumentsDirectory().appendingPathComponent("recordedAudio.wav")
let settings: [String: Any] = [
AVFormatIDKey: kAudioFormatLinearPCM,
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.prepareToRecord()
} catch {
// 处理创建 AVAudioRecorder 实例的错误
}
}
// 开始和停止录音
func startRecording() {
if !audioRecorder.isRecording {
audioRecorder.record()
}
}
func stopRecording() {
if audioRecorder.isRecording {
audioRecorder.stop()
}
}
// 处理录音完成后的事件
extension YourViewController: AVAudioRecorderDelegate {
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
if flag {
// 录音完成,可以处理录音文件,例如保存路径或处理录音数据
} else {
// 录音失败
}
}
}
AVFoundation
的 AVCaptureAudioDataOutput
通过代理来接收音频数据的 buffer, 这个类似于相机捕获图片时的代理方法。
swift
init() {
// 创建 AVCaptureSession
captureSession = AVCaptureSession()
// 设置音频输入设备(麦克风)
if let audioDevice = AVCaptureDevice.default(for: .audio) {
do {
let audioInput = try AVCaptureDeviceInput(device: audioDevice)
if captureSession.canAddInput(audioInput) {
captureSession.addInput(audioInput)
}
} catch {
print("Error setting up audio input: \(error.localizedDescription)")
}
}
// 设置音频数据输出
let audioOutput = AVCaptureAudioDataOutput()
audioOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .utility))
if captureSession.canAddOutput(audioOutput) {
captureSession.addOutput(audioOutput)
}
}
func startCapturing() {
if !captureSession.isRunning {
captureSession.startRunning()
}
}
func stopCapturing() {
if captureSession.isRunning {
captureSession.stopRunning()
}
}
// AVCaptureAudioDataOutputSampleBufferDelegate 方法,获取音频数据的buffer
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// 处理音频数据的sampleBuffer
// 在这里可以获取并处理音频数据的buffer
}
AVFoundation
的 AVAudioEngine
简洁的方式直接 block 回调音频数据的 buffer。
swift
let audioEngine = AVAudioEngine()
audioEngine.inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(bufferSize), format: audioEngine.inputNode.outputFormat(forBus: 0)) { [weak self] (buffer, time) in
// 处理音频数据
}
//开启音频录入引擎
try audioEngine.start()
func stopRecording() {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
}
总结:音频处理是一个非常复杂的技术,我们最终选择了代码简洁的 AVAudioEngine
来实现录音。虽然文章说及时性比AVCaptureAudioDataOutput
慢,由于我们没有对音频做很多处理,体验上差异不明显;
三、AVAudioSession 的设置
激活 AVAudioSession
, 设置 Category, Option Category、Option枚举含义介绍
AVAudioSessionCategoryPlayAndRecord
:支持音频录制和播放,声音在没有外设的情况下,默认为听筒播放,不受手机静音影响。
AVAudioSessionCategoryOptionDefaultToSpeaker
:允许改变音频session默认选择内置扬声器(免提);仅支持 AVAudioSessionCategoryPlayAndRecord
swift
// AVAudioSession 是全局的,容易被其他的代码覆盖掉
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
注意: 设置错误 Category、Option 会 crash,且AVAudioSession是与硬件关联的,不管是你的APP其他地方重新给设置了,还是其他的 APP设置了 AVAudioSession 的Category, Option 都会影响
四、语音转文字
系统自带的 Speech Kit 实现语音识别能识别语音的内容,SFSpeechRecognizer 需要在有网下才能识别
简单认识语音识别相关的类
SFSpeechRecognizer(语音识别器):负责处理语音识别任务,它将录制音频,并将其发送到苹果的服务器进行处理。它提供了语音识别请求创建,支持的语音识别语言功能。
SFSpeechAudioBufferRecognitionRequest(语音识别请求) :继承自 SFSpeechRecognitionRequest
它代表了一个语音识别请求。它定义了识别任务的参数。
SFSpeechRecognitionTask(语音识别任务):它来控制和管理识别任务的执行,控制任务的暂停、取消和结束。
SFSpeechRecognitionResult(语音识别结果): 它包含了识别出的文本、置信度分数等信息。
语音转文字的实现步骤
-
申请权限
info.plist 中配置权限申请说明, 代码中申请权限
Privacy - Speech Recognition Usage Description
swiftSFSpeechRecognizer.requestAuthorization { [weak self] authStatus in switch authStatus { case .authorized: // 用户已授权,可以开始语音识别 case .denied, .restricted: // 用户未授权或不可用,处理错误情况 case .notDetermined: // 未授权 }
-
导入头文件,创建实例
swiftimport Speech let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")) let request = SFSpeechAudioBufferRecognitionRequest()
-
音频导入到 语音识别请求
AVAudioEngine
的录入的音频 buffer 加入到SFSpeechAudioBufferRecognitionRequest
实例中。swiftaudioEngine.inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(bufferSize), format: audioEngine.inputNode.outputFormat(forBus: 0)) { [weak self] (buffer, time) in self?.request.append(buffer) }
-
开启语音识别任务
SFSpeechRecognizer
的实例开启语音识别,并返回一个SFSpeechRecognitionTask
, resultHandler 参数中返回识别的结果(SFSpeechRecognitionResult
)swiftrecognitionTask = speechRecognizer?.recognitionTask(with: request, resultHandler: { [weak self] result, error in // request 是 SFSpeechRecognitionResult 的实例 })
-
取出语音识别的结果
SFSpeechRecognitionResult
内其实可能返回多个结果(用var transcriptions:[SFTranscription]
接收 ),我们通常用最可信的那一个var bestTranscription: SFTranscription
。swift//取出识别的文字结果 let transcription = result.bestTranscription.formattedString
注意点:
- 必须有网才能实现语音识别
- 录入的音频的语言和 SFSpeechRecognizer 不一致,会识别不出来