Search code examples
swiftavaudioplayeraudio-streaming

why currentTime and duration of AVAudioPlayer are nil?


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)
}

}

Solution

  • 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)")
                }
            }
        }
    }