export declare const InlineFeaturesExtractor = "\n// Constants\nconst N_FFT = 1024;  // Default FFT size\nconst MAX_FFT_SIZE = 8192;  // Maximum FFT size to prevent memory issues\nconst N_CHROMA = 12;\n\n// FFT Implementation with normalized Hann window\nfunction FFT(n) {\n    this.n = n;\n    this.cosTable = new Float32Array(n / 2);\n    this.sinTable = new Float32Array(n / 2);\n    this.hannWindow = new Float32Array(n);\n    \n    // Match Android implementation with precomputed tables\n    const normalizationFactor = Math.sqrt(2.0 / n);\n    for (var i = 0; i < n / 2; i++) {\n        this.cosTable[i] = Math.cos(2.0 * Math.PI * i / n);\n        this.sinTable[i] = Math.sin(2.0 * Math.PI * i / n);\n    }\n    \n    // Precompute normalized Hann window to match Android\n    for (var i = 0; i < n; i++) {\n        this.hannWindow[i] = normalizationFactor * 0.5 * (1 - Math.cos(2.0 * Math.PI * i / (n - 1)));\n    }\n}\n\nFFT.prototype.transform = function(data) {\n    const n = data.length;\n    \n    // Validate input length is power of 2\n    if ((n & (n - 1)) !== 0) {\n        throw new Error('FFT length must be power of 2');\n    }\n\n    // Use iterative bit reversal instead of recursive\n    const bitReversedIndices = new Uint32Array(n);\n    for (let i = 0; i < n; i++) {\n        let reversed = 0;\n        let j = i;\n        let bits = Math.log2(n);\n        while (bits--) {\n            reversed = (reversed << 1) | (j & 1);\n            j >>= 1;\n        }\n        bitReversedIndices[i] = reversed;\n    }\n\n    // Apply bit reversal\n    for (let i = 0; i < n; i++) {\n        const j = bitReversedIndices[i];\n        if (i < j) {\n            const temp = data[i];\n            data[i] = data[j];\n            data[j] = temp;\n        }\n    }\n\n    // Iterative FFT computation with optimized memory usage\n    for (let step = 1; step < n; step <<= 1) {\n        const jump = step << 1;\n        const angleStep = Math.PI / step;\n\n        for (let group = 0; group < n; group += jump) {\n            for (let pair = group; pair < group + step; pair++) {\n                const match = pair + step;\n                const angle = angleStep * (pair - group);\n                \n                const currentCos = Math.cos(angle);\n                const currentSin = Math.sin(angle);\n\n                const real = currentCos * data[match] - currentSin * data[match + 1];\n                const imag = currentCos * data[match + 1] + currentSin * data[match];\n\n                data[match] = data[pair] - real;\n                data[match + 1] = data[pair + 1] - imag;\n                data[pair] += real;\n                data[pair + 1] += imag;\n            }\n        }\n    }\n};\n\n// Add realInverse method\nFFT.prototype.realInverse = function(powerSpectrum, output) {\n    const n = powerSpectrum.length;\n    const complexData = new Float32Array(n * 2);\n    \n    // Copy power spectrum to complex format\n    for (let i = 0; i < n/2 + 1; i++) {\n        complexData[2 * i] = powerSpectrum[i];\n        if (2 * i + 1 < complexData.length) {\n            complexData[2 * i + 1] = 0;\n        }\n    }\n    \n    // Conjugate for inverse FFT\n    for (let i = 0; i < n; i++) {\n        if (2 * i + 1 < complexData.length) {\n            complexData[2 * i + 1] = -complexData[2 * i + 1];\n        }\n    }\n    \n    this.transform(complexData);\n    \n    // Copy real part to output and scale\n    for (let i = 0; i < n; i++) {\n        output[i] = complexData[2 * i] / n;\n    }\n};\n\n// Add helper functions to match Android\nfunction nextPowerOfTwo(n) {\n    let value = 1;\n    while (value < n) {\n        value *= 2;\n    }\n    return value;\n}\n\nfunction applyHannWindow(samples) {\n    const output = new Float32Array(samples.length);\n    for (let i = 0; i < samples.length; i++) {\n        const multiplier = 0.5 * (1 - Math.cos(2 * Math.PI * i / (samples.length - 1)));\n        output[i] = samples[i] * multiplier;\n    }\n    return output;\n}\n\n// Update spectral feature computation to match Android\nfunction computeSpectralFeatures(segment, sampleRate, featureOptions = {}) {\n    try {\n        // Early return if no spectral features are requested\n        if (!featureOptions.spectralCentroid && \n            !featureOptions.spectralFlatness && \n            !featureOptions.spectralRollOff && \n            !featureOptions.spectralBandwidth &&\n            !featureOptions.magnitudeSpectrum) {\n            return {\n                centroid: 0,\n                flatness: 0,\n                rollOff: 0,\n                bandwidth: 0,\n                magnitudeSpectrum: []\n            };\n        }\n\n        // Ensure we have valid data\n        if (!segment || segment.length === 0) {\n            throw new Error('Invalid segment data');\n        }\n\n        // Process in fixed-size chunks\n        const chunkSize = N_FFT;\n        const numChunks = Math.ceil(segment.length / chunkSize);\n        \n        let results = {\n            centroid: 0,\n            flatness: 0,\n            rollOff: 0,\n            bandwidth: 0,\n            magnitudeSpectrum: new Float32Array(N_FFT / 2 + 1).fill(0)\n        };\n        \n        let validChunks = 0;\n        \n        // Iterate through chunks\n        for (let i = 0; i < numChunks; i++) {\n            const start = i * chunkSize;\n            const end = Math.min(start + chunkSize, segment.length);\n            const chunk = segment.slice(start, end);\n            \n            if (chunk.length < N_FFT / 4) continue; // Skip very small chunks\n\n            // Process the chunk\n            const paddedChunk = new Float32Array(N_FFT);\n            paddedChunk.set(applyHannWindow(chunk));\n\n            const fft = new FFT(N_FFT);\n            fft.transform(paddedChunk);\n\n            // Calculate magnitude spectrum\n            const chunkMagnitudeSpectrum = new Float32Array(N_FFT / 2 + 1);\n            let hasSignal = false;\n            \n            for (let j = 0; j < N_FFT / 2; j++) {\n                const re = paddedChunk[2 * j];\n                const im = paddedChunk[2 * j + 1];\n                const magnitude = Math.sqrt(re * re + im * im);\n                chunkMagnitudeSpectrum[j] = magnitude;\n                if (magnitude > Number.EPSILON) hasSignal = true;\n            }\n            \n            if (!hasSignal) continue;\n            validChunks++;\n\n            // Accumulate results\n            if (featureOptions.spectralCentroid) {\n                const centroid = computeSpectralCentroid(chunkMagnitudeSpectrum, sampleRate);\n                if (!isNaN(centroid)) results.centroid += centroid;\n            }\n            \n            if (featureOptions.spectralFlatness) {\n                const flatness = computeSpectralFlatness(chunkMagnitudeSpectrum);\n                if (!isNaN(flatness)) results.flatness += flatness;\n            }\n            \n            if (featureOptions.spectralRollOff) {\n                const rolloff = computeSpectralRollOff(chunkMagnitudeSpectrum, sampleRate);\n                if (!isNaN(rolloff)) results.rollOff += rolloff;\n            }\n            \n            if (featureOptions.spectralBandwidth && !isNaN(results.centroid)) {\n                const bandwidth = computeSpectralBandwidth(chunkMagnitudeSpectrum, sampleRate, results.centroid);\n                if (!isNaN(bandwidth)) results.bandwidth += bandwidth;\n            }\n            \n            if (featureOptions.magnitudeSpectrum) {\n                for (let j = 0; j < results.magnitudeSpectrum.length; j++) {\n                    results.magnitudeSpectrum[j] += chunkMagnitudeSpectrum[j];\n                }\n            }\n        }\n\n        // Average the accumulated results\n        if (validChunks > 0) {\n            results.centroid /= validChunks;\n            results.flatness /= validChunks;\n            results.rollOff /= validChunks;\n            results.bandwidth /= validChunks;\n            \n            if (featureOptions.magnitudeSpectrum) {\n                for (let i = 0; i < results.magnitudeSpectrum.length; i++) {\n                    results.magnitudeSpectrum[i] /= validChunks;\n                }\n            }\n        }\n\n        return results;\n    } catch (error) {\n        console.error('[Worker] Spectral feature computation error:', error);\n        return {\n            centroid: 0,\n            flatness: 0,\n            rollOff: 0,\n            bandwidth: 0,\n            magnitudeSpectrum: []\n        };\n    }\n}\n\nfunction computeSpectralCentroid(magnitudeSpectrum, sampleRate) {\n    const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);\n    if (sum <= Number.EPSILON) return 0;\n    \n    const weightedSum = magnitudeSpectrum.reduce((acc, value, index) => \n        acc + (index * (sampleRate / N_FFT) * (value || 0)), 0);\n    \n    return weightedSum / sum;\n}\n\nfunction computeSpectralFlatness(powerSpectrum) {\n    // Add small epsilon to avoid log(0)\n    const epsilon = Number.EPSILON;\n    const validSpectrum = powerSpectrum.map(v => Math.max(v, epsilon));\n    \n    const geometricMean = Math.exp(\n        validSpectrum\n            .map(v => Math.log(v))\n            .reduce((a, b) => a + b) / validSpectrum.length\n    );\n    \n    const arithmeticMean =\n        validSpectrum.reduce((a, b) => a + b) / validSpectrum.length;\n        \n    return geometricMean / arithmeticMean;\n}\n\nfunction computeSpectralRollOff(magnitudeSpectrum, sampleRate) {\n    const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0);\n    const rollOffThreshold = totalEnergy * 0.85;\n    let cumulativeEnergy = 0;\n\n    for (let i = 0; i < magnitudeSpectrum.length; i++) {\n        cumulativeEnergy += magnitudeSpectrum[i];\n        if (cumulativeEnergy >= rollOffThreshold) {\n            return (i / magnitudeSpectrum.length) * (sampleRate / 2);\n        }\n    }\n\n    return 0;\n}\n\nfunction computeSpectralBandwidth(magnitudeSpectrum, sampleRate, centroid) {\n    const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);\n    if (sum <= Number.EPSILON) return 0;\n\n    const weightedSum = magnitudeSpectrum.reduce(\n        (acc, value, index) => {\n            const freq = index * sampleRate / (2 * magnitudeSpectrum.length);\n            return acc + (value || 0) * Math.pow(freq - centroid, 2);\n        }, 0\n    );\n\n    return Math.sqrt(weightedSum / sum);\n}\n\nfunction computeChroma(segmentData, sampleRate) {\n    // Ensure we have valid input data\n    if (!segmentData || segmentData.length === 0) {\n        return new Array(N_CHROMA).fill(0);\n    }\n\n    const fftLength = nextPowerOfTwo(Math.max(segmentData.length, N_FFT));\n    const windowed = applyHannWindow(segmentData);\n    const padded = new Float32Array(fftLength);\n    padded.set(windowed.slice(0, Math.min(windowed.length, fftLength)));\n\n    const fft = new FFT(fftLength);\n    try {\n        fft.transform(padded);\n    } catch (e) {\n        console.error('[Worker] FFT transform failed in chromagram:', e);\n        return new Array(N_CHROMA).fill(0);\n    }\n\n    const chroma = new Float32Array(N_CHROMA).fill(0);\n    const freqsPerBin = sampleRate / fftLength;\n    let totalEnergy = 0;\n\n    // First pass: compute magnitudes and total energy\n    for (let i = 0; i < fftLength / 2; i++) {\n        const freq = i * freqsPerBin;\n        if (freq > 20) { // Only consider frequencies above 20 Hz\n            const re = padded[2 * i];\n            const im = padded[2 * i + 1] || 0;\n            const magnitude = Math.sqrt(re * re + im * im);\n            \n            if (magnitude > Number.EPSILON) {\n                // Use a more stable pitch class calculation\n                const midiNote = 69 + 12 * Math.log2(freq / 440.0);\n                const pitchClass = Math.round(midiNote) % 12;\n                \n                if (pitchClass >= 0 && pitchClass < 12) {\n                    chroma[pitchClass] += magnitude;\n                    totalEnergy += magnitude;\n                }\n            }\n        }\n    }\n\n    // Normalize chroma values only if we have energy\n    if (totalEnergy > Number.EPSILON) {\n        for (let i = 0; i < N_CHROMA; i++) {\n            chroma[i] = chroma[i] / totalEnergy;\n        }\n    }\n\n    // Convert to regular array and ensure no NaN values\n    return Array.from(chroma, v => isNaN(v) ? 0 : v);\n}\n\nfunction extractHNR(segmentData) {\n    const frameSize = segmentData.length;\n    const autocorrelation = new Float32Array(frameSize);\n\n    // Compute the autocorrelation iteratively\n    for (let i = 0; i < frameSize; i++) {\n        let sum = 0;\n        for (let j = 0; j < frameSize - i; j++) {\n            sum += segmentData[j] * segmentData[j + i];\n        }\n        autocorrelation[i] = sum;\n    }\n\n    // Find the maximum autocorrelation value iteratively\n    let maxAutocorrelation = -Infinity;\n    for (let i = 1; i < autocorrelation.length; i++) {\n        if (autocorrelation[i] > maxAutocorrelation) {\n            maxAutocorrelation = autocorrelation[i];\n        }\n    }\n\n    // Compute the HNR\n    return autocorrelation[0] !== 0\n        ? 10 * Math.log10(maxAutocorrelation / (autocorrelation[0] - maxAutocorrelation))\n        : 0;\n}\n\nfunction estimatePitch(segment, sampleRate) {\n    // Early validation\n    if (!segment || segment.length < 2 || !sampleRate) return 0;\n\n    try {\n        // Apply Hann window\n        const windowed = applyHannWindow(segment);\n\n        // Pad for FFT\n        const fftLength = nextPowerOfTwo(segment.length * 2);\n        const padded = new Float32Array(fftLength);\n        padded.set(windowed);\n\n        // Perform FFT\n        const fft = new FFT(fftLength);\n        fft.transform(padded);\n\n        // Compute power spectrum\n        const powerSpectrum = new Float32Array(fftLength / 2 + 1);\n        for (let i = 0; i <= fftLength / 2; i++) {\n            const re = padded[2 * i];\n            const im = padded[2 * i + 1] || 0;\n            powerSpectrum[i] = re * re + im * im;\n        }\n\n        // Find peak frequency\n        let maxPower = 0;\n        let peakIndex = 0;\n        const minFreq = 50;  // Minimum frequency to consider (Hz)\n        const maxFreq = 1000; // Maximum frequency to consider (Hz)\n        const minBin = Math.floor(minFreq * fftLength / sampleRate);\n        const maxBin = Math.ceil(maxFreq * fftLength / sampleRate);\n\n        for (let i = minBin; i <= maxBin; i++) {\n            if (powerSpectrum[i] > maxPower) {\n                maxPower = powerSpectrum[i];\n                peakIndex = i;\n            }\n        }\n\n        // Convert peak index to frequency\n        const fundamentalFreq = peakIndex * sampleRate / fftLength;\n\n        // Return 0 if the detected frequency is outside reasonable bounds\n        return (fundamentalFreq >= minFreq && fundamentalFreq <= maxFreq) ? \n            fundamentalFreq : 0;\n\n    } catch (error) {\n        console.error('[Worker] Pitch estimation error:', error);\n        return 0;\n    }\n}\n\n// Unique ID counter\nlet uniqueIdCounter = 0\nlet accumulatedDataPoints = []\nlet lastEmitTime = Date.now()\n\nself.onmessage = function (event) {\n    // Check if this is a reset command\n    if (event.data.command === 'resetCounter') {\n        uniqueIdCounter = event.data.startCounterFrom || 0;\n        console.log('[Worker] Reset counter to', uniqueIdCounter);\n        return; // Exit early, don't process audio\n    }\n\n    // Regular audio processing\n    const {\n        channelData,\n        sampleRate,\n        segmentDurationMs,\n        algorithm,\n        bitDepth,\n        fullAudioDurationMs,\n        numberOfChannels,\n        features: _features,\n        intervalAnalysis = 500,\n        enableLogging,\n        resetCounter,\n        startCounterFrom,\n    } = event.data\n\n    // Also handle reset as part of regular message\n    if (resetCounter) {\n        uniqueIdCounter = startCounterFrom || 0;\n    }\n\n    // Calculate subChunkStartTime safely, defaulting to 0 if fullAudioDurationMs is not a valid number\n    const subChunkStartTime = (typeof fullAudioDurationMs === 'number' && !isNaN(fullAudioDurationMs) && fullAudioDurationMs >= 0)\n                            ? fullAudioDurationMs / 1000\n                            : 0;\n\n    \n    // Create a simple logger that only logs when enabled\n    const logger = enableLogging ? {\n        debug: (...args) => console.debug('[Worker]', ...args),\n        log: (...args) => console.log('[Worker]', ...args),\n        error: (...args) => console.error('[Worker]', ...args)\n    } : {\n        debug: () => {},\n        log: () => {},\n        error: () => {}\n    }\n    logger.log('[Worker] START Feature Extractor - hasData: ' + (event.data ? true : false) + ', channelData: ' + (event.data.channelData ? event.data.channelData.length : 0) + ', fullAudioDurationMs: ' + (event.data.fullAudioDurationMs || 0) + ', sampleRate: ' + (event.data.sampleRate || 0) + ', segmentDurationMs: ' + (event.data.segmentDurationMs || 0) + ', algorithm: ' + (event.data.algorithm || 'none') + ', bitDepth: ' + (event.data.bitDepth || 0) + ', numberOfChannels: ' + (event.data.numberOfChannels || 0) + ', features: ' + (event.data.features ? Object.keys(event.data.features).length : 0) + ', intervalAnalysis: ' + (event.data.intervalAnalysis || 0) + ', dataKeys: ' + (event.data ? Object.keys(event.data).join(',') : ''));\n    \n    const features = _features || {}\n    const bytesPerSample = bitDepth / 8; // Calculate bytes per sample\n\n    const SILENCE_THRESHOLD = 0.01\n    const MIN_SILENCE_DURATION = 1.5 * sampleRate // 1.5 seconds of silence\n    const SPEECH_INERTIA_DURATION = 0.1 * sampleRate // Speech inertia duration in samples\n    const RMS_THRESHOLD = 0.01\n    const ZCR_THRESHOLD = 0.1\n\n    // Placeholder functions for feature extraction\n    const extractMFCC = (segmentData, sampleRate) => {\n        // Implement MFCC extraction logic here\n        return []\n    }\n\n    const extractSpectralCentroid = (segmentData, sampleRate) => {\n        const magnitudeSpectrum = segmentData.map((v) => v * v)\n        const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n        if (sum === 0) return 0\n\n        const weightedSum = magnitudeSpectrum.reduce(\n            (acc, value, index) => acc + index * value,\n            0\n        )\n        return (\n            ((weightedSum / sum) * (sampleRate / 2)) / magnitudeSpectrum.length\n        )\n    }\n\n    const extractSpectralFlatness = (segmentData) => {\n        const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n        const geometricMean = Math.exp(\n            magnitudeSpectrum\n                .map((v) => Math.log(v + Number.MIN_VALUE))\n                .reduce((a, b) => a + b) / magnitudeSpectrum.length\n        )\n        const arithmeticMean =\n            magnitudeSpectrum.reduce((a, b) => a + b) / magnitudeSpectrum.length\n        return arithmeticMean === 0 ? 0 : geometricMean / arithmeticMean\n    }\n\n    const extractSpectralRollOff = (segmentData, sampleRate) => {\n        const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n        const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n        const rollOffThreshold = totalEnergy * 0.85\n        let cumulativeEnergy = 0\n\n        for (let i = 0; i < magnitudeSpectrum.length; i++) {\n            cumulativeEnergy += magnitudeSpectrum[i]\n            if (cumulativeEnergy >= rollOffThreshold) {\n                return (i / magnitudeSpectrum.length) * (sampleRate / 2)\n            }\n        }\n\n        return 0\n    }\n\n    const extractSpectralBandwidth = (segmentData, sampleRate) => {\n        const centroid = extractSpectralCentroid(segmentData, sampleRate)\n        const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n        const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n        if (sum === 0) return 0\n\n        const weightedSum = magnitudeSpectrum.reduce(\n            (acc, value, index) => acc + value * Math.pow(index - centroid, 2),\n            0\n        )\n        return Math.sqrt(weightedSum / sum)\n    }\n\n    const extractChromagram = (segmentData, sampleRate) => {\n        return [] // TODO implement\n    }\n\n    /**\n     * Creates a features object based on requested features\n     */\n    function createFeaturesObject(\n        features,\n        maxAmp,\n        rms,\n        sumSquares,\n        zeroCrossings,\n        remainingSamples,\n        spectralFeatures,\n        channelData,\n        startIdx,\n        endIdx,\n        sampleRate,\n        numberOfChannels,\n        bytesPerSample\n    ) {\n        // If no features are requested, return undefined\n        if (!Object.values(features).some(function(v) { return v; })) {\n            return undefined;\n        }\n\n        const result = {};\n        \n        if (features.energy) {\n            result.energy = sumSquares;\n        }\n        if (features.rms) {\n            result.rms = rms;\n        }\n        // Always include min/max amplitude if any features are requested\n        result.minAmplitude = -maxAmp;\n        result.maxAmplitude = maxAmp;\n        \n        if (features.zcr) {\n            result.zcr = zeroCrossings / remainingSamples;\n        }\n        if (features.spectralCentroid) {\n            result.spectralCentroid = spectralFeatures.centroid;\n        }\n        if (features.spectralFlatness) {\n            result.spectralFlatness = spectralFeatures.flatness;\n        }\n        if (features.spectralRolloff) {\n            result.spectralRolloff = spectralFeatures.rollOff;\n        }\n        if (features.spectralBandwidth) {\n            result.spectralBandwidth = spectralFeatures.bandwidth;\n        }\n        if (features.chromagram) {\n            result.chromagram = computeChroma(channelData.slice(startIdx, endIdx), sampleRate);\n        }\n        if (features.hnr) {\n            result.hnr = extractHNR(channelData.slice(startIdx, endIdx));\n        }\n        if (features.pitch) {\n            result.pitch = estimatePitch(channelData.slice(startIdx, endIdx), sampleRate);\n        }\n        \n        return result;\n    }\n\n    function extractWaveform(\n        channelData,\n        sampleRate,\n        segmentDurationMs,\n        numberOfChannels,\n        bytesPerSample\n    ) {\n        const logger = enableLogging ? {\n            debug: (...args) => console.debug('[Worker]', ...args),\n            log: (...args) => console.log('[Worker]', ...args),\n            error: (...args) => console.error('[Worker]', ...args)\n        } : {\n            debug: () => {},\n            log: () => {},\n            error: () => {}\n        }\n\n        // Calculate amplitude range\n        let min = Infinity\n        let max = -Infinity\n        for (let i = 0; i < channelData.length; i++) {\n            min = Math.min(min, channelData[i])\n            max = Math.max(max, channelData[i])\n        }\n\n        const totalSamples = channelData.length\n        const durationMs = (totalSamples / sampleRate) * 1000\n        \n        // Calculate fixed segment sizes\n        const samplesPerSegment = Math.floor(sampleRate * (segmentDurationMs / 1000));\n        const numPoints = Math.floor(totalSamples / samplesPerSegment);\n        const remainingSamples = totalSamples % samplesPerSegment;\n\n        const dataPoints = []\n\n        // Process full segments\n        for (let i = 0; i < numPoints; i++) {\n            const startIdx = i * samplesPerSegment\n            const endIdx = startIdx + samplesPerSegment\n            \n            let sumSquares = 0\n            let maxAmp = 0\n            let zeroCrossings = 0\n\n            // Calculate segment features\n            for (let j = startIdx; j < endIdx; j++) {\n                const value = channelData[j]\n                sumSquares += value * value\n                maxAmp = Math.max(maxAmp, Math.abs(value))\n                if (j > 0 && value * channelData[j - 1] < 0) {\n                    zeroCrossings++\n                }\n            }\n\n            const rms = Math.sqrt(sumSquares / samplesPerSegment)\n            const startTime = subChunkStartTime + (startIdx / sampleRate)\n            const endTime = subChunkStartTime + (endIdx / sampleRate)\n            // Calculate byte positions correctly based on numberOfChannels and bytesPerSample\n            const startPosition = startIdx * numberOfChannels * bytesPerSample\n            const endPosition = endIdx * numberOfChannels * bytesPerSample\n\n            var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);\n\n            const dataPoint = {\n                id: uniqueIdCounter++,\n                amplitude: maxAmp,\n                rms,\n                startTime,\n                endTime,\n                dB: 20 * Math.log10(rms + 1e-6),\n                silent: rms < 0.01,\n                startPosition,\n                endPosition,\n                samples: samplesPerSegment,\n            }\n\n            // Extract features if any are requested\n            const extractedFeatures = createFeaturesObject(\n                features,\n                maxAmp,\n                rms,\n                sumSquares,\n                zeroCrossings,\n                samplesPerSegment,\n                spectralFeatures,\n                channelData,\n                startIdx,\n                endIdx,\n                sampleRate,\n                numberOfChannels,\n                bytesPerSample\n            );\n            \n            if (extractedFeatures) {\n                dataPoint.features = extractedFeatures;\n            }\n\n            dataPoints.push(dataPoint)\n        }\n\n        // Handle remaining samples if they exist and are enough to process\n        if (remainingSamples > samplesPerSegment / 4) { // Only process if we have at least 1/4 of a segment\n            const startIdx = numPoints * samplesPerSegment\n            const endIdx = totalSamples\n            \n            let sumSquares = 0\n            let maxAmp = 0\n            let zeroCrossings = 0\n\n            for (let j = startIdx; j < endIdx; j++) {\n                const value = channelData[j]\n                sumSquares += value * value\n                maxAmp = Math.max(maxAmp, Math.abs(value))\n                if (j > 0 && value * channelData[j - 1] < 0) {\n                    zeroCrossings++\n                }\n            }\n\n            const rms = Math.sqrt(sumSquares / remainingSamples)\n            const startTime = subChunkStartTime + (startIdx / sampleRate);\n            const endTime = subChunkStartTime + (endIdx / sampleRate);\n            // Calculate byte positions correctly based on numberOfChannels and bytesPerSample\n            const startPosition = startIdx * numberOfChannels * bytesPerSample\n            const endPosition = endIdx * numberOfChannels * bytesPerSample\n\n            var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);\n\n            const dataPoint = {\n                id: uniqueIdCounter++,\n                amplitude: maxAmp,\n                rms,\n                startTime,\n                endTime,\n                dB: 20 * Math.log10(rms + 1e-6),\n                silent: rms < 0.01,\n                startPosition,\n                endPosition,\n                samples: remainingSamples,\n            }\n\n            logger.log('[Worker] extractWaveform - dataPoint', dataPoint)\n            // Extract features if any are requested\n            const extractedFeatures = createFeaturesObject(\n                features,\n                maxAmp,\n                rms,\n                sumSquares,\n                zeroCrossings,\n                remainingSamples,\n                spectralFeatures,\n                channelData,\n                startIdx,\n                endIdx,\n                sampleRate,\n                numberOfChannels,\n                bytesPerSample\n            );\n            \n            if (extractedFeatures) {\n                dataPoint.features = extractedFeatures;\n            }\n\n            dataPoints.push(dataPoint)\n        }\n\n        return {\n            durationMs,\n            dataPoints,\n            amplitudeRange: { min, max },\n            rmsRange: {\n                min: 0,\n                max: Math.max(Math.abs(min), Math.abs(max))\n            },\n            extractionTimeMs: Date.now() - lastEmitTime\n        }\n    }\n\n    try {\n        const result = extractWaveform(\n            channelData,\n            sampleRate,\n            segmentDurationMs,\n            numberOfChannels || 1, // Default to 1 channel if not provided\n            bytesPerSample\n        )\n\n        // Send complete result immediately\n        self.postMessage({\n            command: 'features',\n            result: {\n                bitDepth,\n                samples: channelData.length,\n                numberOfChannels,\n                sampleRate,\n                segmentDurationMs,\n                durationMs: result.durationMs,\n                dataPoints: result.dataPoints,\n                amplitudeRange: result.amplitudeRange,\n                rmsRange: result.rmsRange,\n            }\n        })\n    } catch (error) {\n        console.error('[Worker] Error', {\n            message: error.message,\n            stack: error.stack\n        });\n        \n        self.postMessage({ \n            error: {\n                message: error.message,\n                stack: error.stack,\n                name: error.name\n            }\n        });\n    }\n}\n";
//# sourceMappingURL=InlineFeaturesExtractor.web.d.ts.map