gpt4 book ai didi

ios - 使用 AVAudioEngine 的详细信息

转载 作者:可可西里 更新时间:2023-11-01 01:38:06 29 4
gpt4 key购买 nike

背景:我发现了一个名为“AVAudioEngine in Practice”的 Apple WWDC session ,我正在尝试制作类似于 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 和 AudioPlayerNode 的音频类

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 的新手,只有基本的编程知识,所以欢迎任何建议/批评。

最佳答案

我一直在 scenekit 中研究 AVAudioEngine 并尝试做其他事情,但这将是您正在寻找的:

https://developer.apple.com/library/mac/samplecode/AVAEGamingExample/Listings/AVAEGamingExample_AudioEngine_m.html

它解释了以下过程:1-实例化你自己的AVAudioEngine子类2-为每个 AVAudioPlayer 加载 PCMBuffers 的方法3-更改环境节点的参数以适应大量弹球对象的混响

编辑:转换、测试并添加了一些功能:

1-创建 AVAudioEngine 的子类,例如将其命名为 AudioLayerEngine。这是为了访问 AVAudioUnit 效果,例如失真、延迟、音调和许多其他可用的 AudioUnits 效果。2-通过为音频引擎设置一些配置来初始化,例如渲染算法,如果您处于 2D 但想要 3D 效果,则公开 AVAudioEnvironmentNode 以播放 SCNNode 对象或 SKNode 对象的 3D 位置3-创建一些辅助方法来为您想要的每个 AudioUnit 效果加载预设4-创建一个辅助方法来创建一个音频播放器,然后将它添加到你想要的任何节点,因为 SCNNode 接受返回 [AVAudioPlayer] 或 [SCNAudioPlayer] 的 .audioPlayers 方法,所以次数不限5-开始播放。

我粘贴了整个类以供引用,这样您就可以按照自己的意愿构建它,但请记住,如果您将其与 SceneKit 或 SpriteKit 耦合,您将使用此音频引擎来管理所有声音,而不是 SceneKit 的内部 AVAudioEngine。这意味着您在 AwakeFromNib 方法期间在 gameView 中实例化它

import Foundation
import SceneKit
import AVFoundation

class AudioLayerEngine:AVAudioEngine{
var engine:AVAudioEngine!
var environment:AVAudioEnvironmentNode!
var outputBuffer:AVAudioPCMBuffer!
var voicePlayer:AVAudioPlayerNode!
var multiChannelEnabled:Bool!
//audio effects
let delay = AVAudioUnitDelay()
let distortion = AVAudioUnitDistortion()
let reverb = AVAudioUnitReverb()

override init(){
super.init()
engine = AVAudioEngine()
environment = AVAudioEnvironmentNode()

engine.attachNode(self.environment)
voicePlayer = AVAudioPlayerNode()
engine.attachNode(voicePlayer)
voicePlayer.volume = 1.0
outputBuffer = loadVoice()
wireEngine()
startEngine()
voicePlayer.scheduleBuffer(self.outputBuffer, completionHandler: nil)
voicePlayer.play()
}

func startEngine(){
do{
try engine.start()
}catch{
print("error loading engine")
}
}

func loadVoice()->AVAudioPCMBuffer{
let URL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("art.scnassets/sounds/interface/test", ofType: "aiff")!)
do{
let soundFile = try AVAudioFile(forReading: URL, commonFormat: AVAudioCommonFormat.PCMFormatFloat32, interleaved: false)
outputBuffer = AVAudioPCMBuffer(PCMFormat: soundFile.processingFormat, frameCapacity: AVAudioFrameCount(soundFile.length))
do{
try soundFile.readIntoBuffer(outputBuffer)
}catch{
print("somethign went wrong with loading the buffer into the sound fiel")
}
print("returning buffer")
return outputBuffer
}catch{
}
return outputBuffer
}

func wireEngine(){
loadDistortionPreset(AVAudioUnitDistortionPreset.MultiCellphoneConcert)
engine.attachNode(distortion)
engine.attachNode(delay)
engine.connect(voicePlayer, to: distortion, format: self.outputBuffer.format)
engine.connect(distortion, to: delay, format: self.outputBuffer.format)
engine.connect(delay, to: environment, format: self.outputBuffer.format)
engine.connect(environment, to: engine.outputNode, format: constructOutputFormatForEnvironment())

}

func constructOutputFormatForEnvironment()->AVAudioFormat{
let outputChannelCount = self.engine.outputNode.outputFormatForBus(1).channelCount
let hardwareSampleRate = self.engine.outputNode.outputFormatForBus(1).sampleRate
let environmentOutputConnectionFormat = AVAudioFormat(standardFormatWithSampleRate: hardwareSampleRate, channels: outputChannelCount)
multiChannelEnabled = false
return environmentOutputConnectionFormat
}

func loadDistortionPreset(preset: AVAudioUnitDistortionPreset){
distortion.loadFactoryPreset(preset)
}

func createPlayer(node: SCNNode){
let player = AVAudioPlayerNode()
distortion.loadFactoryPreset(AVAudioUnitDistortionPreset.SpeechCosmicInterference)
engine.attachNode(player)
engine.attachNode(distortion)
engine.connect(player, to: distortion, format: outputBuffer.format)
engine.connect(distortion, to: environment, format: constructOutputFormatForEnvironment())
let algo = AVAudio3DMixingRenderingAlgorithm.HRTF
player.renderingAlgorithm = algo
player.reverbBlend = 0.3
player.renderingAlgorithm = AVAudio3DMixingRenderingAlgorithm.HRTF
}

}

关于ios - 使用 AVAudioEngine 的详细信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33155925/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com