Controlling the volume of spacialized audio in RealityKit

44 Views Asked by At

I have various programmatic Entity objects playing audio in my RealityView, and that is working just fine with the following code snippets:

// my audio manager has an enum with all my sound effects that looks something like:
    enum SFXType: String, CaseIterable {
        case click = "SFX_menu_blip"
        // more cases here...
    }

// it also has a volume property for sfx
var volumeSFX: Double = 1.0

// in the init of my audio manager
        for type in SFXType.allCases {
            if let resource = try? AudioFileResource.load(named: type.rawValue) {
                sfxResource[type] = resource
            }
        }

// then later when I want to play the SFXType
    public func play(
        effect: SFXType,
        onEntity: Entity? = nil
    ) {
        // volume is a double, between 0.0 and 1.0
        guard volumeSFX > 0.01 else { return }
        if effect.shouldPlayOnEntity() {
            if let onEntity,
               let resource = sfxResource[effect] {
                // someday, set the volume here, maybe with something like:
//                onEntity.spatialAudio?.gain
                onEntity.playAudio(resource)
            }
        }
    }

Again, the above does work, but I'd like to be able to play the audio at half-volume when volumeSFX is set to to 0.5.

Things to try:

  1. I have looked through the APIs, and it looks like there is a helper property on Entity named spatialAudio of type SpatialAudioComponent?, but if I break in my play function, it's always nil. Should I be setting this myself?

  2. I also see references in the docs to AudioMixGroup and AudioMixGroupComponent, but I'm not seeing any examples of how to use them.

  3. In the HappyBeam sample project, I found a reference to playing the audio with a snippet like this:

    let audioController = entity.prepareAudio(cloudSound)
    audioController.gain = 15
    audioController.play()

...but when I change this to use my own volume property (in the 0.0 to 1.0 range) it's always played at full volume.

1

There are 1 best solutions below

0
livingtech On

I was able to make the 3rd option work by adding the following decibel conversion function:

    private func volumeInDecibels(volume: Double) -> Double {
        return 20.0 * log10(volume)
    }

This was cribbed from this helpful answer: https://stackoverflow.com/a/37810295/18961

So my complete play function is now:

    public func play(
        effect: SFXType,
        onEntity: Entity? = nil
    ) {
        // volume is a double, between 0.0 and 1.0
        guard volumeSFX > 0.01 else { return }
        if effect.shouldPlayOnEntity() {
            if let onEntity,
               let resource = sfxResource[effect] {
                let audioController = onEntity.prepareAudio(resource)
                audioController.gain = volumeInDecibels(volume: volumeSFX)
                audioController.play()
            }
        }
    }