Search Docs
Loading...
Skip to content

Audio

Add audio to video scenes, extract audio from video fills, control playback, and generate waveform data with CE.SDK for Android.

8 mins
estimated time
GitHub

Audio blocks let you add background music, voice-overs, sound effects, and other standalone audio to video scenes. CE.SDK also exposes video audio track counts, playback controls, trim controls, and waveform generation 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 play audio.

Use Cases#

Use 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 DesignBlockType.Audio blocks. Audio blocks are attached to a page, reference their media through audio/fileURI, and use the same timeline properties as other time-based blocks.

Each audio block can have:

  • A source URI for standalone audio files
  • 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 custom timeline UIs

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

What Are the 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 is used before it loops or stops. Use the looping APIs to choose that behavior.

What Are Waveforms#

Waveforms are sampled audio amplitudes that you can render in a custom UI. On Android, generateAudioThumbnailSequence() returns a Flow of AudioThumbnailResult chunks. Each chunk contains normalized samples in the range from 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 audio file such as background music or a voice-over. Extract audio when the sound already exists inside a video fill and you need a separate audio block for editing, trimming, or muting the original video.

Examples#

The snippets below use a video scene with an existing page. 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, and load the resource before reading media metadata.

val audioBlock = engine.block.create(DesignBlockType.Audio)
engine.block.appendChild(parent = page, child = audioBlock)
engine.block.setUri(
block = audioBlock,
property = "audio/fileURI",
value = Uri.parse("https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a"),
)
engine.block.forceLoadAVResource(audioBlock)
val resourceDuration = engine.block.getAVResourceTotalDuration(audioBlock)

Extract or Count Video Audio#

Create a video fill and wait for the suspend forceLoadAVResource() API to complete before extracting or counting video audio. The tabs below use this loaded videoFill.

val videoBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(videoBlock, shape = engine.block.createShape(ShapeType.Rect))
val videoFill = engine.block.createFill(FillType.Video)
engine.block.setUri(
block = videoFill,
property = "fill/video/fileURI",
value = Uri.parse("https://cdn.img.ly/assets/demo/v3/ly.img.video/videos/pexels-kampus-production-8154913.mp4"),
)
engine.block.setFill(videoBlock, fill = videoFill)
engine.block.appendChild(parent = page, child = videoBlock)
engine.block.forceLoadAVResource(videoFill)

Check that the loaded source contains audio, and extract the first track into a new audio block. AudioFromVideoOptions keeps the trim settings and mutes the source video fill.

val trackCountBeforeExtraction = engine.block.getAudioTrackCountFromVideo(videoFill)
check(trackCountBeforeExtraction > 0) {
"Video source must contain an audio track."
}
val extractedAudioBlock = engine.block.createAudioFromVideo(
videoFill = videoFill,
trackIndex = 0,
options = AudioFromVideoOptions(
keepTrimSettings = true,
muteOriginalVideo = true,
),
)
engine.block.appendChild(parent = page, child = extractedAudioBlock)

Control Audio Playback#

Playback time and play or pause state are usually controlled on the page so all time-based blocks stay synchronized. Volume, mute state, and playback speed are set on the audio block itself.

engine.block.setPlaybackTime(page, time = 3.0)
val playbackTime = engine.block.getPlaybackTime(page)
engine.block.setPlaying(page, enabled = true)
val playing = engine.block.isPlaying(page)
engine.block.setPlaying(page, enabled = false)
val paused = !engine.block.isPlaying(page)
engine.block.setVolume(audioBlock, volume = 0.7F)
val volume = engine.block.getVolume(audioBlock)
engine.block.setMuted(audioBlock, muted = true)
val muted = engine.block.isMuted(audioBlock)
engine.block.setPlaybackSpeed(audioBlock, speed = 1.25F)
val playbackSpeed = engine.block.getPlaybackSpeed(audioBlock)
engine.block.setSoloPlaybackEnabled(audioBlock, enabled = true)
val soloPlaybackEnabled = engine.block.isSoloPlaybackEnabled(audioBlock)
engine.block.setSoloPlaybackEnabled(audioBlock, enabled = false)
val soloPlaybackDisabled = !engine.block.isSoloPlaybackEnabled(audioBlock)

Audio speed supports values from 0.25 to 3.0 for audio blocks. Changing speed also changes how long the block takes on the timeline.

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, and set looping when the trimmed source should repeat while the block remains active.

