import abc
import typing
import abjad
from .qeventproxy import QEventProxy
from .qevents import QEvent
from .qgrid import QGrid
from .quantizationjob import QuantizationJob
from .searchtrees import SearchTree, UnweightedSearchTree
[docs]class QTargetItem(abc.ABC):
"""
Abstract class for QTargetBeat and QTargetMeasure.
"""
@abc.abstractproperty
def offset_in_ms(self) -> abjad.Offset:
raise NotImplementedError
@abc.abstractproperty
def duration_in_ms(self) -> abjad.Duration:
raise NotImplementedError
[docs]class QTargetBeat(QTargetItem):
"""
Q-target beat.
Represents a single beat in a quantization target.
.. container:: example
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat
QTargetBeat(beatspan=Duration(1, 8), offset_in_ms=Offset((1500, 1)), search_tree=UnweightedSearchTree(definition={3: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=56, textual_indication=None, custom_markup=None, decimal=False, hide=False))
Not composer-safe.
Used internally by the ``quantize`` function.
"""
### CLASS VARIABLES ###
__slots__ = (
"_beatspan",
"_grouping",
"_offset_in_ms",
"_q_events",
"_q_grid",
"_q_grids",
"_search_tree",
"_tempo",
)
### INITIALIZER ###
def __init__(
self,
beatspan: abjad.typings.Duration | None = None,
offset_in_ms: abjad.Duration | int | None = None,
search_tree: SearchTree | None = None,
tempo: abjad.MetronomeMark | tuple | None = None,
):
beatspan = beatspan or abjad.Duration(0)
beatspan = abjad.Duration(beatspan)
offset_in_ms = offset_in_ms or abjad.Duration(0)
offset_in_ms = abjad.Offset(offset_in_ms)
if search_tree is None:
search_tree = UnweightedSearchTree()
assert isinstance(search_tree, SearchTree)
if isinstance(tempo, tuple):
assert len(tempo) == 2
reference_duration = abjad.Duration(tempo[0])
units_per_minute = tempo[1]
tempo = abjad.MetronomeMark(reference_duration, units_per_minute)
tempo = tempo or abjad.MetronomeMark(abjad.Duration(1, 4), 60)
assert not tempo.is_imprecise
q_events: list[QEvent] = []
q_grids: tuple[QGrid, ...] = ()
self._beatspan = beatspan
self._offset_in_ms = offset_in_ms
self._q_events = q_events
self._q_grid: QGrid | None = None
self._q_grids = q_grids
self._search_tree = search_tree
self._tempo = tempo
### SPECIAL METHODS ###
[docs] def __call__(self, job_id: int) -> typing.Optional[QuantizationJob]:
"""
Calls q-target beat.
Returns quantization job.
"""
if not self.q_events:
return None
assert all(isinstance(x, QEvent) for x in self.q_events)
q_event_proxies = []
for q_event in self.q_events:
q_event_proxy = QEventProxy(
q_event,
self.offset_in_ms,
self.offset_in_ms + self.duration_in_ms,
)
q_event_proxies.append(q_event_proxy)
return QuantizationJob(job_id, self.search_tree, q_event_proxies)
[docs] def __repr__(self):
"""
Gets repr.
"""
return f"{type(self).__name__}(beatspan={self.beatspan!r}, offset_in_ms={self.offset_in_ms!r}, search_tree={self.search_tree!r}, tempo={self.tempo!r})"
### PUBLIC PROPERTIES ###
@property
def beatspan(self) -> abjad.Duration:
"""
Beatspan of q-target beat.
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat.beatspan
Duration(1, 8)
"""
return self._beatspan
@property
def duration_in_ms(self) -> abjad.Duration:
"""
Duration in milliseconds of the q-target beat.
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat.duration_in_ms
Duration(3750, 7)
"""
return self.tempo.duration_to_milliseconds(self.beatspan)
@property
def offset_in_ms(self) -> abjad.Offset:
"""
Offset in milliseconds of q-target beat.
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat.offset_in_ms
Offset((1500, 1))
"""
return self._offset_in_ms
@property
def q_events(self) -> list[QEvent]:
"""
A list for storing ``QEventProxy`` instances.
Used internally by the ``quantize`` function.
"""
return self._q_events
@property
def q_grid(self) -> typing.Optional[QGrid]:
"""
The ``QGrid`` instance selected by a ``Heuristic``.
Used internally by the ``quantize`` function.
"""
return self._q_grid
@property
def q_grids(self) -> tuple[QGrid, ...]:
"""
A tuple of ``QGrids`` generated by a ``QuantizationJob``.
Used internally by the ``quantize`` function.
"""
return self._q_grids
@property
def search_tree(self):
"""
Search tree of q-target beat.
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat.search_tree
UnweightedSearchTree(definition={3: None})
Returns search tree.
"""
return self._search_tree
@property
def tempo(self) -> abjad.MetronomeMark:
"""
MetronomeMark of q-target beat.
>>> beatspan = (1, 8)
>>> offset_in_ms = 1500
>>> search_tree = nauert.UnweightedSearchTree({3: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 56)
>>> q_target_beat = nauert.QTargetBeat(
... beatspan=beatspan,
... offset_in_ms=offset_in_ms,
... search_tree=search_tree,
... tempo=tempo,
... )
>>> q_target_beat.tempo
MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=56, textual_indication=None, custom_markup=None, decimal=False, hide=False)
"""
return self._tempo
[docs]class QTargetMeasure(QTargetItem):
"""
Q-target measure.
Represents a single measure in a measurewise quantization target.
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure
QTargetMeasure(offset_in_ms=Offset((1000, 1)), search_tree=UnweightedSearchTree(definition={2: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False), use_full_measure=False)
.. container:: example
``QTargetMeasures`` group ``QTargetBeats``:
>>> for q_target_beat in q_target_measure.beats:
... print(q_target_beat.offset_in_ms, q_target_beat.duration_in_ms)
1000 1000
2000 1000
3000 1000
4000 1000
.. container:: example
If ``use_full_measure`` is set, the ``QTargetMeasure`` will only ever
contain a single ``QTargetBeat`` instance:
>>> another_q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... use_full_measure=True,
... )
>>> for q_target_beat in another_q_target_measure.beats:
... print(q_target_beat.offset_in_ms, q_target_beat.duration_in_ms)
1000 4000
Not composer-safe.
Used internally by the ``quantize`` function.
"""
### CLASS VARIABLES ###
__slots__ = (
"_beats",
"_offset_in_ms",
"_search_tree",
"_tempo",
"_time_signature",
"_use_full_measure",
)
### INITIALIZER ###
def __init__(
self,
offset_in_ms: abjad.Duration | int | None = None,
search_tree: SearchTree | None = None,
time_signature: tuple[int, int] | None = None,
tempo: abjad.MetronomeMark | tuple | None = None,
use_full_measure: bool = False,
):
offset_in_ms = offset_in_ms or 0
offset_in_ms = abjad.Offset(offset_in_ms)
if time_signature is not None:
assert isinstance(time_signature, abjad.TimeSignature), repr(time_signature)
if search_tree is None:
search_tree = UnweightedSearchTree()
assert isinstance(search_tree, SearchTree)
if isinstance(tempo, tuple):
assert len(tempo) == 2
reference_duration = abjad.Duration(tempo[0])
units_per_minute = tempo[1]
tempo = abjad.MetronomeMark(reference_duration, units_per_minute)
tempo = tempo or abjad.MetronomeMark(abjad.Duration(1, 4), 60)
assert not tempo.is_imprecise
if time_signature is None:
pair = (4, 4)
else:
pair = time_signature.pair
_time_signature = abjad.TimeSignature(pair)
use_full_measure = bool(use_full_measure)
beats = []
if use_full_measure:
beatspan = _time_signature.duration
beat = QTargetBeat(
beatspan=beatspan,
offset_in_ms=offset_in_ms,
search_tree=search_tree,
tempo=tempo,
)
beats.append(beat)
else:
beatspan = abjad.Duration(1, _time_signature.denominator)
current_offset_in_ms = offset_in_ms
beatspan_duration_in_ms = tempo.duration_to_milliseconds(beatspan)
for i in range(_time_signature.numerator):
beat = QTargetBeat(
beatspan=beatspan,
offset_in_ms=current_offset_in_ms,
search_tree=search_tree,
tempo=tempo,
)
beats.append(beat)
current_offset_in_ms += beatspan_duration_in_ms
self._beats = tuple(beats)
self._offset_in_ms = offset_in_ms
self._search_tree = search_tree
self._tempo = tempo
self._time_signature = _time_signature
self._use_full_measure = use_full_measure
### SPECIAL METHODS ###
[docs] def __repr__(self):
"""
Gets repr.
"""
return f"{type(self).__name__}(offset_in_ms={self.offset_in_ms!r}, search_tree={self.search_tree!r}, tempo={self.tempo!r}, use_full_measure={self.use_full_measure!r})"
### PUBLIC PROPERTIES ###
@property
def beats(self) -> tuple:
"""
Gets the tuple of ``QTargetBeats`` contained by the
``QTargetMeasure``.
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> for q_target_beat in q_target_measure.beats:
... q_target_beat
...
QTargetBeat(beatspan=Duration(1, 4), offset_in_ms=Offset((1000, 1)), search_tree=UnweightedSearchTree(definition={2: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False))
QTargetBeat(beatspan=Duration(1, 4), offset_in_ms=Offset((2000, 1)), search_tree=UnweightedSearchTree(definition={2: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False))
QTargetBeat(beatspan=Duration(1, 4), offset_in_ms=Offset((3000, 1)), search_tree=UnweightedSearchTree(definition={2: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False))
QTargetBeat(beatspan=Duration(1, 4), offset_in_ms=Offset((4000, 1)), search_tree=UnweightedSearchTree(definition={2: None}), tempo=MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False))
"""
return self._beats
@property
def duration_in_ms(self) -> abjad.Duration:
"""
The duration in milliseconds of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.duration_in_ms
Duration(4000, 1)
"""
return self.tempo.duration_to_milliseconds(self.time_signature.duration)
@property
def offset_in_ms(self) -> abjad.Offset:
"""
The offset in milliseconds of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.offset_in_ms
Offset((1000, 1))
"""
return self._offset_in_ms
@property
def search_tree(self) -> SearchTree:
"""
The search tree of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.search_tree
UnweightedSearchTree(definition={2: None})
"""
return self._search_tree
@property
def tempo(self) -> abjad.MetronomeMark:
"""
The tempo of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.tempo
MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=60, textual_indication=None, custom_markup=None, decimal=False, hide=False)
"""
return self._tempo
@property
def time_signature(self) -> abjad.TimeSignature:
"""
The time signature of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.time_signature
TimeSignature(pair=(4, 4), hide=False, partial=None)
"""
return self._time_signature
@property
def use_full_measure(self) -> bool:
"""
The ``use_full_measure`` flag of the ``QTargetMeasure``:
.. container:: example
>>> search_tree = nauert.UnweightedSearchTree({2: None})
>>> tempo = abjad.MetronomeMark(abjad.Duration(1, 4), 60)
>>> time_signature = abjad.TimeSignature((4, 4))
>>> q_target_measure = nauert.QTargetMeasure(
... offset_in_ms=1000,
... search_tree=search_tree,
... tempo=tempo,
... time_signature=time_signature,
... )
>>> q_target_measure.use_full_measure
False
"""
return self._use_full_measure