将AudioUnit效果附加到SceneKit

时间:2015-11-29 15:02:30

标签: swift audiounit

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

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

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

我想添加音频单元效果,例如延迟回声法兰等,但除了AVAudioEngine和AVAudioFile类之外我无法找到相关信息,这些类似乎是SCNAudioSource的超类,AVAudioEngine包含类如AudioUnitPitch和别的。但是,我无法弄清楚SCNAudioSource,SCNAudioPlayer AVAudioEngine和AVAudioFile之间的连接是什么。没有在sub或super类中引用另一个,并且任何教程(如下面的内容)仅在AVFoundation中讲话而不是在SceneKit中讲话。 http://www.jawadrashid.com/swift-tutorial-udacity-7/

任何关于任何链接的帮助我可以阅读更多有关此内容的信息

编辑:我找到另一个链接,显示使用和AVAudioNode的SCNAudioPlayer构造函数。也许我可以通过以下方式扩展它:

class CustomAudioPlayer:SCNAudioPlayer {}

然后通过将AudioUnit附加到AudioNode来覆盖超类init?但是,它似乎不会出现在AudioEngine中。

以下是目标c中的以下链接:

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

EDIT2: 我找到了对音频单元的引用并执行了以下操作,但现在我有一个段错误

命令因信号失败:分段错误:11

代码:         let source = prepareSynth(welcome,url:URL)                     source.volume = 500.0                     source.reverbBlend = 30.0                     source.rate = 0.8                     let clip = SCNAudioPlayer(来源:来源)                     让mixer = clip.audioNode为AVAudioNode!?                     distortion.loadFactoryPreset(AVAudioUnitDistortionPreset.SpeechRadioTower)                     混频器?.engine?.attachNode(失真)                     混音器?.engine?。connect(混音器!,到:失真,格式:无)                     返回剪辑

1 个答案:

答案 0 :(得分:2)

经过大量的研究以获得任何可用的AVAudioUnitEffec *效果到SceneKit场景之后,我终于得到了一个解决方案,经过测试,尝试和玩过。

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

注意:未包含多声道代码,因为我没有环绕声5.1系统,并且我已经非常满意从AVAudioEnvironmentNode类公开的HRTF(头部相关传输函数)算法。请注意,虽然这种算法是双耳格式,但计算机密集程度最高。

可能的补充: 1-添加混响区预设切换器,需要断开音频引擎,将环境节点重新连接到新的混响预设(大厅,小房间等) 2 - 从SceneKit SCNNode列表创建一个基于RayCast的回声传递维度,以添加更逼真的效果,IE:你位于T字交叉点的中央栏,一个敌人在交界处顶部栏的左边是奶油,声音穿过RayCast离开敌人,然后朝着面向你的墙壁弹跳。 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
    }

}

ë