fix: web audio capture buffer size + relay warning
- Use power-of-2 buffer (1024) for ScriptProcessorNode - Accumulate samples and send exact 960-sample frames - Remove unused watch import from relay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::sync::{Mutex, watch};
|
use tokio::sync::Mutex;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
use wzp_proto::MediaTransport;
|
use wzp_proto::MediaTransport;
|
||||||
|
|||||||
@@ -129,19 +129,32 @@ function startAudioCapture() {
|
|||||||
audioCtx = new AudioContext({ sampleRate: SAMPLE_RATE });
|
audioCtx = new AudioContext({ sampleRate: SAMPLE_RATE });
|
||||||
const source = audioCtx.createMediaStreamSource(mediaStream);
|
const source = audioCtx.createMediaStreamSource(mediaStream);
|
||||||
|
|
||||||
// ScriptProcessorNode for capturing raw PCM
|
// ScriptProcessorNode requires power-of-2 buffer. We use 1024 and
|
||||||
// (AudioWorklet would be better but this is simpler for a prototype)
|
// accumulate samples, sending exactly FRAME_SIZE (960) chunks.
|
||||||
scriptNode = audioCtx.createScriptProcessor(FRAME_SIZE, 1, 1);
|
const CAPTURE_BUF = 1024;
|
||||||
|
scriptNode = audioCtx.createScriptProcessor(CAPTURE_BUF, 1, 1);
|
||||||
|
|
||||||
|
let accumulator = new Float32Array(0);
|
||||||
|
|
||||||
scriptNode.onaudioprocess = (e) => {
|
scriptNode.onaudioprocess = (e) => {
|
||||||
if (!active || !ws || ws.readyState !== WebSocket.OPEN) return;
|
if (!active || !ws || ws.readyState !== WebSocket.OPEN) return;
|
||||||
|
|
||||||
const input = e.inputBuffer.getChannelData(0); // Float32 [-1, 1]
|
const input = e.inputBuffer.getChannelData(0);
|
||||||
|
|
||||||
// Convert float32 to int16
|
// Append to accumulator
|
||||||
const pcm = new Int16Array(input.length);
|
const newAcc = new Float32Array(accumulator.length + input.length);
|
||||||
for (let i = 0; i < input.length; i++) {
|
newAcc.set(accumulator);
|
||||||
pcm[i] = Math.max(-32768, Math.min(32767, Math.round(input[i] * 32767)));
|
newAcc.set(input, accumulator.length);
|
||||||
|
accumulator = newAcc;
|
||||||
|
|
||||||
|
// Send complete FRAME_SIZE chunks
|
||||||
|
while (accumulator.length >= FRAME_SIZE) {
|
||||||
|
const frame = accumulator.slice(0, FRAME_SIZE);
|
||||||
|
accumulator = accumulator.slice(FRAME_SIZE);
|
||||||
|
|
||||||
|
const pcm = new Int16Array(FRAME_SIZE);
|
||||||
|
for (let i = 0; i < FRAME_SIZE; i++) {
|
||||||
|
pcm[i] = Math.max(-32768, Math.min(32767, Math.round(frame[i] * 32767)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update level meter
|
// Update level meter
|
||||||
@@ -149,13 +162,13 @@ function startAudioCapture() {
|
|||||||
for (let i = 0; i < pcm.length; i++) maxVal = Math.max(maxVal, Math.abs(pcm[i]));
|
for (let i = 0; i < pcm.length; i++) maxVal = Math.max(maxVal, Math.abs(pcm[i]));
|
||||||
document.getElementById('levelBar').style.width = (maxVal / 32768 * 100) + '%';
|
document.getElementById('levelBar').style.width = (maxVal / 32768 * 100) + '%';
|
||||||
|
|
||||||
// Send as binary
|
|
||||||
ws.send(pcm.buffer);
|
ws.send(pcm.buffer);
|
||||||
framesSent++;
|
framesSent++;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
source.connect(scriptNode);
|
source.connect(scriptNode);
|
||||||
scriptNode.connect(audioCtx.destination); // needed for scriptProcessor to work
|
scriptNode.connect(audioCtx.destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
function playAudio(pcmInt16) {
|
function playAudio(pcmInt16) {
|
||||||
|
|||||||
Reference in New Issue
Block a user