feat(audio-sequences): beat phase sync and aligned playback
Add bar-phase tracking, audio reset/anchor APIs, BPM holdover, beat-phase sequence switching, sync-phase endpoint, and sample sequence data. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,7 @@ class Zone(Model):
|
||||
"""Preset layout row (stored in ``db/zone.json``); legacy storage was ``tab.json`` / Tab.
|
||||
|
||||
Optional ``content_kind`` on a row: ``\"presets\"`` (preset tiles only) or ``\"sequences\"``
|
||||
(sequence tiles only). Omitted or unknown => both (legacy behaviour).
|
||||
(sequence tiles only). Legacy rows without ``content_kind`` are inferred on load.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -43,6 +43,12 @@ class Zone(Model):
|
||||
if "preset_group_ids" not in doc or not isinstance(doc.get("preset_group_ids"), dict):
|
||||
doc["preset_group_ids"] = {}
|
||||
changed = True
|
||||
if "sequence_ids" not in doc or not isinstance(doc.get("sequence_ids"), list):
|
||||
doc["sequence_ids"] = []
|
||||
changed = True
|
||||
if not self._normalized_content_kind(doc):
|
||||
doc["content_kind"] = self._infer_content_kind(doc)
|
||||
changed = True
|
||||
if changed:
|
||||
self.save()
|
||||
|
||||
@@ -53,6 +59,41 @@ class Zone(Model):
|
||||
kind = doc.get("content_kind")
|
||||
return kind if kind in ("presets", "sequences") else None
|
||||
|
||||
@staticmethod
|
||||
def _preset_ids_in_doc(doc):
|
||||
if not isinstance(doc, dict):
|
||||
return []
|
||||
flat = doc.get("presets_flat")
|
||||
if isinstance(flat, list):
|
||||
return [str(x) for x in flat if x is not None and str(x).strip()]
|
||||
presets = doc.get("presets")
|
||||
if not isinstance(presets, list) or not presets:
|
||||
return []
|
||||
if isinstance(presets[0], str):
|
||||
return [str(x) for x in presets if x is not None and str(x).strip()]
|
||||
if isinstance(presets[0], list):
|
||||
out = []
|
||||
for row in presets:
|
||||
if isinstance(row, list):
|
||||
out.extend(str(x) for x in row if x is not None and str(x).strip())
|
||||
return out
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def _infer_content_kind(cls, doc):
|
||||
kind = cls._normalized_content_kind(doc)
|
||||
if kind:
|
||||
return kind
|
||||
seq_ids = [
|
||||
str(x).strip()
|
||||
for x in (doc.get("sequence_ids") or [])
|
||||
if x is not None and str(x).strip()
|
||||
]
|
||||
preset_ids = cls._preset_ids_in_doc(doc)
|
||||
if seq_ids and not preset_ids:
|
||||
return "sequences"
|
||||
return "presets"
|
||||
|
||||
def _enforce_content_kind_invariants(self, doc):
|
||||
"""Presets-only zones hold no sequences; sequences-only hold no preset tiles."""
|
||||
kind = self._normalized_content_kind(doc)
|
||||
@@ -95,7 +136,8 @@ class Zone(Model):
|
||||
return False
|
||||
patch = data if isinstance(data, dict) else {}
|
||||
self[id_str].update(patch)
|
||||
self._enforce_content_kind_invariants(self[id_str])
|
||||
if "content_kind" in patch:
|
||||
self._enforce_content_kind_invariants(self[id_str])
|
||||
self.save()
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user