ACIDCAT . FILE FORMAT REFERENCE

Ableton Anatomy

Live 9 – Live 12.asd . .als . .adg . .amxd
rev 2026.08
magic 06 'I' / 06 'M'
endian both, declared
container grid + object tree
grain 30 ms ceiling

Ableton writes a .asd beside every audio file it analyses. It is usually described as a waveform cache, and that is wrong: it holds warp markers, loop points, onsets, pitch marks and time signature — Live's whole reading of the audio. The head is a frame-position grid whose last entry is the source file's exact frame count and whose steps never exceed 30 ms of audio; because that ceiling is a fixed fraction of the sample rate, an orphaned sidecar still describes audio that has been deleted. The body is a serialised object tree whose field names are stored in UTF-16, which is why an ASCII scan makes the file look like noise. It also states a tempo -- not as a number, but as a mapping from seconds to beats that the tempo falls out of. Hover a field to light its bytes.

binary.asd .amxdgzipped XML.als .alc .adg .advarchive.alpchunked.amxd

Three unrelated shapes ship under one brand. .asd is binary and undocumented; the Live Set family is gzipped XML that names its own type; .amxd is a plain chunk chain. They are grouped here because a project folder holds all of them at once.

the document types
extwhat it isshape
.asdper-sample analysis sidecarbinary, byte-order-declared
.adgdevice group / rack presetgzip + XML <GroupDevicePreset>
.alsLive Set (project)gzip + XML <LiveSet>
.alpLive Packgzip archive, not XML
.alcLive Clipgzip + XML <LiveSet>
.advdevice presetgzip + XML, device-named root
.amxdMax for Live deviceampf chunk chain
.agrgroove / quantizegzip + XML <Groove>
Live has more document types than these. .ams (Operator meta sound), .abl and .ablbundle (Note) and .ask (theme) are the same gzip + <Ableton> XML shape. The root child element names the type, so an unmapped document still identifies itself.
size10 bytesmagic0x06 + BOMcountgrid + 1reservedalways 0

A ten-byte head, then the grid. The interesting part is byte 1: it is a byte-order mark in the TIFF style, 'I' for Intel and 'M' for Motorola. Big-endian files, written by PowerPC-era Macs, exist and parse only big-endian. A reader that assumes little-endian silently mangles them.

.asd header + first grid entries (18 bytes)

A 44.1 kHz stereo sidecar, 7.774 s of source audio, written by Live 12.

Two magic bytes are not an identification. 0x06 plus a byte-order mark would fire on plenty of unrelated binaries. A safe check also requires the reserved word at offset 6 to be zero and the entry count to be sane.
entriescount - 1typeu32 frame offsetsceiling30 ms of audiolast entry= total frames

The grid is a strictly increasing list of absolute sample offsets into the source audio. Two properties make it far more useful than it looks. First, the last entry equals the source file's exact total frame count, with no rounding. Second, no step ever exceeds 30 ms of audio: 1,323 frames at 44.1 kHz, 1,440 at 48 kHz, and so on.

recovering a deleted file from its sidecar

Because the ceiling is a fixed fraction of the rate, the largest observed step identifies the rate, and the last entry then gives the duration.

quantityhowconfidence
sample ratesmallest standard rate whose 30 ms grain is not smaller than the largest stepexact when the ceiling was reached, otherwise a lower bound
frame countlast grid entry, read directlyexact
durationframe count / rateinherits the rate's
This is the reason to walk the format. An orphaned .asd in a folder is evidence about a file that no longer exists — its rate, its length, its exact sample count. Nothing else in the toolchain recovers that, because nothing else survives the audio.
No tempo NUMBER is stored, but the tempo is recoverable. The value appears nowhere — at no byte offset, as neither f32 nor f64, and in no derived form. Live keeps a mapping instead: warp markers pin seconds to beats, and the tempo falls out of any two of them. See the Warp tab.
namesu32 + UTF-16LEdistinct69layoutinterleavedversionper object

After the grid comes a serialised object tree that writes its own field names inline. They are length-prefixed UTF-16, so every second byte is zero and an ASCII string scan returns nothing — which is most of why this format has a reputation for being opaque. The names are not gathered in a header; they are interleaved with their data across the whole file, so a parser can seek a field by name instead of by a version-specific offset.

a field name on disk (26 bytes)

Note the 0x00 after every character.

what the names reveal
groupfields
warpWarpMarkers IsWarped WarpMode TransientResolution
clip / loopLoopStart LoopEnd HiddenLoopStart SampleOffset
metreNumerator Denominator AufTaktData (anacrusis)
onsetsOnSets UserOnsets HasUserOnsets PitchMarks
stalenessOriginalFileSize
Optional sections, not generations. It is tempting to read the field set as a format version. It is not one: over 1,473 sidecars 62% declare BOTH the beat-tracking set (InitialBPM, Bpms, BeatTrackState, Tonalities) and the overview set (OverViewLevels, UnbiasedTempoEstimate), so they cannot be generations of one another. Versioning is carried per object as individual Version ints, and there is no global version byte.
OriginalFileSize is the staleness check. Live records the source audio's byte size and re-analyses on mismatch — the same mechanism REAPER uses (mtime + size), arrived at independently. Of 1,200 sidecars sitting beside their real audio, 96% reference its current size; one that does not is describing a version of the file that no longer exists. It detects an edit but cannot re-find the file after a rename: the binding is by filename alone, so moving the audio orphans it silently.
Declared is not stored. This schema is shared with the Live Set, so it names things the sidecar never carries. LoopStart, LoopEnd, IsWarped, WarpMode, Numerator and Denominator are all declared here — and LoopEnd is absent from 91% of files whose Set states a real one. Loop points belong to a CLIP, and one audio file can back many clips with different loops. What the sidecar holds is per-file ANALYSIS.
record31 bytesSecTimef64 secondsBeatTimef64 beatstempoderived

