Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2024 Developer ToolsGraphics & GamesSpatial Computing

WWDC24 · 23 min · Developer Tools / Graphics & Games / Spatial Computing

Enhance your spatial computing app with RealityKit audio

Elevate your spatial computing experience using RealityKit audio. Discover how spatial audio can make your 3D immersive experiences come to life. From ambient audio, reverb, to real-time procedural audio that can add character to your 3D content, learn how RealityKit audio APIs can help make your app more engaging.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 12 snippets

Play vapor trail audio swift · at 3:11 ↗
// Vapor trail audio

import RealityKit

func playVaporTrailAudio(from engine: Entity) async throws {
    let resource = try await AudioFileResource(named: "VaporTrail")
    engine.playAudio(resource)
}
Make vapor trail audio playback more dynamic swift · at 4:02 ↗
// Vapor trail audio

import RealityKit

func playVaporTrailAudio(from engine: Entity) async throws {
    let resource = try await AudioFileResource(
        named: "VaporTrail",
        configuration: AudioFileResource.Configuration(
           shouldLoop: true,
           shouldRandomizeStartTime: true
        )
    )
    let controller: AudioPlaybackController = engine.playAudio(resource)
    controller.gain = -.infinity
    controller.fade(to: .zero, duration: 1)
  
    let audioSource = Entity()
    audioSource.orientation = .init(angle: .pi, axis: [0, 1, 0])
    audioSource.components.set(
        SpatialAudioComponent(directivity: .beam(focus: 0.25))
    )
    engine.addChild(audioSource)
    let controller = audioSource.playAudio(resource)

}
Exhaust audio swift · at 7:10 ↗
// Exhaust audio

import RealityKit

func updateAudio(for exhaust: Entity, throttle: Float) {
    let gain = decibels(amplitude: throttle)
    exhaust.components[SpatialAudioComponent.self]?.gain = Audio.Decibel(gain)
}

func decibels(amplitude: Float) -> Float { 20 * log10(amplitude) }
Turbine audio swift · at 8:17 ↗
// Turbine audio

import RealityKit

var turbineController: AudioGeneratorController?

func playTurbineAudio(from engine: Entity) {
    let audioUnit = try await AudioUnitTurbine.instantiate()
    let configuration = AudioGeneratorConfiguration(layoutTag: kAudioChannelLayoutTag_Mono)
    let format = AVAudioFormat(
        standardFormatWithSampleRate: Double(AudioGeneratorConfiguration.sampleRate),
        channelLayout: .init(layoutTag: configuration.layoutTag)!
    )
    try audioUnit.outputBusses[0].setFormat(format)
		try audioUnit.allocateRenderResources()
    let renderBlock = audioUnit.internalRenderBlock
    turbineController = try engine.playAudio(configuration: configuration) { 
        isSilence, timestamp, frameCount, outputData in
        var renderFlags = AudioUnitRenderActionFlags()
        return renderBlock(&renderFlags, timestamp, frameCount, 0, outputData, nil, nil)
    }
}
Setting distance attenuation and gain swift · at 11:28 ↗
import RealityKit

func configureDistanceAttenuation(for spaceshipHifi: Entity) {
    spaceshipHifi.components.set(
        SpatialAudioComponent(
            gain: -18,
            distanceAttenuation: .rolloff(factor: 4)
        )
    )
}
Loudness variation swift · at 12:36 ↗
// Loudness variation

import RealityKit

func handleCollisionBegan(_ collision: CollisionEvents.Began) {
    let resource: AudioFileGroupResource // …
    let controller = collision.entityA.playAudio(resource)
    controller.gain = relativeLoudness(for: collision)
}
Defining audio materials swift · at 14:44 ↗
// Audio materials

import RealityKit

enum AudioMaterial {
    case none
    case plastic
    case rock
    case metal
    case drywall
    case wood
    case glass
    case concrete
    case fabric
}

struct AudioMaterialComponent: Component {
    var material: AudioMaterial
}
Setting audio materials swift · at 14:53 ↗
// Setting Audio Materials

asteroid.components.set(
    AudioMaterialComponent(material: .rock)
)

spaceship.components.set(
    AudioMaterialComponent(material: .plastic)
)
Handling collision audio swift · at 15:04 ↗
// Audio materials

import RealityKit

func handleCollisionBegan(_ collision: CollisionEvents.Began) {
    guard 
        let audioMaterials = audioMaterials(for: collision),
        let resource: AudioFileGroupResource = collisionAudio[audioMaterials] 
    else {
        return 
    }
    let controller = collision.entityA.playAudio(resource)
    controller.gain = relativeLoudness(for: collision)
}
Reverb presets swift · at 17:18 ↗
// Reverb presets

import Studio

func prepareStudioEnvironment() async throws {
    let studio = try await Entity(named: "Studio", in: studioBundle)
    studio.components.set(
       ReverbComponent(reverb: .preset(.veryLargeRoom))
    )
    rootEntity.addChild(studio)
}
Immersive music swift · at 20:05 ↗
// Immersive music

import RealityKit

func playJoyRideMusic(from entity: Entity) async throws {
    let resource = try await AudioFileResource(
        named: “JoyRideMusic”,
        configuration: .init(
            loadingStrategy: .stream,
            shouldLoop: true       
        )
    )
    entity.components.set(AmbientAudioComponent())
    entity.playAudio(resource)
}
Using AudioMixGroup with a RealityKit entity swift · at 21:57 ↗
// Audio mix groups

import RealityKit

let resource = try await AudioFileResource(
    named: “JoyRideMusic”,
    configuration: .init(
        loadingStrategy: .stream,
        shouldLoop: true,
        mixGroupName: “Music”
    )
)

var audioMixerEntity = Entity()

func updateMixGroup(named mixGroupName: String, to level: Audio.Decibel) {
    var mixGroup = AudioMixGroup(name: mixGroupName)
    mixGroup.gain = level
    let component = AudioMixGroupsComponent(mixGroups: [mixGroup])
    audioMixerEntity.components.set(component)
}

Resources