Cookbook icon indicating copy to clipboard operation
Cookbook copied to clipboard

Audio3D mini-app: setting source1mixer3D.position does not affect sound

Open glangmead opened this issue 6 months ago • 8 comments

macOS Version(s) Used to Build

macOS 13 Ventura

Xcode Version(s)

Xcode 14

Description

When dragging around on the scene, only updateListenerPosition3D is called. Frankly this makes sense since the sphere appears to remain stationary. But the code handles a hypothetical movement of the sound source with

func updateSoundSourcePosition(_ position3D: AVAudio3DPoint) {
  source1mixer3D.position = position3D
}

If I change the code that calls updateSoundSourcePosition and pass it the pointOfView.forwardVector.[xyz] instead of passing soundSource.position.[xyz], then it becomes clear that the sound cannot be made to appear to move with source1mixer3D.position at all. If I want multiple sound sources moving independently in space, it's insufficient to manipulate only the listener position.

I lied about my Xcode and macOS version. I'm on macOS 26 and Xcode 26. But I was having trouble with this in the weeks before updating, on Xcode 16 and macOS 15.

Crash Logs, Screenshots or Other Attachments (if applicable)

No response

glangmead avatar Sep 23 '25 16:09 glangmead

@ForeverTangent Might know what's up.

NickCulbertson avatar Sep 23 '25 16:09 NickCulbertson

The code in this thread worked for me, I was able to update playerNode.position and hear the effect. That uses the AVFoundation calls directly, telling each connection what format to use, so that it forces mono when appropriate.

glangmead avatar Sep 23 '25 17:09 glangmead

Hey @glangmead. Yeah sound sources have to be mono. You have to do Ultra Black Magic to position Stereo signals. Sorry I can’t really offer much help this week. I am traveling for work, and only have my work computer. But if that thread worked, bonus! Peace

Staque

ForeverTangent avatar Sep 23 '25 18:09 ForeverTangent

I updated the Apple code from the thread I linked in my previous message to add another mixer between the AVAudioEnvironmentNode and the engine.mainMixerNode and this kills the spatialization. I tried this because that's how AudioKit appears to behave -- an extra mixer between my nodes and the engine's node. I'm trying now to modify AudioKit so as to not have an additional node, but I certainly don't know what I'm doing.

My non-AudioKit node hookups, as a change from the Apple sample code:

engine.connect(player, to: environmentalNode, format: mono)
engine.connect(environmentalNode, to: mixer, format: stereo)
engine.connect(mixer, to: engine.mainMixerNode, format: stereo)

glangmead avatar Sep 26 '25 20:09 glangmead

I will be home Sunday, but I think it is because you are introducing stereo connections. Stereo signals kill the 3D audio. If you want to maintain a ‘stereo field’ you have to pair two mono signals together but keep them separate positioning them relative to each other.

ForeverTangent avatar Sep 26 '25 21:09 ForeverTangent

I think my previous hunch was bogus. So to your point, what I'm doing now is printing the engine both from the Apple-supplied code and from my AudioKit code. I see no material difference. Same formats/channel count, same four-character descriptions of the nodes.

Engine that spatializes successfully:

________ GraphDescription ________
AVAudioEngineGraph 0xa13a08f00: initialized = 1, running = 1, number of nodes = 5

   ******** output chain ********

   node 0xa12cde100 {auou rioc appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0xa12e03900, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0xa12e03900 {aumx mcmx appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0xa12995080, {aumx 3dem appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
     outputs = 1
       (bus0, en1) -> (bus0) 0xa12cde100, {auou rioc appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0xa12995080 {aumx 3dem appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0xa12e03600, {aumx mcmx appl}, [ 1 ch,  44100 Hz, Float32]
     outputs = 1
       (bus0, en1) -> (bus0) 0xa12e03900, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0xa12e03600 {aumx mcmx appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0xa130b9cc0, {augn sspl appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
     outputs = 1
       (bus0, en1) -> (bus0) 0xa12995080, {aumx 3dem appl}, [ 1 ch,  44100 Hz, Float32]

   node 0xa130b9cc0 {augn sspl appl}, 'I'
     outputs = 1
       (bus0, en1) -> (bus0) 0xa12e03600, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
______________________________________

My AudioKit code's engine:

________ GraphDescription ________
AVAudioEngineGraph 0x842a8c900: initialized = 1, running = 1, number of nodes = 5

   ******** output chain ********

   node 0x842894480 {auou rioc appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0x842894540, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0x842894540 {aumx mcmx appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0x843964f00, {aumx 3dem appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
     outputs = 1
       (bus0, en1) -> (bus0) 0x842894480, {auou rioc appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0x843964f00 {aumx 3dem appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0x8428943c0, {aumx mcmx appl}, [ 1 ch,  44100 Hz, Float32]
     outputs = 1
       (bus0, en1) -> (bus0) 0x842894540, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]

   node 0x8428943c0 {aumx mcmx appl}, 'I'
     inputs = 1
       (bus0, en1) <- (bus0) 0x842a3ca00, {augn sspl appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
     outputs = 1
       (bus0, en1) -> (bus0) 0x843964f00, {aumx 3dem appl}, [ 1 ch,  44100 Hz, Float32]

   node 0x842a3ca00 {augn sspl appl}, 'I'
     outputs = 1
       (bus0, en1) -> (bus0) 0x8428943c0, {aumx mcmx appl}, [ 2 ch,  44100 Hz, Float32, deinterleaved]
______________________________________

glangmead avatar Sep 26 '25 23:09 glangmead

Hey @glangmead,

Sorry this took a moment. Trip was long, and then work was busy.

Ok sort version to what I think you need. You need to rewrite your code above that looks like this:

engine.connect(player, to: environmentalNode, format: mono)
engine.connect(environmentalNode, to: mixer, format: stereo)
engine.connect(mixer, to: engine.mainMixerNode, format: stereo)

to look more like this

engine.connect(player, to: environmentalNode, format: mono)
engine.connect(environmentalNode, to: mixer, format: mono)
engine.connect(mixer, to: engine.mainMixerNode, format: mono)

EnvironmentalNodes do not output stereo. (Well, they can, but Apple's system doesn't know what to do with the signal, because there is two sources instead of one.) I know it seems counter-intuitive, but all EnvironmentalNodes do is add 3D Positioning Meta-Data to their signal and then the EnvironmentMixer is what does the actual work changing the signal.

So basically everything must remain mono until it hits the environmentMixer and then you are ok.

I hope this helps.

Staque

ForeverTangent avatar Oct 02 '25 13:10 ForeverTangent

Both those sets of 3 lines produce audio that isn't 3D-spatialized. But I was only trying an experiment with player -> environmentNode -> mixer due to a misunderstanding. I printed my engine logging data to emphasize that in fact both the code that is 3D spatialized and the AudioKit code that is not have the same data in terms of sample rates and channel count. So whatever causes the AudioKit code not to spatialize is pretty mysterious to me!

I opened this issue because calling soundSource.position in the cookbook does not change the sound. The Audio 3D demo only feels 3D because the listener position is successfully updated and does affect the sound, but isn't a solution for having sounds move around independently.

glangmead avatar Oct 07 '25 00:10 glangmead