swift - Xcode 8 Swift 3 改变音调的声音

标签 swift xcode audio avfoundation pitch

我正在尝试制作一个简单的游戏,当你击中某物时,它的音高会有所不同。我以为这很简单,但结果却有很多东西(其中大部分是我完全从别人那里复制的):

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()  
 }  

由于此代码似乎停止播放当前正在播放的任何声音以播放新声音,是否有办法改变此行为,使其不会停止播放任何内容?我尝试删除 engine.stop 和 engine.reset 位,但这只会使应用程序崩溃。此外,这段代码在频繁调用时速度非常慢。我可以做些什么来加快速度吗?经常需要这种击中声音。

最佳答案

每次播放声音时都在重置引擎!而且您正在创建额外的播放器节点 - 如果您只想同时播放一个音高变化的声音实例,它实际上比这要简单得多:

// instance variables
let engine = AVAudioEngine()
let audioPlayerNode = AVAudioPlayerNode()
let changeAudioUnitTime = AVAudioUnitTimePitch()

调用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()
}

并根据需要多次调用 hitSound():

func hitSound(value: Float) {
    changeAudioUnitTime.pitch = value

    audioPlayerNode.scheduleFile(file, at: nil, completionHandler: nil) // File is an AVAudioFile defined previously
}

附注音高可以向上或向下移动两个 Octave ,范围为 4 个 Octave ,并且位于 [-2400, 2400] 的数值范围内,单位为“音分”。

p.p.s AVAudioUnitTimePitch 是一项非常酷的技术。我们小时候肯定没有这样的东西。

更新

如果您想要多 channel ,您可以轻松设置多个播放器和音调节点,但是您必须在启动引擎之前选择 channel 数。下面是两个方法(很容易扩展到 n 个实例,并且您可能想要选择自己的方法来选择在所有播放时中断哪个 channel ):

// 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
}

关于swift - Xcode 8 Swift 3 改变音调的声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39893997/

相关文章:

ios - Swift - iOS NSPredicate 带有可选的 NSDecimalNumber

objective-c - 如何实现触摸相机图标时在 iOS 锁定屏幕中看到的行为?

swift - Swift 中的自定义 TableView 发送参数

iphone - 在标签栏 Controller 中制作几乎相似的标签的最佳方法

ios - iPhone音乐流

swift - 创建一类颜色

ios - Xcode 5 和为以前版本的 iOS 部署的应用程序

ios - Xcode 6.1 构建在设备上崩溃

javascript - 在没有用户交互的情况下如何下载和播放音频片段?

python-2.7 - 用Python播放声音?