78 lines
3.2 KiB
Python
78 lines
3.2 KiB
Python
"""Réglages audio Alfred : ALSA carte 1 et états logiques thread-safe."""
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import threading
|
|
|
|
_LOCK = threading.RLock()
|
|
_STATE = {'volume': 70, 'speaker_muted': False, 'mic_muted': False, 'mic_gain': None}
|
|
_CARD = os.getenv('ALFRED_ALSA_CARD', '1')
|
|
_CONTROL = os.getenv('ALFRED_ALSA_VOLUME_CONTROL', 'Master')
|
|
|
|
def _amixer(*args):
|
|
return subprocess.run(['amixer', '-c', _CARD, *args], capture_output=True,
|
|
text=True, timeout=5, check=True).stdout
|
|
|
|
def initialize():
|
|
"""Lit le volume existant sans modifier les réglages audio au démarrage."""
|
|
try:
|
|
output = _amixer('sget', _CONTROL)
|
|
matches = re.findall(r'\[(\d+)%\]', output)
|
|
with _LOCK:
|
|
if matches:
|
|
_STATE['volume'] = int(matches[0])
|
|
# ALFRED_MIC_GAIN_V2 : gain Capture, sans toucher à Mic Boost.
|
|
try:
|
|
gain_out = _amixer('sget', os.getenv('ALFRED_ALSA_CAPTURE_CONTROL', 'Capture'))
|
|
gains = re.findall(r'\[(\d+)%\]', gain_out)
|
|
_STATE['mic_gain'] = int(gains[0]) if gains else None
|
|
except (OSError, subprocess.SubprocessError) as exc:
|
|
print(f'[ALFRED AUDIO] Gain Capture indisponible : {exc}', flush=True)
|
|
# Ne pas déduire speaker_muted du mixer : état propre à Alfred.
|
|
except (OSError, subprocess.SubprocessError) as exc:
|
|
print(f'[ALFRED AUDIO] Lecture ALSA impossible : {exc}', flush=True)
|
|
|
|
def get_settings():
|
|
with _LOCK:
|
|
return dict(_STATE)
|
|
|
|
def update_settings(payload):
|
|
if not isinstance(payload, dict):
|
|
raise ValueError('Un objet JSON est attendu')
|
|
allowed = {'volume', 'speaker_muted', 'mic_muted', 'mic_gain'}
|
|
if set(payload) - allowed:
|
|
raise ValueError('Paramètre audio inconnu')
|
|
with _LOCK:
|
|
new = dict(_STATE)
|
|
if 'volume' in payload:
|
|
v = payload['volume']
|
|
if isinstance(v, bool) or not isinstance(v, (int, float)) or not 0 <= v <= 100:
|
|
raise ValueError('volume doit être entre 0 et 100')
|
|
new['volume'] = round(v)
|
|
if 'mic_gain' in payload:
|
|
v = payload['mic_gain']
|
|
if isinstance(v, bool) or not isinstance(v, (int, float)) or not 0 <= v <= 100:
|
|
raise ValueError('mic_gain doit être entre 0 et 100')
|
|
new['mic_gain'] = round(v)
|
|
for key in ('speaker_muted', 'mic_muted'):
|
|
if key in payload:
|
|
if not isinstance(payload[key], bool):
|
|
raise ValueError(f'{key} doit être un booléen')
|
|
new[key] = payload[key]
|
|
# Le gain Capture ne change que sur demande explicite.
|
|
# Mic Boost et Internal Mic Boost restent inchangés.
|
|
if 'mic_gain' in payload and new['mic_gain'] != _STATE['mic_gain']:
|
|
_amixer('sset', os.getenv('ALFRED_ALSA_CAPTURE_CONTROL', 'Capture'), f"{new['mic_gain']}%")
|
|
if new['volume'] != _STATE['volume']:
|
|
_amixer('sset', _CONTROL, f"{new['volume']}%")
|
|
_STATE.update(new)
|
|
return dict(_STATE)
|
|
|
|
def speaker_muted():
|
|
with _LOCK:
|
|
return _STATE['speaker_muted']
|
|
|
|
def mic_muted():
|
|
with _LOCK:
|
|
return _STATE['mic_muted']
|