Search Docs
Loading...
Skip to content

Audio

Add audio to your CE.SDK scenes with the Swift Engine API: create audio blocks from external files, extract tracks from video fills, control playback, manage timeline placement, generate waveform samples, and export audio data.

8 mins
estimated time
GitHub

Audio blocks let you add background music, voice-overs, sound effects, and other standalone sound to scenes. CE.SDK also exposes video audio track counts, playback controls, trim controls, waveform generation, and audio-only export through the Engine block API.

Playback examples require an Engine instance created with audioContext: .auto, which is the default. Engines created with audioContext: .none can still edit and export scenes, but they do not drive real-time audio playback.

Use Cases#

Use the CE.SDK audio APIs when you need to add or manage:

  • Background music
  • Voice-overs
  • Sound effects
  • Podcast or narration tracks

How Audio Works in CE.SDK#

CE.SDK represents standalone audio as .audio design blocks. Audio blocks attach to a page, reference their media through the audio/fileURI property, and use the same timeline properties as other time-based blocks.

Each audio block can have:

  • A source URI for an external audio file.
  • Playback properties such as volume, mute state, playback time, and speed.
  • Timeline properties such as offset, duration, trim offset, and trim length.
  • Waveform sample data for a custom timeline UI.

Extraction APIs create separate audio blocks from video fill tracks instead of modifying an existing audio block’s source.

Time-Based Properties#

Audio timing is expressed in seconds:

  • Offset: when the audio block starts relative to its parent.
  • Duration: how long the block stays active on the timeline.
  • Trim offset: where playback starts inside the source audio.
  • Trim length: how much of the source audio plays before it loops or stops. Use the looping APIs to choose that behavior.

Waveforms#

Waveforms are sampled audio amplitudes you can render in your own UI. generateAudioThumbnailSequence(_:samplesPerChunk:timeRange:numberOfSamples:numberOfChannels:) returns an AsyncThrowingStream<AudioThumbnail, Error> of chunks. Each chunk’s samples array holds normalized amplitudes in the range 0 to 1. Stereo requests interleave left and right samples.

When to Create vs. Extract Audio#

Create an audio block when the source is an external file such as background music or a voice-over. Extract audio when the sound already lives inside a video fill and you need a separate audio block for trimming, muting the original video, or independent volume control.

Examples#

The snippets below work against a scene with a page that has a timeline duration. Audio APIs run on the main thread through the same Engine instance as the rest of your scene edits.

Create Audio#

Create a standalone audio block, attach it to the page, set its source URI, then load the resource before reading metadata such as the source duration.

let audioBlock = try engine.block.create(.audio)
try engine.block.appendChild(to: page, child: audioBlock)
try engine.block.setURL(
audioBlock,
property: "audio/fileURI",
value: baseURL.appendingPathComponent("ly.img.audio/audios/far_from_home.m4a"),
)
try await engine.block.forceLoadAVResource(audioBlock)
let sourceDuration = try engine.block.getAVResourceTotalDuration(audioBlock)
print("Audio source duration: \(sourceDuration) seconds")

forceLoadAVResource(_:) is asynchronous; await it before calling getAVResourceTotalDuration(_:) so the engine has parsed the source.

Extract or Count Video Audio#

Build a video fill block and wait for forceLoadAVResource(_:) to complete before extracting or counting video audio. The tabs below reuse the loaded videoFill created in this snippet.

let videoBlock = try engine.block.create(.graphic)
try engine.block.setShape(videoBlock, shape: try engine.block.createShape(.rect))
let videoFill = try engine.block.createFill(.video)
try engine.block.setURL(
videoFill,
property: "fill/video/fileURI",
value: baseURL.appendingPathComponent("ly.img.video/videos/pexels-kampus-production-8154913.mp4"),
)
try engine.block.setFill(videoBlock, fill: videoFill)
try engine.block.appendChild(to: page, child: videoBlock)
try await engine.block.forceLoadAVResource(videoFill)

Extract one audio track from the video fill into a new audio block. AudioFromVideoOptions(keepTrimSettings: true, muteOriginalVideo: true) mirrors the source video’s trim onto the extracted block and silences the original fill so the audio plays from the extracted block alone.

let extractedAudio = try engine.block.createAudioFromVideo(
videoFill,
trackIndex: 0,
options: AudioFromVideoOptions(keepTrimSettings: true, muteOriginalVideo: true),
)
try engine.block.appendChild(to: page, child: extractedAudio)

Control Audio Playback#

Set play and pause state on the page so all time-based blocks stay synchronized. Set volume, mute state, playback speed, and per-block playback time on the audio block itself.

