Skip to content
Cabbage
Install

Opcodes

cabbageAraGet

Returns metadata about ARA audio sources, region information, and plugin state from a consolidated JSON data store. Available in numeric and string variants.

val:i = cabbageAraGet(SProperty)
val:i = cabbageAraGet(SProperty, iIndex)
val:S = cabbageAraGet(SProperty)
val:S = cabbageAraGet(SProperty, iIndex)
  • SProperty — property name using dot notation (e.g., "audioSource.name")
  • iIndex — zero-based index for array properties (source index, selection index, or playback region index)

cabbageAraGet is the primary opcode for querying ARA session data. It reads from a thread-safe JSON store that is updated automatically whenever the host’s document model changes. All values are returned at i-rate (init pass only).

Property names use dot notation to express the data hierarchy. For example, audioSource.region.start queries the crop start position of the audio source. The index argument selects which item to query — the meaning depends on the property prefix (source index, selection index, or playback region index).

ARA properties use two time domains, and the naming convention reflects this:

  • Source-relative properties (crop position within the audio file) are available in both seconds and samples at the source’s sample rate. Properties ending in InSamples are in samples; without the suffix, they’re in seconds. For example, audioSource.region.start is in seconds, while audioSource.region.startInSamples is in samples.

  • Arrangement properties (where a clip sits on the host timeline) are given in seconds. These have no InSamples variant because the host’s playback position is based on the host SR, which can change, whereas the time the clip appears will be the same regardless of the SR. For example, playbackRegion.start and editorView.selectedPlayback.start.

This follows the ARA API design: source file time is based in samples; arrangement time is not.

These are queried without an index:

Property Type Description
"currentIndex" number Index of the audio source associated with this plugin instance
"update" number Counter that increments on each ARA model change
"lastEvent" string Name of the most recent ARA lifecycle callback (see table below)
"audioSourceCount" number Total number of audio sources in the document. Sources will never be duplicated.
"playbackRegionCount" number Total number of playback regions (clips) in the document. A single source can account for multiple clips.
"musicalContextCount" number Total number of musical contexts in the document
"regionSequenceCount" number Total number of region sequences (tracks) in the document
"audioModificationCount" number Total number of audio modifications in the document
"editorView.selectedPlaybackRegionCount" number Number of playback regions (clips) in the current selection

These query properties of the audio source file itself. Use the source pool index (the index of the audio source within the document):

Property Type Index Description
"audioSource.name" string source idx File name or display name of the audio source
"audioSource.channels" number source idx Number of audio channels (1 = mono, 2 = stereo)
"audioSource.sampleCount" number source idx Total number of audio samples in the source file
"audioSource.sampleRate" number source idx Sample rate in Hz (e.g. 44100, 48000)
"audioSource.duration" number source idx Duration in seconds

These query the crop/edit state of a source within its audio modification. The values represent the position and duration of the audio source as cropped by the host. Use the source pool index:

Property Type Index Description
"audioSource.region.start" number source idx Crop start position in seconds
"audioSource.region.startInSamples" number source idx Crop start position in samples (within the source file)
"audioSource.region.duration" number source idx Crop duration in seconds
"audioSource.region.durationInSamples" number source idx Crop duration in samples

These query the current host selection and visibility state.

These are queried without an index:

Property Type Description
"editorView.timeRange.start" number Start of the overall selection on the host arrangement timeline (seconds)
"editorView.timeRange.duration" number Duration of the overall selection on the host arrangement timeline (seconds)
"editorView.hiddenSequenceCount" number Number of hidden region sequences (tracks)

These query individual selected playback regions (clips on the timeline). Use a selection index (not the source pool index):

Property Type Index Description
"editorView.selectedRegion.name" string selection idx Name of the selected playback region at index
"editorView.selectedRegion.startInSamples" number selection idx Start position in samples (modification time)
"editorView.selectedRegion.duration" number selection idx Duration in seconds
"editorView.selectedRegion.durationInSamples" number selection idx Duration in samples
"editorView.selectedPlayback.start" number selection idx Where the selected playback region sits on the host arrangement timeline (seconds)
"editorView.selectedPlayback.duration" number selection idx Duration of the selected playback region on the host arrangement timeline (seconds)

