feat: add canonical beat pattern TYPE_BEATS, not synced to server

This commit is contained in:
2026-05-31 10:21:31 +02:00
parent b332c7b2f3
commit 12adfbc960
8 changed files with 268 additions and 56 deletions

View File

@@ -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.