Skip to content
Cabbage
Install

Using Cabbage 3

ARA Support

Audio Random Access (ARA) is an extension to the standard audio plugin format that gives a plugin direct, offline access to a host’s audio material—without having to process it in real time. Cabbage 3 supports ARA through a consolidated opcode API that provides access to the host’s audio tracks.

In an ARA document, you’ll find references to several key entities:

  • Audio Sources (AudioSource): Provide the low-level PCM data and properties for any audio asset loaded into a DAW session. Note: audio sources have their own regions — don’t confuse these with the higher-level PlaybackRegions listed below.
  • Playback Regions (PlaybackRegion): Represent how an audio source is mapped onto the DAW timeline as a clip. It contains information about the region’s duration, its start time on the DAW timeline, and its internal start time/offset relative to the raw audio file. Cropping the start or end of an audio clip in the DAW will modify this PlaybackRegion structure. Note that duplicating an audio source across multiple tracks will increase the number of regions, but not the number of audio sources.
  • Region Sequences (RegionSequence): Represent a collection of playback regions grouped together, typically mapping to tracks, lanes, or channels in the host. Each region sequence belongs to a musical context.
  • Musical Contexts (MusicalContext): Describe the rhythmic and harmonic structure of the music, including tempo, bar signatures, key signatures, and tuning.
  • Audio Modifications (AudioModification): Contain the edits applied to an audio source. Each modification belongs to an audio source and can contain multiple playback regions.
  • View Selection (ViewSelection): Provides real-time information about the host’s current user selection, which can include specific playback regions, region sequences, and/or a distinct time range.

ARA support is only available in dedicated ARA build targets. When exporting your plugin from the Cabbage extension, you must choose one of the ARA-specific export commands:

  • Export as VST3 Effect (ARA)
  • Export as VST3 Synth (ARA)

These commands are available from the VS Code Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows/Linux). Exporting with a standard VST3 or CLAP target will produce a plugin that does not include ARA support.

When an ARA-capable host loads a Cabbage plugin, it grants the plugin direct access to the audio material on each track. Cabbage reads this audio data directly from the host, stores it in an internal data pool, and makes it available to your Csound instrument through the cabbageAraGet opcode.

All Cabbage ARA plugin instances in a project share one document model. That model can contain many audio sources (for example, across multiple tracks). Cabbage provides opcodes to query source metadata, read audio samples, and detect when the model is updated.

Use cabbageAraGet to query information about the available audio sources. Top-level properties (like currentIndex and audioSourceCount) are queried without an index. Per-source metadata (like audioSource.name, audioSource.channels, and audioSource.duration) takes a source index argument.

instr 1
idx:i = cabbageAraGet("currentIndex")
count:i = cabbageAraGet("audioSourceCount")
name:S = cabbageAraGet("audioSource.name", idx)
channels:i = cabbageAraGet("audioSource.channels", idx)
numSamples:i = cabbageAraGet("audioSource.sampleCount", idx)
sampleRate:i = cabbageAraGet("audioSource.sampleRate", idx)
duration:i = cabbageAraGet("audioSource.duration", idx)
prints("Source '%s': %d ch, %d samples, %d Hz, %.2f sec\n", name, channels, numSamples, sampleRate, duration)
endin

Use cabbageAraGetSourceSamples to read raw PCM data from a source. It supports audio-rate, k-rate, and array output. To read the contents of the track the plugin is currently loaded onto, do this:

instr 1
; Query the pool index for this instance's source
idx:i = cabbageAraGet("currentIndex")
; Query structural asset parameters via the consolidated opcode
name:S = cabbageAraGet("audioSource.name", idx)
sampleRate:i = cabbageAraGet("audioSource.sampleRate", idx)
channels:i = cabbageAraGet("audioSource.channels", idx)
trig:k = cabbageAraGetUpdate()
; Detect new ARA lifecycle events (already a 1-k-cycle trigger, no changed() needed)
if trig == 1 then
; Query region properties (crop position within source file)
regionStart:i = cabbageAraGet("audioSource.region.startInSamples", idx)
regionDuration:i = cabbageAraGet("audioSource.region.durationInSamples", idx)
printf("Asset: %s [SR: %d, Chans: %d]\n", trig, name, sampleRate, channels)
endif
; Read raw audio samples at audio-rate position
; audioSignal:a = cabbageAraGetSourceSamples(position, 0, idx)
; Apply normalization: gain:k = targetLevel / peak
; normalizedSignal:a = audioSignal * gain
endin

