V1
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""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']
|
||||
Reference in New Issue
Block a user