A musical context describes the rhythmic and harmonic structure of the music. Each document can contain multiple musical contexts. Use the musical context index:

Property Type Index Description
"musicalContextCount" number none Total number of musical contexts in the document
"musicalContext.name" string mc idx Display name of the musical context (may be empty)
"musicalContext.orderIndex" number mc idx Sort order index within the document
"musicalContext.color.r" number mc idx Red channel (0.0–1.0)
"musicalContext.color.g" number mc idx Green channel (0.0–1.0)
"musicalContext.color.b" number mc idx Blue channel (0.0–1.0)

A region sequence represents a track or lane in the host, containing playback regions. Each region sequence belongs to a musical context. Use the region sequence index:

Property Type Index Description
"regionSequenceCount" number none Total number of region sequences (tracks) in the document
"regionSequence.name" string rs idx Display name of the region sequence (may be empty)
"regionSequence.orderIndex" number rs idx Sort order index within its musical context
"regionSequence.musicalContextIndex" number rs idx Index of the parent musical context
"regionSequence.color.r" number rs idx Red channel (0.0–1.0)
"regionSequence.color.g" number rs idx Green channel (0.0–1.0)
"regionSequence.color.b" number rs idx Blue channel (0.0–1.0)

An audio modification represents a set of edits applied to an audio source. Each modification belongs to an audio source and can contain multiple playback regions. Use the modification index:

Property Type Index Description
"audioModificationCount" number none Total number of audio modifications in the document
"audioModification.name" string mod idx Display name of the modification (may be empty)
"audioModification.persistentId" string mod idx Persistent ID used for archiving/restoring state

These query all playback regions (clips) in the document, not just the selected ones. Use the playback region index (0-based, in the order they were added to the document):

Property Type Index Description
"playbackRegion.name" string pr idx Source file name for this playback region
"playbackRegion.sequenceName" string pr idx Track/lane name this playback region belongs to
"playbackRegion.startInSamples" number pr idx Crop start position in samples (within the source)
"playbackRegion.durationInSamples" number pr idx Crop duration in samples
"playbackRegion.start" number pr idx Arrangement position on the host timeline (seconds)
"playbackRegion.duration" number pr idx Duration on the host arrangement timeline (seconds)
"playbackRegion.sourceIndex" number pr idx Source pool index for this playback region’s source
"playbackRegion.color.r" number pr idx Red channel (0.0–1.0)
"playbackRegion.color.g" number pr idx Green channel (0.0–1.0)
"playbackRegion.color.b" number pr idx Blue channel (0.0–1.0)
instr 1
idx:i = cabbageAraGet("currentIndex")
cnt:i = cabbageAraGet("audioSourceCount")
name:S = cabbageAraGet("audioSource.name", idx)
ch:i = cabbageAraGet("audioSource.channels", idx)
sr:i = cabbageAraGet("audioSource.sampleRate", idx)
dur:i = cabbageAraGet("audioSource.duration", idx)
prints("Source '%s': %d ch, %d Hz, %.2f sec\n", name, ch, sr, dur)
endin
instr 1
idx:i = cabbageAraGet("currentIndex")
startSamp:i = cabbageAraGet("audioSource.region.startInSamples", iIdx)
durSamp:i = cabbageAraGet("audioSource.region.durationInSamples", iIdx)
startSec:i = cabbageAraGet("audioSource.region.start", iIdx)
durSec:i = cabbageAraGet("audioSource.region.duration", iIdx)
prints("Crop: sample %d-%d (%.3f-%.3f sec)\n", iStartSamp, iStartSamp+iDurSamp, iStartSec, iStartSec+iDurSec)
endin
instr 1
trig:k = cabbageAraGetUpdate()
if trig == 1 then
event "i", "ShowSelection", 0, 1
endif
endin
instr ShowSelection
cnt:i = cabbageAraGet("editorView.selectedPlaybackRegionCount")
hidden:i = cabbageAraGet("editorView.hiddenSequenceCount")
prints("%d regions selected (%d tracks hidden):\n", iCnt, iHidden)
idx = 0
while idx < iCnt do
name:S = cabbageAraGet("editorView.selectedRegion.name", idx)
start:i = cabbageAraGet("editorView.selectedRegion.startInSamples", idx)
durSec:i = cabbageAraGet("editorView.selectedRegion.duration", idx)
durSamp:i = cabbageAraGet("editorView.selectedRegion.durationInSamples", idx)
prints(" [%d] '%s' start=%d dur=%.3fs (%d samples)\n", idx, SName, iStart, iDurSec, iDurSamp)
idx += 1
od
endin
instr 1
idx:i = cabbageAraGet("currentIndex")
n:i = cabbageAraGet("audioSource.sampleCount", iIdx)
; Read all samples from channel 0 as a k-rate array
samples:k[] = cabbageAraGetSourceSamples(0, iN, 0, iIdx)
; Or read sample-by-sample at audio rate
aPhase phasor sr / iN
out:a = cabbageAraGetSourceSamples(aPhase, 0, iIdx)
endin

