def extract_audio_features(self, audio: mx.array, sr: int) -> dict:
        """
        Extract fast-to-compute features from audio segments.
        Each feature is normalized to [0, 1] range.

        Args:
            audio: Audio array of shape [channels, samples]
            sr: Sample rate

        Returns:
            Dictionary of features, each as a single float value
        """
        if not self.validate_audio(audio):
            return self.get_default_features()

        audio_np = np.array(audio)

        # convert to mono if stereo
        if len(audio_np.shape) == 2 and audio_np.shape[0] > 1:
            audio_np = np.mean(audio_np, axis=0, keepdims=True)

        audio = mx.array(audio_np)

        features = {}

        # rms energy (loudness) - normalized to [0, 1]
        features["energy"] = float(mx.sqrt(mx.mean(audio**2)))

        # spectral centroid - normalized to [0, 1]
        spec_np = np.abs(np.fft.rfft(audio_np))
        freqs_np = np.linspace(0, sr / 2, spec_np.shape[-1])
        spec_sum = np.sum(spec_np) + self.eps
        centroid = np.sum(freqs_np * spec_np.squeeze()) / spec_sum
        features["spectral_centroid"] = float(centroid / (sr / 2))

        # pitch - normalized to [0, 1]
        features["pitch"] = extract_single_pitch_value(audio, sr)

        # scale values to 0-100 range
        for name, value in features.items():
            features[name] = self.scale_values(value)

        return features

        Extract fast-to-compute features from audio segments.
        Each feature is normalized to [0, 1] range.

        Args:
            audio: Audio array of shape [channels, samples]
            sr: Sample rate

        Returns:
            Dictionary of features, each as a single float value
        
mlx_audio/tts/models/outetts/audio_processor.py