try engine.block.setPlaying(page, enabled: true)
let isScenePlaying = try engine.block.isPlaying(page)
print("Scene playing: \(isScenePlaying)")
try engine.block.setPlaybackTime(page, time: 3.0)
try engine.block.setVolume(audioBlock, volume: 0.8)
try engine.block.setMuted(audioBlock, muted: false)
try engine.block.setPlaybackSpeed(audioBlock, speed: 1.0)
try engine.block.setPlaying(page, enabled: false)

Audio block speed accepts values from 0.25 to 3.0. Changing the speed also changes how long a non-looping block takes on the timeline; looping blocks keep their authored duration regardless of speed.

Solo Playback#

Preview a single audio block while the rest of the scene stays paused. Solo mode is useful for waveform pickers and trim editors that need to audition one source at a time.

try engine.block.setSoloPlaybackEnabled(audioBlock, enabled: true)
try engine.block.setPlaying(page, enabled: true)
// ... preview the audio block in isolation ...
try engine.block.setPlaying(page, enabled: false)
try engine.block.setSoloPlaybackEnabled(audioBlock, enabled: false)

Read the current state with isSoloPlaybackEnabled(_:).

Manage Audio Timing#

Use offset and duration to place the audio block on the scene timeline. Use trim offset and trim length to choose which part of the source file plays.

try engine.block.setTimeOffset(audioBlock, offset: 2.0)
try engine.block.setDuration(audioBlock, duration: 10.0)

Trim the source range and enable looping when the trimmed source should repeat for as long as the block stays active.

try engine.block.setTrimOffset(audioBlock, offset: 5.0)
try engine.block.setTrimLength(audioBlock, length: 4.0)
try engine.block.setLooping(audioBlock, looping: true)

Load the audio resource with forceLoadAVResource(_:) before trimming so CE.SDK can read the source duration. Trim values are silently clamped to the available range, so loading first lets you compute and pass values the user expects to see.

Generate Audio Thumbnails#

Waveform generation returns an AsyncThrowingStream<AudioThumbnail, Error>. Choose samplesPerChunk, a timeRange, the total number of samples, and the number of channels. Iterate the stream with for try await and render each chunk’s samples in your own timeline component.

let waveformStream = engine.block.generateAudioThumbnailSequence(
audioBlock,
samplesPerChunk: 3,
timeRange: 0.0 ... 10.0,
numberOfSamples: 9,
numberOfChannels: 2,
)
for try await thumbnail in waveformStream {
print("Chunk \(thumbnail.chunkIndex)\(thumbnail.samples.count) samples")
}

Cancel an in-flight stream by terminating the consuming Task; the binding cancels the underlying thumbnail generation automatically.

Export Audio#

Export an audio block to bytes with exportAudio(_:mimeType:options:). The call returns an AsyncThrowingStream<AudioExport, Error> that yields .progress(renderedFrames:encodedFrames:totalFrames:) events during render and a final .finished(audio:) event carrying the exported Blob.

The example below backs the export block with an in-memory buffer so the snippet runs without a network request: engine.editor.createBuffer() returns a buffer:// URL the audio block reads from, and setBufferLength(url:length:) reserves capacity in bytes. skipEncoding: true returns the audio data without running the encoder; set it to false (the default) to receive a fully encoded WAV file. Pass MIMEType.mp4 instead of .wav for an MP4 container.

let exportBlock = try engine.block.create(.audio)
try engine.block.appendChild(to: page, child: exportBlock)
let audioBuffer = engine.editor.createBuffer()
try engine.editor.setBufferLength(url: audioBuffer, length: 96000)
try engine.block.setURL(exportBlock, property: "audio/fileURI", value: audioBuffer)
let exportStream = try await engine.block.exportAudio(
exportBlock,
mimeType: .wav,
options: AudioExportOptions(skipEncoding: true),
)
for try await event in exportStream {
switch event {
case let .progress(rendered, encoded, total):
print("Export progress: \(rendered)/\(total) rendered, \(encoded) encoded")
case let .finished(audio):
print("Exported \(audio.count) bytes of audio data")
}
}

To persist the full scene including audio sources, see the scene persistence APIs covered in the export guides.

API Reference#

