From 66cd7ede3a2ddea312672aa55c28198adfac5698 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 21:50:44 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat(backend):=20phase2-export.v1=20?= =?UTF-8?q?=E2=80=94=20one-call=20handoff=20envelope=20for=20asa-ableton?= =?UTF-8?q?=20+=20the=20rec-proof=20harness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ASA -> asa-ableton boundary (and the GOAL.md fixture loop) ran on hand-extracted interpretation JSON: the result lives embedded in the run snapshot, the warn-and-keep validationWarnings in a different subtree (attempt diagnostics), and provenance in a third. asa-ableton's Gate alpha fixture and every per-fixture phase2.json in the owner plan were produced by snapshot surgery — an unversioned, fragile handoff. 1. phase2_export.py + GET /api/analysis-runs/{run_id}/export/phase2: single self-contained phase2-export.v1 envelope — the stored producer_summary interpretation result verbatim (incl. the frozen recommendations.v1 projection), the authoritative Phase 1 payload its citations resolve against (invariant #2 verifiable offline), the full validationWarnings trail, and provenance. Thin lookup-and-serve, csv_export.py pattern; 404 RUN_NOT_FOUND / PHASE2_EXPORT_NOT_AVAILABLE. Derived and read-only — exports stored state, rewrites nothing. 2. recommendation_evaluation.coerce_phase2_payload: --phase2 (and a fixture-dir phase2.json) now accepts either a bare Phase2Result or the export envelope, so the downloaded file feeds the scorer as-is. 3. docs/ASA_ABLETON_BOUNDARY.md: the cross-repo contract — file-coupled not code-coupled, consumer guidance (prefer recommendations.v1 entries; dedupe is a consumer concern — Gate alpha's 60.7%-vs-50% skip-rate accounting; flagged != invalid), and the v1 freeze policy. Tests: +15 (10 builder incl. envelope-key freeze, 3 route, 2 unwrap). tests.test_server 227 OK; adjacent suites 110 OK; full discover matches the unmodified-tree baseline exactly (env-only matplotlib/torch gaps). https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy --- CLAUDE.md | 4 +- apps/backend/AGENTS.md | 1 + apps/backend/ARCHITECTURE.md | 2 + apps/backend/phase2_export.py | 121 +++++++++++ apps/backend/recommendation_evaluation.py | 21 ++ .../scripts/evaluate_recommendations.py | 11 +- apps/backend/server.py | 61 ++++++ apps/backend/tests/test_phase2_export.py | 201 ++++++++++++++++++ .../tests/test_recommendation_evaluation.py | 19 ++ apps/backend/tests/test_server.py | 116 ++++++++++ docs/ASA_ABLETON_BOUNDARY.md | 113 ++++++++++ 11 files changed, 666 insertions(+), 4 deletions(-) create mode 100644 apps/backend/phase2_export.py create mode 100644 apps/backend/tests/test_phase2_export.py create mode 100644 docs/ASA_ABLETON_BOUNDARY.md diff --git a/CLAUDE.md b/CLAUDE.md index 4160d3f7..bfac519d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -209,7 +209,7 @@ Operational and one-shot scripts live in two places. They are not on the request 2. `packages/loudness-spectro-wasm/` — browser-first WebAssembly DSP (ITU-R BS.1770-5 / EBU R128 loudness, A-weighted spectrum, and spectral-reassignment spectrogram). Rust lifted from [openmeters](https://github.com/httpsworldview/openmeters) and compiled via `wasm-bindgen`, GPL-3.0-or-later. **Phase 1: standalone, not yet imported by `apps/ui` or `apps/backend`.** Has its own Cargo workspace, `npm`/`cargo` build, and EBU/ebur128/pyloudnorm validation layers — see [`packages/loudness-spectro-wasm/README.md`](packages/loudness-spectro-wasm/README.md). When integration lands, the canonical Phase 1 LUFS contract still comes from the Essentia path until this is wired in and proven at parity. (The reassigned spectrogram on the product path today comes from librosa via the `reassigned` spectral-enhancement endpoint, not this package.) 3. `audits/` — dated, automated audit reports (e.g. `nightly-2026-05-19.md`, `phase2-recommendation-surface-2026-05-24.md`, `full-review-2026-05-30.md`). Past-tense paper trail; not imported by either app. (Older one-shot advisory deliverables like the phase 1 audit have been archived under `docs/history/phase1-audit/`.) 4. `incorporations/` — planning docs for incorporating upstream projects (e.g. `forking-plans-2026-05-14.md`, `beat-this-measurement-gate-2026-05-20.md`, `msst-separation-licence-gate-2026-06-05.md`). Planning notes only; not code. -5. `docs/` — long-form rationale and architectural records. Key contents: `ARCHITECTURE_STRATEGY.md` (three-layer design rationale), `SETUP.md` (first-run setup), `LAYER2_EVALUATION.md` and `POLYPHONIC_TRANSCRIPTION_SPIKE.md` (Layer 2 research), `SAMPLE_GENERATION.md` (Phase 3 design), `PHASE2_PROVIDER.md` (Phase 2 provider selection — Gemini vs MOSS sidecar, licence status, provider contract), `adr/` (Architecture Decision Records — `0001-phase1-json-schema-v1.md` declares the v1 schema stability contract; `0002-phase1-loudness-units-v2.md` bumped `truePeak` to dBTP and `bpmConfidence` to normalized 0–1, schema now at `phase1.v2`; `0003-recommendations-contract-v1.md` freezes the Phase 2 `recommendations` contract (`recommendations.v1`) — a normalized, schema-validated, citation-gated projection of the device cards), and `history/` (completed plans, one-shot audits, and deliverables — past-tense). Read the relevant doc *before* structural changes; do not treat them as living API docs. +5. `docs/` — long-form rationale and architectural records. Key contents: `ARCHITECTURE_STRATEGY.md` (three-layer design rationale), `SETUP.md` (first-run setup), `LAYER2_EVALUATION.md` and `POLYPHONIC_TRANSCRIPTION_SPIKE.md` (Layer 2 research), `SAMPLE_GENERATION.md` (Phase 3 design), `PHASE2_PROVIDER.md` (Phase 2 provider selection — Gemini vs MOSS sidecar, licence status, provider contract), `ASA_ABLETON_BOUNDARY.md` (the file-coupled contract between ASA and the sibling `asa-ableton` `.als` generator — the `phase2-export.v1` handoff envelope), `adr/` (Architecture Decision Records — `0001-phase1-json-schema-v1.md` declares the v1 schema stability contract; `0002-phase1-loudness-units-v2.md` bumped `truePeak` to dBTP and `bpmConfidence` to normalized 0–1, schema now at `phase1.v2`; `0003-recommendations-contract-v1.md` freezes the Phase 2 `recommendations` contract (`recommendations.v1`) — a normalized, schema-validated, citation-gated projection of the device cards), and `history/` (completed plans, one-shot audits, and deliverables — past-tense). Read the relevant doc *before* structural changes; do not treat them as living API docs. 6. `tests/ground_truth/` — labeled-corpus fixtures consumed by `scripts/calibrate_confidence.py` (see `tests/ground_truth/README.md`). Not a test suite — each app owns its own (`apps/backend/tests/`, `apps/ui/tests/`). ### Three-Layer Model @@ -298,6 +298,8 @@ Artifact access goes through `artifact_storage.py` rather than direct disk paths 24. **`phase2_provider.py` + `moss_sidecar/`**: Selectable Phase 2 interpretation provider (default-off experiment). `ASA_PHASE2_PROVIDER=moss` routes the producer_summary interpretation to a self-hosted OpenMOSS MOSS-Audio FastAPI sidecar instead of Gemini; `ASA_PHASE2_PROVIDER=claude` routes it to `ClaudeCliProvider` — a text-only interpreter that runs the local Claude Code CLI headless (sandboxed: `--safe-mode`, `--tools ""`, `--no-session-persistence`, response schema enforced via `--json-schema`), grounds purely on the prompt's embedded Phase 1 JSON, and needs no `GEMINI_API_KEY`. All paths flow through the identical parse/citation/catalogue validators. STEP ONE licence gate: MOSS-Audio *weights* are Apache-2.0 but the *modeling code* has no effective licence, so the sidecar ships no OpenMOSS code and the real-model path is a 501 stub — research-only, not promotable. `moss_sidecar/app.py` is the FastAPI sidecar; `moss_sidecar/mock_interpreter.py` generates deterministic schema-valid Phase-1-grounded output for the default `ASA_MOSS_SIDECAR_MODE=mock` path. See `docs/PHASE2_PROVIDER.md` for the full design rationale and licence analysis. +25. **`phase2_export.py`**: Versioned Phase 2 handoff envelope (`phase2-export.v1`) for downstream consumers — the sibling `asa-ableton` repo (turns ASA recommendations into an openable Live 12 `.als` starter set) and `scripts/evaluate_recommendations.py --phase2`. Backs `GET /api/analysis-runs/{run_id}/export/phase2`: the stored `producer_summary` interpretation result verbatim (including the frozen `recommendations.v1` projection), the authoritative Phase 1 payload its citations resolve against, the full `validationWarnings` trail, and provenance — one curl, one self-contained file, no snapshot surgery. Same thin lookup-and-serve pattern as `csv_export.py`; key set frozen by `tests/test_phase2_export.py`. Cross-repo contract documented in `docs/ASA_ABLETON_BOUNDARY.md`. + The subprocess isolation means `analyze.py` works as a standalone CLI. Check `apps/backend/JSON_SCHEMA.md` before adding new analyzer output fields. Check `apps/backend/ARCHITECTURE.md` for the full HTTP flow and contract details. **Phase 2 (`POST /api/phase2`, legacy compat):** Uploads audio to Gemini inline if ≤100 MiB, or via the Gemini Files API if larger. Phase 1 JSON is appended to the system prompt from `prompts/phase2_system.txt`. Also relevant: `prompts/stem_summary_system.txt` and `prompts/live12_device_catalog.json` (the *prompt-injected* device catalogue — distinct from the runtime-validation `data/live12_catalogue.json` consumed by `phase2_catalogue_gates.py`). Backend defense-in-depth: `server_phase2.py`'s `_validate_phase2_citation_paths` mirrors the frontend citation-existence check and emits `validationWarnings` when a recommendation cites a Phase 1 path that doesn't exist — it flags invented citations rather than failing, since Phase 1 stays authoritative (invariant #1). `phase2_catalogue_gates.apply_live12_catalogue_gates` runs immediately after for source-catalogue checks (see backend file #20 above). Additional regression gates: `tests/test_phase2_grammar_fix.py` locks in the gerund-fix post-process in `server_phase2.py`; `tests/test_phase2_prompt_catalog.py` pins prompt examples against the Live 12 catalogue. diff --git a/apps/backend/AGENTS.md b/apps/backend/AGENTS.md index 04f2f9d1..1f52af49 100644 --- a/apps/backend/AGENTS.md +++ b/apps/backend/AGENTS.md @@ -118,6 +118,7 @@ python3.11 -m venv venv - `url_ingest.py`: SSRF-guarded URL-mode ingestion for `POST /api/analysis-runs`. Fetches a public `http`/`https` audio file and streams the bytes through the same downstream pipeline as a multipart upload, enforcing the shared 100 MiB cap. - `audio_mime.py`: Canonical, host-independent filename→MIME map for ingested audio (`canonical_audio_mime`). Mirrors `apps/ui/src/services/audioFile.ts` so a `.flac` resolves to `audio/flac` on every OS (stdlib `mimetypes` is host-dependent). Imported directly by `server_phase2.py` and `url_ingest.py`, and reached by `server.py` via `server_phase2._get_audio_mime_type` — keep the two maps in sync. - `csv_export.py`: CSV exporters for Phase 1 time-series fields; backs `GET /api/analysis-runs/{run_id}/export/csv/{field_path}`. +- `phase2_export.py`: Versioned `phase2-export.v1` handoff envelope for downstream consumers (the sibling `asa-ableton` `.als` generator; `scripts/evaluate_recommendations.py --phase2`); backs `GET /api/analysis-runs/{run_id}/export/phase2`. Same thin lookup-and-serve pattern as `csv_export.py`. Cross-repo contract documented in `docs/ASA_ABLETON_BOUNDARY.md`. - `stage_status.py`: Collapses the eight internal stage statuses into the additive client-facing `publicStatus` field carried on every stage in the run snapshot. - `sample_generation.py`, `sample_theory.py`, `sample_synthesis.py`, `sample_drums.py`: Phase 3 audition-sample generation — PyTheory plan, FluidSynth/sine-additive render, NumPy drum one-shots, citation manifest. On-demand only. - `dsp_bandbank.py`, `dsp_utils.py`: Shared DSP primitives — `BatchedBandpass` Butterworth bank and cross-module utilities. diff --git a/apps/backend/ARCHITECTURE.md b/apps/backend/ARCHITECTURE.md index 1a5ed608..2d1d3c1d 100644 --- a/apps/backend/ARCHITECTURE.md +++ b/apps/backend/ARCHITECTURE.md @@ -17,6 +17,7 @@ | `url_ingest.py` | SSRF-guarded URL-mode ingestion for `POST /api/analysis-runs`. Fetches a public `http`/`https` audio file and streams the bytes through the same downstream pipeline as a multipart upload, enforcing the shared 100 MiB cap. | | `audio_mime.py` | Canonical, host-independent filename→MIME resolution for ingested audio (`canonical_audio_mime`). Mirrors the frontend map in `apps/ui/src/services/audioFile.ts` so a `.flac` resolves to `audio/flac` on every OS — stdlib `mimetypes` is host-dependent (`audio/x-flac` on macOS vs `audio/flac` on Linux), which failed the test gate and could mislabel a FLAC handed to Gemini. Imported directly by `server_phase2.py` and `url_ingest.py`, and reached by `server.py` via `server_phase2._get_audio_mime_type`. | | `csv_export.py` | CSV exporters for Phase 1 time-series fields, keyed by dotted JSON path. Backs `GET /api/analysis-runs/{run_id}/export/csv/{field_path}` and keeps the route handler a thin lookup-and-serve. | +| `phase2_export.py` | Versioned Phase 2 handoff envelope (`phase2-export.v1`) for downstream consumers — the sibling `asa-ableton` `.als` generator and `scripts/evaluate_recommendations.py --phase2`. Backs `GET /api/analysis-runs/{run_id}/export/phase2`; same thin lookup-and-serve pattern as `csv_export.py`. See [`docs/ASA_ABLETON_BOUNDARY.md`](../../docs/ASA_ABLETON_BOUNDARY.md). | | `transcription_pianoroll.py` | Renders the pitch-note translation stage's `transcriptionDetail` as a velocity-encoded `(pitch, time)` uint8 matrix via `symusic`. Backs `GET /api/analysis-runs/{run_id}/transcription/pianoroll`. Derived view — Phase 1 stays authoritative; the response cites the Phase 1 `bpm` and `timeSignature` so chain of custody (PURPOSE invariant #2) is preserved. | | `stage_status.py` | Collapses the eight internal stage statuses into the additive client-facing `publicStatus` field carried on every stage in the run snapshot. | | `server_samples.py` + `sample_generation.py` / `sample_theory.py` / `sample_synthesis.py` / `sample_drums.py` | Phase 3 audition-sample generation. PyTheory musical plan, FluidSynth (sine-additive fallback) audio render, NumPy drum one-shots, and a citation manifest tying every clip back to a Phase 1 field. On-demand only — not part of the staged-execution queue. See [`docs/SAMPLE_GENERATION.md`](../../docs/SAMPLE_GENERATION.md). | @@ -93,6 +94,7 @@ Custom routes: - `GET /api/analysis-runs/{run_id}/artifacts` and `…/artifacts/{artifact_id}` - `GET /api/analysis-runs/{run_id}/source-audio` — re-serves the original ingested audio for the run. Owner-only (no admin bypass). Saves a round-trip vs looking up the source artifact id first. - `GET /api/analysis-runs/{run_id}/export/csv/{field_path}` — CSV export of a Phase 1 time-series field. See [`docs/adr/0001-phase1-json-schema-v1.md`](../../docs/adr/0001-phase1-json-schema-v1.md) and the registry in [`csv_export.py`](csv_export.py). +- `GET /api/analysis-runs/{run_id}/export/phase2` — single-file `phase2-export.v1` handoff envelope: the stored `producer_summary` interpretation result verbatim (including the frozen `recommendations.v1` projection), the authoritative Phase 1 payload its citations resolve against, the full `validationWarnings` trail, and provenance. The cross-repo contract for the sibling `asa-ableton` `.als` generator and the input format `scripts/evaluate_recommendations.py --phase2` accepts. Status codes: 200 (attachment), 404 (`RUN_NOT_FOUND`/`PHASE2_EXPORT_NOT_AVAILABLE`). Implementation in [`phase2_export.py`](phase2_export.py); contract doc at [`docs/ASA_ABLETON_BOUNDARY.md`](../../docs/ASA_ABLETON_BOUNDARY.md). - `GET /api/analysis-runs/{run_id}/transcription/pianoroll` — velocity-encoded pianoroll matrix derived from the pitch-note translation stage's `transcriptionDetail`. Query params: `mode` (`frame`|`onset`, default `frame`), `pitchLow` (default 21), `pitchHigh` (default 109, exclusive), `tpq` (default 4). Response cites the Phase 1 `bpm` and `timeSignature` so every cell traces back to a measurement. Status codes: 200 (payload), 400 (`INVALID_MODE`/`INVALID_PITCH_RANGE`/`INVALID_TPQ`), 404 (`RUN_NOT_FOUND`/`TRANSCRIPTION_NOT_REQUESTED`/`TRANSCRIPTION_NOT_AVAILABLE`), 409 (`MEASUREMENT_NOT_COMPLETED`/`TRANSCRIPTION_NOT_COMPLETED`). Implementation in [`transcription_pianoroll.py`](transcription_pianoroll.py). - `POST /api/analysis-runs/{run_id}/spectral-enhancements/{kind}` — on-demand spectral artifacts. `kind` is one of `cqt`, `hpss`, `onset`, `chroma_interactive`, or `reassigned` (sharper transient/frequency localization via `librosa.reassigned_spectrogram`). - `POST /api/analysis-runs/{run_id}/pitch-note-translations` diff --git a/apps/backend/phase2_export.py b/apps/backend/phase2_export.py new file mode 100644 index 00000000..1177b4c7 --- /dev/null +++ b/apps/backend/phase2_export.py @@ -0,0 +1,121 @@ +"""Phase 2 handoff export — versioned envelope for downstream consumers. + +Backs ``GET /api/analysis-runs/{run_id}/export/phase2`` and keeps the route +handler a thin lookup-and-serve (same pattern as ``csv_export.py``). + +ASA's Phase 2 interpretation result is the input to downstream tooling that +lives *outside* this repo — concretely the sibling ``asa-ableton`` project +(turns the device cards into an openable Live 12 ``.als`` starter set) and the +in-repo recommendation-proof harness (``scripts/evaluate_recommendations.py +--phase2``). Until now that JSON had no export path: it exists only embedded in +the full run snapshot, with the validation warnings living in a different +subtree (attempt ``diagnostics``) and the grounding provenance in a third. +Consumers hand-extracted it, which is exactly the kind of unversioned, fragile +handoff ADR 0001 warns about. + +This module freezes a single-file envelope (``phase2-export.v1``): + + - ``phase2`` — the stored ``producer_summary`` interpretation result, + verbatim, exactly as the validator tail left it (including the additive + ``recommendations`` field — the frozen ``recommendations.v1`` contract, + ADR 0003). + - ``phase1`` — the authoritative measurement payload the interpretation was + grounded on, included so a consumer can independently verify that every + cited ``phase1Fields`` path resolves (PURPOSE.md invariant #2) without a + second request. + - ``validationWarnings`` — the full warn-and-keep trail (citation-existence, + semantic, catalogue-gate) from the attempt diagnostics, so a non-browser + consumer sees the same chain-of-custody flags the UI renders. + - ``provenance`` — the stored attempt provenance (interpretation schema + version, profile, model, prompt version, grounding ids), verbatim. + +Derived and read-only: the export projects stored run state and never +re-estimates or mutates anything (invariant #1). Only the ``producer_summary`` +profile is exportable — it is the profile that carries device cards; +``stem_summary`` has no downstream device consumer. + +See ``docs/ASA_ABLETON_BOUNDARY.md`` for the cross-repo contract this backs. +""" + +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Any, Mapping + +EXPORT_SCHEMA_VERSION = "phase2-export.v1" + +# The device-card profile. stem_summary results carry no device cards and are +# deliberately not exportable through this envelope. +EXPORTABLE_PROFILE_ID = "producer_summary" + + +def _utc_now_iso() -> str: + return datetime.now(timezone.utc).isoformat() + + +def _producer_summary_attempt(snapshot: Mapping[str, Any]) -> Mapping[str, Any] | None: + """The completed producer_summary attempt from a run snapshot, or ``None``. + + Reads the per-profile map rather than the stage-level ``result`` because + the stage's preferred attempt can be a different profile (e.g. + ``stem_summary`` finishing first). + """ + stages = snapshot.get("stages") + if not isinstance(stages, Mapping): + return None + interpretation = stages.get("interpretation") + if not isinstance(interpretation, Mapping): + return None + profiles = interpretation.get("profiles") + if not isinstance(profiles, Mapping): + return None + attempt = profiles.get(EXPORTABLE_PROFILE_ID) + if not isinstance(attempt, Mapping): + return None + if attempt.get("status") != "completed": + return None + if not isinstance(attempt.get("result"), Mapping): + # A completed attempt can carry a null result (skip path) — there is + # nothing to hand off in that case. + return None + return attempt + + +def _validation_warnings(attempt: Mapping[str, Any]) -> list[dict[str, Any]]: + diagnostics = attempt.get("diagnostics") + if not isinstance(diagnostics, Mapping): + return [] + warnings = diagnostics.get("validationWarnings") + if not isinstance(warnings, list): + return [] + return [warning for warning in warnings if isinstance(warning, dict)] + + +def build_phase2_export( + snapshot: Mapping[str, Any], + *, + exported_at: str | None = None, +) -> dict[str, Any] | None: + """Project a run snapshot into a ``phase2-export.v1`` envelope. + + Returns ``None`` when the run has no completed ``producer_summary`` + interpretation result to hand off (not requested, still running, failed, + or completed-with-skip). Deterministic and side-effect-free. + """ + attempt = _producer_summary_attempt(snapshot) + if attempt is None: + return None + measurement = snapshot.get("stages", {}).get("measurement", {}) + measurement_result = ( + measurement.get("result") if isinstance(measurement, Mapping) else None + ) + provenance = attempt.get("provenance") + return { + "schemaVersion": EXPORT_SCHEMA_VERSION, + "runId": snapshot.get("runId"), + "exportedAt": exported_at if exported_at is not None else _utc_now_iso(), + "provenance": provenance if isinstance(provenance, Mapping) else None, + "validationWarnings": _validation_warnings(attempt), + "phase1": measurement_result if isinstance(measurement_result, Mapping) else None, + "phase2": attempt["result"], + } diff --git a/apps/backend/recommendation_evaluation.py b/apps/backend/recommendation_evaluation.py index 1a643525..3eb56940 100644 --- a/apps/backend/recommendation_evaluation.py +++ b/apps/backend/recommendation_evaluation.py @@ -576,6 +576,27 @@ def infer_domain(track_context: str | None, category: str | None) -> str: return UNKNOWN_DOMAIN +def coerce_phase2_payload(raw: Mapping[str, Any]) -> Mapping[str, Any]: + """Accept either a bare ``Phase2Result`` or a ``phase2-export.v1`` envelope. + + The backend's ``GET /api/analysis-runs/{run_id}/export/phase2`` route + (``phase2_export.py``) wraps the interpretation result in a versioned + handoff envelope ``{schemaVersion: "phase2-export.v1", ..., phase2: {...}}`` + so one downloaded file feeds both this harness and the sibling + ``asa-ableton`` repo. Unwrap it here so ``--phase2`` (and a fixture-dir + ``phase2.json``) can be that file directly; a bare result passes through + unchanged. + """ + schema_version = raw.get("schemaVersion") + if ( + isinstance(schema_version, str) + and schema_version.startswith("phase2-export.") + and isinstance(raw.get("phase2"), Mapping) + ): + return raw["phase2"] + return raw + + def normalize_phase2(phase2: Mapping[str, Any]) -> list[NormalizedRec]: """Adapter: ``Phase2Result`` JSON -> normalized recs. diff --git a/apps/backend/scripts/evaluate_recommendations.py b/apps/backend/scripts/evaluate_recommendations.py index a535c868..de09bd24 100644 --- a/apps/backend/scripts/evaluate_recommendations.py +++ b/apps/backend/scripts/evaluate_recommendations.py @@ -15,8 +15,11 @@ * ``baseline`` — trivial no-op source (empty rec set). Runs now, no deps. The floor any real source must clear. * ``gemini`` — score a stored ``Phase2Result`` JSON (``--phase2`` or a - sibling ``phase2.json`` in the fixture dir). Producing it - live needs GEMINI_API_KEY + rendered audio (needs-fixture). + sibling ``phase2.json`` in the fixture dir). Either a bare + result or a ``phase2-export.v1`` envelope downloaded from + ``GET /api/analysis-runs/{run_id}/export/phase2`` works. + Producing it live needs GEMINI_API_KEY + rendered audio + (needs-fixture). * ``deterministic`` — score the ``abletonDevices.ts`` path. Wiring the node bridge that emits normalized recs from a real Phase 1 fingerprint is a documented follow-on (NEEDS.md); until @@ -86,7 +89,9 @@ def _resolve_recs( candidate = sibling if sibling.exists() else None if candidate is None or not candidate.exists(): return None, "no Phase2Result JSON (pass --phase2 or drop phase2.json in the fixture dir)" - phase2 = json.loads(candidate.read_text(encoding="utf-8")) + phase2 = rev.coerce_phase2_payload( + json.loads(candidate.read_text(encoding="utf-8")) + ) return rev.normalize_phase2(phase2), f"Phase2Result from {candidate.name}" if source == "deterministic": diff --git a/apps/backend/server.py b/apps/backend/server.py index 7c224621..ba6c7683 100644 --- a/apps/backend/server.py +++ b/apps/backend/server.py @@ -49,6 +49,7 @@ ) from utils.cleanup import cleanup_artifacts import csv_export +import phase2_export import transcription_pianoroll import upload_limits import url_ingest @@ -3132,6 +3133,66 @@ async def export_run_field_as_csv( ) +@app.get("/api/analysis-runs/{run_id}/export/phase2") +async def export_run_phase2( + run_id: str, + x_asa_user_id: str | None = Header(None), + x_asa_user_email: str | None = Header(None), +) -> JSONResponse: + """Export the run's Phase 2 handoff envelope (``phase2-export.v1``). + + A single self-contained JSON file for downstream consumers — the + sibling ``asa-ableton`` ``.als`` generator and the recommendation-proof + harness (``scripts/evaluate_recommendations.py --phase2``). Carries the + stored ``producer_summary`` interpretation result verbatim (including + the frozen ``recommendations.v1`` envelope), the authoritative Phase 1 + measurement payload its citations resolve against, the full + warn-and-keep ``validationWarnings`` trail, and the stored provenance. + See :mod:`phase2_export` and ``docs/ASA_ABLETON_BOUNDARY.md``. + + Returns: + - 200 ``application/json`` with an attachment Content-Disposition. + - 404 ``RUN_NOT_FOUND`` if the run does not exist or is not owned + by the requesting user. + - 404 ``PHASE2_EXPORT_NOT_AVAILABLE`` if the run has no completed + ``producer_summary`` interpretation result to hand off. + """ + user_context = _resolve_route_user_context(x_asa_user_id, x_asa_user_email) + if isinstance(user_context, JSONResponse): + return user_context + runtime = get_analysis_runtime() + try: + snapshot = runtime.get_run(run_id, owner_user_id=user_context.user_id) + except (KeyError, PermissionError): + return _run_not_found_response(run_id) + + envelope = phase2_export.build_phase2_export(snapshot) + if envelope is None: + return JSONResponse( + status_code=404, + content={ + "error": { + "code": "PHASE2_EXPORT_NOT_AVAILABLE", + "message": ( + f"Run '{run_id}' has no completed producer_summary " + f"interpretation result to export. Interpretation " + f"may be off, still running, failed, or skipped." + ), + "retryable": False, + } + }, + ) + + return JSONResponse( + content=envelope, + headers={ + "Content-Disposition": ( + f'attachment; filename="{run_id}_phase2_export.json"' + ), + }, + ) + + @app.get("/api/analysis-runs/{run_id}/transcription/pianoroll") async def get_transcription_pianoroll( run_id: str, diff --git a/apps/backend/tests/test_phase2_export.py b/apps/backend/tests/test_phase2_export.py new file mode 100644 index 00000000..404272c0 --- /dev/null +++ b/apps/backend/tests/test_phase2_export.py @@ -0,0 +1,201 @@ +"""Tests for phase2_export.py — the phase2-export.v1 handoff envelope. + +Pure-builder tests only; the HTTP shell (status codes, ownership, headers) +is covered by Phase2ExportRouteTests in tests/test_server.py. +""" + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +import phase2_export # noqa: E402 + + +def _snapshot( + *, + interpretation_status: str = "completed", + result: dict | None = None, + diagnostics: dict | None = None, + provenance: dict | None = None, + profile_id: str = "producer_summary", + measurement_result: dict | None = None, +) -> dict: + """A minimal run snapshot shaped like AnalysisRuntime.get_run output.""" + return { + "runId": "run-123", + "stages": { + "measurement": { + "status": "completed", + "result": measurement_result, + }, + "interpretation": { + "status": interpretation_status, + "result": result, + "profiles": { + profile_id: { + "attemptId": "attempt-1", + "status": interpretation_status, + "result": result, + "provenance": provenance, + "diagnostics": diagnostics, + "error": None, + } + }, + }, + }, + } + + +PHASE2_RESULT = { + "trackCharacter": "Driving techno", + "abletonRecommendations": [ + { + "device": "Glue Compressor", + "parameter": "Threshold", + "value": "-12 dB", + "phase1Fields": ["lufsIntegrated"], + } + ], + "recommendations": { + "version": "recommendations.v1", + "recommendations": [ + { + "device": "Glue Compressor", + "parameter": "Threshold", + "value": -12.0, + "unit": "dB", + "range": [-15.0, -9.0], + "cited_measurements": ["lufsIntegrated"], + } + ], + }, +} + +MEASUREMENT_RESULT = {"bpm": 130.0, "lufsIntegrated": -9.2} + +PROVENANCE = { + "schemaVersion": "interpretation.v2", + "profileId": "producer_summary", + "modelName": "gemini-2.5-flash", + "groundedMeasurementRunId": "run-123", +} + +DIAGNOSTICS = { + "requestId": "req-1", + "validationWarnings": [ + { + "code": "RECOMMENDATION_UNVERIFIED", + "path": "abletonRecommendations[0]", + "message": "parameter_unknown", + } + ], +} + + +class BuildPhase2ExportTests(unittest.TestCase): + def test_complete_run_exports_full_envelope(self) -> None: + envelope = phase2_export.build_phase2_export( + _snapshot( + result=PHASE2_RESULT, + diagnostics=DIAGNOSTICS, + provenance=PROVENANCE, + measurement_result=MEASUREMENT_RESULT, + ), + exported_at="2026-06-10T00:00:00+00:00", + ) + self.assertIsNotNone(envelope) + self.assertEqual(envelope["schemaVersion"], "phase2-export.v1") + self.assertEqual(envelope["runId"], "run-123") + self.assertEqual(envelope["exportedAt"], "2026-06-10T00:00:00+00:00") + # phase2 is the stored interpretation result verbatim — including the + # frozen recommendations.v1 envelope the validator tail attached. + self.assertEqual(envelope["phase2"], PHASE2_RESULT) + self.assertEqual( + envelope["phase2"]["recommendations"]["version"], "recommendations.v1" + ) + # phase1 is the authoritative measurement payload, so a consumer can + # resolve cited phase1Fields paths without a second request. + self.assertEqual(envelope["phase1"], MEASUREMENT_RESULT) + self.assertEqual(envelope["provenance"], PROVENANCE) + self.assertEqual( + envelope["validationWarnings"], + DIAGNOSTICS["validationWarnings"], + ) + + def test_envelope_key_set_is_frozen(self) -> None: + """The v1 key set is a published contract — additions need a version bump.""" + envelope = phase2_export.build_phase2_export( + _snapshot(result=PHASE2_RESULT, measurement_result=MEASUREMENT_RESULT) + ) + self.assertEqual( + set(envelope.keys()), + { + "schemaVersion", + "runId", + "exportedAt", + "provenance", + "validationWarnings", + "phase1", + "phase2", + }, + ) + + def test_exported_at_defaults_to_now(self) -> None: + envelope = phase2_export.build_phase2_export( + _snapshot(result=PHASE2_RESULT) + ) + self.assertIsInstance(envelope["exportedAt"], str) + self.assertIn("T", envelope["exportedAt"]) + + def test_missing_diagnostics_yields_empty_warning_list(self) -> None: + envelope = phase2_export.build_phase2_export( + _snapshot(result=PHASE2_RESULT, diagnostics=None) + ) + self.assertEqual(envelope["validationWarnings"], []) + + def test_non_dict_warning_entries_are_dropped(self) -> None: + envelope = phase2_export.build_phase2_export( + _snapshot( + result=PHASE2_RESULT, + diagnostics={"validationWarnings": ["stray-string", {"code": "X"}]}, + ) + ) + self.assertEqual(envelope["validationWarnings"], [{"code": "X"}]) + + def test_no_interpretation_stage_returns_none(self) -> None: + snapshot = {"runId": "run-123", "stages": {"measurement": {"result": {}}}} + self.assertIsNone(phase2_export.build_phase2_export(snapshot)) + + def test_incomplete_interpretation_returns_none(self) -> None: + self.assertIsNone( + phase2_export.build_phase2_export( + _snapshot(interpretation_status="running", result=None) + ) + ) + + def test_completed_with_null_result_returns_none(self) -> None: + """The skip path completes an attempt with a null result — not exportable.""" + self.assertIsNone( + phase2_export.build_phase2_export(_snapshot(result=None)) + ) + + def test_stem_summary_only_run_returns_none(self) -> None: + """Only producer_summary carries device cards; stem_summary is not a handoff.""" + self.assertIsNone( + phase2_export.build_phase2_export( + _snapshot(result={"stems": []}, profile_id="stem_summary") + ) + ) + + def test_missing_measurement_result_degrades_to_null_phase1(self) -> None: + envelope = phase2_export.build_phase2_export( + _snapshot(result=PHASE2_RESULT, measurement_result=None) + ) + self.assertIsNotNone(envelope) + self.assertIsNone(envelope["phase1"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/backend/tests/test_recommendation_evaluation.py b/apps/backend/tests/test_recommendation_evaluation.py index 90c39c7c..d1c3fcc3 100644 --- a/apps/backend/tests/test_recommendation_evaluation.py +++ b/apps/backend/tests/test_recommendation_evaluation.py @@ -363,6 +363,25 @@ def test_normalize_phase2(self): self.assertEqual(recs[0].citations, ("kickDetail.fundamentalHz",)) self.assertEqual(recs[1].domain, "master") # mix chain defaults to master + def test_coerce_phase2_payload_unwraps_export_envelope(self): + """A phase2-export.v1 file (GET .../export/phase2) feeds --phase2 directly.""" + bare = {"abletonRecommendations": [{"device": "Operator"}]} + envelope = { + "schemaVersion": "phase2-export.v1", + "runId": "run-123", + "phase1": {"bpm": 130.0}, + "phase2": bare, + } + self.assertEqual(rev.coerce_phase2_payload(envelope), bare) + + def test_coerce_phase2_payload_passes_bare_result_through(self): + bare = {"abletonRecommendations": [], "schemaVersion": None} + self.assertEqual(rev.coerce_phase2_payload(bare), bare) + # A result that happens to carry an unrelated schemaVersion string is + # NOT unwrapped — only the phase2-export.* envelope is. + other = {"schemaVersion": "interpretation.v2", "phase2": {"x": 1}} + self.assertEqual(rev.coerce_phase2_payload(other), other) + class AuthoredFixtureCatalogTests(unittest.TestCase): """Regression: every committed example spec is catalog-valid.""" diff --git a/apps/backend/tests/test_server.py b/apps/backend/tests/test_server.py index 1e3c1a75..94512617 100644 --- a/apps/backend/tests/test_server.py +++ b/apps/backend/tests/test_server.py @@ -5241,6 +5241,122 @@ def test_unknown_run_returns_404(self) -> None: self.assertEqual(payload["error"]["code"], "RUN_NOT_FOUND") +class Phase2ExportRouteTests(unittest.TestCase): + """Route tests for GET /api/analysis-runs/{run_id}/export/phase2. + + The envelope projection logic is covered by tests/test_phase2_export.py. + These tests verify only the HTTP shell: status codes, error envelopes, + headers, and that the route pulls the stored interpretation attempt out + of the run snapshot. + """ + + PHASE2_RESULT = { + "trackCharacter": "Driving techno", + "abletonRecommendations": [ + { + "device": "Glue Compressor", + "parameter": "Threshold", + "value": "-12 dB", + "phase1Fields": ["lufsIntegrated"], + } + ], + } + + def _decode_json_response(self, response) -> dict: + return json.loads(response.body.decode("utf-8")) + + def _make_run(self, runtime, *, with_interpretation: bool) -> str: + created = runtime.create_run( + filename="track.mp3", + content=b"fake-audio", + mime_type="audio/mpeg", + pitch_note_mode="off", + pitch_note_backend="auto", + interpretation_mode="sync" if with_interpretation else "off", + interpretation_profile="producer_summary", + interpretation_model=None, + ) + run_id = created["runId"] + runtime.complete_measurement( + run_id, + payload={"bpm": 130.0, "lufsIntegrated": -9.2}, + provenance={"schemaVersion": "measurement.v1", "engineVersion": "analyze.py"}, + diagnostics={"backendDurationMs": 1000}, + ) + if with_interpretation: + attempt_id = runtime.create_interpretation_attempt( + run_id, + profile_id="producer_summary", + model_name="gemini-2.5-flash", + ) + runtime.complete_interpretation_attempt( + attempt_id, + result=self.PHASE2_RESULT, + provenance={ + "schemaVersion": "interpretation.v2", + "profileId": "producer_summary", + "modelName": "gemini-2.5-flash", + }, + diagnostics={ + "validationWarnings": [ + {"code": "RECOMMENDATION_UNVERIFIED", "path": "x"} + ] + }, + ) + return run_id + + def test_returns_envelope_with_attachment_disposition(self) -> None: + from analysis_runtime import AnalysisRuntime + + with tempfile.TemporaryDirectory(prefix="asa_phase2_export_") as temp_dir: + runtime = AnalysisRuntime(Path(temp_dir) / "runtime") + run_id = self._make_run(runtime, with_interpretation=True) + + with patch.object(server, "get_analysis_runtime", return_value=runtime): + response = asyncio.run(server.export_run_phase2(run_id)) + + self.assertEqual(response.status_code, 200) + payload = self._decode_json_response(response) + self.assertEqual(payload["schemaVersion"], "phase2-export.v1") + self.assertEqual(payload["runId"], run_id) + self.assertEqual(payload["phase2"], self.PHASE2_RESULT) + self.assertEqual(payload["phase1"]["bpm"], 130.0) + self.assertEqual( + payload["validationWarnings"], + [{"code": "RECOMMENDATION_UNVERIFIED", "path": "x"}], + ) + self.assertEqual(payload["provenance"]["profileId"], "producer_summary") + disposition = response.headers.get("content-disposition", "") + self.assertIn(f"{run_id}_phase2_export.json", disposition) + + def test_run_without_interpretation_returns_404(self) -> None: + from analysis_runtime import AnalysisRuntime + + with tempfile.TemporaryDirectory(prefix="asa_phase2_export_") as temp_dir: + runtime = AnalysisRuntime(Path(temp_dir) / "runtime") + run_id = self._make_run(runtime, with_interpretation=False) + + with patch.object(server, "get_analysis_runtime", return_value=runtime): + response = asyncio.run(server.export_run_phase2(run_id)) + + self.assertEqual(response.status_code, 404) + payload = self._decode_json_response(response) + self.assertEqual(payload["error"]["code"], "PHASE2_EXPORT_NOT_AVAILABLE") + self.assertFalse(payload["error"]["retryable"]) + + def test_unknown_run_returns_404(self) -> None: + from analysis_runtime import AnalysisRuntime + + with tempfile.TemporaryDirectory(prefix="asa_phase2_export_") as temp_dir: + runtime = AnalysisRuntime(Path(temp_dir) / "runtime") + with patch.object(server, "get_analysis_runtime", return_value=runtime): + response = asyncio.run(server.export_run_phase2("does-not-exist")) + + self.assertEqual(response.status_code, 404) + payload = self._decode_json_response(response) + self.assertEqual(payload["error"]["code"], "RUN_NOT_FOUND") + + class TranscriptionPianorollRouteTests(unittest.TestCase): """Route tests for GET /api/analysis-runs/{run_id}/transcription/pianoroll. diff --git a/docs/ASA_ABLETON_BOUNDARY.md b/docs/ASA_ABLETON_BOUNDARY.md new file mode 100644 index 00000000..5de96d42 --- /dev/null +++ b/docs/ASA_ABLETON_BOUNDARY.md @@ -0,0 +1,113 @@ +# The ASA ↔ asa-ableton Boundary + +**Status:** living contract doc. Backed by `apps/backend/phase2_export.py`, +`GET /api/analysis-runs/{run_id}/export/phase2`, and the envelope-freeze test in +`apps/backend/tests/test_phase2_export.py`. + +## The two repos + +- **ASA** (this repo, `slittycode/ableton-sonic-analyzer`) — measures a track + deterministically (Phase 1) and produces AI device recommendations grounded in + those measurements (Phase 2). Its product output, for downstream tooling, is a + `producer_summary` interpretation result: device cards across + `abletonRecommendations`, `mixAndMasterChain`, and `secretSauce.workflowSteps`, + plus the frozen `recommendations.v1` projection (ADR 0003) attached as a + `recommendations` field. +- **asa-ableton** (sibling repo, `slittycode/asa-ableton`) — stdlib-only Python + that turns ASA's Phase 2 recommendations into an openable Live 12 `.als` + starter set. It is a *consumer*: it never talks to ASA's server at runtime and + shares no code with ASA. The boundary is a file. + +The two repos are deliberately **file-coupled, not code-coupled**. asa-ableton's +fidelity gates (Gate α in its `docs/GATE_ALPHA.md`) run on a saved ASA +interpretation JSON committed as a fixture on its side. The same file shape also +feeds ASA's own recommendation-proof harness +(`apps/backend/scripts/evaluate_recommendations.py --phase2`, GOAL.md). + +## The handoff artifact: `phase2-export.v1` + +One HTTP call produces the complete, self-describing handoff file: + +```bash +curl http://127.0.0.1:8100/api/analysis-runs//export/phase2 \ + -o .phase2-export.json +``` + +(Local profile needs no auth headers; hosted mode resolves ownership from the +usual `X-ASA-User-Id` header.) + +Envelope shape (key set frozen by `test_envelope_key_set_is_frozen` — additions +require a version bump): + +```json +{ + "schemaVersion": "phase2-export.v1", + "runId": "…", + "exportedAt": "ISO-8601", + "provenance": { "schemaVersion": "interpretation.v2", "profileId": "producer_summary", "modelName": "…", "promptVersion": "…", "groundedMeasurementRunId": "…", "…": "…" }, + "validationWarnings": [ { "code": "RECOMMENDATION_UNVERIFIED", "path": "…", "message": "…" } ], + "phase1": { "…full authoritative Phase 1 measurement payload…" }, + "phase2": { "…producer_summary interpretation result, verbatim…" } +} +``` + +Field semantics: + +1. **`phase2`** — the stored interpretation result exactly as ASA's validator + tail left it, including the additive `recommendations` field (the frozen + `recommendations.v1` contract). Nothing is rewritten or filtered at export + time. +2. **`phase1`** — the authoritative measurement payload the interpretation was + grounded on. Included so a consumer can independently resolve every cited + `phase1Fields` path (PURPOSE.md invariant #2) without a second request. + Phase 1 values always win over anything Phase 2 says (invariant #1). +3. **`validationWarnings`** — the full warn-and-keep trail (citation-existence, + semantic, and Live 12 catalogue-gate warnings). ASA never drops or rewrites + a flagged card; consumers decide what to do with flagged entries. +4. **`provenance`** — interpretation schema version, profile, model, prompt + version, and grounding IDs, verbatim from the run. + +Returns 404 `PHASE2_EXPORT_NOT_AVAILABLE` until a `producer_summary` +interpretation has completed with a result. Only `producer_summary` is +exportable — `stem_summary` carries no device cards. + +## Guidance for device-applying consumers (asa-ableton) + +- **Prefer `phase2.recommendations.recommendations`** (the `recommendations.v1` + entries) when you need normalized `{device, parameter, value, unit, range, + cited_measurements[]}` records: values are parsed to numbers where possible + and every entry is citation-gated. Fall back to the three raw card arrays for + prose context (`reason`, `trackContext`, ordering). +- **Dedupe before counting.** The raw arrays — and therefore the flat + `recommendations.v1` projection — can legitimately repeat a + `(device, parameter, value)` tuple across containers (a mix-chain card and a + workflow step may name the same move). asa-ableton's Gate α already observed + this inflating its skip-rate accounting (~60.7% raw vs ~50% deduped). + Deduplication is intentionally a consumer concern: ASA's contract is + warn-and-keep, never drop. +- **Treat flagged cards as flagged, not invalid.** A + `RECOMMENDATION_UNVERIFIED` warning means the source-extracted catalogue + could not confirm a device/parameter spelling — the card may still be a + correct UI-vocabulary spelling (see `UI_PARAMETER_ALIASES` in + `apps/backend/live12_catalogue.py`). + +## Relationship to other export surfaces + +- **UI "Export JSON"** (`track-analysis.json` from `AnalysisResults.tsx`) is a + looser, browser-side superset for humans: `{phase1, phase2, exportedAt}`, + without `provenance` or `validationWarnings`, and without a schema version. + The backend route is the canonical machine handoff; converging the UI export + onto `phase2-export.v1` is a known follow-up. +- **`evaluate_recommendations.py --phase2`** accepts either a bare + `Phase2Result` or a `phase2-export.v1` file + (`recommendation_evaluation.coerce_phase2_payload`), so the downloaded export + drops straight into a fixture dir as `phase2.json` for the GOAL.md campaign. + +## Stability promise + +`phase2-export.v1` follows the same policy as `recommendations.v1` (ADR 0003): +the key set above is frozen; new top-level fields require bumping the +`schemaVersion`. The *interior* of `phase1` and `phase2` is governed by their +own contracts (`phase1.v2` per ADR 0002, `interpretation.v2`/`recommendations.v1` +for Phase 2) and evolves with them — the envelope passes them through verbatim +and adds no transformation of its own. From fccf0990258776b489edc51a08cac5fb0cf43854 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 00:00:51 +0000 Subject: [PATCH 2/3] feat(fixtures): re-author techno/dnb ground-truth fixtures as hard techno 145 + UKG 2-step 132 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner confirmed genre fit for house/melodic-techno/acid but swapped the other two recommendation-corpus fixtures to genres they actually produce: - techno_rumble_130 -> hard_techno_rumble_145: distorted sine kick (Drum Buss on the kick chain, Boom 45 Hz), rumble reverb return + held sub, 145 BPM. Stays the pilot fixture (smallest spec, 9 device entries). - dnb_reese_174 -> ukg_2step_shuffle_132: 2-step shuffle at 132 BPM, G minor. Swing lives in the committed MIDI (57% 16ths), asserted via grooveDetail.hihatSwing / perDrumSwing.snare / bassDetail.grooveType. Retires the fixture whose proxy fingerprint misread BPM half-time (174->116). Both new fixtures ship spec-only (phase1Fingerprint: null, no fingerprint file) — the distrusted _synthetic proxy fingerprints are retired with the old slugs, and load_fixture tolerates the missing file (citation checks SKIP until the real Ableton render lands). truePeak intents now use dBTP per ADR 0002 (old manifests carried stale linear targets). New MIDI clips bake correct tempo metas and were byte-verified (the retired dnb melody encoded ~116 BPM, not 174). ukg also ships audio_drums.mid so the shuffle answer key is reproducible without hand-programmed groove. Docs updated: NEEDS.md (genre confirmation resolved, inventory + build queue), plans/owner-actions-recommendation-proof-plan.md (pilot + render order), RECOMMENDATION_VERDICT.md (corpus-composition note; old per-fixture proxy numbers not comparable). Verified: evaluate_recommendations.py --fixture --source baseline (catalog-valid, zero issues), --self-test PASS, 34/34 tests.test_recommendation_evaluation. https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy --- apps/backend/NEEDS.md | 35 +- apps/backend/RECOMMENDATION_VERDICT.md | 6 + .../dnb_reese_174/README.md | 26 - .../dnb_reese_174/audio_melody.mid | Bin 473 -> 0 bytes .../dnb_reese_174/manifest.json | 131 - .../dnb_reese_174/phase1_fingerprint.json | 4149 --------------- .../hard_techno_rumble_145/README.md | 39 + .../hard_techno_rumble_145/audio_melody.mid | Bin 0 -> 353 bytes .../manifest.json | 78 +- .../techno_rumble_130/README.md | 34 - .../techno_rumble_130/audio_melody.mid | Bin 457 -> 0 bytes .../techno_rumble_130/phase1_fingerprint.json | 4729 ----------------- .../ukg_2step_shuffle_132/README.md | 45 + .../ukg_2step_shuffle_132/audio_drums.mid | Bin 0 -> 1313 bytes .../ukg_2step_shuffle_132/audio_melody.mid | Bin 0 -> 865 bytes .../ukg_2step_shuffle_132/manifest.json | 142 + ...owner-actions-recommendation-proof-plan.md | 19 +- 17 files changed, 304 insertions(+), 9129 deletions(-) delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/README.md delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/audio_melody.mid delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/manifest.json delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/phase1_fingerprint.json create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/README.md create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/audio_melody.mid rename apps/backend/tests/fixtures/recommendation_tracks/{techno_rumble_130 => hard_techno_rumble_145}/manifest.json (51%) delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/README.md delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/audio_melody.mid delete mode 100644 apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/phase1_fingerprint.json create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/README.md create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_drums.mid create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_melody.mid create mode 100644 apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/manifest.json diff --git a/apps/backend/NEEDS.md b/apps/backend/NEEDS.md index 4ac36a15..de91b4e0 100644 --- a/apps/backend/NEEDS.md +++ b/apps/backend/NEEDS.md @@ -16,7 +16,7 @@ not Ableton renders), so their *data* is provisional pending real renders. | Sub-goal | Done? | Caveat | |---|---|---| -| 1 — ground-truth corpus | ✅ 5 catalog-valid fixtures, audio + fingerprint, ingest passes | audio is **synthetic-proxy**, not Ableton | +| 1 — ground-truth corpus | ✅ 5 catalog-valid fixtures, ingest passes | audio is **synthetic-proxy**, not Ableton; 2026-06-10 two fixtures re-authored to owner genres (`hard_techno_rumble_145`, `ukg_2step_shuffle_132`) and ship spec-only — their proxy audio/fingerprints retired with the old slugs | | 2 — the scorer | ✅ fully real (32 tests, self-test) | none | | 3 — verdict + improvement | ✅ verdict (`RECOMMENDATION_VERDICT.md`: Gemini 0.227 vs deterministic/baseline 0.000), intent-credit improvement 0.141→0.227, harness runnable | corpus numbers are on proxy audio; real-track cross-check is real | | 4 — UI proof | ✅ badge on `ui/` primitives, real bands (master MED, bass/melody LOW, rest NONE), `npm run verify` green | bands derived from proxy corpus | @@ -31,10 +31,14 @@ not Ableton renders), so their *data* is provisional pending real renders. 2. **Gemini spend already incurred** (6 live `gemini-2.5-flash` calls: 1 real track + 5 proxy fixtures) using `VITE_GEMINI_API_KEY`. Future re-runs cost the same. 3. **Proxy artifacts to distrust until re-rendered:** dnb BPM read half-time - (174→116); `acidDetail` fires on all proxies (synth is saw-heavy); - melodic_techno key mis-detected AND Gemini returned 0 structured cards there. -4. **Genre confirmation** — house/techno/melodic-techno/dnb/acid were chosen as - owner-plausible; confirm or swap. + (174→116) — moot since 2026-06-10, `dnb_reese_174` retired (re-authored as + `ukg_2step_shuffle_132`); `acidDetail` fires on all proxies (synth is + saw-heavy); melodic_techno key mis-detected AND Gemini returned 0 structured + cards there. +4. **Genre confirmation** — ✅ resolved 2026-06-10. Owner confirmed + house/melodic-techno/acid; the other two were re-authored to owner genres: + `techno_rumble_130` → `hard_techno_rumble_145` (hard/peak techno) and + `dnb_reese_174` → `ukg_2step_shuffle_132` (UK garage / 2-step). --- @@ -95,10 +99,13 @@ place on citation + full-surface coverage. Confirm on real renders; the syntheti - Catalog-validity ingest (`validate_fixture_spec`) — runs now, gates every spec against `prompts/live12_device_catalog.json`. - **Five catalog-valid spec fixtures**, all covering the seven domains, in - owner-plausible electronic genres — meeting GOAL.md sub-goal 1's ≥5 *spec* target + owner-confirmed electronic genres — meeting GOAL.md sub-goal 1's ≥5 *spec* target (the renders are the remaining half): `house_sidechain_pluck_124` (house), - `techno_rumble_130` (techno), `melodic_techno_arp_124` (melodic techno), - `dnb_reese_174` (drum & bass), `acid_303_128` (acid — exercises `acidDetail`). + `hard_techno_rumble_145` (hard techno — the pilot), + `melodic_techno_arp_124` (melodic techno), + `ukg_2step_shuffle_132` (UK garage / 2-step — swing asserted via + `grooveDetail.*` / `bassDetail.grooveType`), + `acid_303_128` (acid — exercises `acidDetail`). --- @@ -109,8 +116,9 @@ the recommendations themselves come from analyzing a real render. **This is the hard human dependency** — the agent writes the spec, the owner builds and renders. ### 1. Build + render the five authored fixtures -For each of `house_sidechain_pluck_124`, `techno_rumble_130`, -`melodic_techno_arp_124`, `dnb_reese_174`, `acid_303_128`: +For each of `house_sidechain_pluck_124`, `hard_techno_rumble_145`, +`melodic_techno_arp_124`, `ukg_2step_shuffle_132`, `acid_303_128` +(start with `hard_techno_rumble_145` — the pilot, smallest spec): - Build the `manifest.json → deviceSpec` **exactly** in Live 12. - Set the project to **48 kHz / 24-bit**. - Render the loop to `audio.flac` in the fixture dir (no export normalization). @@ -124,10 +132,9 @@ For each of `house_sidechain_pluck_124`, `techno_rumble_130`, and confirm the no-fingerprint note is gone. ### 2. Genre fit + held-out fixture -Five specs now exist (house, techno, melodic techno, drum & bass, acid). If any of -these are *not* genres you actually produce, say so and the agent will re-author -that spec in a genre you make — the spec-then-dial cost is on the agent, the render -on you. +✅ Genre fit resolved 2026-06-10: owner confirmed house/melodic-techno/acid and the +agent re-authored the other two to owner genres (hard techno 145, UK garage 2-step +132) — the spec-then-dial cost stayed on the agent, the render on you. > Equivalence/overfitting note (GOAL.md sub-goal 3.3): hold out at least one > fixture from any tuning loop so improvements reflect quality, not memorization. diff --git a/apps/backend/RECOMMENDATION_VERDICT.md b/apps/backend/RECOMMENDATION_VERDICT.md index 20d758c1..2587bc9d 100644 --- a/apps/backend/RECOMMENDATION_VERDICT.md +++ b/apps/backend/RECOMMENDATION_VERDICT.md @@ -11,6 +11,12 @@ specifically on the chain-of-custody invariant.** Numbers below. Research note. > renders for an authoritative known-settings verdict.** See "Where real input is > needed" at the bottom and `NEEDS.md`. +> **Corpus composition changed 2026-06-10, after this verdict.** Two fixtures were +> re-authored to owner genres: `techno_rumble_130` → `hard_techno_rumble_145` +> (hard/peak techno) and `dnb_reese_174` → `ukg_2step_shuffle_132` (UK garage / +> 2-step). The per-fixture proxy numbers below for "techno" and "dnb" refer to the +> retired fixtures and are not comparable to future real-render scores. + ## Method Per fixture: proxy render → `analyze.py` (real Phase 1) → three recommendation sources scored by `recommendation_evaluation.score_recommendations`: diff --git a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/README.md b/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/README.md deleted file mode 100644 index bed30883..00000000 --- a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# dnb_reese_174 - -**Genre:** drum & bass · **Tempo:** 174 BPM · **Key:** F minor · **Render:** 48 kHz / 24-bit, 16 s loop - -## What this fixture isolates - -Drum & bass at 174 BPM built on the two genre pillars: - -1. **The Reese bass** — detuned-unison saws in `Wavetable` (Unison 40 %) through a - lowpass, saturated for harmonics. Recovering it means recommending a - detuned/unison synth + drive, not a clean sub. -2. **Sampled breaks** — `Drum Rack` kick + amen-style breaks with hard bus - compression (fast attack, short release). - -Low end mono'd via `Utility`. Tests fast-tempo handling and the supersaw-like -detection on the Reese (`supersawDetail.isSupersaw`). Covers all seven domains. - -## Answer key - -`manifest.json` → `deviceSpec`. Build verbatim in Live 12 (checklist in -`../README.md`), render to `audio.flac`, store `phase1_fingerprint.json`. - -## Status -- [x] Catalog-valid spec authored -- [ ] **NEEDS-FIXTURE:** rendered `audio.flac` -- [ ] **NEEDS-FIXTURE:** stored `phase1_fingerprint.json` diff --git a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/audio_melody.mid b/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/audio_melody.mid deleted file mode 100644 index a1f6e7805f418a926d6ba36341a6f0c49128d128..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 473 zcmZvYK?=e!6hwaz6txPqh)S!iib9)$=TPWE=qejsxDecVf*c`t@*p0i&exQNB3*<( zd71gsqP*XTs7nIhwJ4ttBJrZ?-08h9iIFPSk_IMMbDfhgsfu(;W9R{N0X>Gk#T?mr zW4R@`k;Cnvdw3r}7tq6&?+W_R;S=yDCztO%=brsS%!&1x*<9v?$&|0NZ;o>04B- z`bx)-do=G-y#MXVy|_R7yFBLy$O&xD6kP4g|B3taS^G@v9^9+m+vVH+m^&hOfwS>z Odj0p4YL6VM4)g(mb#Mg$ diff --git a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/manifest.json b/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/manifest.json deleted file mode 100644 index 3d4ff115..00000000 --- a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/manifest.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "schemaVersion": "recommendation-fixture.v1", - "id": "dnb_reese_174", - "title": "Drum & bass — Reese bass, sampled breaks, hard compression", - "genre": "drum and bass", - "audioPath": "audio.flac", - "phase1FingerprintPath": "phase1_fingerprint.json", - "phase1Fingerprint": null, - "render": { - "sampleRateHz": 48000, - "bitDepth": 24, - "lengthSeconds": 16, - "bpm": 174, - "key": "F minor", - "notes": "8-bar loop at 174 BPM. Detuned-unison Reese bass (Wavetable), sampled kick + breaks (Drum Rack), a held stab. Render the full loop to one stereo file, no export normalization." - }, - "deviceSpec": { - "kick": [ - { - "device": "Drum Rack", - "family": "NATIVE", - "role": "Sampled kick", - "parameters": [ - { "name": "Pad Volume", "value": "-3 dB" }, - { "name": "Pad Pitch", "value": "0" } - ] - } - ], - "bass": [ - { - "device": "Wavetable", - "family": "NATIVE", - "role": "Reese bass (detuned-unison saws)", - "parameters": [ - { "name": "Oscillator 1 Wavetable", "value": "Basic Shapes" }, - { "name": "Oscillator 2 Wavetable", "value": "Basic Shapes" }, - { "name": "Unison Mode", "value": "Classic" }, - { "name": "Unison Amount", "value": "40%" }, - { "name": "Filter Type", "value": "Lowpass" }, - { "name": "Filter Frequency", "value": "800 Hz" }, - { "name": "Filter Resonance", "value": "20%" } - ] - } - ], - "melody": [ - { - "device": "Operator", - "family": "NATIVE", - "role": "Held stab", - "parameters": [ - { "name": "Oscillator A Waveform", "value": "Saw" }, - { "name": "Amp Envelope Decay", "value": "400 ms" } - ] - } - ], - "groove": [ - { - "device": "Drum Rack", - "family": "NATIVE", - "role": "Amen-style breaks", - "parameters": [ - { "name": "Pad Volume", "value": "-4 dB" }, - { "name": "Choke Group", "value": "1" } - ] - } - ], - "fx": [ - { - "device": "Compressor", - "family": "NATIVE", - "role": "Break bus compression", - "parameters": [ - { "name": "Threshold", "value": "-15 dB" }, - { "name": "Ratio", "value": "4:1" }, - { "name": "Attack", "value": "5 ms" }, - { "name": "Release", "value": "80 ms" } - ] - }, - { - "device": "Saturator", - "family": "NATIVE", - "role": "Reese drive / harmonics", - "parameters": [ - { "name": "Drive", "value": "10 dB" }, - { "name": "Type", "value": "Soft Sine" }, - { "name": "Dry/Wet", "value": "100%" } - ] - } - ], - "stereo": [ - { - "device": "Utility", - "family": "NATIVE", - "role": "Low-end mono / break width", - "parameters": [ - { "name": "Bass Mono", "value": "On" }, - { "name": "Stereo Width", "value": "115%" } - ] - } - ], - "master": [ - { - "device": "Glue Compressor", - "family": "NATIVE", - "role": "Bus glue", - "parameters": [ - { "name": "Threshold", "value": "-14 dB" }, - { "name": "Ratio", "value": "4:1" }, - { "name": "Attack", "value": "1 ms" }, - { "name": "Release", "value": "Auto" }, - { "name": "Makeup", "value": "2 dB" } - ] - }, - { - "device": "Limiter", - "family": "NATIVE", - "role": "Master ceiling", - "parameters": [ - { "name": "Ceiling", "value": "-0.3 dB" } - ] - } - ] - }, - "measurableIntent": { - "bpm": { "target": 174, "tolerance": 1, "direction": "exact" }, - "key": { "target": "F minor" }, - "lufsIntegrated": { "target": -8, "tolerance": 2, "unit": "LUFS" }, - "truePeak": { "target": 1.0, "tolerance": 0.05 }, - "supersawDetail.isSupersaw": { "equals": true, "intent": "detuned-unison Reese should read as supersaw-like" } - } -} diff --git a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/phase1_fingerprint.json b/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/phase1_fingerprint.json deleted file mode 100644 index a150b9f9..00000000 --- a/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/phase1_fingerprint.json +++ /dev/null @@ -1,4149 +0,0 @@ -{ - "_synthetic": true, - "_note": "Phase 1 of a numpy PROXY render, NOT an Ableton render. Replace with a real Live render's fingerprint for an authoritative verdict.", - "bpm": 116.3, - "bpmConfidence": 3.06, - "bpmPercival": 86.9, - "bpmAgreement": false, - "bpmDoubletime": false, - "bpmSource": "rhythm_extractor", - "bpmRawOriginal": 116.3, - "key": "F Minor", - "keyConfidence": 0.59, - "keyProfile": "edma", - "tuningFrequency": 444.09, - "tuningCents": 16.0, - "timeSignature": "5/4", - "timeSignatureSource": "onset_autocorrelation", - "timeSignatureConfidence": 0.67, - "durationSeconds": 11.0, - "sampleRate": 44100, - "lufsIntegrated": -8.1, - "lufsRange": 0.1, - "lufsMomentaryMax": -7.3, - "lufsShortTermMax": -8.0, - "lufsCurve": { - "shortTerm": [ - { - "t": 0.0, - "lufs": -8.0 - }, - { - "t": 0.1, - "lufs": -8.2 - }, - { - "t": 0.2, - "lufs": -8.1 - }, - { - "t": 0.3, - "lufs": -8.1 - }, - { - "t": 0.4, - "lufs": -8.1 - }, - { - "t": 0.5, - "lufs": -8.1 - }, - { - "t": 0.6, - "lufs": -8.1 - }, - { - "t": 0.7, - "lufs": -8.0 - }, - { - "t": 0.8, - "lufs": -8.1 - }, - { - "t": 0.9, - "lufs": -8.1 - }, - { - "t": 1.0, - "lufs": -8.1 - }, - { - "t": 1.1, - "lufs": -8.2 - }, - { - "t": 1.2, - "lufs": -8.1 - }, - { - "t": 1.3, - "lufs": -8.1 - }, - { - "t": 1.4, - "lufs": -8.1 - }, - { - "t": 1.5, - "lufs": -8.1 - }, - { - "t": 1.6, - "lufs": -8.1 - }, - { - "t": 1.7, - "lufs": -8.0 - }, - { - "t": 1.8, - "lufs": -8.1 - }, - { - "t": 1.9, - "lufs": -8.1 - }, - { - "t": 2.0, - "lufs": -8.2 - }, - { - "t": 2.1, - "lufs": -8.1 - }, - { - "t": 2.2, - "lufs": -8.1 - }, - { - "t": 2.3, - "lufs": -8.2 - }, - { - "t": 2.4, - "lufs": -8.1 - }, - { - "t": 2.5, - "lufs": -8.1 - }, - { - "t": 2.6, - "lufs": -8.1 - }, - { - "t": 2.7, - "lufs": -8.1 - }, - { - "t": 2.8, - "lufs": -8.1 - }, - { - "t": 2.9, - "lufs": -8.1 - }, - { - "t": 3.0, - "lufs": -8.2 - }, - { - "t": 3.1, - "lufs": -8.1 - }, - { - "t": 3.2, - "lufs": -8.2 - }, - { - "t": 3.3, - "lufs": -8.1 - }, - { - "t": 3.4, - "lufs": -8.1 - }, - { - "t": 3.5, - "lufs": -8.1 - }, - { - "t": 3.6, - "lufs": -8.1 - }, - { - "t": 3.7, - "lufs": -8.2 - }, - { - "t": 3.8, - "lufs": -8.1 - }, - { - "t": 3.9, - "lufs": -8.1 - }, - { - "t": 4.0, - "lufs": -8.2 - }, - { - "t": 4.1, - "lufs": -8.1 - }, - { - "t": 4.2, - "lufs": -8.2 - }, - { - "t": 4.3, - "lufs": -8.1 - }, - { - "t": 4.4, - "lufs": -8.2 - }, - { - "t": 4.5, - "lufs": -8.1 - }, - { - "t": 4.6, - "lufs": -8.1 - }, - { - "t": 4.7, - "lufs": -8.2 - }, - { - "t": 4.8, - "lufs": -8.1 - }, - { - "t": 4.9, - "lufs": -8.1 - }, - { - "t": 5.0, - "lufs": -8.1 - }, - { - "t": 5.1, - "lufs": -8.2 - }, - { - "t": 5.2, - "lufs": -8.1 - }, - { - "t": 5.3, - "lufs": -8.2 - }, - { - "t": 5.4, - "lufs": -8.2 - }, - { - "t": 5.5, - "lufs": -8.1 - }, - { - "t": 5.6, - "lufs": -8.1 - }, - { - "t": 5.7, - "lufs": -8.1 - }, - { - "t": 5.8, - "lufs": -8.1 - }, - { - "t": 5.9, - "lufs": -8.1 - }, - { - "t": 6.0, - "lufs": -8.1 - }, - { - "t": 6.1, - "lufs": -8.2 - }, - { - "t": 6.2, - "lufs": -8.1 - }, - { - "t": 6.3, - "lufs": -8.2 - }, - { - "t": 6.4, - "lufs": -8.1 - }, - { - "t": 6.5, - "lufs": -8.1 - }, - { - "t": 6.6, - "lufs": -8.1 - }, - { - "t": 6.7, - "lufs": -8.1 - }, - { - "t": 6.8, - "lufs": -8.2 - }, - { - "t": 6.9, - "lufs": -8.1 - }, - { - "t": 7.0, - "lufs": -8.1 - }, - { - "t": 7.1, - "lufs": -8.2 - }, - { - "t": 7.2, - "lufs": -8.1 - }, - { - "t": 7.3, - "lufs": -8.2 - }, - { - "t": 7.4, - "lufs": -8.1 - }, - { - "t": 7.5, - "lufs": -8.2 - }, - { - "t": 7.6, - "lufs": -8.1 - }, - { - "t": 7.7, - "lufs": -8.1 - }, - { - "t": 7.8, - "lufs": -8.2 - }, - { - "t": 7.9, - "lufs": -8.1 - }, - { - "t": 8.0, - "lufs": -8.1 - }, - { - "t": 8.1, - "lufs": -8.2 - } - ], - "momentary": [ - { - "t": 0.0, - "lufs": -7.7 - }, - { - "t": 0.1, - "lufs": -8.6 - }, - { - "t": 0.2, - "lufs": -8.4 - }, - { - "t": 0.3, - "lufs": -8.6 - }, - { - "t": 0.4, - "lufs": -8.3 - }, - { - "t": 0.5, - "lufs": -7.8 - }, - { - "t": 0.6, - "lufs": -8.5 - }, - { - "t": 0.7, - "lufs": -7.5 - }, - { - "t": 0.8, - "lufs": -7.9 - }, - { - "t": 0.9, - "lufs": -7.8 - }, - { - "t": 1.0, - "lufs": -7.9 - }, - { - "t": 1.1, - "lufs": -8.5 - }, - { - "t": 1.2, - "lufs": -7.9 - }, - { - "t": 1.3, - "lufs": -8.7 - }, - { - "t": 1.4, - "lufs": -8.3 - }, - { - "t": 1.5, - "lufs": -8.4 - }, - { - "t": 1.6, - "lufs": -8.4 - }, - { - "t": 1.7, - "lufs": -7.9 - }, - { - "t": 1.8, - "lufs": -7.8 - }, - { - "t": 1.9, - "lufs": -7.3 - }, - { - "t": 2.0, - "lufs": -8.0 - }, - { - "t": 2.1, - "lufs": -8.0 - }, - { - "t": 2.2, - "lufs": -8.2 - }, - { - "t": 2.3, - "lufs": -8.6 - }, - { - "t": 2.4, - "lufs": -8.3 - }, - { - "t": 2.5, - "lufs": -8.4 - }, - { - "t": 2.6, - "lufs": -8.1 - }, - { - "t": 2.7, - "lufs": -8.5 - }, - { - "t": 2.8, - "lufs": -7.7 - }, - { - "t": 2.9, - "lufs": -7.4 - }, - { - "t": 3.0, - "lufs": -7.9 - }, - { - "t": 3.1, - "lufs": -7.8 - }, - { - "t": 3.2, - "lufs": -8.6 - }, - { - "t": 3.3, - "lufs": -8.4 - }, - { - "t": 3.4, - "lufs": -8.6 - }, - { - "t": 3.5, - "lufs": -8.3 - }, - { - "t": 3.6, - "lufs": -7.9 - }, - { - "t": 3.7, - "lufs": -8.5 - }, - { - "t": 3.8, - "lufs": -7.6 - }, - { - "t": 3.9, - "lufs": -7.9 - }, - { - "t": 4.0, - "lufs": -7.8 - }, - { - "t": 4.1, - "lufs": -7.8 - }, - { - "t": 4.2, - "lufs": -8.5 - }, - { - "t": 4.3, - "lufs": -7.9 - }, - { - "t": 4.4, - "lufs": -8.7 - }, - { - "t": 4.5, - "lufs": -8.3 - }, - { - "t": 4.6, - "lufs": -8.6 - }, - { - "t": 4.7, - "lufs": -8.4 - }, - { - "t": 4.8, - "lufs": -8.1 - }, - { - "t": 4.9, - "lufs": -7.8 - }, - { - "t": 5.0, - "lufs": -7.3 - }, - { - "t": 5.1, - "lufs": -8.0 - }, - { - "t": 5.2, - "lufs": -7.9 - }, - { - "t": 5.3, - "lufs": -8.3 - }, - { - "t": 5.4, - "lufs": -8.5 - }, - { - "t": 5.5, - "lufs": -8.4 - }, - { - "t": 5.6, - "lufs": -8.5 - }, - { - "t": 5.7, - "lufs": -8.1 - }, - { - "t": 5.8, - "lufs": -8.6 - }, - { - "t": 5.9, - "lufs": -7.8 - }, - { - "t": 6.0, - "lufs": -7.5 - }, - { - "t": 6.1, - "lufs": -7.9 - }, - { - "t": 6.2, - "lufs": -7.8 - }, - { - "t": 6.3, - "lufs": -8.5 - }, - { - "t": 6.4, - "lufs": -8.3 - }, - { - "t": 6.5, - "lufs": -8.7 - }, - { - "t": 6.6, - "lufs": -8.3 - }, - { - "t": 6.7, - "lufs": -8.0 - }, - { - "t": 6.8, - "lufs": -8.5 - }, - { - "t": 6.9, - "lufs": -7.6 - }, - { - "t": 7.0, - "lufs": -7.9 - }, - { - "t": 7.1, - "lufs": -7.7 - }, - { - "t": 7.2, - "lufs": -7.8 - }, - { - "t": 7.3, - "lufs": -8.5 - }, - { - "t": 7.4, - "lufs": -7.9 - }, - { - "t": 7.5, - "lufs": -8.6 - }, - { - "t": 7.6, - "lufs": -8.4 - }, - { - "t": 7.7, - "lufs": -8.6 - }, - { - "t": 7.8, - "lufs": -8.4 - }, - { - "t": 7.9, - "lufs": -8.2 - }, - { - "t": 8.0, - "lufs": -7.8 - }, - { - "t": 8.1, - "lufs": -7.3 - }, - { - "t": 8.2, - "lufs": -8.0 - }, - { - "t": 8.3, - "lufs": -7.9 - }, - { - "t": 8.4, - "lufs": -8.3 - }, - { - "t": 8.5, - "lufs": -8.5 - }, - { - "t": 8.6, - "lufs": -8.3 - }, - { - "t": 8.7, - "lufs": -8.5 - }, - { - "t": 8.8, - "lufs": -8.1 - }, - { - "t": 8.9, - "lufs": -8.6 - }, - { - "t": 9.0, - "lufs": -7.8 - }, - { - "t": 9.1, - "lufs": -7.6 - }, - { - "t": 9.2, - "lufs": -7.9 - }, - { - "t": 9.3, - "lufs": -7.8 - }, - { - "t": 9.4, - "lufs": -8.5 - }, - { - "t": 9.5, - "lufs": -8.2 - }, - { - "t": 9.6, - "lufs": -8.7 - }, - { - "t": 9.7, - "lufs": -8.3 - }, - { - "t": 9.8, - "lufs": -8.0 - }, - { - "t": 9.9, - "lufs": -8.5 - }, - { - "t": 10.0, - "lufs": -7.6 - }, - { - "t": 10.1, - "lufs": -7.9 - }, - { - "t": 10.2, - "lufs": -7.7 - }, - { - "t": 10.3, - "lufs": -7.8 - }, - { - "t": 10.4, - "lufs": -8.4 - }, - { - "t": 10.5, - "lufs": -7.8 - }, - { - "t": 10.6, - "lufs": -8.6 - }, - { - "t": 10.7, - "lufs": -9.4 - } - ] - }, - "truePeak": 1.1, - "plr": 9.2, - "crestFactor": 9.0, - "dynamicSpread": 27.14, - "dynamicCharacter": { - "dynamicComplexity": 1.1603, - "loudnessDb": -16.2965, - "loudnessVariation": -16.2965, - "spectralFlatness": 0.1378, - "logAttackTime": -5.0, - "attackTimeStdDev": 0.0 - }, - "textureCharacter": { - "textureScore": 0.3756, - "lowBandFlatness": 0.3786, - "midBandFlatness": 0.3994, - "highBandFlatness": 0.3686, - "inharmonicity": 0.0875 - }, - "stereoDetail": { - "stereoWidth": 0.0, - "stereoCorrelation": 1.0, - "subBassCorrelation": 1.0, - "subBassMono": true, - "correlationCurve": [ - { - "t": 0.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 1.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 2.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 3.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 4.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 5.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 6.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 7.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 8.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 9.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 10.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 11.5, - "full": 1.0, - "sub": 1.0 - } - ], - "bandCorrelations": { - "subBass": 1.0, - "lowBass": 1.0, - "lowMids": 1.0, - "mids": 1.0, - "upperMids": 1.0, - "highs": 1.0, - "brilliance": 1.0 - } - }, - "monoCompatible": true, - "spectralBalance": { - "subBass": -6.7, - "lowBass": -7.2, - "lowMids": -23.5, - "mids": -22.4, - "upperMids": -31.3, - "highs": -27.3, - "brilliance": -19.6 - }, - "spectralBalanceTimeSeries": [ - { - "t": 0.02, - "subBass": -3.7, - "lowBass": -2.9, - "lowMids": -21.1, - "mids": -24.5, - "upperMids": -34.7, - "highs": -49.8, - "brilliance": -77.8 - }, - { - "t": 0.09, - "subBass": -4.8, - "lowBass": -9.1, - "lowMids": -21.3, - "mids": -24.5, - "upperMids": -33.7, - "highs": -48.7, - "brilliance": -77.8 - }, - { - "t": 0.16, - "subBass": -5.8, - "lowBass": -8.5, - "lowMids": -26.1, - "mids": -22.0, - "upperMids": -28.7, - "highs": -22.3, - "brilliance": -14.3 - }, - { - "t": 0.23, - "subBass": -7.5, - "lowBass": -7.2, - "lowMids": -37.6, - "mids": -21.3, - "upperMids": -30.8, - "highs": -25.9, - "brilliance": -18.3 - }, - { - "t": 0.3, - "subBass": -9.7, - "lowBass": -8.4, - "lowMids": -43.6, - "mids": -20.1, - "upperMids": -32.8, - "highs": -43.5, - "brilliance": -77.8 - }, - { - "t": 0.37, - "subBass": -6.7, - "lowBass": -5.5, - "lowMids": -20.2, - "mids": -23.9, - "upperMids": -33.5, - "highs": -49.9, - "brilliance": -53.0 - }, - { - "t": 0.44, - "subBass": -5.4, - "lowBass": -7.9, - "lowMids": -21.0, - "mids": -24.8, - "upperMids": -34.7, - "highs": -69.4, - "brilliance": -78.5 - }, - { - "t": 0.51, - "subBass": -6.9, - "lowBass": -8.5, - "lowMids": -26.8, - "mids": -21.4, - "upperMids": -28.9, - "highs": -22.3, - "brilliance": -14.2 - }, - { - "t": 0.58, - "subBass": -7.2, - "lowBass": -8.1, - "lowMids": -39.3, - "mids": -20.8, - "upperMids": -30.8, - "highs": -26.5, - "brilliance": -19.2 - }, - { - "t": 0.65, - "subBass": -8.3, - "lowBass": -9.0, - "lowMids": -44.1, - "mids": -20.5, - "upperMids": -33.2, - "highs": -43.9, - "brilliance": -72.1 - }, - { - "t": 0.72, - "subBass": -6.0, - "lowBass": -5.0, - "lowMids": -20.3, - "mids": -24.0, - "upperMids": -34.3, - "highs": -52.0, - "brilliance": -55.3 - }, - { - "t": 0.79, - "subBass": -6.2, - "lowBass": -9.1, - "lowMids": -20.9, - "mids": -25.1, - "upperMids": -33.1, - "highs": -66.5, - "brilliance": -74.5 - }, - { - "t": 0.86, - "subBass": -7.7, - "lowBass": -8.1, - "lowMids": -27.7, - "mids": -21.1, - "upperMids": -28.0, - "highs": -21.5, - "brilliance": -13.7 - }, - { - "t": 0.93, - "subBass": -9.0, - "lowBass": -7.3, - "lowMids": -24.2, - "mids": -22.5, - "upperMids": -31.5, - "highs": -27.8, - "brilliance": -20.3 - }, - { - "t": 1.0, - "subBass": -10.8, - "lowBass": -8.3, - "lowMids": -21.8, - "mids": -26.2, - "upperMids": -34.0, - "highs": -56.6, - "brilliance": -59.7 - }, - { - "t": 1.07, - "subBass": -3.1, - "lowBass": -3.0, - "lowMids": -20.9, - "mids": -25.3, - "upperMids": -35.3, - "highs": -53.2, - "brilliance": -56.0 - }, - { - "t": 1.14, - "subBass": -5.3, - "lowBass": -9.2, - "lowMids": -33.5, - "mids": -20.0, - "upperMids": -32.1, - "highs": -44.3, - "brilliance": -73.2 - }, - { - "t": 1.21, - "subBass": -6.6, - "lowBass": -8.3, - "lowMids": -22.3, - "mids": -23.7, - "upperMids": -28.2, - "highs": -20.9, - "brilliance": -13.1 - }, - { - "t": 1.28, - "subBass": -8.3, - "lowBass": -7.5, - "lowMids": -21.3, - "mids": -25.2, - "upperMids": -32.0, - "highs": -28.8, - "brilliance": -21.2 - }, - { - "t": 1.35, - "subBass": -9.4, - "lowBass": -8.0, - "lowMids": -22.3, - "mids": -26.1, - "upperMids": -34.2, - "highs": -51.7, - "brilliance": -55.2 - }, - { - "t": 1.42, - "subBass": -7.4, - "lowBass": -7.4, - "lowMids": -29.8, - "mids": -19.5, - "upperMids": -31.4, - "highs": -44.9, - "brilliance": -59.0 - }, - { - "t": 1.49, - "subBass": -6.1, - "lowBass": -8.4, - "lowMids": -20.9, - "mids": -23.8, - "upperMids": -33.2, - "highs": -48.2, - "brilliance": -82.8 - }, - { - "t": 1.56, - "subBass": -6.8, - "lowBass": -8.1, - "lowMids": -21.2, - "mids": -24.8, - "upperMids": -27.7, - "highs": -20.6, - "brilliance": -13.0 - }, - { - "t": 1.63, - "subBass": -5.7, - "lowBass": -7.9, - "lowMids": -21.5, - "mids": -25.2, - "upperMids": -33.2, - "highs": -30.0, - "brilliance": -22.6 - }, - { - "t": 1.7, - "subBass": -7.9, - "lowBass": -8.2, - "lowMids": -22.7, - "mids": -25.2, - "upperMids": -35.4, - "highs": -51.9, - "brilliance": -59.5 - }, - { - "t": 1.76, - "subBass": -5.8, - "lowBass": -5.5, - "lowMids": -33.4, - "mids": -20.4, - "upperMids": -33.1, - "highs": -43.4, - "brilliance": -59.0 - }, - { - "t": 1.83, - "subBass": -5.0, - "lowBass": -7.9, - "lowMids": -34.9, - "mids": -20.0, - "upperMids": -31.9, - "highs": -44.0, - "brilliance": -67.8 - }, - { - "t": 1.9, - "subBass": -7.8, - "lowBass": -7.8, - "lowMids": -36.4, - "mids": -20.0, - "upperMids": -26.8, - "highs": -20.3, - "brilliance": -12.7 - }, - { - "t": 1.97, - "subBass": -9.3, - "lowBass": -7.5, - "lowMids": -22.9, - "mids": -23.0, - "upperMids": -32.5, - "highs": -31.0, - "brilliance": -23.6 - }, - { - "t": 2.04, - "subBass": -10.4, - "lowBass": -7.5, - "lowMids": -23.2, - "mids": -24.3, - "upperMids": -33.9, - "highs": -45.9, - "brilliance": -52.3 - }, - { - "t": 2.11, - "subBass": -3.6, - "lowBass": -3.2, - "lowMids": -28.2, - "mids": -22.0, - "upperMids": -34.0, - "highs": -46.0, - "brilliance": -64.9 - }, - { - "t": 2.18, - "subBass": -4.1, - "lowBass": -8.5, - "lowMids": -20.9, - "mids": -24.7, - "upperMids": -32.8, - "highs": -61.9, - "brilliance": -72.2 - }, - { - "t": 2.25, - "subBass": -6.9, - "lowBass": -7.9, - "lowMids": -21.1, - "mids": -24.5, - "upperMids": -28.0, - "highs": -20.6, - "brilliance": -12.9 - }, - { - "t": 2.32, - "subBass": -8.0, - "lowBass": -7.5, - "lowMids": -27.1, - "mids": -20.9, - "upperMids": -31.8, - "highs": -32.5, - "brilliance": -25.0 - }, - { - "t": 2.39, - "subBass": -10.5, - "lowBass": -6.8, - "lowMids": -24.2, - "mids": -21.9, - "upperMids": -33.2, - "highs": -45.5, - "brilliance": -51.7 - }, - { - "t": 2.46, - "subBass": -5.7, - "lowBass": -6.7, - "lowMids": -21.7, - "mids": -25.6, - "upperMids": -35.1, - "highs": -60.8, - "brilliance": -63.0 - }, - { - "t": 2.53, - "subBass": -6.1, - "lowBass": -8.4, - "lowMids": -20.5, - "mids": -24.7, - "upperMids": -32.6, - "highs": -67.2, - "brilliance": -84.4 - }, - { - "t": 2.6, - "subBass": -7.0, - "lowBass": -8.3, - "lowMids": -21.1, - "mids": -24.9, - "upperMids": -27.8, - "highs": -21.1, - "brilliance": -13.1 - }, - { - "t": 2.67, - "subBass": -7.8, - "lowBass": -8.3, - "lowMids": -21.1, - "mids": -24.1, - "upperMids": -33.0, - "highs": -34.4, - "brilliance": -26.8 - }, - { - "t": 2.74, - "subBass": -8.0, - "lowBass": -7.2, - "lowMids": -24.1, - "mids": -22.3, - "upperMids": -33.6, - "highs": -45.3, - "brilliance": -59.0 - }, - { - "t": 2.81, - "subBass": -5.8, - "lowBass": -6.3, - "lowMids": -32.4, - "mids": -21.9, - "upperMids": -34.4, - "highs": -45.0, - "brilliance": -57.5 - }, - { - "t": 2.88, - "subBass": -6.4, - "lowBass": -9.0, - "lowMids": -20.6, - "mids": -24.3, - "upperMids": -33.9, - "highs": -70.0, - "brilliance": -74.2 - }, - { - "t": 2.95, - "subBass": -8.4, - "lowBass": -7.6, - "lowMids": -21.2, - "mids": -24.7, - "upperMids": -27.6, - "highs": -20.9, - "brilliance": -13.0 - }, - { - "t": 3.02, - "subBass": -9.4, - "lowBass": -7.6, - "lowMids": -20.8, - "mids": -24.8, - "upperMids": -32.5, - "highs": -37.2, - "brilliance": -29.4 - }, - { - "t": 3.09, - "subBass": -9.4, - "lowBass": -6.4, - "lowMids": -24.8, - "mids": -22.3, - "upperMids": -33.3, - "highs": -44.9, - "brilliance": -54.3 - }, - { - "t": 3.16, - "subBass": -2.5, - "lowBass": -3.8, - "lowMids": -27.5, - "mids": -23.5, - "upperMids": -35.8, - "highs": -46.8, - "brilliance": -61.4 - }, - { - "t": 3.23, - "subBass": -6.0, - "lowBass": -9.6, - "lowMids": -20.8, - "mids": -24.7, - "upperMids": -34.7, - "highs": -70.4, - "brilliance": -79.6 - }, - { - "t": 3.3, - "subBass": -6.8, - "lowBass": -7.6, - "lowMids": -20.9, - "mids": -24.1, - "upperMids": -27.3, - "highs": -20.5, - "brilliance": -12.8 - }, - { - "t": 3.37, - "subBass": -8.6, - "lowBass": -7.7, - "lowMids": -20.6, - "mids": -24.3, - "upperMids": -34.1, - "highs": -41.8, - "brilliance": -34.1 - }, - { - "t": 3.44, - "subBass": -10.4, - "lowBass": -7.3, - "lowMids": -20.9, - "mids": -25.0, - "upperMids": -34.5, - "highs": -52.6, - "brilliance": -54.7 - }, - { - "t": 3.51, - "subBass": -5.5, - "lowBass": -7.3, - "lowMids": -23.0, - "mids": -24.4, - "upperMids": -34.8, - "highs": -50.7, - "brilliance": -63.0 - }, - { - "t": 3.58, - "subBass": -6.7, - "lowBass": -8.8, - "lowMids": -37.5, - "mids": -20.3, - "upperMids": -32.4, - "highs": -44.4, - "brilliance": -65.3 - }, - { - "t": 3.65, - "subBass": -6.0, - "lowBass": -7.7, - "lowMids": -20.9, - "mids": -24.0, - "upperMids": -27.0, - "highs": -20.3, - "brilliance": -12.6 - }, - { - "t": 3.72, - "subBass": -6.4, - "lowBass": -8.3, - "lowMids": -20.6, - "mids": -24.4, - "upperMids": -34.3, - "highs": -45.7, - "brilliance": -37.5 - }, - { - "t": 3.78, - "subBass": -8.9, - "lowBass": -6.8, - "lowMids": -21.7, - "mids": -25.2, - "upperMids": -34.4, - "highs": -50.1, - "brilliance": -60.1 - }, - { - "t": 3.85, - "subBass": -4.7, - "lowBass": -6.6, - "lowMids": -22.4, - "mids": -25.5, - "upperMids": -34.7, - "highs": -48.8, - "brilliance": -58.0 - }, - { - "t": 3.92, - "subBass": -5.7, - "lowBass": -8.5, - "lowMids": -21.9, - "mids": -25.8, - "upperMids": -34.5, - "highs": -46.8, - "brilliance": -42.7 - }, - { - "t": 3.99, - "subBass": -8.5, - "lowBass": -7.2, - "lowMids": -20.7, - "mids": -24.1, - "upperMids": -27.0, - "highs": -19.9, - "brilliance": -12.3 - }, - { - "t": 4.06, - "subBass": -9.8, - "lowBass": -7.8, - "lowMids": -31.4, - "mids": -19.8, - "upperMids": -32.4, - "highs": -42.8, - "brilliance": -44.5 - }, - { - "t": 4.13, - "subBass": -7.9, - "lowBass": -4.5, - "lowMids": -28.7, - "mids": -21.1, - "upperMids": -33.4, - "highs": -43.7, - "brilliance": -52.7 - }, - { - "t": 4.2, - "subBass": -3.2, - "lowBass": -5.2, - "lowMids": -31.3, - "mids": -21.6, - "upperMids": -33.9, - "highs": -45.0, - "brilliance": -63.1 - }, - { - "t": 4.27, - "subBass": -5.3, - "lowBass": -9.3, - "lowMids": -40.3, - "mids": -21.3, - "upperMids": -33.7, - "highs": -38.3, - "brilliance": -32.4 - }, - { - "t": 4.34, - "subBass": -7.4, - "lowBass": -7.3, - "lowMids": -20.7, - "mids": -24.3, - "upperMids": -27.4, - "highs": -20.2, - "brilliance": -12.4 - }, - { - "t": 4.41, - "subBass": -8.6, - "lowBass": -7.8, - "lowMids": -31.8, - "mids": -19.8, - "upperMids": -31.7, - "highs": -43.9, - "brilliance": -58.9 - }, - { - "t": 4.48, - "subBass": -10.9, - "lowBass": -7.2, - "lowMids": -22.6, - "mids": -23.7, - "upperMids": -32.7, - "highs": -47.4, - "brilliance": -51.8 - }, - { - "t": 4.55, - "subBass": -5.3, - "lowBass": -6.9, - "lowMids": -21.7, - "mids": -24.9, - "upperMids": -33.9, - "highs": -51.0, - "brilliance": -63.7 - }, - { - "t": 4.62, - "subBass": -6.8, - "lowBass": -8.7, - "lowMids": -22.7, - "mids": -26.0, - "upperMids": -34.3, - "highs": -32.6, - "brilliance": -26.1 - }, - { - "t": 4.69, - "subBass": -7.2, - "lowBass": -7.9, - "lowMids": -42.9, - "mids": -19.9, - "upperMids": -27.6, - "highs": -20.8, - "brilliance": -12.9 - }, - { - "t": 4.76, - "subBass": -7.4, - "lowBass": -8.5, - "lowMids": -20.7, - "mids": -24.0, - "upperMids": -34.2, - "highs": -57.2, - "brilliance": -68.0 - }, - { - "t": 4.83, - "subBass": -7.1, - "lowBass": -5.4, - "lowMids": -25.8, - "mids": -20.5, - "upperMids": -32.5, - "highs": -44.7, - "brilliance": -58.2 - }, - { - "t": 4.9, - "subBass": -6.0, - "lowBass": -9.0, - "lowMids": -23.3, - "mids": -22.7, - "upperMids": -33.9, - "highs": -48.3, - "brilliance": -58.1 - }, - { - "t": 4.97, - "subBass": -7.1, - "lowBass": -9.0, - "lowMids": -22.4, - "mids": -25.8, - "upperMids": -33.4, - "highs": -28.2, - "brilliance": -21.4 - }, - { - "t": 5.04, - "subBass": -8.5, - "lowBass": -7.1, - "lowMids": -21.4, - "mids": -25.5, - "upperMids": -28.0, - "highs": -21.7, - "brilliance": -13.6 - }, - { - "t": 5.11, - "subBass": -9.9, - "lowBass": -7.9, - "lowMids": -35.4, - "mids": -19.5, - "upperMids": -32.2, - "highs": -42.9, - "brilliance": -72.7 - }, - { - "t": 5.18, - "subBass": -5.3, - "lowBass": -3.6, - "lowMids": -22.5, - "mids": -24.5, - "upperMids": -33.6, - "highs": -48.9, - "brilliance": -53.5 - }, - { - "t": 5.25, - "subBass": -3.9, - "lowBass": -6.9, - "lowMids": -21.9, - "mids": -25.2, - "upperMids": -34.1, - "highs": -49.9, - "brilliance": -60.3 - }, - { - "t": 5.32, - "subBass": -5.3, - "lowBass": -9.1, - "lowMids": -22.1, - "mids": -26.2, - "upperMids": -31.6, - "highs": -25.2, - "brilliance": -18.4 - }, - { - "t": 5.39, - "subBass": -7.6, - "lowBass": -7.2, - "lowMids": -21.8, - "mids": -25.9, - "upperMids": -28.5, - "highs": -22.7, - "brilliance": -14.4 - }, - { - "t": 5.46, - "subBass": -8.9, - "lowBass": -8.0, - "lowMids": -20.1, - "mids": -23.9, - "upperMids": -33.5, - "highs": -69.3, - "brilliance": -72.8 - }, - { - "t": 5.53, - "subBass": -10.1, - "lowBass": -7.4, - "lowMids": -21.2, - "mids": -24.6, - "upperMids": -32.7, - "highs": -52.4, - "brilliance": -54.5 - }, - { - "t": 5.6, - "subBass": -4.6, - "lowBass": -6.4, - "lowMids": -26.3, - "mids": -21.5, - "upperMids": -32.7, - "highs": -47.0, - "brilliance": -60.9 - }, - { - "t": 5.67, - "subBass": -7.3, - "lowBass": -8.7, - "lowMids": -25.1, - "mids": -21.6, - "upperMids": -29.2, - "highs": -23.4, - "brilliance": -16.3 - }, - { - "t": 5.74, - "subBass": -5.3, - "lowBass": -7.7, - "lowMids": -22.3, - "mids": -26.3, - "upperMids": -29.6, - "highs": -23.7, - "brilliance": -15.4 - }, - { - "t": 5.8, - "subBass": -7.7, - "lowBass": -8.7, - "lowMids": -35.9, - "mids": -19.1, - "upperMids": -31.2, - "highs": -43.3, - "brilliance": -78.1 - }, - { - "t": 5.87, - "subBass": -9.0, - "lowBass": -6.6, - "lowMids": -21.2, - "mids": -24.2, - "upperMids": -32.7, - "highs": -53.4, - "brilliance": -60.7 - }, - { - "t": 5.94, - "subBass": -4.2, - "lowBass": -7.1, - "lowMids": -27.2, - "mids": -21.3, - "upperMids": -32.5, - "highs": -46.4, - "brilliance": -56.9 - }, - { - "t": 6.01, - "subBass": -6.7, - "lowBass": -8.3, - "lowMids": -35.2, - "mids": -20.9, - "upperMids": -28.6, - "highs": -22.0, - "brilliance": -15.1 - }, - { - "t": 6.08, - "subBass": -8.6, - "lowBass": -7.2, - "lowMids": -38.2, - "mids": -21.3, - "upperMids": -29.4, - "highs": -23.8, - "brilliance": -15.8 - }, - { - "t": 6.15, - "subBass": -10.3, - "lowBass": -8.1, - "lowMids": -40.2, - "mids": -19.2, - "upperMids": -32.0, - "highs": -42.6, - "brilliance": -77.1 - }, - { - "t": 6.22, - "subBass": -4.6, - "lowBass": -3.1, - "lowMids": -22.0, - "mids": -24.7, - "upperMids": -33.8, - "highs": -50.3, - "brilliance": -53.5 - }, - { - "t": 6.29, - "subBass": -4.1, - "lowBass": -8.4, - "lowMids": -21.5, - "mids": -25.0, - "upperMids": -34.4, - "highs": -59.4, - "brilliance": -61.6 - }, - { - "t": 6.36, - "subBass": -6.8, - "lowBass": -9.1, - "lowMids": -24.8, - "mids": -22.0, - "upperMids": -29.1, - "highs": -21.7, - "brilliance": -14.4 - }, - { - "t": 6.43, - "subBass": -7.6, - "lowBass": -7.2, - "lowMids": -33.5, - "mids": -21.5, - "upperMids": -29.8, - "highs": -24.4, - "brilliance": -16.4 - }, - { - "t": 6.5, - "subBass": -9.2, - "lowBass": -8.2, - "lowMids": -20.6, - "mids": -24.3, - "upperMids": -33.2, - "highs": -48.1, - "brilliance": -81.3 - }, - { - "t": 6.57, - "subBass": -8.5, - "lowBass": -6.2, - "lowMids": -30.5, - "mids": -20.1, - "upperMids": -32.5, - "highs": -42.3, - "brilliance": -52.2 - }, - { - "t": 6.64, - "subBass": -6.0, - "lowBass": -8.7, - "lowMids": -21.6, - "mids": -23.3, - "upperMids": -33.3, - "highs": -47.4, - "brilliance": -64.4 - }, - { - "t": 6.71, - "subBass": -7.2, - "lowBass": -8.5, - "lowMids": -21.7, - "mids": -25.2, - "upperMids": -29.1, - "highs": -22.0, - "brilliance": -14.2 - }, - { - "t": 6.78, - "subBass": -7.1, - "lowBass": -8.0, - "lowMids": -22.3, - "mids": -26.2, - "upperMids": -30.7, - "highs": -25.1, - "brilliance": -17.5 - }, - { - "t": 6.85, - "subBass": -7.0, - "lowBass": -8.8, - "lowMids": -20.9, - "mids": -24.8, - "upperMids": -34.3, - "highs": -76.6, - "brilliance": -89.9 - }, - { - "t": 6.92, - "subBass": -7.7, - "lowBass": -6.0, - "lowMids": -19.9, - "mids": -23.7, - "upperMids": -33.1, - "highs": -47.8, - "brilliance": -56.1 - }, - { - "t": 6.99, - "subBass": -5.3, - "lowBass": -8.1, - "lowMids": -27.5, - "mids": -20.2, - "upperMids": -31.9, - "highs": -45.7, - "brilliance": -66.3 - }, - { - "t": 7.06, - "subBass": -7.6, - "lowBass": -8.3, - "lowMids": -35.6, - "mids": -20.9, - "upperMids": -28.4, - "highs": -22.1, - "brilliance": -14.3 - }, - { - "t": 7.13, - "subBass": -8.9, - "lowBass": -7.3, - "lowMids": -35.5, - "mids": -21.1, - "upperMids": -30.4, - "highs": -25.9, - "brilliance": -18.4 - }, - { - "t": 7.2, - "subBass": -10.5, - "lowBass": -8.3, - "lowMids": -41.7, - "mids": -20.2, - "upperMids": -32.4, - "highs": -44.4, - "brilliance": -74.9 - }, - { - "t": 7.27, - "subBass": -3.3, - "lowBass": -2.8, - "lowMids": -21.2, - "mids": -23.7, - "upperMids": -35.0, - "highs": -50.1, - "brilliance": -52.2 - }, - { - "t": 7.34, - "subBass": -5.4, - "lowBass": -9.5, - "lowMids": -28.6, - "mids": -20.0, - "upperMids": -31.8, - "highs": -45.8, - "brilliance": -73.4 - }, - { - "t": 7.41, - "subBass": -5.5, - "lowBass": -8.3, - "lowMids": -33.6, - "mids": -20.7, - "upperMids": -28.2, - "highs": -22.0, - "brilliance": -14.1 - }, - { - "t": 7.48, - "subBass": -8.0, - "lowBass": -7.4, - "lowMids": -24.7, - "mids": -22.2, - "upperMids": -31.4, - "highs": -26.9, - "brilliance": -19.5 - }, - { - "t": 7.55, - "subBass": -9.3, - "lowBass": -8.4, - "lowMids": -21.6, - "mids": -25.5, - "upperMids": -35.5, - "highs": -62.0, - "brilliance": -64.4 - }, - { - "t": 7.62, - "subBass": -6.7, - "lowBass": -5.5, - "lowMids": -20.8, - "mids": -24.0, - "upperMids": -32.5, - "highs": -51.5, - "brilliance": -53.7 - }, - { - "t": 7.69, - "subBass": -5.5, - "lowBass": -7.7, - "lowMids": -31.4, - "mids": -20.2, - "upperMids": -32.1, - "highs": -44.4, - "brilliance": -74.3 - }, - { - "t": 7.76, - "subBass": -6.5, - "lowBass": -8.3, - "lowMids": -32.5, - "mids": -20.9, - "upperMids": -27.8, - "highs": -21.7, - "brilliance": -13.8 - }, - { - "t": 7.83, - "subBass": -6.2, - "lowBass": -7.9, - "lowMids": -23.7, - "mids": -22.2, - "upperMids": -31.5, - "highs": -27.8, - "brilliance": -20.5 - }, - { - "t": 7.89, - "subBass": -8.8, - "lowBass": -9.0, - "lowMids": -21.8, - "mids": -25.5, - "upperMids": -34.5, - "highs": -49.2, - "brilliance": -65.1 - }, - { - "t": 7.96, - "subBass": -6.1, - "lowBass": -5.7, - "lowMids": -28.5, - "mids": -19.6, - "upperMids": -31.5, - "highs": -44.9, - "brilliance": -56.9 - }, - { - "t": 8.03, - "subBass": -5.5, - "lowBass": -8.5, - "lowMids": -21.2, - "mids": -24.1, - "upperMids": -33.5, - "highs": -48.5, - "brilliance": -84.8 - }, - { - "t": 8.1, - "subBass": -7.6, - "lowBass": -7.9, - "lowMids": -29.0, - "mids": -21.1, - "upperMids": -27.4, - "highs": -20.6, - "brilliance": -13.1 - }, - { - "t": 8.17, - "subBass": -9.0, - "lowBass": -7.4, - "lowMids": -36.6, - "mids": -20.3, - "upperMids": -31.4, - "highs": -28.7, - "brilliance": -21.4 - }, - { - "t": 8.24, - "subBass": -10.7, - "lowBass": -8.1, - "lowMids": -33.6, - "mids": -21.1, - "upperMids": -33.4, - "highs": -45.1, - "brilliance": -55.4 - }, - { - "t": 8.31, - "subBass": -3.8, - "lowBass": -3.4, - "lowMids": -21.4, - "mids": -25.3, - "upperMids": -35.2, - "highs": -56.8, - "brilliance": -59.0 - }, - { - "t": 8.38, - "subBass": -4.4, - "lowBass": -8.6, - "lowMids": -20.9, - "mids": -24.5, - "upperMids": -34.5, - "highs": -69.8, - "brilliance": -82.1 - }, - { - "t": 8.45, - "subBass": -7.5, - "lowBass": -8.3, - "lowMids": -20.9, - "mids": -23.8, - "upperMids": -27.4, - "highs": -20.4, - "brilliance": -12.7 - }, - { - "t": 8.52, - "subBass": -7.8, - "lowBass": -7.4, - "lowMids": -26.0, - "mids": -21.4, - "upperMids": -31.9, - "highs": -29.8, - "brilliance": -22.3 - }, - { - "t": 8.59, - "subBass": -10.1, - "lowBass": -7.5, - "lowMids": -29.9, - "mids": -21.2, - "upperMids": -33.6, - "highs": -45.1, - "brilliance": -53.0 - }, - { - "t": 8.66, - "subBass": -7.2, - "lowBass": -7.3, - "lowMids": -20.9, - "mids": -25.0, - "upperMids": -34.5, - "highs": -61.0, - "brilliance": -63.2 - }, - { - "t": 8.73, - "subBass": -6.2, - "lowBass": -8.6, - "lowMids": -28.3, - "mids": -19.6, - "upperMids": -31.3, - "highs": -45.1, - "brilliance": -81.5 - }, - { - "t": 8.8, - "subBass": -7.4, - "lowBass": -8.3, - "lowMids": -38.6, - "mids": -20.1, - "upperMids": -27.3, - "highs": -20.3, - "brilliance": -12.7 - }, - { - "t": 8.87, - "subBass": -6.5, - "lowBass": -8.1, - "lowMids": -37.5, - "mids": -20.4, - "upperMids": -31.6, - "highs": -31.0, - "brilliance": -23.8 - }, - { - "t": 8.94, - "subBass": -7.2, - "lowBass": -7.3, - "lowMids": -28.6, - "mids": -21.4, - "upperMids": -33.5, - "highs": -44.8, - "brilliance": -57.6 - }, - { - "t": 9.01, - "subBass": -6.7, - "lowBass": -7.3, - "lowMids": -35.6, - "mids": -20.7, - "upperMids": -33.4, - "highs": -43.9, - "brilliance": -61.3 - }, - { - "t": 9.08, - "subBass": -5.5, - "lowBass": -8.2, - "lowMids": -20.5, - "mids": -24.2, - "upperMids": -34.3, - "highs": -63.2, - "brilliance": -71.4 - }, - { - "t": 9.15, - "subBass": -8.0, - "lowBass": -7.7, - "lowMids": -21.5, - "mids": -24.7, - "upperMids": -27.6, - "highs": -20.7, - "brilliance": -12.9 - }, - { - "t": 9.22, - "subBass": -9.3, - "lowBass": -7.5, - "lowMids": -27.1, - "mids": -20.9, - "upperMids": -31.9, - "highs": -32.6, - "brilliance": -25.2 - }, - { - "t": 9.29, - "subBass": -10.1, - "lowBass": -6.9, - "lowMids": -24.4, - "mids": -22.0, - "upperMids": -32.7, - "highs": -45.8, - "brilliance": -52.5 - }, - { - "t": 9.36, - "subBass": -3.3, - "lowBass": -3.5, - "lowMids": -22.4, - "mids": -27.6, - "upperMids": -35.6, - "highs": -60.0, - "brilliance": -62.7 - }, - { - "t": 9.43, - "subBass": -4.7, - "lowBass": -8.9, - "lowMids": -20.6, - "mids": -24.4, - "upperMids": -34.4, - "highs": -68.9, - "brilliance": -77.1 - }, - { - "t": 9.5, - "subBass": -6.2, - "lowBass": -7.7, - "lowMids": -21.3, - "mids": -24.4, - "upperMids": -27.8, - "highs": -21.0, - "brilliance": -13.1 - }, - { - "t": 9.57, - "subBass": -8.5, - "lowBass": -7.6, - "lowMids": -20.8, - "mids": -24.5, - "upperMids": -33.4, - "highs": -35.5, - "brilliance": -27.6 - }, - { - "t": 9.64, - "subBass": -10.3, - "lowBass": -6.2, - "lowMids": -22.0, - "mids": -25.0, - "upperMids": -34.4, - "highs": -51.2, - "brilliance": -53.1 - }, - { - "t": 9.71, - "subBass": -5.0, - "lowBass": -6.4, - "lowMids": -22.4, - "mids": -25.7, - "upperMids": -35.8, - "highs": -54.8, - "brilliance": -60.9 - }, - { - "t": 9.78, - "subBass": -6.3, - "lowBass": -8.4, - "lowMids": -35.9, - "mids": -19.8, - "upperMids": -31.9, - "highs": -44.0, - "brilliance": -79.8 - }, - { - "t": 9.85, - "subBass": -6.1, - "lowBass": -7.9, - "lowMids": -31.1, - "mids": -20.3, - "upperMids": -27.4, - "highs": -21.0, - "brilliance": -13.2 - }, - { - "t": 9.91, - "subBass": -7.5, - "lowBass": -8.3, - "lowMids": -39.4, - "mids": -19.6, - "upperMids": -32.0, - "highs": -36.4, - "brilliance": -29.6 - }, - { - "t": 9.98, - "subBass": -9.2, - "lowBass": -7.0, - "lowMids": -30.5, - "mids": -20.7, - "upperMids": -32.9, - "highs": -44.3, - "brilliance": -59.8 - }, - { - "t": 10.05, - "subBass": -5.4, - "lowBass": -7.0, - "lowMids": -35.5, - "mids": -21.6, - "upperMids": -33.7, - "highs": -45.3, - "brilliance": -58.2 - }, - { - "t": 10.12, - "subBass": -6.0, - "lowBass": -8.8, - "lowMids": -39.8, - "mids": -20.1, - "upperMids": -32.8, - "highs": -43.5, - "brilliance": -85.3 - }, - { - "t": 10.19, - "subBass": -8.5, - "lowBass": -7.4, - "lowMids": -21.0, - "mids": -25.0, - "upperMids": -27.2, - "highs": -20.4, - "brilliance": -12.7 - }, - { - "t": 10.26, - "subBass": -9.5, - "lowBass": -7.6, - "lowMids": -20.6, - "mids": -23.9, - "upperMids": -32.9, - "highs": -40.6, - "brilliance": -33.3 - }, - { - "t": 10.33, - "subBass": -9.5, - "lowBass": -6.1, - "lowMids": -24.9, - "mids": -22.3, - "upperMids": -33.0, - "highs": -45.8, - "brilliance": -55.2 - }, - { - "t": 10.4, - "subBass": -2.7, - "lowBass": -4.2, - "lowMids": -25.1, - "mids": -23.3, - "upperMids": -34.8, - "highs": -49.2, - "brilliance": -60.0 - }, - { - "t": 10.47, - "subBass": -6.3, - "lowBass": -9.8, - "lowMids": -21.3, - "mids": -25.3, - "upperMids": -35.2, - "highs": -67.1, - "brilliance": -60.7 - }, - { - "t": 10.54, - "subBass": -7.5, - "lowBass": -7.6, - "lowMids": -34.5, - "mids": -19.8, - "upperMids": -27.1, - "highs": -20.0, - "brilliance": -12.4 - }, - { - "t": 10.61, - "subBass": -8.2, - "lowBass": -7.7, - "lowMids": -20.9, - "mids": -23.7, - "upperMids": -33.7, - "highs": -47.0, - "brilliance": -40.1 - }, - { - "t": 10.68, - "subBass": -11.3, - "lowBass": -7.3, - "lowMids": -26.6, - "mids": -21.5, - "upperMids": -32.9, - "highs": -45.3, - "brilliance": -53.8 - }, - { - "t": 10.75, - "subBass": -5.8, - "lowBass": -7.4, - "lowMids": -30.2, - "mids": -21.0, - "upperMids": -32.7, - "highs": -45.4, - "brilliance": -62.4 - }, - { - "t": 10.82, - "subBass": -6.6, - "lowBass": -8.7, - "lowMids": -32.2, - "mids": -20.8, - "upperMids": -32.7, - "highs": -44.2, - "brilliance": -41.4 - }, - { - "t": 10.89, - "subBass": -6.9, - "lowBass": -7.8, - "lowMids": -20.7, - "mids": -24.6, - "upperMids": -27.6, - "highs": -20.0, - "brilliance": -12.4 - }, - { - "t": 10.96, - "subBass": -6.3, - "lowBass": -8.3, - "lowMids": -32.1, - "mids": -19.9, - "upperMids": -31.9, - "highs": -43.8, - "brilliance": -45.7 - }, - { - "t": 11.03, - "subBass": -11.0, - "lowBass": -12.1, - "lowMids": -37.8, - "mids": -26.7, - "upperMids": -38.4, - "highs": -48.1, - "brilliance": -52.7 - } - ], - "spectralDetail": { - "spectralCentroid": 1304.4, - "spectralRolloff": 1525.3, - "spectralBandwidth": 3412.1, - "spectralFlatness": 0.137751, - "mfcc": [ - -686.308, - 184.2957, - -7.4131, - 65.8112, - 18.7413, - 22.001, - 26.153, - 15.9766, - 19.0587, - 15.8666, - 18.6521, - 14.8944, - 14.1728 - ], - "chroma": [ - 0.0511, - 0.1873, - 0.0463, - 0.2066, - 0.2722, - 0.0855, - 0.2191, - 0.1116, - 0.3051, - 0.1294, - 0.3776, - 0.3744 - ], - "barkBands": [ - -22.6561, - -6.7652, - -10.4343, - -21.3403, - -29.1591, - -27.4846, - -26.1144, - -26.4436, - -29.9765, - -33.1211, - -33.2707, - -31.6134, - -36.8811, - -35.7067, - -37.3189, - -38.5858, - -38.7137, - -38.9775, - -39.8856, - -39.3102, - -37.9194, - -34.6127, - -34.7568, - -30.7898 - ], - "erbBands": [ - 10.9257, - 18.9309, - 20.2046, - 14.7907, - 8.5778, - 4.6008, - 7.4916, - 14.2067, - 15.4885, - 16.5202, - 16.2692, - 12.1139, - 12.3703, - 11.9321, - 13.9283, - 13.5117, - 10.8483, - 10.9626, - 10.5619, - 9.2639, - 9.2054, - 8.6515, - 8.1158, - 7.8919, - 6.8227, - 7.3208, - 8.023, - 8.3109, - 10.3457, - 10.7953, - 10.2733, - 12.378, - 11.1925, - 11.6254, - 10.9264, - 10.7251, - 8.7916, - 5.013, - -1.024, - -10.4651 - ], - "spectralContrast": [ - -0.2335, - -0.5832, - -0.6454, - -0.7005, - -0.7311, - -0.7663 - ], - "spectralValley": [ - -5.3328, - -6.3516, - -7.2242, - -7.8012, - -8.4532, - -10.5726 - ] - }, - "stemAnalysis": null, - "transientDensityDetail": { - "subBass": { - "onsetRatePerSecond": 15.32, - "meanOnsetStrength": 0.264, - "peakOnsetStrength": 0.693, - "eventCount": 169 - }, - "lowBass": { - "onsetRatePerSecond": 8.79, - "meanOnsetStrength": 0.384, - "peakOnsetStrength": 0.987, - "eventCount": 97 - }, - "lowMids": { - "onsetRatePerSecond": 12.69, - "meanOnsetStrength": 0.887, - "peakOnsetStrength": 1.926, - "eventCount": 140 - }, - "mids": { - "onsetRatePerSecond": 12.51, - "meanOnsetStrength": 3.693, - "peakOnsetStrength": 6.438, - "eventCount": 138 - }, - "upperMids": { - "onsetRatePerSecond": 12.51, - "meanOnsetStrength": 3.492, - "peakOnsetStrength": 8.135, - "eventCount": 138 - }, - "highs": { - "onsetRatePerSecond": 8.25, - "meanOnsetStrength": 5.451, - "peakOnsetStrength": 13.226, - "eventCount": 91 - }, - "brilliance": { - "onsetRatePerSecond": 8.61, - "meanOnsetStrength": 6.911, - "peakOnsetStrength": 15.42, - "eventCount": 95 - } - }, - "saturationDetail": { - "clippedSampleCount": 0, - "clippedSamplePercent": 0.0, - "nearClippedSampleCount": 0, - "nearClippedSamplePercent": 0.0, - "peakRatio95to50": 2.466, - "rmsToPeakRatioDb": 9.02, - "saturationLikely": false - }, - "snareDetail": { - "hitCount": 39, - "hitsPerSecond": 3.53, - "meanAttackSharpness": 0.0075, - "meanBodyEnergyRatio": 0.966, - "meanSnapEnergyRatio": 0.034, - "meanCentroidHz": 487.7, - "meanDecayFrames": 50.2, - "meanDecaySeconds": 0.291, - "bandHz": [ - 120.0, - 2000.0 - ] - }, - "hihatDetail": { - "hitCount": 32, - "hitsPerSecond": 2.9, - "meanAttackSharpness": 0.0031, - "meanBodyEnergyRatio": 0.178, - "meanSnapEnergyRatio": 0.822, - "meanCentroidHz": 7897.6, - "meanDecayFrames": 8.1, - "meanDecaySeconds": 0.047, - "bandHz": [ - 2000.0, - 12000.0 - ] - }, - "rhythmDetail": { - "onsetRate": 2.9, - "beatGrid": [ - 0.488, - 1.022, - 1.533, - 2.043, - 2.566, - 3.088, - 3.599, - 4.11, - 4.632, - 5.155, - 5.666, - 6.177, - 6.699, - 7.221, - 7.732, - 8.243, - 8.777, - 9.288, - 9.799, - 10.321, - 10.844 - ], - "downbeats": [ - 2.566, - 5.155, - 7.732, - 10.321 - ], - "beatPositions": [ - 2, - 3, - 4, - 5, - 1, - 2, - 3, - 4, - 5, - 1, - 2, - 3, - 4, - 5, - 1, - 2, - 3, - 4, - 5, - 1, - 2 - ], - "downbeatSource": "kick_accent", - "downbeatConfidence": 0.0079, - "grooveAmount": 0.0149, - "tempoStability": 0.9851, - "phraseGrid": { - "phrases4Bar": [ - 2.566 - ], - "phrases8Bar": [ - 2.566 - ], - "phrases16Bar": [ - 2.566 - ], - "totalBars": 4, - "totalPhrases8Bar": 1 - }, - "tempoCurve": [ - { - "t": 0.75, - "bpm": 117.5 - }, - { - "t": 1.28, - "bpm": 116.1 - }, - { - "t": 1.79, - "bpm": 114.8 - }, - { - "t": 2.3, - "bpm": 117.5 - }, - { - "t": 2.83, - "bpm": 117.5 - }, - { - "t": 3.34, - "bpm": 114.8 - }, - { - "t": 3.85, - "bpm": 114.8 - }, - { - "t": 4.37, - "bpm": 117.5 - }, - { - "t": 4.89, - "bpm": 117.5 - }, - { - "t": 5.41, - "bpm": 114.8 - }, - { - "t": 5.92, - "bpm": 114.8 - }, - { - "t": 6.44, - "bpm": 117.5 - }, - { - "t": 6.96, - "bpm": 117.5 - }, - { - "t": 7.48, - "bpm": 114.8 - }, - { - "t": 7.99, - "bpm": 117.5 - }, - { - "t": 8.51, - "bpm": 117.5 - }, - { - "t": 9.03, - "bpm": 117.5 - }, - { - "t": 9.54, - "bpm": 114.8 - }, - { - "t": 10.06, - "bpm": 116.1 - }, - { - "t": 10.58, - "bpm": 114.8 - } - ] - }, - "melodyDetail": { - "noteCount": 55, - "notes": [ - { - "midi": 43, - "onset": 0.174, - "duration": 0.163 - }, - { - "midi": 44, - "onset": 0.395, - "duration": 0.125 - }, - { - "midi": 41, - "onset": 0.525, - "duration": 0.154 - }, - { - "midi": 43, - "onset": 0.752, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 0.859, - "duration": 0.168 - }, - { - "midi": 43, - "onset": 1.213, - "duration": 0.151 - }, - { - "midi": 44, - "onset": 1.37, - "duration": 0.183 - }, - { - "midi": 41, - "onset": 1.559, - "duration": 0.154 - }, - { - "midi": 43, - "onset": 1.785, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 1.892, - "duration": 0.197 - }, - { - "midi": 41, - "onset": 2.157, - "duration": 0.099 - }, - { - "midi": 43, - "onset": 2.261, - "duration": 0.145 - }, - { - "midi": 44, - "onset": 2.461, - "duration": 0.128 - }, - { - "midi": 41, - "onset": 2.595, - "duration": 0.16 - }, - { - "midi": 43, - "onset": 2.818, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 2.926, - "duration": 0.171 - }, - { - "midi": 44, - "onset": 3.495, - "duration": 0.128 - }, - { - "midi": 41, - "onset": 3.628, - "duration": 0.16 - }, - { - "midi": 43, - "onset": 3.852, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 3.959, - "duration": 0.2 - }, - { - "midi": 43, - "onset": 4.304, - "duration": 0.171 - }, - { - "midi": 44, - "onset": 4.531, - "duration": 0.125 - }, - { - "midi": 41, - "onset": 4.661, - "duration": 0.157 - }, - { - "midi": 42, - "onset": 4.824, - "duration": 0.093 - }, - { - "midi": 43, - "onset": 4.923, - "duration": 0.093 - }, - { - "midi": 44, - "onset": 5.021, - "duration": 0.174 - }, - { - "midi": 43, - "onset": 5.341, - "duration": 0.171 - }, - { - "midi": 44, - "onset": 5.564, - "duration": 0.128 - }, - { - "midi": 43, - "onset": 5.921, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 6.028, - "duration": 0.165 - }, - { - "midi": 42, - "onset": 6.2, - "duration": 0.093 - }, - { - "midi": 41, - "onset": 6.298, - "duration": 0.093 - }, - { - "midi": 43, - "onset": 6.397, - "duration": 0.148 - }, - { - "midi": 44, - "onset": 6.597, - "duration": 0.128 - }, - { - "midi": 43, - "onset": 6.954, - "duration": 0.102 - }, - { - "midi": 44, - "onset": 7.062, - "duration": 0.2 - }, - { - "midi": 42, - "onset": 7.364, - "duration": 0.099 - }, - { - "midi": 43, - "onset": 7.468, - "duration": 0.113 - }, - { - "midi": 44, - "onset": 7.631, - "duration": 0.131 - }, - { - "midi": 41, - "onset": 7.767, - "duration": 0.154 - }, - { - "midi": 41, - "onset": 7.927, - "duration": 0.093 - }, - { - "midi": 43, - "onset": 8.025, - "duration": 0.093 - }, - { - "midi": 44, - "onset": 8.124, - "duration": 0.171 - }, - { - "midi": 43, - "onset": 8.443, - "duration": 0.171 - }, - { - "midi": 44, - "onset": 8.661, - "duration": 0.134 - }, - { - "midi": 41, - "onset": 8.8, - "duration": 0.16 - }, - { - "midi": 43, - "onset": 9.021, - "duration": 0.104 - }, - { - "midi": 44, - "onset": 9.131, - "duration": 0.174 - }, - { - "midi": 44, - "onset": 9.694, - "duration": 0.136 - }, - { - "midi": 41, - "onset": 9.837, - "duration": 0.154 - }, - { - "midi": 42, - "onset": 9.996, - "duration": 0.093 - }, - { - "midi": 43, - "onset": 10.095, - "duration": 0.093 - }, - { - "midi": 44, - "onset": 10.194, - "duration": 0.174 - }, - { - "midi": 43, - "onset": 10.513, - "duration": 0.171 - }, - { - "midi": 43, - "onset": 10.731, - "duration": 0.206 - } - ], - "dominantNotes": [ - 43, - 44, - 41, - 42 - ], - "pitchRange": { - "min": 41, - "max": 44 - }, - "pitchConfidence": 0.0434, - "midiFile": "/Users/christiansmith/code/projects/asa/.claude/worktrees/goal-doc/apps/backend/tests/fixtures/recommendation_tracks/dnb_reese_174/audio_melody.mid", - "sourceSeparated": false, - "vibratoPresent": false, - "vibratoExtent": 0.0, - "vibratoRate": 0.0, - "vibratoConfidence": 0.0 - }, - "transcriptionDetail": null, - "pitchDetail": null, - "grooveDetail": { - "kickSwing": 0.0053, - "hihatSwing": 0.2576, - "kickAccent": [ - 0.2864, - 0.8264, - 0.303, - 0.3001, - 1.0083, - 0.3514, - 0.3468, - 1.0061, - 0.3449, - 0.2754, - 1.0108, - 0.3654, - 0.2783, - 0.9978, - 0.2806, - 0.2827 - ], - "hihatAccent": [ - 0.0626, - 0.0, - 0.0632, - 0.0638, - 0.0001, - 0.0322, - 0.0209, - 0.0, - 0.0207, - 0.0632, - 0.0, - 0.0307, - 0.0438, - 0.0, - 0.0433, - 0.0436 - ], - "perDrumSwing": { - "kick": 0.0053, - "snare": 0.2184, - "hihat": 0.2576 - } - }, - "beatsLoudness": { - "kickDominantRatio": 0.9454, - "midDominantRatio": 0.0001, - "highDominantRatio": 0.0348, - "patternBeatsPerBar": 4, - "lowBandAccentPattern": [ - 0.2938, - 0.9659, - 0.3277, - 1.0 - ], - "midBandAccentPattern": [ - 0.1729, - 1.0, - 0.3549, - 0.1782 - ], - "highBandAccentPattern": [ - 1.0, - 0.0008, - 0.7665, - 0.001 - ], - "overallAccentPattern": [ - 0.3519, - 0.966, - 0.3721, - 1.0 - ], - "accentPattern": [ - 0.3519, - 0.966, - 0.3721, - 1.0 - ], - "meanBeatLoudness": 0.6692, - "beatLoudnessVariation": 0.4767, - "beatCount": 21 - }, - "rhythmTimeline": { - "beatsPerBar": 4, - "stepsPerBeat": 4, - "availableBars": 5, - "selectionMethod": "representative_dsp_window", - "windows": [ - { - "bars": 5, - "startBar": 1, - "endBar": 5, - "lowBandSteps": [ - 0.4009, - 0.4449, - 0.4482, - 0.3331, - 0.8369, - 0.3743, - 0.261, - 0.5143, - 0.3644, - 0.4097, - 0.4194, - 0.3187, - 0.9787, - 0.3905, - 0.2755, - 0.5466, - 0.3807, - 0.4435, - 0.4236, - 0.3197, - 0.8591, - 0.3743, - 0.2619, - 0.5221, - 0.366, - 0.3834, - 0.4248, - 0.3229, - 0.9841, - 0.3899, - 0.2765, - 0.5536, - 0.3826, - 0.4174, - 0.4295, - 0.3242, - 0.8757, - 0.3743, - 0.262, - 0.5287, - 0.3674, - 0.3638, - 0.4304, - 0.3258, - 0.9776, - 0.3923, - 0.2783, - 0.5614, - 0.3837, - 0.4006, - 0.4356, - 0.3273, - 0.8886, - 0.3751, - 0.2632, - 0.5367, - 0.3687, - 0.3551, - 0.4356, - 0.3286, - 1.0, - 0.4093, - 0.2886, - 0.5612, - 0.3621, - 0.3987, - 0.4118, - 0.3145, - 0.8941, - 0.3745, - 0.2648, - 0.5448, - 0.3856, - 0.383, - 0.4439, - 0.3319, - 0.9198, - 0.3933, - 0.2758, - 0.5397 - ], - "midBandSteps": [ - 0.7013, - 0.8896, - 0.6326, - 0.8548, - 0.4919, - 0.7428, - 0.5882, - 0.7104, - 0.5943, - 0.7622, - 0.5867, - 0.6444, - 0.6201, - 0.7054, - 0.6516, - 0.7407, - 0.6216, - 0.817, - 0.5804, - 0.8272, - 0.4776, - 0.7687, - 0.6225, - 0.6707, - 0.6228, - 0.6984, - 0.6327, - 0.5941, - 0.664, - 0.6584, - 0.6714, - 0.693, - 0.5841, - 0.936, - 0.5929, - 0.7396, - 0.4777, - 0.7479, - 0.6465, - 0.6511, - 0.615, - 0.7449, - 0.6398, - 0.5593, - 0.6372, - 0.6373, - 0.7244, - 0.6634, - 0.6576, - 1.0, - 0.5831, - 0.7333, - 0.5879, - 0.6878, - 0.6732, - 0.6236, - 0.621, - 0.8635, - 0.6797, - 0.5453, - 0.7547, - 0.7543, - 0.6639, - 0.7774, - 0.4957, - 0.8831, - 0.5669, - 0.703, - 0.5942, - 0.7037, - 0.7085, - 0.6002, - 0.67, - 0.7531, - 0.5959, - 0.728, - 0.5957, - 0.6753, - 0.6424, - 0.7407 - ], - "highBandSteps": [ - 0.7521, - 0.0006, - 0.0338, - 0.0118, - 0.0002, - 1.0, - 0.0005, - 0.0009, - 0.495, - 0.0012, - 0.01, - 0.0302, - 0.0008, - 0.8429, - 0.0011, - 0.0005, - 0.5149, - 0.0015, - 0.0403, - 0.0112, - 0.0008, - 0.9543, - 0.0002, - 0.001, - 0.5491, - 0.0007, - 0.006, - 0.0386, - 0.0009, - 0.7894, - 0.0011, - 0.0008, - 0.5717, - 0.001, - 0.0279, - 0.0159, - 0.0004, - 0.9143, - 0.0, - 0.0011, - 0.6037, - 0.0007, - 0.0038, - 0.0483, - 0.0005, - 0.725, - 0.0009, - 0.0009, - 0.6243, - 0.0007, - 0.0191, - 0.0202, - 0.0002, - 0.8655, - 0.0003, - 0.0009, - 0.659, - 0.001, - 0.002, - 0.0596, - 0.0001, - 0.8152, - 0.0011, - 0.0013, - 0.4112, - 0.0016, - 0.0223, - 0.0194, - 0.0004, - 0.8092, - 0.0, - 0.0009, - 0.7079, - 0.0011, - 0.0099, - 0.0291, - 0.0008, - 0.9196, - 0.0001, - 0.0013 - ], - "overallSteps": [ - 0.4803, - 0.457, - 0.4578, - 0.3473, - 0.8338, - 0.4783, - 0.2695, - 0.5214, - 0.4179, - 0.4197, - 0.4263, - 0.3303, - 0.9762, - 0.4786, - 0.2852, - 0.5538, - 0.4363, - 0.4541, - 0.4331, - 0.3335, - 0.8555, - 0.4746, - 0.2712, - 0.5282, - 0.4252, - 0.3924, - 0.4321, - 0.3341, - 0.9825, - 0.472, - 0.2866, - 0.5597, - 0.4427, - 0.4309, - 0.438, - 0.3364, - 0.8717, - 0.4704, - 0.2717, - 0.5343, - 0.4314, - 0.3741, - 0.4376, - 0.3372, - 0.9754, - 0.4679, - 0.2896, - 0.5667, - 0.4503, - 0.4158, - 0.4429, - 0.3398, - 0.8868, - 0.4652, - 0.2736, - 0.5415, - 0.4381, - 0.3681, - 0.4435, - 0.3406, - 1.0, - 0.4956, - 0.2984, - 0.569, - 0.4056, - 0.4115, - 0.4195, - 0.3264, - 0.8924, - 0.4597, - 0.2758, - 0.5489, - 0.4604, - 0.3932, - 0.4505, - 0.345, - 0.9178, - 0.488, - 0.2852, - 0.5471 - ] - } - ] - }, - "sidechainDetail": { - "pumpingStrength": 0.1115, - "pumpingRegularity": 0.326, - "pumpingRate": null, - "pumpingConfidence": 0.2521, - "envelopeShape": [ - 0.76, - 0.829, - 0.796, - 0.72, - 1.0, - 0.778, - 0.663, - 0.815, - 0.761, - 0.798, - 0.817, - 0.712, - 0.929, - 0.802, - 0.656, - 0.813 - ], - "envelopeShape32": [ - 0.76, - 0.69, - 0.557, - 0.829, - 0.796, - 0.744, - 0.72, - 0.588, - 1.0, - 0.851, - 0.759, - 0.778, - 0.629, - 0.663, - 0.815, - 0.756, - 0.761, - 0.715, - 0.549, - 0.798, - 0.817, - 0.769, - 0.712, - 0.631, - 0.929, - 0.901, - 0.739, - 0.802, - 0.626, - 0.656, - 0.813, - 0.765 - ] - }, - "acidDetail": { - "isAcid": true, - "confidence": 0.66, - "resonanceLevel": 1.0, - "centroidOscillationHz": 28, - "bassRhythmDensity": 2.9 - }, - "reverbDetail": { - "rt60": null, - "isWet": false, - "tailEnergyRatio": null, - "measured": false, - "perBandRt60": null, - "preDelayMs": null - }, - "vocalDetail": { - "hasVocals": false, - "confidence": 0.35, - "vocalEnergyRatio": 0.05, - "formantStrength": 0.25, - "mfccLikelihood": 0.86, - "stemEnergyRatio": null, - "stemOtherCorrelation": null - }, - "supersawDetail": { - "isSupersaw": true, - "confidence": 0.85, - "voiceCount": 7, - "avgDetuneCents": 27.6, - "spectralComplexity": 6.8 - }, - "bassDetail": { - "averageDecayMs": 1000, - "type": "sustained", - "transientRatio": 0.2, - "fundamentalHz": 94, - "transientCount": 0, - "swingPercent": 0, - "grooveType": "straight" - }, - "kickDetail": { - "isDistorted": true, - "thd": 0.26, - "harmonicRatio": 0.85, - "fundamentalHz": 84, - "kickCount": 80 - }, - "genreDetail": { - "genre": "dub-techno", - "confidence": 0.9888, - "secondaryGenre": "hiphop", - "genreFamily": "techno", - "topScores": [ - { - "genre": "dub-techno", - "score": 0.9118 - }, - { - "genre": "hiphop", - "score": 0.8274 - }, - { - "genre": "ambient", - "score": 0.8134 - }, - { - "genre": "dubstep", - "score": 0.7568 - }, - { - "genre": "ambient-techno", - "score": 0.7341 - } - ] - }, - "effectsDetail": { - "gatingDetected": false, - "gatingRate": null, - "gatingRegularity": 0.0, - "gatingEventCount": 1 - }, - "synthesisCharacter": { - "inharmonicity": 0.0875, - "oddToEvenRatio": 1.5821 - }, - "danceability": { - "danceability": 4.7001, - "dfa": 0.2128 - }, - "structure": { - "segments": [ - { - "start": 0.0, - "end": 11.034, - "index": 0 - } - ], - "segmentCount": 1 - }, - "arrangementDetail": { - "noveltyCurve": [ - 0.0, - 0.0, - 0.0066, - 0.012, - 0.0944, - 0.2599, - 0.3082, - 0.1397, - 0.019, - 0.0241, - 0.0164, - 0.0164, - 0.0607, - 0.112, - 0.096, - 0.0675, - 0.0135, - 0.0284, - 0.0299, - 0.018, - 0.1754, - 0.2816, - 0.1807, - 0.0736, - 0.0104, - 0.0184, - 0.0211, - 0.0141, - 0.3129, - 0.4615, - 0.2348, - 0.0853, - 0.0157, - 0.0309, - 0.0484, - 0.0324, - 0.2632, - 0.3656, - 0.3167, - 0.1091, - 0.0116, - 0.0242, - 0.035, - 0.0225, - 0.2136, - 0.5492, - 0.517, - 0.1838, - 0.0383, - 0.0399, - 0.0151, - 0.0121, - 0.1386, - 0.3961, - 0.4345, - 0.1751, - 0.0278, - 0.0353, - 0.0215, - 0.0203, - 0.066, - 0.1553, - 0.1935, - 0.0967, - 0.0384, - 0.0671, - 0.0459, - 0.0213, - 0.0766, - 0.1426, - 0.0838, - 0.0154, - 0.032, - 0.0491, - 0.0571, - 0.0273, - 0.2778, - 0.4189, - 0.1596, - 0.0185, - 0.0142, - 0.0204, - 0.033, - 0.0243, - 0.3449, - 0.5805, - 0.2467, - 0.0147, - 0.0158, - 0.0239, - 0.029, - 0.0175, - 0.4514, - 0.7892, - 0.3494, - 0.1848, - 0.0319, - 0.0286, - 0.0205, - 0.019, - 0.3091, - 0.6102, - 0.3085, - 0.1681, - 0.0188, - 0.0244, - 0.0299, - 0.0289, - 0.1962, - 0.4326, - 0.3392, - 0.1513, - 0.0303, - 0.0477, - 0.029, - 0.0309, - 0.1174, - 0.1596, - 0.1684, - 0.0781, - 0.0214, - 0.0367, - 0.0295, - 0.1074, - 0.1488, - 0.2334, - 0.1103, - 0.0189, - 0.017, - 0.0446, - 0.0364, - 0.258, - 0.3407, - 0.509, - 0.1804, - 0.0164, - 0.0188, - 0.029, - 0.0225, - 0.0166, - 0.4091, - 0.6736, - 0.2772, - 0.0291, - 0.0238, - 0.0126, - 0.0113, - 0.0075, - 0.4035, - 0.7644, - 0.3674, - 0.0202, - 0.0292, - 0.0613, - 0.0616, - 0.0521, - 0.2265, - 0.4592, - 0.2417, - 0.0198, - 0.0299, - 0.0374, - 0.0315, - 0.0302, - 0.1155, - 0.2162, - 0.114, - 0.0181, - 0.0267, - 0.0453, - 0.0341, - 0.0686, - 0.1204, - 0.1267, - 0.0612, - 0.0294, - 0.0173, - 0.0465, - 0.0401, - 0.1109, - 0.1692, - 0.1093, - 0.0462, - 0.0154, - 0.0236, - 0.0478, - 0.0345, - 0.2592, - 0.4497, - 0.2961, - 0.2013, - 0.016, - 0.0137, - 0.0119, - 0.0098, - 0.3504, - 0.7823, - 1.0, - 0.4391, - 0.0235, - 0.0246, - 0.0379, - 0.0408, - 0.1396, - 0.3626, - 0.4924, - 0.252, - 0.019, - 0.0316, - 0.0416, - 0.0352, - 0.1321, - 0.2446, - 0.529, - 0.2925, - 0.0258, - 0.0381, - 0.0275, - 0.0204, - 0.0251, - 0.1317, - 0.2415, - 0.1258, - 0.0101, - 0.0294, - 0.0386, - 0.0371, - 0.0592, - 0.1193, - 0.1358, - 0.0687, - 0.0157, - 0.0322, - 0.0389, - 0.033, - 0.2093, - 0.3469, - 0.2245, - 0.0852, - 0.014, - 0.0224, - 0.0337, - 0.0246, - 0.3235, - 0.6486, - 0.4927, - 0.1669, - 0.0095, - 0.0142, - 0.0397, - 0.0338, - 0.1642, - 0.4653, - 0.4669, - 0.1659, - 0.0085 - ], - "noveltyPeaks": [ - { - "time": 1.95, - "strength": 0.7237 - }, - { - "time": 4.017, - "strength": 0.7892 - }, - { - "time": 6.432, - "strength": 0.7644 - }, - { - "time": 8.499, - "strength": 1.0 - }, - { - "time": 10.565, - "strength": 0.8102 - } - ], - "noveltyMean": 0.1386, - "noveltyStdDev": 0.1695 - }, - "segmentLoudness": [ - { - "segmentIndex": 0, - "start": 0.0, - "end": 11.034, - "lufs": -8.6, - "lra": 0.1 - } - ], - "segmentSpectral": [ - { - "segmentIndex": 0, - "barkBands": [ - -22.6535, - -6.7653, - -10.4344, - -21.3404, - -29.1583, - -27.4838, - -26.114, - -26.4434, - -29.9769, - -33.1218, - -33.2718, - -31.6134, - -36.8803, - -35.7062, - -37.3194, - -38.5859, - -38.7144, - -38.9772, - -39.8857, - -39.3104, - -37.9193, - -34.6128, - -34.7568, - -30.7898 - ], - "spectralCentroid": 1304.3, - "spectralRolloff": 1525.4, - "stereoWidth": 0.0, - "stereoCorrelation": 1.0 - } - ], - "segmentStereo": [ - { - "segmentIndex": 0, - "stereoWidth": 0.0, - "stereoCorrelation": 1.0 - } - ], - "segmentKey": [ - { - "segmentIndex": 0, - "key": "F Minor", - "keyConfidence": 0.59 - } - ], - "chordDetail": { - "chordSequence": [ - "Eb", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "Fm", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "Fm", - "C#", - "C#", - "C#", - "C#" - ], - "chordStrength": 0.6337, - "progression": [ - "Eb", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#" - ], - "dominantChords": [ - "C#", - "Fm", - "Eb" - ], - "chordTimeline": [ - { - "startSec": 0.0, - "endSec": 11.053, - "label": "Fm", - "labelLong": "F minor", - "confidence": 0.5474 - } - ], - "chordChangeCount": 0, - "chordTimelineSource": "librosa_viterbi", - "chordTimelineAgreement": false - }, - "perceptual": { - "sharpness": 0.0792, - "roughness": 0.0 - }, - "essentiaFeatures": { - "zeroCrossingRate": 0.043, - "hfc": 225.4467, - "spectralComplexity": 11.4738, - "dissonance": 0.0 - } -} \ No newline at end of file diff --git a/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/README.md b/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/README.md new file mode 100644 index 00000000..929ea449 --- /dev/null +++ b/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/README.md @@ -0,0 +1,39 @@ +# hard_techno_rumble_145 + +**Genre:** hard techno · **Tempo:** 145 BPM · **Key:** F minor · **Render:** 48 kHz / 24-bit, 16 s loop + +## What this fixture isolates + +A driving hard-techno loop — the rumble technique pushed harder and faster: + +1. **Distorted sine kick** — `Operator` sine kick into `Drum Buss` *on the kick + chain itself* (Drive 8, Boom Frequency 45 Hz). Recovering it means + recommending kick-channel distortion, not just "a saturator somewhere." +2. **The rumble** — the kick fed through `Reverb` (2.5 s decay, 35 % wet) on a + return, summed with a held sub `Operator`. The defining techno low-end move, + here at peak-time tempo. +3. **Dark bandpass stab** (`Wavetable`, BP @ 1.5 kHz, no sustain) for the mid. +4. **Driving offbeat hats** (`Drum Rack`) and a **mono'd low end** (`Utility` + Bass Mono). +5. **Master glue** (`Glue Compressor` 4:1) into `Limiter` (-0.3 dBFS). + +`measurableIntent` targets the Drum Buss boom (kick fundamental ~45 Hz) and a +brighter `spectralDetail.spectralCentroidMean` than dark techno — distortion +adds harmonics — so an equivalent reconstruction by a different device path +still earns credit. + +Covers all seven production domains. **This is the pilot fixture** — smallest +spec; build and render this one first per +`plans/owner-actions-recommendation-proof-plan.md` §3b. + +## Answer key + +The exact device chain is in `manifest.json` → `deviceSpec`. Build it verbatim in +Live 12 (checklist in `../README.md`), render to `audio.flac`, then store +`phase1_fingerprint.json`. + +## Status + +- [x] Catalog-valid spec authored +- [ ] **NEEDS-FIXTURE:** rendered `audio.flac` (owner builds + renders) +- [ ] **NEEDS-FIXTURE:** stored `phase1_fingerprint.json` diff --git a/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/audio_melody.mid b/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/audio_melody.mid new file mode 100644 index 0000000000000000000000000000000000000000..df1032b90dd78156a5870a3ff9cd6a121f99c277 GIT binary patch literal 353 zcmeYb$w*;fU|?fl1i}R0kfLk`21aj&|AEYG0f`I~WD^V~Xhx|tXgb(T&;imqK-vIE t8#vfB$T~1U<#@pA(BxtI!0I4!5Oq-fFnO4{V092Vh`OO~FT;O*1^{WPVrl>Y literal 0 HcmV?d00001 diff --git a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/manifest.json b/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/manifest.json similarity index 51% rename from apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/manifest.json rename to apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/manifest.json index 7424d092..9ffefac1 100644 --- a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/manifest.json +++ b/apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/manifest.json @@ -1,8 +1,8 @@ { "schemaVersion": "recommendation-fixture.v1", - "id": "techno_rumble_130", - "title": "Techno — sine kick, reverb-tail rumble bass, saturated", - "genre": "techno", + "id": "hard_techno_rumble_145", + "title": "Hard techno — distorted sine kick, rumble return, driving offbeat", + "genre": "hard techno", "audioPath": "audio.flac", "phase1FingerprintPath": "phase1_fingerprint.json", "phase1Fingerprint": null, @@ -10,9 +10,9 @@ "sampleRateHz": 48000, "bitDepth": 24, "lengthSeconds": 16, - "bpm": 130, + "bpm": 145, "key": "F minor", - "notes": "8-bar loop. The 'rumble' is the kick fed through Reverb on a return, summed with the sub. Render the full loop to one stereo file, no export normalization." + "notes": "8-bar loop (~13.2 s) plus tail. The 'rumble' is the kick fed through Reverb on a return, summed with the held sub. Drum Buss sits directly on the kick chain for the distorted hard-techno character. Render the full loop to one stereo file, no export normalization." }, "deviceSpec": { "kick": [ @@ -23,9 +23,21 @@ "parameters": [ { "name": "Oscillator A Waveform", "value": "Sine" }, { "name": "Filter Type", "value": "Lowpass" }, - { "name": "Filter Frequency", "value": "120 Hz" }, - { "name": "Amp Envelope Decay", "value": "300 ms" }, - { "name": "Amp Envelope Release", "value": "80 ms" } + { "name": "Filter Frequency", "value": "150 Hz" }, + { "name": "Amp Envelope Decay", "value": "250 ms", "intent": "tighter than 130-BPM techno; leaves room at 145" }, + { "name": "Amp Envelope Release", "value": "50 ms" } + ] + }, + { + "device": "Drum Buss", + "family": "NATIVE", + "role": "Kick distortion + sub boom", + "parameters": [ + { "name": "Drive", "value": "8", "intent": "hard-techno saturation (unitless 0-10 knob)" }, + { "name": "Crunch", "value": "30%" }, + { "name": "Boom Frequency", "value": "45 Hz", "intent": "pins the kick fundamental the measurableIntent targets" }, + { "name": "Boom", "value": "4" }, + { "name": "Dry/Wet", "value": "100%" } ] } ], @@ -33,11 +45,11 @@ { "device": "Operator", "family": "NATIVE", - "role": "Sub bass", + "role": "Held rumble sub", "parameters": [ { "name": "Oscillator A Waveform", "value": "Sine" }, - { "name": "Filter Frequency", "value": "150 Hz" }, - { "name": "Amp Envelope Sustain", "value": "100%", "intent": "held sub, no decay" } + { "name": "Filter Frequency", "value": "120 Hz" }, + { "name": "Amp Envelope Sustain", "value": "100%", "intent": "held sub under the reverb tail" } ] } ], @@ -45,13 +57,13 @@ { "device": "Wavetable", "family": "NATIVE", - "role": "Bandpass stab", + "role": "Dark offbeat stab", "parameters": [ { "name": "Oscillator 1 Wavetable", "value": "Basic Shapes" }, { "name": "Filter Type", "value": "Bandpass" }, - { "name": "Filter Frequency", "value": "1200 Hz" }, - { "name": "Amp Envelope Decay", "value": "200 ms" }, - { "name": "Amp Envelope Sustain", "value": "0%", "intent": "plucky stab, no sustain" } + { "name": "Filter Frequency", "value": "1500 Hz" }, + { "name": "Amp Envelope Decay", "value": "150 ms" }, + { "name": "Amp Envelope Sustain", "value": "0%", "intent": "stab, no sustain" } ] } ], @@ -59,30 +71,20 @@ { "device": "Drum Rack", "family": "NATIVE", - "role": "Offbeat hats", + "role": "Driving offbeat closed hats", "parameters": [ - { "name": "Pad Volume", "value": "-10 dB" } + { "name": "Pad Volume", "value": "-8 dB" } ] } ], "fx": [ - { - "device": "Saturator", - "family": "NATIVE", - "role": "Bass drive", - "parameters": [ - { "name": "Drive", "value": "8 dB" }, - { "name": "Type", "value": "Soft Sine" }, - { "name": "Dry/Wet", "value": "100%" } - ] - }, { "device": "Reverb", "family": "NATIVE", "role": "Rumble tail on kick return", "parameters": [ - { "name": "Decay Time", "value": "3.0 s" }, - { "name": "Dry/Wet", "value": "40%" } + { "name": "Decay Time", "value": "2.5 s" }, + { "name": "Dry/Wet", "value": "35%" } ] } ], @@ -93,7 +95,7 @@ "role": "Low-end mono / stab width", "parameters": [ { "name": "Bass Mono", "value": "On" }, - { "name": "Stereo Width", "value": "110%" } + { "name": "Stereo Width", "value": "105%" } ] } ], @@ -103,11 +105,11 @@ "family": "NATIVE", "role": "Bus glue", "parameters": [ - { "name": "Threshold", "value": "-16 dB" }, + { "name": "Threshold", "value": "-18 dB" }, { "name": "Ratio", "value": "4:1" }, - { "name": "Attack", "value": "3 ms" }, + { "name": "Attack", "value": "1 ms" }, { "name": "Release", "value": "Auto" }, - { "name": "Makeup", "value": "2 dB" } + { "name": "Makeup", "value": "3 dB" } ] }, { @@ -121,11 +123,11 @@ ] }, "measurableIntent": { - "bpm": { "target": 130, "tolerance": 1, "direction": "exact" }, + "bpm": { "target": 145, "tolerance": 1, "direction": "exact" }, "key": { "target": "F minor" }, - "kickDetail.fundamentalHz": { "target": 50, "tolerance": 15, "unit": "Hz" }, - "lufsIntegrated": { "target": -8, "tolerance": 2, "unit": "LUFS" }, - "truePeak": { "target": 1.0, "tolerance": 0.05 }, - "spectralDetail.spectralCentroidMean": { "target": 1500, "tolerance": 600, "unit": "Hz", "intent": "dark techno spectrum" } + "kickDetail.fundamentalHz": { "target": 45, "tolerance": 15, "unit": "Hz", "intent": "Drum Buss Boom Frequency 45 Hz" }, + "lufsIntegrated": { "target": -7, "tolerance": 2, "unit": "LUFS", "intent": "hard techno slams" }, + "truePeak": { "target": -0.3, "tolerance": 0.4, "unit": "dBTP", "intent": "Limiter ceiling -0.3 dB; dBTP per ADR 0002 (phase1.v2)" }, + "spectralDetail.spectralCentroidMean": { "target": 2200, "tolerance": 700, "unit": "Hz", "intent": "brighter than dark techno — distortion adds harmonics" } } } diff --git a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/README.md b/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/README.md deleted file mode 100644 index 35699def..00000000 --- a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# techno_rumble_130 - -**Genre:** techno · **Tempo:** 130 BPM · **Key:** F minor · **Render:** 48 kHz / 24-bit, 16 s loop - -## What this fixture isolates - -A dark, driving techno loop centered on the "rumble" technique: - -1. **Sine kick** with a lowpassed body (`Operator`, Filter 120 Hz, 300 ms decay). -2. **The rumble** — the kick fed through a long `Reverb` (3.0 s decay, 40 % wet) - on a return, summed with a held sub `Operator`. This is the defining techno - low-end move; recovering it means recommending a reverb-on-kick send plus a - sustained sub, not just "a bass." -3. **Bandpass stab** (`Wavetable`, BP @ 1.2 kHz, no sustain) for the mid. -4. **Saturated bass drive** (`Saturator`, 8 dB) and a **mono'd low end** - (`Utility` Bass Mono) — the kind of stereo discipline techno needs. -5. **Master glue** (`Glue Compressor` 4:1) into `Limiter` (-0.3 dBFS). - -`measurableIntent` records a dark `spectralDetail.spectralCentroidMean` target so -a recommendation that achieves the right tonal balance by a different device path -still earns credit. - -Covers all seven production domains. - -## Answer key - -The exact device chain is in `manifest.json` → `deviceSpec`. Build it verbatim in -Live 12, render to `audio.flac`, then store `phase1_fingerprint.json`. - -## Status - -- [x] Catalog-valid spec authored -- [ ] **NEEDS-FIXTURE:** rendered `audio.flac` (owner builds + renders) -- [ ] **NEEDS-FIXTURE:** stored `phase1_fingerprint.json` diff --git a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/audio_melody.mid b/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/audio_melody.mid deleted file mode 100644 index bc392c282f10b7aec16bcdd423445f3fa3491e63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 457 zcmaKpJqp4=5QS$WD5#0xPp6;>{zgp#5>JsrNVSzk3Q@4|1UbUq#XEV8_(oH>Tze!MsPP0{Chyw4RUzl$U9V&~aBiRABykJWX-eevPk|8#-m VVO@%QE7U#s1*rLd%#n+#h~8_Ka4P@+ diff --git a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/phase1_fingerprint.json b/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/phase1_fingerprint.json deleted file mode 100644 index c9237904..00000000 --- a/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/phase1_fingerprint.json +++ /dev/null @@ -1,4729 +0,0 @@ -{ - "_synthetic": true, - "_note": "Phase 1 of a numpy PROXY render, NOT an Ableton render. Replace with a real Live render's fingerprint for an authoritative verdict.", - "bpm": 130.0, - "bpmConfidence": 3.71, - "bpmPercival": 130.0, - "bpmAgreement": true, - "bpmDoubletime": false, - "bpmSource": "rhythm_extractor_confirmed", - "bpmRawOriginal": 130.0, - "key": "F Minor", - "keyConfidence": 0.59, - "keyProfile": "edma", - "tuningFrequency": 432.44, - "tuningCents": -30.0, - "timeSignature": "4/4", - "timeSignatureSource": "onset_autocorrelation_low_margin", - "timeSignatureConfidence": 0.0, - "durationSeconds": 14.8, - "sampleRate": 44100, - "lufsIntegrated": -9.0, - "lufsRange": 0.1, - "lufsMomentaryMax": -8.3, - "lufsShortTermMax": -8.9, - "lufsCurve": { - "shortTerm": [ - { - "t": 0.0, - "lufs": -8.9 - }, - { - "t": 0.1, - "lufs": -9.0 - }, - { - "t": 0.2, - "lufs": -9.0 - }, - { - "t": 0.3, - "lufs": -9.1 - }, - { - "t": 0.4, - "lufs": -9.1 - }, - { - "t": 0.5, - "lufs": -9.0 - }, - { - "t": 0.6, - "lufs": -9.0 - }, - { - "t": 0.7, - "lufs": -9.0 - }, - { - "t": 0.8, - "lufs": -9.1 - }, - { - "t": 0.9, - "lufs": -9.0 - }, - { - "t": 1.0, - "lufs": -9.0 - }, - { - "t": 1.1, - "lufs": -9.0 - }, - { - "t": 1.2, - "lufs": -9.0 - }, - { - "t": 1.3, - "lufs": -9.0 - }, - { - "t": 1.4, - "lufs": -8.9 - }, - { - "t": 1.5, - "lufs": -9.0 - }, - { - "t": 1.6, - "lufs": -9.0 - }, - { - "t": 1.7, - "lufs": -9.1 - }, - { - "t": 1.8, - "lufs": -9.1 - }, - { - "t": 1.9, - "lufs": -9.0 - }, - { - "t": 2.0, - "lufs": -9.0 - }, - { - "t": 2.1, - "lufs": -9.1 - }, - { - "t": 2.2, - "lufs": -9.1 - }, - { - "t": 2.3, - "lufs": -9.0 - }, - { - "t": 2.4, - "lufs": -9.0 - }, - { - "t": 2.5, - "lufs": -9.0 - }, - { - "t": 2.6, - "lufs": -9.0 - }, - { - "t": 2.7, - "lufs": -9.0 - }, - { - "t": 2.8, - "lufs": -8.9 - }, - { - "t": 2.9, - "lufs": -9.0 - }, - { - "t": 3.0, - "lufs": -9.0 - }, - { - "t": 3.1, - "lufs": -9.1 - }, - { - "t": 3.2, - "lufs": -9.1 - }, - { - "t": 3.3, - "lufs": -9.0 - }, - { - "t": 3.4, - "lufs": -9.0 - }, - { - "t": 3.5, - "lufs": -9.1 - }, - { - "t": 3.6, - "lufs": -9.1 - }, - { - "t": 3.7, - "lufs": -9.0 - }, - { - "t": 3.8, - "lufs": -9.0 - }, - { - "t": 3.9, - "lufs": -9.0 - }, - { - "t": 4.0, - "lufs": -9.0 - }, - { - "t": 4.1, - "lufs": -9.0 - }, - { - "t": 4.2, - "lufs": -9.0 - }, - { - "t": 4.3, - "lufs": -9.0 - }, - { - "t": 4.4, - "lufs": -9.1 - }, - { - "t": 4.5, - "lufs": -9.1 - }, - { - "t": 4.6, - "lufs": -9.1 - }, - { - "t": 4.7, - "lufs": -9.0 - }, - { - "t": 4.8, - "lufs": -9.0 - }, - { - "t": 4.9, - "lufs": -9.1 - }, - { - "t": 5.0, - "lufs": -9.1 - }, - { - "t": 5.1, - "lufs": -9.0 - }, - { - "t": 5.2, - "lufs": -9.0 - }, - { - "t": 5.3, - "lufs": -9.0 - }, - { - "t": 5.4, - "lufs": -9.0 - }, - { - "t": 5.5, - "lufs": -9.0 - }, - { - "t": 5.6, - "lufs": -9.0 - }, - { - "t": 5.7, - "lufs": -9.0 - }, - { - "t": 5.8, - "lufs": -9.1 - }, - { - "t": 5.9, - "lufs": -9.1 - }, - { - "t": 6.0, - "lufs": -9.0 - }, - { - "t": 6.1, - "lufs": -9.0 - }, - { - "t": 6.2, - "lufs": -9.0 - }, - { - "t": 6.3, - "lufs": -9.1 - }, - { - "t": 6.4, - "lufs": -9.1 - }, - { - "t": 6.5, - "lufs": -9.0 - }, - { - "t": 6.6, - "lufs": -9.0 - }, - { - "t": 6.7, - "lufs": -9.1 - }, - { - "t": 6.8, - "lufs": -9.0 - }, - { - "t": 6.9, - "lufs": -9.0 - }, - { - "t": 7.0, - "lufs": -9.0 - }, - { - "t": 7.1, - "lufs": -9.0 - }, - { - "t": 7.2, - "lufs": -9.1 - }, - { - "t": 7.3, - "lufs": -9.1 - }, - { - "t": 7.4, - "lufs": -9.0 - }, - { - "t": 7.5, - "lufs": -9.0 - }, - { - "t": 7.6, - "lufs": -9.1 - }, - { - "t": 7.7, - "lufs": -9.1 - }, - { - "t": 7.8, - "lufs": -9.1 - }, - { - "t": 7.9, - "lufs": -9.0 - }, - { - "t": 8.0, - "lufs": -9.0 - }, - { - "t": 8.1, - "lufs": -9.1 - }, - { - "t": 8.2, - "lufs": -9.0 - }, - { - "t": 8.3, - "lufs": -9.0 - }, - { - "t": 8.4, - "lufs": -9.0 - }, - { - "t": 8.5, - "lufs": -9.0 - }, - { - "t": 8.6, - "lufs": -9.1 - }, - { - "t": 8.7, - "lufs": -9.1 - }, - { - "t": 8.8, - "lufs": -9.0 - }, - { - "t": 8.9, - "lufs": -9.0 - }, - { - "t": 9.0, - "lufs": -9.1 - }, - { - "t": 9.1, - "lufs": -9.1 - }, - { - "t": 9.2, - "lufs": -9.1 - }, - { - "t": 9.3, - "lufs": -9.0 - }, - { - "t": 9.4, - "lufs": -9.0 - }, - { - "t": 9.5, - "lufs": -9.1 - }, - { - "t": 9.6, - "lufs": -9.0 - }, - { - "t": 9.7, - "lufs": -9.0 - }, - { - "t": 9.8, - "lufs": -9.0 - }, - { - "t": 9.9, - "lufs": -9.0 - }, - { - "t": 10.0, - "lufs": -9.1 - }, - { - "t": 10.1, - "lufs": -9.1 - }, - { - "t": 10.2, - "lufs": -9.0 - }, - { - "t": 10.3, - "lufs": -9.0 - }, - { - "t": 10.4, - "lufs": -9.1 - }, - { - "t": 10.5, - "lufs": -9.1 - }, - { - "t": 10.6, - "lufs": -9.1 - }, - { - "t": 10.7, - "lufs": -9.0 - }, - { - "t": 10.8, - "lufs": -9.0 - }, - { - "t": 10.9, - "lufs": -9.0 - }, - { - "t": 11.0, - "lufs": -9.0 - }, - { - "t": 11.1, - "lufs": -8.9 - }, - { - "t": 11.2, - "lufs": -9.0 - }, - { - "t": 11.3, - "lufs": -9.1 - }, - { - "t": 11.4, - "lufs": -9.1 - }, - { - "t": 11.5, - "lufs": -9.1 - }, - { - "t": 11.6, - "lufs": -9.0 - }, - { - "t": 11.7, - "lufs": -9.0 - }, - { - "t": 11.8, - "lufs": -9.2 - } - ], - "momentary": [ - { - "t": 0.0, - "lufs": -8.3 - }, - { - "t": 0.1, - "lufs": -9.6 - }, - { - "t": 0.2, - "lufs": -9.2 - }, - { - "t": 0.3, - "lufs": -9.7 - }, - { - "t": 0.4, - "lufs": -9.1 - }, - { - "t": 0.5, - "lufs": -9.2 - }, - { - "t": 0.6, - "lufs": -9.1 - }, - { - "t": 0.7, - "lufs": -9.2 - }, - { - "t": 0.8, - "lufs": -9.0 - }, - { - "t": 0.9, - "lufs": -8.8 - }, - { - "t": 1.0, - "lufs": -9.3 - }, - { - "t": 1.1, - "lufs": -8.5 - }, - { - "t": 1.2, - "lufs": -9.1 - }, - { - "t": 1.3, - "lufs": -8.4 - }, - { - "t": 1.4, - "lufs": -8.5 - }, - { - "t": 1.5, - "lufs": -9.5 - }, - { - "t": 1.6, - "lufs": -9.1 - }, - { - "t": 1.7, - "lufs": -9.4 - }, - { - "t": 1.8, - "lufs": -9.0 - }, - { - "t": 1.9, - "lufs": -9.3 - }, - { - "t": 2.0, - "lufs": -9.1 - }, - { - "t": 2.1, - "lufs": -9.5 - }, - { - "t": 2.2, - "lufs": -9.0 - }, - { - "t": 2.3, - "lufs": -8.8 - }, - { - "t": 2.4, - "lufs": -9.0 - }, - { - "t": 2.5, - "lufs": -8.4 - }, - { - "t": 2.6, - "lufs": -9.2 - }, - { - "t": 2.7, - "lufs": -8.4 - }, - { - "t": 2.8, - "lufs": -8.9 - }, - { - "t": 2.9, - "lufs": -9.4 - }, - { - "t": 3.0, - "lufs": -9.1 - }, - { - "t": 3.1, - "lufs": -9.3 - }, - { - "t": 3.2, - "lufs": -9.1 - }, - { - "t": 3.3, - "lufs": -9.3 - }, - { - "t": 3.4, - "lufs": -9.1 - }, - { - "t": 3.5, - "lufs": -9.7 - }, - { - "t": 3.6, - "lufs": -8.9 - }, - { - "t": 3.7, - "lufs": -8.9 - }, - { - "t": 3.8, - "lufs": -8.7 - }, - { - "t": 3.9, - "lufs": -8.4 - }, - { - "t": 4.0, - "lufs": -8.8 - }, - { - "t": 4.1, - "lufs": -8.4 - }, - { - "t": 4.2, - "lufs": -9.2 - }, - { - "t": 4.3, - "lufs": -9.3 - }, - { - "t": 4.4, - "lufs": -9.5 - }, - { - "t": 4.5, - "lufs": -9.1 - }, - { - "t": 4.6, - "lufs": -9.0 - }, - { - "t": 4.7, - "lufs": -9.4 - }, - { - "t": 4.8, - "lufs": -9.1 - }, - { - "t": 4.9, - "lufs": -9.9 - }, - { - "t": 5.0, - "lufs": -9.0 - }, - { - "t": 5.1, - "lufs": -9.0 - }, - { - "t": 5.2, - "lufs": -8.5 - }, - { - "t": 5.3, - "lufs": -8.4 - }, - { - "t": 5.4, - "lufs": -8.6 - }, - { - "t": 5.5, - "lufs": -8.4 - }, - { - "t": 5.6, - "lufs": -9.6 - }, - { - "t": 5.7, - "lufs": -9.2 - }, - { - "t": 5.8, - "lufs": -9.7 - }, - { - "t": 5.9, - "lufs": -9.1 - }, - { - "t": 6.0, - "lufs": -9.0 - }, - { - "t": 6.1, - "lufs": -9.3 - }, - { - "t": 6.2, - "lufs": -9.0 - }, - { - "t": 6.3, - "lufs": -9.7 - }, - { - "t": 6.4, - "lufs": -8.9 - }, - { - "t": 6.5, - "lufs": -9.3 - }, - { - "t": 6.6, - "lufs": -8.6 - }, - { - "t": 6.7, - "lufs": -8.5 - }, - { - "t": 6.8, - "lufs": -8.5 - }, - { - "t": 6.9, - "lufs": -8.3 - }, - { - "t": 7.0, - "lufs": -9.3 - }, - { - "t": 7.1, - "lufs": -9.2 - }, - { - "t": 7.2, - "lufs": -9.9 - }, - { - "t": 7.3, - "lufs": -9.1 - }, - { - "t": 7.4, - "lufs": -9.2 - }, - { - "t": 7.5, - "lufs": -9.4 - }, - { - "t": 7.6, - "lufs": -9.1 - }, - { - "t": 7.7, - "lufs": -9.3 - }, - { - "t": 7.8, - "lufs": -8.9 - }, - { - "t": 7.9, - "lufs": -9.2 - }, - { - "t": 8.0, - "lufs": -8.5 - }, - { - "t": 8.1, - "lufs": -8.9 - }, - { - "t": 8.2, - "lufs": -8.4 - }, - { - "t": 8.3, - "lufs": -8.3 - }, - { - "t": 8.4, - "lufs": -9.5 - }, - { - "t": 8.5, - "lufs": -9.1 - }, - { - "t": 8.6, - "lufs": -10.0 - }, - { - "t": 8.7, - "lufs": -9.1 - }, - { - "t": 8.8, - "lufs": -9.1 - }, - { - "t": 8.9, - "lufs": -9.2 - }, - { - "t": 9.0, - "lufs": -9.1 - }, - { - "t": 9.1, - "lufs": -9.2 - }, - { - "t": 9.2, - "lufs": -8.9 - }, - { - "t": 9.3, - "lufs": -9.4 - }, - { - "t": 9.4, - "lufs": -8.5 - }, - { - "t": 9.5, - "lufs": -9.1 - }, - { - "t": 9.6, - "lufs": -8.4 - }, - { - "t": 9.7, - "lufs": -8.5 - }, - { - "t": 9.8, - "lufs": -9.5 - }, - { - "t": 9.9, - "lufs": -9.1 - }, - { - "t": 10.0, - "lufs": -9.5 - }, - { - "t": 10.1, - "lufs": -9.1 - }, - { - "t": 10.2, - "lufs": -9.2 - }, - { - "t": 10.3, - "lufs": -9.3 - }, - { - "t": 10.4, - "lufs": -9.5 - }, - { - "t": 10.5, - "lufs": -9.1 - }, - { - "t": 10.6, - "lufs": -8.9 - }, - { - "t": 10.7, - "lufs": -9.1 - }, - { - "t": 10.8, - "lufs": -8.4 - }, - { - "t": 10.9, - "lufs": -9.2 - }, - { - "t": 11.0, - "lufs": -8.4 - }, - { - "t": 11.1, - "lufs": -8.7 - }, - { - "t": 11.2, - "lufs": -9.3 - }, - { - "t": 11.3, - "lufs": -9.0 - }, - { - "t": 11.4, - "lufs": -9.2 - }, - { - "t": 11.5, - "lufs": -9.0 - }, - { - "t": 11.6, - "lufs": -9.5 - }, - { - "t": 11.7, - "lufs": -9.2 - }, - { - "t": 11.8, - "lufs": -9.8 - }, - { - "t": 11.9, - "lufs": -9.0 - }, - { - "t": 12.0, - "lufs": -8.9 - }, - { - "t": 12.1, - "lufs": -8.9 - }, - { - "t": 12.2, - "lufs": -8.5 - }, - { - "t": 12.3, - "lufs": -9.0 - }, - { - "t": 12.4, - "lufs": -8.5 - }, - { - "t": 12.5, - "lufs": -9.1 - }, - { - "t": 12.6, - "lufs": -9.2 - }, - { - "t": 12.7, - "lufs": -9.2 - }, - { - "t": 12.8, - "lufs": -9.1 - }, - { - "t": 12.9, - "lufs": -8.9 - }, - { - "t": 13.0, - "lufs": -9.3 - }, - { - "t": 13.1, - "lufs": -9.2 - }, - { - "t": 13.2, - "lufs": -9.9 - }, - { - "t": 13.3, - "lufs": -9.1 - }, - { - "t": 13.4, - "lufs": -9.1 - }, - { - "t": 13.5, - "lufs": -8.6 - }, - { - "t": 13.6, - "lufs": -8.4 - }, - { - "t": 13.7, - "lufs": -8.7 - }, - { - "t": 13.8, - "lufs": -8.4 - }, - { - "t": 13.9, - "lufs": -9.5 - }, - { - "t": 14.0, - "lufs": -9.2 - }, - { - "t": 14.1, - "lufs": -9.5 - }, - { - "t": 14.2, - "lufs": -9.1 - }, - { - "t": 14.3, - "lufs": -8.9 - }, - { - "t": 14.4, - "lufs": -9.9 - } - ] - }, - "truePeak": 1.0, - "plr": 10.0, - "crestFactor": 10.0, - "dynamicSpread": 30.86, - "dynamicCharacter": { - "dynamicComplexity": 1.3119, - "loudnessDb": -17.3562, - "loudnessVariation": -17.3562, - "spectralFlatness": 0.1075, - "logAttackTime": -3.9723, - "attackTimeStdDev": 0.0109 - }, - "textureCharacter": { - "textureScore": 0.3283, - "lowBandFlatness": 0.3501, - "midBandFlatness": 0.3357, - "highBandFlatness": 0.3217, - "inharmonicity": 0.0781 - }, - "stereoDetail": { - "stereoWidth": 0.0, - "stereoCorrelation": 1.0, - "subBassCorrelation": 1.0, - "subBassMono": true, - "correlationCurve": [ - { - "t": 0.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 1.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 2.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 3.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 4.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 5.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 6.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 7.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 8.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 9.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 10.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 11.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 12.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 13.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 14.5, - "full": 1.0, - "sub": 1.0 - }, - { - "t": 15.5, - "full": 1.0, - "sub": 1.0 - } - ], - "bandCorrelations": { - "subBass": 1.0, - "lowBass": 1.0, - "lowMids": 1.0, - "mids": 1.0, - "upperMids": 1.0, - "highs": 1.0, - "brilliance": 1.0 - } - }, - "monoCompatible": true, - "spectralBalance": { - "subBass": -7.4, - "lowBass": -7.9, - "lowMids": -24.3, - "mids": -24.0, - "upperMids": -34.9, - "highs": -29.1, - "brilliance": -21.4 - }, - "spectralBalanceTimeSeries": [ - { - "t": 0.03, - "subBass": -4.6, - "lowBass": -4.3, - "lowMids": -22.0, - "mids": -26.4, - "upperMids": -39.3, - "highs": -55.6, - "brilliance": -79.6 - }, - { - "t": 0.13, - "subBass": -4.5, - "lowBass": -8.9, - "lowMids": -22.3, - "mids": -26.8, - "upperMids": -38.9, - "highs": -55.1, - "brilliance": -73.4 - }, - { - "t": 0.22, - "subBass": -8.0, - "lowBass": -9.3, - "lowMids": -26.3, - "mids": -22.8, - "upperMids": -30.2, - "highs": -22.5, - "brilliance": -14.7 - }, - { - "t": 0.31, - "subBass": -8.5, - "lowBass": -7.7, - "lowMids": -41.5, - "mids": -22.3, - "upperMids": -35.5, - "highs": -30.8, - "brilliance": -23.2 - }, - { - "t": 0.41, - "subBass": -11.5, - "lowBass": -9.0, - "lowMids": -40.0, - "mids": -22.0, - "upperMids": -38.2, - "highs": -49.1, - "brilliance": -56.2 - }, - { - "t": 0.5, - "subBass": -6.5, - "lowBass": -6.5, - "lowMids": -21.0, - "mids": -25.7, - "upperMids": -38.1, - "highs": -50.8, - "brilliance": -53.1 - }, - { - "t": 0.59, - "subBass": -6.4, - "lowBass": -8.6, - "lowMids": -21.7, - "mids": -26.8, - "upperMids": -39.7, - "highs": -70.7, - "brilliance": -73.7 - }, - { - "t": 0.68, - "subBass": -8.1, - "lowBass": -8.9, - "lowMids": -27.8, - "mids": -22.4, - "upperMids": -29.9, - "highs": -22.1, - "brilliance": -14.5 - }, - { - "t": 0.78, - "subBass": -6.9, - "lowBass": -8.4, - "lowMids": -44.3, - "mids": -22.3, - "upperMids": -35.9, - "highs": -31.6, - "brilliance": -24.1 - }, - { - "t": 0.87, - "subBass": -8.8, - "lowBass": -9.4, - "lowMids": -40.5, - "mids": -22.4, - "upperMids": -38.5, - "highs": -49.1, - "brilliance": -56.0 - }, - { - "t": 0.96, - "subBass": -6.6, - "lowBass": -6.0, - "lowMids": -21.1, - "mids": -25.9, - "upperMids": -38.4, - "highs": -51.2, - "brilliance": -54.0 - }, - { - "t": 1.06, - "subBass": -6.5, - "lowBass": -9.1, - "lowMids": -21.6, - "mids": -27.0, - "upperMids": -37.7, - "highs": -74.6, - "brilliance": -79.6 - }, - { - "t": 1.15, - "subBass": -8.1, - "lowBass": -8.6, - "lowMids": -28.5, - "mids": -22.5, - "upperMids": -30.1, - "highs": -22.1, - "brilliance": -14.5 - }, - { - "t": 1.24, - "subBass": -10.0, - "lowBass": -7.8, - "lowMids": -25.3, - "mids": -24.3, - "upperMids": -36.4, - "highs": -32.7, - "brilliance": -25.0 - }, - { - "t": 1.34, - "subBass": -13.8, - "lowBass": -9.1, - "lowMids": -22.7, - "mids": -27.9, - "upperMids": -38.3, - "highs": -52.8, - "brilliance": -54.9 - }, - { - "t": 1.43, - "subBass": -3.7, - "lowBass": -4.1, - "lowMids": -21.5, - "mids": -27.3, - "upperMids": -40.3, - "highs": -59.9, - "brilliance": -63.1 - }, - { - "t": 1.52, - "subBass": -5.9, - "lowBass": -9.6, - "lowMids": -33.8, - "mids": -21.4, - "upperMids": -36.8, - "highs": -50.4, - "brilliance": -79.5 - }, - { - "t": 1.61, - "subBass": -6.6, - "lowBass": -8.6, - "lowMids": -23.2, - "mids": -25.4, - "upperMids": -30.4, - "highs": -22.3, - "brilliance": -14.6 - }, - { - "t": 1.71, - "subBass": -9.0, - "lowBass": -7.9, - "lowMids": -22.4, - "mids": -27.6, - "upperMids": -37.3, - "highs": -34.2, - "brilliance": -26.4 - }, - { - "t": 1.8, - "subBass": -11.4, - "lowBass": -9.1, - "lowMids": -22.8, - "mids": -27.9, - "upperMids": -38.2, - "highs": -51.7, - "brilliance": -53.9 - }, - { - "t": 1.89, - "subBass": -6.7, - "lowBass": -7.2, - "lowMids": -30.0, - "mids": -20.9, - "upperMids": -36.1, - "highs": -50.3, - "brilliance": -58.8 - }, - { - "t": 1.99, - "subBass": -6.8, - "lowBass": -8.8, - "lowMids": -21.9, - "mids": -26.2, - "upperMids": -38.4, - "highs": -54.5, - "brilliance": -74.5 - }, - { - "t": 2.08, - "subBass": -7.3, - "lowBass": -8.8, - "lowMids": -22.3, - "mids": -27.0, - "upperMids": -30.5, - "highs": -22.6, - "brilliance": -14.8 - }, - { - "t": 2.17, - "subBass": -8.2, - "lowBass": -8.6, - "lowMids": -22.2, - "mids": -27.2, - "upperMids": -37.8, - "highs": -35.0, - "brilliance": -27.4 - }, - { - "t": 2.26, - "subBass": -9.1, - "lowBass": -9.1, - "lowMids": -22.8, - "mids": -27.2, - "upperMids": -39.2, - "highs": -50.4, - "brilliance": -52.6 - }, - { - "t": 2.36, - "subBass": -5.5, - "lowBass": -6.0, - "lowMids": -33.9, - "mids": -21.7, - "upperMids": -37.8, - "highs": -49.3, - "brilliance": -61.1 - }, - { - "t": 2.45, - "subBass": -5.9, - "lowBass": -8.6, - "lowMids": -37.9, - "mids": -21.4, - "upperMids": -37.0, - "highs": -50.3, - "brilliance": -76.8 - }, - { - "t": 2.54, - "subBass": -9.2, - "lowBass": -8.7, - "lowMids": -37.6, - "mids": -21.6, - "upperMids": -30.2, - "highs": -22.8, - "brilliance": -14.8 - }, - { - "t": 2.64, - "subBass": -10.2, - "lowBass": -7.9, - "lowMids": -24.6, - "mids": -24.4, - "upperMids": -37.7, - "highs": -36.9, - "brilliance": -29.3 - }, - { - "t": 2.73, - "subBass": -13.3, - "lowBass": -8.4, - "lowMids": -23.4, - "mids": -27.3, - "upperMids": -39.2, - "highs": -51.6, - "brilliance": -55.9 - }, - { - "t": 2.82, - "subBass": -3.6, - "lowBass": -4.2, - "lowMids": -30.5, - "mids": -22.6, - "upperMids": -38.1, - "highs": -51.5, - "brilliance": -75.6 - }, - { - "t": 2.91, - "subBass": -6.0, - "lowBass": -9.7, - "lowMids": -21.8, - "mids": -26.8, - "upperMids": -37.6, - "highs": -65.7, - "brilliance": -81.9 - }, - { - "t": 3.01, - "subBass": -7.9, - "lowBass": -8.7, - "lowMids": -22.3, - "mids": -26.7, - "upperMids": -30.2, - "highs": -22.6, - "brilliance": -14.7 - }, - { - "t": 3.1, - "subBass": -8.9, - "lowBass": -7.9, - "lowMids": -27.2, - "mids": -22.9, - "upperMids": -37.5, - "highs": -40.0, - "brilliance": -32.3 - }, - { - "t": 3.19, - "subBass": -11.1, - "lowBass": -8.4, - "lowMids": -27.5, - "mids": -23.0, - "upperMids": -37.7, - "highs": -48.2, - "brilliance": -52.1 - }, - { - "t": 3.29, - "subBass": -6.8, - "lowBass": -8.0, - "lowMids": -21.9, - "mids": -26.8, - "upperMids": -39.2, - "highs": -78.7, - "brilliance": -81.2 - }, - { - "t": 3.38, - "subBass": -6.8, - "lowBass": -8.8, - "lowMids": -21.7, - "mids": -27.1, - "upperMids": -37.8, - "highs": -70.1, - "brilliance": -73.3 - }, - { - "t": 3.47, - "subBass": -7.1, - "lowBass": -8.5, - "lowMids": -22.3, - "mids": -26.9, - "upperMids": -30.1, - "highs": -22.3, - "brilliance": -14.6 - }, - { - "t": 3.56, - "subBass": -6.8, - "lowBass": -8.5, - "lowMids": -22.4, - "mids": -26.8, - "upperMids": -38.8, - "highs": -42.3, - "brilliance": -34.6 - }, - { - "t": 3.66, - "subBass": -9.6, - "lowBass": -8.3, - "lowMids": -23.0, - "mids": -25.6, - "upperMids": -38.3, - "highs": -48.2, - "brilliance": -51.4 - }, - { - "t": 3.75, - "subBass": -6.0, - "lowBass": -6.7, - "lowMids": -35.9, - "mids": -22.4, - "upperMids": -38.6, - "highs": -50.5, - "brilliance": -101.2 - }, - { - "t": 3.84, - "subBass": -6.7, - "lowBass": -9.1, - "lowMids": -21.4, - "mids": -26.1, - "upperMids": -38.7, - "highs": -66.5, - "brilliance": -74.5 - }, - { - "t": 3.94, - "subBass": -9.1, - "lowBass": -8.4, - "lowMids": -22.0, - "mids": -27.1, - "upperMids": -29.9, - "highs": -21.9, - "brilliance": -14.2 - }, - { - "t": 4.03, - "subBass": -10.3, - "lowBass": -8.0, - "lowMids": -22.0, - "mids": -27.5, - "upperMids": -37.9, - "highs": -46.6, - "brilliance": -38.6 - }, - { - "t": 4.12, - "subBass": -12.9, - "lowBass": -7.8, - "lowMids": -24.3, - "mids": -25.4, - "upperMids": -38.3, - "highs": -50.3, - "brilliance": -54.8 - }, - { - "t": 4.21, - "subBass": -3.8, - "lowBass": -4.5, - "lowMids": -30.1, - "mids": -23.7, - "upperMids": -39.8, - "highs": -51.7, - "brilliance": -96.3 - }, - { - "t": 4.31, - "subBass": -5.0, - "lowBass": -9.3, - "lowMids": -21.6, - "mids": -26.6, - "upperMids": -39.5, - "highs": -67.2, - "brilliance": -73.9 - }, - { - "t": 4.4, - "subBass": -7.9, - "lowBass": -8.4, - "lowMids": -22.0, - "mids": -25.8, - "upperMids": -29.6, - "highs": -21.7, - "brilliance": -14.0 - }, - { - "t": 4.49, - "subBass": -9.1, - "lowBass": -8.0, - "lowMids": -21.7, - "mids": -26.9, - "upperMids": -39.4, - "highs": -52.3, - "brilliance": -44.5 - }, - { - "t": 4.59, - "subBass": -12.2, - "lowBass": -7.9, - "lowMids": -22.0, - "mids": -27.0, - "upperMids": -39.0, - "highs": -50.4, - "brilliance": -52.4 - }, - { - "t": 4.68, - "subBass": -5.4, - "lowBass": -6.9, - "lowMids": -22.6, - "mids": -27.7, - "upperMids": -40.2, - "highs": -79.4, - "brilliance": -88.0 - }, - { - "t": 4.77, - "subBass": -7.1, - "lowBass": -9.0, - "lowMids": -37.2, - "mids": -20.9, - "upperMids": -36.5, - "highs": -49.9, - "brilliance": -74.8 - }, - { - "t": 4.86, - "subBass": -8.1, - "lowBass": -8.7, - "lowMids": -22.0, - "mids": -25.8, - "upperMids": -29.7, - "highs": -21.7, - "brilliance": -14.1 - }, - { - "t": 4.96, - "subBass": -7.5, - "lowBass": -8.7, - "lowMids": -21.8, - "mids": -26.9, - "upperMids": -39.6, - "highs": -60.3, - "brilliance": -51.6 - }, - { - "t": 5.05, - "subBass": -9.5, - "lowBass": -8.0, - "lowMids": -22.2, - "mids": -25.7, - "upperMids": -38.7, - "highs": -48.9, - "brilliance": -51.5 - }, - { - "t": 5.14, - "subBass": -5.6, - "lowBass": -7.0, - "lowMids": -23.4, - "mids": -28.6, - "upperMids": -40.3, - "highs": -56.5, - "brilliance": -83.2 - }, - { - "t": 5.24, - "subBass": -6.5, - "lowBass": -9.0, - "lowMids": -21.5, - "mids": -26.3, - "upperMids": -38.2, - "highs": -54.4, - "brilliance": -77.9 - }, - { - "t": 5.33, - "subBass": -8.9, - "lowBass": -8.1, - "lowMids": -21.7, - "mids": -26.4, - "upperMids": -30.1, - "highs": -22.1, - "brilliance": -14.3 - }, - { - "t": 5.42, - "subBass": -10.5, - "lowBass": -8.1, - "lowMids": -29.0, - "mids": -22.1, - "upperMids": -37.9, - "highs": -50.3, - "brilliance": -63.9 - }, - { - "t": 5.51, - "subBass": -11.6, - "lowBass": -6.9, - "lowMids": -33.8, - "mids": -22.4, - "upperMids": -38.0, - "highs": -48.9, - "brilliance": -55.2 - }, - { - "t": 5.61, - "subBass": -3.4, - "lowBass": -5.2, - "lowMids": -31.5, - "mids": -24.1, - "upperMids": -39.7, - "highs": -53.1, - "brilliance": -86.1 - }, - { - "t": 5.7, - "subBass": -6.4, - "lowBass": -9.8, - "lowMids": -46.5, - "mids": -21.2, - "upperMids": -37.4, - "highs": -49.2, - "brilliance": -87.6 - }, - { - "t": 5.79, - "subBass": -7.4, - "lowBass": -8.1, - "lowMids": -22.0, - "mids": -25.9, - "upperMids": -30.3, - "highs": -22.5, - "brilliance": -14.6 - }, - { - "t": 5.89, - "subBass": -9.4, - "lowBass": -8.2, - "lowMids": -29.5, - "mids": -21.8, - "upperMids": -37.1, - "highs": -50.9, - "brilliance": -71.2 - }, - { - "t": 5.98, - "subBass": -12.4, - "lowBass": -8.5, - "lowMids": -23.8, - "mids": -24.0, - "upperMids": -37.3, - "highs": -49.5, - "brilliance": -53.1 - }, - { - "t": 6.07, - "subBass": -6.1, - "lowBass": -8.2, - "lowMids": -23.5, - "mids": -28.8, - "upperMids": -39.5, - "highs": -68.5, - "brilliance": -80.6 - }, - { - "t": 6.16, - "subBass": -7.3, - "lowBass": -9.1, - "lowMids": -21.8, - "mids": -26.8, - "upperMids": -38.5, - "highs": -54.7, - "brilliance": -89.3 - }, - { - "t": 6.26, - "subBass": -6.6, - "lowBass": -8.3, - "lowMids": -35.4, - "mids": -21.7, - "upperMids": -30.1, - "highs": -22.6, - "brilliance": -14.8 - }, - { - "t": 6.35, - "subBass": -8.5, - "lowBass": -8.9, - "lowMids": -22.3, - "mids": -25.6, - "upperMids": -39.4, - "highs": -57.1, - "brilliance": -65.4 - }, - { - "t": 6.44, - "subBass": -9.4, - "lowBass": -7.5, - "lowMids": -24.9, - "mids": -23.5, - "upperMids": -37.6, - "highs": -47.9, - "brilliance": -51.5 - }, - { - "t": 6.54, - "subBass": -4.9, - "lowBass": -7.3, - "lowMids": -30.4, - "mids": -23.6, - "upperMids": -39.0, - "highs": -52.9, - "brilliance": -82.7 - }, - { - "t": 6.63, - "subBass": -6.9, - "lowBass": -9.2, - "lowMids": -21.8, - "mids": -27.2, - "upperMids": -40.0, - "highs": -80.1, - "brilliance": -100.2 - }, - { - "t": 6.72, - "subBass": -9.8, - "lowBass": -8.1, - "lowMids": -21.8, - "mids": -27.0, - "upperMids": -30.2, - "highs": -22.5, - "brilliance": -14.6 - }, - { - "t": 6.82, - "subBass": -10.7, - "lowBass": -8.2, - "lowMids": -30.8, - "mids": -21.9, - "upperMids": -37.7, - "highs": -49.8, - "brilliance": -61.9 - }, - { - "t": 6.91, - "subBass": -9.5, - "lowBass": -5.3, - "lowMids": -24.4, - "mids": -24.9, - "upperMids": -38.7, - "highs": -51.9, - "brilliance": -57.3 - }, - { - "t": 7.0, - "subBass": -3.5, - "lowBass": -6.3, - "lowMids": -23.6, - "mids": -28.8, - "upperMids": -40.2, - "highs": -63.1, - "brilliance": -78.6 - }, - { - "t": 7.09, - "subBass": -6.5, - "lowBass": -9.9, - "lowMids": -22.2, - "mids": -27.3, - "upperMids": -38.9, - "highs": -55.1, - "brilliance": -110.6 - }, - { - "t": 7.19, - "subBass": -8.5, - "lowBass": -8.1, - "lowMids": -21.7, - "mids": -26.6, - "upperMids": -29.9, - "highs": -22.2, - "brilliance": -14.4 - }, - { - "t": 7.28, - "subBass": -9.3, - "lowBass": -8.3, - "lowMids": -21.3, - "mids": -26.4, - "upperMids": -38.5, - "highs": -57.0, - "brilliance": -59.1 - }, - { - "t": 7.37, - "subBass": -12.2, - "lowBass": -8.2, - "lowMids": -21.7, - "mids": -27.0, - "upperMids": -37.9, - "highs": -51.9, - "brilliance": -53.9 - }, - { - "t": 7.47, - "subBass": -5.2, - "lowBass": -7.1, - "lowMids": -24.5, - "mids": -25.9, - "upperMids": -38.8, - "highs": -58.0, - "brilliance": -77.9 - }, - { - "t": 7.56, - "subBass": -7.5, - "lowBass": -9.1, - "lowMids": -33.9, - "mids": -21.8, - "upperMids": -37.2, - "highs": -52.5, - "brilliance": -75.4 - }, - { - "t": 7.65, - "subBass": -7.1, - "lowBass": -8.3, - "lowMids": -21.8, - "mids": -26.4, - "upperMids": -29.5, - "highs": -21.9, - "brilliance": -14.2 - }, - { - "t": 7.74, - "subBass": -7.3, - "lowBass": -8.8, - "lowMids": -32.4, - "mids": -21.2, - "upperMids": -36.5, - "highs": -49.2, - "brilliance": -57.1 - }, - { - "t": 7.84, - "subBass": -9.1, - "lowBass": -6.6, - "lowMids": -22.5, - "mids": -24.6, - "upperMids": -37.4, - "highs": -49.1, - "brilliance": -52.2 - }, - { - "t": 7.93, - "subBass": -6.1, - "lowBass": -9.0, - "lowMids": -24.5, - "mids": -24.7, - "upperMids": -38.1, - "highs": -56.5, - "brilliance": -79.8 - }, - { - "t": 8.02, - "subBass": -7.0, - "lowBass": -9.3, - "lowMids": -34.1, - "mids": -22.1, - "upperMids": -37.4, - "highs": -51.5, - "brilliance": -50.0 - }, - { - "t": 8.12, - "subBass": -9.5, - "lowBass": -7.9, - "lowMids": -34.5, - "mids": -20.9, - "upperMids": -29.5, - "highs": -21.8, - "brilliance": -14.1 - }, - { - "t": 8.21, - "subBass": -11.2, - "lowBass": -8.4, - "lowMids": -38.7, - "mids": -21.0, - "upperMids": -37.0, - "highs": -48.0, - "brilliance": -56.3 - }, - { - "t": 8.3, - "subBass": -7.4, - "lowBass": -4.7, - "lowMids": -23.3, - "mids": -25.5, - "upperMids": -38.7, - "highs": -53.9, - "brilliance": -60.1 - }, - { - "t": 8.39, - "subBass": -4.5, - "lowBass": -7.6, - "lowMids": -22.8, - "mids": -28.1, - "upperMids": -39.6, - "highs": -76.2, - "brilliance": -79.9 - }, - { - "t": 8.49, - "subBass": -5.4, - "lowBass": -9.6, - "lowMids": -23.0, - "mids": -27.9, - "upperMids": -40.1, - "highs": -45.8, - "brilliance": -38.7 - }, - { - "t": 8.58, - "subBass": -8.0, - "lowBass": -7.7, - "lowMids": -32.0, - "mids": -21.1, - "upperMids": -29.6, - "highs": -21.8, - "brilliance": -14.1 - }, - { - "t": 8.67, - "subBass": -10.2, - "lowBass": -8.6, - "lowMids": -21.6, - "mids": -25.3, - "upperMids": -37.3, - "highs": -50.9, - "brilliance": -55.4 - }, - { - "t": 8.77, - "subBass": -11.9, - "lowBass": -8.0, - "lowMids": -27.6, - "mids": -22.2, - "upperMids": -37.6, - "highs": -48.1, - "brilliance": -54.9 - }, - { - "t": 8.86, - "subBass": -5.1, - "lowBass": -6.9, - "lowMids": -25.2, - "mids": -24.7, - "upperMids": -39.4, - "highs": -53.0, - "brilliance": -75.4 - }, - { - "t": 8.95, - "subBass": -7.8, - "lowBass": -9.3, - "lowMids": -23.2, - "mids": -28.4, - "upperMids": -39.2, - "highs": -38.7, - "brilliance": -31.6 - }, - { - "t": 9.04, - "subBass": -7.5, - "lowBass": -8.3, - "lowMids": -21.5, - "mids": -26.9, - "upperMids": -29.9, - "highs": -22.1, - "brilliance": -14.3 - }, - { - "t": 9.14, - "subBass": -8.2, - "lowBass": -8.9, - "lowMids": -21.0, - "mids": -26.2, - "upperMids": -38.2, - "highs": -54.1, - "brilliance": -56.2 - }, - { - "t": 9.23, - "subBass": -9.3, - "lowBass": -7.4, - "lowMids": -21.9, - "mids": -26.2, - "upperMids": -38.4, - "highs": -50.4, - "brilliance": -54.0 - }, - { - "t": 9.32, - "subBass": -4.8, - "lowBass": -7.5, - "lowMids": -26.1, - "mids": -23.9, - "upperMids": -38.4, - "highs": -53.9, - "brilliance": -76.9 - }, - { - "t": 9.42, - "subBass": -7.3, - "lowBass": -9.4, - "lowMids": -34.0, - "mids": -22.8, - "upperMids": -36.6, - "highs": -33.8, - "brilliance": -26.7 - }, - { - "t": 9.51, - "subBass": -9.3, - "lowBass": -7.6, - "lowMids": -38.4, - "mids": -21.2, - "upperMids": -30.3, - "highs": -22.7, - "brilliance": -14.8 - }, - { - "t": 9.6, - "subBass": -11.3, - "lowBass": -8.4, - "lowMids": -38.4, - "mids": -20.8, - "upperMids": -36.3, - "highs": -48.5, - "brilliance": -56.7 - }, - { - "t": 9.69, - "subBass": -6.2, - "lowBass": -4.4, - "lowMids": -23.3, - "mids": -26.1, - "upperMids": -40.0, - "highs": -56.4, - "brilliance": -62.4 - }, - { - "t": 9.79, - "subBass": -4.7, - "lowBass": -8.6, - "lowMids": -26.2, - "mids": -23.7, - "upperMids": -38.4, - "highs": -54.8, - "brilliance": -78.7 - }, - { - "t": 9.88, - "subBass": -7.0, - "lowBass": -10.2, - "lowMids": -31.8, - "mids": -22.6, - "upperMids": -35.1, - "highs": -30.1, - "brilliance": -23.1 - }, - { - "t": 9.97, - "subBass": -8.4, - "lowBass": -7.6, - "lowMids": -38.9, - "mids": -21.4, - "upperMids": -30.7, - "highs": -23.6, - "brilliance": -15.4 - }, - { - "t": 10.07, - "subBass": -9.7, - "lowBass": -8.4, - "lowMids": -21.5, - "mids": -26.1, - "upperMids": -38.9, - "highs": -55.3, - "brilliance": -57.8 - }, - { - "t": 10.16, - "subBass": -9.8, - "lowBass": -7.8, - "lowMids": -21.8, - "mids": -27.2, - "upperMids": -37.7, - "highs": -53.2, - "brilliance": -55.2 - }, - { - "t": 10.25, - "subBass": -6.2, - "lowBass": -8.8, - "lowMids": -26.9, - "mids": -23.7, - "upperMids": -38.2, - "highs": -53.3, - "brilliance": -79.0 - }, - { - "t": 10.34, - "subBass": -7.9, - "lowBass": -9.2, - "lowMids": -34.2, - "mids": -22.9, - "upperMids": -34.2, - "highs": -27.5, - "brilliance": -20.4 - }, - { - "t": 10.44, - "subBass": -6.0, - "lowBass": -8.1, - "lowMids": -35.0, - "mids": -21.7, - "upperMids": -31.4, - "highs": -24.3, - "brilliance": -16.2 - }, - { - "t": 10.53, - "subBass": -8.7, - "lowBass": -9.1, - "lowMids": -21.6, - "mids": -26.0, - "upperMids": -38.0, - "highs": -52.2, - "brilliance": -58.3 - }, - { - "t": 10.62, - "subBass": -9.3, - "lowBass": -7.7, - "lowMids": -25.6, - "mids": -22.0, - "upperMids": -36.4, - "highs": -48.2, - "brilliance": -52.7 - }, - { - "t": 10.72, - "subBass": -4.9, - "lowBass": -7.6, - "lowMids": -24.0, - "mids": -25.1, - "upperMids": -38.8, - "highs": -54.6, - "brilliance": -72.9 - }, - { - "t": 10.81, - "subBass": -8.2, - "lowBass": -9.7, - "lowMids": -24.5, - "mids": -25.5, - "upperMids": -33.3, - "highs": -25.6, - "brilliance": -18.6 - }, - { - "t": 10.9, - "subBass": -9.5, - "lowBass": -7.6, - "lowMids": -40.7, - "mids": -22.0, - "upperMids": -31.7, - "highs": -24.9, - "brilliance": -16.5 - }, - { - "t": 10.99, - "subBass": -11.7, - "lowBass": -8.5, - "lowMids": -37.8, - "mids": -20.8, - "upperMids": -36.3, - "highs": -48.8, - "brilliance": -58.0 - }, - { - "t": 11.09, - "subBass": -5.3, - "lowBass": -3.9, - "lowMids": -22.2, - "mids": -26.3, - "upperMids": -39.7, - "highs": -55.9, - "brilliance": -60.0 - }, - { - "t": 11.18, - "subBass": -4.9, - "lowBass": -9.4, - "lowMids": -22.5, - "mids": -27.4, - "upperMids": -40.2, - "highs": -70.5, - "brilliance": -73.4 - }, - { - "t": 11.27, - "subBass": -6.9, - "lowBass": -9.9, - "lowMids": -23.0, - "mids": -26.6, - "upperMids": -32.9, - "highs": -24.4, - "brilliance": -17.2 - }, - { - "t": 11.37, - "subBass": -8.3, - "lowBass": -7.6, - "lowMids": -22.6, - "mids": -27.8, - "upperMids": -32.0, - "highs": -25.4, - "brilliance": -17.2 - }, - { - "t": 11.46, - "subBass": -10.7, - "lowBass": -8.7, - "lowMids": -37.7, - "mids": -20.5, - "upperMids": -36.0, - "highs": -48.3, - "brilliance": -57.0 - }, - { - "t": 11.55, - "subBass": -8.0, - "lowBass": -6.3, - "lowMids": -21.6, - "mids": -25.4, - "upperMids": -38.1, - "highs": -50.8, - "brilliance": -53.2 - }, - { - "t": 11.64, - "subBass": -5.7, - "lowBass": -8.2, - "lowMids": -26.9, - "mids": -22.5, - "upperMids": -37.5, - "highs": -53.4, - "brilliance": -73.5 - }, - { - "t": 11.74, - "subBass": -8.0, - "lowBass": -9.1, - "lowMids": -33.1, - "mids": -22.3, - "upperMids": -31.8, - "highs": -23.8, - "brilliance": -16.4 - }, - { - "t": 11.83, - "subBass": -7.3, - "lowBass": -8.3, - "lowMids": -44.7, - "mids": -22.2, - "upperMids": -32.0, - "highs": -25.8, - "brilliance": -17.7 - }, - { - "t": 11.92, - "subBass": -7.9, - "lowBass": -9.0, - "lowMids": -39.3, - "mids": -20.7, - "upperMids": -36.0, - "highs": -48.3, - "brilliance": -55.9 - }, - { - "t": 12.02, - "subBass": -7.7, - "lowBass": -6.5, - "lowMids": -27.3, - "mids": -21.5, - "upperMids": -37.0, - "highs": -47.5, - "brilliance": -53.1 - }, - { - "t": 12.11, - "subBass": -6.3, - "lowBass": -9.2, - "lowMids": -22.9, - "mids": -25.4, - "upperMids": -39.6, - "highs": -56.8, - "brilliance": -75.3 - }, - { - "t": 12.2, - "subBass": -7.4, - "lowBass": -9.1, - "lowMids": -22.8, - "mids": -27.4, - "upperMids": -32.1, - "highs": -23.6, - "brilliance": -16.1 - }, - { - "t": 12.29, - "subBass": -9.5, - "lowBass": -7.6, - "lowMids": -23.2, - "mids": -28.2, - "upperMids": -33.1, - "highs": -26.4, - "brilliance": -18.4 - }, - { - "t": 12.39, - "subBass": -12.3, - "lowBass": -8.7, - "lowMids": -38.3, - "mids": -20.5, - "upperMids": -35.7, - "highs": -48.5, - "brilliance": -55.5 - }, - { - "t": 12.48, - "subBass": -5.1, - "lowBass": -4.2, - "lowMids": -22.3, - "mids": -27.1, - "upperMids": -38.3, - "highs": -58.3, - "brilliance": -62.4 - }, - { - "t": 12.57, - "subBass": -5.4, - "lowBass": -9.4, - "lowMids": -22.0, - "mids": -27.2, - "upperMids": -39.5, - "highs": -76.0, - "brilliance": -80.8 - }, - { - "t": 12.67, - "subBass": -6.1, - "lowBass": -9.3, - "lowMids": -23.0, - "mids": -27.1, - "upperMids": -32.1, - "highs": -23.7, - "brilliance": -16.1 - }, - { - "t": 12.76, - "subBass": -8.5, - "lowBass": -7.7, - "lowMids": -23.4, - "mids": -28.6, - "upperMids": -33.6, - "highs": -27.1, - "brilliance": -19.2 - }, - { - "t": 12.85, - "subBass": -10.6, - "lowBass": -8.8, - "lowMids": -20.7, - "mids": -25.8, - "upperMids": -38.0, - "highs": -54.0, - "brilliance": -56.1 - }, - { - "t": 12.95, - "subBass": -8.4, - "lowBass": -6.9, - "lowMids": -21.3, - "mids": -26.8, - "upperMids": -39.4, - "highs": -55.1, - "brilliance": -57.2 - }, - { - "t": 13.04, - "subBass": -6.2, - "lowBass": -8.5, - "lowMids": -29.5, - "mids": -22.2, - "upperMids": -37.6, - "highs": -51.4, - "brilliance": -79.1 - }, - { - "t": 13.13, - "subBass": -7.5, - "lowBass": -9.0, - "lowMids": -34.4, - "mids": -22.4, - "upperMids": -31.8, - "highs": -24.0, - "brilliance": -16.2 - }, - { - "t": 13.22, - "subBass": -7.4, - "lowBass": -8.4, - "lowMids": -35.3, - "mids": -22.8, - "upperMids": -33.8, - "highs": -27.5, - "brilliance": -19.8 - }, - { - "t": 13.32, - "subBass": -9.0, - "lowBass": -9.2, - "lowMids": -41.5, - "mids": -20.9, - "upperMids": -36.9, - "highs": -48.0, - "brilliance": -56.6 - }, - { - "t": 13.41, - "subBass": -6.9, - "lowBass": -6.9, - "lowMids": -29.2, - "mids": -21.3, - "upperMids": -36.4, - "highs": -48.3, - "brilliance": -54.8 - }, - { - "t": 13.5, - "subBass": -5.3, - "lowBass": -8.2, - "lowMids": -39.5, - "mids": -22.0, - "upperMids": -38.2, - "highs": -50.3, - "brilliance": -83.4 - }, - { - "t": 13.6, - "subBass": -8.5, - "lowBass": -9.2, - "lowMids": -25.1, - "mids": -24.8, - "upperMids": -31.5, - "highs": -24.0, - "brilliance": -15.9 - }, - { - "t": 13.69, - "subBass": -9.8, - "lowBass": -7.7, - "lowMids": -23.4, - "mids": -28.6, - "upperMids": -35.1, - "highs": -28.3, - "brilliance": -20.7 - }, - { - "t": 13.78, - "subBass": -12.2, - "lowBass": -8.7, - "lowMids": -21.6, - "mids": -26.7, - "upperMids": -38.0, - "highs": -52.4, - "brilliance": -58.7 - }, - { - "t": 13.87, - "subBass": -5.3, - "lowBass": -4.5, - "lowMids": -27.2, - "mids": -22.0, - "upperMids": -37.1, - "highs": -51.7, - "brilliance": -60.2 - }, - { - "t": 13.97, - "subBass": -4.6, - "lowBass": -8.8, - "lowMids": -22.6, - "mids": -26.2, - "upperMids": -39.6, - "highs": -61.3, - "brilliance": -72.7 - }, - { - "t": 14.06, - "subBass": -7.9, - "lowBass": -9.5, - "lowMids": -25.9, - "mids": -23.6, - "upperMids": -31.0, - "highs": -23.6, - "brilliance": -15.7 - }, - { - "t": 14.15, - "subBass": -8.7, - "lowBass": -7.7, - "lowMids": -29.7, - "mids": -23.5, - "upperMids": -35.2, - "highs": -29.2, - "brilliance": -21.6 - }, - { - "t": 14.25, - "subBass": -10.7, - "lowBass": -8.8, - "lowMids": -21.5, - "mids": -26.5, - "upperMids": -38.8, - "highs": -56.8, - "brilliance": -58.8 - }, - { - "t": 14.34, - "subBass": -7.5, - "lowBass": -6.9, - "lowMids": -31.3, - "mids": -21.1, - "upperMids": -36.5, - "highs": -48.4, - "brilliance": -54.6 - }, - { - "t": 14.43, - "subBass": -6.4, - "lowBass": -8.7, - "lowMids": -32.2, - "mids": -21.6, - "upperMids": -36.9, - "highs": -52.0, - "brilliance": -80.4 - }, - { - "t": 14.52, - "subBass": -7.7, - "lowBass": -8.9, - "lowMids": -24.4, - "mids": -24.9, - "upperMids": -30.4, - "highs": -23.1, - "brilliance": -15.3 - }, - { - "t": 14.62, - "subBass": -6.3, - "lowBass": -8.2, - "lowMids": -24.5, - "mids": -26.0, - "upperMids": -36.4, - "highs": -30.0, - "brilliance": -22.6 - }, - { - "t": 14.71, - "subBass": -9.1, - "lowBass": -9.3, - "lowMids": -42.4, - "mids": -21.5, - "upperMids": -37.1, - "highs": -49.7, - "brilliance": -58.1 - }, - { - "t": 14.78, - "subBass": -18.2, - "lowBass": -17.5, - "lowMids": -32.3, - "mids": -33.3, - "upperMids": -41.9, - "highs": -46.7, - "brilliance": -48.9 - } - ], - "spectralDetail": { - "spectralCentroid": 1027.5, - "spectralRolloff": 1264.3, - "spectralBandwidth": 2782.5, - "spectralFlatness": 0.107547, - "mfcc": [ - -738.4915, - 205.4349, - -1.7101, - 63.088, - 23.7295, - 18.8658, - 28.7747, - 15.4407, - 18.4548, - 16.4652, - 17.9787, - 15.473, - 14.9618 - ], - "chroma": [ - 0.0415, - 0.1756, - 0.0429, - 0.191, - 0.2523, - 0.0628, - 0.1971, - 0.1162, - 0.3132, - 0.115, - 0.3503, - 0.3631 - ], - "barkBands": [ - -22.8842, - -7.4646, - -11.1697, - -22.5971, - -29.5628, - -28.2235, - -26.9773, - -27.5063, - -31.3593, - -34.783, - -35.2757, - -33.9973, - -40.001, - -39.0523, - -41.015, - -42.4412, - -42.5889, - -42.9875, - -43.6306, - -42.7086, - -40.038, - -36.4273, - -36.4679, - -32.6915 - ], - "erbBands": [ - 10.152, - 18.3137, - 19.4526, - 13.6596, - 7.9028, - 4.2215, - 6.8201, - 13.4461, - 14.6268, - 15.5112, - 15.0926, - 10.5209, - 10.7066, - 10.0596, - 11.7062, - 10.9949, - 7.8507, - 7.6491, - 6.9768, - 5.4952, - 5.2941, - 4.8399, - 4.1877, - 3.9394, - 2.9865, - 4.2631, - 5.4792, - 6.5138, - 8.3693, - 9.2006, - 8.5376, - 10.2364, - 9.5018, - 9.9721, - 9.2391, - 9.0305, - 6.9922, - 3.2646, - -2.7205, - -12.195 - ], - "spectralContrast": [ - -0.2417, - -0.5634, - -0.6369, - -0.6994, - -0.73, - -0.7587 - ], - "spectralValley": [ - -5.5818, - -6.7781, - -7.7405, - -8.4542, - -9.3325, - -12.0928 - ] - }, - "stemAnalysis": null, - "transientDensityDetail": { - "subBass": { - "onsetRatePerSecond": 13.27, - "meanOnsetStrength": 0.261, - "peakOnsetStrength": 0.69, - "eventCount": 196 - }, - "lowBass": { - "onsetRatePerSecond": 8.19, - "meanOnsetStrength": 0.494, - "peakOnsetStrength": 1.38, - "eventCount": 121 - }, - "lowMids": { - "onsetRatePerSecond": 10.43, - "meanOnsetStrength": 0.955, - "peakOnsetStrength": 2.394, - "eventCount": 154 - }, - "mids": { - "onsetRatePerSecond": 10.77, - "meanOnsetStrength": 3.868, - "peakOnsetStrength": 8.442, - "eventCount": 159 - }, - "upperMids": { - "onsetRatePerSecond": 10.83, - "meanOnsetStrength": 4.141, - "peakOnsetStrength": 10.024, - "eventCount": 160 - }, - "highs": { - "onsetRatePerSecond": 10.77, - "meanOnsetStrength": 5.194, - "peakOnsetStrength": 13.839, - "eventCount": 159 - }, - "brilliance": { - "onsetRatePerSecond": 10.29, - "meanOnsetStrength": 5.116, - "peakOnsetStrength": 14.557, - "eventCount": 152 - } - }, - "saturationDetail": { - "clippedSampleCount": 0, - "clippedSamplePercent": 0.0, - "nearClippedSampleCount": 0, - "nearClippedSamplePercent": 0.0, - "peakRatio95to50": 2.491, - "rmsToPeakRatioDb": 9.98, - "saturationLikely": false - }, - "snareDetail": { - "hitCount": 61, - "hitsPerSecond": 4.13, - "meanAttackSharpness": 0.003, - "meanBodyEnergyRatio": 0.977, - "meanSnapEnergyRatio": 0.023, - "meanCentroidHz": 439.6, - "meanDecayFrames": 54.9, - "meanDecaySeconds": 0.319, - "bandHz": [ - 120.0, - 2000.0 - ] - }, - "hihatDetail": { - "hitCount": 32, - "hitsPerSecond": 2.17, - "meanAttackSharpness": 0.003, - "meanBodyEnergyRatio": 0.162, - "meanSnapEnergyRatio": 0.838, - "meanCentroidHz": 7961.1, - "meanDecayFrames": 8.3, - "meanDecaySeconds": 0.048, - "bandHz": [ - 2000.0, - 12000.0 - ] - }, - "rhythmDetail": { - "onsetRate": 2.17, - "beatGrid": [ - 0.348, - 0.72, - 1.149, - 1.614, - 2.067, - 2.531, - 2.995, - 3.46, - 3.913, - 4.377, - 4.841, - 5.306, - 5.759, - 6.223, - 6.687, - 7.152, - 7.605, - 8.069, - 8.533, - 8.998, - 9.451, - 9.915, - 10.379, - 10.844, - 11.297, - 11.761, - 12.225, - 12.69, - 13.142, - 13.607, - 14.071, - 14.536 - ], - "downbeats": [ - 0.72, - 2.531, - 4.377, - 6.223, - 8.069, - 9.915, - 11.761, - 13.607 - ], - "beatPositions": [ - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3, - 4, - 1, - 2, - 3 - ], - "downbeatSource": "kick_accent", - "downbeatConfidence": 0.0163, - "grooveAmount": 0.038, - "tempoStability": 0.962, - "phraseGrid": { - "phrases4Bar": [ - 0.72, - 8.069 - ], - "phrases8Bar": [ - 0.72 - ], - "phrases16Bar": [ - 0.72 - ], - "totalBars": 8, - "totalPhrases8Bar": 1 - }, - "tempoCurve": [ - { - "t": 0.53, - "bpm": 139.7 - }, - { - "t": 0.93, - "bpm": 136.1 - }, - { - "t": 1.38, - "bpm": 132.5 - }, - { - "t": 1.84, - "bpm": 129.2 - }, - { - "t": 2.3, - "bpm": 129.2 - }, - { - "t": 2.76, - "bpm": 129.2 - }, - { - "t": 3.23, - "bpm": 129.2 - }, - { - "t": 3.69, - "bpm": 129.2 - }, - { - "t": 4.14, - "bpm": 129.2 - }, - { - "t": 4.61, - "bpm": 129.2 - }, - { - "t": 5.07, - "bpm": 129.2 - }, - { - "t": 5.53, - "bpm": 129.2 - }, - { - "t": 5.99, - "bpm": 129.2 - }, - { - "t": 6.46, - "bpm": 129.2 - }, - { - "t": 6.92, - "bpm": 129.2 - }, - { - "t": 7.38, - "bpm": 129.2 - }, - { - "t": 7.84, - "bpm": 129.2 - }, - { - "t": 8.3, - "bpm": 129.2 - }, - { - "t": 8.77, - "bpm": 129.2 - }, - { - "t": 9.22, - "bpm": 129.2 - }, - { - "t": 9.68, - "bpm": 129.2 - }, - { - "t": 10.15, - "bpm": 129.2 - }, - { - "t": 10.61, - "bpm": 129.2 - }, - { - "t": 11.07, - "bpm": 129.2 - }, - { - "t": 11.53, - "bpm": 129.2 - }, - { - "t": 11.99, - "bpm": 129.2 - }, - { - "t": 12.46, - "bpm": 129.2 - }, - { - "t": 12.92, - "bpm": 129.2 - }, - { - "t": 13.37, - "bpm": 129.2 - }, - { - "t": 13.84, - "bpm": 129.2 - }, - { - "t": 14.3, - "bpm": 129.2 - } - ] - }, - "melodyDetail": { - "noteCount": 53, - "notes": [ - { - "midi": 43, - "onset": 0.232, - "duration": 0.215 - }, - { - "midi": 44, - "onset": 0.511, - "duration": 0.18 - }, - { - "midi": 41, - "onset": 0.697, - "duration": 0.221 - }, - { - "midi": 43, - "onset": 0.984, - "duration": 0.16 - }, - { - "midi": 44, - "onset": 1.149, - "duration": 0.229 - }, - { - "midi": 41, - "onset": 1.469, - "duration": 0.134 - }, - { - "midi": 43, - "onset": 1.608, - "duration": 0.223 - }, - { - "midi": 44, - "onset": 1.895, - "duration": 0.18 - }, - { - "midi": 41, - "onset": 2.081, - "duration": 0.223 - }, - { - "midi": 44, - "onset": 2.531, - "duration": 0.232 - }, - { - "midi": 41, - "onset": 2.853, - "duration": 0.134 - }, - { - "midi": 43, - "onset": 2.992, - "duration": 0.221 - }, - { - "midi": 44, - "onset": 3.28, - "duration": 0.18 - }, - { - "midi": 41, - "onset": 3.466, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 3.75, - "duration": 0.16 - }, - { - "midi": 44, - "onset": 3.918, - "duration": 0.258 - }, - { - "midi": 43, - "onset": 4.386, - "duration": 0.218 - }, - { - "midi": 42, - "onset": 4.609, - "duration": 0.093 - }, - { - "midi": 44, - "onset": 4.708, - "duration": 0.136 - }, - { - "midi": 41, - "onset": 4.85, - "duration": 0.218 - }, - { - "midi": 43, - "onset": 5.135, - "duration": 0.16 - }, - { - "midi": 44, - "onset": 5.303, - "duration": 0.229 - }, - { - "midi": 44, - "onset": 6.06, - "duration": 0.165 - }, - { - "midi": 41, - "onset": 6.232, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 6.519, - "duration": 0.157 - }, - { - "midi": 44, - "onset": 6.684, - "duration": 0.261 - }, - { - "midi": 43, - "onset": 7.155, - "duration": 0.215 - }, - { - "midi": 44, - "onset": 7.43, - "duration": 0.183 - }, - { - "midi": 41, - "onset": 7.619, - "duration": 0.223 - }, - { - "midi": 44, - "onset": 7.874, - "duration": 0.102 - }, - { - "midi": 43, - "onset": 7.982, - "duration": 0.093 - }, - { - "midi": 44, - "onset": 8.081, - "duration": 0.25 - }, - { - "midi": 44, - "onset": 8.815, - "duration": 0.183 - }, - { - "midi": 41, - "onset": 9.004, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 9.288, - "duration": 0.157 - }, - { - "midi": 44, - "onset": 9.451, - "duration": 0.238 - }, - { - "midi": 44, - "onset": 10.199, - "duration": 0.183 - }, - { - "midi": 41, - "onset": 10.388, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 10.672, - "duration": 0.16 - }, - { - "midi": 44, - "onset": 10.841, - "duration": 0.232 - }, - { - "midi": 43, - "onset": 11.308, - "duration": 0.215 - }, - { - "midi": 44, - "onset": 11.578, - "duration": 0.189 - }, - { - "midi": 41, - "onset": 11.773, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 12.054, - "duration": 0.163 - }, - { - "midi": 44, - "onset": 12.225, - "duration": 0.232 - }, - { - "midi": 43, - "onset": 12.693, - "duration": 0.215 - }, - { - "midi": 44, - "onset": 12.963, - "duration": 0.189 - }, - { - "midi": 41, - "onset": 13.157, - "duration": 0.223 - }, - { - "midi": 43, - "onset": 13.439, - "duration": 0.165 - }, - { - "midi": 44, - "onset": 13.613, - "duration": 0.229 - }, - { - "midi": 43, - "onset": 14.1, - "duration": 0.192 - }, - { - "midi": 44, - "onset": 14.364, - "duration": 0.168 - }, - { - "midi": 41, - "onset": 14.539, - "duration": 0.177 - } - ], - "dominantNotes": [ - 44, - 43, - 41, - 42 - ], - "pitchRange": { - "min": 41, - "max": 44 - }, - "pitchConfidence": 0.0408, - "midiFile": "/Users/christiansmith/code/projects/asa/.claude/worktrees/goal-doc/apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/audio_melody.mid", - "sourceSeparated": false, - "vibratoPresent": false, - "vibratoExtent": 0.0, - "vibratoRate": 0.0, - "vibratoConfidence": 0.0 - }, - "transcriptionDetail": null, - "pitchDetail": null, - "grooveDetail": { - "kickSwing": 0.2177, - "hihatSwing": 0.1902, - "kickAccent": [ - 0.1629, - 0.2912, - 0.3235, - 0.2699, - 0.2839, - 0.3133, - 0.2989, - 0.2833, - 0.3164, - 0.3002, - 0.2831, - 0.3199, - 0.2612, - 0.2821, - 0.3229, - 0.2324 - ], - "hihatAccent": [ - 0.0, - 0.0585, - 0.0343, - 0.038, - 0.0513, - 0.0371, - 0.0518, - 0.0512, - 0.0374, - 0.0516, - 0.0515, - 0.0373, - 0.0375, - 0.0515, - 0.0372, - 0.0534 - ], - "perDrumSwing": { - "kick": 0.2177, - "snare": 0.1897, - "hihat": 0.1902 - } - }, - "beatsLoudness": { - "kickDominantRatio": 0.8378, - "midDominantRatio": 0.0001, - "highDominantRatio": 0.1261, - "patternBeatsPerBar": 4, - "lowBandAccentPattern": [ - 0.9528, - 1.0, - 0.9837, - 0.9664 - ], - "midBandAccentPattern": [ - 1.0, - 0.8492, - 0.8231, - 0.7917 - ], - "highBandAccentPattern": [ - 0.8219, - 0.9758, - 0.9943, - 1.0 - ], - "overallAccentPattern": [ - 0.9409, - 1.0, - 0.9899, - 0.9784 - ], - "accentPattern": [ - 0.9409, - 1.0, - 0.9899, - 0.9784 - ], - "meanBeatLoudness": 0.3442, - "beatLoudnessVariation": 0.1104, - "beatCount": 32 - }, - "rhythmTimeline": { - "beatsPerBar": 4, - "stepsPerBeat": 4, - "availableBars": 8, - "selectionMethod": "representative_dsp_window", - "windows": [ - { - "bars": 8, - "startBar": 1, - "endBar": 8, - "lowBandSteps": [ - 0.1498, - 0.2199, - 0.3454, - 0.2289, - 0.3118, - 0.1847, - 0.552, - 0.3656, - 0.3921, - 0.2272, - 0.9106, - 0.4305, - 0.3708, - 0.2172, - 0.5781, - 0.4217, - 0.3957, - 0.2546, - 0.6056, - 0.4417, - 0.3945, - 0.2336, - 0.9553, - 0.4359, - 0.3914, - 0.2273, - 0.6365, - 0.4303, - 0.371, - 0.2367, - 0.5699, - 0.4218, - 0.3963, - 0.2404, - 1.0, - 0.4415, - 0.3937, - 0.2335, - 0.6179, - 0.4361, - 0.3914, - 0.2483, - 0.6076, - 0.4309, - 0.3716, - 0.2206, - 0.9252, - 0.4229, - 0.3952, - 0.2406, - 0.5943, - 0.4416, - 0.3942, - 0.2548, - 0.5911, - 0.4369, - 0.3922, - 0.2306, - 0.8944, - 0.4309, - 0.3711, - 0.2205, - 0.5845, - 0.4225, - 0.3963, - 0.2609, - 0.569, - 0.4426, - 0.3947, - 0.2373, - 0.9368, - 0.4366, - 0.3918, - 0.2314, - 0.6477, - 0.4313, - 0.3714, - 0.2431, - 0.5421, - 0.4236, - 0.3969, - 0.244, - 0.9775, - 0.4425, - 0.3941, - 0.238, - 0.6299, - 0.4369, - 0.392, - 0.255, - 0.5918, - 0.4318, - 0.3722, - 0.2246, - 0.9005, - 0.4231, - 0.3959, - 0.245, - 0.6121, - 0.4425, - 0.3945, - 0.2612, - 0.5682, - 0.4377, - 0.3922, - 0.2353, - 0.8678, - 0.4313, - 0.3714, - 0.2251, - 0.6031, - 0.4234, - 0.3966, - 0.2669, - 0.5412, - 0.4436, - 0.3948, - 0.2419, - 0.9053, - 0.4372, - 0.3921, - 0.2359, - 0.6658, - 0.432, - 0.0956, - 0.0906, - 0.0784, - 0.0497 - ], - "midBandSteps": [ - 0.4798, - 0.9208, - 0.619, - 0.2355, - 0.3051, - 0.3663, - 0.5164, - 0.6006, - 0.6484, - 0.8311, - 0.7537, - 0.6421, - 0.7244, - 0.8078, - 0.7216, - 0.7756, - 0.9115, - 1.0, - 0.6932, - 0.7218, - 0.7302, - 0.8873, - 0.657, - 0.7695, - 0.7361, - 0.7728, - 0.7996, - 0.733, - 0.7533, - 0.7944, - 0.6427, - 0.8336, - 0.8646, - 0.8928, - 0.6767, - 0.8315, - 0.7812, - 0.936, - 0.8578, - 0.6847, - 0.8163, - 0.8608, - 0.7135, - 0.6937, - 0.7587, - 0.6862, - 0.641, - 0.6643, - 0.9109, - 0.86, - 0.8293, - 0.7906, - 0.6836, - 0.9004, - 0.701, - 0.7966, - 0.7734, - 0.7098, - 0.7465, - 0.6952, - 0.7094, - 0.9085, - 0.7582, - 0.7319, - 0.831, - 0.839, - 0.838, - 0.7418, - 0.7311, - 0.7582, - 0.7843, - 0.8213, - 0.6829, - 0.7875, - 0.632, - 0.7024, - 0.7134, - 0.8968, - 0.7458, - 0.7262, - 0.7735, - 0.8474, - 0.8307, - 0.7436, - 0.7243, - 0.8853, - 0.7734, - 0.6847, - 0.7025, - 0.7472, - 0.6907, - 0.6939, - 0.5987, - 0.7339, - 0.7422, - 0.8117, - 0.8285, - 0.8314, - 0.9189, - 0.7461, - 0.7309, - 0.769, - 0.6771, - 0.798, - 0.7312, - 0.7351, - 0.7179, - 0.7398, - 0.6797, - 0.902, - 0.7799, - 0.7118, - 0.7849, - 0.7917, - 0.7678, - 0.6789, - 0.819, - 0.8179, - 0.679, - 0.7848, - 0.6508, - 0.9159, - 0.6716, - 0.6633, - 0.3101, - 0.0853, - 0.2807, - 0.0728 - ], - "highBandSteps": [ - 0.001, - 0.0001, - 0.0001, - 0.3016, - 0.0455, - 0.0007, - 0.0001, - 0.0003, - 0.7182, - 0.001, - 0.0001, - 0.0006, - 0.6042, - 0.0008, - 0.0007, - 0.0005, - 0.9796, - 0.0004, - 0.0009, - 0.0007, - 0.8509, - 0.0011, - 0.0005, - 0.0003, - 0.7059, - 0.0012, - 0.0, - 0.0003, - 0.6058, - 0.001, - 0.0008, - 0.0, - 0.9922, - 0.001, - 0.0007, - 0.0002, - 0.8394, - 0.0007, - 0.0, - 0.0006, - 0.7078, - 0.0007, - 0.0005, - 0.0005, - 0.6137, - 0.0014, - 0.0005, - 0.0009, - 0.991, - 0.0013, - 0.0004, - 0.0005, - 0.8423, - 0.0007, - 0.0006, - 0.0002, - 0.7128, - 0.0015, - 0.0002, - 0.0005, - 0.6034, - 0.0005, - 0.0003, - 0.0007, - 0.9916, - 0.0013, - 0.0004, - 0.0008, - 0.8515, - 0.0015, - 0.0003, - 0.0, - 0.709, - 0.001, - 0.0008, - 0.0005, - 0.6122, - 0.0005, - 0.0005, - 0.0007, - 1.0, - 0.0013, - 0.0001, - 0.0008, - 0.8428, - 0.0007, - 0.0003, - 0.0006, - 0.7148, - 0.001, - 0.0007, - 0.0005, - 0.6151, - 0.0013, - 0.0, - 0.0002, - 0.988, - 0.0013, - 0.0, - 0.0008, - 0.8491, - 0.0012, - 0.0009, - 0.0002, - 0.7178, - 0.0011, - 0.0002, - 0.0001, - 0.6036, - 0.0005, - 0.0001, - 0.0007, - 0.9924, - 0.0016, - 0.0007, - 0.0009, - 0.846, - 0.0009, - 0.0006, - 0.0002, - 0.7114, - 0.0005, - 0.0006, - 0.0007, - 0.5496, - 0.0, - 0.0003, - 0.0001 - ], - "overallSteps": [ - 0.1562, - 0.2328, - 0.3518, - 0.2434, - 0.3154, - 0.1887, - 0.5544, - 0.3714, - 0.4298, - 0.2385, - 0.9128, - 0.4363, - 0.405, - 0.2283, - 0.5836, - 0.4299, - 0.449, - 0.2684, - 0.6103, - 0.4488, - 0.4392, - 0.2457, - 0.9555, - 0.4438, - 0.4299, - 0.2376, - 0.6426, - 0.4377, - 0.4057, - 0.2473, - 0.5742, - 0.4309, - 0.4495, - 0.2525, - 1.0, - 0.4503, - 0.4388, - 0.2465, - 0.6252, - 0.4426, - 0.4314, - 0.2599, - 0.6127, - 0.4376, - 0.4068, - 0.2296, - 0.9255, - 0.4293, - 0.4491, - 0.2523, - 0.6014, - 0.4498, - 0.4378, - 0.2669, - 0.5961, - 0.4452, - 0.4317, - 0.2399, - 0.8967, - 0.4377, - 0.405, - 0.2331, - 0.5905, - 0.43, - 0.4488, - 0.272, - 0.5765, - 0.45, - 0.4395, - 0.2473, - 0.9393, - 0.4454, - 0.4296, - 0.242, - 0.651, - 0.4381, - 0.4058, - 0.2553, - 0.5484, - 0.4309, - 0.4488, - 0.2554, - 0.9803, - 0.4499, - 0.4384, - 0.25, - 0.6357, - 0.4434, - 0.4304, - 0.2646, - 0.5967, - 0.4385, - 0.4048, - 0.2344, - 0.9027, - 0.4318, - 0.4483, - 0.2561, - 0.6205, - 0.45, - 0.4392, - 0.2711, - 0.5731, - 0.4461, - 0.4312, - 0.2449, - 0.8699, - 0.4388, - 0.4048, - 0.2376, - 0.6093, - 0.4305, - 0.4484, - 0.2771, - 0.5479, - 0.4499, - 0.4408, - 0.2528, - 0.9064, - 0.4453, - 0.4295, - 0.2485, - 0.6695, - 0.4382, - 0.1236, - 0.091, - 0.0822, - 0.0504 - ] - } - ] - }, - "sidechainDetail": { - "pumpingStrength": 0.1677, - "pumpingRegularity": 0.0, - "pumpingRate": null, - "pumpingConfidence": 0.1384, - "envelopeShape": [ - 0.863, - 0.693, - 0.99, - 0.91, - 0.883, - 0.7, - 0.959, - 0.887, - 0.893, - 0.687, - 0.978, - 0.897, - 0.902, - 0.689, - 1.0, - 0.911 - ], - "envelopeShape32": [ - 0.863, - 0.768, - 0.693, - 0.573, - 0.904, - 0.99, - 0.91, - 0.786, - 0.883, - 0.76, - 0.7, - 0.571, - 0.881, - 0.959, - 0.887, - 0.798, - 0.893, - 0.737, - 0.687, - 0.566, - 0.894, - 0.978, - 0.897, - 0.749, - 0.902, - 0.739, - 0.689, - 0.579, - 0.878, - 1.0, - 0.911, - 0.795 - ] - }, - "acidDetail": { - "isAcid": true, - "confidence": 0.6, - "resonanceLevel": 1.0, - "centroidOscillationHz": 27, - "bassRhythmDensity": 2.0 - }, - "reverbDetail": { - "rt60": null, - "isWet": false, - "tailEnergyRatio": null, - "measured": false, - "perBandRt60": null, - "preDelayMs": null - }, - "vocalDetail": { - "hasVocals": false, - "confidence": 0.35, - "vocalEnergyRatio": 0.05, - "formantStrength": 0.27, - "mfccLikelihood": 0.86, - "stemEnergyRatio": null, - "stemOtherCorrelation": null - }, - "supersawDetail": { - "isSupersaw": true, - "confidence": 0.86, - "voiceCount": 7, - "avgDetuneCents": 28.1, - "spectralComplexity": 7.1 - }, - "bassDetail": { - "averageDecayMs": 1000, - "type": "sustained", - "transientRatio": 0.2, - "fundamentalHz": 90, - "transientCount": 0, - "swingPercent": 0, - "grooveType": "straight" - }, - "kickDetail": { - "isDistorted": true, - "thd": 0.27, - "harmonicRatio": 0.91, - "fundamentalHz": 81, - "kickCount": 117 - }, - "genreDetail": { - "genre": "dub-techno", - "confidence": 0.8882, - "secondaryGenre": "ambient", - "genreFamily": "techno", - "topScores": [ - { - "genre": "dub-techno", - "score": 0.8087 - }, - { - "genre": "ambient", - "score": 0.7104 - }, - { - "genre": "dubstep", - "score": 0.6902 - }, - { - "genre": "ambient-techno", - "score": 0.6647 - }, - { - "genre": "pop", - "score": 0.6644 - } - ] - }, - "effectsDetail": { - "gatingDetected": false, - "gatingRate": null, - "gatingRegularity": 0.0, - "gatingEventCount": 1 - }, - "synthesisCharacter": { - "inharmonicity": 0.0781, - "oddToEvenRatio": 1.5557 - }, - "danceability": { - "danceability": 3.9118, - "dfa": 0.2556 - }, - "structure": { - "segments": [ - { - "start": 0.0, - "end": 14.769, - "index": 0 - } - ], - "segmentCount": 1 - }, - "arrangementDetail": { - "noveltyCurve": [ - 0.0, - 0.0, - 0.0002, - 0.0009, - 0.1443, - 0.7084, - 0.3775, - 0.0026, - 0.0043, - 0.011, - 0.0064, - 0.0019, - 0.1551, - 0.6325, - 0.3194, - 0.0038, - 0.009, - 0.028, - 0.0156, - 0.0042, - 0.2341, - 0.9672, - 0.4902, - 0.0061, - 0.0062, - 0.011, - 0.0073, - 0.0066, - 0.1728, - 0.6795, - 0.3403, - 0.0053, - 0.0079, - 0.0215, - 0.0145, - 0.006, - 0.0575, - 0.2256, - 0.1137, - 0.0022, - 0.005, - 0.0137, - 0.0072, - 0.003, - 0.0472, - 0.1493, - 0.0587, - 0.0031, - 0.0034, - 0.0061, - 0.0032, - 0.0023, - 0.0683, - 0.1862, - 0.064, - 0.0018, - 0.0032, - 0.0079, - 0.0036, - 0.0012, - 0.0653, - 0.1252, - 0.0425, - 0.0016, - 0.0082, - 0.0153, - 0.0063, - 0.0005, - 0.1856, - 0.3247, - 0.1085, - 0.0009, - 0.0087, - 0.0152, - 0.0062, - 0.0013, - 0.3606, - 0.5821, - 0.1963, - 0.0036, - 0.0076, - 0.0116, - 0.008, - 0.0036, - 0.2925, - 0.4441, - 0.1508, - 0.0051, - 0.0137, - 0.0254, - 0.0095, - 0.0043, - 0.2533, - 0.4974, - 0.12, - 0.0084, - 0.0095, - 0.0134, - 0.0065, - 0.0069, - 0.1474, - 0.279, - 0.0618, - 0.0054, - 0.0081, - 0.0126, - 0.0036, - 0.0034, - 0.0793, - 0.1322, - 0.0156, - 0.0029, - 0.0076, - 0.0118, - 0.0018, - 0.0017, - 0.1105, - 0.1614, - 0.0033, - 0.0018, - 0.0029, - 0.004, - 0.0009, - 0.0012, - 0.2272, - 0.2864, - 0.0006, - 0.0006, - 0.0087, - 0.0118, - 0.0014, - 0.0013, - 0.2311, - 0.2607, - 0.0014, - 0.0016, - 0.0176, - 0.0184, - 0.0033, - 0.0029, - 0.5383, - 0.5559, - 0.0028, - 0.0031, - 0.0115, - 0.0117, - 0.0022, - 0.002, - 0.7473, - 0.7581, - 0.0059, - 0.0066, - 0.0229, - 0.0249, - 0.0062, - 0.0042, - 0.3851, - 0.3877, - 0.0027, - 0.0029, - 0.0131, - 0.0126, - 0.0047, - 0.0039, - 0.2404, - 0.2194, - 0.0026, - 0.0028, - 0.0056, - 0.0047, - 0.0052, - 0.0052, - 0.1354, - 0.1482, - 0.016, - 0.0018, - 0.0065, - 0.0083, - 0.0021, - 0.0049, - 0.0619, - 0.0586, - 0.0017, - 0.0016, - 0.0098, - 0.0117, - 0.0018, - 0.0657, - 0.3089, - 0.2441, - 0.0009, - 0.002, - 0.0041, - 0.0032, - 0.0009, - 0.0994, - 0.4012, - 0.3042, - 0.0019, - 0.0053, - 0.0134, - 0.0142, - 0.0042, - 0.139, - 0.4193, - 0.283, - 0.0015, - 0.0115, - 0.0279, - 0.0209, - 0.0035, - 0.172, - 0.5373, - 0.3659, - 0.005, - 0.0078, - 0.0108, - 0.0088, - 0.0042, - 0.1566, - 0.467, - 0.3097, - 0.002, - 0.0053, - 0.0126, - 0.0111, - 0.0048, - 0.0638, - 0.1854, - 0.122, - 0.0046, - 0.0094, - 0.0173, - 0.0099, - 0.0023, - 0.0673, - 0.1988, - 0.0892, - 0.0018, - 0.0034, - 0.0067, - 0.0038, - 0.0023, - 0.0828, - 0.2177, - 0.0688, - 0.0006, - 0.0059, - 0.0208, - 0.0087, - 0.0011, - 0.0779, - 0.1887, - 0.056, - 0.0014, - 0.0005 - ], - "noveltyPeaks": [ - { - "time": 1.207, - "strength": 0.9672 - }, - { - "time": 4.435, - "strength": 0.7484 - }, - { - "time": 8.591, - "strength": 1.0 - }, - { - "time": 12.283, - "strength": 0.7182 - }, - { - "time": 14.582, - "strength": 0.1887 - } - ], - "noveltyMean": 0.0909, - "noveltyStdDev": 0.1622 - }, - "segmentLoudness": [ - { - "segmentIndex": 0, - "start": 0.0, - "end": 14.769, - "lufs": -9.5, - "lra": 0.0 - } - ], - "segmentSpectral": [ - { - "segmentIndex": 0, - "barkBands": [ - -22.8842, - -7.4646, - -11.1697, - -22.5971, - -29.5628, - -28.2235, - -26.9773, - -27.5063, - -31.3593, - -34.783, - -35.2757, - -33.9973, - -40.001, - -39.0523, - -41.015, - -42.4412, - -42.5889, - -42.9875, - -43.6306, - -42.7086, - -40.038, - -36.4273, - -36.4679, - -32.6915 - ], - "spectralCentroid": 1027.5, - "spectralRolloff": 1264.3, - "stereoWidth": 0.0, - "stereoCorrelation": 1.0 - } - ], - "segmentStereo": [ - { - "segmentIndex": 0, - "stereoWidth": 0.0, - "stereoCorrelation": 1.0 - } - ], - "segmentKey": [ - { - "segmentIndex": 0, - "key": "F Minor", - "keyConfidence": 0.59 - } - ], - "chordDetail": { - "chordSequence": [ - "Bbm", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "Fm", - "C#", - "C#", - "C#", - "C#", - "C#", - "Fm", - "Fm", - "C#", - "C#", - "C#", - "C#", - "Fm", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#", - "C#" - ], - "chordStrength": 0.641, - "progression": [ - "Bbm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#", - "Fm", - "C#" - ], - "dominantChords": [ - "C#", - "Fm", - "Cm", - "Bbm" - ], - "chordTimeline": [ - { - "startSec": 0.0, - "endSec": 14.814, - "label": "Fm", - "labelLong": "F minor", - "confidence": 0.5423 - } - ], - "chordChangeCount": 0, - "chordTimelineSource": "librosa_viterbi", - "chordTimelineAgreement": false - }, - "perceptual": { - "sharpness": 0.0614, - "roughness": 0.0 - }, - "essentiaFeatures": { - "zeroCrossingRate": 0.0327, - "hfc": 155.3578, - "spectralComplexity": 8.1144, - "dissonance": 0.0 - } -} \ No newline at end of file diff --git a/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/README.md b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/README.md new file mode 100644 index 00000000..7adb534d --- /dev/null +++ b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/README.md @@ -0,0 +1,45 @@ +# ukg_2step_shuffle_132 + +**Genre:** UK garage · **Tempo:** 132 BPM · **Key:** G minor · **Render:** 48 kHz / 24-bit, 16 s loop + +## What this fixture isolates + +A UK garage / 2-step loop built on the genre's defining pillar — the shuffle: + +1. **The 2-step shuffle** — kick is broken (NOT four-on-floor), snare on 2 and 4, + closed hats on swung 16ths. **The swing lives in the MIDI, not in a device**: + 16th swing ≈ 57 % is baked into the committed `audio_melody.mid` and + `audio_drums.mid` — equivalently, apply Live's groove pool **"Swing 16ths" at + 57 %** to the drum and bass clips. The answer key asserts it via + `grooveDetail.hihatSwing`, `grooveDetail.perDrumSwing.snare`, and + `bassDetail.grooveType`, so a recommendation that recovers the feel by any + valid route (groove pool, MIDI nudge) earns credit. +2. **Warm plucky sub** — `Operator` sine with decay/sustain shaped for bounce, + not a drone. The bounce is what swings. +3. **Chopped organ-style chord stab** — `Wavetable` (no sustain, fast attack) + through `Chorus-Ensemble` for the classic organ/vocal-chop warble. +4. **Dub delay return** — `Echo`, synced 3/16 against 1/4, filtered, on a return. +5. **Gentle master glue** (`Glue Compressor` 2:1 — UKG breathes, don't slam) + into `Limiter` (-0.3 dBFS). + +Covers all seven production domains. + +The swing targets in `measurableIntent` are **pre-render estimates** on the +tanh-compressed 0–1 scale — after rendering, re-baseline them (and +`bassDetail.grooveType`) from the stored fingerprint per `../README.md` step 6. +If the rendered bass reads `"straight"`, relax the manifest rather than forcing +the MIDI. + +## Answer key + +`manifest.json` → `deviceSpec`. Build verbatim in Live 12 (checklist in +`../README.md`), render to `audio.flac`, store `phase1_fingerprint.json`. +`audio_melody.mid` carries the swung G-minor stab/bass material; +`audio_drums.mid` carries the swung 2-step kit pattern (kick 36 / snare 38 / +closed hat 42) so the shuffle is reproducible without hand-programming groove. + +## Status + +- [x] Catalog-valid spec authored +- [ ] **NEEDS-FIXTURE:** rendered `audio.flac` (owner builds + renders) +- [ ] **NEEDS-FIXTURE:** stored `phase1_fingerprint.json` diff --git a/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_drums.mid b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_drums.mid new file mode 100644 index 0000000000000000000000000000000000000000..ea5c59d292c6452666a799e6ec7f42b45abd541d GIT binary patch literal 1313 zcmeYb$w*;fU|?fl1i}R0kfLk`23Br{|AEYG?$)Pcjjy^iJ>222l3{?+%f7t^5SDO{6OJ`NN=O*Z#4g6mTL_E G^%($zc|+|0 literal 0 HcmV?d00001 diff --git a/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_melody.mid b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/audio_melody.mid new file mode 100644 index 0000000000000000000000000000000000000000..33bbd655d45da15f305e0f636ba287455a3a2696 GIT binary patch literal 865 zcmeYb$w*;fU|?fl1i}R0kfLk`24-)D|AEYG?KrA04)?n_y&|n3` zb`Bg3@(#K{F*zXZL_nQ7l032*tWf>oNbeP|skJLYfr>#B4K!3pENE+xLW1wFkam3Le4fKyR&_B{Z|KL}r hjwFw41}jv5IFdXAPz+fOb?w0)?n5np82;-s004Vl=OzFE literal 0 HcmV?d00001 diff --git a/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/manifest.json b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/manifest.json new file mode 100644 index 00000000..f1b8fff1 --- /dev/null +++ b/apps/backend/tests/fixtures/recommendation_tracks/ukg_2step_shuffle_132/manifest.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "recommendation-fixture.v1", + "id": "ukg_2step_shuffle_132", + "title": "UK garage — 2-step shuffle, warm sub, chopped chord stab, dub echo", + "genre": "uk garage", + "audioPath": "audio.flac", + "phase1FingerprintPath": "phase1_fingerprint.json", + "phase1Fingerprint": null, + "render": { + "sampleRateHz": 48000, + "bitDepth": 24, + "lengthSeconds": 16, + "bpm": 132, + "key": "G minor", + "notes": "8-bar 2-step loop (~14.5 s). Kick is NOT four-on-floor; snare on 2 and 4; hats and snare ride a 16th swing (~57%, baked into the committed MIDI — or apply Ableton's 'Swing 16ths' groove at 57%). Render the full loop to one stereo file, no export normalization." + }, + "deviceSpec": { + "kick": [ + { + "device": "Drum Rack", + "family": "NATIVE", + "role": "Sampled 2-step kick (broken, not 4/4)", + "parameters": [ + { "name": "Pad Volume", "value": "-3 dB" }, + { "name": "Pad Pitch", "value": "0" } + ] + } + ], + "bass": [ + { + "device": "Operator", + "family": "NATIVE", + "role": "Warm plucky UKG sub", + "parameters": [ + { "name": "Oscillator A Waveform", "value": "Sine" }, + { "name": "Filter Type", "value": "Lowpass" }, + { "name": "Filter Frequency", "value": "200 Hz" }, + { "name": "Amp Envelope Decay", "value": "400 ms" }, + { "name": "Amp Envelope Sustain", "value": "60%" }, + { "name": "Amp Envelope Release", "value": "120 ms", "intent": "bouncy, not a drone — the bounce is what swings" } + ] + } + ], + "melody": [ + { + "device": "Wavetable", + "family": "NATIVE", + "role": "Chopped organ-style chord stab", + "parameters": [ + { "name": "Oscillator 1 Wavetable", "value": "Basic Shapes" }, + { "name": "Filter Type", "value": "Lowpass" }, + { "name": "Filter Frequency", "value": "3500 Hz" }, + { "name": "Amp Envelope Attack", "value": "2 ms" }, + { "name": "Amp Envelope Decay", "value": "220 ms" }, + { "name": "Amp Envelope Sustain", "value": "0%", "intent": "stabby chop, no sustain — classic UKG organ/vocal-chop articulation" } + ] + }, + { + "device": "Chorus-Ensemble", + "family": "NATIVE", + "role": "Organ warble", + "parameters": [ + { "name": "Mode", "value": "Classic" }, + { "name": "Rate", "value": "0.8 Hz" }, + { "name": "Amount", "value": "50%" }, + { "name": "Dry/Wet", "value": "30%" } + ] + } + ], + "groove": [ + { + "device": "Drum Rack", + "family": "NATIVE", + "role": "Swung 2-step snare + shuffled hats — swing lives in the MIDI, not a device", + "parameters": [ + { "name": "Pad Volume", "value": "-8 dB" }, + { "name": "Send Level", "value": "-12 dB", "intent": "snare/hat send into the Echo return" } + ] + } + ], + "fx": [ + { + "device": "Echo", + "family": "NATIVE", + "role": "Dub delay return on stabs + snare", + "parameters": [ + { "name": "Sync", "value": "On" }, + { "name": "Delay Time (L)", "value": "3/16" }, + { "name": "Delay Time (R)", "value": "1/4" }, + { "name": "Feedback", "value": "35%" }, + { "name": "Filter On", "value": "On" }, + { "name": "Filter Frequency", "value": "1200 Hz" }, + { "name": "Stereo", "value": "60%" }, + { "name": "Dry/Wet", "value": "100%", "intent": "on a return — 100% wet" } + ] + } + ], + "stereo": [ + { + "device": "Utility", + "family": "NATIVE", + "role": "Low-end mono / top width", + "parameters": [ + { "name": "Bass Mono", "value": "On" }, + { "name": "Stereo Width", "value": "115%" } + ] + } + ], + "master": [ + { + "device": "Glue Compressor", + "family": "NATIVE", + "role": "Gentle bus glue — UKG breathes, don't slam", + "parameters": [ + { "name": "Threshold", "value": "-12 dB" }, + { "name": "Ratio", "value": "2:1" }, + { "name": "Attack", "value": "3 ms" }, + { "name": "Release", "value": "Auto" }, + { "name": "Makeup", "value": "2 dB" } + ] + }, + { + "device": "Limiter", + "family": "NATIVE", + "role": "Master ceiling", + "parameters": [ + { "name": "Ceiling", "value": "-0.3 dB" } + ] + } + ] + }, + "measurableIntent": { + "bpm": { "target": 132, "tolerance": 1, "direction": "exact" }, + "key": { "target": "G minor" }, + "lufsIntegrated": { "target": -9, "tolerance": 2, "unit": "LUFS" }, + "truePeak": { "target": -0.3, "tolerance": 0.4, "unit": "dBTP", "intent": "Limiter ceiling; dBTP per ADR 0002" }, + "grooveDetail.hihatSwing": { "target": 0.45, "tolerance": 0.2, "direction": "min", "intent": "shuffled 16th hats must register; estimate — re-baseline from the render" }, + "grooveDetail.perDrumSwing.snare": { "target": 0.4, "tolerance": 0.2, "direction": "min", "intent": "2-step snare rides the same swing" }, + "bassDetail.grooveType": { "equals": "slight-swing", "intent": "syncopated swung sub line; categorical — re-baseline from the render" }, + "spectralDetail.spectralCentroidMean": { "target": 2500, "tolerance": 800, "unit": "Hz", "intent": "bright, hat-and-stab-forward top" } + } +} diff --git a/plans/owner-actions-recommendation-proof-plan.md b/plans/owner-actions-recommendation-proof-plan.md index 770a708c..1c19f816 100644 --- a/plans/owner-actions-recommendation-proof-plan.md +++ b/plans/owner-actions-recommendation-proof-plan.md @@ -20,8 +20,10 @@ without paying for Gemini API calls. - The `asa-ableton` branch is local only. Its 66 tests pass and it includes the tracked real VTSS fixture, Gate alpha receipt, and Gate beta generator. - Ableton Live is not installed on this Mac. -- All five recommendation fixtures still use fingerprints marked `_synthetic: true`. - No real `audio.flac` render exists yet. +- No real `audio.flac` render exists yet. Three fixtures still carry fingerprints + marked `_synthetic: true`; the two re-authored on 2026-06-10 + (`hard_techno_rumble_145`, `ukg_2step_shuffle_132`) ship spec-only, with no + fingerprint until the real render. ## Important Correction @@ -80,9 +82,10 @@ receipt. Use a Mac with Ableton Live 12. Pull both repositories and the published `feat/track-ii-gate-tooling` branch. -Before building anything, confirm that house, techno, melodic techno, drum and bass, -and acid techno are useful test genres for your work. If one is irrelevant, stop and -have an agent re-author that fixture first. +Genre fit was confirmed 2026-06-10: house, melodic techno, and acid stay; the +techno and drum-and-bass fixtures were re-authored to owner genres as +`hard_techno_rumble_145` (hard/peak techno) and `ukg_2step_shuffle_132` +(UK garage / 2-step). #### 3a. Run Gate beta first @@ -109,7 +112,7 @@ mapping and regenerate the probes before Track III proceeds. #### 3b. Pilot one fixture before building all five -Start with `techno_rumble_130`, the smallest spec: +Start with `hard_techno_rumble_145`, the smallest spec: 1. Open its `manifest.json` and README. 2. Build the listed device chains and values exactly. @@ -119,7 +122,7 @@ Start with `techno_rumble_130`, the smallest spec: Return that single render to: -`apps/backend/tests/fixtures/recommendation_tracks/techno_rumble_130/audio.flac` +`apps/backend/tests/fixtures/recommendation_tracks/hard_techno_rumble_145/audio.flac` An agent should ingest and validate this pilot before the remaining four are built. This catches unclear instructions or an invalid measurement target before more manual @@ -129,7 +132,7 @@ work is spent. After the pilot passes, render: -1. `dnb_reese_174` +1. `ukg_2step_shuffle_132` 2. `acid_303_128` 3. `house_sidechain_pluck_124` 4. `melodic_techno_arp_124` From 8f7c9044568e1252a5da5a64c8d2570140a24f00 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 06:33:47 +0000 Subject: [PATCH 3/3] docs: demote abletonDevices.ts to research-only baseline (NEEDS-WIRING decision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner resolved the wire-or-demote choice from NEEDS.md's dead-code finding: the deterministic recommendation engine stays as the scored free baseline in the GOAL.md sub-goal 3 three-source comparison but is NOT to be wired into the product. Score-driven product improvements land on the Phase 2 provider path instead. Recorded in: the module header (abletonDevices.ts), the eval bridge header, NEEDS.md (decision + the now-moot citation-emit candidate), BACKLOG.md, RECOMMENDATION_VERDICT.md, and CLAUDE.md's Backport Candidates line. No code changes — comments and docs only. Verified: npm run lint green, evaluate_recommendations.py --self-test PASS. https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy --- BACKLOG.md | 2 +- CLAUDE.md | 2 +- apps/backend/NEEDS.md | 38 ++++++++++--------- apps/backend/RECOMMENDATION_VERDICT.md | 5 ++- .../scripts/emit_deterministic_recs.ts | 6 ++- apps/ui/src/data/abletonDevices.ts | 11 ++++++ 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/BACKLOG.md b/BACKLOG.md index 7c07ba75..470d5fb0 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -4,7 +4,7 @@ Source: `active/sonic-architect-app` (keep in `active/` as reference). Most of t ## Shipped — Data - ✅ `data/genreProfiles.ts` — 35 EDM genre spectral targets, LUFS/crest factor/PLR ranges. **Landed:** [`apps/ui/src/data/genreProfiles.ts`](apps/ui/src/data/genreProfiles.ts). -- ✅ `data/abletonDevices.ts` — 14 spectral-band → Ableton Live 12 device mappings + FX rule engine. **Landed:** [`apps/ui/src/data/abletonDevices.ts`](apps/ui/src/data/abletonDevices.ts). +- ✅ `data/abletonDevices.ts` — 14 spectral-band → Ableton Live 12 device mappings + FX rule engine. **Landed:** [`apps/ui/src/data/abletonDevices.ts`](apps/ui/src/data/abletonDevices.ts). *Ported but demoted to a research-only scoring baseline (owner decision 2026-06-11): imported by no product code, consumed only by the recommendation-eval bridge. See the NEEDS-WIRING decision in [`apps/backend/NEEDS.md`](apps/backend/NEEDS.md).* ## Shipped — Mix Analysis - ✅ `services/mixDoctor.ts` + `components/MixDoctorPanel.tsx` — spectral-balance scoring against genre profiles. **Landed:** [`apps/ui/src/services/mixDoctor.ts`](apps/ui/src/services/mixDoctor.ts), [`apps/ui/src/components/MixDoctorPanel.tsx`](apps/ui/src/components/MixDoctorPanel.tsx). diff --git a/CLAUDE.md b/CLAUDE.md index bfac519d..734e5bd1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -440,7 +440,7 @@ A quick map from intent to the right place to start: ## Backport Candidates -The full original `sonic-architect-app` backlog is **shipped** — genre profiles, Ableton device mappings, mix doctor, eight detectors, Phase 3 audition-sample generation, and `patchSmith.ts` (Phase 3 Vital preset generation, [`apps/ui/src/services/patchSmith.ts`](apps/ui/src/services/patchSmith.ts)). See [`BACKLOG.md`](BACKLOG.md) for the landed inventory. Consult it before re-implementing genre detection, mix analysis, acid/reverb/vocal/supersaw/bass/kick detection — they're already in [`apps/backend/analyze_detection.py`](apps/backend/analyze_detection.py) and emit fields visible in `EXPECTED_TOP_LEVEL_KEYS`. +The full original `sonic-architect-app` backlog is **shipped** — genre profiles, Ableton device mappings (`abletonDevices.ts` — ported but **research-only** since the 2026-06-11 demotion decision: a scoring baseline for the recommendation-proof harness, imported by no product code; see the NEEDS-WIRING decision in `apps/backend/NEEDS.md`), mix doctor, eight detectors, Phase 3 audition-sample generation, and `patchSmith.ts` (Phase 3 Vital preset generation, [`apps/ui/src/services/patchSmith.ts`](apps/ui/src/services/patchSmith.ts)). See [`BACKLOG.md`](BACKLOG.md) for the landed inventory. Consult it before re-implementing genre detection, mix analysis, acid/reverb/vocal/supersaw/bass/kick detection — they're already in [`apps/backend/analyze_detection.py`](apps/backend/analyze_detection.py) and emit fields visible in `EXPECTED_TOP_LEVEL_KEYS`. ## Companion Agent Docs diff --git a/apps/backend/NEEDS.md b/apps/backend/NEEDS.md index de91b4e0..da8e18da 100644 --- a/apps/backend/NEEDS.md +++ b/apps/backend/NEEDS.md @@ -85,8 +85,9 @@ citations**, so the chain-of-custody penalty drives its adjusted aggregate to #2). The rules **are** feature-triggered, so attaching the triggering measurement as a citation is a concrete, harness-rewarded improvement — a candidate sub-goal-3 "score-driven change" (frontend edit to `abletonDevices.ts` → - `npm run verify`). But see the dead-code finding under NEEDS-WIRING before - investing in this path. + `npm run verify`). Moot since 2026-06-11: the NEEDS-WIRING decision below + demoted `abletonDevices.ts` to a research-only baseline, so this candidate is + no longer a *product* improvement path. This is the first real signal toward the Gemini verdict — likely Gemini earns its place on citation + full-surface coverage. Confirm on real renders; the synthetic @@ -169,22 +170,23 @@ factor, onset density) is the app's own projection (`analyzer.ts`); reuse it rat than re-deriving, and only then is the deterministic score a real verdict input rather than the synthetic illustration above. -**FINDING — the deterministic source is currently dead code (affects sub-goal 3 -framing).** `apps/ui/src/data/abletonDevices.ts` is imported **nowhere** in the UI -(no component, no test) — verified by grep. ASA's product recommendations come -entirely from Gemini (`Phase2Result`); the deterministic engine is unwired, -ported-but-unused. Consequence: a "score-driven improvement" to `abletonDevices.ts` -(e.g. the citation-emit fix flagged above) would **not reach any user** — it would -raise a harness number for code nobody runs, which is harness-gaming, not a product -improvement (PURPOSE.md decision #5). So before sub-goal 3 treats the deterministic -path as a real recommendation source, decide one of: -1. **Wire `abletonDevices.ts` into the product** (so its recs — and any citation - improvement — actually reach users), then score + improve it; or -2. **Treat it as a research-only baseline** in the three-source comparison (score - it, but land score-driven *product* improvements on the **Gemini** path - instead — which needs the `chore/...` branch + renders). -This is the honest reason no score-driven improvement was "landed" this session: -the only render-free candidate would have improved dead code. +**FINDING — the deterministic source is dead code on the product path. ✅ DECIDED +2026-06-11: demoted to research-only baseline (option 2).** +`apps/ui/src/data/abletonDevices.ts` is imported **nowhere** in the UI (no +component, no test) — verified by grep. ASA's product recommendations come +entirely from the Phase 2 providers (`Phase2Result`); the deterministic engine is +unwired, ported-but-unused. Consequence: a "score-driven improvement" to +`abletonDevices.ts` (e.g. the citation-emit fix flagged above) would **not reach +any user** — it would raise a harness number for code nobody runs, which is +harness-gaming, not a product improvement (PURPOSE.md decision #5). The owner +resolved the wire-or-demote choice: **demote**. Standing consequences: +1. The module stays where it is as the scored free baseline in the three-source + comparison (its file header now records the status); it is **not** to be + wired into the product. +2. Score-driven *product* improvements land on the **Phase 2 provider** path + instead. The citation-emit candidate above is off the table as a product + change (it remains a legitimate baseline-fairness tweak if the comparison + ever needs it). --- diff --git a/apps/backend/RECOMMENDATION_VERDICT.md b/apps/backend/RECOMMENDATION_VERDICT.md index 2587bc9d..04d90a91 100644 --- a/apps/backend/RECOMMENDATION_VERDICT.md +++ b/apps/backend/RECOMMENDATION_VERDICT.md @@ -88,4 +88,7 @@ not a synthesis artifact. 2. **`melodic_techno_arp_124` Gemini 0 recs** — investigate the parse/shape outlier on a real render before trusting that fixture's contribution. 3. The deterministic `AudioFeatures` projection is approximate (not the app's - `analyzer.ts`); reconcile when wiring the deterministic path for real. + `analyzer.ts`). The wire-or-demote question was resolved 2026-06-11: the + deterministic engine is a research-only baseline and will not be wired into + the product (see `NEEDS.md`), so reconciling the projection matters only for + baseline fairness in this comparison, not for any product path. diff --git a/apps/backend/scripts/emit_deterministic_recs.ts b/apps/backend/scripts/emit_deterministic_recs.ts index f295db0c..141868b9 100644 --- a/apps/backend/scripts/emit_deterministic_recs.ts +++ b/apps/backend/scripts/emit_deterministic_recs.ts @@ -1,8 +1,10 @@ /** * Deterministic recommendation source bridge (GOAL.md sub-goal 3 inner loop). * - * EVAL / RESEARCH ONLY. Wraps the product's deterministic recommendation engine - * (`apps/ui/src/data/abletonDevices.ts`) and emits the scorer's normalized rec + * EVAL / RESEARCH ONLY. Wraps the deterministic recommendation engine + * (`apps/ui/src/data/abletonDevices.ts` — itself research-only since the + * 2026-06-11 demotion decision; see apps/backend/NEEDS.md) and emits the + * scorer's normalized rec * shape so `scripts/evaluate_recommendations.py --source deterministic` can grade * the *free* path without a Gemini call. Importing the real module keeps a single * source of truth — no Python re-port to drift from the TS. diff --git a/apps/ui/src/data/abletonDevices.ts b/apps/ui/src/data/abletonDevices.ts index 1235dc10..bb25559b 100644 --- a/apps/ui/src/data/abletonDevices.ts +++ b/apps/ui/src/data/abletonDevices.ts @@ -3,6 +3,17 @@ * * Maps spectral characteristics to specific Ableton device recommendations. * This is deterministic — no hallucination possible. + * + * RESEARCH-ONLY BASELINE — not on the product path (owner decision, 2026-06-11). + * No UI component or service imports this module; product recommendations come + * from the Phase 2 interpretation providers. Its sole consumer is the eval + * bridge `apps/backend/scripts/emit_deterministic_recs.ts`, which scores it as + * the free deterministic baseline in GOAL.md sub-goal 3's three-source + * comparison. Do NOT wire it into the product, and land score-driven *product* + * improvements on the Phase 2 path instead — raising this module's harness + * score improves code no user runs (see the NEEDS-WIRING decision in + * apps/backend/NEEDS.md). Keep it import-pure: the bridge runs it under Node + * native type-stripping with no build step. */ export interface DeviceRecommendation {