I'm trying to create an app that, when the user doesn't know, says a text that changes every levels. The goal is to say a sentence displayed (yes, it is made for kids):
@IBAction func dontknow(_ sender: Any) {
let utterance = AVSpeechUtterance(string: textLabel.text)
utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")
utterance.rate = 0.4
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
}
The app is constructed like this: if user doesn't know -> he can click the button to say the text else if he is right -> he go to the next level.
The first time he enter the button to say, the app says something, but when the user tries to say the text and for the next level he clicks the button to say, nothing happens. It just throws this error: Failure starting audio queue ≥˚˛ˇ
Full code:
import UIKit
import AVFoundation
import Speech
class ReadViewController: UIViewController, SFSpeechRecognizerDelegate {
var texts = ["Je mange des pâtes", "Bonjour Jean comment vas-tu", "Qui est-ce", "J'en ai marre", "Je ne te trouve pas gentil", "Pourquoi tu ne veux pas","Tu es si gentil", "Tu es beau", "Dans combien de temps", "Tu as fait de beaux rêves", "Cette application est une révolution"];
var text = ""
var transcriptedText = "";
var effect:UIVisualEffect!
@IBOutlet weak var dontknowButton: UIButton!
@IBOutlet weak var listenButton: UIButton!
@IBOutlet weak var visualEffectView: UIVisualEffectView!
@IBOutlet var alertView: UIView!
@IBOutlet weak var regameButton: UIButton!
@IBOutlet weak var textView: UILabel!
@IBOutlet weak var textLabel: UILabel!
@IBOutlet weak var microphoneButton: UIButton!
@IBOutlet weak var transci: UILabel!
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "fr-FR")) //1
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
var recordingSession: AVAudioSession!
var player: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
listenButton.layer.cornerRadius = 10
dontknowButton.layer.cornerRadius = dontknowButton.frame.width / 2
self.restart()
// Do any additional setup after loading the view.
effect = visualEffectView.effect
visualEffectView.layer.opacity = 0
visualEffectView.effect = nil
regameButton.layer.cornerRadius = 10
microphoneButton.layer.cornerRadius = 10
microphoneButton.isEnabled = false //2
speechRecognizer?.delegate = self //3
SFSpeechRecognizer.requestAuthorization { (authStatus) in //4
var isButtonEnabled = false
switch authStatus { //5
case .authorized:
isButtonEnabled = true
case .denied:
isButtonEnabled = false
self.alert()
case .restricted:
isButtonEnabled = false
self.alert()
case .notDetermined:
isButtonEnabled = false
print("Speech recognition not yet authorized")
}
OperationQueue.main.addOperation() {
self.microphoneButton.isEnabled = isButtonEnabled
}
// self.effect = self.visualEffectView.effect
// self.visualEffectView.effect = nil
}
}
func alert () {
let alertController = UIAlertController (title: "Désolé", message: "Pour le bon fonctionnement de l'application, vous devez activer la reconnaissance vocale dans les réglages.", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Réglages", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("Settings opened: \(success)") // Prints true
})
}
}
alertController.addAction(settingsAction)
present(alertController, animated: true, completion: nil)
}
func restart() {
var randomNumber = random(0..<(texts.count))
text = texts[randomNumber]
textLabel.text = "\(text)"
}
func startRecording() {
if recognitionTask != nil {
recognitionTask?.cancel()
recognitionTask = nil
}
let audioSession = AVAudioSession.sharedInstance()
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()
var inputNode = audioEngine.inputNode
guard let recognitionRequest = recognitionRequest else {
fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
}
recognitionRequest.shouldReportPartialResults = true
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
var isFinal = false
if result != nil {
self.transcriptedText = (result?.bestTranscription.formattedString)!
self.transci.text = self.transcriptedText
isFinal = (result?.isFinal)!
}
if error != nil || isFinal {
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
self.transci.text = ""
self.microphoneButton.isEnabled = true
}
})
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch {
print("audioEngine couldn't start because of an error.")
}
}
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func random(_ range:Range<Int>) -> Int
{
return range.lowerBound + Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound)))
}
@IBAction func start(_ sender: Any) {
if audioEngine.isRunning {
if self.transcriptedText == self.text {
// When user won
self.restart()
}
else {
// When user loses
animateIn()
}
audioEngine.stop()
recognitionRequest?.endAudio()
microphoneButton.isEnabled = false
microphoneButton.setTitle("Commencer", for: .normal)
} else {
startRecording()
microphoneButton.setTitle("Arrêter", for: .normal)
}
}
@IBAction func listen(_ sender: Any) {
let utterance = AVSpeechUtterance(string: "wesh")
utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")!
utterance.rate = 0.4
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
}
@IBAction func reGameOn(_ sender: Any) {
animateOut()
}
@IBAction func dontknow(_ sender: Any) {
var randomNumber = random(0..<(texts.count))
var tet = texts[randomNumber]
let utterance = AVSpeechUtterance(string: textLabel.text)
utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")!
utterance.rate = 0.4
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
print(synthesizer.isSpeaking)
print(synthesizer.isPaused)
}
func animateIn() {
self.view.addSubview(alertView)
alertView.center = self.view.center
alertView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
alertView.alpha = 0
UIView.animate(withDuration: 0.4) {
self.alertView.layer.cornerRadius = 10
self.visualEffectView.layer.opacity = 1
self.visualEffectView.effect = self.effect
self.alertView.alpha = 1
self.alertView.transform = CGAffineTransform.identity
}
}
func animateOut () {
UIView.animate(withDuration: 0.3, animations: {
self.alertView.layer.cornerRadius = 0
self.alertView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
self.alertView.alpha = 0
self.visualEffectView.effect = nil
self.visualEffectView.layer.opacity = 0
}) { (success:Bool) in
self.alertView.removeFromSuperview()
}
}
}
In @IBAction func dontKnow try adding the following code in the end. This may work.
do{
let _ = try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback,
with: .duckOthers)
}catch{
print(error)
}