"""Audio level computation implementation. Provides RMS and dB level calculation for VU meter display. """ from __future__ import annotations import math from typing import Final import numpy as np from numpy.typing import NDArray class RmsLevelProvider: """RMS-based audio level provider. Computes RMS (Root Mean Square) level from audio frames for VU meter display. """ # Minimum dB value to report (silence threshold) MIN_DB: Final[float] = -60.0 def get_rms(self, frames: NDArray[np.float32]) -> float: """Calculate RMS level from audio frames. Args: frames: Audio samples as float32 array (normalized -1.0 to 1.0). Returns: RMS level normalized to 0.0-1.0 range. """ if len(frames) == 0: return 0.0 # Calculate RMS: sqrt(mean(samples^2)) rms = float(np.sqrt(np.mean(frames.astype(np.float64) ** 2))) # Clamp to 0.0-1.0 range return min(1.0, max(0.0, rms)) def get_db(self, frames: NDArray[np.float32]) -> float: """Calculate dB level from audio frames. Args: frames: Audio samples as float32 array (normalized -1.0 to 1.0). Returns: Level in dB (MIN_DB to 0 range). """ rms = self.get_rms(frames) if rms <= 0: return self.MIN_DB # Convert to dB: 20 * log10(rms) db = 20.0 * math.log10(rms) # Clamp to MIN_DB to 0 range return max(self.MIN_DB, min(0.0, db)) def rms_to_db(self, rms: float) -> float: """Convert RMS value to dB. Args: rms: RMS level (0.0-1.0). Returns: Level in dB (MIN_DB to 0 range). """ if rms <= 0: return self.MIN_DB db = 20.0 * math.log10(rms) return max(self.MIN_DB, min(0.0, db)) def db_to_rms(self, db: float) -> float: """Convert dB value to RMS. Args: db: Level in dB. Returns: RMS level (0.0-1.0). """ return 0.0 if db <= self.MIN_DB else 10.0 ** (db / 20.0)