BeatGrid

timelines.BeatGrid(
    length,
    beats_per_measure=4,
    beat_unit=Fraction(1, 4),
    start_measure=1,
    start_mn=None,
    anacrusis_quarters=None,
    uid=None,
    name=None,
)

A metrical timeline whose native coordinate axis is quarter notes.

BeatGrid delegates measure-boundary lookup to its attached MetricMap, beat-in-measure lookup to its attached BeatInMeasureMap, and combined measure/beat lookup to its attached MetricalPositionMap. Its public beat queries also apply beat_unit so that a musical beat need not equal a quarter note.

BeatGrid and MetricMap are converging representations of meter. BeatGrid provides the Timeline interface; MetricMap provides an increasingly rich conversion model. Irregular measures, anacrusis, and meter changes modeled by MetricMap.from_boundaries() are intended to become available through BeatGrid as that model develops.

Attributes

Name Type Description
beats_per_measure int Number of beats per measure.
beat_unit Fraction The note value of one beat (e.g., Fraction(1, 4) for quarter note).
start_measure int The number of the first measure (default 1).
quarters_per_measure Fraction Derived: quarters per measure.

C-Maps (automatically created): - quarters -> mc (MetricMap): Integer measure count. - quarters -> beat (BeatInMeasureMap): Beat position as Fraction. - quarters -> {mc, beat} (MetricalPositionMap): Combined output.

Examples

>>> from fractions import Fraction
>>> from timetoalign import ContinuousPhysicalTimeline, TimeUnit
>>>
>>> # Create a beat grid for 4/4 time, 222 measures
>>> grid = BeatGrid(
...     length=Fraction(888, 1),  # 888 quarter notes
...     beats_per_measure=4,
... )
>>>
>>> # Query measure and beat at quarter 100
>>> grid.measure_at(100)  # -> 26 (integer!)
>>> grid.beat_at(100)     # -> Fraction(1, 1) (proper Fraction!)
>>> grid.metrical_position(100)  # -> {"mc": 26, "beat": Fraction(1, 1)}
>>>
>>> # Attach to audio timeline
>>> audio = ContinuousPhysicalTimeline(length=300.0, unit=TimeUnit.seconds)
>>> audio.add_child(grid, offset=1.3)  # First beat at 1.3 seconds
>>>
>>> # Create from tempo
>>> grid2 = BeatGrid.from_tempo(
...     tempo_bpm=120.0,
...     beats_per_measure=4,
...     length_quarters=Fraction(888, 1),
... )

Methods

Name Description
beat_at Get the beat position within the measure at a given quarter-note position.
beat_at_seconds Get the beat number within the measure at a given time in seconds.
beat_quarters All beat positions in quarters. Vectorized O(1).
beat_seconds All beat times in seconds. Vectorized O(1).
downbeat_seconds Alias for measure_seconds(). All downbeat times in seconds.
export_to_csv Export BeatGrid data to a CSV file.
from_dict Create a BeatGrid from a serialized dictionary.
from_tempo Create a BeatGrid from tempo information.
materialize_beats Add Beat events to this timeline at each beat position.
materialize_measures Add Measure events to this timeline at each measure boundary.
measure_at Get the measure count (MC) at a given quarter-note position.
measure_at_seconds Get the measure number at a given time in seconds.
measure_quarters All measure start positions in quarters. Vectorized O(1).
measure_seconds All measure start times in seconds. Vectorized O(1).
metrical_position Get the full metrical position (mc and beat) at a given quarter position.
mn_at Get the measure number label (MN) at a given quarter-note position.
quarter_at Get the quarter-note position for a given measure and beat.
to_dict Convert the grid and its construction parameters to a dictionary.

beat_at

timelines.BeatGrid.beat_at(quarters)

Get the beat position within the measure at a given quarter-note position.

Parameters

Name Type Description Default
quarters CoordinateSpec Position in quarter notes. required

Returns

Name Type Description
Fraction The beat position as Fraction (1-indexed, e.g., Fraction(3, 2) for beat 1.5).

beat_at_seconds

timelines.BeatGrid.beat_at_seconds(seconds)

Get the beat number within the measure at a given time in seconds.

Parameters

Name Type Description Default
seconds float Time position in seconds. required

Returns

Name Type Description
int Beat number (1-indexed).

Raises

Name Type Description
RuntimeError If no tempo information is available.
ValueError If seconds is before the first beat.

beat_quarters

timelines.BeatGrid.beat_quarters()

All beat positions in quarters. Vectorized O(1).

Returns

Name Type Description
'NDArray[np.floating[Any]]' numpy array of beat positions in quarter notes.

