/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
TL;DR: Three Lines to Beatgrid Your Audio
If you already know what you’re doing, here’s the pattern:
# Create beatgrid: 120 BPM, 4/4, 3-minute track, first beat at 0.5 secondsgrid = BeatGrid.from_tempo(tempo_bpm=120, length_seconds=180, start_seconds=0.5)# Get all beat times in seconds (numpy array)beat_times = grid.beat_seconds()# Quick look at what we have{"Total beats": len(beat_times),"Total measures": grid.n_measures,"First 4 beats (seconds)": list(beat_times[:4].round(3)),"First 4 measures (seconds)": list(grid.measure_seconds()[:4].round(3)),}
You have audio files. You know the tempo and when the first beat occurs. You want to generate a complete list of beat and measure times.
1.1 The Use Case
Let’s work with three techno tracks that all have: - Tempo: 160 BPM (constant throughout) - Time Signature: 4/4 - Known first-beat offset (measured by ear or beat detection)
# Our test tracks - tempo is constant 160 BPMTEMPO_BPM =160.0BEATS_PER_MEASURE =4# Load audio files to get accurate durations# First-beat offsets are measured by ear or beat detectionTRACK_FILES = {"Ao Céu": {"file": "Ao Céu.m4a", "first_beat": 0.092},"Bye Bye": {"file": "Bye Bye.m4a", "first_beat": 0.035},"Bass Kick": {"file": "Bass Kick.mp3", "first_beat": 0.061},}# Create loaders and extract durations from actual audio filesTRACKS = {}for name, info in TRACK_FILES.items(): loader = AudioLoader.from_file(AUDIO_DIR / info["file"]) TRACKS[name] = {"loader": loader,"duration": loader.duration_seconds,"first_beat": info["first_beat"], }print(f"{name}: {loader.duration_seconds:.3f}s ({loader.format})")
When working with classical music that has complex meter changes, repeats, and pickup measures, you need to create BeatGrids from score data rather than simple tempo information.
5.1 The Use Case: Beethoven String Quartet
Consider Beethoven’s String Quartet Op. 18 No. 4, 4th movement: - Has repeat structures (needs unfolding for audio alignment) - Pickup measure (anacrusis) - 2/2 time signature throughout
We have a recording: StringQuartetEEP_I_Normal_mono.mp3
The score data is available in TSV format with: - Measure coordinates in quarterbeats - Flow control information (repeats, voltas) - Unfolded versions for performance alignment
# This is the score data structure we're working with# (From: tests/data/score/beethoven_op18-4iv_multimodal/ABC/)BEETHOVEN_MEASURES ="""mc mn quarterbeats duration_qb timesig repeats1 0 0 1.0 2/2 start2 1 1 4.0 2/2...9 8 29 3.0 2/2 end10 8 32 1.0 2/2 start..."""# The unfolded version has ~291 measures (with repeats expanded)UNFOLDED_TOTAL_QUARTERS =1116UNFOLDED_MEASURES =291
5.2 Creating BeatGrid from Score (API Draft)
Note: This API is a draft for future implementation. The pattern shows how TimeToAlign! will support creating BeatGrids from rich score metadata.
# API DRAFT: Creating BeatGrid from score measures## This functionality is planned for the Score Integration milestone.# The code below shows the intended API design.# --- FUTURE API ---## from timetoalign.loader import MeasureMapLoader## # Load measure map from score TSV# loader = MeasureMapLoader.from_file(# "tests/data/score/beethoven_op18-4iv_multimodal/ABC/n04op18-4_04_flow_unfolded.measures.tsv"# )## # Create BeatGrid with the actual meter structure# grid = loader.create_beatgrid(# tempo_bpm=120.0, # Performance tempo (from audio analysis or metadata)# start_seconds=0.5, # First beat offset# )## # The grid now has the exact measure structure from the score# # Including partial measures, meter changes, etc.# grid.n_measures # -> 291 (unfolded)# grid.n_beats # -> based on actual time signatures## # Export to Audacity with score measure labels# beatgrid_to_audacity_csv(grid, "beethoven_op18-4iv_beats.txt")# --- END FUTURE API ---# For now, we can approximate with uniform measuresapprox_grid = BeatGrid.from_tempo( tempo_bpm=120.0, # Assumed tempo beats_per_measure=4, # 2/2 = 4 quarter beats per measure length_seconds=540.0, # ~9 minutes (estimated) start_seconds=0.5, name="Beethoven Op.18/4-iv (approximate)",){"Track": "StringQuartetEEP_I_Normal_mono.mp3","Approximate measures": approx_grid.n_measures,"Approximate beats": approx_grid.n_beats,"Note": "Use MeasureMapLoader for exact score structure",}