Iterating all playback regions in the session

Section titled “Iterating all playback regions in the session”
instr 1
trig:k = cabbageAraGetUpdate()
if trig == 1 then
event "i", "ShowAllClips", 0, 1
endif
endin
instr ShowAllClips
cnt:i = cabbageAraGet("playbackRegionCount")
prints("%d clips in session:\n", iCnt)
idx = 0
while idx < iCnt do
name:S = cabbageAraGet("playbackRegion.name", idx)
track:S = cabbageAraGet("playbackRegion.sequenceName", idx)
start:i = cabbageAraGet("playbackRegion.startInSamples", idx)
dur:i = cabbageAraGet("playbackRegion.durationInSamples", idx)
pbStart:i = cabbageAraGet("playbackRegion.start", idx)
pbDur:i = cabbageAraGet("playbackRegion.duration", idx)
srcIdx:i = cabbageAraGet("playbackRegion.sourceIndex", idx)
colR:i = cabbageAraGet("playbackRegion.color.r", idx)
colG:i = cabbageAraGet("playbackRegion.color.g", idx)
colB:i = cabbageAraGet("playbackRegion.color.b", idx)
prints(" [%d] '%s' on '%s': src#%d pb=%.3f-%.3f\n",
idx, SName, STrack, iSrcIdx, iPbStart, iPbStart+iPbDur)
prints(" colour: (%.2f, %.2f, %.2f)\n", iColR, iColG, iColB)
idx += 1
od
endin
instr ShowMusicalContexts
cnt:i = cabbageAraGet("musicalContextCount")
prints("%d musical contexts:\n", iCnt)
idx = 0
while idx < iCnt do
name:S = cabbageAraGet("musicalContext.name", idx)
order:i = cabbageAraGet("musicalContext.orderIndex", idx)
colR:i = cabbageAraGet("musicalContext.color.r", idx)
colG:i = cabbageAraGet("musicalContext.color.g", idx)
colB:i = cabbageAraGet("musicalContext.color.b", idx)
prints(" [%d] '%s' order=%d colour=(%.2f, %.2f, %.2f)\n",
idx, SName, iOrder, iColR, iColG, iColB)
idx += 1
od
endin
instr ShowRegionSequences
cnt:i = cabbageAraGet("regionSequenceCount")
prints("%d region sequences:\n", iCnt)
idx = 0
while idx < iCnt do
name:S = cabbageAraGet("regionSequence.name", idx)
order:i = cabbageAraGet("regionSequence.orderIndex", idx)
mcIdx:i = cabbageAraGet("regionSequence.musicalContextIndex", idx)
colR:i = cabbageAraGet("regionSequence.color.r", idx)
colG:i = cabbageAraGet("regionSequence.color.g", idx)
colB:i = 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
instr ShowAudioModifications
cnt:i = cabbageAraGet("audioModificationCount")
prints("%d audio modifications:\n", iCnt)
idx = 0
while idx < iCnt do
name:S = cabbageAraGet("audioModification.name", idx)
id:S = cabbageAraGet("audioModification.persistentId", idx)
prints(" [%d] '%s' id='%s'\n", idx, SName, SId)
idx += 1
od
endin
  • All values are i-rate: call during init or inside an i-rate instrument triggered by cabbageAraGetUpdate.
  • The JSON store is updated on a background thread when the host modifies the document. Use cabbageAraGetUpdate as a trigger to know when to re-read values.
  • String values are allocated once at init and remain valid for the instrument’s lifetime.

