【发布时间】:2015-10-15 18:30:28
【问题描述】:
背景:我发现了一个名为“实践中的 AVAudioEngine”的 Apple WWDC 会议,并正在尝试制作类似于 43:35 (https://youtu.be/FlMaxen2eyw?t=2614) 显示的上一个演示的内容。我使用的是 SpriteKit 而不是 SceneKit,但原理是一样的:我想生成球体,将它们扔来扔去,当它们碰撞时,引擎会播放每个球体独有的声音。
问题:
我想要一个唯一的 AudioPlayerNode 附加到每个 SpriteKitNode,以便我可以为每个球体播放不同的声音。即现在,如果我创建两个球体并为它们的每个 AudioPlayerNode 设置不同的音高,即使原始球体发生碰撞,似乎也只有最近创建的 AudioPlayerNode 正在播放。在演示期间,他提到“我正在为每个球绑定一个球员,一个专注的球员”。我该怎么做呢?
每次发生新的碰撞时都会出现音频点击/伪影。我假设这与 AVAudioPlayerNodeBufferOptions 和/或我试图在每次发生联系时非常快速地创建、调度和使用缓冲区的事实有关,这不是最有效的方法。对此有什么好的解决方法?
代码:如视频中所述,“...对于每个诞生在这个世界上的球,也会创建一个新的玩家节点”。我有一个单独的球体类,它有一个返回 SpriteKitNode 的方法,并且每次调用它时都会创建一个 AudioPlayerNode:
class Sphere {
var sphere: SKSpriteNode = SKSpriteNode(color: UIColor(), size: CGSize())
var sphereScale: CGFloat = CGFloat(0.01)
var spherePlayer = AVAudioPlayerNode()
let audio = Audio()
let sphereCollision: UInt32 = 0x1 << 0
func createSphere(position: CGPoint, pitch: Float) -> SKSpriteNode {
let texture = SKTexture(imageNamed: "Slice")
let collisionTexture = SKTexture(imageNamed: "Collision")
// Define the node
sphere = SKSpriteNode(texture: texture, size: texture.size())
sphere.position = position
sphere.name = "sphere"
sphere.physicsBody = SKPhysicsBody(texture: collisionTexture, size: sphere.size)
sphere.physicsBody?.dynamic = true
sphere.physicsBody?.mass = 0
sphere.physicsBody?.restitution = 0.5
sphere.physicsBody?.usesPreciseCollisionDetection = true
sphere.physicsBody?.categoryBitMask = sphereCollision
sphere.physicsBody?.contactTestBitMask = sphereCollision
sphere.zPosition = 1
// Create AudioPlayerNode
spherePlayer = audio.createPlayer(pitch)
return sphere
}
这是我创建 AudioPCMBuffers 和 AudioPlayerNodes 的音频类
class Audio {
let engine: AVAudioEngine = AVAudioEngine()
func createBuffer(name: String, type: String) -> AVAudioPCMBuffer {
let audioFilePath = NSBundle.mainBundle().URLForResource(name as String, withExtension: type as String)!
let audioFile = try! AVAudioFile(forReading: audioFilePath)
let buffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: UInt32(audioFile.length))
try! audioFile.readIntoBuffer(buffer)
return buffer
}
func createPlayer(pitch: Float) -> AVAudioPlayerNode {
let player = AVAudioPlayerNode()
let buffer = self.createBuffer("PianoC1", type: "wav")
let pitcher = AVAudioUnitTimePitch()
let delay = AVAudioUnitDelay()
pitcher.pitch = pitch
delay.delayTime = 0.2
delay.feedback = 90
delay.wetDryMix = 0
engine.attachNode(pitcher)
engine.attachNode(player)
engine.attachNode(delay)
engine.connect(player, to: pitcher, format: buffer.format)
engine.connect(pitcher, to: delay, format: buffer.format)
engine.connect(delay, to: engine.mainMixerNode, format: buffer.format)
engine.prepare()
try! engine.start()
return player
}
}
然后在我的 GameScene 类中测试碰撞,安排缓冲区并在发生接触时播放 AudioPlayerNode
func didBeginContact(contact: SKPhysicsContact) {
let firstBody: SKPhysicsBody = contact.bodyA
if (firstBody.categoryBitMask & sphere.sphereCollision != 0) {
let buffer1 = audio.createBuffer("PianoC1", type: "wav")
sphere.spherePlayer.scheduleBuffer(buffer1, atTime: nil, options: AVAudioPlayerNodeBufferOptions.Interrupts, completionHandler: nil)
sphere.spherePlayer.play()
}
}
我是 Swift 新手,只有基本的编程知识,因此欢迎提出任何建议/批评。
【问题讨论】:
标签: ios swift audio swift2 core-audio