feat: add useSilenceDetector composable with Web Audio API amplitude monitoring
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
import { ref, readonly } from 'vue'
|
||||||
|
|
||||||
|
export interface SilenceDetectorOptions {
|
||||||
|
thresholdDb?: number // default -40
|
||||||
|
silenceDurationMs?: number // default 1500
|
||||||
|
minRecordingMs?: number // default 500
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSilenceDetector(options: SilenceDetectorOptions = {}) {
|
||||||
|
const {
|
||||||
|
thresholdDb = -40,
|
||||||
|
silenceDurationMs = 1500,
|
||||||
|
minRecordingMs = 500,
|
||||||
|
} = options
|
||||||
|
|
||||||
|
const amplitude = ref(0)
|
||||||
|
let audioCtx: AudioContext | null = null
|
||||||
|
let intervalId: ReturnType<typeof setInterval> | null = null
|
||||||
|
let silenceMs = 0
|
||||||
|
let startedAt = 0
|
||||||
|
|
||||||
|
function start(stream: MediaStream, onSilence: () => void): void {
|
||||||
|
stop()
|
||||||
|
audioCtx = new AudioContext()
|
||||||
|
const source = audioCtx.createMediaStreamSource(stream)
|
||||||
|
const analyser = audioCtx.createAnalyser()
|
||||||
|
analyser.fftSize = 256
|
||||||
|
source.connect(analyser)
|
||||||
|
|
||||||
|
const data = new Uint8Array(analyser.frequencyBinCount)
|
||||||
|
silenceMs = 0
|
||||||
|
startedAt = Date.now()
|
||||||
|
|
||||||
|
intervalId = setInterval(() => {
|
||||||
|
analyser.getByteFrequencyData(data)
|
||||||
|
const rms = Math.sqrt(data.reduce((s, v) => s + v * v, 0) / data.length) / 255
|
||||||
|
amplitude.value = rms
|
||||||
|
|
||||||
|
const db = rms > 0 ? 20 * Math.log10(rms) : -100
|
||||||
|
if (db < thresholdDb) {
|
||||||
|
silenceMs += 100
|
||||||
|
if (silenceMs >= silenceDurationMs && Date.now() - startedAt >= minRecordingMs) {
|
||||||
|
stop()
|
||||||
|
onSilence()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
silenceMs = 0
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(): void {
|
||||||
|
if (intervalId !== null) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
intervalId = null
|
||||||
|
}
|
||||||
|
if (audioCtx) {
|
||||||
|
audioCtx.close().catch(() => {})
|
||||||
|
audioCtx = null
|
||||||
|
}
|
||||||
|
amplitude.value = 0
|
||||||
|
silenceMs = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return { amplitude: readonly(amplitude), start, stop }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user