The "lastEvent" top-level property returns one of the following strings, indicating which ARA callback was most recently fired:

Event Description
"beginEditing" Host started an edit gesture
"endEditing" Host ended an edit gesture
Event Description
"musicalContextAddedToDocument" New musical context added
"musicalContextRemovedFromDocument" Musical context removed
"regionSequenceAddedToDocument" New region sequence (track) added
"regionSequenceRemovedFromDocument" Region sequence removed
"audioSourceAddedToDocument" New audio source added
"audioSourceRemovedFromDocument" Audio source removed
"documentWillDestroy" Document is about to be destroyed
Event Description
"documentPropertiesUpdated" Document properties changed (did)
"documentPropertiesWillUpdate" Document properties about to change (will)
"musicalContextPropertiesUpdated" Musical context properties changed
"musicalContextPropertiesWillUpdate" Musical context properties about to change
"regionSequencePropertiesUpdated" Region sequence properties changed
"regionSequencePropertiesWillUpdate" Region sequence properties about to change
"audioSourcePropertiesUpdated" Audio source properties changed
"audioSourcePropertiesWillUpdate" Audio source properties about to change
"audioModificationPropertiesUpdated" Audio modification properties changed
"audioModificationPropertiesWillUpdate" Audio modification properties about to change
"playbackRegionPropertiesWillUpdate" Playback region properties about to change
Event Description
"regionSequenceAddedToMusicalContext" Region sequence added to a musical context
"regionSequenceRemovedFromMusicalContext" Region sequence removed from a musical context
"musicalContextWillDestroy" Musical context about to be destroyed
"regionSequenceWillDestroy" Region sequence about to be destroyed
Event Description
"audioSourceWillDestroy" Audio source about to be destroyed
"audioModificationAddedToAudioSource" New modification added to a source
"audioModificationRemovedFromAudioSource" Modification removed from a source
"audioModificationWillDestroy" Audio modification about to be destroyed
"audioSourceDeactivatedForUndo" Source deactivated for undo history
"audioSourceReactivatedFromUndo" Source reactivated after undo
"audioModificationDeactivatedForUndo" Modification deactivated for undo
"audioModificationReactivatedFromUndo" Modification reactivated after undo
Event Description
"playbackRegionAddedToRegionSequence" Playback region added to a region sequence
"playbackRegionRemovedFromRegionSequence" Playback region removed from a region sequence
"playbackRegionAddedToAudioModification" Playback region added to a modification
"playbackRegionRemovedFromAudioModification" Playback region removed from a modification
"playbackRegionWillDestroy" Playback region about to be destroyed
Event Description
"notifySelection" Host sent a new selection (user selected regions/tracks)
"notifyHideRegionSequences" Host updated hidden/shown tracks

The ARA analyser below exercises the ARA opcodes against the host document model:

