feat: add canonical beat pattern TYPE_BEATS, not synced to server
This commit is contained in:
58
DESIGN.md
58
DESIGN.md
@@ -11,6 +11,64 @@ Product goals, DSP scope, and repo layout are summarized in [SPECS.md](SPECS.md)
|
||||
- Collection mode: app records sensor data during a run
|
||||
- Settings: allows to enable modes above, logout button
|
||||
|
||||
## Data files
|
||||
|
||||
Filesystem layout of JSON files on primary storage (e.g. Pixel 5: `/storage/emulated/0/Documents/Lockstep/`):
|
||||
|
||||
```
|
||||
/storage/emulated/0/Documents/Lockstep/
|
||||
├── 2026-05-31_10-15-30/ ← annotation session (TYPE_ANNOTATION)
|
||||
│ ├── Running Mix_001.json
|
||||
│ └── Running Mix_002.json
|
||||
├── 2026-05-31_14-22-05/ ← now-playing collection session (TYPE_COLLECTION)
|
||||
│ ├── Running Mix_001.json
|
||||
│ └── Running Mix_002.json
|
||||
└── Beats/ ← canonical beats per playlist (TYPE_BEATS)
|
||||
└── Running Mix/
|
||||
├── 001.json
|
||||
└── 002.json
|
||||
```
|
||||
|
||||
Each type is recorded in Room `file_metadata` (`FileMetadataEntity`) with `fileUri`, `trackId`, `type`, `version`, `synced`, and `lastSyncedAt` (epoch millis).
|
||||
|
||||
| Type | Constant | Written by | Path pattern | Server sync |
|
||||
|------|----------|------------|--------------|-------------|
|
||||
| **Annotation session** | `TYPE_ANNOTATION` | Annotation screen (`BeatAnnotationStorage`) | `Documents/Lockstep/{sessionFolder}/{playlist}_{NNN}.json` — one timestamped folder per annotation session | Yes — uploaded via `POST /metadata` when signed in |
|
||||
| **Run collection** | `TYPE_COLLECTION` | Now Playing with collect-run-data enabled (`RunDataStorage`) | `Documents/Lockstep/{sessionFolder}/{playlist}_{NNN}.json` — one timestamped folder per run session | Yes |
|
||||
| **Canonical beats** | `TYPE_BEATS` | Annotation screen (`BeatAnnotationStorage`) — written alongside each session annotation | `Documents/Lockstep/Beats/{playlist_name}/{NNN}.json` — one folder per playlist; **overwrites** on re-annotation | **No** — tracked locally only; `synced = 1` and `lastSyncedAt` set at write time |
|
||||
|
||||
During an **annotation session**, leaving a track writes **both** `TYPE_ANNOTATION` (append-only session archive) and `TYPE_BEATS` (latest beats for that playlist slot).
|
||||
|
||||
### Beat JSON schema (`TYPE_ANNOTATION` and `TYPE_BEATS`)
|
||||
|
||||
Same schema for session annotations and canonical beats:
|
||||
|
||||
```json
|
||||
{
|
||||
"contentId": "primary:Documents/Music/track.mp3",
|
||||
"title": "Track Title",
|
||||
"artist": "Artist Name",
|
||||
"beatTimesSec": [1.23, 2.45, 3.67]
|
||||
}
|
||||
```
|
||||
|
||||
- **`contentId`** — SAF document id of the paired local MP3 when available; otherwise Spotify track id.
|
||||
- **`beatTimesSec`** — user-tapped beat times in seconds (playback position).
|
||||
|
||||
### Collection JSON schema (`TYPE_COLLECTION`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [ { "timestamp": 0, "positionMs": 0, "values": [0.0, 0.0, 9.8] } ],
|
||||
"gyro": [ { "timestamp": 0, "values": [0.0, 0.0, 0.0] } ],
|
||||
"gps": [ { "timestamp": 0, "values": [48.2, 16.3, 200.0] } ],
|
||||
"meta": "content://…",
|
||||
"title": "Track Title",
|
||||
"artist": "Artist Name",
|
||||
"versionCode": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Already captured from SPECS.md
|
||||
|
||||
- **Product**: Pace-aware playlist ordering + real-time playback adaptation via accelerometer, **libpasada** (C++/JNI) + **Oboe**, user-supplied **MP3** via file descriptors, feedback loop for sensor-driven playback.
|
||||
|
||||
Reference in New Issue
Block a user