Annexe : Noyau - audio.py

Rôle et utilité

Le script core/audio.py agit comme un traducteur entre l'interface Python de GrimOS et le serveur de son natif de Linux (généralement ALSA via les commandes amixer). Il permet à la barre des tâches de lire le volume actuel et de le modifier.

Implémentation technique

Pistes de modification

Code Source

import subprocess
import re
import threading

class AudioManager:
    def __init__(self, desktop):
        self.desktop = desktop
        self.control_name = self._find_master_control()

    def _find_master_control(self):
        """Tente de trouver le contrôle principal ALSA (Master, PCM, ou autre)."""
        try:
            # Chercher le contrôle 'Master' en premier
            res = subprocess.run(['amixer', 'sget', 'Master'], capture_output=True, text=True)
            if res.returncode == 0:
                return 'Master'

            # Sinon, lister tous les contrôles scontrols
            res = subprocess.run(['amixer', 'scontrols'], capture_output=True, text=True)
            if "PCM" in res.stdout:
                return "PCM"

            # Prendre le premier dispo
            match = re.search(r"Simple mixer control '([^']+)'", res.stdout)
            if match:
                return match.group(1)
        except Exception:
            pass
        return 'Master' # Valeur par défaut de secours

    def get_volume(self):
        try:
            res = subprocess.run(['amixer', 'sget', self.control_name], capture_output=True, text=True)
            if res.returncode == 0:
                # Chercher par ex: [50%]
                match = re.search(r"\[(\d+)%\]", res.stdout)
                if match:
                    return int(match.group(1))
        except Exception:
            pass
        return 0

    def set_volume(self, level):
        try:
            subprocess.run(['amixer', 'sset', self.control_name, f'{level}%', 'unmute'], capture_output=True)
            # Démuter spécifiquement les haut-parleurs au cas où
            subprocess.run(['amixer', 'sset', 'Speaker', 'unmute'], capture_output=True)
        except Exception:
            pass

    def test_audio(self):
        # speaker-test est bloquant, on le lance dans un thread
        def run_test():
            try:
                # Test avec un son wav natif (court et reconnaissable : "Front Center")
                # Si aplay ne trouve pas le fichier, speaker-test fera un bruit rose ou un bip.
                res = subprocess.run(['aplay', '/usr/share/sounds/alsa/Front_Center.wav'], capture_output=True)
                if res.returncode != 0:
                    # Fallback sur speaker-test
                    subprocess.run(['speaker-test', '-t', 'sine', '-f', '440', '-c', '1', '-l', '1'], timeout=2, capture_output=True)
            except Exception:
                pass

        threading.Thread(target=run_test, daemon=True).start()