engine.block.setPlaybackSpeed(audioBlock, speed = 1.0F)
engine.block.setTimeOffset(audioBlock, offset = 2.0)
val timeOffset = engine.block.getTimeOffset(audioBlock)
engine.block.setDuration(audioBlock, duration = 8.0)
engine.block.setTrimOffset(audioBlock, offset = 1.0)
val trimOffset = engine.block.getTrimOffset(audioBlock)
engine.block.setLooping(audioBlock, looping = true)
val looping = engine.block.isLooping(audioBlock)
engine.block.setTrimLength(audioBlock, length = 6.0)
val trimLength = engine.block.getTrimLength(audioBlock)
val blockDuration = engine.block.getDuration(audioBlock)

Load the audio resource before trimming so CE.SDK can read the source duration and metadata.

Generate Audio Thumbnails#

Waveform generation emits a Flow of chunks. Choose samplesPerChunk, a time range, the total number of samples, and the number of channels you want to render.

val waveformChunks = engine.block.generateAudioThumbnailSequence(
block = audioBlock,
samplesPerChunk = 4,
timeBegin = 0.0,
timeEnd = 4.0,
numberOfSamples = 16,
numberOfChannels = 1,
).toList()
val waveformSampleCount = waveformChunks.sumOf { chunk -> chunk.samples.size }

Render the returned sample values in your own timeline or waveform component.

Save Audio Scenes#

The current Android binding does not expose an audio-only export method. Persist audio edits by saving the scene, or export a video page with audio through the video export APIs when you need an MP4 result.

Extracted audio can reference a transient buffer:// resource. Before calling saveToString(), read each transient resource, store it with your app’s storage client, then call relocateResource() so the scene contains durable URIs only. The default allowedResourceSchemes list is blob, bundle, file, http, and https; buffer:// is always transient. The sample narrows the list to http and https, so saving fails if any extracted buffer:// resource or local blob, bundle, or file resource remains instead of being relocated.

val transientAudioResources = engine.editor.findAllTransientResources()
transientAudioResources.forEach { (transientUri, _) ->
val resourceBytes = ByteArrayOutputStream()
engine.editor.getResourceData(
uri = transientUri,
chunkSize = 64 * 1024,
) { chunk ->
val copy = chunk.duplicate()
val bytes = ByteArray(copy.remaining())
copy.get(bytes)
resourceBytes.write(bytes)
true
}
val permanentUri = uploadTransientAudioResource(
sourceUri = transientUri,
data = resourceBytes.toByteArray(),
)
engine.editor.relocateResource(
currentUri = transientUri,
relocatedUri = permanentUri,
)
}
val remainingTransientAudioResources = engine.editor.findAllTransientResources()
val savedScene = engine.scene.saveToString(
scene = scene,
allowedResourceSchemes = listOf("http", "https"),
)

The upload helper represents your app storage layer. Replace it with a real upload or local persistence implementation that returns a URI your app can load later.

private fun uploadTransientAudioResource(
sourceUri: Uri,
data: ByteArray,
): Uri {
check(data.isNotEmpty()) { "Cannot persist an empty audio resource." }
// Replace this with your app's storage client and return its permanent URI.
// Transient buffer URIs do not carry stable file names, so the app owns the storage key.
val sourceId = sourceUri.toString().hashCode().toString(radix = 16)
val fileName = "extracted-audio-$sourceId-${data.size}.m4a"
return Uri.parse("https://your-storage.example/audio/$fileName")
}

API Reference#

The table below lists the Android APIs used in the examples above.