A warp marker pins one instant of the recording to one position in the bar. Each record carries its own class name inline, u8-length-prefixed, so the markers are self-locating — there is no count to find and no offset to derive.

one warp marker (31 bytes)

Marker id 1 from a real project sidecar. The Live Set that wrote it states exactly these values.

The tempo is the mapping, not a number. Nothing in a .asd stores a BPM, at any byte offset, as neither f32 nor f64, and in no derived form. But two markers give it directly: (beats / seconds) x 60. Here 0.03125 beats in 0.011029411764705883 s is 170 BPM, and the Live Set declares 170. Across 40 clips the derivation matched the declared tempo every time, and over a 2,000-file sample it yields only clean musical values: 140, 170, 138, 120, 150, 145, 70, 172.
Most files have none, and that is not a gap. A one-shot nobody warped has nothing to pin, so most sidecars carry no markers at all. A clip the Live Set marks unwarped never has them. The presence of this section is not a format version: it appears under Live 9.7 through 12, and one project written by a single version contains sidecars both with and without it — a .asd is written when the audio is analysed, and outlives the projects that use it.
shapetwo arrayspositionsu32 framesenergiesf32countrepeated

Live's transient detection, stored as two length-prefixed arrays back to back: the count, the frame positions, the count again, then one energy per onset. That repeated count is what identifies the structure in a file with no other landmark.

onset array head (16 bytes)

A 13-transient loop. Positions are absolute frame offsets, checkable against the frame count the grid already gave us.

warp-engine parameters (32 bytes)

Sitting just above the onsets, in the order the Live Set declares them.

Two traps. The arrays are not word-aligned to the end of the frame grid, so a 4-stepped scan walks straight past them. And a count of 1 carries no evidence: with a single position "strictly increasing" constrains nothing, so any stray pair of equal u32 satisfies the test. A one-shot is therefore indistinguishable from an unrelated pair of values on this field alone.
Onsets are not warp markers. Live snaps markers to detected transients, so a marker frequently lands exactly on an onset — 12 of 57 across five files. The counts are what separate them: one file carries 14 onsets and 9 markers. Reading one as the other looks convincing and is wrong.
containergzippayloadUTF-8 XMLexpansion~20xroot<Ableton>

Sets, clips, racks and device presets are all the same thing: a gzip stream wrapping one XML document whose root is <Ableton>. The root carries the Live build that wrote it, which makes these the easiest provenance evidence in the whole ecosystem — an exact version string, not an inference.

the root element
<Ableton MajorVersion="5" MinorVersion="12.0_12120"
         SchemaChangeCount="2" Creator="Ableton Live 12.1.5"
         Revision="18e155016678da1939bdc3938f981adf84ebc96d">
telling them apart
extroot's first childnote
.als<LiveSet>a project
.alc<LiveSet>identical to a Set in content
.adg<GroupDevicePreset>rack
.advthe device's own class<Operator>, <Wavetable>, ...
A Set and a Clip are indistinguishable by content. Both open <LiveSet>. Only the extension separates them. There is no content rule that tells them apart, so any reader has to go by the name and say that it is doing so.
The expansion ratio is the hazard. A 336 KB Set becomes about 6.9 MB of XML, roughly 20x. The compressed size therefore says nothing about how much memory the document needs, so a reader must impose its own ceiling and say when a file reaches it.
magic"ampf"header12 byteschainid + u32 lengthpayloadMax patcher JSON

A Max for Live device is an Ableton Max Patch Format container: the ASCII magic ampf, a version word, and then — before the chunk chain begins — a four-byte marker, aaaa. Only at offset 12 does the chain start: four-byte id, little-endian length, payload. The ptch chunk carries the Max patcher, a short binary preamble followed by JSON.

layout
0x00  "ampf"  u32:version  "aaaa"      <- 12-byte header
0x0C  "meta"  u32:len      ...
0x18  "ptch"  u32:len      mx@c ... { patcher JSON }
The marker is not a chunk, and mistaking it for one is loud. Reading aaaa as an id makes the following four bytes — the ASCII meta — decode as a length of 1,635,018,093 bytes, and the whole chain collapses to one impossible chunk. The chain starts at 12, not 8.
The length field cannot be trusted. A chunk claiming more bytes than remain must stop the walk rather than read past the end. The ptch chunk ends exactly on the last byte of the file, so a chain that stops short means something is missing.
AppleDouble._name stubs.alpgzip, not XML

Two things in a real library wear Ableton clothing and are not Ableton documents. Both matter because the honest answer is more useful than a forced one.

macOS AppleDouble stubs

Not every file with a .asd extension is a sidecar. ._name.wav.asd resource-fork stubs are left behind when a Mac writes to a non-HFS volume, and they are common enough to matter. They open with 00 05 16 07 and the string Mac OS X.

Named, not swallowed. They belong to AppleDouble, not to Ableton, and identifying them as such is what keeps them from being counted as damaged sidecars.
Live Packs

A .alp is gzip like the Set family, but it is an archive, not a document: there is no <Ableton> element inside. The gzip magic alone therefore cannot tell a Pack from a Set; only the root element can.

Declining is a result. A gzip file either contains an <Ableton> root or it does not. The extension is not evidence either way.