最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - Error when pressing the sound button quickly - Stack Overflow

programmeradmin3浏览0评论

I have a volume toggle button. When I press the button, then pause or play is called with some fading. The problem is that if I quickly press the button twice, for example, pause and then playback, I will hear a jump in volume. Moreover, if I quickly call play, and then press sharply and call pause, then in playback mode the audio will stop due to calling DispatchQueue with a delay. How to fix this?

ViewController:

class ViewController: UIViewController {

    @objc func soundState() {
        if defaults.string(forKey: "sound") == "false" {
            audio.pause(volume: 0.0, duration: 3.0)
            
        } else if defaults.string(forKey: "sound") == "true" {
            audio.play(volume: 1.0, duration: 3.0) 
        }
    }

}

AudioController:

class Audio: NSObject, AVAudioPlayerDelegate {

    var audioPlayer: AVAudioPlayer!
    
    func loopedAudio(fileName: String, fileExtension: String) {
                
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
        try? AVAudioSession.sharedInstance().setActive(true)
        
        let url = Bundle.main.url(forResource: fileName, withExtension: fileExtension)
        
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url!)
            audioPlayer.prepareToPlay()
            audioPlayer.delegate = self
            audioPlayer.numberOfLoops = -1
        } catch {
            print("error")
        }
        
    }

    func play(volume: Float, duration: Double) {
        audioPlayer.volume = 0
        audioPlayer.play()
        audioPlayer.setVolume(volume, fadeDuration: duration)
    }
    
    func pause(volume: Float, duration: Double) {
        audioPlayer.volume = 1
        audioPlayer.setVolume(volume, fadeDuration: duration)
        DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
            self.audioPlayer.pause()
        }
    }

}

I have a volume toggle button. When I press the button, then pause or play is called with some fading. The problem is that if I quickly press the button twice, for example, pause and then playback, I will hear a jump in volume. Moreover, if I quickly call play, and then press sharply and call pause, then in playback mode the audio will stop due to calling DispatchQueue with a delay. How to fix this?

ViewController:

class ViewController: UIViewController {

    @objc func soundState() {
        if defaults.string(forKey: "sound") == "false" {
            audio.pause(volume: 0.0, duration: 3.0)
            
        } else if defaults.string(forKey: "sound") == "true" {
            audio.play(volume: 1.0, duration: 3.0) 
        }
    }

}

AudioController:

class Audio: NSObject, AVAudioPlayerDelegate {

    var audioPlayer: AVAudioPlayer!
    
    func loopedAudio(fileName: String, fileExtension: String) {
                
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
        try? AVAudioSession.sharedInstance().setActive(true)
        
        let url = Bundle.main.url(forResource: fileName, withExtension: fileExtension)
        
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url!)
            audioPlayer.prepareToPlay()
            audioPlayer.delegate = self
            audioPlayer.numberOfLoops = -1
        } catch {
            print("error")
        }
        
    }

    func play(volume: Float, duration: Double) {
        audioPlayer.volume = 0
        audioPlayer.play()
        audioPlayer.setVolume(volume, fadeDuration: duration)
    }
    
    func pause(volume: Float, duration: Double) {
        audioPlayer.volume = 1
        audioPlayer.setVolume(volume, fadeDuration: duration)
        DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
            self.audioPlayer.pause()
        }
    }

}
Share Improve this question asked Jan 19 at 6:37 user1342352532user1342352532 119 bronze badges 8
  • How do you want to fix it? What should happen? – matt Commented Jan 19 at 7:17
  • @matt I want to know this or get an idea on how to fix this from more experienced users or from those who have encountered a similar problem. I would divide this into several problems. The first is volume jumps. To fix this, when I call pause/play I need to get the current volume. To use the current volume instead of 1 or 0. How to do this? The second is call pause() after delay. Maybe I can somehow track this call and if play func is called quickly, then cancel the pause() delay call? – user1342352532 Commented Jan 19 at 7:32
  • What is the point of the volume shenanigans and taking time to pause? Just pause or play immediately. With an immediate play but a delayed pause you are going to have issues with rapid button taps. You could set a flag so that the play doesn't take effect if there is a pending pause (check another flag at the end of the pause to see if you need to restart playback) but really, I don't think the fade or delay is worth the hassle. – Paulw11 Commented Jan 19 at 7:44
  • @Paulw11 This is an app for children. Audio fade is important for this app. There is an apps in the app store with a similar play button behaviour. They have the same logic. And everything works without problems for them. I wonder how they did it – user1342352532 Commented Jan 19 at 7:51
  • Then you need to implement as I suggested. Ensure that you don't process the play until the pause is done and make sure you do process a play event that is queued during a pause – Paulw11 Commented Jan 19 at 8:06
 |  Show 3 more comments

1 Answer 1

Reset to default 1

Your issue arises because one of your actions,pause is delayed, while your other action play is instant. This means that if you change state rapidly, they delayed action may no longer be required.

The solution is to explicitly track the desired state in order to avoid unnecessary delayed actions:

enum PlaybackState {
    case playing
    case paused
}

class Audio: NSObject, AVAudioPlayerDelegate {

    var playbackState: PlaybackState = .paused

    var audioPlayer: AVAudioPlayer!
    
    func loopedAudio(fileName: String, fileExtension: String) {
                
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
        try? AVAudioSession.sharedInstance().setActive(true)
        
        let url = Bundle.main.url(forResource: fileName, withExtension: fileExtension)
        
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url!)
            audioPlayer.prepareToPlay()
            audioPlayer.delegate = self
            audioPlayer.numberOfLoops = -1
        } catch {
            print("error")
        }
        
    }

    func play(volume: Float, duration: Double) {
        audioPlayer.volume = 0
        audioPlayer.play()
        audioPlayer.setVolume(volume, fadeDuration: duration)
        self.playbackState = .playing
    }
    
    func pause(volume: Float, duration: Double) {
        audioPlayer.volume = 1
        audioPlayer.setVolume(volume, fadeDuration: duration)
        self.playbackState = paused
        DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
            guard self.playbackState == .paused else {
                return
            }
            self.audioPlayer.pause()    
        }
    }

}

Now, when your dispatchAfter executes, it will first check to see if the pause state is still required. If not, it will simply return, leaving the audio playing.

发布评论

评论列表(0)

  1. 暂无评论