Category API Purpose
Engine audio context Engine.getInstance(id=_, audioContext=AudioContext.AUTO) Create an Engine instance that can play audio
Create blocks engine.block.create(blockType=DesignBlockType.Audio) Create a standalone audio block
Create blocks engine.block.create(blockType=DesignBlockType.Graphic) Create a block that can display the video fill
Create blocks engine.block.createShape(type=_) Create the video block shape
Create blocks engine.block.createFill(fillType=_) Create a video fill for extraction
Scene hierarchy engine.block.appendChild(parent=_, child=_) Attach audio, video, or extracted blocks to the scene hierarchy
Assign sources engine.block.setUri(block=_, property="audio/fileURI", value=_) Attach an audio file URI to an audio block
Assign sources engine.block.setUri(block=_, property="fill/video/fileURI", value=_) Attach a video file URI to a video fill
Video fill setup engine.block.setShape(block=_, shape=_) Assign a shape to the video block
Video fill setup engine.block.setFill(block=_, fill=_) Assign the loaded video fill to the video block
Extract video audio engine.block.createAudioFromVideo(videoFill=_, trackIndex=_, options=_) Extract one audio track by zero-based audio-track ordinal from a video fill
Extract video audio engine.block.createAudiosFromVideo(videoFill=_, options=_) Extract every audio track from a video fill
Count video audio engine.block.getAudioTrackCountFromVideo(videoFill=_) Count audio tracks in a video fill
Inspect video audio engine.block.getAudioInfoFromVideo(videoFill=_) Read AudioTrackInfo metadata; use the returned list position for extraction because AudioTrackInfo.trackIndex is the container track index
Playback engine.block.setPlaying(block=_, enabled=_) Start or stop playback for a page or playable block
Playback engine.block.isPlaying(block=_) Read the current play or pause state
Playback engine.block.supportsPlaybackControl(block=_) Check whether playback control APIs are supported for a block
Playback engine.block.setPlaybackTime(block=_, time=_) Move playback to a timeline position
Playback engine.block.getPlaybackTime(block=_) Read the current playback time
Playback engine.block.supportsPlaybackTime(block=_) Check whether a block exposes a playback time cursor
Playback engine.block.setVolume(block=_, volume=_) Set volume from 0.0 to 1.0
Playback engine.block.getVolume(block=_) Read the current volume
Playback engine.block.setMuted(block=_, muted=_) Mute or unmute audio
Playback engine.block.isMuted(block=_) Read whether audio is muted
Playback engine.block.setPlaybackSpeed(block=_, speed=_) Set audio speed from 0.25x to 3.0x
Playback engine.block.getPlaybackSpeed(block=_) Read the current playback speed
Playback engine.block.setSoloPlaybackEnabled(block=_, enabled=_) Preview one block while the rest of the scene stays paused
Playback engine.block.isSoloPlaybackEnabled(block=_) Read whether solo playback is enabled for a block
Timing engine.block.supportsTimeOffset(block=_) Check whether a block can be positioned on its parent’s timeline
Timing engine.block.setTimeOffset(block=_, offset=_) Move the audio block on the timeline
Timing engine.block.getTimeOffset(block=_) Read where the audio block starts on the timeline
Timing engine.block.supportsDuration(block=_) Check whether a block exposes an active timeline duration
Timing engine.block.setDuration(block=_, duration=_) Set the active block duration
Timing engine.block.getDuration(block=_) Read the active block duration
Timing engine.block.supportsTrim(block=_) Check whether a block or fill exposes trim controls
Timing engine.block.setTrimOffset(block=_, offset=_) Start inside the source audio
Timing engine.block.getTrimOffset(block=_) Read the source trim start
Timing engine.block.setTrimLength(block=_, length=_) Limit the source range used for playback
Timing engine.block.getTrimLength(block=_) Read the source trim length
Timing engine.block.setLooping(block=_, looping=_) Loop the trimmed source while the block is active
Timing engine.block.isLooping(block=_) Read whether the source loops or stops
Resources engine.block.forceLoadAVResource(block=_) Load audio or video metadata before querying it
Resources engine.block.getAVResourceTotalDuration(block=_) Read the loaded audio or video source duration
Waveforms engine.block.generateAudioThumbnailSequence(block=_, samplesPerChunk=_, timeBegin=_, timeEnd=_, numberOfSamples=_, numberOfChannels=_) Generate waveform sample chunks
Persistence engine.editor.findAllTransientResources() Find extracted buffer:// resources that must be persisted before saving
Persistence engine.editor.getResourceData(uri=_, chunkSize=_, onData=_) Read transient resource bytes for app storage
Persistence engine.editor.relocateResource(currentUri=_, relocatedUri=_) Replace a transient URI with a durable URI
Persistence engine.scene.saveToString(scene=_, allowedResourceSchemes=_) Serialize only scenes whose resources use allowed durable schemes

Next Steps#

  • CE.SDK API Reference - Review the full API surface.
  • Add Music — Add background music and audio tracks to video projects using CE.SDK’s audio block system.
  • Adjust Audio Volume — Learn how to adjust audio volume in CE.SDK to control playback levels, mute audio, and balance multiple audio sources in video projects.
  • Loop Audio — Create seamless repeating audio playback for background music and sound effects using CE.SDK’s audio looping system.
  • Adjust Audio Playback Speed - Learn how to adjust audio playback speed in CE.SDK to create slow-motion, time-stretched, and fast-forward audio effects.