Source code for psychopy.hardware.speaker

# -*- coding: utf-8 -*-

"""Classes and functions managing physical speaker devices for audio playback.
"""

# Part of the PsychoPy library
# Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
# Distributed under the terms of the GNU General Public License (GPL).

__all__ = [
    "SpeakerDevice",
]

import importlib.metadata
from psychopy.preferences import prefs
from psychopy import logging


[docs] class SpeakerDevice: # selected backend backend = None # known backends backends = { 'ptb': importlib.metadata.EntryPoint( name="ptb", value="psychopy.hardware.speaker.speaker_psychtoolbox:PsychtoolboxSpeakerDevice", group="psychopy.hardware.speaker.backends" ), 'sounddevice': importlib.metadata.EntryPoint( name="sounddevice", value="psychopy.hardware.speaker.speaker_sounddevice:SoundDeviceSpeakerDevice", group="psychopy.hardware.speaker.backends" ) } # alias backend names backends['psychtoolbox'] = backends['ptb'] backends['sd'] = backends['sounddevice'] def __new__(cls, *args, **kwargs): backend = cls.resolveBackend() return backend(*args, **kwargs) @classmethod def resolveBackend(cls): backend = cls.backend # if backend is None, get from prefs if backend is None: backend = prefs.hardware['audioLib'] # handle list if isinstance(backend, (list, tuple)): try: # try to get the first valid backend backend = [ val for val in backend if val in cls.backends ][0] except: # otherwise get the first backend backend = backend[0] # if not present, error if backend not in cls.backends: raise ModuleNotFoundError( f"Invalid value '{backend}' for {cls.__name__}.backend, known backends are: {list(cls.backends)}" ) # import backend try: return cls.backends[backend].load() except ImportError: # if backend fails to import, remove it from .backends del cls.backends[backend] # if there's no backends left, error if not len(cls.backends): raise ImportError( "All audio backends failed to load." ) # try next option cls.backend = list(cls.backends)[0] # log logging.error( f"Failed to load {backend} backend, trying {cls.backend}..." ) return cls.resolveBackend() @staticmethod def getAvailableDevices(): return SpeakerDevice.resolveBackend().getAvailableDevices() @classmethod def __getattr__(cls, key): return getattr(cls.resolveBackend(), key)
# get sound backends from plugins for ep in importlib.metadata.entry_points(group="psychopy.hardware.speaker.backends"): SpeakerDevice.backends[ep.name] = ep

Back to top