1
0
mirror of synced 2024-12-12 15:01:09 +01:00

[#mono-column] #circlefree mode accepts non-16ths notes and falls back to normal symbols when needed

This commit is contained in:
Stepland 2021-05-04 11:27:03 +02:00
parent 9e0c4f5c2b
commit 857dd899a7
5 changed files with 25 additions and 52 deletions

View File

@ -1,4 +1,6 @@
# Unreleased # Unreleased
## Added
- [#mono-column] #circlefree mode accepts non-16ths notes and falls back to normal symbols when needed
## Fixed ## Fixed
- [jubeat-analyser] - [jubeat-analyser]
- Raise exception earlier when a mono-column file is detected by the other #memo parsers (based on "--" separator lines) - Raise exception earlier when a mono-column file is detected by the other #memo parsers (based on "--" separator lines)

View File

@ -31,6 +31,7 @@ from ..dump_tools import (
DEFAULT_EXTRA_SYMBOLS, DEFAULT_EXTRA_SYMBOLS,
DIRECTION_TO_ARROW, DIRECTION_TO_ARROW,
DIRECTION_TO_LINE, DIRECTION_TO_LINE,
NOTE_TO_CIRCLE_FREE_SYMBOL,
JubeatAnalyserDumpedSection, JubeatAnalyserDumpedSection,
LongNoteEnd, LongNoteEnd,
SortedDefaultDict, SortedDefaultDict,
@ -38,7 +39,6 @@ from ..dump_tools import (
fraction_to_decimal, fraction_to_decimal,
jubeat_analyser_file_dumper, jubeat_analyser_file_dumper,
) )
from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
class MonoColumnDumpedSection(JubeatAnalyserDumpedSection): class MonoColumnDumpedSection(JubeatAnalyserDumpedSection):
@ -62,19 +62,18 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection):
frames: List[Dict[NotePosition, str]] = [] frames: List[Dict[NotePosition, str]] = []
frame: Dict[NotePosition, str] = {} frame: Dict[NotePosition, str] = {}
for note in self.notes: for note in self.notes:
time_in_section = note.time - self.current_beat
symbol = self.symbols[time_in_section]
if isinstance(note, LongNote): if isinstance(note, LongNote):
needed_positions = set(note.positions_covered()) needed_positions = set(note.positions_covered())
if needed_positions & frame.keys(): if needed_positions & frame.keys():
frames.append(frame) frames.append(frame)
frame = {} frame = {}
direction = note.tail_direction() direction = note.tail_direction()
arrow = DIRECTION_TO_ARROW[direction] arrow = DIRECTION_TO_ARROW[direction]
line = DIRECTION_TO_LINE[direction] line = DIRECTION_TO_LINE[direction]
for is_first, is_last, pos in mark_ends(note.positions_covered()): for is_first, is_last, pos in mark_ends(note.positions_covered()):
if is_first: if is_first:
time_in_section = note.time - self.current_beat
symbol = self.symbols[time_in_section]
frame[pos] = symbol frame[pos] = symbol
elif is_last: elif is_last:
frame[pos] = arrow frame[pos] = arrow
@ -84,18 +83,13 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection):
if note.position in frame: if note.position in frame:
frames.append(frame) frames.append(frame)
frame = {} frame = {}
time_in_section = note.time - self.current_beat
symbol = self.symbols[time_in_section]
frame[note.position] = symbol frame[note.position] = symbol
elif isinstance(note, LongNoteEnd): elif isinstance(note, LongNoteEnd):
if note.position in frame: if note.position in frame:
frames.append(frame) frames.append(frame)
frame = {} frame = {}
time_in_section = note.time - self.current_beat if circle_free and symbol in NOTE_TO_CIRCLE_FREE_SYMBOL:
if circle_free: symbol = NOTE_TO_CIRCLE_FREE_SYMBOL[symbol]
symbol = CIRCLE_FREE_SYMBOLS[int(time_in_section)]
else:
symbol = self.symbols[time_in_section]
frame[note.position] = symbol frame[note.position] = symbol
frames.append(frame) frames.append(frame)
@ -108,9 +102,7 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection):
yield "".join(frame.get(NotePosition(x, y), "") for x in range(4)) yield "".join(frame.get(NotePosition(x, y), "") for x in range(4))
def _raise_if_unfit_for_mono_column( def _raise_if_unfit_for_mono_column(chart: Chart, timing: Timing) -> None:
chart: Chart, timing: Timing, circle_free: bool = False
) -> None:
if len(timing.events) < 1: if len(timing.events) < 1:
raise ValueError("No BPM found in file") from None raise ValueError("No BPM found in file") from None
@ -128,16 +120,6 @@ def _raise_if_unfit_for_mono_column(
" mono_column format is not supported by jubeatools" " mono_column format is not supported by jubeatools"
) )
if circle_free and any(
(note.time + note.duration) % BeatsTime(1, 4) != 0
for note in chart.notes
if isinstance(note, LongNote)
):
raise ValueError(
"Chart contains long notes whose ending timing aren't"
" representable in #circlefree mode"
)
def _section_factory(b: BeatsTime) -> MonoColumnDumpedSection: def _section_factory(b: BeatsTime) -> MonoColumnDumpedSection:
return MonoColumnDumpedSection(current_beat=b) return MonoColumnDumpedSection(current_beat=b)
@ -151,7 +133,7 @@ def _dump_mono_column_chart(
circle_free: bool = False, circle_free: bool = False,
) -> StringIO: ) -> StringIO:
_raise_if_unfit_for_mono_column(chart, timing, circle_free) _raise_if_unfit_for_mono_column(chart, timing)
sections = create_sections_from_chart( sections = create_sections_from_chart(
_section_factory, chart, difficulty, timing, metadata, circle_free _section_factory, chart, difficulty, timing, metadata, circle_free

View File

@ -188,28 +188,20 @@ class MonoColumnParser(JubeatAnalyserParser):
unfinished_longs: Dict[NotePosition, UnfinishedLongNote] = {} unfinished_longs: Dict[NotePosition, UnfinishedLongNote] = {}
for section_starting_beat, section, bloc in self._iter_blocs(): for section_starting_beat, section, bloc in self._iter_blocs():
should_skip: Set[NotePosition] = set() should_skip: Set[NotePosition] = set()
# 1/3 : look for ends to unfinished long notes # 1/3 : look for ends to unfinished long notes
for pos, unfinished_long in unfinished_longs.items(): for pos, unfinished_long in unfinished_longs.items():
x, y = astuple(pos) x, y = astuple(pos)
symbol = bloc[y][x] symbol = bloc[y][x]
if self.circle_free: if self.circle_free and symbol in CIRCLE_FREE_SYMBOLS:
if symbol in CIRCLE_FREE_SYMBOLS: should_skip.add(pos)
should_skip.add(pos) symbol_time = CIRCLE_FREE_TO_BEATS_TIME[symbol]
symbol_time = CIRCLE_FREE_TO_BEATS_TIME[symbol] note_time = section_starting_beat + symbol_time
note_time = section_starting_beat + symbol_time yield unfinished_long.ends_at(note_time)
yield unfinished_long.ends_at(note_time) elif symbol in section.symbols:
elif symbol in section.symbols: should_skip.add(pos)
raise SyntaxError( symbol_time = section.symbols[symbol]
"Can't have a note symbol on the holding square of" note_time = section_starting_beat + symbol_time
" an unfinished long note when #circlefree is on" yield unfinished_long.ends_at(note_time)
)
else:
if symbol in section.symbols:
should_skip.add(pos)
symbol_time = section.symbols[symbol]
note_time = section_starting_beat + symbol_time
yield unfinished_long.ends_at(note_time)
unfinished_longs = { unfinished_longs = {
k: unfinished_longs[k] for k in unfinished_longs.keys() - should_skip k: unfinished_longs[k] for k in unfinished_longs.keys() - should_skip

View File

@ -2,9 +2,8 @@ from typing import Iterable, Union
import pytest import pytest
from jubeatools.song import BeatsTime, LongNote, NotePosition, TapNote
from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser
from jubeatools.song import BeatsTime, LongNote, NotePosition, TapNote
def compare_chart_notes( def compare_chart_notes(

View File

@ -5,6 +5,9 @@ from typing import List, Set, Union
import hypothesis.strategies as st import hypothesis.strategies as st
from hypothesis import given from hypothesis import given
from jubeatools.formats import Format
from jubeatools.formats.jubeat_analyser.mono_column.dump import _dump_mono_column_chart
from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser
from jubeatools.song import ( from jubeatools.song import (
BeatsTime, BeatsTime,
BPMEvent, BPMEvent,
@ -12,19 +15,14 @@ from jubeatools.song import (
LongNote, LongNote,
Metadata, Metadata,
SecondsTime, SecondsTime,
Song,
TapNote, TapNote,
Timing, Timing,
Song
) )
from jubeatools.testutils.strategies import NoteOption, long_note from jubeatools.testutils.strategies import NoteOption, long_note
from jubeatools.testutils.strategies import notes as notes_strat from jubeatools.testutils.strategies import notes as notes_strat
from jubeatools.testutils.strategies import tap_note from jubeatools.testutils.strategies import tap_note
from jubeatools.formats import Format
from jubeatools.formats.jubeat_analyser.mono_column.dump import _dump_mono_column_chart
from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser
from ..test_utils import load_and_dump_then_check, memo_compatible_song from ..test_utils import load_and_dump_then_check, memo_compatible_song
@ -86,4 +84,4 @@ def test_that_many_notes_roundtrip(notes: List[Union[TapNote, LongNote]]) -> Non
@given(memo_compatible_song(), st.booleans()) @given(memo_compatible_song(), st.booleans())
def test_that_full_chart_roundtrips(song: Song, circle_free: bool) -> None: def test_that_full_chart_roundtrips(song: Song, circle_free: bool) -> None:
load_and_dump_then_check(Format.MONO_COLUMN, song, circle_free) load_and_dump_then_check(Format.MONO_COLUMN, song, circle_free)