将 AudioUnit 效果附加到 SceneKit 中的 SCNAudioSource 节点

Attaching AudioUnit effects to SCNAudioSource nodes in SceneKit

我已经成功创建了一些函数来获取 SNSpeechSynthesizer.startSpeakingString(string, url) 并将生成的 .aiff 文件通过闭包附加到 SCNAudioPlayer 并将整个文件附加到 SCNNode

这将运行给定 SCNNode.position(x, y, z) 的音频片段。

一切都很好,但没有与 SCNAudioSource 相关的效果,除了音量、速率和混响本身什么都不做。

我想添加音频单元效果,如延迟回声法兰等,但除了AVAudioEngineAVAudioFile classes 似乎没有找到相关信息是 SCNAudioSource 的超级 classes,其中 AVAudioEngine 包含 classes,例如 AudioUnitPitch 等。但是,我无法弄清楚 SCNAudioSourceSCNAudioPlayerAVAudioEngineAVAudioFile 之间的联系是什么。 None 指的是 sub 或 super class 中的另一个,下面的任何教程仅在 AVFoundation 内讲,而不是 SceneKithttp://www.jawadrashid.com/swift-tutorial-udacity-7/

任何帮助link 的地方我可以阅读更多关于这非常感谢

编辑:我找到了另一个 link,它显示了使用 AVAudioNodeSCNAudioPlayer 构造函数。也许我可以通过以下方式扩展它:

class CustomAudioPlayer:SCNAudioPlayer{}

然后通过将 AudioUnit 附加到 AudioNode 来覆盖超级 class 初始化?但是,这似乎不会出现在 AudioEngine 中。

下面是 objective c 中的 link:

http://developer.xamarin.com/api/constructor/SceneKit.SCNAudioPlayer.SCNAudioPlayer/p/AVFoundation.AVAudioNode/

编辑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