Source code for pitch_garden.voters.base
"""A module defining the base class of voters."""
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Mapping, Sequence
from typing import Any
from ..interfaces.types import Pitch
[docs]
class BaseVoter(ABC):
"""An abstract base class for voters integrating the outputs of trackers.
A voter receives the outputs of trackers keyed by the tracker names, so that
tracker-specific processing such as alignment and weighting can be applied.
Voters requiring training can override `fit`, and their learned parameters can
be saved and restored via `state_dict` and `load_state_dict`.
"""
[docs]
def fit(
self,
estimates: Sequence[Mapping[str, Pitch]],
references: Sequence[Pitch],
) -> BaseVoter:
"""Fit the voter to training data.
Parameters
----------
estimates : Sequence[Mapping[str, Pitch]]
The outputs of the trackers for each utterance.
references : Sequence[Pitch]
The reference F0 contours for each utterance.
Returns
-------
out : BaseVoter
The fitted voter itself.
"""
return self
[docs]
def check_trackers(self, names: Sequence[str]) -> None:
"""Check that the voter can be used with the trackers.
Parameters
----------
names : Sequence[str]
The names of the trackers.
Raises
------
ValueError
If the voter cannot be used with the trackers.
"""
[docs]
def state_dict(self) -> dict[str, Any]:
"""Get the learned parameters.
Returns
-------
out : dict[str, Any]
The learned parameters, which must be JSON serializable.
"""
return {}
[docs]
def load_state_dict(self, state: Mapping[str, Any]) -> None:
"""Restore the learned parameters.
Parameters
----------
state : Mapping[str, Any]
The learned parameters.
"""
def __call__(self, pitches: Mapping[str, Pitch]) -> Pitch:
"""Integrate the outputs of the trackers.
Parameters
----------
pitches : Mapping[str, Pitch]
The outputs of the trackers keyed by the tracker names. All contours must
be on the same frame grid.
Returns
-------
out : Pitch
The integrated F0 contour.
Raises
------
ValueError
If the inputs are inconsistent.
"""
if len(pitches) == 0:
raise ValueError("At least one pitch contour is required.")
first = next(iter(pitches.values()))
for name, pitch in pitches.items():
if pitch.length != first.length:
raise ValueError(
f"The number of frames of '{name}' ({pitch.length}) differs "
f"from the others ({first.length})."
)
if pitch.frame_period != first.frame_period:
raise ValueError(f"The frame period of '{name}' differs.")
return self.vote(pitches)
[docs]
@abstractmethod
def vote(self, pitches: Mapping[str, Pitch]) -> Pitch:
"""Integrate the validated outputs of the trackers.
Parameters
----------
pitches : Mapping[str, Pitch]
The outputs of the trackers keyed by the tracker names.
Returns
-------
out : Pitch
The integrated F0 contour.
"""