如何从歌曲的分钟数正确倒计时?

Posted

技术标签:

【中文标题】如何从歌曲的分钟数正确倒计时?【英文标题】:how to properly do a countDown from the minutes of a Song? 【发布时间】:2019-05-24 17:11:41 【问题描述】:
    我想知道如何正确获取歌曲的总分和秒。一旦我得到总的分钟和秒数,我希望它开始倒计时。只需显示 2 位小数秒和两位小数。 我想知道怎么从头数数 并且还想显示 2 位小数秒和 2 位分钟

提前致谢

@objc func timerUpdate() 

    if (player != nil && player!.currentTime > 0.0) 
        increaseTime ()
        DecreaseTime()
        progressBar.setProgress(Float(player!.currentTime / player!.duration), animated:  false)

    
    else 
        timer.invalidate()

    

var totalOfTime :Double?
var minutes : Double?
var seconds : Double?
func changeSecondToMinutes() 
    minutes = player!.duration / 60.0

   seconds = minutes! / 60
   seconds! *= 100
    var rightOfSecods = Int(round(seconds!))
    seconds = Double(rightOfSecods) * 00.01
    print(seconds!)


var time  = 0
var minut = 0
func increaseTime () 

    startTime.text =  "\(Double(minut))   "
    print(minut)
    print(time)


var secondos = 0.0
func DecreaseTime() 
    seconds! -= 0.010


    print(seconds!)
    print(minutes!)
    if Double(round(10000*seconds!)/10000) == 0.00 

        seconds! += 0.60
        minutes! -= 1
    
    time += 1
    print(secondos)
    if time == 61 
       minut += 1
       time = 0

    


func PlayerFunction() 
 changeSecondToMinutes()

            Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)
 

【问题讨论】:

Format float value with 2 decimal places的可能重复 有什么建议,请提供代码 【参考方案1】:

我在我的项目中使用这个单例类

class MediaPlayerManager: NSObject 

static let shared = MediaPlayerManager()

private var _player = AVPlayer()
private var _timer = Timer()
private var _songURL = ""
private var _isPaused = false
private var _isPlaying = false
private var _isStopped = false
private var _isRepeatEnabled = false
private var _isShuffleEnabled = false
private var _songProgressed : Float = 0.0
private var _observer: Any!
private var _rate: Float = 1.0

var songURL : String 
    set  _songURL = newValue 
    get  return _songURL 


var isPaused : Bool 
    set  _isPaused = newValue 
    get  return _isPaused 


var isPlaying : Bool 
    set  _isPlaying = newValue 
    get  return _isPlaying 


var isRepeatEnabled : Bool 
    set  _isRepeatEnabled = newValue 
    get  return _isRepeatEnabled 


var isShuffleEnabled : Bool 
    set  _isShuffleEnabled = newValue 
    get  return _isShuffleEnabled 


var songProgressed : Float 
    set  _songProgressed = newValue 
    get  return _songProgressed 


var rate : Float 
    set 
        _rate = newValue
        _player.rate = _rate
    
    get  return _rate 


override init() 
    super.init()
    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)
    
    _player = AVPlayer.init()


func playSong() 
    if isPaused 
        _player.play()
        isPlaying = true
        isPaused = false
        return
    
    isPlaying = true
    isPaused = false
    let url = URL(string: self.songURL)
    let item = AVPlayerItem(url: url!)
    _player = AVPlayer(playerItem: item)
    _player.play()
    isPlaying = true
    isPaused = false
    addObserver()


func playSong(urlString: String) 

    let url = URL(string: urlString)
    songURL = urlString
    if isPaused 
        _player.play()
        isPlaying = true
        isPaused = false
        return
    
    isPlaying = true
    isPaused = false
    let item = AVPlayerItem(url: url!)
    _player = AVPlayer(playerItem: item)
    _player.play()
    addObserver()


