#Jamcorder app library export
This document specifies the library backup created by Export Library in the Jamcorder app. The exported file is a ZIP archive named jamcorder-app-export.zip. It contains the app's downloaded Jamcorder recordings, saved clips, and albums in their native on-disk representation.
Purpose: give people full control over their MIDI data, so they can access it outside the Jamcorder app and use it however they choose. This also enables custom export and restore workflows, such as creating custom libraries, excluding unwanted data, or otherwise controlling what is restored.
Scope: app settings are intentionally not specified here. Current app builds may include a
settings/subtree, except forsettings/soundfonts/. Consumers of the library format should ignore that subtree.
#Related documentation
- JMX MIDI files — the recording format embedded in exported libraries, including its structure, timing, bookmarks, metadata, and parsing requirements.
#Contents
- Format overview
- ZIP container
- Downloaded recordings
- Saved clips
- Albums
- Time and identifiers
- Import and merge behavior
- Reader requirements
#Format overview
A representative archive has this layout:
jamcorder-app-export.zip
├── JAMC/
│ └── <year>/
│ └── <jamcorderUuid>/
│ └── Jmx-A<assetIdx>-<Mon>-<DD>-<YYYY>.mid/
│ ├── <stoneIdx>.stone.mid.gz
│ ├── <stoneIdx>.stone.mid.gz.completed.json
│ └── completed.json
├── clips/
│ └── <year>/
│ └── JClip-<unixtime>-<offset>-<clipUuid>-<Mon>-<DD>-<YYYY>/
│ └── clip.stone.mid.gz
├── albums/
│ └── <albumUuid>/
│ ├── info.json
│ └── clips/
│ └── JClip-<unixtime>-<offset>-<clipUuid>-<Mon>-<DD>-<YYYY>.json
└── settings/ out of scope; may be presentAll paths are relative to the ZIP root. There is no enclosing directory inside the archive. The .mid item below JAMC/ is a directory containing separately compressed stone files; it is not itself a MIDI file.
| Top-level path | Purpose | Required |
|---|---|---|
JAMC/ | Downloaded continuous recordings, divided into JMX stones. | No; absent when no recordings have been downloaded. |
clips/ | User-created and imported MIDI clips. | No; absent when no clips have been saved. |
albums/ | Album metadata and clip-membership markers. | No; absent when no albums have been created. |
settings/ | App state outside this specification. | No. Readers of this specification should ignore it. |
#ZIP container
- The filename produced by the app is
jamcorder-app-export.zip. Import does not depend on that filename; it accepts a user-selected.zipfile. - Files use ordinary ZIP entries and DEFLATE compression at the encoder's best-speed level.
- Only files are enumerated. Empty directories do not receive explicit entries and are not preserved.
- Entry order follows the platform's recursive directory listing and is not stable or semantically meaningful.
- The archive has no manifest, media type, checksum catalog, digital signature, library UUID, creation timestamp, or declared format version.
- Every file in the app documents directory is archived except the exact
settings/soundfontspath and everything below it.
ZIP CRC values protect individual entries against accidental corruption, but they are not an authenticity mechanism. Treat archive paths and decompressed sizes as untrusted input.
#Downloaded recordings
The JAMC/ tree mirrors Jamcorder recording identity while storing each downloaded stone as a separate gzip-compressed MIDI file:
JAMC/<year>/<jamcorderUuid>/Jmx-A<assetIdx>-<Mon>-<DD>-<YYYY>.mid/<stoneIdx>.stone.mid.gzExample:
JAMC/2025/60b2d69c-333d-9b33-6c3b-9b38886c102d/
Jmx-A00074-Apr-24-2025.mid/3.stone.mid.gz#Recording path components
| Component | Meaning |
|---|---|
<year> | Four-digit year of the asset's local calendar date. |
<jamcorderUuid> | 36-character Jamcorder UUID in five hyphen-separated groups. |
<assetIdx> | Device-local recording index. It is normally zero-padded to at least five decimal digits after A. |
<Mon>-<DD>-<YYYY> | English abbreviated month, two-digit day, and four-digit local year, such as Apr-24-2025. |
<stoneIdx> | Decimal stone index within the recording, normally beginning at 1. |
The year directory and date in the asset directory name are derived from days since the Unix epoch at UTC midnight. The actual wall-clock anchors and local offsets are stored inside each JMX stone and should be authoritative for musical-event time.
#Stone payload
<stoneIdx>.stone.mid.gz is an RFC 1952 gzip stream. Decompression yields a complete format-0 Standard MIDI File:
- one
MThdchunk and oneMTrkchunk; - a
jmxStoneHdrevent as the first MIDI event; - a tempo event giving the app's one-millisecond-per-tick mapping;
- the stone's standard MIDI and JMX events; and
- a zero-delta End of Track event.
The JMX header carries the asset, device, stone, time, position, tempo, sound, lifetime-statistics, firmware, fabrication, and optional clip fields. See the JMX MIDI file specification for its event framing and complete field catalog.
#Completion markers
Downloaded recordings can have two kinds of sidecar marker:
| Path | Payload | Meaning |
|---|---|---|
<stoneIdx>.stone.mid.gz.completed.json | {"completed":true} | The app considers that stone fully downloaded and immutable. |
completed.json | {"completed":true} | The app considers the entire asset fully downloaded. |
These files are presence markers. Current app code checks whether they exist; it does not parse their JSON value. A missing marker means only that completeness is not established. It does not by itself prove that the associated MIDI bytes are invalid.
#Saved clips
Each saved clip occupies one directory and one payload file:
clips/<year>/JClip-<unixtime>-<offset>-<clipUuid>-<Mon>-<DD>-<YYYY>/clip.stone.mid.gzExample:
clips/2024/
JClip-1707346233-N600-cc9d75fc-50d3-4ea5-93b9-fd1459fb1363-Feb-07-2024/
clip.stone.mid.gz| Component | Meaning |
|---|---|
<unixtime> | Clip creation time as whole seconds since 1970-01-01 00:00:00 UTC. |
<offset> | Local offset from UTC in minutes, encoded as P<minutes> for zero or positive and N<minutes> for negative. Examples: P0, P330, N600. |
<clipUuid> | 36-character clip UUID. App-created clips currently begin with cc; imported clips currently begin with im. |
<Mon>-<DD>-<YYYY> | Local calendar date computed from unixtime plus offset. |
<year> | The same local year printed in the clip directory name. |
#Clip payload
clip.stone.mid.gz has the same gzip-to-format-0-SMF envelope as a recording stone. Its first event is jmxStoneHdr, whose clip object provides the clip-specific metadata:
{
"clip": {
"clipUuid": "cc9d75fc-50d3-4ea5-93b9-fd1459fb1363",
"unixtime": 1707346233,
"localOffset": -600,
"title": "Late-night idea"
}
}The example shows only the clip member of the much larger jmxStoneHdr object. The directory identity and embedded values should agree. The title exists only in the embedded header; there is no separate clip metadata JSON file.
#Albums
An album is stored under its UUID:
albums/<albumUuid>/info.json
albums/<albumUuid>/clips/<clipName>.jsonalbumUuid is a 36-character hexadecimal UUID; UUIDs generated by current app builds begin with aa. The built-in Recents view is virtual and is not exported as an album.
#info.json
{
"albumUuid": "aa1077cb-7f94-f663-49dd-c2337374abfb",
"title": "Rehearsal ideas",
"creationTime": {
"unixtime": 1745510400,
"localOffset": -300
},
"lastModified": {
"unixtime": 1745596800,
"localOffset": -300
},
"clipCount": 2
}| Field | Type | Meaning |
|---|---|---|
albumUuid | string | Album identity. It should equal the enclosing directory name. |
title | string | User-visible album title. |
creationTime | object | Album creation time as unixtime seconds and localOffset minutes. |
lastModified | object | Most recent album edit in the same time representation. |
clipCount | integer | Cached membership count. Readers should verify it against valid marker files. |
#Clip membership
Every file in albums/<albumUuid>/clips/ is an empty marker. Its basename is a complete clip directory name plus .json:
JClip-1707346233-N600-cc9d75fc-50d3-4ea5-93b9-fd1459fb1363-Feb-07-2024.jsonDespite the extension, the marker has no JSON payload; current writers create a zero-byte file. Membership is encoded entirely by the filename. The corresponding clip payload lives under clips/. Albums do not contain copies or links to MIDI data, and the same clip can appear in any number of albums.
Readers should ignore malformed marker names and should handle dangling markers whose referenced clip is absent. Album order is not stored. Clip membership is presented newest first by sorting the creation timestamps parsed from marker filenames.
#Time and identifiers
The app represents a wall-clock value with two integers:
{
"unixtime": 1745510400,
"localOffset": -300
}unixtimeis whole seconds since the Unix epoch in UTC.localOffsetis the local offset from UTC in minutes.- Local calendar fields are obtained by adding
localOffset * 60seconds before calendar conversion. - The offset is stored with the timestamp; readers should not replace it with the importing machine's current timezone.
Asset indexes and stone indexes provide device-local ordering. Stable identity comes from UUID fields embedded in JMX data and from the Jamcorder, clip, and album UUIDs. Do not assume that an asset index or filename is globally unique without its Jamcorder UUID.
#Import and merge behavior
The app normalizes ZIP entry separators to /, rejects empty and parent-traversing paths, and recognizes four top-level names case-insensitively: JAMC, clips, albums, and settings. Unknown top-level entries are ignored.
| Tree | Current merge rule |
|---|---|
JAMC/ | Imported files do not overwrite files already present at the same path. |
clips/ | Imported files do not overwrite files already present at the same path. |
albums/ | An archive album is intended to be skipped when the same album UUID directory already exists, preventing partial merges with an existing album. |
settings/ | Out of scope. Current imports consider it only when the destination has no settings directory. |
After extraction, the app rescans recordings, albums, and clips from disk. A library archive is therefore a merge source rather than a transactional image of the destination documents directory. Import does not remove destination content that is absent from the archive.
#Reader requirements
A robust reader or importer should:
- Enforce limits on compressed size, decompressed size, entry count, path length, JSON size, and MIDI event lengths before allocating or extracting.
- Normalize both slash styles, reject absolute paths, drive prefixes, parent traversal, symlinks, and any output path that escapes the chosen destination.
- Compare top-level path names case-insensitively, but preserve and validate the documented case of names below them.
- Ignore unknown top-level trees, unknown sidecars, unknown JSON fields, and the out-of-scope
settings/subtree. - Gunzip every
.stone.mid.gzindependently and validate the resulting SMF and JMX structure before trusting embedded offsets, lengths, times, or identifiers. - Cross-check path identity against embedded JMX fields and resolve conflicts explicitly. Do not silently use a path's UUID with another payload's metadata.
- Treat completion JSON as a presence marker, not proof that an entry is complete, authentic, or uncorrupted.
- Parse album JSON by field name, not property order, and tolerate additional fields for forward compatibility.
- Derive album membership from valid marker filenames, verify referenced clips exist, and treat
clipCountas cached advisory data. - Preserve unknown valid files when performing a lossless round trip, because the archive has no manifest declaring an exhaustive schema.