diff --git a/CHANGELOG.md b/CHANGELOG.md index aa659a7..5fbe4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.14.5] - 2022-02-10 +### Changed +- Changed behaviour of `ma2_to_simai` and `simai_to_ma2` to compensate for the fact that Simai disregards BPM changes for slide delays, and slide and hold durations. + ## [0.14.4] - 2022-01-24 ### Fixed -- Missing new line in BPM and MET lines in ma2. +- Fixed missing new line in BPM and MET lines in ma2. - Fixed bug in simaitoma2 and ma2tosimai BPM conversion. - Fixed bug in simai export. @@ -73,7 +77,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ma2 to Sdt conversion and vice versa. - Simai to Sdt conversion and vice versa. -[Unreleased]: https://github.com/donmai-me/MaiConverter/compare/0.14.4...HEAD +[Unreleased]: https://github.com/donmai-me/MaiConverter/compare/0.14.5...HEAD +[0.14.5]: https://github.com/donmai-me/MaiConverter/compare/0.14.4...0.14.5 [0.14.4]: https://github.com/donmai-me/MaiConverter/compare/0.14.3...0.14.4 [0.14.3]: https://github.com/donmai-me/MaiConverter/compare/0.14.2...0.14.3 [0.14.2]: https://github.com/donmai-me/MaiConverter/compare/0.14.1...0.14.2 diff --git a/maiconverter/converter/maima2tosimai.py b/maiconverter/converter/maima2tosimai.py index 0d62446..cc0e73a 100644 --- a/maiconverter/converter/maima2tosimai.py +++ b/maiconverter/converter/maima2tosimai.py @@ -1,10 +1,21 @@ -from typing import Sequence +from typing import Sequence, List from ..simai import ( SimaiChart, pattern_from_int, + HoldNote as SimaiHoldNote, + TouchHoldNote as SimaiTouchHoldNote, + SlideNote as SimaiSlideNote, +) +from ..maima2 import ( + MaiMa2, + TapNote, + HoldNote, + SlideNote, + TouchTapNote, + TouchHoldNote, + BPM, ) -from ..maima2 import MaiMa2, TapNote, HoldNote, SlideNote, TouchTapNote, TouchHoldNote from ..event import MaiNote, NoteType @@ -18,6 +29,9 @@ def ma2_to_simai(ma2: MaiMa2) -> SimaiChart: convert_notes(simai_chart, ma2.notes) + if len(simai_chart.bpms) != 1: + fix_durations(simai_chart) + return simai_chart @@ -79,3 +93,55 @@ def convert_notes(simai_chart: SimaiChart, ma2_notes: Sequence[MaiNote]) -> None ) else: print("Warning: Unknown note type {}".format(note_type)) + + +def fix_durations(simai: SimaiChart): + """Simai note durations (slide delay, slide duration, hold note duration) + disregards bpm changes midway, unlike ma2. So we'll have to compensate for those. + """ + + def bpm_changes(start: float, duration: float) -> List[BPM]: + result: List[BPM] = [] + for bpm in simai.bpms: + if start < bpm.measure < start + duration: + result.append(bpm) + + return result + + def compensate_duration( + start: float, duration: float, base_bpm: float, changes: List[BPM] + ) -> float: + new_duration = 0 + + note_start = start + for bpm in changes: + new_duration += ( + base_bpm + * (bpm.measure - note_start) + / simai.get_bpm(bpm.measure - 0.0001) + ) + + note_start = bpm.measure + + if note_start < start + duration: + new_duration += ( + base_bpm + * (start + duration - note_start) + / simai.get_bpm(note_start + 0.0001) + ) + + return new_duration + + for note in simai.notes: + if isinstance(note, (SimaiHoldNote, SimaiTouchHoldNote, SimaiSlideNote)): + bpms = bpm_changes(note.measure, note.duration) + if len(bpms) != 0: + note.duration = compensate_duration( + note.measure, note.duration, simai.get_bpm(note.measure), bpms + ) + if isinstance(note, SimaiSlideNote): + bpms = bpm_changes(note.measure, note.delay) + if len(bpms) != 0: + note.delay = compensate_duration( + note.measure, note.delay, simai.get_bpm(note.measure), bpms + ) diff --git a/maiconverter/converter/simaitomaima2.py b/maiconverter/converter/simaitomaima2.py index 2b04457..649d4cc 100644 --- a/maiconverter/converter/simaitomaima2.py +++ b/maiconverter/converter/simaitomaima2.py @@ -1,6 +1,12 @@ -from typing import List +from typing import List, Tuple, Optional -from ..maima2 import MaiMa2 +from ..maima2 import ( + MaiMa2, + BPM, + HoldNote as Ma2HoldNote, + TouchHoldNote as Ma2TouchHoldNote, + SlideNote as Ma2SlideNote, +) from ..simai import ( SimaiChart, pattern_to_int, @@ -23,6 +29,10 @@ def simai_to_ma2(simai: SimaiChart, fes_mode: bool = False) -> MaiMa2: ma2.set_meter(0.0, 4, 4) convert_notes(ma2, simai.notes) + + if len(ma2.bpms) != 1: + fix_durations(ma2) + return ma2 @@ -81,3 +91,55 @@ def convert_notes(ma2: MaiMa2, simai_notes: List[SimaiNote]) -> None: ) else: print(f"Warning: Unknown note type {note_type}") + + +def fix_durations(ma2: MaiMa2): + """Simai note durations (slide delay, slide duration, hold note duration) + disregards bpm changes midway, unlike ma2. So we'll have to compensate for those. + """ + + def bpm_changes(start: float, duration: float) -> List[BPM]: + result: List[BPM] = [] + for bpm in ma2.bpms: + if start < bpm.measure < start + duration: + result.append(bpm) + + return result + + def compensate_duration( + start: float, duration: float, base_bpm: float, changes: List[BPM] + ) -> float: + new_duration = 0 + + note_start = start + for bpm in changes: + new_duration += ( + ma2.get_bpm(bpm.measure - 0.0001) + * (bpm.measure - note_start) + / base_bpm + ) + + note_start = bpm.measure + + if note_start < start + duration: + new_duration += ( + ma2.get_bpm(note_start + 0.0001) + * (start + duration - note_start) + / base_bpm + ) + + return new_duration + + for note in ma2.notes: + if isinstance(note, (Ma2HoldNote, Ma2TouchHoldNote, Ma2SlideNote)): + bpms = bpm_changes(note.measure, note.duration) + if len(bpms) != 0: + note.duration = compensate_duration( + note.measure, note.duration, ma2.get_bpm(note.measure), bpms + ) + if isinstance(note, Ma2SlideNote): + bpms = bpm_changes(note.measure, note.delay) + if len(bpms) != 0: + note.delay = compensate_duration( + note.measure, note.delay, ma2.get_bpm(note.measure), bpms + )