// @ts-nocheck
class VocalTractProcessor extends AudioWorkletProcessor {
    static get parameterDescriptors() {
        return [
            { name: 'tongueIndex', defaultValue: 20, minValue: 5, maxValue: 35 },
            { name: 'tongueDiameter', defaultValue: 2.0, minValue: 0.1, maxValue: 3.5 },
            { name: 'glottisPitch', defaultValue: 120, minValue: 50, maxValue: 500 },
            { name: 'intensity', defaultValue: 0, minValue: 0, maxValue: 1.0 } // 0 = silent, 1 = speaking
        ];
    }

    constructor() {
        super();
        this.N = 44;
        this.R = new Float64Array(this.N); // Right-going waves
        this.L = new Float64Array(this.N); // Left-going waves
        this.reflection = new Float64Array(this.N + 1);
        this.A = new Float64Array(this.N); // Cross-sectional areas
        
        // Glottis state
        this.timeInWaveform = 0;
        
        // Initialize neutral tube
        this.A.fill(2.5);
    }

    updateTractShape(tongueIndex, tongueDiameter) {
        // Base rest diameter
        const restDiameter = 2.5;
        
        for (let i = 0; i < this.N; i++) {
            let diameter = restDiameter;
            
            // Constriction from the tongue
            if (i >= 5 && i <= 35) {
                // Bell curve centered around tongueIndex
                const dist = Math.abs(i - tongueIndex);
                if (dist < 5) {
                    const factor = 0.5 * (1 + Math.cos(Math.PI * dist / 5));
                    diameter -= factor * (restDiameter - tongueDiameter);
                }
            }
            
            // Throat is a bit narrower
            if (i < 10) diameter *= 0.8;
            
            // Clamp
            this.A[i] = Math.max(0.01, diameter * diameter); // Area is proportional to diameter squared
        }

        // Calculate reflection coefficients between segments
        for (let i = 0; i < this.N - 1; i++) {
            this.reflection[i + 1] = (this.A[i] - this.A[i + 1]) / (this.A[i] + this.A[i + 1]);
        }
        
        // Lips (open to air)
        this.reflection[this.N] = -0.85; 
        // Glottis (closed mostly)
        this.reflection[0] = 0.75;
    }

    // Simplified Rosenburg Glottal Pulse Model
    runGlottis(pitch, tenseness, intensity, sampleRate) {
        this.timeInWaveform += pitch / sampleRate;
        if (this.timeInWaveform > 1.0) this.timeInWaveform -= 1.0;

        let glottalOutput = 0;
        // Simple synthetic glottal wave (buzzy sawtooth with some lowpass)
        if (intensity > 0) {
            // Sawtooth
            glottalOutput = (this.timeInWaveform * 2.0 - 1.0) * intensity;
            
            // Add some noise for breath
            const breath = (Math.random() * 2.0 - 1.0) * 0.1 * intensity * (1.0 - tenseness);
            glottalOutput += breath;
        }

        return glottalOutput;
    }

    process(inputs, outputs, parameters) {
        const output = outputs[0];
        if (output.length === 0) return true;
        const channel = output[0];

        // Read param values (only taking the first value of the block for simplicity)
        const tIndex = parameters.tongueIndex[0] !== undefined ? parameters.tongueIndex[0] : 20;
        const tDiam = parameters.tongueDiameter[0] !== undefined ? parameters.tongueDiameter[0] : 2.0;
        const pitch = parameters.glottisPitch[0] !== undefined ? parameters.glottisPitch[0] : 120;
        const intensity = parameters.intensity[0] !== undefined ? parameters.intensity[0] : 0;
        
        this.updateTractShape(tIndex, tDiam);

        for (let i = 0; i < channel.length; i++) {
            const glottalOutput = this.runGlottis(pitch, 0.6, intensity, sampleRate);
            
            // Inject glottal sound into the beginning of the tube (Right-going)
            this.R[0] = glottalOutput + this.reflection[0] * this.L[0];

            // Waveguide scattering math
            for (let j = 1; j < this.N; j++) {
                const r = this.reflection[j];
                const w = r * (this.R[j - 1] + this.L[j]);
                this.R[j] = this.R[j - 1] - w;
                this.L[j - 1] = this.L[j] + w;
            }

            // Lips boundary
            this.L[this.N - 1] = this.R[this.N - 1] * this.reflection[this.N];
            
            // Output is what radiates from the lips (the Right-going wave at the end)
            channel[i] = this.R[this.N - 1];
        }

        return true;
    }
}

registerProcessor('vocal-tract-processor', VocalTractProcessor);