cabbageAraGetUpdate returns a k-rate trigger that fires 1 for a single k-cycle whenever the ARA model data changes. Use it directly as a trigger to re-read source metadata (no changed() needed — wrapping it in changed() would double-fire on both edges). This fires whenever changes are made to the session — timeline edits, new clips added, clips resized, tracks hidden, etc.

instr 1
updated:k = cabbageAraGetUpdate()
if updated == 1 then
event "i", "ReadAraData", 0, 3600
endif
endin
instr ReadAraData
iIdx cabbageAraGet "currentIndex"
iCnt cabbageAraGet "audioSourceCount"
SLast cabbageAraGet "lastEvent"
prints("Update [%s]: source %d of %d\n", SLast, iIdx, iCnt)
endin

ARA hosts define audio source playback regions that specify which portion of a source is active. Use the region properties to query these:

instr 1
iIdx cabbageAraGet "currentIndex"
iStart cabbageAraGet "audioSource.region.startInSamples", iIdx
iCount cabbageAraGet "audioSource.region.durationInSamples", iIdx
iDur cabbageAraGet "audioSource.duration", iIdx
prints("Region: sample %d, %d samples (%.2f sec)\n", iStart, iCount, iDur)
endin

List all clips in the document (not just the selected ones), using the playbackRegionCount property and per-index playbackRegion.* properties. This gives you access to every clip on the timeline, including its crop offset within the source and its arrangement position:

instr 1
kTrig cabbageAraGetUpdate
if kTrig == 1 then
event "i", "ShowAllClips", 0, 1
endif
endin
instr ShowAllClips
iCnt cabbageAraGet "playbackRegionCount"
prints("%d clips in session:\n", iCnt)
idx = 0
while idx < iCnt do
SName cabbageAraGet "playbackRegion.name", idx
STrack cabbageAraGet "playbackRegion.sequenceName", idx
iStart cabbageAraGet "playbackRegion.startInSamples", idx
iDur cabbageAraGet "playbackRegion.durationInSamples", idx
iPbStart cabbageAraGet "playbackRegion.start", idx
iPbDur cabbageAraGet "playbackRegion.duration", idx
iSrcIdx cabbageAraGet "playbackRegion.sourceIndex", idx
prints(" [%d] '%s' on '%s': src#%d crop=%d-%d pb=%.3f-%.3f\n",
idx, SName, STrack, iSrcIdx, iStart, iStart+iDur, iPbStart, iPbStart+iPbDur)
idx += 1
od
endin

The playback region array is automatically maintained via ARA lifecycle callbacks — clips are added when playbackRegionAddedToRegionSequence fires and removed when playbackRegionRemovedFromRegionSequence or playbackRegionWillDestroy fires.

When the user selects tracks or regions in the host, the plugin is notified via the EditorView. Selection data is accessible through indexed properties — use editorView.selectedPlaybackRegionCount to get the number of selected playback regions (clips), then query each by index:

instr 1
kTrig cabbageAraGetUpdate
if kTrig == 1 then
event "i", "ShowSelection", 0, 1
endif
endin
instr ShowSelection
iCnt cabbageAraGet "editorView.selectedPlaybackRegionCount"
iHidden cabbageAraGet "editorView.hiddenSequenceCount"
prints("%d regions selected (%d tracks hidden):\n", iCnt, iHidden)
idx = 0
while idx < iCnt do
SName cabbageAraGet "editorView.selectedRegion.name", idx
iStart cabbageAraGet "editorView.selectedRegion.startInSamples", idx
iDurSec cabbageAraGet "editorView.selectedRegion.duration", idx
iDurSamp cabbageAraGet "editorView.selectedRegion.durationInSamples", idx
prints(" [%d] '%s' start=%d dur=%.3fs (%d samples)\n", idx, SName, iStart, iDurSec, iDurSamp)
idx += 1
od
endin

