2

I created a SCNNode and added an Audio to it.

It is a Mono audio. Everything is set up correctly.

It is working as Spatial Audio, that's not the problem.

The problem is that as i get closer or far away it barely changes the volume. I know it changes if i get very very far away, but it's nothing like Apple demonstrated here:

https://youtu.be/d9kb1LfNNU4?t=23

Some other games i see the audio volume really changing from one step distance.

With mine, with one step you can't even tell the volume changed. You need at least 4 steps.

Anyone has any clue why?

Code bellow:

SCNNode *audioNode = [[SCNNode alloc] init];
SCNAudioSource *audioSource = [[SCNAudioSource alloc] initWithFileNamed:audioFileName];
audioSource.loops = YES;
[audioSource load];
audioSource.volume = 0.05; // <-- i used different values. won't change much either
audioSource.positional = YES;
//audioSource.shouldStream = NO; // <-- makes no difference
[audioNode addAudioPlayer:[SCNAudioPlayer audioPlayerWithSource:audioSource]];

[audioNode runAction:[SCNAction playAudioSource:audioSource waitForCompletion:NO] completionHandler:nil];
[massNode addChildNode:audioNode];

Maybe scale of the nodes?

The whole scene is the size of around 4 feet.

When i add an object i usually scale it to 0.005 (otherwise it gets way too big). But i also tried with one that was already in the right size from .scn file.

It shouldn't affect anything tho, since the result is a coffee table size scene and i can see the objects alright.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Alan Cruz
  • 83
  • 1
  • 11

2 Answers2

2

Updated.

Here's a working code for controlling sound's decay (works in iOS and macOS):

import AVFoundation
import ARKit

class ViewController: UIViewController, AVAudioMixing {

    @IBOutlet var sceneView: SCNView!
    // @IBOutlet var sceneView: ARSCNView!
    
    func destination(forMixer mixer: AVAudioNode,
                                bus: AVAudioNodeBus) -> AVAudioMixingDestination? {
        return nil
    }
    var volume: Float = 0.0
    var pan: Float = 0.0
    
    var sourceMode: AVAudio3DMixingSourceMode = .bypass
    var pointSourceInHeadMode: AVAudio3DMixingPointSourceInHeadMode = .bypass
    
    var renderingAlgorithm = AVAudio3DMixingRenderingAlgorithm.sphericalHead
    var rate: Float = 1.2
    var reverbBlend: Float = 40.0
    var obstruction: Float = -100.0
    var occlusion: Float = -100.0
    var position = AVAudio3DPoint(x: 0, y: 0, z: 10)
    let audioNode = SCNNode()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let myScene = SCNScene()
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(0, 0, 0)
        myScene.rootNode.addChildNode(cameraNode)
        
        // let sceneView = view as! SCNView
        sceneView.scene = myScene
        sceneView.backgroundColor = UIColor.orange
        
        let myPath = Bundle.main.path(forResource: "Mono_Audio", ofType: "mp3")           
        let myURL = URL(fileURLWithPath: myPath!)
        let mySource = SCNAudioSource(url: myURL)!
        mySource.loops = true
        mySource.isPositional = true           // Positional Audio
        mySource.shouldStream = false          // FALSE for Positional Audio
        mySource.volume = volume
        mySource.reverbBlend = reverbBlend
        mySource.rate = rate

        mySource.load()
        
        let player = SCNAudioPlayer(source: mySource)
        let sphere: SCNGeometry = SCNSphere(radius: 0.1)
        let sphereNode = SCNNode(geometry: sphere)
        sphereNode.addChildNode(audioNode)
        myScene.rootNode.addChildNode(sphereNode)
        audioNode.addAudioPlayer(player)            

        sceneView.audioEnvironmentNode.distanceAttenuationParameters.maximumDistance = 2
        sceneView.audioEnvironmentNode.distanceAttenuationParameters.referenceDistance = 0.1   
        sceneView.audioEnvironmentNode.renderingAlgorithm = .auto

        // sceneView.audioEnvironmentNode.reverbParameters.enable = true
        // sceneView.audioEnvironmentNode.reverbParameters.loadFactoryReverbPreset(.plate)
        
        let hither = SCNAction.moveBy(x: 0, y: 0, z: 1, duration: 2)
        let thither = SCNAction.moveBy(x: 0, y: 0, z: -1, duration: 2)
        
        let sequence = SCNAction.sequence([hither, thither])
        let loop = SCNAction.repeatForever(sequence)
        sphereNode.runAction(loop) 
    }
}

And, yes, you're absolutely right – there are some obligatory settings.

But there are 7 of them:

  • use AVAudioMixing protocol with its stubs (properties and methods).

  • use MONO audio file.

  • use source.isPositional = true.

  • use source.shouldStream = false.

  • assign maximumDistance value to distanceAttenuationParameters property.

  • assign referenceDistance value to distanceAttenuationParameters property.

  • and location of mySource.load() is very important in your code.

P.S. If the aforementioned tips didn't help you, then use additional instance properties to make your sound even quieter using a graph, obstacles and orientation of implicit listener:

var rolloffFactor: Float { get set }      // attenuation's graph, default = 1 

var obstruction: Float { get set }        // default = 0.0

var occlusion: Float { get set }          // default = 0.0

