Quickstart
Music exists simultaneously as audio, notation, and images — each with its own coordinate system. TimeToAlign! provides a single framework for connecting them.
Physical
Wall-clock time
Seconds, samples, frames
Logical
Symbolic/musical time
Beats, quarters, ticks
Graphical
Visual coordinates
Pixels, centimetres
This whirlwind tour covers the core concepts in under five minutes. Each section links to a full tutorial.
from pathlib import Path
from timetoalign import BeatGrid, MatchfileLoader, Ms3Loader, TimelineGroup
from timetoalign.loader.midi.performance import PerformanceMidiLoader
from timetoalign.loader.score.partitura import PartituraLoader
from timetoalign.maps import TicksToQuarters
DATA_DIR = Path("." ).resolve().parents[1 ] / "tests" / "data"
/home/laser/miniconda3/envs/timetoalign/lib/python3.11/site-packages/partitura/__init__.py:9: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
Same content, different formats
Before the tour, a quick demonstration of what TimeToAlign! is for. We load Chopin’s Etude Op. 10 No. 3 from both MusicXML and a TSV note list, and check that both loaders see the same musical content.
pt_loader = PartituraLoader()
pt_loader.load(DATA_DIR / "vienna_1x22" / "Chopin_op10_no3.musicxml" )
tsv_loader = Ms3Loader.from_file(
DATA_DIR / "vienna_1x22" / "ms3" / "chopin_op10_no3.notes.tsv"
)
{
"Partitura (MusicXML)" : len (pt_loader.store.notes),
"TSV (MS3 export)" : len (tsv_loader.store.notes),
}
{'Partitura (MusicXML)': 498, 'TSV (MS3 export)': 498}
Both loaders find 498 notes — different formats, same musical content. From here on we use the Partitura loader as the source for the tour.
1. Timelines & Events
Promote the loaded score into a timeline with nested children. (Full tutorial )
score = pt_loader.create_timeline(uid= "score" )
score
ContinuousLogicalTimeline[score] (547 events, 4 children, 3 cmaps)
0 ________________________________ 41.5 quarters
├─ notes 0 ________________________________ 41.5 (498 events)
├─ measures 0 ________________________________ 41.5 (22 events)
├─ controls 0 ______________________________ 40 (22 events)
└─ annotations 0 _________________________ 32.5 (5 events)
2. Timestamps
A cross-section showing coordinates in the root and all children. (Full tutorial )
score.to_dataframe().head(5 )
id
notes:note:000001
0
0
0
0
0
0
0
1
-1/2
notes:note:000002
1/2
1/2
1/2
1/2
1/2
1/2
240
2
0
notes:note:000006
3/4
3/4
3/4
3/4
3/4
3/4
360
17/8
1/4
notes:note:000008
1
1
1
1
1
1
480
9/4
1/2
notes:note:000010
5/4
5/4
5/4
5/4
5/4
5/4
600
19/8
3/4
3. Conversion Maps
Translate between units (e.g., quarters to MIDI ticks). (Full tutorial )
q2t = TicksToQuarters(ppq= 480 ).inverse()
score.add_conversion_map(q2t)
score.convert_to(score.make_coordinate(8 ), target_unit= "ticks" )
4. Timeline Groups
Link timelines for coordinate transfer. (Full tutorial )
perf = PerformanceMidiLoader.from_file(
DATA_DIR / "midi" / "performance" / "rachmaninoff_perf.mid"
).create_timeline(uid= "perf" )
group = TimelineGroup(id = "demo" )
group.add_timeline(score)
group.add_timeline(perf)
ts = group.get_timestamp_at(20.0 , "score" )
ts
TimeStamp interpolated
score
20 quarters
axis
perf
5560 ticks
child
score:ticks
9600 ticks
cmap
floating_measures
11.75 floating_measures
cmap
quarters
19.5 quarters
cmap
score:ticks
9600 ticks
cmap
Try: ts.get(<tl_id>), ts.get_unit(<unit>), ts[<cmap>]
5. Alignment Bundles
Load 22 performances from .match files in one go. (Full tutorial )
match_files = sorted ((DATA_DIR / "vienna_1x22" ).glob("*.match" ))
bundle = MatchfileLoader().load(* match_files).create_bundle()
{"timelines" : len (bundle.timeline_ids), "groups" : len (bundle.group_ids)}
{'timelines': 23, 'groups': 1}
6. Beat Grids
Rapid measure/beat queries for metrical structure. (Full tutorial )
grid = BeatGrid.from_tempo(tempo_bpm= 120 , beats_per_measure= 4 , length_seconds= 30 )
{"quarters" : 6.0 , "measure" : grid.measure_at(6.0 ), "beat" : grid.beat_at(6.0 )}
{'quarters': 6.0, 'measure': 2, 'beat': Fraction(3, 1)}
That’s it. The tutorials that follow unpack each topic in detail; the How-To Guides show real-world workflows.