The "lastEvent" property will be "notifySelection" when the selection changes, or "notifyHideRegionSequences" when tracks are hidden or shown. Selection index is separate from source pool index — the first selected region is always index 0 regardless of which source it belongs to.

Musical contexts describe the rhythmic and harmonic structure of the music. Region sequences represent tracks or lanes in the host. Use the musicalContextCount and regionSequenceCount properties to iterate over these entities:

instr 1
kTrig cabbageAraGetUpdate
if kTrig == 1 then
event "i", "ShowTracks", 0, 1
endif
endin
instr ShowTracks
; Show musical contexts
iMcCnt cabbageAraGet "musicalContextCount"
prints("%d musical contexts:\n", iMcCnt)
idx = 0
while idx < iMcCnt do
SName cabbageAraGet "musicalContext.name", idx
iOrder cabbageAraGet "musicalContext.orderIndex", idx
iColR cabbageAraGet "musicalContext.color.r", idx
iColG cabbageAraGet "musicalContext.color.g", idx
iColB cabbageAraGet "musicalContext.color.b", idx
prints(" [%d] '%s' order=%d colour=(%.2f, %.2f, %.2f)\n",
idx, SName, iOrder, iColR, iColG, iColB)
idx += 1
od
; Show region sequences (tracks)
iRsCnt cabbageAraGet "regionSequenceCount"
prints("%d region sequences:\n", iRsCnt)
idx = 0
while idx < iRsCnt do
SName cabbageAraGet "regionSequence.name", idx
iOrder cabbageAraGet "regionSequence.orderIndex", idx
iMcIdx cabbageAraGet "regionSequence.musicalContextIndex", idx
iColR cabbageAraGet "regionSequence.color.r", idx
iColG cabbageAraGet "regionSequence.color.g", idx
iColB cabbageAraGet "regionSequence.color.b", idx
prints(" [%d] '%s' order=%d context=#%d colour=(%.2f, %.2f, %.2f)\n",
idx, SName, iOrder, iMcIdx, iColR, iColG, iColB)
idx += 1
od
endin

Audio modifications represent edits applied to an audio source. Each modification has a name and a persistent ID used for archiving:

instr ShowModifications
iCnt cabbageAraGet "audioModificationCount"
prints("%d audio modifications:\n", iCnt)
idx = 0
while idx < iCnt do
SName cabbageAraGet "audioModification.name", idx
SId cabbageAraGet "audioModification.persistentId", idx
prints(" [%d] '%s' id='%s'\n", idx, SName, SId)
idx += 1
od
endin

During development, you can test ARA instruments without an ARA-capable host by using CabbageApp’s standalone mode. There are two ways to provide audio files for testing:

VS Code extension prompt: When the VS Code extension launches a standalone ARA instrument, it will prompt you to select an audio file (WAV, FLAC, Ogg, or MP3).

Widget JSON property: You can pre-specify test files in the widget JSON so they load automatically without a prompt:

{
"ara": {
"testFiles": ["guitar.wav", "drums.wav"]
}
}

Relative paths are resolved from the CSD file’s directory. The audio is loaded on a background thread and made available through the same ARA opcodes, making it straightforward to develop and debug without leaving your normal Cabbage workflow.

The ARA implementation is still in its early stages. The following table represents the current implementation status.

ARA Entity Status Notes
AudioSource Supported Name, channels, sample count, sample rate, duration, region crop
PlaybackRegion Supported Name, track name, crop offset, arrangement position, source index, colour
EditorView selection Supported Selected region/track data, time range, hidden tracks
MusicalContext Supported Name, order index, colour
RegionSequence Supported Name, order index, parent musical context, colour
AudioModification Supported Name, persistent ID

This will be expanded in future versions as the ARA integration matures. Musical context content (tempo map, bar signatures, key signatures, tuning, chords) will be added in a future release.

See the ARA Opcodes reference for the full list of available opcodes.