/** Select a robust ASR channel while leaving audible playback untouched. */
export class SpeechChannelSelector {
    selected = -1; // -1 denotes the normal mono downmix.
    candidate = -1;
    candidateBlocks = 0;
    select(input) {
        const frameLength = input[0]?.length ?? 0;
        if (!frameLength)
            return undefined;
        if (input.length === 1)
            return input[0].slice();
        const mixed = new Float32Array(frameLength);
        const rms = input.map((channel) => channelRms(channel, frameLength));
        for (const channel of input)
            for (let index = 0; index < frameLength; index++)
                mixed[index] += (channel[index] ?? 0) / input.length;
        const mixedRms = channelRms(mixed, frameLength);
        let strongest = 0;
        for (let index = 1; index < rms.length; index++)
            if (rms[index] > rms[strongest])
                strongest = index;
        // A 6 dB loss after averaging is a strong indicator of anti-phase or a
        // heavily asymmetric mix. Otherwise averaging retains both alternating speakers.
        const wanted = rms[strongest] > Math.max(1e-6, mixedRms) * 2 ? strongest : -1;
        this.observe(wanted);
        return this.selected < 0 ? mixed : (input[this.selected] ?? mixed).slice();
    }
    observe(wanted) {
        if (wanted === this.selected) {
            this.candidate = wanted;
            this.candidateBlocks = 0;
            return;
        }
        if (wanted !== this.candidate) {
            this.candidate = wanted;
            this.candidateBlocks = 1;
            return;
        }
        this.candidateBlocks += 1;
        // 20 AudioWorklet quanta ~= 53 ms at 48 kHz: enough hysteresis to avoid
        // switching on short transients while still preserving a new talker.
        if (this.candidateBlocks >= 20) {
            this.selected = wanted;
            this.candidateBlocks = 0;
        }
    }
}
function channelRms(channel, length) {
    let sum = 0;
    for (let index = 0; index < length; index++) {
        const value = channel[index] ?? 0;
        sum += value * value;
    }
    return Math.sqrt(sum / Math.max(1, length));
}
