Ejemplo n.º 1
0
def compute_density_graph(events: List[konami.Event],
                          end_time: int) -> List[int]:
    events_by_type = group_by(events, lambda e: e.command)
    buckets: DefaultDict[int, int] = defaultdict(int)
    for tap in events_by_type[konami.Command.PLAY]:
        bucket = int((tap.time / end_time) * 120)
        buckets[bucket] += 1

    for long in events_by_type[konami.Command.LONG]:
        press_bucket = int((long.time / end_time) * 120)
        buckets[press_bucket] += 1
        duration = konami.EveLong.from_value(long.value).duration
        release_time = long.time + duration
        release_bucket = int((release_time / end_time) * 120)
        buckets[release_bucket] += 1

    res = []
    for i in range(0, 120, 2):
        # The jbsq density graph in a array of nibbles, the twist is that for
        # some obscure reason each pair of nibbles is swapped in the byte ...
        # little-endianness is a hell of a drug, don't do drugs kids ...
        first_nibble = min(buckets[i], 15)
        second_nibble = min(buckets[i + 1], 15)
        density_byte = (second_nibble << 4) + first_nibble
        res.append(density_byte)

    return res
Ejemplo n.º 2
0
def naive_approach(beats: song.Timing, beat: song.BeatsTime) -> Fraction:
    if beat < 0:
        raise ValueError("Can't compute seconds at negative beat")

    if not beats.events:
        raise ValueError("No BPM defined")

    grouped_by_time = group_by(beats.events, key=lambda e: e.time)
    for time, events in grouped_by_time.items():
        if len(events) > 1:
            raise ValueError(
                f"Multiple BPMs defined on beat {time} : {events}")

    sorted_events = sorted(beats.events, key=lambda e: e.time)
    first_event = sorted_events[0]
    if first_event.time != song.BeatsTime(0):
        raise ValueError("First BPM event is not on beat zero")

    if beat > sorted_events[-1].time:
        events_before = sorted_events
    else:
        last_index = next(i for i, e in enumerate(sorted_events)
                          if e.time >= beat)
        events_before = sorted_events[:last_index]
    total_seconds = Fraction(0)
    current_beat = beat
    for event in reversed(events_before):
        beats_since_previous = current_beat - event.time
        seconds_since_previous = (60 * beats_since_previous) / Fraction(
            event.BPM)
        total_seconds += seconds_since_previous
        current_beat = event.time

    total_seconds = total_seconds + Fraction(beats.beat_zero_offset)
    return total_seconds
Ejemplo n.º 3
0
    def from_seconds(cls, events: List[BPMAtSecond]) -> TimeMap:
        """Create a time map from a list of BPM changes with time positions
        given in seconds. The first BPM implicitely happens at beat zero"""
        if not events:
            raise ValueError("No BPM defined")

        grouped_by_time = group_by(events, key=lambda e: e.seconds)
        for time, events_at_time in grouped_by_time.items():
            if len(events_at_time) > 1:
                raise ValueError(f"Multiple BPMs defined at {time} seconds : {events}")

        # take the first BPM change then compute from there
        sorted_events = sorted(events, key=lambda e: e.seconds)
        first_event = sorted_events[0]
        current_beat = Fraction(0)
        bpm_changes = [BPMChange(current_beat, first_event.seconds, first_event.BPM)]
        for previous, current in windowed(sorted_events, 2):
            if previous is None or current is None:
                continue

            seconds_since_last_event = current.seconds - previous.seconds
            beats_since_last_event = (
                previous.BPM * seconds_since_last_event
            ) / Fraction(60)
            current_beat += beats_since_last_event
            bpm_change = BPMChange(current_beat, current.seconds, current.BPM)
            bpm_changes.append(bpm_change)

        return cls(
            events_by_beats=SortedKeyList(bpm_changes, key=lambda b: b.beats),
            events_by_seconds=SortedKeyList(bpm_changes, key=lambda b: b.seconds),
        )
Ejemplo n.º 4
0
    def from_beats(cls, events: List[BPMAtBeat], offset: SecondsAtBeat) -> TimeMap:
        """Create a time map from a list of BPM changes with times given in
        beats, the offset parameter is more flexible than a "regular" beat zero
        offset as it accepts non-zero beats"""
        if not events:
            raise ValueError("No BPM defined")

        grouped_by_time = group_by(events, key=lambda e: e.beats)
        for time, events_at_time in grouped_by_time.items():
            if len(events_at_time) > 1:
                raise ValueError(f"Multiple BPMs defined at beat {time} : {events}")

        # First compute everything as if the first BPM change happened at
        # zero seconds, then shift according to the offset
        sorted_events = sorted(events, key=lambda e: e.beats)
        first_event = sorted_events[0]
        current_second = Fraction(0)
        bpm_changes = [
            BPMChange(first_event.beats, current_second, Fraction(first_event.BPM))
        ]
        for previous, current in windowed(sorted_events, 2):
            if previous is None or current is None:
                continue

            beats_since_last_event = current.beats - previous.beats
            seconds_since_last_event = (60 * beats_since_last_event) / Fraction(
                previous.BPM
            )
            current_second += seconds_since_last_event
            bpm_change = BPMChange(current.beats, current_second, Fraction(current.BPM))
            bpm_changes.append(bpm_change)

        not_shifted = cls(
            events_by_beats=SortedKeyList(bpm_changes, key=lambda b: b.beats),
            events_by_seconds=SortedKeyList(bpm_changes, key=lambda b: b.seconds),
        )
        unshifted_seconds_at_offset = not_shifted.fractional_seconds_at(offset.beats)
        shift = offset.seconds - unshifted_seconds_at_offset
        shifted_bpm_changes = [
            replace(b, seconds=b.seconds + shift) for b in bpm_changes
        ]
        return cls(
            events_by_beats=SortedKeyList(shifted_bpm_changes, key=lambda b: b.beats),
            events_by_seconds=SortedKeyList(
                shifted_bpm_changes, key=lambda b: b.seconds
            ),
        )
Ejemplo n.º 5
0
def make_chart_from_events(events: Iterable[Event],
                           beat_snap: int = 240) -> song.Chart:
    events_by_command = group_by(events, lambda e: e.command)
    bpms = [
        BPMAtSecond(seconds=ticks_to_seconds(e.time),
                    BPM=value_to_truncated_bpm(e.value))
        for e in sorted(events_by_command[Command.TEMPO])
    ]
    time_map = TimeMap.from_seconds(bpms)
    tap_notes: List[AnyNote] = [
        make_tap_note(e.time, e.value, time_map, beat_snap)
        for e in events_by_command[Command.PLAY]
    ]
    long_notes: List[AnyNote] = [
        make_long_note(e.time, e.value, time_map, beat_snap)
        for e in events_by_command[Command.LONG]
    ]
    all_notes = sorted(tap_notes + long_notes,
                       key=lambda n: (n.time, n.position))
    timing = time_map.convert_to_timing_info(beat_snap=beat_snap)
    return song.Chart(level=Decimal(0), timing=timing, notes=all_notes)
Ejemplo n.º 6
0
def compute_max_combo(notes: List[AnyNote]) -> int:
    notes_by_type = group_by(notes, type)
    tap_notes = len(notes_by_type[song.TapNote])
    long_notes = len(notes_by_type[song.LongNote])
    return tap_notes + 2 * long_notes