The project document is stored in a “simple binary XML format used exclusively for
project documents” (Audacity’s ProjectSerializer). To read it, join the
dict column of the project
table to the front of the doc column and read the result as one list of
fields.
Layout
- The character size of the text: 1 (UTF-8), 2 (UTF-16) or 4
(UTF-32), in an
FT_CharSizefield. It is the character size of the Audacity build that wrote the file. - The name dictionary: every element and attribute name used in the
document, as
FT_Namefields. - The data fields: the encoded XML document.
The dictionary is written once per Audacity session, and it grows as new names are met.
A document can hold a sub-tree that uses its own name table:
FT_Push and FT_Pop save and restore the set of names around
it.
Field types
Each field starts with a one-byte type. The codes are the order of the
FieldTypes enumeration. Integers are little-endian, 2-byte IDs refer to the
name dictionary, floats and doubles are stored as they are in memory, and all names and
strings are in the file’s character size.
| Code | Name | Layout after the type byte | Meaning |
|---|---|---|---|
| 0 | FT_CharSize | 1 byte: size | Character size of all text that follows |
| 1 | FT_StartTag | 2-byte ID | An element opens |
| 2 | FT_EndTag | 2-byte ID | An element closes |
| 3 | FT_String | 2-byte ID, 4-byte byte length, characters | String attribute |
| 4 | FT_Int | 2-byte ID, 4-byte signed | Integer attribute |
| 5 | FT_Bool | 2-byte ID, 1 byte | Boolean attribute |
| 6 | FT_Long | 2-byte ID, 4-byte signed | Long attribute |
| 7 | FT_LongLong | 2-byte ID, 8-byte signed | 64-bit attribute, used for sample positions |
| 8 | FT_SizeT | 2-byte ID, 4-byte unsigned | Size attribute |
| 9 | FT_Float | 2-byte ID, 4-byte float, 4-byte signed digits | Float attribute. The digits, ignored on reading, are how many decimal places to write in text form. |
| 10 | FT_Double | 2-byte ID, 8-byte double, 4-byte signed digits | Double attribute, with digits as above |
| 11 | FT_Data | 4-byte byte length, characters | Text content of an element |
| 12 | FT_Raw | 4-byte byte length, characters | Text written as it stands, such as the XML header; a reader ignores it |
| 13 | FT_Push | none | Save the current name table and start an empty one |
| 14 | FT_Pop | none | Restore the name table saved by the last FT_Push |
| 15 | FT_Name | 2-byte ID, 2-byte byte length, characters | A dictionary entry: the name for this ID |
Name lengths are limited to 32,767 bytes, and string and data lengths are 4-byte signed numbers. Audacity’s reader stops at the end of the combined blob, and treats an ID not found in the name table as an error. String lengths count bytes, not characters.
A worked example
This is the start of a document written with 2-byte characters, whose first name is
project with ID 0:
dict:
00 02 FT_CharSize: 2
0F 00 00 0E 00 FT_Name: ID 0, 14 bytes follow
70 00 72 00 6F 00 6A 00 65 00 63 00 74 00 "project"
doc:
01 00 00 FT_StartTag: ID 0 (project)
…
02 00 00 FT_EndTag: ID 0 (project)
An integer attribute named by ID 3 with value 2 is 04 03 00 02 00 00 00:
code, ID, then a 4-byte little-endian value.