agent.kompo.se

Media Analysis (BPM, Beat Grid, MusicDNA)

After uploading an audio file (see file-management), request analysis to extract BPM, a beat/downbeat grid, and segment structure. This is the “Analyze & tag” step — its output feeds directly into komposition-format beat timing.

Read source-metadata-approach first. This unit is the endpoint contract — what the API returns and how to poll it. That unit is the approach: why the grid rather than the scalar bpm is the product, how to sanity-check a tempo estimate, why a grid is only valid for one exact audio file, and why naming segments is part of this step rather than an afterthought.

Trigger analysis

Analysis is submitted automatically when you complete an audio upload via `kli upload-analyze

` (audio files only — `.mp3`, `.wav`, `.flac`, `.aac`, `.ogg`, `.m4a`). The command confirms that the upload response queued analysis; it does not wait for analysis to finish. Inspect progress and the completed beat grid with: ```bash kli media-analysis/ ``` If you upload through the raw `POST /api/upload/complete` endpoint directly instead of the CLI shortcut, confirm with the response or by polling the analysis endpoint below whether analysis was submitted automatically for that content type. ## Poll for results ``` GET /api/multimedia/{fileId}/analysis Authorization: Bearer Accept: application/json ``` **Analysis is complete when the response includes a populated `analyzedAt` timestamp and a finite `bpm`.** There is no separate job/status field on this resource — poll this endpoint directly (every 5s is reasonable) until `analyzedAt` is present, or timeout after a few minutes. ### Real response shape (verified against a live deployment — flat, no wrapper object) ```json { "fileId": "ab87f27d-62df-433a-a86d-8d31e607208a", "bpm": 144.25, "confidence": "MEDIUM", "method": "windowed-consensus-q2q3", "beatCount": 567, "beats": [140, 557, 975, "..."], "downbeats": [140, 1810, 3480, "..."], "beat1Ms": 140, "durationMs": 237672, "segments": ["..."], "analyzedAt": "2026-07-12T10:30:00.000Z", "audioUrl": "...", "backends": ["..."], "fusedSegments": ["..."], "perBeatSignals": ["..."], "summary": "...", "userBoundaries": ["..."] } ``` **There is no `musicDNA` wrapper, no `analysisJob` object, and no `status` field on this resource.** Read fields directly off the top-level response. ### Field reference | Field | Meaning | |-------|---------| | `downbeats` | **The bar grid** — the subset of beats marking bar/measure starts, in milliseconds. This is what you place segments against. A bar's position is *read from here*, never derived by multiplying a BPM. | | `beats` | Array of beat positions in **milliseconds** from track start. The full grid; `downbeats` is its bar-start subset. | | `bpm` | Detected tempo — a **label**, not the grid. Use it for the komposition's `## Metadata / - BPM` field and for sanity checks; do not use it to compute where anything is. | | `confidence` | `LOW`/`MEDIUM`/`HIGH` — how confident the detector is, not a pass/fail gate. | | `method` | Which detection algorithm/backend produced this result (informational — different tracks may use different methods; do not branch logic on this value). | | `beat1Ms` | Position of the first detected beat, in milliseconds — effectively the track's rhythmic start offset (not necessarily 0 if there's a silent intro). A non-zero value here is exactly why bar positions cannot be multiplied out. | | `durationMs` | Full track duration in milliseconds. | | `segments` | Structural segments (verse/chorus-style boundaries), if detected. Machine labels are generic — treat them as the *start* of segment naming, not the end. The names the user gives these sections are the vocabulary they will compose with; capture them onto the kilde ([sources-workflow](/docs/kcp/sources-workflow.html)). | | `analyzedAt` | ISO timestamp — **presence of this field is the completion signal.** | ### Cross-checking BPM (do this before any beat-critical work) The scalar `bpm` field can disagree with the track's own `beats` array on difficult material. A cheap sanity check: compute the median inter-beat interval from `beats` and derive `60000 / medianIntervalMs` as a secondary estimate, then compare. For four-on-the-floor material the correction vocabulary is **2× / 1× / ½× and nothing else**: - **Agreement, or a clean 2× or 0.5× factor** — usable. Pick the reading that matches the bass drum (see [source-metadata-approach](/docs/kcp/source-metadata-approach.html) for why the bass drum first, hi-hats second). - **Anything else** — the analysis path is broken, regardless of what `confidence` says. This is not a metrical ambiguity for the user to arbitrate and not something to split the difference on. Re-analyze, or treat the source as unusable for beat-critical work. Ratios such as 3:2 or 2:3 are **not** valid explanations here — they are musically strange for this material and treating one as acceptable launders a broken result into a plausible-looking one. Material with a 3/8 feel, swing, or live playing needs a different posture entirely — don't assume machine-made music, and ask the user to describe the structure during acquisition. See [source-metadata-approach](/docs/kcp/source-metadata-approach.html). ### A grid is valid for exactly one audio file Beat and downbeat positions describe **the specific file they were analyzed from**. They are meaningless against a different remaster, a single edit vs the album version, or a re-upload or re-encode of the same track — different intro length, different tempo read. Keep every grid, offset, and segment boundary bound to the `fileId` it came from, and never reuse one across two file IDs even when it is "the same song". If the audio is re-uploaded, re-analyze. ## Using the result in a komposition A komposition has **one BPM for the whole document** (see [komposition-format](/docs/kcp/komposition-format.html)) — every track's beat positions are converted to time using that single value, not each source's own measured tempo. This is a V1/V2 format constraint. For V3's alternative, see the entered `## Overlay Segments` format in [komposition-v3](/docs/kcp/komposition-v3.html). With multiple audio kilder at different measured BPMs, either stay in V1/V2 and declare the BPM you want the timeline to run at while adjusting the other tracks' beat counts, or keep the tracks close in tempo. Use V3 only through its documented Overlay Segments format. The server also declares a `POST /api/multimedia/beat-segments` operation that resolves a beat-based layout across **multiple** files against each file's own measured grid — see [source-metadata-approach](/docs/kcp/source-metadata-approach.html). It is declared but not yet verified from this client.