Search code examples
swiftavaudioplayeravaudioengine

Swift - AVAudioPlayer doesn't work properly


I have the following code :

let speechRecognizer = SFSpeechRecognizer()!
let audioEngine = AVAudioEngine()
var recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
var recognitionTask = SFSpeechRecognitionTask()
var audioPlayer : AVAudioPlayer!

override func viewDidLoad() {
    super.viewDidLoad()
    playSound(sound: "oops")
    speechRecognizer.delegate = self
    requestSpeechAuth()

}

func requestSpeechAuth(){
    SFSpeechRecognizer.requestAuthorization { (authStatus) in
        OperationQueue.main.addOperation({ 
            switch authStatus {
            case.authorized:
                print("authorized")
            case.denied:
                print("denied")
            case.restricted:
                print("restricted")
            case.notDetermined:
                print("not determined")
            }
        })

    }
}

// Function called when I press on my record button
func SpeechButtonDown() {
    print("Start recording")

    if audioEngine.isRunning {

        endRecording() {

    } else {
       do {

        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(AVAudioSessionCategoryRecord)
        try audioSession.setMode(AVAudioSessionModeMeasurement)
        try audioSession.setActive(true, with: .notifyOthersOnDeactivation)

        if let inputNode = audioEngine.inputNode {

            recognitionRequest.shouldReportPartialResults = true

            recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
                print("1")
                if let result = result {
                    self.instructionLabel.text = result.bestTranscription.formattedString
                    print("2")
                    if result.isFinal {
                        self.audioEngine.stop()
                        inputNode.removeTap(onBus: 0)
                        if self.instructionLabel.text != "" {
                            self.compareWordwithVoice()
                        }
                    }   
                }
            })

            let recognitionFormat = inputNode.outputFormat(forBus: 0)

            inputNode.installTap(onBus: 0, bufferSize: 1024, format: recognitionFormat, block: { (buffer, when) in
                self.recognitionRequest.append(buffer)
            })

            audioEngine.prepare()

                try audioEngine.start()
       }
    } catch {

    } 
    }
}

// Function called when I release the record button
func EndRecording() {
    endRecording()
    print("Stop recording")
}

func endRecording() {
    audioEngine.stop()
    recognitionRequest.endAudio()
    audioEngine.inputNode?.removeTap(onBus: 0)
}

func playSound(sound: String) {
    if let url = Bundle.main.url(forResource: sound, withExtension: "wav") {
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url)
            guard let player = audioPlayer else { return }
            player.prepareToPlay()
            player.play()
            print("tutu")
        } catch let error {
            print(error.localizedDescription)
        }
    }
}

func compareWordwithVoice() {

    let StringToLearn = setWordToLearn()
    print("StringToLearn : \(StringToLearn)")
    if let StringRecordedFull = instructionLabel.text{
        let StringRecorded = (StringRecordedFull as NSString).replacingOccurrences(of: " ", with: "").lowercased()
    print("StringRecorded : \(StringRecorded)")
        if StringRecorded == "appuyezsurleboutonendessousetprenoncezl’expression" {
            print("not yet")
        } else {
            if StringToLearn == StringRecorded {

        playSound(sound: "success")
        print("success")
        // update UI
    } else {
        playSound(sound: "oops")
        print("oops")
        // update UI
    }
        }

    }
}

 func setWordToLearn() -> String {
    if let wordToLearnFull = expr?.expression {
        print(wordToLearnFull)
        var wordToLearn = (wordToLearnFull as NSString).replacingOccurrences(of: " ", with: "").lowercased()
        wordToLearn = (wordToLearn as NSString).replacingOccurrences(of: ".", with: "")
        wordToLearn = (wordToLearn as NSString).replacingOccurrences(of: "!", with: "")
        wordToLearn = (wordToLearn as NSString).replacingOccurrences(of: "?", with: "")
        wordToLearn = (wordToLearn as NSString).replacingOccurrences(of: ",", with: "")
        wordToLearn = (wordToLearn as NSString).replacingOccurrences(of: "/", with: "")
        print(wordToLearn)
        return wordToLearn
    }
    print("no wordToLearn")
    return ""

}

The problem is that the playSound works perfectly when it is in the viewDidLoad but doesn't work when it is called by the compareThing() function but it display "tutu" on both cases so it performs the playSound function every time.

Can the problem be if AVAudioPlayer and AVAudioEngine cannot work at the same time ?

Thx


Solution

  • Ive experienced the same thing with my code and from searching online it seems like there is an unspoken bug "when using AvAudioPlayer and Engine separately"

    I got the information from the following link. I did not find anything else online that states why this bug happens though. https://swiftios8dev.wordpress.com/2015/03/05/sound-effects-using-avaudioengine/

    The suggestion was to use AVAudioEngine for everything.