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

Swift 将AudioUnit效果附加到SceneKit中的SCNAudioSource节点,swift,audiounit,Swift,Audiounit,我已经成功地创建了一些函数来获取SNSpeechSynthesizer.startSpeakingString(string,url),并通过闭包将生成的.aiff文件附加到SCNAudioPlayer,然后将整个内容附加到SCNNode 这将使用给定的SCNNode.position(x,y,z)运行音频片段 所有这些都很好,但是除了音量、速率和混响(本身不起任何作用)之外,SCNAudioSource没有任何影响 我想添加音频单元效果,如延迟回音法兰等,但除了AVAudioEngine和AV

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

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

所有这些都很好,但是除了音量、速率和混响(本身不起任何作用)之外,
SCNAudioSource
没有任何影响

我想添加音频单元效果,如延迟回音法兰等,但除了
AVAudioEngine
AVAudioFile
类之外,我找不到相关信息,这些类似乎是
SCNAudioSource
的超类,其中
AVAudioEngine
包含
AudioUnitPitc
h等类。但是,我不知道
SCNAudioSource
SCNAudioPlayer
AVAudioEngine
AVAudioFile
之间有什么联系。在sub或super类中,没有一个是指另一个,下面的任何教程仅在
AVFoundation
中使用,而不是在
SceneKit
中使用。

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

编辑:我找到了另一个链接,显示了使用and
AVAudioNode
SCNAudioPlayer
构造函数。或许我可以通过以下方式扩展这一点:

class CustomAudioPlayer:SCNAudioPlayer{}
然后通过将AudioUnit连接到AudioNode来覆盖超类init?然而,这似乎不会出现在AudioEngine中

以下是目标c中的链接:

编辑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的以下子类将 1-实例化具有特定配置的AVAudioEngine 2-添加了一些封装错误处理和影响预设加载的方法 3-使用连线方法将每个播放器和效果节点放入音频引擎图中 4-使用配置的帧计数和文件格式创建AVAudioPCMBuffer实例作为辅助方法,以方便从SceneKit调用这些函数

注:多声道代码未包括在内,因为我没有环绕声5.1系统,我已经非常满意AVAudioEnvironmentNode类中公开的HRTF(头部相关传递函数)算法。请注意,尽管该算法是双耳格式,但它是计算机密集型的

可能的补充: 1-添加混响区预设切换器,这将需要断开音频引擎,将环境节点重新布线到新的混响预设(大厅、小房间等) 2-从SceneKit SCNNode列表中创建基于光线投射的回声传输维度,以添加更真实的效果,即:您位于T形交叉点的中心栏,敌人在交叉点顶部栏的左侧拍打,声音穿过光线投射离开敌人,并反弹到面对您的墙壁上。AVAudioUnitDelay类具有内部函数,可以更改早期延迟以创建所需的回音效果,而无需在任何位置使用相同的效果清洗节点

代码如下:

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

要获得关于堆栈的最佳结果,请确保您的问题符合并确保您查看,欢迎访问堆栈溢出谢谢您的研究和精彩片段!createPlayer函数如何将voicePlayer附加到输入节点:SCNNode?该函数接受node:SCNNode作为输入,但从不使用它。