Source code for j5.components.piezo

"""Classes for Piezo support."""

from abc import abstractmethod
from datetime import timedelta
from enum import Enum
from typing import Generator, Type, Union

from j5.components.component import Component, Interface


[docs]class Note(float, Enum): """An enumeration of notes. An enumeration of notes from scientific pitch notation and their related frequencies in Hz. """ C6 = 1047.0 D6 = 1174.7 E6 = 1318.5 F6 = 1396.9 G6 = 1568.0 A6 = 1760.0 B6 = 1975.5 C7 = 2093.0 D7 = 2349.3 E7 = 2637.0 F7 = 2793.8 G7 = 3136.0 A7 = 3520.0 B7 = 3951.1 C8 = 4186.0 def __reverse__(self) -> Generator['Note', None, None]: # Type is ignored because of an open bug within mypy # https://github.com/python/typeshed/issues/1590 # https://github.com/python/typeshed/issues/1595 yield from reversed(self.__members__.items()) # type: ignore
Pitch = Union[int, float, Note]
[docs]class PiezoInterface(Interface): """An interface containing the methods required to control an piezo."""
[docs] @abstractmethod def buzz(self, identifier: int, duration: timedelta, frequency: float) -> None: """Queue a pitch to be played.""" raise NotImplementedError # pragma: no cover
[docs]class Piezo(Component): """A standard piezo.""" def __init__(self, identifier: int, backend: PiezoInterface) -> None: self._backend = backend self._identifier = identifier
[docs] @staticmethod def interface_class() -> Type[PiezoInterface]: """Get the interface class that is required to use this component.""" return PiezoInterface
@property def identifier(self) -> int: """An integer to identify the component on a board.""" return self._identifier
[docs] def buzz(self, duration: timedelta, pitch: Pitch) -> None: """Queue a note to be played.""" if type(pitch) is int: pitch = float(pitch) self.verify_pitch(pitch) self.verify_duration(duration) self._backend.buzz(self._identifier, duration, pitch)
[docs] @staticmethod def verify_pitch(pitch: Pitch) -> None: """Verify that a pitch is valid.""" # Verify that the type is correct. pitch_is_float = type(pitch) is float pitch_is_note = type(pitch) is Note if not (pitch_is_float or pitch_is_note): raise TypeError("Pitch must be float or Note") # Verify the length of the pitch is non-zero if pitch < 0: raise ValueError("Frequency must be greater than zero")
[docs] @staticmethod def verify_duration(duration: timedelta) -> None: """Verify that a duration is valid.""" if not isinstance(duration, timedelta): raise TypeError("Duration must be of type datetime.timedelta") if duration < timedelta(seconds=0): raise ValueError("Duration must be greater than or equal to zero.")