Examples

>>> grid = BeatGrid(length=16, beats_per_measure=4)
>>> grid.beat_quarters()
array([ 0.,  1.,  2.,  3.,  4.,  5., ...])

beat_seconds

timelines.BeatGrid.beat_seconds()

All beat times in seconds. Vectorized O(1).

Requires the grid to have been created with from_tempo() and start_seconds, or to have a tempo_map attached.

Returns

Name Type Description
'NDArray[np.floating[Any]]' numpy array of beat times in seconds.

Raises

Name Type Description
RuntimeError If no tempo information is available.

Examples

>>> grid = BeatGrid.from_tempo(tempo_bpm=120, length_seconds=60, start_seconds=0.5)
>>> grid.beat_seconds()[:4]
array([0.5 , 1.0 , 1.5 , 2.0 ])

downbeat_seconds

timelines.BeatGrid.downbeat_seconds()

Alias for measure_seconds(). All downbeat times in seconds.

export_to_csv

timelines.BeatGrid.export_to_csv(
    filepath,
    *,
    format='default',
    labels='beats',
    **kwargs,
)

Export BeatGrid data to a CSV file.

Extends the base Timeline.export_to_csv() with special formats for audio annotation tools.

Parameters

Name Type Description Default
filepath str Output CSV file path. required
format str Output format. Options: - “default”: Standard timestamp table (inherited behavior). - “sonic_visualiser”: Sonic Visualiser / Audacity label track. Two fields (TIME, LABEL) with header row. - “tilia”: Tilia beat track format. Four fields (time, measure, beat, is_first_in_measure). 'default'
labels str What to export when using “sonic_visualiser” format: - “beats”: All beat positions with labels like “M1B1”, “M1B2”. - “measures”: Measure start positions with labels like “M1”, “M2”. - “both”: Both beats and measures. 'beats'
**kwargs Any Additional arguments passed to base export_to_csv() when using “default” format. {}

Returns

Name Type Description
int Number of rows written.

Raises

Name Type Description
RuntimeError If format requires tempo but none is available.
ValueError If format is not recognized.

Examples

>>> grid = BeatGrid.from_tempo(tempo_bpm=120, length_seconds=60)
>>> # Export for Sonic Visualiser
>>> grid.export_to_csv("beats.csv", format="sonic_visualiser")
120
>>> # Export for Tilia
>>> grid.export_to_csv("beats.csv", format="tilia")
120
>>> # Standard timestamp table
>>> grid.export_to_csv("data.csv", format="default")
120

from_dict

timelines.BeatGrid.from_dict(data)

Create a BeatGrid from a serialized dictionary.

The constructor recreates the meter-map family; the serialized conversion_maps list contains only the maps attached beyond that family (for example, a from_tempo tempo map or a user-attached map), so every entry is restored via ConversionMap.from_dict.

Parameters

Name Type Description Default
data dict[str, Any] Dictionary created by :meth:to_dict. required

Returns

Name Type Description
BeatGrid The reconstructed BeatGrid.

from_tempo

timelines.BeatGrid.from_tempo(
    tempo_bpm,
    beats_per_measure=4,
    beat_unit=Fraction(1, 4),
    length_seconds=None,
    length_quarters=None,
    start_seconds=0.0,
    start_measure=1,
    start_mn=None,
    anacrusis_quarters=None,
    uid=None,
    name=None,
)

Create a BeatGrid from tempo information.

You must provide either length_seconds or length_quarters.

Parameters

Name Type Description Default
tempo_bpm float Tempo in beats per minute. required
beats_per_measure int Number of beats per measure. Default 4. 4
beat_unit Fraction Note value of one beat. Default 1/4 (quarter note). Fraction(1, 4)
length_seconds float | None Duration in seconds (converted using tempo). If start_seconds > 0, this should be the TOTAL audio duration; the grid will span from start_seconds to length_seconds. None
length_quarters Fraction | int | None Duration in quarter notes. None
start_seconds float Offset in seconds where the first beat occurs. Default 0.0. Used by beat_seconds() and measure_seconds(). 0.0
start_measure int MC of the first measure. Default 1. 1
start_mn str | None MN label of the first measure. Default: same as start_measure. None
anacrusis_quarters Fraction | None If set, first measure is shorter (pickup). None
uid str | None Explicit unique identifier. None
name str | None Human-readable name. None

Returns

Name Type Description
BeatGrid A new BeatGrid instance with vectorized accessors for beat/measure times.

Raises

Name Type Description
ValueError If neither length_seconds nor length_quarters is provided.
ValueError If both length_seconds and length_quarters are provided.

Examples

