--- phase: 02-audio-engine plan: "03" type: execute wave: 3 depends_on: ["02-02"] files_modified: - lightsync/audio/waveform.py - lightsync/api/audio.py - lightsync/frontend/index.html - lightsync/frontend/app.js - lightsync/frontend/style.css autonomous: false requirements: [AUD-05] must_haves: truths: - "Waveform is rendered in the timeline panel from loaded audio" - "Play/pause/seek controls work in the browser transport bar" - "Position cursor moves in real time during playback" - "Transport bar shows current time, duration, and playback state" artifacts: - path: "lightsync/audio/waveform.py" provides: "extract_peaks() function for waveform data" exports: ["extract_peaks"] - path: "lightsync/api/audio.py" provides: "GET /api/audio/waveform endpoint" contains: "extract_peaks" - path: "lightsync/frontend/app.js" provides: "Transport controls, waveform canvas, position tick handler" contains: "waveform" - path: "lightsync/frontend/index.html" provides: "Transport bar HTML, waveform canvas element" contains: "transport" key_links: - from: "lightsync/frontend/app.js" to: "/api/audio/waveform" via: "fetch after audio load" pattern: "fetch.*api/audio/waveform" - from: "lightsync/frontend/app.js" to: "WebSocket tick messages" via: "handleMessage dispatches type=tick to update position display" pattern: 'msg.type.*tick' - from: "lightsync/api/audio.py" to: "lightsync/audio/waveform.py" via: "import extract_peaks" pattern: "from lightsync.audio.waveform import extract_peaks" --- Build the waveform extraction module, wire the waveform HTTP endpoint, and create the browser transport UI (play/pause/seek buttons, time display, waveform canvas with position cursor). Purpose: The user needs visual feedback — waveform shows song structure, transport controls audio, position cursor tracks playback in real time. Output: Working transport bar with play/pause/seek, waveform canvas in timeline panel, real-time position updates from WebSocket ticks. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-audio-engine/02-RESEARCH.md @.planning/phases/02-audio-engine/02-01-SUMMARY.md @.planning/phases/02-audio-engine/02-02-SUMMARY.md From lightsync/audio/engine.py: ```python class MPVEngine: def get_state(self) -> dict[str, Any]: # Returns: {"position": float, "paused": bool, "duration": float|None, "loaded": bool, "file": str|None} ``` WebSocket tick message format: ```json {"type": "tick", "position": 42.1, "paused": false, "duration": 180.5, "loaded": true, "file": "/path/to/song.mp3"} ``` WebSocket commands accepted: ```json {"type": "play"} {"type": "pause"} {"type": "seek", "position": 42.0} {"type": "load", "path": "/path/to/file.mp3"} ``` REST endpoints: ``` POST /api/audio/load — body: {"path": "..."} GET /api/audio/state — returns engine state dict ``` From lightsync/api/audio.py: ```python router = APIRouter() # Already has POST /load and GET /state ``` From lightsync/frontend/index.html: - `
` — currently shows placeholder text - `