AVAudioPlayer currentTime exceeds duration - iphone

I'm making my own audio player using AVAudioPlayer.
NOTE: "p" is my instance of the player
Here's how I'm reading the track progress in one of the labels:
currentTime.text = [NSString stringWithFormat:#"%d:%02d", (int)p.currentTime / 60, (int)p.currentTime % 60];
Here's how I set the total duration of the track in one of the labels:
int seconds = (int)p.duration % 60;
int minutes = (int)p.duration / 60;
duration.text = [NSString stringWithFormat:#"%d:%02d", minutes, seconds];
When I run the app on the device, the track's current time ALWAYS exceeds the duration (by about 5-10 seconds).
Is this a bug in AVAudioPlayer, or am I not doing it correctly?
NOTE: This behavior also occurs on the device (not just on the simulator)

After finding the seconds by using % 60, you should remove those seconds when converting the remaining for the minutes. For e.g., with the total duration of 119 seconds after finding 59 seconds you should remove that from 119 and then do minute conversion for 60 seconds (119-59). That might solve your problem.

Minutes should be float: 152 seconds / 60.0f = 2.5333 not 2.
That being said, if you want to show the remaining minutes without the seconds you already obtain: int minutes = (p.duration-seconds) / 60
Also, for a better method to format time the way you want to, have a look at the second answer in this question (not the accepted solution).

Here is the function:
func setTimeString(duration: Double)->String {
var audioDurationSeconds = duration
var expression = ""
var minutesString = "00"
var minutesFloat = 0.0
if (audioDurationSeconds)/60 >= 1 {
minutesFloat = (audioDurationSeconds) / 60
audioDurationSeconds = TimeInterval(Int(audioDurationSeconds)%60)
if minutesFloat < 10.0 {
minutesString = String.init(format: "0%.f", floor(minutesFloat))
} else {
minutesString = String.init(format: "%.f", floor(minutesFloat))
}
}
if audioDurationSeconds < 10.0 {
expression = String.init(format: "%#:0%i", minutesString, Int(audioDurationSeconds))
} else {
expression = String.init(format: "%#:%i", minutesString, (Int(audioDurationSeconds)))
}
return expression
}

extension UILabel{
func getTimeString(from duration:Double) -> String{
let hours = Int(duration / 3600)
let minutes = Int(duration / 60) % 60
let seconds = Int(duration.truncatingRemainder(dividingBy: 60))
if hours > 0 {
return String(format: "%i:%02i:%02i", arguments: [hours,minutes,seconds])
}else {
return String(format: "%02i:%02i", arguments: [minutes,seconds])
}
}

Related

MInutes and seconds with 2 digits

I have an input in seconds I store it in a var:
var totalTime = 60
I want to show it with 2 digits for minutes and 2 digits for seconds:
01:00
What I have tried:
let minutes = String(totalTime / 60)
let seconds = String(totalTime % 60)
label.text = minutes + ":" + seconds
This gives me: 1:0
But I want 01:00
I tried and It does not work:
label.text = String(format: "%02d:%02d", minutes, seconds)
The problem with your second approach is that it expects an integer and you are passing a string.
let minutes = totalTime / 60
let seconds = totalTime % 60
label.text = String(format: "%02d:%02d", minutes, seconds)

Get 12h hours am/pm Time

I want to display the time from a Slider to a Label. So i must convert the value and get the current day time on it like 00:00 am/pm.
So i need there a stepper how print all 5 steps like (5,10,15,20,25....50,55)
So this code on the bottom don´t run good, have anybody a better way to make that ?
I have try it but it becomes an error when i slider back (zb: when it´s 8:00 am and i slider back to 7:55 am it come for first 7:00 am.
Here is the code:
func valueChange(_ sender: CircleSlider) {
let countmin = Int(Double(sender.value) * 14.4)
var hour = countmin / 60
let mins = countmin - (hour*60)
if hour >= 12 {
hour -= 12
Am.text = "Pm"
} else {
Am.text = "Am"
}
hours = hour
let i = String(mins)
switch i {
case "Nil":
minutes = 00
case "0":
minutes = 00
case "5":
minutes = 05
case "10":
minutes = 10
case "15":
minutes = 15
case "25":
minutes = 25
case "30":
minutes = 30
case "35":
minutes = 35
case "40":
minutes = 40
case "45":
minutes = 45
case "50":
minutes = 50
case "55":
minutes = 55
case "60":
minutes = 60
default:
break
}
self.circleTime.text = "\(String(format: "%02d", hours!)):\(String(format: "%02d", minutes!))"
}
Thank´s for Help :)
I think there's an issue in your method for 7:56, 7:57, 7:58, 7:59 (it is not truncating well). This code should work for you:
func valueChange(_ sender: CircleSlider) {
let countmin = Int(Double(sender.value) * 14.4)
var hour = countmin / 60
let mins = countmin - (hour * 60)
if hour >= 12 {
hour -= 12
Am = "Pm"
} else {
Am = "Am"
}
hours = hour
minutes = roundToFives(Double(mins))
// This fixes when you have hh:60. For instance, it fixes 7:60 to 8:00
if minutes == 60 {
hours = hour + 1
minutes = 0
}
self.circleTime.text = "\(String(format: "%02d", hours!)):\(String(format: "%02d", minutes!))"
}
// idea of this method comes from: http://stackoverflow.com/questions/27922406/round-double-to-closest-10-swift
private func roundToFives(x : Double) -> Int {
return 5 * Int(round(x / 5.0))
}

Convert decimal to hours:minutes:seconds

I am storing numbers in a MySQL DB as doubles so I can get min, max and sums.
I have a decimal number 1.66777777778 which equals 01:40:04 however I am wanting to be able to convert this decimal in to hour:minutes:seconds in Swift so I can display the value as 01:40:04 however I don't know how.
I have done some searching but most results are calculators without explanation.
I have this function to convert to decimal:
func timeToHour(hour: String, minute:String, second:String) -> Double
{
var hourSource = 0.00
if hour == ""
{
hourSource = 0.00
}
else
{
hourSource = Double(hour)!
}
let minuteSource = Double(minute)!
let secondSource = Double(second)!
let timeDecimal: Double = hourSource + (minuteSource / 60) + (secondSource / 3600)
return timeDecimal
}
but need one to go back the other way.
Thanks
Try:
func hourToString(hour:Double) -> String {
let hours = Int(floor(hour))
let mins = Int(floor(hour * 60) % 60)
let secs = Int(floor(hour * 3600) % 60)
return String(format:"%d:%02d:%02d", hours, mins, secs)
}
Basically break each component out and concatenate them all together.

Calculate average pace

I need to convert a decimal hour in to hh:mm:ss to display as average pace for a walking app.
I have converted the time to decimal and I have calculated the pace in to decimal however I am unsure how to convert this to time.
My timeDecimal is:
let timeDecimal:Double = (hourSource + (minuteSource / 60) + (secondSource / 3600)) / distanceSource
which gives me something like 0.4375
0 = 0
.4375 * 60 = 26.25
.25 = * 60 = 15
I know the time should be 00:26:15 but not sure the formula to achieve this without splitting up the result and performing the multiplication multiple times.
Any help is appreciated.
let formatter = NSDateComponentsFormatter()
formatter.allowedUnits = [ .Hour, .Minute, .Second ]
formatter.unitsStyle = .Positional
formatter.zeroFormattingBehavior = .Pad
let string = formatter.stringFromTimeInterval(0.4375 * 3600)!
Result: 0:26:15.
Try
var mytime = timeDecimal * 60
var minutes = floor(mytime)
var seconds = (mytime - minutes) * 60
func timeStringFrom(time time: Int) -> String {
let HoursLeft = time/3600
let MinutesLeft = (time%3600)/60
let SecondsLeft = (((time%3600)%60)%60)
if HoursLeft == 0 {
return String(format:"%.2d:%.2d", MinutesLeft, SecondsLeft)
} else {
return String(format:"%2d:%.2d:%.2d", HoursLeft, MinutesLeft, SecondsLeft)
}
}
Note: I'll probably turn it into a generic function soon

How to decrease a value constantly when countdown is started?

I want to merge something with this code. My goal is to add a price value in a label which is decreasing constantly ever second when countdown is started. For ex: CurrentPrice - DiscountPerSecond on first second then CurrentPrice - DiscountPerSecond*2 on second etc.. until countdown is finished.
var timerCounter:NSTimeInterval!
func updateTime(interval: NSTimeInterval) -> String {
let interval = Int(interval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let hours = interval / 3600
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
func startTimer(hour:Int) {
timerCounter = NSTimeInterval(hour * 60 * 60)
let aSelector : Selector = "onTimer:"
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: aSelector, userInfo: nil, repeats: true)
}
func onTimer(timer:NSTimer!) {
countdownShow.text = updateTime(timerCounter)
timerCounter!--
if (timerCounter == 0) {
timer.invalidate()
countdownShow.text = "Event Closed!"
}
}
A few notes:
Instead of counting down from a time interval, you should store a fixed end date and calculate time intervals from the current time as needed.
You should calculate discounts based on a proportion of a maximum discount divided by the proportion of time remaining. (i.e. price = listPrice - (maximumDiscount * proportionOfTimeElapsed))
You should use NSDateComponentsFormatter to format the remaining time.