Song and chart data model#

The song module#

Provides the Song class, the central model for chartsets

  • Every input format is converted to a Song instance

  • Every output format is created from a Song instance

Most timing-related info is stored as beat fractions, otherwise a decimal number of seconds is used

jubeatools.song.BeatsTime#

A time measured in beats

jubeatools.song.SecondsTime#

A time measured in seconds

jubeatools.song.beats_time_from_ticks(ticks: int, resolution: int) Fraction#
jubeatools.song.convert_other(f: Callable[[Position, Position], Position]) Callable[[Position, Any], Position]#
class jubeatools.song.Position(x: int, y: int)#

2D integer vector

x: int#
y: int#
class jubeatools.song.NotePosition(x: int, y: int)#

A specific square on the controller. (0, 0) is the top-left button, x goes right, y goes down.

    x →
    0 1 2 3
y 0 □ □ □ □
↓ 1 □ □ □ □
  2 □ □ □ □
  3 □ □ □ □

The main difference with Position is that x and y MUST be between 0 and 3

property index: int#
classmethod from_index(index: int) NotePosition#
classmethod from_raw_position(pos: Position) NotePosition#
x: int#
y: int#
class jubeatools.song.TapNote(time: 'BeatsTime', position: 'NotePosition')#
time: Fraction#
position: NotePosition#
class jubeatools.song.LongNote(time: 'BeatsTime', position: 'NotePosition', duration: 'BeatsTime', tail_tip: 'NotePosition')#
time: Fraction#
position: NotePosition#
duration: Fraction#
tail_tip: NotePosition#
has_straight_tail() bool#
tail_direction() Direction#

Direction in which the tail moves

positions_covered() Iterator[NotePosition]#
class jubeatools.song.Direction(value)#

An enumeration.

UP = 1#
DOWN = 2#
LEFT = 3#
RIGHT = 4#
class jubeatools.song.BPMEvent(time: 'BeatsTime', BPM: 'Decimal')#
time: Fraction#
BPM: Decimal#
class jubeatools.song.Timing(events: 'Sequence[BPMEvent]', beat_zero_offset: 'SecondsTime')#
events: Sequence[BPMEvent]#
beat_zero_offset: Decimal#
class jubeatools.song.Chart(level: 'Optional[Decimal]' = None, timing: 'Optional[Timing]' = None, hakus: 'Optional[Set[BeatsTime]]' = None, notes: 'Sequence[Union[TapNote, LongNote]]' = <factory>)#
level: Optional[Decimal] = None#
timing: Optional[Timing] = None#
hakus: Optional[Set[Fraction]] = None#
notes: Sequence[Union[TapNote, LongNote]]#
class jubeatools.song.Preview(start: Decimal, length: Decimal)#

Frozen so it can be hashed to be deduped using a set when merging Metadata instances

start: Decimal#
length: Decimal#
class jubeatools.song.Metadata(title: 'Optional[str]' = None, artist: 'Optional[str]' = None, audio: 'Optional[Path]' = None, cover: 'Optional[Path]' = None, preview: 'Optional[Preview]' = None, preview_file: 'Optional[Path]' = None)#
title: Optional[str] = None#
artist: Optional[str] = None#
audio: Optional[Path] = None#
cover: Optional[Path] = None#
preview: Optional[Preview] = None#
preview_file: Optional[Path] = None#
classmethod permissive_merge(*metadatas: Metadata) Metadata#

Make the “sum” of all the given metadata instances, if possible. If several instances have different defined values for the same field, merging will fail. Fields with Noneor empty values (empty string or empty path) are conscidered undefined and their values can be replaced by an actual value if supplied by at least one object from the given iterable.

class jubeatools.song.Difficulty(value)#

An enumeration.

BASIC = 'BSC'#
ADVANCED = 'ADV'#
EXTREME = 'EXT'#
class jubeatools.song.Song(metadata: ~jubeatools.song.Metadata, charts: ~typing.Dict[str, ~jubeatools.song.Chart] = <factory>, common_timing: ~typing.Optional[~jubeatools.song.Timing] = None, common_hakus: ~typing.Optional[~typing.Set[~fractions.Fraction]] = None)#

The abstract representation format for all jubeat chart sets. A Song is a set of charts with associated metadata

metadata: Metadata#

Miscellaneous information about the song

charts: Dict[str, Chart]#

A regular dict that maps a difficulty name to a Chart. The names for the usual jubeat difficulties should be "BSC", "ADV", and "EXT"

common_timing: Optional[Timing] = None#

The optional shared timing object that applies to all charts by default

common_hakus: Optional[Set[Fraction]] = None#

The optional shared set of HAKUs that apply to all charts by default

classmethod from_monochart_instances(*songs: Song) Song#
minimize_timings() None#

Turn timings into minimal form : Use the most common timing object as the common timing, if it is used by more than two charts, otherwise each chart gets its own timing object and no common timing is defined

remove_common_timing() None#

Modify the song object so that no chart relies on the common timing object, charts that previously did rely on it now have a chart-specific timing object equal to the old common timing

minimize_hakus() None#

Same deal as “minimize_timings” but with hakus

remove_common_hakus() None#
iter_charts_with_applicable_timing() Iterator[Tuple[str, Chart, Timing]]#
iter_charts() Iterator[Tuple[str, Chart, Timing, Optional[Set[Fraction]]]]#
edit_diffs() Iterator[str]#