Beatmap API¶
Parse, inspect and re-encode .osu files.
Beatmap¶
- class parsecore.Beatmap.beatmap.Beatmap(format_version, general, editor, metadata, difficulty, events, timing_points, colors, hit_objects)[source]¶
Bases:
objectA fully parsed
.osubeatmap.Bundles every section (general, metadata, difficulty, events, timing points, hit objects, colours, editor) and exposes convenient accessors plus decoding and encoding entry points.
- Parameters:
format_version (int)
general (General)
editor (Editor)
metadata (Metadata)
difficulty (Difficulty)
events (Events)
timing_points (TimingPointsState)
colors (Colors)
hit_objects (HitObjectsState)
- general: General¶
- editor: Editor¶
- metadata: Metadata¶
- difficulty: Difficulty¶
- events: Events¶
- timing_points: TimingPointsState¶
- colors: Colors¶
- hit_objects: HitObjectsState¶
- property control_points: ControlPoints¶
Return the beatmap’s timing/difficulty/sample/effect control points.
- property mode: GameMode¶
Return the beatmap’s game mode.
Sections¶
Parser and data model for the [General] section of a .osu file.
- exception parsecore.Beatmap.section.general.ParseGeneralError(message)[source]
Bases:
ExceptionRaised when a line in the
[General]section cannot be parsed.- Parameters:
message (str)
- class parsecore.Beatmap.section.general.GeneralKey(*values)[source]
Bases:
EnumRecognised keys of the
[General]section.- AudioFilename = 'AudioFilename'
- AudioLeadIn = 'AudioLeadIn'
- AudioHash = 'AudioHash'
- PreviewTime = 'PreviewTime'
- Countdown = 'Countdown'
- SampleSet = 'SampleSet'
- StackLeniency = 'StackLeniency'
- Mode = 'Mode'
- LetterboxInBreaks = 'LetterboxInBreaks'
- StoryFireInFront = 'StoryFireInFront'
- UseSkinSprites = 'UseSkinSprites'
- AlwaysShowPlayfield = 'AlwaysShowPlayfield'
- OverlayPosition = 'OverlayPosition'
- SkinPreference = 'SkinPreference'
- EpilepsyWarning = 'EpilepsyWarning'
- CountdownOffset = 'CountdownOffset'
- SpecialStyle = 'SpecialStyle'
- WidescreenStoryboard = 'WidescreenStoryboard'
- SamplesMatchPlaybackRate = 'SamplesMatchPlaybackRate'
- classmethod from_str(s)[source]
Return the
GeneralKeymatching a raw key string.- Parameters:
s (
str) – The key text as it appears in the file.- Return type:
GeneralKey- Returns:
The matching enum member.
- Raises:
ValueError – If the key is not a recognised general key.
- class parsecore.Beatmap.section.general.General[source]
Bases:
objectParsed contents of the
[General]section with osu!-stable defaults.- audio_filename: str
- audio_lead_in: int
- preview_time: int
- countdown: CountdownType
- sample_bank: SampleBank
- stack_leniency: float
- mode: GameMode
- letterbox_in_breaks: bool
- special_style: bool
- widescreen_storyboard: bool
- epilepsy_warning: bool
- samples_match_playback_rate: bool
- parse_general(line)[source]
Parse a single
[General]line into this instance.Unknown keys are ignored. Recognised keys update the corresponding field in place.
- parsecore.Beatmap.section.general.GeneralState
alias of
General
Parser and data model for the [Metadata] section of a .osu file.
- exception parsecore.Beatmap.section.metadata.ParseMetadataError(message)[source]
Bases:
ExceptionRaised when a line in the
[Metadata]section cannot be parsed.- Parameters:
message (str)
- class parsecore.Beatmap.section.metadata.MetadataKey(*values)[source]
Bases:
EnumRecognised keys of the
[Metadata]section.- Title = 'Title'
- TitleUnicode = 'TitleUnicode'
- Artist = 'Artist'
- ArtistUnicode = 'ArtistUnicode'
- Creator = 'Creator'
- Version = 'Version'
- Source = 'Source'
- Tags = 'Tags'
- BeatmapID = 'BeatmapID'
- BeatmapSetID = 'BeatmapSetID'
- classmethod from_str(s)[source]
Return the
MetadataKeymatching a raw key string.- Parameters:
s (
str) – The key text as it appears in the file.- Return type:
MetadataKey- Returns:
The matching enum member.
- Raises:
ValueError – If the key is not a recognised metadata key.
- class parsecore.Beatmap.section.metadata.Metadata[source]
Bases:
objectParsed contents of the
[Metadata]section (title, artist, creator, IDs, …).- title: str
- title_unicode: str
- artist: str
- artist_unicode: str
- creator: str
- version: str
- source: str
- tags: str
- beatmap_id: int
- beatmap_set_id: int
- parse_metadata(line)[source]
Parse a single
[Metadata]line into this instance.Unknown keys are ignored; recognised keys update the matching field in place.
- parsecore.Beatmap.section.metadata.MetadataState
alias of
Metadata
Parser and data model for the [Difficulty] section of a .osu file.
- exception parsecore.Beatmap.section.difficulty.ParseDifficultyError(message)[source]
Bases:
ExceptionRaised when a line in the
[Difficulty]section cannot be parsed.- Parameters:
message (str)
- class parsecore.Beatmap.section.difficulty.DifficultyKey(*values)[source]
Bases:
EnumRecognised keys of the
[Difficulty]section.- HPDrainRate = 'HPDrainRate'
- CircleSize = 'CircleSize'
- OverallDifficulty = 'OverallDifficulty'
- ApproachRate = 'ApproachRate'
- SliderMultiplier = 'SliderMultiplier'
- SliderTickRate = 'SliderTickRate'
- classmethod from_str(s)[source]
Return the
DifficultyKeymatching a raw key string.- Parameters:
s (
str) – The key text as it appears in the file.- Return type:
DifficultyKey- Returns:
The matching enum member.
- Raises:
ValueError – If the key is not a recognised difficulty key.
- class parsecore.Beatmap.section.difficulty.Difficulty[source]
Bases:
objectDifficulty settings of a beatmap (HP, CS, OD, AR, slider multiplier/tick rate).
- hp_drain_rate: float
- circle_size: float
- overall_difficulty: float
- approach_rate: float
- slider_multiplier: float
- slider_tick_rate: float
- class parsecore.Beatmap.section.difficulty.DifficultyState(format_version=14)[source]
Bases:
objectAccumulates
[Difficulty]values while decoding.The beatmap format version is tracked because older versions default the approach rate to the overall difficulty when
ApproachRateis absent.- Parameters:
format_version (int)
- has_approach_rate: bool
- difficulty: Difficulty
Parser and data model for the [TimingPoints] section (control points).
- exception parsecore.Beatmap.section.timing_points.ParseTimingPointsError(message)[source]
Bases:
ExceptionRaised when a line in the
[TimingPoints]section cannot be parsed.- Parameters:
message (str)
- class parsecore.Beatmap.section.timing_points.EffectFlags[source]
Bases:
objectBit flags of a timing point’s effect column (kiai, omit first barline).
- NONE = 0
- KIAI = 1
- OMIT_FIRST_BAR_LINE = 8
- class parsecore.Beatmap.section.timing_points.TimingPoint(time=0.0, beat_len=1000.0, omit_first_bar_line=False, time_signature=4)[source]
Bases:
objectAn uninherited (red) timing point defining a beat length from a time onward.
- time: float
- beat_len: float
- omit_first_bar_line: bool
- time_signature: int
- class parsecore.Beatmap.section.timing_points.DifficultyPoint(time=0.0, slider_velocity=1.0, generate_ticks=True)[source]
Bases:
objectAn inherited (green) point overriding slider velocity from a time onward.
- time: float
- slider_velocity: float
- generate_ticks: bool
- class parsecore.Beatmap.section.timing_points.SamplePoint(time=0.0, sample_bank=SampleBank.Normal, sample_volume=100, custom_sample_bank=0)[source]
Bases:
objectA point overriding the hit-sound sample bank and volume from a time onward.
- time: float
- sample_bank: SampleBank
- sample_volume: int
- custom_sample_bank: int
- class parsecore.Beatmap.section.timing_points.EffectPoint(time=0.0, kiai=False, scroll_speed=1.0)[source]
Bases:
objectA point carrying effect state (kiai time, scroll speed) from a time onward.
- time: float
- kiai: bool
- scroll_speed: float
- class parsecore.Beatmap.section.timing_points.ControlPoints(timing_points=<factory>, difficulty_points=<factory>, effect_points=<factory>, sample_points=<factory>)[source]
Bases:
objectAll timing/difficulty/sample/effect points of a beatmap, kept time-sorted.
- Parameters:
- timing_points: list[TimingPoint]
- difficulty_points: list[DifficultyPoint]
- effect_points: list[EffectPoint]
- sample_points: list[SamplePoint]
- difficulty_point_at(time)[source]
Return the difficulty point in effect at
time.- Parameters:
time (
float) – The time in milliseconds.- Return type:
DifficultyPoint- Returns:
The active difficulty point (a default if none precedes
time).
- effect_point_at(time)[source]
Return the effect point in effect at
time.- Parameters:
time (
float) – The time in milliseconds.- Return type:
EffectPoint- Returns:
The active effect point (a default if none precedes
time).
- sample_point_at(time)[source]
Return the sample point in effect at
time.- Parameters:
time (
float) – The time in milliseconds.- Return type:
SamplePoint- Returns:
The active sample point (a default if none precedes
time).
- timing_point_at(time)[source]
Return the timing point in effect at
time.- Parameters:
time (
float) – The time in milliseconds.- Return type:
TimingPoint- Returns:
The active timing point (the first one if
timeprecedes all points).
- add_timing(point)[source]
Insert a timing point, keeping the list time-sorted.
- Parameters:
point (
TimingPoint) – The timing point to add.- Return type:
- add_difficulty(point)[source]
Insert a difficulty point unless it is redundant.
- Parameters:
point (
DifficultyPoint) – The difficulty point to add.- Return type:
- add_effect(point)[source]
Insert an effect point unless it is redundant.
- Parameters:
point (
EffectPoint) – The effect point to add.- Return type:
- class parsecore.Beatmap.section.timing_points.TimingPointsState(mode, default_bank, default_volume)[source]
Bases:
objectAccumulates control points while decoding, resolving pending sample state.
- general_mode
- general_default_sample_bank
- general_default_sample_volume
- pending_time: float
- pending_timing: TimingPoint | None
- pending_difficulty: DifficultyPoint | None
- pending_effect: EffectPoint | None
- pending_sample: SamplePoint | None
- control_points
- push_point(time, point, timing_change)[source]
Buffer a parsed control point at a given time.
Hit objects & sliders¶
Parser and data model for the [HitObjects] section (circles, sliders, …).
- exception parsecore.Beatmap.section.hit_objects.hit_objects.ParseHitObjectsError(message)[source]
Bases:
ExceptionRaised when a line in the
[HitObjects]section cannot be parsed.- Parameters:
message (str)
- Return type:
None
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectType[source]
Bases:
objectBit flags of a hit object’s type column (circle, slider, spinner, hold, new combo).
- CIRCLE = 1
- SLIDER = 2
- NEW_COMBO = 4
- SPINNER = 8
- COMBO_OFFSET = 112
- HOLD = 128
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitSampleDefaultName(*values)[source]
Bases:
EnumDefault hit-sound sample names (normal, whistle, finish, clap).
- Normal = 'hitnormal'
- Whistle = 'hitwhistle'
- Finish = 'hitfinish'
- Clap = 'hitclap'
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitSampleInfo(name_default, name_file, bank, suffix, volume, custom_sample_bank, bank_specified, is_layered)[source]
Bases:
objectA resolved hit sample: bank, name/file, volume and layering.
- Parameters:
- name_default: HitSampleDefaultName | None
- bank: SampleBank
- volume: int
- custom_sample_bank: int
- bank_specified: bool
- is_layered: bool
- class parsecore.Beatmap.section.hit_objects.hit_objects.SampleBankInfo(filename=None, bank_for_normal=None, bank_for_addition=None, volume=0, custom_sample_bank=0)[source]
Bases:
objectWorking sample-bank state while parsing one hit object’s hit sounds.
- Parameters:
- bank_for_normal: SampleBank | None
- bank_for_addition: SampleBank | None
- volume: int
- custom_sample_bank: int
- read_custom_sample_bank(parts, banks_only)[source]
Parse the trailing
sampleSet:...fields of a hit object.
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectCircle(pos, new_combo, combo_offset)[source]
Bases:
objectA hit circle at a position.
- pos: Pos
- new_combo: bool
- combo_offset: int
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectSpinner(pos, duration, new_combo)[source]
Bases:
objectA spinner with a start and end time.
- pos: Pos
- duration: float
- new_combo: bool
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectHold(pos_x, duration)[source]
Bases:
objectAn osu!mania hold note (column plus duration).
- pos_x: float
- duration: float
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectSlider(pos, new_combo, combo_offset, path, node_samples, repeat_count, velocity, edge_sounds=<factory>)[source]
Bases:
objectA slider: path, repeats, per-node samples and edge sounds.
- Parameters:
- pos: Pos
- new_combo: bool
- combo_offset: int
- path: SliderPath
- repeat_count: int
- velocity: float
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObject(start_time, kind, samples)[source]
Bases:
objectA single parsed hit object (its kind plus shared start time and samples).
- Parameters:
- start_time: float
- kind: HitObjectCircle | HitObjectSpinner | HitObjectHold | HitObjectSlider
- samples: list[HitSampleInfo]
- parsecore.Beatmap.section.hit_objects.hit_objects.is_linear(p0, p1, p2)[source]
Return whether three points are collinear.
- parsecore.Beatmap.section.hit_objects.hit_objects.convert_points(curve_points, points, end_points, first, offset)[source]
Parse one segment of a slider path string into control points.
- Parameters:
- Return type:
- parsecore.Beatmap.section.hit_objects.hit_objects.convert_path_str(point_str, offset)[source]
Parse a full slider path string into control points.
- class parsecore.Beatmap.section.hit_objects.hit_objects.HitObjectsState[source]
Bases:
objectAccumulates hit objects while decoding the
[HitObjects]section.- hit_objects: list[HitObject]
- last_object_was_spinner()[source]
Return whether the previously parsed object was a spinner.
- Return type:
- Returns:
Trueif the last object was a spinner (affects new-combo handling).
Slider path geometry: control points, curve types and length calculation.
- class parsecore.Beatmap.section.hit_objects.slider.PathType(kind, degree=None)[source]
Bases:
objectThe interpolation type of a slider segment (linear, bezier, catmull, perfect circle).
- Parameters:
kind (SplineType)
degree (int | None)
- kind: SplineType
- class parsecore.Beatmap.section.hit_objects.slider.PathControlPoint(pos, path_type=None)[source]
Bases:
objectA slider anchor point, optionally starting a new segment of a given type.
- Parameters:
pos (Pos)
path_type (PathType | None)
- pos: Pos
- path_type: PathType | None
- class parsecore.Beatmap.section.hit_objects.slider.Curve(mode, points, expected_len)[source]
Bases:
objectA sampled slider path built from its control points.
The path is approximated exactly like osu!-stable (piecewise bezier, catmull and circular-arc segments) and then clamped/extended to the expected length so distances match the game.
- dist()[source]
Return the total sampled path length.
- Return type:
- Returns:
The curve length in osu! pixels.
- class parsecore.Beatmap.section.hit_objects.slider.SliderPath(mode, control_points, expected_dist)[source]
Bases:
objectA slider’s control points plus its expected length, lazily sampled into a
Curve.- mode: GameMode
- control_points: list[PathControlPoint]
- curve()[source]
Return the sampled curve, building it on first access.
- Return type:
Curve- Returns:
The cached
Curvefor this path.
- class parsecore.Beatmap.section.hit_objects.slider.SliderEventType(*values)[source]
Bases:
EnumThe kind of event emitted while traversing a slider (head, tick, repeat, tail).
- Head = 0
- Tick = 1
- Repeat = 2
- LastTick = 3
- Tail = 4
- class parsecore.Beatmap.section.hit_objects.slider.SliderEvent(kind, span_idx, span_start_time, time, path_progress)[source]
Bases:
objectA scoring event on a slider at a given time (head, tick, repeat or tail).
- Parameters:
- kind: SliderEventType
- span_idx: int
- span_start_time: float
- time: float
- path_progress: float
- parsecore.Beatmap.section.hit_objects.slider.generate_slider_events(start_time, span_duration, velocity, tick_dist, total_dist, span_count)[source]
Yield the scoring events of a slider in time order.
Reproduces osu!-stable’s tick/repeat/tail placement.
- Parameters:
start_time (
float) – The slider’s start time in milliseconds.span_duration (
float) – Duration of a single span (one traversal).velocity (
float) – Slider velocity in pixels per millisecond.tick_dist (
float) – Distance between ticks in pixels.total_dist (
float) – Total path length in pixels.span_count (
int) – Number of spans (repeats + 1).
- Yields:
Each
SliderEventin chronological order.- Return type:
Generator[SliderEvent, None, None]
Utilities¶
- class parsecore.Beatmap.utils.Pos(x=0.0, y=0.0)[source]¶
Bases:
objectA 2D position/vector whose arithmetic is computed in 32-bit floats.
Every operation routes through
f32()so that distances, dot products and curve math reproduce osu!’s float behaviour exactly.- length_squared()[source]¶
Return the squared length (
x*x + y*y) in f32.- Return type:
- Returns:
The squared magnitude of the vector.
- length()[source]¶
Return the vector length in f32.
- Return type:
- Returns:
The Euclidean magnitude, computed as osu! does.
- parsecore.Beatmap.utils.f32(value)[source]¶
Round a double to IEEE-754 single precision (32-bit float).
osu! and osu!lazer perform position and several timing calculations in 32-bit floats. Mirroring that here is what keeps parsecore bit-exact with the game, so this helper is applied wherever the reference uses
float(Rustf32).