- Implemented `storage.py` for managing metadata storage, including sample addition, retrieval, and review state management. - Created `training.py` for training a local model using Random Forest, including functions for training and predicting samples. - Developed a web interface in `app.js` for capturing audio samples, managing labels, and training the model. - Added HTML structure in `index.html` for the SnoreStopper control room with sections for sample capture, overnight gathering, training, and status display. - Styled the application with `styles.css` to enhance user experience and interface aesthetics.
108 lines
3.9 KiB
HTML
108 lines
3.9 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>SnoreStopper Control Room</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
<link rel="stylesheet" href="/web/styles.css" />
|
|
</head>
|
|
<body>
|
|
<div class="ambient-backdrop" aria-hidden="true"></div>
|
|
<main class="layout">
|
|
<header class="hero">
|
|
<p class="eyebrow">Self-hosted sleep signal lab</p>
|
|
<h1>SnoreStopper</h1>
|
|
<p>
|
|
Capture ambient sleep audio, label snoring events, and train a local model from your own
|
|
nights.
|
|
</p>
|
|
</header>
|
|
|
|
<section class="panel controls">
|
|
<h2>Capture Sample</h2>
|
|
<div class="row">
|
|
<label for="deviceSelect">Input device</label>
|
|
<select id="deviceSelect"></select>
|
|
</div>
|
|
<div class="row split">
|
|
<div>
|
|
<label for="durationInput">Duration (seconds)</label>
|
|
<input id="durationInput" type="number" min="2" max="90" value="10" />
|
|
</div>
|
|
<div>
|
|
<label for="tagInput">Tag</label>
|
|
<input id="tagInput" type="text" maxlength="80" placeholder="night-1, side-sleep" />
|
|
</div>
|
|
</div>
|
|
<button id="captureButton" class="btn primary">Record Sample</button>
|
|
</section>
|
|
|
|
<section class="panel overnight">
|
|
<h2>Overnight Gatherer</h2>
|
|
<div class="row split overnight-grid">
|
|
<div>
|
|
<label for="overnightHours">Duration (hours)</label>
|
|
<input id="overnightHours" type="number" min="1" max="12" value="8" />
|
|
</div>
|
|
<div>
|
|
<label for="overnightClipSeconds">Clip length (seconds)</label>
|
|
<input id="overnightClipSeconds" type="number" min="2" max="90" value="20" />
|
|
</div>
|
|
<div>
|
|
<label for="overnightIntervalSeconds">Interval (seconds)</label>
|
|
<input id="overnightIntervalSeconds" type="number" min="2" max="90" value="30" />
|
|
</div>
|
|
</div>
|
|
<label class="toggle-row" for="overnightAutoWatch">
|
|
<input id="overnightAutoWatch" type="checkbox" checked />
|
|
Run snore watch on each overnight clip
|
|
</label>
|
|
<div class="actions">
|
|
<button id="overnightStartButton" class="btn primary">Start Overnight</button>
|
|
<button id="overnightStopButton" class="btn ghost">Stop</button>
|
|
</div>
|
|
<pre id="overnightOutput" class="output">No active overnight session.</pre>
|
|
</section>
|
|
|
|
<section class="panel training">
|
|
<h2>Training</h2>
|
|
<p>
|
|
Label samples as <code>snore</code>, <code>not_snore</code>, or <code>unclear</code>, then
|
|
train locally.
|
|
</p>
|
|
<button id="trainButton" class="btn">Train Model</button>
|
|
<pre id="trainingOutput" class="output">No training run yet.</pre>
|
|
</section>
|
|
|
|
<section class="panel status">
|
|
<h2>Status</h2>
|
|
<p id="statusBanner" aria-live="polite">Loading...</p>
|
|
</section>
|
|
|
|
<section class="panel pending">
|
|
<div class="samples-header">
|
|
<h2>Snore Watch Review Queue</h2>
|
|
<button id="refreshPendingButton" class="btn ghost">Refresh Queue</button>
|
|
</div>
|
|
<div id="pendingList" class="pending-list"></div>
|
|
</section>
|
|
|
|
<section class="panel samples">
|
|
<div class="samples-header">
|
|
<h2>Samples</h2>
|
|
<button id="refreshButton" class="btn ghost">Refresh</button>
|
|
</div>
|
|
<div id="samplesList" class="sample-list"></div>
|
|
</section>
|
|
</main>
|
|
|
|
<script src="/web/app.js" defer></script>
|
|
</body>
|
|
</html>
|