swiftavaudioplayeravaudioengine

Playing a sound with AVAudioPlayer after AVAudioEngine has been utilized for input


I have an app that involves an Alexa like digital assistant.

We are successfully receiving input for our Speech-To-Text engine with AVAudioEngine, and then using NLP we are interpreting that text into requests and responding.

Once we receive a command, we acknowledge that command with audio, played through AVAudioPlayer.

This wasn't a problem on our simulators, but on device we have noticed that if we play a sound any time after the AVAudioEngine has been initialized, the audio does not play on the device. The functions do not fail, and our audioPlayerDidFinishPlaying fires at the appropriate time, but no audio is heard.

If we play audio when the app first launches without initializing the recording, the audio plays fine. If we stop the recording and wait one to many seconds later to play an audio file, no audio is ever heard.

We need to be able to stop the input when a command is recognized, play an audio file in response, and then resume recording.

What are we doing wrong?

AVAudioEngine instantiation and de-initialization:

Instantiation:

let audioSession = AVAudioSession.sharedInstance()

do {
    try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
    try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
} catch {
    validatePermissions()
}

let inputNode = audioEngine.inputNode

De-initialization:

public func stopListening() {
    if audioEngine.isRunning {
        audioEngine.stop()
    }
    //Other code to stop recognition service
}

Audio Player:

func playAudio() {
    let path = Bundle.main.path(forResource: fileName, ofType:nil)!
    let url = URL(fileURLWithPath: path)

    do {
        myAudioPlayer = try AVAudioPlayer(contentsOf: url)
        myAudioPlayer.delegate = self
        myAudioPlayer.play()
    } catch {
        print("Error playing audio")
    }
}

Solution

  • The problem is with the audioSession, with .record it prevents other audio outputs. You can use .playAndRecord instead of .record:

    try audioSession.setCategory(.playAndRecord, mode: .measurement, options: .duckOthers)
    

    You can edit audioSession in your stopListening method if you need a value of .record while listening.