将 AudioUnit 效果附加到 SceneKit 中的 SCNAudioSource 节点
Attaching AudioUnit effects to SCNAudioSource nodes in SceneKit
我已经成功创建了一些函数来获取 SNSpeechSynthesizer.startSpeakingString(string, url)
并将生成的 .aiff 文件通过闭包附加到 SCNAudioPlayer
并将整个文件附加到 SCNNode
。
这将运行给定 SCNNode.position(x, y, z)
的音频片段。
一切都很好,但没有与 SCNAudioSource
相关的效果,除了音量、速率和混响本身什么都不做。
我想添加音频单元效果,如延迟回声法兰等,但除了AVAudioEngine
和AVAudioFile
classes 似乎没有找到相关信息是 SCNAudioSource
的超级 classes,其中 AVAudioEngine
包含 classes,例如 AudioUnitPitc
h 等。但是,我无法弄清楚 SCNAudioSource
、SCNAudioPlayer
、AVAudioEngine
和 AVAudioFile
之间的联系是什么。 None 指的是 sub 或 super class 中的另一个,下面的任何教程仅在 AVFoundation
内讲,而不是 SceneKit
。
http://www.jawadrashid.com/swift-tutorial-udacity-7/
任何帮助link 的地方我可以阅读更多关于这非常感谢
编辑:我找到了另一个 link,它显示了使用 AVAudioNode
的 SCNAudioPlayer
构造函数。也许我可以通过以下方式扩展它:
class CustomAudioPlayer:SCNAudioPlayer{}
然后通过将 AudioUnit 附加到 AudioNode 来覆盖超级 class 初始化?但是,这似乎不会出现在 AudioEngine 中。
下面是 objective c 中的 link:
编辑2:
我找到了对音频单元的引用并执行了以下操作,但现在我遇到了段错误
由于信号导致命令失败:分段错误:11
code:
let source = prepareSynth(welcome, url: URL)
source.volume = 500.0
source.reverbBlend = 30.0
source.rate = 0.8
let clip = SCNAudioPlayer(source: source)
let mixer = clip.audioNode as AVAudioNode!?
distortion.loadFactoryPreset(AVAudioUnitDistortionPreset.SpeechRadioTower)
mixer?.engine?.attachNode(distortion)
mixer?.engine?.connect(mixer!, to: distortion, format: nil)
return clip
因此,在进行大量研究以将任何可用的 AVAudioUnitEffec* 效果放入 SceneKit 场景之后,我终于找到了一个解决方案,经过测试、尝试和尝试。
下面AVAudioEngine的子class会
1-实例化具有特定配置的AVAudioEngine
2-添加了一些方法来封装错误处理和效果预设加载
3-使用连线方法将每个播放器和效果器节点放入音频引擎图中
4-创建 AVAudioPCMBuffer 实例,配置帧数,文件格式作为辅助方法,以简化从 SceneKit
调用这些函数
注意:多声道代码未包括在内,因为我没有环绕声 5.1 系统并且我已经对从 AVAudioEnvironmentNode class 公开的 HRTF(头部相关传输函数)算法感到非常满意。请注意,尽管此算法是双耳格式,但它是计算机密集度最高的算法。
可能的补充:
1-添加一个混响区域预设切换器,这需要断开音频引擎,将环境节点重新连接到新的混响预设(大厅、小房间等)
2-从 SceneKit SCNNode 列表中创建一个基于 RayCast 的回声传输维度以添加更逼真的效果,即:你在 T 形路口的中央栏,一个敌人在路口顶部栏的左侧滑行,声音穿过 RayCast 离开敌人并反弹到面对你的墙上。 AVAudioUnitDelay class 具有内部函数来更改早期延迟以创建所需的回声效果,而无需在任何地方使用相同的效果清洗节点。
代码在这里:
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
}
}
e
我已经成功创建了一些函数来获取 SNSpeechSynthesizer.startSpeakingString(string, url)
并将生成的 .aiff 文件通过闭包附加到 SCNAudioPlayer
并将整个文件附加到 SCNNode
。
这将运行给定 SCNNode.position(x, y, z)
的音频片段。
一切都很好,但没有与 SCNAudioSource
相关的效果,除了音量、速率和混响本身什么都不做。
我想添加音频单元效果,如延迟回声法兰等,但除了AVAudioEngine
和AVAudioFile
classes 似乎没有找到相关信息是 SCNAudioSource
的超级 classes,其中 AVAudioEngine
包含 classes,例如 AudioUnitPitc
h 等。但是,我无法弄清楚 SCNAudioSource
、SCNAudioPlayer
、AVAudioEngine
和 AVAudioFile
之间的联系是什么。 None 指的是 sub 或 super class 中的另一个,下面的任何教程仅在 AVFoundation
内讲,而不是 SceneKit
。
http://www.jawadrashid.com/swift-tutorial-udacity-7/
任何帮助link 的地方我可以阅读更多关于这非常感谢
编辑:我找到了另一个 link,它显示了使用 AVAudioNode
的 SCNAudioPlayer
构造函数。也许我可以通过以下方式扩展它:
class CustomAudioPlayer:SCNAudioPlayer{}
然后通过将 AudioUnit 附加到 AudioNode 来覆盖超级 class 初始化?但是,这似乎不会出现在 AudioEngine 中。
下面是 objective c 中的 link:
编辑2: 我找到了对音频单元的引用并执行了以下操作,但现在我遇到了段错误
由于信号导致命令失败:分段错误:11
code:
let source = prepareSynth(welcome, url: URL)
source.volume = 500.0
source.reverbBlend = 30.0
source.rate = 0.8
let clip = SCNAudioPlayer(source: source)
let mixer = clip.audioNode as AVAudioNode!?
distortion.loadFactoryPreset(AVAudioUnitDistortionPreset.SpeechRadioTower)
mixer?.engine?.attachNode(distortion)
mixer?.engine?.connect(mixer!, to: distortion, format: nil)
return clip
因此,在进行大量研究以将任何可用的 AVAudioUnitEffec* 效果放入 SceneKit 场景之后,我终于找到了一个解决方案,经过测试、尝试和尝试。
下面AVAudioEngine的子class会 1-实例化具有特定配置的AVAudioEngine 2-添加了一些方法来封装错误处理和效果预设加载 3-使用连线方法将每个播放器和效果器节点放入音频引擎图中 4-创建 AVAudioPCMBuffer 实例,配置帧数,文件格式作为辅助方法,以简化从 SceneKit
调用这些函数注意:多声道代码未包括在内,因为我没有环绕声 5.1 系统并且我已经对从 AVAudioEnvironmentNode class 公开的 HRTF(头部相关传输函数)算法感到非常满意。请注意,尽管此算法是双耳格式,但它是计算机密集度最高的算法。
可能的补充: 1-添加一个混响区域预设切换器,这需要断开音频引擎,将环境节点重新连接到新的混响预设(大厅、小房间等) 2-从 SceneKit SCNNode 列表中创建一个基于 RayCast 的回声传输维度以添加更逼真的效果,即:你在 T 形路口的中央栏,一个敌人在路口顶部栏的左侧滑行,声音穿过 RayCast 离开敌人并反弹到面对你的墙上。 AVAudioUnitDelay class 具有内部函数来更改早期延迟以创建所需的回声效果,而无需在任何地方使用相同的效果清洗节点。
代码在这里:
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
}
}
e