使用多个 AKPlayer 播放多个音频文件
Using multiple AKPlayers for multiple audio files playback
我正在尝试使用 AudioKit 的 AKPlayer 同步播放 4 个 mp3 文件,并且非常成功。不过,作为我 Swift 研究的一部分,我想使用数组(或我还没有学过的东西)来简化我的代码,因为我觉得我的代码中有些多余的东西,只是简单地复制玩家代码四次。下面是我写的代码:
let file1 = try? AKAudioFile(readFileName: "mixing_1_vocal.mp3")
let file2 = try? AKAudioFile(readFileName: "mixing_2_drums.mp3")
let file3 = try? AKAudioFile(readFileName: "mixing_3_synth.mp3")
let file4 = try? AKAudioFile(readFileName: "mixing_4_bass.mp3")
let player1 = AKPlayer(audioFile: file1!)
let player2 = AKPlayer(audioFile: file2!)
let player3 = AKPlayer(audioFile: file3!)
let player4 = AKPlayer(audioFile: file4!)
let startTime = AVAudioTime.now() + 0.25
let mixer = AKMixer()
player1 >>> mixer
player2 >>> mixer
player3 >>> mixer
player4 >>> mixer
player1.isLooping = true
player1.buffering = .always
player2.isLooping = true
player2.buffering = .always
player3.isLooping = true
player3.buffering = .always
player4.isLooping = true
player4.buffering = .always
AudioKit.output = mixer
try? AudioKit.start()
player1.start(at: startTime)
player2.start(at: startTime)
player3.start(at: startTime)
player4.start(at: startTime)
就是它了!有四个不同的曲目(单独的乐器),它们应该同时播放,无限循环。如果有人可以帮助我改进我的代码,那将非常有帮助。有没有更好的方法来完成同样的工作?
这是一个非常开放的问题,但这里有一个 iOS 的示例 ViewController。
class ViewController: UIViewController {
let mixer = AKMixer()
let players: [AKPlayer] = {
do {
let filenames = ["mixing_1_vocal.mp3",
"mixing_2_drums.mp3",
"mixing_3_synth.mp3",
"mixing_4_bass.mp3"]
return try filenames.map { AKPlayer(audioFile: try AKAudioFile(readFileName: [=10=])) }
} catch {
fatalError()
}
}()
override func viewDidLoad() {
super.viewDidLoad()
makeConnections()
startAudioEngine()
preparePlayers()
startPlayers()
}
func makeConnections() {
players.forEach { [=10=] >>> mixer }
AudioKit.output = mixer
}
func startAudioEngine() {
do {
try AudioKit.start()
} catch {
print(error)
fatalError()
}
}
func preparePlayers() {
players.forEach { player in
player.isLooping = true
player.buffering = .always
player.prepare()
}
}
func startPlayers() {
let startTime = AVAudioTime.now() + 0.25
players.forEach { [=10=].start(at: startTime) }
}
}
要点:
使用 do {try expression} catch { error }
,而不是 try? expression
。虽然忽略错误很方便,但它会很快咬住你。
Map and forEach,地图对于处理数组至关重要。请注意文件名数组是如何转换为 AKPlayer 数组的。相关的,shorthand 参数名称:players.forEach { player in player.play() }
与 players.forEach { [=14=].play() }
相同。同样相关的是,地图重新抛出,所以你可以在地图的闭包内部尝试。
使用闭包进行封装。 players 变量按照使用立即评估的闭包定义的方式进行填充。另一种方法是必须在初始化时创建这个数组,然后再填充它。
最后,尽可能将逻辑分离成函数。它使您的代码更具可读性,并且更易于维护。
我正在尝试使用 AudioKit 的 AKPlayer 同步播放 4 个 mp3 文件,并且非常成功。不过,作为我 Swift 研究的一部分,我想使用数组(或我还没有学过的东西)来简化我的代码,因为我觉得我的代码中有些多余的东西,只是简单地复制玩家代码四次。下面是我写的代码:
let file1 = try? AKAudioFile(readFileName: "mixing_1_vocal.mp3")
let file2 = try? AKAudioFile(readFileName: "mixing_2_drums.mp3")
let file3 = try? AKAudioFile(readFileName: "mixing_3_synth.mp3")
let file4 = try? AKAudioFile(readFileName: "mixing_4_bass.mp3")
let player1 = AKPlayer(audioFile: file1!)
let player2 = AKPlayer(audioFile: file2!)
let player3 = AKPlayer(audioFile: file3!)
let player4 = AKPlayer(audioFile: file4!)
let startTime = AVAudioTime.now() + 0.25
let mixer = AKMixer()
player1 >>> mixer
player2 >>> mixer
player3 >>> mixer
player4 >>> mixer
player1.isLooping = true
player1.buffering = .always
player2.isLooping = true
player2.buffering = .always
player3.isLooping = true
player3.buffering = .always
player4.isLooping = true
player4.buffering = .always
AudioKit.output = mixer
try? AudioKit.start()
player1.start(at: startTime)
player2.start(at: startTime)
player3.start(at: startTime)
player4.start(at: startTime)
就是它了!有四个不同的曲目(单独的乐器),它们应该同时播放,无限循环。如果有人可以帮助我改进我的代码,那将非常有帮助。有没有更好的方法来完成同样的工作?
这是一个非常开放的问题,但这里有一个 iOS 的示例 ViewController。
class ViewController: UIViewController {
let mixer = AKMixer()
let players: [AKPlayer] = {
do {
let filenames = ["mixing_1_vocal.mp3",
"mixing_2_drums.mp3",
"mixing_3_synth.mp3",
"mixing_4_bass.mp3"]
return try filenames.map { AKPlayer(audioFile: try AKAudioFile(readFileName: [=10=])) }
} catch {
fatalError()
}
}()
override func viewDidLoad() {
super.viewDidLoad()
makeConnections()
startAudioEngine()
preparePlayers()
startPlayers()
}
func makeConnections() {
players.forEach { [=10=] >>> mixer }
AudioKit.output = mixer
}
func startAudioEngine() {
do {
try AudioKit.start()
} catch {
print(error)
fatalError()
}
}
func preparePlayers() {
players.forEach { player in
player.isLooping = true
player.buffering = .always
player.prepare()
}
}
func startPlayers() {
let startTime = AVAudioTime.now() + 0.25
players.forEach { [=10=].start(at: startTime) }
}
}
要点:
使用 do {try expression} catch { error }
,而不是 try? expression
。虽然忽略错误很方便,但它会很快咬住你。
Map and forEach,地图对于处理数组至关重要。请注意文件名数组是如何转换为 AKPlayer 数组的。相关的,shorthand 参数名称:players.forEach { player in player.play() }
与 players.forEach { [=14=].play() }
相同。同样相关的是,地图重新抛出,所以你可以在地图的闭包内部尝试。
使用闭包进行封装。 players 变量按照使用立即评估的闭包定义的方式进行填充。另一种方法是必须在初始化时创建这个数组,然后再填充它。
最后,尽可能将逻辑分离成函数。它使您的代码更具可读性,并且更易于维护。