Swift AVFoundation:结束 AudioSession 导致动画滞后

问题描述 投票:0回答:1

我有一个培训应用程序,用户可以在其中设置间隔时间,例如 30 秒。

我想做什么:

当用户开始训练时,动画开始。当时间结束时,我播放一个声音,动画重新开始。当我播放我的声音时,我还想避开可能从另一个应用程序播放的背景音乐。

问题:

问题是当我播放声音时,动画和声音有一个小的延迟。动画从第一个循环开始就停留在结束位置,不到一秒后声音开始播放,但它应该会立即播放。播放完声音后,动画再次开始。

当我不将audioSession-Category改回

.mixwithOthers
时,不会出现这个问题。但我必须更改它,否则背景音乐不会取消。还是有别的办法?

代码

计时器和动画:

ZStack{
            Circle()
                .trim(from: 0, to: 1)
                .stroke(colorScheme == .dark ? Color.white.opacity(0.08) : Color.black.opacity(0.09), style: StrokeStyle(lineWidth: 35, lineCap: .round))
                .frame(width: x - x/7, height: y - y/7)
            
            Circle()
                .trim(from: 0, to: intervallTimer.position)
                .stroke(intervallTimer.ringColor.convertToColor(), style: StrokeStyle(lineWidth: 35, lineCap: .round))
                .frame(width: x - x/7, height: y - y/7)
                .rotationEffect(.init(degrees: -90))
            
            VStack(spacing: 0){
                
                intervallTimer.time.inTrainingsFormat()
                
                Text(intervallTimer.currentEx)
                    .font(.system(size: 40))
                    .padding(.top, 20)
                    .multilineTextAlignment(.center)
                    .lineLimit(3)
                
            }
        }
.onAppear{
    self.timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
}
.onReceive(timer){ _ in
     Sound.playSounds(sound.wav)
     intervallTimer.position = 0.01
 }

播放声音:

class Sound{

static var audioPlayer:AVAudioPlayer?

static func playSounds(soundfile: String) {
    
    if let path = Bundle.main.path(forResource: soundfile, ofType: nil){
        let audioSession = AVAudioSession.sharedInstance()
        
        do{
            try audioSession.setCategory(.ambient, options: .duckOthers)
        
            audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
            audioPlayer?.prepareToPlay()
            
            DispatchQueue.global(qos: .background).async {
                audioPlayer?.play()
            }
            
            try audioSession.setCategory(.ambient, options: .mixWithOthers) //when I leave this line out the delay does not occur but the background sound stays quieter
            
        }catch {
            print("Error")
        }
    }
}



}

提前致谢:)

播放声音的新代码

class Sound{

var audioPlayer: AVAudioPlayer?
let audioSession = AVAudioSession.sharedInstance()

func prepareAudioPlayer(soundfile: String){
    if let path = Bundle.main.path(forResource: soundfile, ofType: nil){
        do{
            try audioSession.setCategory(.ambient, options: .duckOthers) // background music gets quieter after I call the function, but this should only happen when the sound is played
            audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
            audioPlayer?.prepareToPlay()
        }catch{
            print("Error - Coudnt create audioplayer")
        }
    }else{
        print("Error - Path not found")
    }
}

func play() {
    
    if let audioPlayer = audioPlayer{
        DispatchQueue.global(qos: .background).async { //is necessary, otherwise delay in animation is still there
            audioPlayer.play()
        }
    }else{
        print("Error - AudioPlayer is nil")
    }
}
}
swift animation audio swiftui avfoundation
1个回答
0
投票

这里有很多错误。看起来你每秒调用

playSounds
10 次,这没有意义。通常你会在没有计时器的情况下调用一次。

接下来,你在最后一刻加载文件,这需要一些时间。理想情况下你应该设置你的 AVAudioSession 和 AVAudioPlayer 一次,最好是在程序的早期,而不是你想玩的那一刻。

如果你想在玩的时候躲避别人,只需设置一次。目前尚不清楚为什么要不断切换模式。一般 AVAudioPlayer 会自动为你激活会话。如果您想要停止闪避,您可能需要停用该会话。但是你不应该需要不断切换模式,除非你在玩不同的东西有不同的需求。

你绝对不应该在后台队列中调用

.play
。它是异步的。这肯定会引起问题。

调用

prepareToPlay
然后立即调用
play
也是没有意义的。
prepareToPlay
的要点是在您可以在程序的早期排队音频资产并准备好播放的情况下,这样当您调用
play
时它会是即时的。

你要做的关键是大大简化这个。你有很多不需要的部分(定时器,多次设置会话选项,后台队列,

prepareToPlay
)。把所有这些都拿出来,让它在不闪避的情况下工作(它应该很短)。然后添加闪避。

© www.soinside.com 2019 - 2024. All rights reserved.