I'm trying to play sounds with different effects. In a previous viewController I record a sound, then in the next screen, it can be played with the effects. First time it works ok but the second time it crashes with error as follows:
2015-08-07 13:00:45.900 Pitch Perfect[9643:1121173] 13:00:45.900 ERROR: AVAudioEngine.mm:253: AttachNode: required condition is false: !nodeimpl->HasEngineImpl() 2015-08-07 13:00:45.953 Pitch Perfect[9643:1121173] Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: !nodeimpl->HasEngineImpl()'
import UIKit
import AVFoundation
class PlaySoundsViewController: UIViewController, AVAudioPlayerDelegate {
var receivedAudio:RecordedAudio!
var audioPlayer: AVAudioPlayer!
var disabledButton:UIButton!
var firstTime:Bool = true
var audioEngine:AVAudioEngine!
var audioFile:AVAudioFile!
var audioPlayerNode:AVAudioPlayerNode!
var audioStopped:Bool!
var typeOfSound:IntegerLiteralType!
@IBOutlet weak var stopButton: UIButton!
@IBOutlet weak var reverbButton: UIButton!
@IBOutlet weak var echoButton: UIButton!
@IBOutlet weak var darthButton: UIButton!
@IBOutlet weak var chipmonkButton: UIButton!
@IBOutlet weak var snailButton: UIButton!
@IBOutlet weak var rabbitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AVAudioPlayer(contentsOfURL: receivedAudio.filePathUrl, error: nil)
audioPlayer.enableRate=true
audioPlayer.delegate=self
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayback, error: nil)
audioPlayerNode=AVAudioPlayerNode();
audioEngine = AVAudioEngine()
audioFile = AVAudioFile(forReading: receivedAudio.filePathUrl, error: nil)
audioStopped=true;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func playAnimalSound(animal: String) {
if audioStopped==false {
resetAudio(typeOfSound)
}
typeOfSound = 1
audioPlayer.currentTime=0
switch animal {
case "snail":
audioPlayer.rate=0.5
case "rabbit":
audioPlayer.rate=2.0
default:
showMessage("Sound not found. How can it be?")
}
audioPlayer.play()
stopButton.hidden=false
stopButton.enabled=true
}
@IBAction func playSnailSound(sender: UIButton) {
highlightButton(sender)
playAnimalSound("snail")
}
@IBAction func playRabbitSound(sender: UIButton) {
highlightButton(sender)
playAnimalSound("rabbit")
}
func soundEnded() {
stopButton.hidden=true
disabledButton.enabled=true;
if(audioEngine.running) {
audioEngine.stop()
audioEngine.reset();
}
}
func playAudioWithVariablePitch(pitch: Float, type: String) {
if audioStopped==false {
resetAudio(typeOfSound)
}
audioEngine.attachNode(audioPlayerNode)
switch type {
case "normal":
typeOfSound = 2
var changePitchEffect = AVAudioUnitTimePitch()
changePitchEffect.pitch = pitch
audioEngine.attachNode(changePitchEffect)
audioEngine.connect(audioPlayerNode, to: changePitchEffect, format: nil)
audioEngine.connect(changePitchEffect, to: audioEngine.outputNode, format: nil)
case "reverb":
typeOfSound = 3
var changeReverbEffect = AVAudioUnitReverb()
changeReverbEffect.loadFactoryPreset(AVAudioUnitReverbPreset(rawValue: 4)!)
changeReverbEffect.wetDryMix=50;
audioEngine.attachNode(changeReverbEffect)
audioEngine.connect(audioPlayerNode, to: changeReverbEffect, format: nil)
audioEngine.connect(changeReverbEffect, to: audioEngine.outputNode, format: nil)
case "delay":
typeOfSound = 3
var changeDelayEffect = AVAudioUnitDelay()
audioEngine.attachNode(changeDelayEffect)
audioEngine.connect(audioPlayerNode, to: changeDelayEffect, format: nil)
audioEngine.connect(changeDelayEffect, to: audioEngine.outputNode, format: nil)
default:
showMessage("oops, there was an internal problem. Never mind")
}
audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: soundEnded)
audioEngine.startAndReturnError(nil)
stopButton.hidden=false
stopButton.enabled=true
audioPlayerNode.play()
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
if flag {
stopButton.hidden=true
disabledButton.enabled=true;
audioStopped=true
println("I hid stopButton and enabled the disabled one")
}
}
@IBAction func playDelaySound(sender: UIButton) {
highlightButton(sender)
playAudioWithVariablePitch(0, type: "delay")
}
@IBAction func playReverbSound(sender: UIButton) {
highlightButton(sender)
playAudioWithVariablePitch(0, type: "reverb")
}
@IBAction func playChipmunkSound(sender: UIButton) {
highlightButton(sender)
playAudioWithVariablePitch(1000.0, type: "normal")
}
@IBAction func playDarthVaderSound(sender: UIButton) {
highlightButton(sender)
playAudioWithVariablePitch(-900.0, type: "normal")
}
@IBAction func stopPlaying(sender: UIButton) {
resetAudio(typeOfSound)
stopButton.hidden=true
stopButton.enabled=false;
disabledButton.enabled=true;
}
func highlightButton(button: UIButton) {
if firstTime {
firstTime=false
} else {
disabledButton.enabled=true;
}
button.enabled=false;
disabledButton=button;
}
func resetAudio(type: IntegerLiteralType) {
switch type {
case 1 :
audioPlayer.stop()
println("case 1")
case 2 :
println("case 2")
if audioEngine.running {
audioEngine.stop()
}
audioEngine.reset()
case 3 :
audioEngine.stop()
default:
break
}
audioStopped=true;
}
func showMessage(msg: String) {
var message=UIAlertView(title: "Alert", message: msg, delegate: nil, cancelButtonTitle: "ok I won't panic")
}
}
Does anybody know why it crashes? I have researched the AVAudioEngine, AVAudioPlayer and AVAudioPlayerNode classes with no results.
Thanks
I know this is an old issue, but I didn't see the correct answer above.
The reason why it crashes is actually outlined in the error message:
AttachNode: required condition is false: !nodeimpl->HasEngineImpl()
In other words, when attaching a node it is mandatory that that node is not already attached to an engine (!nodeimpl->HasEngineImpl()
).
The solution is to remove the node using audioEngine.detachNode
before attempting to add it again.
Finally the crashed was caused by initializing the audioPlayerNode and the audioEngine objects in the viewDidLoad function. They need to be instantiated every time you use them, apparently, or maybe after being stopped and reset. Placing those lines in the beginning of the playAudioWithVariablePitch function directly instead of in the viewDidLoad function, solved the crash problem. I still have a problem with the playback of the pitched, reverb and echo sounds. They get cut before it's due and I still don't know why. It has to do with the completionHandler of the audioPlayerNode.scheduleFile method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With