why currentTime and duration of AVAudioPlayer are nil?
Recently faced with a task when creating a player
link to the mp3 working, checked
but at the same time track duration and currenttime nil
I try to change AVAudioPlayer to AVPlayer, but I have same problem
What I need to do to correct this?
Maybe I must to download this mp3 before read duration?
But it would be better to solve this problem without downloading mp3
import UIKit
import AVFoundation
class PlayerViewController: UIViewController {
//Instantiate the AVFoundation audio player class
var player: AVAudioPlayer?
//Timer for tracking the progress
var timer: Timer? = nil
#IBOutlet weak var bookCoverImageView: UIImageView!
#IBOutlet weak var timeSlider: CustomSlider!
#IBOutlet weak var timeFromStartLabel: UILabel!
#IBOutlet weak var remainingTimeLabel: UILabel!
#IBOutlet weak var previousButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var bookNameLabel: UILabel!
#IBOutlet weak var authorNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
_ = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateSlider), userInfo: nil, repeats: true)
do {
//Set the path to the audio file (comes from the bundle)
let path = URL(string: "https://firebasestorage.googleapis.com/v0/b/audio-summary-v3.appspot.com/o/%D0%92%D1%8B%D1%81%D1%82%D1%83%D0%BF%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B2%20%D1%81%D1%82%D0%B8%D0%BB%D0%B5%20TED%20-%20%D0%94%D0%B6%D0%B5%D1%80%D0%B5%D0%BC%D0%B8%20%D0%94%D0%BE%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD.mp3?alt=media&token=58cae883-36b8-445f-8338-cc04cd518eee")
//Unpacking the path string optional
if let unpackedPath = path {
try player = AVAudioPlayer(contentsOf: unpackedPath)
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
self.timeFromStartLabel.text = String(format: "%d:%02d", Int(self.player!.currentTime) / 60, Int(self.player!.currentTime) % 60)
self.remainingTimeLabel.text = String(format: "%d:%02d", Int(self.player!.duration - self.player!.currentTime) / 60, Int(self.player!.duration - self.player!.currentTime) % 60)
}
player!.play()
timer!.fire()
}
} catch {
print(error)
}
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
I have error here
timeSlider.maximumValue = Float(self.player!.duration)
}
#IBAction func playButtonTapped(_ sender: Any) {
if player!.isPlaying {
player?.stop()
playButton.setImage(UIImage(named: "play_button"), for: .normal)
}
else {
player?.play()
playButton.setImage(UIImage(named: "stop_button"), for: .normal)
}
}
#IBAction func timeSliderScrolling(_ sender: Any) {
player?.stop()
player?.currentTime = TimeInterval(timeSlider.value)
player?.prepareToPlay()
player?.play()
}
#objc func updateSlider() {
I have error here
timeSlider.value = Float(player!.currentTime)
}
}
Use AVPlayer over AVAudioPlayer so that you can observe the current play time with addPeriodicTimeObserver.
import UIKit
import AVFoundation
class ViewController: UIViewController {
// MARK: - Variables
var player: AVPlayer?
#IBAction func playAudio(_ sender: UIBarButtonItem) {
player?.play()
}
override func viewDidLoad() {
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "myAudio", ofType:"m4a") else {
debugPrint("File not found")
return
}
let audioURL = URL(fileURLWithPath: path)
let playerItem = AVPlayerItem(url: audioURL)
player = AVPlayer(playerItem: playerItem)
player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.player!.currentItem?.status == .readyToPlay {
let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
print("Current play time: \(time)")
}
}
}
}
Related
I'm trying to make a simple music player. I want the label to display the current position of the AVMIDIPlayer. With my code the label is only updated once at the very beginning:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player:AVMIDIPlayer = AVMIDIPlayer()
var playerTime:Double = 999 {
didSet {
label.text = String(playerTime)
}
}
#IBOutlet var label: UILabel!
#IBAction func Play(_ sender: Any) {
player.play()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do {
let audioPath = Bundle.main.path(forResource: “song”, ofType: "mid")
try player = AVMIDIPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL, soundBankURL: nil)
playerTime = player.currentPosition
}
catch {
}
}
}
What have I missed, please? Thank you. Scarlett
The reason the label isn’t updating is you’re setting the text in viewDidLoad which is called only once. Use a Timer to update the label.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player:AVMIDIPlayer = AVMIDIPlayer()
var playerTime:Double = 999 {
didSet {
label.text = String(playerTime)
}
}
var timer = Timer()
#IBOutlet var label: UILabel!
#IBAction func Play(_ sender: Any) {
player.play()
// this will execute every 0.1 seconds, allowing you to update the label.
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
self.playerTime += 0.1
let min = self.playerTime.truncatingRemainder(dividingBy: 3600)) / 60
let sec = self.playerTime.truncatingRemainder(dividingBy: 60)
self.label.text = String(format: "%02d:%02d", min, sec)
}
}
func stop() {
// when you stop playback
timer.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do {
let audioPath = Bundle.main.path(forResource: “song”, ofType: "mid")
try player = AVMIDIPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL, soundBankURL: nil)
playerTime = player.currentPosition
}
catch {
}
}
}
The idea is simple and I do not think that the question has been asked in the past.
I want to build a simple mp3 player.
some songs displayed in a collection view the user selects a song
segue to another view with options to play, pause or stop only issue
is when you go back to the home screen to select a new song with the
current still playing. It is impossible to deactivate the current
player. When you need to play the 2 songs, the 2 are playing together
I have tried a lot of things
- create a new instance of player (player = AVAudioPlayer())
- player.pause() and player.play()
I do not see what I am doing wrong really.
this is my code :
import UIKit
import AVFoundation
class LecteurViewController: UIViewController {
var chansonSelected: Chanson? = nil
var lecteur:AVAudioPlayer = AVAudioPlayer()
var timer1 = Timer()
var timer2 = Timer()
#IBOutlet weak var dureeChansonSlider: UISlider!
#IBOutlet weak var chansonImageView: UIImageView!
#IBOutlet weak var chansonVolumeSlider: UISlider!
#IBOutlet weak var debutLabel: UILabel!
#IBOutlet weak var finLabel: UILabel!
#IBAction func stopMusicAction(_ sender: UIBarButtonItem) {
var player = AVAudioPlayer()
lecteur.stop()
LecteurManager.isActive = false
}
#IBAction func pauseMusicAction(_ sender: UIBarButtonItem) {
var player = AVAudioPlayer()
lecteur.pause()
LecteurManager.isActive = false
}
#IBAction func jouerMusicAction(_ sender: UIButton) {
if LecteurManager.isActive {
changeSong()
print("lecteur déjà en cours")
} else {
var player = AVAudioPlayer()
lecteur.play()
}
print(LecteurManager.isActive )
LecteurManager.isActive = true
}
func changeSong() {
lecteur.stop()
//lecteur = AVAudioPlayer()
jouerLecteurMp3()
print(chansonSelected!)
lecteur.play()
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
jouerLecteurMp3()
}
func configureView() {
self.title = (chansonSelected!.titre!).capitalized
chansonImageView.image = UIImage(named: "\(chansonSelected!.image).jpgs")
//formatter 'back' button
let backBtn = UIBarButtonItem(title: "< Playlist", style: .plain, target: self, action: #selector(LecteurViewController.reset(_sender:)))
self.navigationItem.leftBarButtonItem = backBtn
self.navigationController?.navigationBar.tintColor = UIColor.white
//contrôler volume chanson
chansonVolumeSlider.addTarget(self, action: #selector(LecteurViewController.ajusterVolume(_ :)), for: UIControlEvents.valueChanged)
//contrôler durée chanson
dureeChansonSlider.addTarget(self, action: #selector(LecteurViewController.ajusterDurée(_ :)), for: UIControlEvents.valueChanged)
updateUI()
}
func updateUI() {
//indiquer position chanson
timer1 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(LecteurViewController.mettreAJourDurée), userInfo: nil, repeats: true)
//afficher durée chanson
timer2 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(LecteurViewController.afficherDurée), userInfo: nil, repeats: true)
}
func reset(_sender:UIBarButtonItem) {
self.navigationController?.popViewController(animated: true)
}
func ajusterVolume(_ sender:UISlider) {
//print("volume ajusté \(chansonVolumeSlider.value)")
lecteur.volume = chansonVolumeSlider.value
}
func ajusterDurée(_ sender:UISlider) {
lecteur.currentTime = TimeInterval(dureeChansonSlider.value)
}
func mettreAJourDurée() {
dureeChansonSlider.value = Float(lecteur.currentTime)
}
func afficherDurée() {
print("durée actuelle: \(lecteur.duration - lecteur.currentTime)")
debutLabel.text = retournerPositionActuelle()
finLabel.text = retournerDureeTotal()
}
func retournerPositionActuelle() -> String {
let seconds = Int(lecteur.currentTime) % 60
let minutes = (Int(lecteur.currentTime) / 60) % 60
return String(format: "%0.2i:%0.2i", minutes, seconds)
}
func retournerDureeTotal() -> String {
let seconds = Int(lecteur.currentTime) % 60
let minutes = (Int(lecteur.currentTime) / 60) % 60
return String(format: "%0.2i:%0.2i", minutes, seconds)
}
func jouerLecteurMp3() {
let chanson = "bensound-\(chansonSelected!.titre!)"
let fichierMp3 = Bundle.main.path(forResource: chanson, ofType: "mp3")
do {
try lecteur = AVAudioPlayer(contentsOf: URL(string: fichierMp3!)!)
dureeChansonSlider.maximumValue = Float(lecteur.duration)
} catch {
print("erreur lecture mp3")
}
}
}
Try this:
func reset(_sender:UIBarButtonItem)
{
self.navigationController?.popViewController(animated: true)
lecteur.stop()
}
I have the following code for a tvOS app. It plays video just as I would like, but the the contentOverlayView I've set does not appear. From examples I've looked at I think it should be working. Can someone please explain what I am doing wrong?
import Foundation
import UIKit
import AVKit
class videoPlayer: AVPlayerViewController {
var thePlayer: AVPlayer?
var movieToPlay: AVPlayerItem?
var movieURL: String?
let theVideoPlayer = AVPlayerViewController()
#IBOutlet weak var videoInfo: UIView!
override func viewDidLoad(){
theVideoPlayer.contentOverlayView?.addSubview(videoInfo)
theVideoPlayer.player = thePlayer
self.view.addSubview(theVideoPlayer.view)
theVideoPlayer.view.frame = self.view.frame
NotificationCenter.default.addObserver(self, selector: #selector(videoPlayer.videoDone), name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: thePlayer?.currentItem)
playVideo(movieURL!)
}
func playVideo(_ movieURL: String){
let movieToPlay = AVPlayerItem(url: URL(string: movieURL)!)
thePlayer = AVPlayer(playerItem: movieToPlay)
thePlayer!.play()
}
func videoDone(){
dismiss(animated: true, completion: nil)
}
}
I was confused about some things. Here's what worked:
import Foundation
import UIKit
import AVKit
class videoPlayer: UIViewController {
var movieURL:String = ""
var movieTitle:String = ""
var playerController:AVPlayerViewController?
#IBOutlet weak var videoInfo: UIView!
#IBOutlet var titleLabel: UILabel!
override func viewDidLoad(){
titleLabel.text = movieTitle
playerController = AVPlayerViewController()
playerController?.player = AVPlayer()
self.addChildViewController(playerController!)
self.view.addSubview((playerController?.view)!)
playerController?.didMove(toParentViewController: self)
playerController?.view.frame = self.view.frame
playerController?.contentOverlayView?.addSubview(videoInfo)
let movieToPlay = AVPlayerItem(url: URL(string: movieURL)!)
playerController?.player = AVPlayer(playerItem: movieToPlay)
playerController?.player?.play()
NotificationCenter.default.addObserver(self, selector: #selector(videoDone), name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: playerController?.player?.currentItem)
}
func videoDone(){
print("DONE")
dismiss(animated: true, completion: nil)
}
}
The UIView displays just as expected.
I'm facing a very unique situation, AVPlayer is trying to stream a URL that it loads multiple times (I see in control centre) before eventually playing it, after a long delay of about 120 seconds or so (sometimes more). I don't know why that is happening. I use a singleton instance of KDEAudioPlayer - any help would be appreciated.
KDEAudioPlayer Code:
import UIKit
import KDEAudioPlayer
var urlToPlay : NSURL!
var titleTrackBeingPlayed = String?()
class MediaPlayerViewController: FXBlurView, AudioPlayerDelegate {
//MARK: Variables
var seekToTime = NSTimeInterval()
var streaming = true
var timer:NSTimer?
var change:CGFloat = 0.01
//MARK: Connections
#IBOutlet weak var loadingActivityIndicator: UIActivityIndicatorView!
#IBOutlet weak var streamingFromServerLabel: UILabel!
#IBOutlet weak var timelineSlider: UISlider!
#IBOutlet weak var totalTimeLabel: UILabel!
#IBOutlet weak var nowTimeLabel: UILabel!
#IBOutlet weak var toggleButton: UIButton!
#IBOutlet weak var volumeControlSlider: UISlider!
#IBOutlet weak var titleLabel: UILabel!
//MARK: Actions
#IBAction func endTrackPlayerButtonPressed(sender: UIButton) {
//TODO: R&D how to dismiss.
print("media player view to be dismissed")
if let viewWithTag = self.viewWithTag(1089) {
print("Tag 1089")
Singleton.stop()
viewWithTag.removeFromSuperview()
}
}
#IBAction func timelineSliderValueChanged(sender: UISlider) {
Singleton.pause()
Singleton.seekToTime(NSTimeInterval(sender.value))
timelineSlider?.setValue(sender.value, animated: true)
Singleton.resume()
}
#IBAction func playPauseButtonPressed(sender: UIButton) {
toggle()
}
//MARK: Initializer for Class
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.tintColor = UIColor.clearColor()
if Singleton.isPlaying {
Singleton.isPlaying = false
}
playerSetup()
toggle()
}
//MARK: Setup for Audio Player
func playerSetup() {
Singleton.playItemWithUrl(urlToPlay)
Singleton.player.delegate = self
timelineSlider?.continuous = true
self.titleLabel?.text = titleTrackBeingPlayed
}
internal func refreshAudioView(_ :NSTimer) {
}
func toggle(){
if Singleton.isPlaying {
pauseTrack()
toggleButton.setImage(UIImage(named: "Play"), forState: .Normal)
}
else if !Singleton.isPlaying {
playTrack()
toggleButton?.setImage(UIImage(named: "Pause"), forState: .Normal)
}
}
func audioPlayer(audioPlayer: AudioPlayer, willStartPlayingItem item: AudioItem) {
titleLabel.text = titleTrackBeingPlayed
}
func audioPlayer(audioPlayer: AudioPlayer, didFindDuration duration: NSTimeInterval, forItem item: AudioItem) {
totalTimeLabel?.text = makeTimeString(Float(duration))
timelineSlider?.maximumValue = Float(duration)
timelineSlider?.minimumValue = 0.0
self.titleLabel?.text = titleTrackBeingPlayed
}
func audioPlayer(audioPlayer: AudioPlayer, didUpdateProgressionToTime time: NSTimeInterval, percentageRead: Float) {
timelineSlider?.setValue(Float(time), animated: true)
nowTimeLabel?.text = makeTimeString(Float(time))
Singleton.player.volume = volumeControlSlider.value
seekToTime = time
}
override func remoteControlReceivedWithEvent(event: UIEvent?) {
if let event = event {
Singleton.player.remoteControlReceivedWithEvent(event)
self.canBecomeFirstResponder()
}
}
func audioPlayer(audioPlayer: AudioPlayer, didChangeStateFrom from: AudioPlayerState, toState to: AudioPlayerState) {
if to == .Playing {
if streaming {
streamingFromServerLabel.hidden = false
loadingActivityIndicator.hidden = true
toggleButton?.setImage(UIImage(named: "Pause"), forState: .Normal)
}
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: #selector(TrackMediaPlayerViewController.refreshAudioView(_:)), userInfo: nil, repeats: true)
}
else if to == .Buffering || to == .WaitingForConnection {
loadingActivityIndicator.hidden = false
}
else {
streamingFromServerLabel.hidden = true
}
}
func playTrack() {
if seekToTime > 0.0 {
Singleton.resume()
} else {
Singleton.play()
}
Singleton.isPlaying = true
}
func pauseTrack() {
Singleton.pause()
Singleton.isPlaying = false
}
//MARK: Convert NSTime to String
func makeTimeString(value:Float) -> String
{
if value == 0 || value.isNaN
{
return "00:00"
}
let hr = (Int)(value / 3600)
let min = (Int)((value % 3600) / 60)
let sec = (Int)((value % 3600) % 60)
let timestamp = String(format:"%d:%02d.%02d", hr, min, sec)
return timestamp
}
}
Singleton Code:
import Foundation
import UIKit
import KDEAudioPlayer
class Singleton: AudioPlayerDelegate {
static let shareInstance = DarsPlayerSingleton()
static let player = AudioPlayer()
static var isPlaying = Bool()
private init (){
}
static func playItemWithUrl(url: NSURL) {
let item = AudioItem(mediumQualitySoundURL: url)
player.playItem(item!)
}
static func play() {
player.resume()
isPlaying = true
}
static func pause() {
player.pause()
isPlaying = false
}
static func stop() {
player.stop()
}
static func seekToTime(time: NSTimeInterval){
player.seekToTime(time)
}
static func resume() {
player.resume()
}
}
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.
In my case, my timer was calling again and again and this code was written inside my timer
self.recognitionTask?.finish()
node.removeTap(onBus: 0)
self.request.endAudio()
self.recognitionTask = nil
//Maybe this line was causing the issue
self.audioEngine.stop()
So if you already have stopped the request, removed the tap and stopped the engine, then these lines should not be called again.
Hope this helps some one
It looks like you are resetting the engine after playing the variable pitch effect.
func soundEnded() {
stopButton.hidden=true
disabledButton.enabled=true;
if(audioEngine.running) {
audioEngine.stop()
audioEngine.reset();
}
}
audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: soundEnded)
audioEngine.startAndReturnError(nil)
stopButton.hidden=false
stopButton.enabled=true
audioPlayerNode.play()
So the engine hasnt been setup again with the nodes added and the chain linked. When you try to play the playerNode, it crashes.