How do I set initial music volume to be 50% using UIslider iOs? I am using AVFoundation

158 views Asked by At

I searched around stackoverflow but I couldn't find anything that helped. I am currently building a simple music app. My problem is that the slider that I am using to control the volume levels plays the song at full volume, or 100% of the volume when the app first runs. What I want to do is to adjust this to be at only 50% of the volume so when I drag the slider to the right, I can increase the volume. Currently, when I drag the slider to the right, the volume stays the same because like I mentioned before when I run the app, the volume is already at maximum level. However when I drag it to the left, the volume does decrease as expected which has me even more confused. I should also add the slider's value is set to be 0.5 at the time of creation as it should. I will post the code below, please help. I create the slider inside the configurePlayer() method, and I am trying to adjust the volume inside the last method

'''

import UIKit
import AVFoundation

class playerViewController: UIViewController {
var position: Int = 0
var Gabay = [Gabays]()
var player: AVAudioPlayer?


var timer: Timer?



@IBOutlet var holder: UIView!
let forWardButton = UIButton()
let backwardsButton = UIButton()
let playPauseButton = UIButton()

//userinterfact elements
private let coverImageView: UIImageView = {
    
    let imageView=UIImageView()
    imageView.contentMode = .scaleAspectFit
    return imageView
}()

private let trackNameLabel: UILabel = {
    let gabayNameLabel = UILabel()
    gabayNameLabel.font = UIFont(name: "Helvetica-bold", size: 20)
    return gabayNameLabel
}()

private let artist: UILabel = {
    let artistName = UILabel()
    artistName.font = UIFont(name: "Helvetica-bold", size: 20)
    artistName.textAlignment = .left

    return artistName
}()


    




//MARK: - life cycle
override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemGreen
}

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if holder.subviews.count == 0{
        configurePlayer()
    }
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if let player = player{
        player.stop()
    }
}


// MARK: - Table view data source




//configure
func configurePlayer() {
    //set up player
    let gabay = Gabay[position]

    //loading the gabays from disc or array
    let urlString = Bundle.main.path(forResource: gabay.trackName, ofType: "mp3")
    
    do{
        try AVAudioSession.sharedInstance().setMode(.default)
        try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
        
        guard let urlString = urlString else{return}
        
        player = try AVAudioPlayer(contentsOf: URL(string: urlString)!)
        guard let player = player else{return}
        //play the audio
        player.play()

        
    }
    
    catch{
        print("Error is: \(error.localizedDescription)")
    }
    
        //set up imageView cover
    coverImageView.frame = CGRect(x: 15, y: 10, width: holder.frame.size.width - 20, height: holder.frame.size.height - 350)
    coverImageView.image = UIImage(named: gabay.imageName)
    holder.addSubview(coverImageView)
    
    //set up labels
    trackNameLabel.frame = CGRect(x: 150, y: 15, width: holder.frame.size.width - 40, height: holder.frame.size.width + 470)
    trackNameLabel.text = gabay.trackName
    
    holder.addSubview(trackNameLabel)
    
    artist.frame = CGRect(x: 150, y: 15, width: holder.frame.size.width  - 40, height: holder.frame.size.width + 540)
    artist.text = gabay.artist
    holder.addSubview(artist)
    
    //setting up buttons
    playPauseButton.setTitle("", for: .normal)
    playPauseButton.addTarget(self, action: #selector(playButtonTapped), for: .touchUpInside)
    playPauseButton.frame = CGRect(x: 155, y: 600, width: (holder.frame.size.width)/6, height: holder.frame.size.height - 690)
    playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
    holder.addSubview(playPauseButton)
    
    backwardsButton.setTitle("", for: .normal)
    backwardsButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
    backwardsButton.frame = CGRect(x: 15, y: 600, width: (holder.frame.size.width)/6, height: holder.frame.size.height - 690)
    backwardsButton.setImage(UIImage(systemName: "backward.fill"), for: .normal)
    holder.addSubview(backwardsButton)
    
    forWardButton.setTitle("", for: .normal)
    forWardButton.addTarget(self, action: #selector(forwardButtonTapped), for: .touchUpInside)
    forWardButton.frame = CGRect(x: 285, y: 600, width: (holder.frame.size.width)/6, height: holder.frame.size.height - 690)
    forWardButton.setImage(UIImage(systemName: "forward.fill"), for: .normal)
    holder.addSubview(forWardButton)
    
    //set volume slider
    
    let volumeSlider = UISlider()
    volumeSlider.minimumValue = 0.0
    volumeSlider.maximumValue = 1.0
    volumeSlider.alpha = 0.4
    volumeSlider.addTarget(self, action: #selector(volumeSliderChanged), for: .valueChanged)
    volumeSlider.value = 0.1
    volumeSlider.frame = CGRect(x: 15, y: 560, width: holder.frame.size.width  - 40, height: (holder.frame.size.width)/8)
    volumeSlider.setThumbImage(UIImage(systemName: "volume.3")?.withTintColor(.systemGray, renderingMode: .alwaysOriginal).resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right:0)), for: .normal)
    holder.addSubview(volumeSlider)

    
    //set fast forward slider
    
    let seekSlider = UISlider()
    
    seekSlider.addTarget(self, action: #selector(seekThrough), for: .valueChanged)
    seekSlider.minimumValue = 0.0
    seekSlider.maximumValue = 1.0
    seekSlider.frame = CGRect(x: 15, y: 700, width: holder.frame.size.width  - 40, height: (holder.frame.size.width)/8)
    seekSlider.setThumbImage(UIImage(systemName: "circle.fill")?.withTintColor(.clear, renderingMode: .alwaysOriginal).resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right:0)), for: .normal)

    holder.addSubview(seekSlider)
    
 

}

@objc func playButtonTapped(){
    guard let player = player else{return}
    if player.isPlaying{
        playPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
        player.stop()
    }else{
        playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
        player.play()

    }
}

@objc func backButtonTapped(){
    if position > 0 {
        position = position - 1
        player?.stop()
        for subview in holder.subviews {
            subview.removeFromSuperview()
        }
        configurePlayer()
        
    }
}

@objc func forwardButtonTapped(){
   
    if position < Gabay.count - 1 {
       
        guard let player = player else{return}
        position = position + 1
        player.stop()
        for subview in holder.subviews {
            subview.removeFromSuperview()
        }
        configurePlayer()

    }
}






@objc func seekThrough(_ slider: UISlider){
    //get the duration of the current playing gabay
    var duration = player!.duration
    duration += 1
    //calculate
    let newPosition = Double(slider.value)*duration
    player!.currentTime = newPosition
    
    
    //make slider move along the song
 
}



@objc func volumeSliderChanged(_ slider: UISlider){
    let volumeValue = slider.value
    

    player!.volume = volumeValue
    
    //make the initial position of volume slider to be 50% of the volume
}

}

1

There are 1 answers

0
jp21 On

The function volumeSliderChanged() is called every time the slider is used, which means any initialization added there will be repeatedly called every time someone uses the slider to the volume.

Try adding player!.volume = 0.5 right underneath player = try AVAudioPlayer(contentsOf: URL(string: urlString)!) instead.

do{
    try AVAudioSession.sharedInstance().setMode(.default)
    try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
        
    guard let urlString = urlString else{return}
        
    player = try AVAudioPlayer(contentsOf: URL(string: urlString)!)


    //Set up volume here 
    player!.volume = 0.5    


    guard let player = player else{return}
    //play the audio
    player.play()  
}
    
catch{
    print("Error is: \(error.localizedDescription)")
}

Also try modifying the initial slider value from 0.1 to 0.5 in order for the slider position to match the starting volume.

let volumeSlider = UISlider()
volumeSlider.minimumValue = 0.0
volumeSlider.maximumValue = 1.0
volumeSlider.alpha = 0.4
volumeSlider.addTarget(self, action: #selector(volumeSliderChanged), for: .valueChanged)
//Set initial volume slider value to 0.5 (50%) to match the initial volume 
//volumeSlider.value = 0.1
volumeSlider.value = 0.5 

As for the volume not decreasing, it might be because of linear vs. logarithmic volume control. Here's the extension from the article (AVPlayer changed to AVAudioPlayer):

extension AVAudioPlayer {
    var logarithmicVolume: Float {
        get {
            return sqrt(volume)
        }
        set {
            volume = pow(newValue, 2)
        }
    }
}

Replacing every instance of player!.volume with player!.logarithmicVolume might help with the issue.