func addObserver() 
    _player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)
    _player.currentItem!.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
    _player.currentItem!.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
    _player.currentItem!.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil)

    let interval = CMTime(value: 1, timescale: 2)
    _observer = _player.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using:  (progressTime) in
        self.scheduleTimer()
        let seconds = CMTimeGetSeconds(progressTime)
        let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60.0)))
        let minutesString = String(format: "%02d", Int(seconds / 60))

        print( "\(minutesString):\(secondsString)" )
        self.songProgressed = Float( seconds )

    )


func pauseSong() 
    if isPlaying 
        isPlaying = false
        isPaused = true
        _player.pause()
    


func stopSong() 
    isPlaying = false
    isPaused = false
    invalidateTimer()
    if let observer = _observer 
        _player.removeTimeObserver(observer)
        _observer = nil
    
    _player = AVPlayer.init()


func reset() 
    songURL = ""
    stopSong()


func seekTo(Time time: Double) 
    let seconds : Int64 = Int64(time)
    let targetTime: CMTime = CMTimeMake(value: seconds, timescale: 1)

    _player.seek(to: targetTime)


func getRemainingTime() -> Double 
    let duration = _player.currentItem!.duration.seconds //total time
    let currentTime = _player.currentTime().seconds //playing time

    let remainingTime = duration - currentTime
    return (remainingTime.isNaN || remainingTime.isInfinite) ? 0 : remainingTime


func getSongDuration() -> Double 
    return (_player.currentItem!.duration.seconds.isNaN || _player.currentItem!.duration.seconds.isInfinite) ? 0 : _player.currentItem!.duration.seconds


func getSongProgress() -> Double 
    return (Double(self.songProgressed).isNaN || Double(self.songProgressed).isInfinite) ? 0 : Double(self.songProgressed)


private func scheduleTimer() 
    if (!_timer.isValid) 
        _timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)

    


private func invalidateTimer() 
    _timer.invalidate()


@objc func updateTimer() 
    NotificationCenter.default.post(name: .didReceiveRemainingTime, object: nil, userInfo: [Notification.Name.didReceiveRemainingTime: getRemainingTime()])
    NotificationCenter.default.post(name: .didReceiveElapsedTime, object: nil, userInfo: [Notification.Name.didReceiveElapsedTime: getSongProgress()])


override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
    switch keyPath 
    case "rate":
        if let rate = change?[NSKeyValueChangeKey.newKey] as? Float 
            if rate == 0.0 
                print("playback stopped")
                isPlaying = false
                isPaused = true
                invalidateTimer()
            
            if rate == 1.0 
                print("normal playback")
            
            if rate == -1.0 
                print("reverse playback")
            
        
    case "playbackBufferEmpty":
        // Show loader
        print("buffer empty")
    case "playbackLikelyToKeepUp":
        // Hide loader
        print("buffer")
    case "playbackBufferFull":
        // Hide loader
        print("buffer full")
    case .none:
        break
    case .some(_):
        break
    



这是你如何使用这个类并获得剩余时间

override func viewDidLoad() 
    super.viewDidLoad()
    addMediaPlayerObservers()