>>> # Audio track: 279 seconds, first beat at 0.092s, 160 BPM, 4/4
>>> grid = BeatGrid.from_tempo(
...     tempo_bpm=160.0,
...     beats_per_measure=4,
...     length_seconds=279.0,
...     start_seconds=0.092,
... )
>>> grid.n_measures
186
>>> grid.beat_seconds()[:4]
array([0.092, 0.467, 0.842, 1.217])
>>> grid.measure_seconds()[:4]
array([0.092, 1.592, 3.092, 4.592])

materialize_beats

timelines.BeatGrid.materialize_beats(include_downbeats_only=False)

Add Beat events to this timeline at each beat position.

Parameters

Name Type Description Default
include_downbeats_only bool If True, only create events for beat 1 (downbeats). False

Returns

Name Type Description
int Number of beat events created.

materialize_measures

timelines.BeatGrid.materialize_measures()

Add Measure events to this timeline at each measure boundary.

Creates IntervalEvents for each complete measure.

Returns

Name Type Description
int Number of measure events created.

measure_at

timelines.BeatGrid.measure_at(quarters)

Get the measure count (MC) at a given quarter-note position.

Parameters

Name Type Description Default
quarters CoordinateSpec Position in quarter notes. required

Returns

Name Type Description
int The measure count (integer, 1-indexed by default).

measure_at_seconds

timelines.BeatGrid.measure_at_seconds(seconds)

Get the measure number at a given time in seconds.

Parameters

Name Type Description Default
seconds float Time position in seconds. required

Returns

Name Type Description
int Measure count (MC, 1-indexed by default).

Raises

Name Type Description
RuntimeError If no tempo information is available.
ValueError If seconds is before the first beat.

measure_quarters

timelines.BeatGrid.measure_quarters()

All measure start positions in quarters. Vectorized O(1).

Returns

Name Type Description
'NDArray[np.floating[Any]]' numpy array of measure start positions in quarter notes.

Examples

>>> grid = BeatGrid(length=16, beats_per_measure=4)
>>> grid.measure_quarters()
array([ 0.,  4.,  8., 12.])

measure_seconds

timelines.BeatGrid.measure_seconds()

All measure start times in seconds. Vectorized O(1).

Requires the grid to have been created with from_tempo() and start_seconds, or to have a tempo_map attached.

Returns

Name Type Description
'NDArray[np.floating[Any]]' numpy array of measure start times in seconds.

Raises

Name Type Description
RuntimeError If no tempo information is available.

Examples

>>> grid = BeatGrid.from_tempo(tempo_bpm=120, beats_per_measure=4,
...                            length_seconds=60, start_seconds=0.5)
>>> grid.measure_seconds()[:4]
array([0.5 , 2.5 , 4.5 , 6.5 ])

metrical_position

timelines.BeatGrid.metrical_position(quarters)

Get the full metrical position (mc and beat) at a given quarter position.

Parameters

Name Type Description Default
quarters CoordinateSpec Position in quarter notes. required

Returns

Name Type Description
dict[str, Any] Dictionary with ‘mc’ (int), ‘beat’ (Fraction), and ‘mn’ (str) keys.

mn_at

timelines.BeatGrid.mn_at(quarters)

Get the measure number label (MN) at a given quarter-note position.

Parameters

Name Type Description Default
quarters CoordinateSpec Position in quarter notes. required

Returns

Name Type Description
str | None The measure number label (string like “1”, “0”, “1a”).

quarter_at

timelines.BeatGrid.quarter_at(measure, beat=Fraction(1, 1))

Get the quarter-note position for a given measure and beat.

Parameters

Name Type Description Default
measure int Measure count (MC, uses start_measure as reference). required
beat float | Fraction Beat within the measure (1-indexed). Default Fraction(1, 1). Fraction(1, 1)

Returns

Name Type Description
Fraction Position in quarter notes.

Raises

Name Type Description
ValueError If measure < start_measure or beat < 1.

to_dict

timelines.BeatGrid.to_dict(events=False, external_references=False)

Convert the grid and its construction parameters to a dictionary.

The three metrical maps created by __init__ (meter, beat-in- measure, and metrical-position maps) are excluded from the serialized conversion_maps list, since from_dict rebuilds them from the construction parameters instead. Any other attached conversion map (for example, a tempo map from from_tempo or a user-attached map) is serialized normally.

Parameters

Name Type Description Default
events bool If True, include the "events" key (beat events and any other events added to the grid). False
external_references bool If True, include the "external_references" key, even when empty. False

Returns

Name Type Description
dict[str, Any] A dictionary representation that reconstructs the attached meter maps.