var listenerAngularOrientation: AVAudio3DAngularOrientation { get set } //(0,0,0)

It definitely works if you'll write it in Objective-C.

In this example the distance of audioNode is 1 meter away from a listener.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • it's clear that doesn't matter the value i use, they have no effect.. the attenuation is just the same.. – Alan Cruz Sep 22 '19 at 21:15
  • Yes, and it doesn't make any difference. Like i said before, it woks but you need at least 4 meters to notice any lowering of the volume, the same way it was with the default Spatial Audio setup. I wanted something that would lower the volume close to zero in 1 meter = 1. – Alan Cruz Sep 23 '19 at 19:23
  • Wait a minute.. i think i got this.. just another test – Alan Cruz Sep 23 '19 at 20:16
  • 1
    MAN IT DID IT!!.. that's awesome. One thing i found out is that if you use a number out of their precision (not accepted) like referenceDistance = 0.01; it will reset to default (without any warning) and it will work but like i said before.. And you never know what actually went wrong. – Alan Cruz Sep 23 '19 at 20:36
  • It sounds nice)) with decay)) – Andy Jazz Sep 23 '19 at 20:37
  • it sounds awesome.. way more real.. it's a space game i'm building for AR – Alan Cruz Sep 23 '19 at 20:53
  • Hey @ARGeo i have a question for you.. are you there? – Alan Cruz Sep 27 '19 at 20:54
  • Yep. What's the question? – Andy Jazz Sep 27 '19 at 20:55
  • how do you stop and re-play an audio when it's in a node using spatial audio like that? i know how to do it by removing the player and re-loading the audio source to a new player but it's not practical in a high-end game with hundreds of elements moving around and dozens of sound effects at the same time being played – Alan Cruz Sep 27 '19 at 20:56
  • example: i have this .mp3 i pre-load on audio source.. but multiple nodes will be playing it back and forth (imagine lazer shots everywhere).. and some with different volume levels (but using the same audio source) – Alan Cruz Sep 27 '19 at 20:58
  • the problem: i even tried to run with a single node playing the same audio multiple times by hitting "BURN" multiple times per second.. and it crashes..not exactly playing audio but it affects everything i guess.. Seems to me ARKit is not quite advanced to deal with complex scenarios .. yet some games managed to do it like "Machines" (yet, they keep it a very small map. – Alan Cruz Sep 27 '19 at 21:00
  • deleting the audio node is even worse.. even if i do it on renderer:updateAtTime:.. when i hit the button pretty fast it crashes EXC_BAD_ACCESS, even tho i do all the pre-check before deleting.. it crashed once on AddChildName.. even tho the parent node always exists.. it's never deleted.. it's always there – Alan Cruz Sep 27 '19 at 21:02
  • Have you tried using multiple sound effects being played and stopped pretty fast almost at the same time? – Alan Cruz Sep 27 '19 at 21:04
  • i know how to play using avfoundation.. i've done it many times before.. my problem here is with this ARkit Spatial Way of playing by adding in a node.. there's not stop option.. so far people tell you to just remove the node.. or the players.. but it works for very simples trivial apps. unfortunately, i never develop trivial ones lol – Alan Cruz Sep 27 '19 at 21:13
  • i can stop by getting the playing engine.. cool BUT for some reason you can not play it again.. you need to re-load again the Audio Source to the player.. and it's just the same as deleting and adding again the audio node.. if you try to do it dozens of times per second (multiple elements shooting) it crashes... EXC_BAD_ACCESS without a real reason.. – Alan Cruz Sep 27 '19 at 21:15
  • There's stop option in that post. It's good for nodal hierarchy too. – Andy Jazz Sep 27 '19 at 21:16
  • Ok, I see now. Sorry but I know only this method. Suppose, there's something else but I don't know other ones.(( – Andy Jazz Sep 27 '19 at 21:18
  • I know.. i've used it before. SCNAudioPlayer there's none.. and even if you get the engine or player inside it, it doesn't have a PLAYBACK.. the startaudiowitherror also doesn't work after you stopped.. you have to runaction playAudio over again and for that you need to reload the AudioSource on it.. which is just the same as deleting the node and re-adding it over.. which is not practical in very active scenes.. – Alan Cruz Sep 27 '19 at 21:22
  • 1
    yeah :/ i think it is not documents on PURPOSE .. really.. i got some friends in the valley who worked with apple before.. he used to tell me how big companies used to take advantage of getting documentation on things all the rest wouldn't get.. so they can make the best games before anyone could have time and money to catch up with them – Alan Cruz Sep 27 '19 at 21:24
  • 1
    Trying to use the code to see what the sound experience is like but having some trouble. Getting a Fatal error: Unexpectedly found nil while unwrapping an Optional value. in reference to myURL – skynard Aug 09 '20 at 23:39
  • I checked it before publishing, the code worked fine. Try using `guard let` optional binding then... What's your path? It found nil in case your path (or name) is wrong. – Andy Jazz Aug 10 '20 at 07:30
0

If none of the above answers seem to work, try the following code:

sceneView.audioEnvironmentNode.reverbParameters.enable = true

And if even these seem to barely work, or if you wanna optimal performance, there is a property called level where you can set the level of how spatial the code can be.

sceneView.audioEnvironmentNode.reverbParameters.level = 40

(the level of the reverbParameters ranges between -40 to 40 parameters)