func addMediaPlayerObservers()
    NotificationCenter.default.addObserver(self, selector: #selector(didReceiveRemainingTime), name: NSNotification.Name.didReceiveRemainingTime, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(didReceiveElapsedTime), name: NSNotification.Name.didReceiveElapsedTime, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(didSongFinished(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)


func playSong() 
    MediaPlayerManager.shared.playSong(urlString: "song url here")


@objc func didReceiveRemainingTime(_ notification: NSNotification) 
    if let remainingTime = notification.userInfo?[NSNotification.Name.didReceiveRemainingTime] as? Double 
//            print("Remaining time from observer: \(remainingTime)")

        self.hmsFrom(seconds: (Int)(remainingTime))  (hours, minutes, seconds) in
            print("hours: \(hours)")
            print("minutes: \(minutes)")
            print("seconds: \(seconds)")
        
    

@objc func didReceiveElapsedTime(_ notification: NSNotification) 
    if let elapsedTime = notification.userInfo?[NSNotification.Name.didReceiveElapsedTime] as? Double 
 //            print("Elapsed time from observer: \(elapsedTime)")

        self.hmsFrom(seconds: (Int)(elapsedTime))  (hours, minutes, seconds) in
            print("hours: \(hours)")
            print("minutes: \(minutes)")
            print("seconds: \(seconds)")
        
    

@objc func didSongFinished(_ notification: NSNotification) 
    print("Finished")
    MediaPlayerManager.shared.stopSong()

这个函数实际上将总秒数分别转换为小时、分钟和秒

func hmsFrom(seconds: Int, completion: @escaping (_ hours: Int, _ minutes: Int, _ seconds: Int)->()) 
    completion(seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)   

这是通知扩展

extension Notification.Name 
    static let didReceiveRemainingTime = Notification.Name("didReceiveRemainingTime")
    static let didReceiveElapsedTime = Notification.Name("didReceiveElapsedTime")

希望你能得到想要的结果。

【讨论】:

嗨,我如何将 player.duration 转换为 CMTimeGetSeconds,我遇到了问题,请提前提供代码,谢谢 让时间 = CMTimeGetSeconds(_player.currentItem!.duration) if let duration = player?.duration print(duration) let totalOfSeconds = CMTimeGetSeconds(duration) 我也尝试调用 player.currentItem.duration 并且它没有 currentItem 属性,我正在使用AV音频播放器?作为我的玩家 guard let player = player else return let remainingTime = Int(player.duration - player.currentTime) print(remainingTime) let remainingTimeCMTimeObj = CMTimeGetSeconds(CMTime(seconds: Double(remainingTime), preferredTimescale: CMTimeScale.init(Double(remainingTime)))) 打印(remainingTimeCMTimeObj) remainingTime 是 Integer,remainingTimeCMTimeObj 是您需要的 CMTimeGetSeconds,其中 as player 是 AVAudioPlayer【参考方案2】:

看起来DateComponenetsFormatter 加上一些强制转换是一个很好的例子。

var player : AVPlayerItem? //Assuming you initialized this correctly.
var timer : Timer?

@objc func timerUpdate() 

    if let currentTime = player?.currentTime(), let duration = player?.duration, duration != CMTime.zero 

        let timeLeft = duration - currentTime

        //https://***.com/a/43890305/5153744
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.hour, .minute, .second]
        formatter.unitsStyle = .positional

        let formattedCurrentTime = formatter.string(from: TimeInterval(CMTimeGetSeconds(currentTime)))!
        let formattedTimeLeft = formatter.string(from: TimeInterval(CMTimeGetSeconds(timeLeft)))!
        let formattedTimeDuration = formatter.string(from: TimeInterval(CMTimeGetSeconds(duration)))!

        print("Current Time: \(formattedCurrentTime)")
        print("Time Left: \(formattedTimeLeft)")
        print("TotalDuration: \(formattedTimeDuration)")
    
    else 
        timer?.invalidate()
    


func playerFunction() 
    timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)

【讨论】:

播放器是 var player: AVAudioPlayer?我应该为 AVPlayerItem 更改它吗? 不,我不知道它是哪种资产,所以我只需要根据您的代码做出假设,因为您没有发布您的实例化。 AVPlayerItem 具有我用来计算的相同属性,所以不,你不需要。 它不允许转换 CMTimeGetSeconds(currentTime) 我该怎么做是因为它是一个 AVAudioPlayer?

以上是关于如何从歌曲的分钟数正确倒计时?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:显示天、小时、分钟和秒的倒计时小部件

10分钟,手把手教学正确还原京东倒计时,初学者必看,简单易懂!

10分钟,手把手教学正确还原京东倒计时,初学者必看,简单易懂!

Date应用计时器和倒计时

jquery倒计时插件,日期来自Mysql数据库

防止 BPM 计时码表与真正的节拍器慢慢不同步