add manual microphone gain and signal meter
This commit is contained in:
1 parent
ee81c519a1
commit
7b9ff4c098
5 files changed
+125
-15
No files matched your search
@@ -0,0 +1,68 @@
|
||||
export class AudioManager {
|
||||
constructor() {
|
||||
this.context = null;
|
||||
this.rawStream = null;
|
||||
this.outputStream = null;
|
||||
this.source = null;
|
||||
this.gainNode = null;
|
||||
this.analyser = null;
|
||||
this.meterData = new Float32Array(1024);
|
||||
this.gain = Number(localStorage.getItem('voicepoc.micGain') || 1);
|
||||
}
|
||||
|
||||
async ensureContext() {
|
||||
if (!this.context) this.context = new AudioContext();
|
||||
await this.context.resume().catch(() => {});
|
||||
return this.context;
|
||||
}
|
||||
|
||||
async attachInput(stream) {
|
||||
await this.ensureContext();
|
||||
this.detachInput();
|
||||
this.rawStream = stream;
|
||||
this.source = this.context.createMediaStreamSource(stream);
|
||||
this.gainNode = this.context.createGain();
|
||||
this.gainNode.gain.value = this.gain;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
this.analyser.fftSize = 1024;
|
||||
const destination = this.context.createMediaStreamDestination();
|
||||
this.source.connect(this.gainNode).connect(this.analyser);
|
||||
this.gainNode.connect(destination);
|
||||
this.outputStream = destination.stream;
|
||||
return this.outputStream;
|
||||
}
|
||||
|
||||
detachInput() {
|
||||
for (const node of [this.source, this.gainNode, this.analyser]) {
|
||||
try { node?.disconnect(); } catch {}
|
||||
}
|
||||
this.outputStream?.getTracks().forEach((track) => track.stop());
|
||||
this.rawStream?.getTracks().forEach((track) => track.stop());
|
||||
this.source = this.gainNode = this.analyser = null;
|
||||
this.outputStream = this.rawStream = null;
|
||||
}
|
||||
|
||||
setGain(value) {
|
||||
this.gain = Math.max(0, Math.min(2, Number(value) || 0));
|
||||
localStorage.setItem('voicepoc.micGain', String(this.gain));
|
||||
if (this.gainNode) this.gainNode.gain.setTargetAtTime(this.gain, this.context.currentTime, 0.015);
|
||||
}
|
||||
|
||||
getLevel() {
|
||||
if (!this.analyser) return { rms: 0, peak: 0, clipping: false };
|
||||
this.analyser.getFloatTimeDomainData(this.meterData);
|
||||
let sum = 0;
|
||||
let peak = 0;
|
||||
for (const sample of this.meterData) {
|
||||
const magnitude = Math.abs(sample);
|
||||
sum += sample * sample;
|
||||
peak = Math.max(peak, magnitude);
|
||||
}
|
||||
const rms = Math.sqrt(sum / this.meterData.length);
|
||||
return { rms, peak, clipping: peak >= 0.98 };
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.detachInput();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user