1
0
mirror of synced 2024-11-13 18:10:48 +01:00

Jubeat Analyser formats debug

This commit is contained in:
Stepland 2020-07-25 14:21:12 +02:00
parent 29c380588a
commit 6bae083a57
7 changed files with 45 additions and 26 deletions

View File

@ -1,9 +1,8 @@
"""Command Line Interface"""
from pathlib import Path
from typing import Optional
import click
from path import Path
from jubeatools.formats import DUMPERS, LOADERS
from jubeatools.formats.enum import JUBEAT_ANALYSER_FORMATS
@ -27,8 +26,8 @@ from jubeatools.formats.guess import guess_format
)
def convert(src, dst, output_format, **kwargs):
"""Convert SRC to DST using the format specified by -f"""
input_format = guess_format(Path(src))
click.echo(f"Detected input file format : {input_format}")
try:
loader = LOADERS[input_format]
@ -40,7 +39,7 @@ def convert(src, dst, output_format, **kwargs):
except KeyError:
raise ValueError(f"Unsupported output format : {input_format}")
song = loader(src)
song = loader(Path(src))
extra_args = {}
if output_format in JUBEAT_ANALYSER_FORMATS:
@ -49,7 +48,6 @@ def convert(src, dst, output_format, **kwargs):
files = dumper(song, Path(dst), **extra_args)
for path, contents in files.items():
path.makedirs_p()
with path.open("wb") as f:
f.write(contents)

View File

@ -15,7 +15,7 @@ def guess_format(path: Path) -> Format:
try:
with path.open() as f:
obj = json.load(f)
except json.JSONDecodeError:
except (json.JSONDecodeError, UnicodeDecodeError):
pass
else:
return guess_memon_version(obj)

View File

@ -46,7 +46,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
EMPTY_BEAT_SYMBOL = "" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
EMPTY_BEAT_SYMBOL = '' # U+02212 : MINUS SIGN
EMPTY_POSITION_SYMBOL = "" # U+025A1 : WHITE SQUARE
@ -252,7 +252,7 @@ def _dump_memo_chart(
file.write(f"// Converted using jubeatools {__version__}\n")
file.write(f"// https://github.com/Stepland/jubeatools\n\n")
for _, section in sections.items():
file.write(section.render(circle_free) + "\n")
file.write(section.render(circle_free) + "\n\n")
return file

View File

@ -47,7 +47,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
EMPTY_BEAT_SYMBOL = "" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
EMPTY_BEAT_SYMBOL = "" # U+02212 : MINUS SIGN
EMPTY_POSITION_SYMBOL = "" # U+025A1 : WHITE SQUARE

View File

@ -49,7 +49,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
EMPTY_BEAT_SYMBOL = "" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
EMPTY_BEAT_SYMBOL = '' # U+02212 : MINUS SIGN
EMPTY_POSITION_SYMBOL = "" # U+025A1 : WHITE SQUARE

View File

@ -247,28 +247,39 @@ def _long_note_tail_value_v0(note: LongNote) -> int:
f"memon cannot represent a long note with its tail starting ({dx}, {dy}) away from the note"
) from None
def _get_timing(song: Song) -> Timing:
if song.global_timing is not None:
return song.global_timing
else:
return next(
chart.timing
for chart in song.charts.values()
if chart is not None
)
def _raise_if_unfit_for_v0(song: Song, version: str) -> None:
"""Raises an exception if the Song object is ill-formed or contains information
that cannot be represented in a memon v0.x.x file (includes legacy)"""
that cannot be represented in a memon v0.x.y file (includes legacy)"""
if any(chart.timing is not None for chart in song.charts.values()):
if len(set(chart.timing for chart in song.charts.values() if chart is not None)) > 1:
raise ValueError(
f"memon:{version} cannot represent a song with per-chart timing"
)
if song.global_timing is None and all(chart.timing is None for chart in song.charts.values()):
raise ValueError("The song has no timing information")
if song.global_timing is None:
raise ValueError("The song has no global timing information")
timing = _get_timing(song)
number_of_timing_events = len(song.global_timing.events)
number_of_timing_events = len(timing.events)
if number_of_timing_events != 1:
if number_of_timing_events == 0:
raise ValueError("The song has no BPM")
else:
raise ValueError(f"memon:{version} does not handle BPM changes")
event = song.global_timing.events[0]
event = timing.events[0]
if event.BPM <= 0:
raise ValueError(f"memon:{version} only accepts strictly positive BPMs")
@ -320,9 +331,11 @@ def _dump_memon_note_v0(
return memon_note
def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
_raise_if_unfit_for_v0(song, "legacy")
timing = _get_timing(song)
memon = {
"metadata": {
@ -330,8 +343,8 @@ def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
"artist": song.metadata.artist,
"music path": str(song.metadata.audio),
"jacket path": str(song.metadata.cover),
"BPM": song.global_timing.events[0].BPM,
"offset": -song.global_timing.beat_zero_offset,
"BPM": timing.events[0].BPM,
"offset": -timing.beat_zero_offset,
},
"data": [],
}
@ -361,7 +374,8 @@ def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
_raise_if_unfit_for_v0(song, "legacy")
_raise_if_unfit_for_v0(song, "v0.1.0")
timing= _get_timing(song)
memon = {
"version": "0.1.0",
@ -370,8 +384,8 @@ def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
"artist": song.metadata.artist,
"music path": str(song.metadata.audio),
"album cover path": str(song.metadata.cover),
"BPM": song.global_timing.events[0].BPM,
"offset": -song.global_timing.beat_zero_offset,
"BPM": timing.events[0].BPM,
"offset": -timing.beat_zero_offset,
},
"data": dict(),
}
@ -396,7 +410,8 @@ def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
_raise_if_unfit_for_v0(song, "legacy")
_raise_if_unfit_for_v0(song, "v0.2.0")
timing = _get_timing(song)
memon = {
"version": "0.2.0",
@ -405,8 +420,8 @@ def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
"artist": song.metadata.artist,
"music path": str(song.metadata.audio),
"album cover path": str(song.metadata.cover),
"BPM": song.global_timing.events[0].BPM,
"offset": -song.global_timing.beat_zero_offset,
"BPM": timing.events[0].BPM,
"offset": -timing.beat_zero_offset,
},
"data": {},
}
@ -433,4 +448,4 @@ def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
else:
filepath = path
return {filepath: _dump_to_json(memon)}
return {filepath: _dump_to_json(memon)}

View File

@ -123,7 +123,7 @@ class LongNote:
yield position
@dataclass
@dataclass(frozen=True)
class BPMEvent:
time: BeatsTime
BPM: Decimal
@ -134,6 +134,12 @@ class Timing:
events: List[BPMEvent]
beat_zero_offset: SecondsTime
def __hash__(self):
return hash((
tuple(self.events),
self.beat_zero_offset,
))
@dataclass
class Chart: