Source code for quasimoto.sampler.time

"""
A module implementing a simple time-keeper interface.
"""

# built-in
from copy import copy
from typing import Callable, TypeVar

# internal
from quasimoto.wave.writer import DEFAULT_SAMPLE_RATE

T = TypeVar("T", bound="TimeKeeper")

TimeCallback = Callable[[float], None]


[docs] class PeriodMixin: """ A simple class mixin for other classes that have a time-period component. """ def __init__(self, sample_rate: int = DEFAULT_SAMPLE_RATE) -> None: """Initialize this instance.""" # Constants / final. self.sample_rate = sample_rate self.period = 1.0 / self.sample_rate
[docs] def sample_number(self, time: float) -> int: """Determine the sample number that the given time starts.""" return int(time / self.period)
[docs] def num_samples(self, duration_s: float) -> int: """Get the number of samples for a specified amount of time.""" return int(duration_s * self.sample_rate)
[docs] class TimeKeeper(PeriodMixin): """A class for keeping time.""" def __init__( self, time: float = 0.0, sample_rate: int = DEFAULT_SAMPLE_RATE ) -> None: """Initialize this instance.""" super().__init__(sample_rate=sample_rate) self.time = time self.iteration: int = self.sample_number(self.time) self.callbacks: dict[int, list[TimeCallback]] = {}
[docs] def call_at(self, time: float, callback: TimeCallback) -> None: """Register a callback that runs at the specified time.""" assert time > self.time, time index = self.num_samples(time) if index not in self.callbacks: self.callbacks[index] = [] self.callbacks[index].append(callback)
[docs] def call_in(self, time: float, callback: TimeCallback) -> float: """ Register a callback for some relative amount of time in the future. """ call_time = self.time + time self.call_at(self.time + time, callback) return call_time
[docs] def call_sequence(self, *callbacks: tuple[float, TimeCallback]) -> None: """Register a call sequence.""" curr = self.time for offset, callback in callbacks: self.call_at(curr + offset, callback) curr += offset
[docs] def advance(self) -> None: """Advance time forward.""" self.time += self.period self.iteration += 1 # Check for callbacks. callbacks = self.callbacks.get(self.iteration) if callbacks: for callback in callbacks: callback(self.time) del self.callbacks[self.iteration]
def __copy__(self: T) -> T: """Get a copy of this instance.""" result = type(self)(time=self.time, sample_rate=self.sample_rate) result.callbacks = copy(self.callbacks) return result
[docs] def copy(self: T) -> T: """Get a copy of this instance.""" return copy(self)