Mys*_*ake 3 audio xcode avfoundation pitch swift
所以,我之前在 Apple Developer Forums 上问过这个问题,但从来没有得到正确的答案,所以我想我会在这里问:
我正在尝试制作一个简单的游戏,当您击打某些东西时,该游戏的击打声会具有不同的音高。我认为这很简单,但最终得到了很多东西(其中大部分是我完全从别人那里复制的):
func hitSound(value: Float) {
let audioPlayerNode = AVAudioPlayerNode()
audioPlayerNode.stop()
engine.stop() // This is an AVAudioEngine defined previously
engine.reset()
engine.attach(audioPlayerNode)
let changeAudioUnitTime = AVAudioUnitTimePitch()
changeAudioUnitTime.pitch = value
engine.attach(changeAudioUnitTime)
engine.connect(audioPlayerNode, to: changeAudioUnitTime, format: nil)
engine.connect(changeAudioUnitTime, to: engine.outputNode, format: nil)
audioPlayerNode.scheduleFile(file, at: nil, completionHandler: nil) // File is an AVAudioFile defined previously
try? engine.start()
audioPlayerNode.play()
}
Run Code Online (Sandbox Code Playgroud)
由于此代码似乎停止播放当前正在播放的任何声音以播放新声音,有没有办法可以改变这种行为,使其不停止播放任何东西?我尝试删除 engine.stop 和 engine.reset 位,但这只会使应用程序崩溃。此外,这段代码在频繁调用时非常慢。有什么我可以做的来加快速度吗?非常频繁地需要这种击打声。
每次播放声音时,您都在重置引擎!并且您正在创建额外的播放器节点 - 如果您只想一次播放一个音高转换声音的实例,它实际上比这简单得多:
// instance variables
let engine = AVAudioEngine()
let audioPlayerNode = AVAudioPlayerNode()
let changeAudioUnitTime = AVAudioUnitTimePitch()
Run Code Online (Sandbox Code Playgroud)
调用setupAudioEngine()一次:
func setupAudioEngine() {
engine.attach(self.audioPlayerNode)
engine.attach(changeAudioUnitTime)
engine.connect(audioPlayerNode, to: changeAudioUnitTime, format: nil)
engine.connect(changeAudioUnitTime, to: engine.outputNode, format: nil)
try? engine.start()
audioPlayerNode.play()
}
Run Code Online (Sandbox Code Playgroud)
并hitSound()根据需要多次调用:
func hitSound(value: Float) {
changeAudioUnitTime.pitch = value
audioPlayerNode.scheduleFile(file, at: nil, completionHandler: nil) // File is an AVAudioFile defined previously
}
Run Code Online (Sandbox Code Playgroud)
ps 音高可以向上或向下移动两个八度,范围为 4 个八度,其数值范围为 [-2400, 2400],单位为“分”。
ppsAVAudioUnitTimePitch是很酷的技术。当我还是个孩子的时候,我们绝对没有这样的东西。
更新
如果你想要多通道,你可以很容易地设置多个播放器和音高节点,但是你必须在启动引擎之前选择通道的数量。以下是您将如何执行两个操作(很容易扩展到 n 个实例,并且您可能想要选择自己的方法来选择在所有播放时中断哪个频道):
// instance variables
let engine = AVAudioEngine()
var nextPlayerIndex = 0
let audioPlayers = [AVAudioPlayerNode(), AVAudioPlayerNode()]
let pitchUnits = [AVAudioUnitTimePitch(), AVAudioUnitTimePitch()]
func setupAudioEngine() {
var i = 0
for playerNode in audioPlayers {
let pitchUnit = pitchUnits[i]
engine.attach(playerNode)
engine.attach(pitchUnit)
engine.connect(playerNode, to: pitchUnit, format: nil)
engine.connect(pitchUnit, to:engine.mainMixerNode, format: nil)
i += 1
}
try? engine.start()
for playerNode in audioPlayers {
playerNode.play()
}
}
func hitSound(value: Float) {
let playerNode = audioPlayers[nextPlayerIndex]
let pitchUnit = pitchUnits[nextPlayerIndex]
pitchUnit.pitch = value
// interrupt playing sound if you have to
if playerNode.isPlaying {
playerNode.stop()
playerNode.play()
}
playerNode.scheduleFile(file, at: nil, completionHandler: nil) // File is an AVAudioFile defined previously
nextPlayerIndex = (nextPlayerIndex + 1) % audioPlayers.count
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1458 次 |
| 最近记录: |