首页 > 解决方案 > 3D Positional Audio – 沿 Y 轴和 Z 轴移动 SCNAudioPlayer

问题描述

使用 SceneKit,我可以在 x 轴上从左向右移动 audioNode,但在 y 和 z 轴上移动时遇到问题。我戴着耳机,所以我可以听到双耳(3d 音频)效果。我也在 MacOS 上运行它。

我的测试代码如下。有人可以让我知道我错过了什么吗?我会很感激的!

import Cocoa
import SceneKit

class ViewController: NSViewController {

    @IBOutlet weak var sceneView: SCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let path = Bundle.main.path(forResource: "Sounds/Test.mp3", 
                                         ofType: nil)

        let url = URL(fileURLWithPath: path!)
        let source = SCNAudioSource(url:url)!
        source.loops = true
        source.shouldStream = false
        source.isPositional = true
        source.load()

        let player = SCNAudioPlayer(source: source)
        let box = SCNBox(width: 100.0, 
                        height: 100.0, 
                        length: 100.0, 
                 chamferRadius: 100.0)
        let boxNode = SCNNode(geometry: box)
        let audioNode = SCNNode()
        boxNode.addChildNode(audioNode)

        let scene = SCNScene()
        scene.rootNode.addChildNode(boxNode)
        sceneView.scene = scene
        audioNode.addAudioPlayer(player)

        let avm = player.audioNode as! AVAudioMixing
        avm.volume = 1.0
        let up = SCNAction.moveBy(x: 0, y: 100, z: 0, duration: 5)
        let down = SCNAction.moveBy(x: 0, y: -100, z: 0, duration: 5)
        let sequence = SCNAction.sequence([up, down])
        let loop = SCNAction.repeatForever(sequence)
        boxNode.runAction(loop)

        // Do any additional setup after loading the view.
    }
}

标签: swiftmacosscenekitscnaudioplayer

解决方案


更新

您正在转换player.audioNodetoAVAudioMixing协议:

let avm = player.audioNode as! AVAudioMixing

但不是它,你必须将它转换为一个类。代码如下所示:

let avm = player.audioNode as? AVAudioEnvironmentNode

任何符合AVAudioMixing协议的节点(例如,AVAudioPlayerNode)都可以在此环境中充当源。环境具有隐含的listener. 通过控制听者的位置和方向,应用程序控制用户体验虚拟世界的方式。该节点还定义了距离衰减和混响的属性,有助于表征环境。

并考虑到

只有与环境节点具有单声道连接格式的输入才会被空间化。If the input is stereo, the audio is passed through without being spatialized. 不支持具有两个以上通道的连接格式的输入。

而且,当然,您需要实现AVAudio3DMixing协议

这是一个工作代码:

import SceneKit
import AVFoundation

class ViewController: NSViewController, AVAudio3DMixing {

    // YOU NEED MONO AUDIO !!!

    var renderingAlgorithm = AVAudio3DMixingRenderingAlgorithm.sphericalHead
    var rate: Float = 0.0
    var reverbBlend: Float = 0.5
    var obstruction: Float = -100.0
    var occlusion: Float = -100.0
    var position: AVAudio3DPoint = AVAudio3DPoint(x: 0, y: 0, z: -100)

    override func viewDidLoad() {
        super.viewDidLoad()            
        let scene = SCNScene()

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.camera?.zFar = 200
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 40)
        scene.rootNode.addChildNode(cameraNode)

        let sceneView = self.view as! SCNView
        sceneView.scene = scene
        sceneView.backgroundColor = NSColor.black
        sceneView.autoenablesDefaultLighting = true

        let path = Bundle.main.path(forResource: "Test_Mono", ofType: "mp3")
        let url = URL(fileURLWithPath: path!)
        let source = SCNAudioSource(url: url)!
        source.loops = true
        source.shouldStream = false  // MUST BE FALSE
        source.isPositional = true
        source.load()

        let player = SCNAudioPlayer(source: source)
        let audioNode = SCNNode()

        let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.2)
        let boxNode = SCNNode(geometry: box)

        boxNode.addChildNode(audioNode)
        scene.rootNode.addChildNode(boxNode)

        audioNode.addAudioPlayer(player)
        let avm = player.audioNode as? AVAudioEnvironmentNode
        avm?.reverbBlend = reverbBlend
        avm?.renderingAlgorithm = renderingAlgorithm
        avm?.occlusion = occlusion
        avm?.obstruction = obstruction

        let up = SCNAction.moveBy(x: 0, y: 0, z: 70, duration: 5)
        let down = SCNAction.moveBy(x: 0, y: 0, z: -70 , duration: 5)
        let sequence = SCNAction.sequence([up, down])
        let loop = SCNAction.repeatForever(sequence)
        boxNode.runAction(loop)

        avm?.position = AVAudio3DPoint(
                 x: Float(boxNode.position.x),
                 y: Float(boxNode.position.y),
                 z: Float(boxNode.position.z))
    }
}

推荐阅读