使用 SFSpeechRecognizer 后 AVSpeechSynthesizer 不说话
AVSpeechSynthesizer does not speak after using SFSpeechRecognizer
所以我构建了一个简单的应用程序,它使用 SFSpeechRecognizer 进行语音识别,并在屏幕上的 UITextView 中将转换后的语音显示为文本。现在我正在尝试让 phone 说出显示的文本。由于某种原因它不起作用。 AVSpeechSynthesizer 说话功能仅在使用 SFSpeechRecognizer 之前有效。例如,当应用程序启动时,它会在 UITextView 中显示一些欢迎文本,如果我点击说话按钮,phone 将说出欢迎文本。然后如果我录制(用于语音识别),识别的语音将显示在 UITextView 中。现在我想让 phone 说出那个文本,但不幸的是它没有。
这是代码
import UIKit
import Speech
import AVFoundation
class ViewController: UIViewController, SFSpeechRecognizerDelegate, AVSpeechSynthesizerDelegate {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var microphoneButton: UIButton!
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
override func viewDidLoad() {
super.viewDidLoad()
microphoneButton.isEnabled = false
speechRecognizer.delegate = self
SFSpeechRecognizer.requestAuthorization { (authStatus) in
var isButtonEnabled = false
switch authStatus {
case .authorized:
isButtonEnabled = true
case .denied:
isButtonEnabled = false
print("User denied access to speech recognition")
case .restricted:
isButtonEnabled = false
print("Speech recognition restricted on this device")
case .notDetermined:
isButtonEnabled = false
print("Speech recognition not yet authorized")
}
OperationQueue.main.addOperation() {
self.microphoneButton.isEnabled = isButtonEnabled
}
}
}
@IBAction func speakTapped(_ sender: UIButton) {
let string = self.textView.text
let utterance = AVSpeechUtterance(string: string!)
let synthesizer = AVSpeechSynthesizer()
synthesizer.delegate = self
synthesizer.speak(utterance)
}
@IBAction func microphoneTapped(_ sender: AnyObject) {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
microphoneButton.isEnabled = false
microphoneButton.setTitle("Start Recording", for: .normal)
} else {
startRecording()
microphoneButton.setTitle("Stop Recording", for: .normal)
}
}
func startRecording() {
if recognitionTask != nil { //1
recognitionTask?.cancel()
recognitionTask = nil
}
let audioSession = AVAudioSession.sharedInstance() //2
do {
try audioSession.setCategory(AVAudioSessionCategoryRecord)
try audioSession.setMode(AVAudioSessionModeMeasurement)
try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't set because of an error.")
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest() //3
guard let inputNode = audioEngine.inputNode else {
fatalError("Audio engine has no input node")
} //4
guard let recognitionRequest = recognitionRequest else {
fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
} //5
recognitionRequest.shouldReportPartialResults = true //6
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in //7
var isFinal = false //8
if result != nil {
self.textView.text = result?.bestTranscription.formattedString //9
isFinal = (result?.isFinal)!
}
if error != nil || isFinal { //10
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
self.microphoneButton.isEnabled = true
}
})
let recordingFormat = inputNode.outputFormat(forBus: 0) //11
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare() //12
do {
try audioEngine.start()
} catch {
print("audioEngine couldn't start because of an error.")
}
textView.text = "Say something, I'm listening!"
}
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
}
问题是当您启动语音识别时,您已将音频会话类别设置为录制。您无法使用录制的音频会话播放任何音频(包括语音合成)。
您应该将 startRecording
方法的这一行更改为:
try audioSession.setCategory(AVAudioSessionCategoryRecord)
至:
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
试试这个:
audioSession.setCategory(AVAudioSessionCategoryRecord)
请使用以下代码解决问题:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
try audioSession.setMode(AVAudioSessionModeDefault)
} catch {
print("audioSession properties weren't set because of an error.")
}
Here, we have to use the above code in the following way:
@IBAction func microphoneTapped(_ sender: AnyObject) {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
try audioSession.setMode(AVAudioSessionModeDefault)
} catch {
print("audioSession properties weren't set because of an error.")
}
microphoneButton.isEnabled = false
microphoneButton.setTitle("Start Recording", for: .normal)
} else {
startRecording()
microphoneButton.setTitle("Stop Recording", for: .normal)
}
}
在这里,停止 audioengine 后,我们将 audioSession Category 设置为 AVAudioSessionCategoryPlayback 和 audioSession 模式 到
AVAudioSessionModeDefault。然后当你调用下一个文本转语音方法时,它将正常工作。
使用STT时,需要这样设置:
AVAudioSession *avAudioSession = [AVAudioSession sharedInstance];
if (avAudioSession) {
[avAudioSession setCategory:AVAudioSessionCategoryRecord error:nil];
[avAudioSession setMode:AVAudioSessionModeMeasurement error:nil];
[avAudioSession setActive:true withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
}
使用 TTS 时再次设置 AudioSession,如下所示:
[regRequest endAudio];
AVAudioSession *avAudioSession = [AVAudioSession sharedInstance];
if (avAudioSession) {
[avAudioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[avAudioSession setMode:AVAudioSessionModeDefault error:nil];
}
它非常适合我。
LOW AUDIO问题也解决了。
所以我构建了一个简单的应用程序,它使用 SFSpeechRecognizer 进行语音识别,并在屏幕上的 UITextView 中将转换后的语音显示为文本。现在我正在尝试让 phone 说出显示的文本。由于某种原因它不起作用。 AVSpeechSynthesizer 说话功能仅在使用 SFSpeechRecognizer 之前有效。例如,当应用程序启动时,它会在 UITextView 中显示一些欢迎文本,如果我点击说话按钮,phone 将说出欢迎文本。然后如果我录制(用于语音识别),识别的语音将显示在 UITextView 中。现在我想让 phone 说出那个文本,但不幸的是它没有。
这是代码
import UIKit
import Speech
import AVFoundation
class ViewController: UIViewController, SFSpeechRecognizerDelegate, AVSpeechSynthesizerDelegate {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var microphoneButton: UIButton!
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
override func viewDidLoad() {
super.viewDidLoad()
microphoneButton.isEnabled = false
speechRecognizer.delegate = self
SFSpeechRecognizer.requestAuthorization { (authStatus) in
var isButtonEnabled = false
switch authStatus {
case .authorized:
isButtonEnabled = true
case .denied:
isButtonEnabled = false
print("User denied access to speech recognition")
case .restricted:
isButtonEnabled = false
print("Speech recognition restricted on this device")
case .notDetermined:
isButtonEnabled = false
print("Speech recognition not yet authorized")
}
OperationQueue.main.addOperation() {
self.microphoneButton.isEnabled = isButtonEnabled
}
}
}
@IBAction func speakTapped(_ sender: UIButton) {
let string = self.textView.text
let utterance = AVSpeechUtterance(string: string!)
let synthesizer = AVSpeechSynthesizer()
synthesizer.delegate = self
synthesizer.speak(utterance)
}
@IBAction func microphoneTapped(_ sender: AnyObject) {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
microphoneButton.isEnabled = false
microphoneButton.setTitle("Start Recording", for: .normal)
} else {
startRecording()
microphoneButton.setTitle("Stop Recording", for: .normal)
}
}
func startRecording() {
if recognitionTask != nil { //1
recognitionTask?.cancel()
recognitionTask = nil
}
let audioSession = AVAudioSession.sharedInstance() //2
do {
try audioSession.setCategory(AVAudioSessionCategoryRecord)
try audioSession.setMode(AVAudioSessionModeMeasurement)
try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't set because of an error.")
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest() //3
guard let inputNode = audioEngine.inputNode else {
fatalError("Audio engine has no input node")
} //4
guard let recognitionRequest = recognitionRequest else {
fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
} //5
recognitionRequest.shouldReportPartialResults = true //6
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in //7
var isFinal = false //8
if result != nil {
self.textView.text = result?.bestTranscription.formattedString //9
isFinal = (result?.isFinal)!
}
if error != nil || isFinal { //10
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
self.microphoneButton.isEnabled = true
}
})
let recordingFormat = inputNode.outputFormat(forBus: 0) //11
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare() //12
do {
try audioEngine.start()
} catch {
print("audioEngine couldn't start because of an error.")
}
textView.text = "Say something, I'm listening!"
}
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
}
问题是当您启动语音识别时,您已将音频会话类别设置为录制。您无法使用录制的音频会话播放任何音频(包括语音合成)。
您应该将 startRecording
方法的这一行更改为:
try audioSession.setCategory(AVAudioSessionCategoryRecord)
至:
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
试试这个:
audioSession.setCategory(AVAudioSessionCategoryRecord)
请使用以下代码解决问题:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
try audioSession.setMode(AVAudioSessionModeDefault)
} catch {
print("audioSession properties weren't set because of an error.")
}
Here, we have to use the above code in the following way:
@IBAction func microphoneTapped(_ sender: AnyObject) {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
try audioSession.setMode(AVAudioSessionModeDefault)
} catch {
print("audioSession properties weren't set because of an error.")
}
microphoneButton.isEnabled = false
microphoneButton.setTitle("Start Recording", for: .normal)
} else {
startRecording()
microphoneButton.setTitle("Stop Recording", for: .normal)
}
}
在这里,停止 audioengine 后,我们将 audioSession Category 设置为 AVAudioSessionCategoryPlayback 和 audioSession 模式 到 AVAudioSessionModeDefault。然后当你调用下一个文本转语音方法时,它将正常工作。
使用STT时,需要这样设置:
AVAudioSession *avAudioSession = [AVAudioSession sharedInstance];
if (avAudioSession) {
[avAudioSession setCategory:AVAudioSessionCategoryRecord error:nil];
[avAudioSession setMode:AVAudioSessionModeMeasurement error:nil];
[avAudioSession setActive:true withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
}
使用 TTS 时再次设置 AudioSession,如下所示:
[regRequest endAudio];
AVAudioSession *avAudioSession = [AVAudioSession sharedInstance];
if (avAudioSession) {
[avAudioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[avAudioSession setMode:AVAudioSessionModeDefault error:nil];
}
它非常适合我。 LOW AUDIO问题也解决了。