AVSpeechSynthesizer 检测语音何时结束

AVSpeechSynthesizer detect when the speech is finished

我只是不知道该怎么做...

我在这里和 google 搜索,人们谈论 AVSpeechSynthesizerDelegate,但我无法使用它。

我想在演讲结束时运行一个函数。

我怎样才能做到这一点?如果一定要用delegate,怎么办?

我这样试过:

func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
    falando = false
    print("FINISHED")
}

这是我在开发人员的文档上找到的功能之一,虽然讲了讲,但没有打印任何内容。

我试着把 Class A : AVSpeechSynthesizerDelegate 那么我会做 Speech.delegate = self (语音是 A 的一个属性,属于 AVSpeechSynthesizer 类型)但它说 A 不符合协议NSObjectProtocol.

如何在演讲结束后立即运行执行某些功能(甚至打印)?

谢谢!

A does not conform to protocol NSObjectProtocol表示你的class必须继承自NSObject,你可以阅读更多相关内容here.

现在我不知道你是如何构建你的代码的,但这个小例子似乎对我有用。首先是一个非常简单的 class ,它包含 AVSpeechSynthesizer:

class Speaker: NSObject {
    let synth = AVSpeechSynthesizer()

    override init() {
        super.init()
        synth.delegate = self
    }

    func speak(_ string: String) {
        let utterance = AVSpeechUtterance(string: string)
        synth.speakUtterance(utterance)
    }
}

注意我在这里设置委托(在 init 方法中)并注意它必须继承自 NSObject 以使编译器满意(非常重要!)

然后是实际的委托方法:

extension Speaker: AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        print("all done")
    }
}

最后,我可以在这里使用 class,就像这样:

class ViewController: UIViewController {
    let speaker = Speaker()

    @IBAction func buttonTapped(sender: UIButton) {
        speaker.speak("Hello world")
    }
}

奖励我

all done

AVSpeechSynthesizer 停止说话时在我的控制台中。

希望对你有所帮助。

更新

所以,时间过去了,@case-silva 在下面的评论中询问是否有实际示例,@dima-gershman 建议直接在 ViewController 中使用 AVSpeectSynthesizer

为了兼顾两者,我在这里制作了一个简单的 ViewController 示例,其中包含 UITextFieldUIButton

流量为:

  1. 您在文本字段中输入一些文本(如果没有,将设置默认值)
  2. 你按下按钮
  3. 按钮被禁用并且背景颜色被更改(抱歉,这是我能想到的最好的:))
  4. 语音结束后,按钮启用,文本字段被清除,背景颜色再次更改。

这是它的样子

一个简单的UIViewController例子

import UIKit
import AVFoundation

class ViewController: UIViewController {

    //MARK: Outlets
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var speakButton: UIButton!

    let synth = AVSpeechSynthesizer()

    override func viewDidLoad() {
        super.viewDidLoad()
        synth.delegate = self
    }

    @IBAction func speakButtonTapped(_ sender: UIButton) {
        //We're ready to start speaking, disable UI while we're speaking
        view.backgroundColor = .darkGray
        speakButton.isEnabled = false
        let inputText = textField.text ?? ""
        let textToSpeak = inputText.isEmpty ? "Please enter some text" : inputText

        let speakUtterance = AVSpeechUtterance(string: textToSpeak)
        synth.speak(speakUtterance)
    }
}

extension ViewController: AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        //Speaking is done, enable speech UI for next round
        speakButton.isEnabled = true
        view.backgroundColor = .lightGray
        textField.text = ""
    }
}

希望能给你一个线索案例。

// import AVFoundation
import AVFoundation
// Adhere to AVSpeechSynthesizerDelegate
class GetDirectionsViewController: UIViewController, AVSpeechSynthesizerDelegate {
// define a speech session
let speechsynthesizer = AVSpeechSynthesizer()
// set the delegate
speechsynthesizer.delegate = self
// Implement didFinish. 
// This is called when speechsynthesizer completes speech
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        print("Speech finished")
    }
// Done!

函数viewDidAppear仅作为示例使用,相同的代码可以根据需要放在任何地方:

class MyViewController: UIViewController, AVSpeechSynthesizerDelegate {

    var synth = AVSpeechSynthesizer()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // make sure to set the delegate before "speaking"
        synth.delegate = self

        let utterance = AVSpeechUtterance(string: "Hello world!")
        synth.speak(utterance)
    }

    // will be called when speech did finish
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        // do something useful here ...
    }
}

SwiftUI

创建一个继承自 NSObjectObservableObjectSpeaker class。

internal class Speaker: NSObject, ObservableObject {
    internal var errorDescription: String? = nil
    private let synthesizer: AVSpeechSynthesizer = AVSpeechSynthesizer()
    @Published var isSpeaking: Bool = false
    @Published var isShowingSpeakingErrorAlert: Bool = false

    override init() {
        super.init()
        self.synthesizer.delegate = self
    }

    internal func speak(_ text: String, language: String) {
        do {
            let utterance = AVSpeechUtterance(string: text)
            utterance.voice = AVSpeechSynthesisVoice(language: language)
            
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
            self.synthesizer.speak(utterance)
        } catch let error {
            self.errorDescription = error.localizedDescription
            isShowingSpeakingErrorAlert.toggle()
        }
    }
    
    internal func stop() {
        self.synthesizer.stopSpeaking(at: .immediate)
    }
}

扩展它并实现必要的委托方法。

extension Speaker: AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
        self.isSpeaking = true
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
        self.isSpeaking = false
        try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        self.isSpeaking = false
        try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
    }
}

使用 StateObject 包装器将 Speaker 添加到必要的视图。

struct ContentView: View {
    let text: String = "Hello World!"
    @StateObject var speaker: Speaker = Speaker()
    
    var body: some View {
        HStack {
            Text(text)
            Spacer()
            Button(action: {
                if self.speaker.isSpeaking {
                    speaker.stop()
                } else {
                    speaker.speak(text, language: "en-US")
                }
            }) {
                Image(systemName: self.speaker.isSpeaking ? "stop.circle" : "speaker.wave.2.circle")
                    .resizable()
                    .frame(width: 30, height: 30)
            }
            .buttonStyle(BorderlessButtonStyle())
            .alert(isPresented: $speaker.isShowingSpeakingErrorAlert) {
                Alert(title: Text("Pronunciation error", comment: "Pronunciation error alert title."), message: Text(speaker.errorDescription ?? ""))
            }
        }
        .padding()
    }
}

我制作了应用程序并为我找到了这个解决方案。

func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer,
                           willSpeakRangeOfSpeechString characterRange: NSRange,
                           utterance: AVSpeechUtterance) {
        progress = Float(characterRange.location + characterRange.length)
                       / Float(utterance.speechString.count)
        speechCount = Float(utterance.speechString.count)

        self.soundSliderView.setValue(progress, animated: true)
}

完成后可以使用“进度值”

func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        if progress >= 1.0 {
            // finis playing with end speaking
            } else {
            // finis playing with stopping
        }
 }