Captures the TV's audio and sends it out over several transports. The
primary one is HyperHDR: RTP/L16 to a host-side loopback device, since
HyperHDR has no network audio input of its own. A second route renders
the spectrum on the TV and sends FlatBuffers images to port 19400
instead, for setups where touching the host's sound config is not an
option.
native/ the service: capture backends (PulseAudio, ALSA, exec,
test tone, all dlopen-based), DSP, and one file per sink
frontend/ D-pad driven UI at a fixed 1920x1080
servicefiles/ native service manifest plus the boot script
host/ RTP receiver and the loopback installer for the HyperHDR
machine
tools/ build/package, asset generation, Homebrew Channel
manifest, on-TV probe
test/ host-side suites: FlatBuffers and RTP verified against
real decoders, the engine end to end, the page in jsdom
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
124 lines
4.1 KiB
HTML
124 lines
4.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=1920, initial-scale=1">
|
|
<title>Audio Cap</title>
|
|
<link rel="stylesheet" href="css/app.css">
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<header id="header">
|
|
<div class="brand">
|
|
<h1>Audio Cap</h1>
|
|
<div class="subtitle">TV audio to HyperHDR and friends</div>
|
|
</div>
|
|
<div class="header-status">
|
|
<span id="state-pill" class="pill">Connecting</span>
|
|
<span id="uptime" class="uptime"></span>
|
|
</div>
|
|
<button id="power" type="button" class="focusable btn primary">Start</button>
|
|
</header>
|
|
|
|
<nav id="tabs">
|
|
<button type="button" class="focusable tab" data-panel="panel-status">Status</button>
|
|
<button type="button" class="focusable tab" data-panel="panel-sinks">Outputs</button>
|
|
<button type="button" class="focusable tab" data-panel="panel-capture">Capture</button>
|
|
<button type="button" class="focusable tab" data-panel="panel-system">System</button>
|
|
</nav>
|
|
|
|
<main id="content">
|
|
<section id="panel-status" class="panel">
|
|
<div class="card">
|
|
<h2>Level</h2>
|
|
<div class="meter">
|
|
<div class="meter-row">
|
|
<span class="meter-label">Peak</span>
|
|
<div id="meter-peak" class="bar peak"><div class="bar-fill"></div></div>
|
|
<span id="meter-peak-db" class="meter-db">—</span>
|
|
</div>
|
|
<div class="meter-row">
|
|
<span class="meter-label">RMS</span>
|
|
<div id="meter-rms" class="bar rms"><div class="bar-fill"></div></div>
|
|
<span id="meter-rms-db" class="meter-db">—</span>
|
|
</div>
|
|
</div>
|
|
<div id="bands" class="bands"></div>
|
|
<div id="clip" class="clip hidden">Clipping</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Capture</h2>
|
|
<div id="capture-info" class="info-grid"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Outputs</h2>
|
|
<div id="sink-status" class="sink-status">
|
|
<div class="muted">Not running.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="error-card" class="card error hidden">
|
|
<h2>Error</h2>
|
|
<div id="error-text"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="panel-sinks" class="panel hidden">
|
|
<div id="sink-cards"></div>
|
|
</section>
|
|
|
|
<section id="panel-capture" class="panel hidden">
|
|
<div class="card">
|
|
<h2>Source</h2>
|
|
<div id="capture-fields"></div>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Level follower</h2>
|
|
<p class="blurb">
|
|
How quickly the reported level rises and falls. Lower is faster.
|
|
</p>
|
|
<div id="dsp-fields"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="panel-system" class="panel hidden">
|
|
<div class="card">
|
|
<h2>Service</h2>
|
|
<div id="system-fields"></div>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Diagnostics</h2>
|
|
<p class="blurb">
|
|
What the service can see on this TV: audio libraries, capture
|
|
devices and whether it is running as root.
|
|
</p>
|
|
<div class="actions">
|
|
<button id="run-diagnostics" type="button" class="focusable btn">Run diagnostics</button>
|
|
<button id="load-logs" type="button" class="focusable btn">Show log</button>
|
|
<button id="clear-logs" type="button" class="focusable btn">Clear log</button>
|
|
</div>
|
|
<pre id="output" class="output hidden"></pre>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Settings file</h2>
|
|
<div id="config-path" class="info-grid"></div>
|
|
<div class="actions">
|
|
<button id="reset-config" type="button" class="focusable btn danger">Reset to defaults</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<div id="toast" class="toast hidden"></div>
|
|
</div>
|
|
|
|
<script src="js/mock.js"></script>
|
|
<script src="js/luna.js"></script>
|
|
<script src="js/nav.js"></script>
|
|
<script src="js/ui.js"></script>
|
|
<script src="js/app.js"></script>
|
|
</body>
|
|
</html>
|