package com.foxx.androidcast.media;

/*********************************************************************
 * PcmLevelAnalyzer.java
 * Created at: Sat 16 May 2026 13:35:48 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class PcmLevelAnalyzer
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 74 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	ae36e5eb1e60c3d77e63238569736b4f33851352c18eb8e29dee2c9022f0a005
**********************************************************************/
/** Peak/RMS analysis for 16-bit little-endian PCM. */
public final class PcmLevelAnalyzer {
    /** ~-46 dBFS; below this counts as silence. */
    public static final float SILENT_PEAK_THRESHOLD = 0.005f;

    public static final class Levels {
        public final float peak;
        public final float rms;
        public final boolean silent;

        Levels(float peak, float rms, boolean silent) {
            this.peak = peak;
            this.rms = rms;
            this.silent = silent;
        }

        public float peakDb() {
            if (peak <= 1e-9f) {
                return -96f;
            }
            return (float) (20.0 * Math.log10(peak));
        }
    }

    private PcmLevelAnalyzer() {}

    public static Levels analyze(byte[] pcm, int length) {
        if (pcm == null || length < 2) {
            return new Levels(0f, 0f, true);
        }
        int samples = length / 2;
        long sumSq = 0;
        int peakAbs = 0;
        for (int i = 0; i + 1 < length; i += 2) {
            short s = (short) ((pcm[i] & 0xff) | (pcm[i + 1] << 8));
            int a = Math.abs(s);
            if (a > peakAbs) {
                peakAbs = a;
            }
            sumSq += (long) s * s;
        }
        float peak = peakAbs / 32768f;
        float rms = samples > 0
                ? (float) Math.sqrt(sumSq / (double) samples) / 32768f
                : 0f;
        boolean silent = peak < SILENT_PEAK_THRESHOLD && rms < SILENT_PEAK_THRESHOLD * 0.7f;
        return new Levels(peak, rms, silent);
    }

    /** 0–100 visual level from RMS (scaled for speech/music). */
    public static int vuPercent(float rms) {
        if (rms <= 0f) {
            return 0;
        }
        float db = (float) (20.0 * Math.log10(rms + 1e-9));
        float norm = (db + 60f) / 60f;
        if (norm < 0f) {
            norm = 0f;
        }
        if (norm > 1f) {
            norm = 1f;
        }
        return Math.round(norm * 100f);
    }
}