CabbageARA.csd
<Cabbage>
{
"pluginId": "def1",
"widgets" : [
{
"type" : "form",
"id" : "MainForm",
"caption" : "ARA Analyser",
"size" : {"width": 864, "height": 600},
"style" : {"backgroundColor": "#2b2b41"},
"channels": [ {"id": "formChannel"} ]
},
{
"type" : "rotarySlider",
"bounds" : {"left": 20, "top": 60, "width": 120, "height": 80},
"style" : { "label": {"fontColor": "#dddddd"}, "valueText": {"fontSize": 12} },
"channels": [
{
"id" : "wetdry",
"label": "Wet/Dry",
"event": "valueChanged",
"range": {"min": 0, "max": 1, "defaultValue": 0, "skew": 1, "increment": 0.001}
}
]
},
{
"type" : "rotarySlider",
"bounds" : {"left": 20, "top": 160, "width": 120, "height": 80},
"style" : { "label": {"fontColor": "#dddddd"}, "valueText": {"fontSize": 12} },
"channels": [
{
"id" : "feedback",
"label": "Feedback",
"event": "valueChanged",
"range": {"min": 0, "max": 1, "defaultValue": 0.5, "skew": 1, "increment": 0.001}
}
]
},
{
"type" : "csoundOutput",
"bounds" : {"left": 160, "top": 57, "width": 650, "height": 529},
"channels": [
{ "id": "csoundOutput2", "range": {"increment": 0.001} }
]
}
]
}
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -m0d
</CsOptions>
<CsInstruments>
ksmps = 1
nchnls = 2
0dbfs = 1
; This instrument listens for ARA updates and when it detects one
; it triggers instrument 99 to process the audio data. No point
; in trying to process anything until an event is received from the host,
; as there will be no audio data to process.
instr 1
updated:k = cabbageAraGetUpdate()
if updated == 1 then
event("i", "ShowAllInfo", 0, 1)
endif
endin
instr ShowAllInfo
cabbageAraDump()
; --- Top-level state ---
idx:i = cabbageAraGet("currentIndex")
srcCnt:i = cabbageAraGet("audioSourceCount")
lastEvent:S = cabbageAraGet("lastEvent")
; Print the header block
printfi("=========================================\n", 1)
printfi("ARA UPDATE No.%d\n", 1, p5)
printfi("=========================================\n", 1)
printfi("[Status] Last Event: %s\n", 1, lastEvent)
; --- Selection properties ---
regCnt:i = cabbageAraGet("playbackRegionCount")
mcCnt:i = cabbageAraGet("musicalContextCount")
rsCnt:i = cabbageAraGet("regionSequenceCount")
modCnt:i = cabbageAraGet("audioModificationCount")
selRegCnt:i = cabbageAraGet("editorView.selectedPlaybackRegionCount")
printfi("[Metrics] Sources: %d | Regions: %d | Contexts: %d | Sequences: %d | Modifications: %d | Selected: %d | Current Index: %d\n", 1, srcCnt, regCnt, mcCnt, rsCnt, modCnt, selRegCnt, idx)
; --- Time Range Calculations ---
trStart:i = cabbageAraGet("editorView.timeRange.start")
trDur:i = cabbageAraGet("editorView.timeRange.duration")
printfi("[Time] Host Range: %.3f to %.3f (%.3fs)\n\n", 1, trStart, trStart + trDur, trDur)
; --- Musical Contexts Loop ---
printfi("------------ MUSICAL CONTEXTS -----------\n", 1)
if mcCnt == 0 then
printfi(" [None]\n", 1)
else
mcIdx:i = 0
while mcIdx < mcCnt do
mcName:S = cabbageAraGet("musicalContext.name", mcIdx)
mcOrder:i = cabbageAraGet("musicalContext.orderIndex", mcIdx)
mcColR:i = cabbageAraGet("musicalContext.color.r", mcIdx)
mcColG:i = cabbageAraGet("musicalContext.color.g", mcIdx)
mcColB:i = cabbageAraGet("musicalContext.color.b", mcIdx)
printfi("[%d] '%s'\n", mcIdx + 1, mcIdx, mcName)
printfi(" Order: %d\n", 1, mcOrder)
printfi(" Colour: (%.2f, %.2f, %.2f)\n", 1, mcColR, mcColG, mcColB)
mcIdx += 1
od
endif
printfi("\n", 1)
; --- Region Sequences Loop ---
printfi("------------ REGION SEQUENCES -----------\n", 1)
if rsCnt == 0 then
printfi(" [None]\n", 1)
else
rsIdx:i = 0
while rsIdx < rsCnt do
rsName:S = cabbageAraGet("regionSequence.name", rsIdx)
rsOrder:i = cabbageAraGet("regionSequence.orderIndex", rsIdx)
rsMcIdx:i = cabbageAraGet("regionSequence.musicalContextIndex", rsIdx)
rsColR:i = cabbageAraGet("regionSequence.color.r", rsIdx)
rsColG:i = cabbageAraGet("regionSequence.color.g", rsIdx)
rsColB:i = cabbageAraGet("regionSequence.color.b", rsIdx)
printfi("[%d] '%s'\n", rsIdx + 1, rsIdx, rsName)
printfi(" Order: %d\n", 1, rsOrder)
printfi(" Context: #%d\n", 1, rsMcIdx)
printfi(" Colour: (%.2f, %.2f, %.2f)\n", 1, rsColR, rsColG, rsColB)
rsIdx += 1
od
endif
printfi("\n", 1)
; --- Playback Regions Loop ---
printfi("------------ PLAYBACK REGIONS -----------\n", 1)
if regCnt == 0 then
printfi(" [None]\n", 1)
else
selIdx:i = 0
while selIdx < regCnt do
selName:S = cabbageAraGet("playbackRegion.name", selIdx)
pbStart:i = cabbageAraGet("playbackRegion.start", selIdx)
pbDur:i = cabbageAraGet("playbackRegion.duration", selIdx)
pbColR:i = cabbageAraGet("playbackRegion.color.r", selIdx)
pbColG:i = cabbageAraGet("playbackRegion.color.g", selIdx)
pbColB:i = cabbageAraGet("playbackRegion.color.b", selIdx)
printfi("[%d] '%s'\n", selIdx + 1, selIdx, selName)
printfi(" Start: %.3fs\n", 1, pbStart)
printfi(" Duration: %.3fs\n", 1, pbDur)
printfi(" Colour: (%.2f, %.2f, %.2f)\n", 1, pbColR, pbColG, pbColB)
selIdx += 1
od
endif
printfi("\n", 1)
; --- Selected Regions Loop (New Section) ---
printfi("------------ SELECTED REGIONS -----------\n", 1)
if selRegCnt == 0 then
printfi(" [None]\n", 1)
else
activeSelIdx:i = 0
while activeSelIdx < selRegCnt do
selRegName:S = cabbageAraGet("editorView.selectedRegion.name", activeSelIdx)
selTimelineStart:i = cabbageAraGet("editorView.selectedPlayback.start", activeSelIdx)
selTimelineDur:i = cabbageAraGet("editorView.selectedPlayback.duration", activeSelIdx)
selSrcStartSamp:i = cabbageAraGet("editorView.selectedRegion.startInSamples", activeSelIdx)
selSrcDurSamp:i = cabbageAraGet("editorView.selectedRegion.durationInSamples", activeSelIdx)
printfi("[%d] '%s'\n", activeSelIdx + 1, activeSelIdx, selRegName)
printfi(" Timeline Pos: %.3fs (Dur: %.3fs)\n", 1, selTimelineStart, selTimelineDur)
printfi(" Source Crop: Start=%d samples, Dur=%d samples\n", 1, selSrcStartSamp, selSrcDurSamp)
activeSelIdx += 1
od
endif
printfi("\n", 1)
; --- Audio Modifications Loop ---
printfi("------------ AUDIO MODIFICATIONS --------\n", 1)
if modCnt == 0 then
printfi(" [None]\n", 1)
else
modIdx:i = 0
while modIdx < modCnt do
modName:S = cabbageAraGet("audioModification.name", modIdx)
modId:S = cabbageAraGet("audioModification.persistentId", modIdx)
printfi("[%d] '%s'\n", modIdx + 1, modIdx, modName)
printfi(" Persistent ID: '%s'\n", 1, modId)
modIdx += 1
od
endif
printfi("\n", 1)
; --- Sources Loop ---
printfi("------------ SOURCES ---------------------\n", 1)
if srcCnt == 0 then
printfi(" [None]\n", 1)
else
srcIdx:i = 0
while srcIdx < srcCnt do
name:S = cabbageAraGet("audioSource.name", srcIdx)
channels:i = cabbageAraGet("audioSource.channels", srcIdx)
sampCnt:i = cabbageAraGet("audioSource.sampleCount", srcIdx)
sr:i = cabbageAraGet("audioSource.sampleRate", srcIdx)
duration:i = cabbageAraGet("audioSource.duration", srcIdx)
regStart:i = cabbageAraGet("audioSource.region.startInSamples", srcIdx)
regDur:i = cabbageAraGet("audioSource.region.durationInSamples", srcIdx)
printfi("[%d] '%s'\n", srcIdx + 1, srcIdx, name)
printfi(" Channels: %d\n", 1, channels)
printfi(" Sample Rate: %d Hz\n", 1, sr)
printfi(" Sample Count: %d\n", 1, sampCnt)
printfi(" Duration: %.3fs\n", 1, duration)
printfi(" Region: Start=%d, Duration=%d\n", 1, regStart, regDur)
srcIdx += 1
od
endif
printfi("=========================================\n", 1)
endin
</CsInstruments>
<CsScore>
i 1 0 z ; run for the lifetime of the plugin
</CsScore>
</CsoundSynthesizer>

Download CabbageARA.csd