I am using TvOSSlider in my tv app to create a seek-bar and play audio track with AVAudioPlayer. My problem is that it is stuttering every time i update the seekbar/slider through code. So currently it is set to stutter every 1.0seconds. UISlider is unavailable in tvOS that is why i am using this different library.
func playAudio(url: URL, completion: (() -> Void)? = nil) {
stopAudio()
do {
audioPlayer = try AVAudioPlayer(contentsOf: url as URL)
audioPlayer?.delegate = self
audioPlayer?.prepareToPlay()
audioPlayer?.play()
isPaused = false
// Start the playback timer to update the seek bar
DispatchQueue.main.async { [self] in
// This is where timer is set and update Slider method is called. I tried to set it from 0.1 sec to 5 sec
playbackTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateSlider), userInfo: nil, repeats: true)
}
completion?()
} catch let error as NSError {
print("Error initializing AVAudioPlayer: (error.localizedDescription)")
completion?()
}
}
// Update the slider value based on the current playback time
@objc func updateSlider() { // This function gets called above
guard let audioPlayer = audioPlayer else { return }
slider?.setValue(Float(audioPlayer.currentTime / audioPlayer.duration), animated: true) // This line causes the stuttering
}
I have tried background DispatchQueue but it crashes the app saying slider.setvalue must be called from main thread as it updates ui. I also tried playing audio in the background thread but that was a doomed idea to begin with. Following is my audio manager code
import AVFAudio
import TvOSSlider
class AudioManager {
static let shared = AudioManager()
private var audioPlayer: AVAudioPlayer?
var playbackTimer: Timer?
private var isPaused = false
var slider: TvOSSlider? // Add a reference to the slider
private init() { }
func playAudio(url: URL, completion: (() -> Void)? = nil) {
stopAudio()
do {
audioPlayer = try AVAudioPlayer(contentsOf: url as URL)
audioPlayer?.prepareToPlay()
audioPlayer?.play()
isPaused = false
// Start the playback timer to update the seek bar
DispatchQueue.main.async { [self] in
playbackTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateSlider), userInfo: nil, repeats: true)
}
completion?()
} catch let error as NSError {
print("Error initializing AVAudioPlayer: (error.localizedDescription)")
completion?()
}
}
func togglePlayPause() {
guard let audioPlayer = audioPlayer else {
return
}
if audioPlayer.isPlaying {
audioPlayer.pause()
playbackTimer?.invalidate()
playbackTimer = nil
isPaused = true
} else {
audioPlayer.play()
playbackTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateSlider), userInfo: nil, repeats: true)
isPaused = false
}
}
func stopAudio() {
audioPlayer?.stop()
audioPlayer = nil
isPaused = false
playbackTimer?.invalidate()
playbackTimer = nil
}
// MARK: - Slider methods
// Update the slider value based on the current playback time
@objc func updateSlider() {
guard let audioPlayer = audioPlayer else { return }
slider?.setValue(Float(audioPlayer.currentTime / audioPlayer.duration), animated: true)
}
// Seek to the desired position in the audio
@objc func sliderValueChanged(_ sender: TvOSSlider) {
guard let audioPlayer = audioPlayer else { return }
let targetTime = TimeInterval(sender.value) * audioPlayer.duration
audioPlayer.currentTime = targetTime
}
}
And here is my use of this in my view controller
import TvOSSlider
class MyViewController: UIViewController {
...
@IBOutlet weak var tvosSlider: TvOSSlider!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
playSongFromUrl()
getData()
}
func setupView(){
// // Set the AudioManager's slider property
AudioManager.shared.slider = tvosSlider
AudioManager.shared.slider?.addTarget(AudioManager.shared, action: #selector(AudioManager.sliderValueChanged(_:)), for: .valueChanged)
...
}
func play(url: URL) {
AudioManager.shared.playAudio(url: url){
self.stopLoader()
}
}
...
}
Now i am unable to find the solution. Any help will be appreciated.
2
Answers
I ended up editing in the library TvOSSlider's code to solve the stuttering problem.
This method is used to update the slider
slider?.setValue(Float(audioPlayer.currentTime / audioPlayer.duration), animated: true)
I right click onsetValue
method and selectJump to definition
there I click unlock on making changes to the code and made following changes.So in above method the only thing I changed was i commented
stopDeceleratingTimer()
. Now I do not have a complete grasp of what it does. However, this solved my problem without creating a new one. So cheers.