Skip to content

Audio

Sound in the scene: sources that play clips, quieter and panned by distance from a listener, plus clips decoded from WAV files or synthesised in code.

module: awake:scene:audio module: awake:core:audio Playback: Desktop

awake:core:audio has clips, WAV decoding, a sound synthesiser and the AudioPlayer contract. awake:scene:audio has the ECS side: AudioSource, AudioListener and AudioSystem. The scene DSL functions live in awake:scene:authoring.

Make a clip

An AudioClip is uncompressed PCM in memory. Decode one from WAV bytes, however you load them:

Kotlin
val footstep = WavDecoder.decode(id = "footstep", name = "Footstep", bytes = bytes) // null if it is not an uncompressed PCM WAV

Or synthesise one:

Kotlin
val chime = SoundSynthesizer.build(id = "chime", name = "Chime") {
    duration = 0.4f
    waveform = Waveform.SINE
    startFrequencyHz = 880f
    endFrequencyHz = 660f
}

SoundSynthesizer.build also takes waveform (SINE, SQUARE, TRIANGLE, SAWTOOTH, NOISE), an ADSR envelope, lowPassFilter and volume.

Add a sound source

A looping sound three metres to the side of a camera that hears it.

The scene runtime has no scene document component for audio yet. AwakeKt Studio saves an audio_source component, but only Studio registers it. Use the scene DSL.

Kotlin
world.scene {
    entity("camera") {
        transform(z = 5f)
        audioListener() // one per scene: spatial sounds are heard from here
    }
    entity("fountain") {
        transform(x = 3f)
        audioSource(chime, volume = 0.8f, maxDistance = 20f, loop = true)
    }
}

sound(clip, name, volume, maxDistance) spawns a whole emitter entity in one call.

  1. In the Hierarchy, click Add entity and choose Audio: Audio Source.
  2. In the Inspector, pick the Audio Asset, then set Volume, Spatial 3D, Max Distance, Auto Play and Loop.

Studio's audio source starts with Auto Play off.

Play it

AudioSystem starts, positions and stops sources through an AudioPlayer:

Kotlin
val player: AudioPlayer = NoOpAudioPlayer() // JvmAudioPlayer() plays through Java Sound on desktop
val audio = AudioSystem(player)
audio.update(world, 1f / 60f) // autoPlay sources start on their first update

Register it as a per-frame system in an app, for example frameSystem("audio") { AudioSystem(player) }. The player also plays music outside the ECS: playMusic(clip) and stopMusic(), with masterVolume, sfxVolume and musicVolume to mix them.

Properties

Property Type Default What it does
clip AudioClip required The sound to play.
volume number 1 Volume, from 0 to 1.
isSpatial3D boolean true Fade with distance and pan by direction from the listener. Off plays at volume, centred.
maxDistance number 50 Beyond this distance a spatial sound is silent.
autoPlay boolean true Start on the first update.
loop boolean false Repeat until stopped.

At run time AudioSource.isPlaying says whether it is playing, and stop() stops it.

How it works

Each update, AudioSystem finds the first entity with an AudioListener and a Transform, or uses the origin if there is none. A spatial source's volume falls off with its distance from the listener and reaches zero at maxDistance; its pan follows the listener's right-hand direction. A source whose sound has finished drops its handle, and isPlaying turns false.

Platform AudioPlayer
Desktop JvmAudioPlayer, through Java Sound.
Android AndroidAudioPlayer, through AudioTrack.
Web WebAudioPlayer, through Web Audio API.
iOS Supply your own AudioPlayer, or use NoOpAudioPlayer.

A spatial source out of range never auto-plays

autoPlay gets one attempt, on the first update. If the listener is beyond maxDistance then, the player returns no sound and the source does not try again. Set hasAutoPlayed back to false to retry, or place the source within range.

See also

  • Physics contact events, for impact sounds.
  • Large worlds, for streaming emitters with their cells.