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 ↗Chapters
Code shown on screen · 12 snippets
Play vapor trail audio
// 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
// 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
// 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
// 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
import RealityKit
func configureDistanceAttenuation(for spaceshipHifi: Entity) {
spaceshipHifi.components.set(
SpatialAudioComponent(
gain: -18,
distanceAttenuation: .rolloff(factor: 4)
)
)
} Loudness variation
// 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
// 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
// Setting Audio Materials
asteroid.components.set(
AudioMaterialComponent(material: .rock)
)
spaceship.components.set(
AudioMaterialComponent(material: .plastic)
) Handling collision audio
// 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
// 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
// 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
// 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
Related sessions
-
32 min -
32 min -
17 min