class PCMAudioProcessor extends AudioWorkletProcessor {
    constructor() {
        super()
        this._inRate = sampleRate // z.B. 48000
        this._pos = 0 // Phase in Eingangssamples [0, step)
        this._carry = null // letztes Eingangssample des vorigen Blocks
        this._outRate = 24000
    }

    process(inputs) {
        const chs = inputs[0]
        if (!chs || chs.length === 0) return true

        const inF32 = chs[0] // mono Float32
        const step = this._inRate / this._outRate

        // src ggf. mit carry präfixieren, damit i+1 existiert
        let src = inF32
        if (this._carry !== null) {
            const tmp = new Float32Array(1 + inF32.length)
            tmp[0] = this._carry
            tmp.set(inF32, 1)
            src = tmp
        }

        // Anzahl ausgebbarer Samples (lineare Interp.: i+1 < src.length)
        const avail = src.length - 1 - this._pos
        const outLen = avail > 0 ? Math.ceil(avail / step) : 0

        const outI16 = new Int16Array(outLen)

        let pos = this._pos
        for (let k = 0; k < outLen; k++) {
            const i = Math.floor(pos)
            const frac = pos - i

            const x0 = src[i]
            const x1 = src[i + 1] // existiert garantiert durch -1 in avail

            // Linearinterp. + Clamping
            let y = x0 + frac * (x1 - x0)
            if (y > 1) y = 1
            else if (y < -1) y = -1

            // Float32 -> int16 (runden, saturieren)
            const s = y <= -1 ? -0x8000 : Math.round(y * 0x7fff)
            outI16[k] = s

            pos += step
        }

        // Phase für nächsten Block relativ zu dessen src verschieben
        // (src[0] == letztes Sample des aktuellen Eingangs)
        this._pos = pos - (src.length - 1)
        if (this._pos < 0) this._pos = 0 // numerische Sicherheit

        // letztes echtes Eingangssample als carry behalten
        this._carry = src[src.length - 1]

        // zero-copy an die UI (Transfer des Buffers)
        try {
            this.port.postMessage(outI16, [outI16.buffer])
        } catch {
            // Fallback ohne Transferliste (z. B. in älteren Browsern)
            this.port.postMessage(outI16)
        }

        return true
    }
}

registerProcessor("audio-processor-worklet", PCMAudioProcessor)
