Skip to content

Commit 8672b29

Browse files
committed
feat: add smoke test HTML file with audio processing controls
1 parent 9f9a6a3 commit 8672b29

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

test/browser/index.html

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>decibri-web smoke test</title>
7+
<style>
8+
* { box-sizing: border-box; margin: 0; padding: 0; }
9+
body { font-family: system-ui, sans-serif; max-width: 640px; margin: 40px auto; padding: 0 20px; color: #222; }
10+
h1 { font-size: 1.4rem; margin-bottom: 16px; }
11+
.controls { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
12+
button { padding: 8px 16px; font-size: 0.9rem; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; background: #f8f8f8; }
13+
button:hover { background: #e8e8e8; }
14+
button:disabled { opacity: 0.4; cursor: default; }
15+
.format-toggle { margin-bottom: 16px; font-size: 0.9rem; }
16+
.format-toggle label { margin-right: 12px; cursor: pointer; }
17+
.meter-container { height: 20px; background: #eee; border-radius: 4px; margin-bottom: 16px; overflow: hidden; }
18+
.meter-bar { height: 100%; width: 0%; background: #4caf50; transition: width 50ms; }
19+
.stats { font-size: 0.85rem; color: #666; margin-bottom: 12px; }
20+
#log { background: #1a1a1a; color: #0f0; font-family: monospace; font-size: 0.8rem; padding: 12px; border-radius: 4px; height: 320px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; }
21+
</style>
22+
</head>
23+
<body>
24+
<h1>decibri-web smoke test</h1>
25+
26+
<div class="format-toggle">
27+
<strong>Format:</strong>
28+
<label><input type="radio" name="format" value="int16" checked> int16</label>
29+
<label><input type="radio" name="format" value="float32"> float32</label>
30+
</div>
31+
32+
<div class="controls">
33+
<button id="btn-start">Start</button>
34+
<button id="btn-stop" disabled>Stop</button>
35+
<button id="btn-devices">Devices</button>
36+
<button id="btn-version">Version</button>
37+
</div>
38+
39+
<div class="meter-container"><div class="meter-bar" id="meter"></div></div>
40+
<div class="stats" id="stats"></div>
41+
<div id="log"></div>
42+
43+
<script src="../../dist/index.global.js"></script>
44+
<script>
45+
const { Decibri } = DecibriWeb;
46+
const logEl = document.getElementById('log');
47+
const meterEl = document.getElementById('meter');
48+
const statsEl = document.getElementById('stats');
49+
const btnStart = document.getElementById('btn-start');
50+
const btnStop = document.getElementById('btn-stop');
51+
52+
let mic = null;
53+
let chunkCount = 0;
54+
let startTime = 0;
55+
56+
function log(msg) {
57+
const ts = new Date().toISOString().slice(11, 23);
58+
logEl.textContent += `[${ts}] ${msg}\n`;
59+
logEl.scrollTop = logEl.scrollHeight;
60+
}
61+
62+
function getFormat() {
63+
return document.querySelector('input[name="format"]:checked').value;
64+
}
65+
66+
function computeRms(chunk) {
67+
let sum = 0;
68+
if (chunk instanceof Float32Array) {
69+
for (let i = 0; i < chunk.length; i++) sum += chunk[i] * chunk[i];
70+
} else {
71+
for (let i = 0; i < chunk.length; i++) { const s = chunk[i] / 32768; sum += s * s; }
72+
}
73+
return chunk.length > 0 ? Math.sqrt(sum / chunk.length) : 0;
74+
}
75+
76+
btnStart.addEventListener('click', async () => {
77+
const format = getFormat();
78+
chunkCount = 0;
79+
startTime = Date.now();
80+
log(`Creating Decibri({ sampleRate: 16000, format: '${format}', vad: true })`);
81+
82+
mic = new Decibri({ sampleRate: 16000, format, vad: true });
83+
84+
mic.on('data', (chunk) => {
85+
chunkCount++;
86+
const rms = computeRms(chunk);
87+
const pct = Math.min(100, rms * 400);
88+
meterEl.style.width = pct + '%';
89+
meterEl.style.background = pct > 30 ? '#f44336' : pct > 10 ? '#ff9800' : '#4caf50';
90+
91+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
92+
const type = chunk instanceof Int16Array ? 'Int16Array' : 'Float32Array';
93+
statsEl.textContent = `Chunks: ${chunkCount} | Type: ${type} | Length: ${chunk.length} | RMS: ${rms.toFixed(4)} | Elapsed: ${elapsed}s`;
94+
});
95+
96+
mic.on('speech', () => log('EVENT: speech'));
97+
mic.on('silence', () => log('EVENT: silence'));
98+
mic.on('end', () => log('EVENT: end'));
99+
mic.on('close', () => log('EVENT: close'));
100+
mic.on('error', (err) => log('ERROR: ' + err.message));
101+
102+
try {
103+
await mic.start();
104+
log('Started — mic.isOpen = ' + mic.isOpen);
105+
btnStart.disabled = true;
106+
btnStop.disabled = false;
107+
} catch (err) {
108+
log('START FAILED: ' + err.message);
109+
}
110+
});
111+
112+
btnStop.addEventListener('click', () => {
113+
if (mic) {
114+
mic.stop();
115+
log('Stopped — mic.isOpen = ' + mic.isOpen);
116+
mic = null;
117+
meterEl.style.width = '0%';
118+
btnStart.disabled = false;
119+
btnStop.disabled = true;
120+
}
121+
});
122+
123+
document.getElementById('btn-devices').addEventListener('click', async () => {
124+
try {
125+
const devices = await Decibri.devices();
126+
log('Devices:\n' + JSON.stringify(devices, null, 2));
127+
} catch (err) {
128+
log('DEVICES ERROR: ' + err.message);
129+
}
130+
});
131+
132+
document.getElementById('btn-version').addEventListener('click', () => {
133+
log('Version: ' + JSON.stringify(Decibri.version()));
134+
});
135+
</script>
136+
</body>
137+
</html>

0 commit comments

Comments
 (0)