"""
Module de gestion et de routage dynamique des peripheriques audio.
Permet d'enumerer et de selectionner dynamiquement les entrees micro et sorties audio
sans regles materielles specifiques hardcodees.
"""
from typing import Any
import sounddevice as sd
from ..core.logger import getMirzaLogger
logger = getMirzaLogger("mirza.audio.devices")
[docs]
class DeviceManager:
"""Selection des peripheriques audio d'entree et de sortie, a chaud depuis le widget.
Attributes
----------
micId : int
Index du micro selectionne.
micName : str
Libelle du micro selectionne.
speakerId : int
Index de la sortie selectionnee.
speakerName : str
Libelle de la sortie selectionnee.
"""
[docs]
def __init__(self):
"""
Initialise le gestionnaire et prend les interfaces par defaut.
Returns
-------
None
"""
self.micId: int = 0
self.micName: str = ""
self.speakerId: int = 0
self.speakerName: str = ""
self.detectDefaultDevices()
[docs]
def getAvailableDevices(self) -> dict[str, list[dict[str, Any]]]:
"""
Renvoie la liste exhaustive de tous les peripheriques audio disponibles.
Returns
-------
dict
Dictionnaire contenant deux cles : 'inputs' et 'outputs', chacune associant
une liste de dictionnaires descriptifs des peripheriques.
"""
allDevices = sd.query_devices()
defaultIn = sd.default.device[0]
defaultOut = sd.default.device[1]
inputs: list[dict[str, Any]] = []
outputs: list[dict[str, Any]] = []
for index, device in enumerate(allDevices):
if device["max_input_channels"] > 0:
inputs.append({
"id": index,
"name": device["name"],
"channels": device["max_input_channels"],
"sampleRate": device["default_samplerate"],
"isDefault": (index == defaultIn),
})
if device["max_output_channels"] > 0:
outputs.append({
"id": index,
"name": device["name"],
"channels": device["max_output_channels"],
"sampleRate": device["default_samplerate"],
"isDefault": (index == defaultOut),
})
return {"inputs": inputs, "outputs": outputs}
[docs]
def detectDefaultDevices(self) -> tuple[int, str, int, str]:
"""
Initialise les peripheriques sur les valeurs par defaut du systeme d'exploitation.
Returns
-------
tuple
Tuple contenant (micId, micName, speakerId, speakerName).
"""
allDevices = sd.query_devices()
defaultIn = sd.default.device[0]
defaultOut = sd.default.device[1]
# Entree par defaut
if (
defaultIn is not None
and isinstance(defaultIn, int)
and 0 <= defaultIn < len(allDevices)
and allDevices[defaultIn].get("max_input_channels", 0) > 0
):
self.micId = defaultIn
self.micName = allDevices[defaultIn]["name"]
else:
for index, device in enumerate(allDevices):
if device.get("max_input_channels", 0) > 0:
self.micId = index
self.micName = device["name"]
break
# Sortie par defaut
if (
defaultOut is not None
and isinstance(defaultOut, int)
and 0 <= defaultOut < len(allDevices)
and allDevices[defaultOut].get("max_output_channels", 0) > 0
):
self.speakerId = defaultOut
self.speakerName = allDevices[defaultOut]["name"]
else:
for index, device in enumerate(allDevices):
if device.get("max_output_channels", 0) > 0:
self.speakerId = index
self.speakerName = device["name"]
break
return self.micId, self.micName, self.speakerId, self.speakerName
[docs]
def setMicDevice(self, deviceId: int) -> bool:
"""
Modifie le microphone actif.
Parameters
----------
deviceId : int
Identifiant entier du peripherique audio d'entree.
Returns
-------
bool
True si le peripherique a ete selectionne avec succes, False sinon.
"""
try:
device = sd.query_devices(deviceId)
if device["max_input_channels"] > 0:
self.micId = deviceId
self.micName = device["name"]
return True
logger.warning("Le peripherique d'entree %s n'a pas le bon nombre de canaux.", deviceId)
except Exception as exc:
logger.warning("Peripherique d'entree %s introuvable : %s", deviceId, exc)
return False
[docs]
def setSpeakerDevice(self, deviceId: int) -> bool:
"""
Modifie la sortie audio active.
Parameters
----------
deviceId : int
Identifiant entier du peripherique audio de sortie.
Returns
-------
bool
True si le peripherique a ete selectionne avec succes, False sinon.
"""
try:
device = sd.query_devices(deviceId)
if device["max_output_channels"] > 0:
self.speakerId = deviceId
self.speakerName = device["name"]
return True
logger.warning("Le peripherique de sortie %s n'a pas le bon nombre de canaux.", deviceId)
except Exception as exc:
logger.warning("Peripherique de sortie %s introuvable : %s", deviceId, exc)
return False