我正在开发一个应用程序来练习与 16 个不同曲目的混音。我在一个函数中加载每个文件,然后按一个按钮即可播放所有文件,但它们在不同的时间开始播放,导致音频不同步。您知道如何确保一切同步吗?当这些文件在 DAW 中播放时,它们都完全同步,所以这不是问题,而是代码中的问题。
主应用程序文件
import swiftUI
import UIKit
@main
struct MixLabApp: App {
@StateObject var conductor = MixerClass()
var body: some Scene {
WindowGroup {
FreePlayMain()
.environmentObject(conductor)
}
}
}
Mixer 类(精简以便更容易理解)
import Foundation
import AudioKit
import AVFoundation
final class MixerClass: ObservableObject {
let engine = AudioEngine()
var mixer = Mixer()
@Published var track1 = AudioPlayer()
@Published var track2 = AudioPlayer()
@Published var track3 = AudioPlayer()
@Published var isTrack1Playing: Bool = false
@Published var isTrack2Playing: Bool = false
@Published var isTrack3Playing: Bool = false
init() {
do {
Settings.bufferLength = .medium
try AVAudioSession.sharedInstance().setPreferredIOBufferDuration((Settings.bufferLength.duration))
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetoothA2DP])
try AVAudioSession.sharedInstance().setActive(true)
} catch let err {
print(err)
}
do {
if let track1FileURL = Bundle.main.url(forResource: "DQ_Kick", withExtension: "mp3") {
try track1.load(url: track1FileURL)
} else {
Log("Could not find Track 1 file")
}
if let track2FileURL = Bundle.main.url(forResource: "DQ_Snare", withExtension: "mp3") {
try track2.load(url: track2FileURL)
} else {
Log("Could not find Track 2 file")
}
if let track3FileURL = Bundle.main.url(forResource: "DQ_Tom1", withExtension: "mp3") {
try track3.load(url: track3FileURL)
} else {
Log("Could not find Track 3 file")
}
track1.isLooping = true
track2.isLooping = true
track3.isLooping = true
}
func playStopToggle() {
isTrack1Playing ? track1.stop() : track1.start()
isTrack1Playing.toggle()
isTrack2Playing ? track2.stop() : track2.start()
isTrack2Playing.toggle()
isTrack3Playing ? track3.stop() : track3.start()
isTrack3Playing.toggle()
}
}
FreePlay主要
import SwiftUI
struct FreePlayMain: View {
@EnvironmentObject var conductor: MixerClass
var body: some View {
NavigationStack {
VStack {
VStack {
Slider(value: $conductor.track1.volume, in: 0...1)
.padding()
Slider(value: $conductor.track2.volume, in: 0...1)
.padding()
Slider(value: $conductor.track3.volume, in: 0...1)
.padding()
}
playStopButton
}
}
}
private var playStopButton: some View {
Button(action: {
conductor.playStopToggle()
}) {
VStack {
Text("\(Image(systemName: conductor.isTrack1Playing ? "stop.fill" : "play.fill")) \(conductor.isTrack1Playing ? "Stop" : "Start") Track 1")
}
}
}
}
不确定您是否已经找到了解决方案,但几个月前我遇到了类似的问题,技巧是使用 .play(at:) 方法为所有曲目提供统一的开始时间。这就是该行在我的playbackChannels中的样子,它是一个对象数组,每个对象都包含一个audioKit播放器
self.playbackChannels.forEach{
$0.getAudioPlayer().play(from: 0, at: startTime)
}