CategoryAPIPurpose
Engine audio contextEngine(context:_, audioContext: .auto, license: _)Create an Engine that can drive a hardware audio device
Create blocksengine.block.create(.audio)Create a standalone audio block
Create blocksengine.block.create(.graphic)Create a block that can host the video fill
Create blocksengine.block.createShape(.rect)Create the shape for the video block
Create blocksengine.block.createFill(.video)Create a video fill the audio extraction APIs accept
Scene hierarchyengine.block.appendChild(to:child:)Attach audio, video, or extracted blocks to the scene hierarchy
Assign sourcesengine.block.setString(_:property: "audio/fileURI", value: _)Attach an audio file URI to an audio block
Assign sourcesengine.block.setString(_:property: "fill/video/fileURI", value: _)Attach a video file URI to a video fill
Video fill setupengine.block.setShape(_:shape:)Assign a shape to the video block
Video fill setupengine.block.setFill(_:fill:)Assign the loaded video fill to the video block
Extract video audioengine.block.createAudioFromVideo(_:trackIndex:options:)Extract one audio track by zero-based audio-track ordinal from a video fill
Extract video audioengine.block.createAudiosFromVideo(_:options:)Extract every audio track from a video fill
Extract video audioAudioFromVideoOptions(keepTrimSettings:muteOriginalVideo:)Configure trim mirroring and source muting
Count video audioengine.block.getAudioTrackCountFromVideo(_:)Count the audio tracks in a video fill
Inspect video audioengine.block.getAudioInfoFromVideo(_:)Read AudioTrackInfo metadata; use the returned list position for extraction because AudioTrackInfo.trackIndex is the container track index
Playbackengine.block.setPlaying(_:enabled:)Start or stop playback for a page or playable block
Playbackengine.block.isPlaying(_:)Read the current play or pause state
Playbackengine.block.supportsPlaybackControl(_:)Check whether playback control APIs apply to a block
Playbackengine.block.setPlaybackTime(_:time:)Move playback to a timeline position
Playbackengine.block.getPlaybackTime(_:)Read the current playback time
Playbackengine.block.supportsPlaybackTime(_:)Check whether a block exposes a playback time cursor
Playbackengine.block.setVolume(_:volume:)Set volume from 0.0 to 1.0
Playbackengine.block.getVolume(_:)Read the current volume
Playbackengine.block.setMuted(_:muted:)Mute or unmute audio
Playbackengine.block.isMuted(_:)Read whether audio is muted
Playbackengine.block.isForceMuted(_:)Read whether the engine is muting the block (for example a video fill played above 3.0x)
Playbackengine.block.setPlaybackSpeed(_:speed:)Set audio speed from 0.25 to 3.0
Playbackengine.block.getPlaybackSpeed(_:)Read the current playback speed
Playbackengine.block.setSoloPlaybackEnabled(_:enabled:)Preview one block while the rest of the scene stays paused
Playbackengine.block.isSoloPlaybackEnabled(_:)Read whether solo playback is enabled
Timingengine.block.supportsTimeOffset(_:)Check whether a block can be positioned on its parent’s timeline
Timingengine.block.setTimeOffset(_:offset:)Move the audio block on the timeline
Timingengine.block.getTimeOffset(_:)Read where the audio block starts on the timeline
Timingengine.block.supportsDuration(_:)Check whether a block exposes an active timeline duration
Timingengine.block.setDuration(_:duration:)Set the active block duration
Timingengine.block.getDuration(_:)Read the active block duration
Timingengine.block.supportsTrim(_:)Check whether a block or fill exposes trim controls
Timingengine.block.setTrimOffset(_:offset:)Start playback inside the source audio
Timingengine.block.getTrimOffset(_:)Read the source trim start
Timingengine.block.setTrimLength(_:length:)Limit the source range used for playback
Timingengine.block.getTrimLength(_:)Read the source trim length
Timingengine.block.setLooping(_:looping:)Loop the trimmed source while the block stays active
Timingengine.block.isLooping(_:)Read whether the source loops or stops
Resourcesengine.block.forceLoadAVResource(_:)Load audio or video metadata before querying it
Resourcesengine.block.getAVResourceTotalDuration(_:)Read the loaded audio or video source duration
Waveformsengine.block.generateAudioThumbnailSequence(_:samplesPerChunk:timeRange:numberOfSamples:numberOfChannels:)Generate waveform sample chunks as an AsyncThrowingStream<AudioThumbnail, Error>
Exportengine.block.exportAudio(_:mimeType:options:)Export an audio block to bytes; returns an AsyncThrowingStream<AudioExport, Error>
ExportAudioExportOptions(sampleRate:numberOfChannels:timeOffset:duration:skipEncoding:)Configure the audio export sample rate, channels, time range, and encoding
Exportengine.editor.createBuffer()Create an in-memory buffer:// URL that an audio block can read from
Exportengine.editor.setBufferLength(url:length:)Reserve buffer capacity for the audio block to read
Exportengine.block.setURL(_:property:value:)Bind a URL (including a buffer:// URL) to a block property

Next Steps#

  • CE.SDK API Reference — Browse the complete IMGLYEngine API surface.
  • Add Music — Add background music and audio tracks to video projects.
  • Adjust Audio Volume — Control playback levels, mute audio, and balance multiple audio sources.
  • Adjust Audio Playback Speed — Create slow-motion, time-stretched, and fast-forward audio effects.
  • Loop Audio — Create seamless repeating audio for background music and sound effects.