converters.py
: Move computation into MeasureFumen
class
This commit is contained in:
parent
79cca3d75e
commit
f088e95e02
@ -163,8 +163,7 @@ def convert_tja_to_fumen(tja):
|
||||
|
||||
# Iterate through the measures within the branch
|
||||
for idx_m, measure_tja in enumerate(branch_tja):
|
||||
# Fetch a pair of measures
|
||||
measure_fumen_prev = fumen.measures[idx_m-1] if idx_m else None
|
||||
# Fetch the corresponding fumen measure
|
||||
measure_fumen = fumen.measures[idx_m]
|
||||
|
||||
# Copy over basic measure properties from the TJA
|
||||
@ -173,48 +172,26 @@ def convert_tja_to_fumen(tja):
|
||||
measure_fumen.bpm = measure_tja.bpm
|
||||
|
||||
# Compute the duration of the measure
|
||||
# First, we compute the duration for a full 4/4 measure
|
||||
# Next, we adjust this duration based on both:
|
||||
# 1. The *actual* measure size (e.g. #MEASURE 1/8, 5/4, etc.)
|
||||
# 2. Whether this is a "submeasure" (i.e. whether it contains
|
||||
# mid-measure commands, which split up the measure)
|
||||
# - If this is a submeasure, then `measure_length` will be
|
||||
# less than the total number of subdivisions.
|
||||
# - In other words, `measure_ratio` will be less than 1.0.
|
||||
measure_duration_full_measure = (240000 / measure_fumen.bpm)
|
||||
measure_size = (measure_tja.time_sig[0] / measure_tja.time_sig[1])
|
||||
measure_length = (measure_tja.pos_end - measure_tja.pos_start)
|
||||
measure_ratio = (
|
||||
1.0 if measure_tja.subdivisions == 0.0 # Avoid "/0"
|
||||
else (measure_length / measure_tja.subdivisions)
|
||||
measure_length = measure_tja.pos_end - measure_tja.pos_start
|
||||
measure_fumen.set_duration(
|
||||
time_sig=measure_tja.time_sig,
|
||||
measure_length=measure_length,
|
||||
subdivisions=measure_tja.subdivisions
|
||||
)
|
||||
measure_fumen.duration = (measure_duration_full_measure
|
||||
* measure_size * measure_ratio)
|
||||
|
||||
# Compute the millisecond offsets for the start of each measure
|
||||
# First, start the measure using the end timing of the
|
||||
# previous measure (plus any #DELAY commands)
|
||||
# Next, adjust the start timing to account for #BPMCHANGE
|
||||
# commands (!!! Discovered by tana :3 !!!)
|
||||
if idx_m == 0:
|
||||
measure_fumen.offset_start = (
|
||||
(tja.offset * 1000 * -1) - measure_duration_full_measure
|
||||
)
|
||||
else:
|
||||
measure_fumen.offset_start = measure_fumen_prev.offset_end
|
||||
measure_fumen.offset_start += measure_tja.delay
|
||||
measure_fumen.offset_start += (240000 / measure_fumen_prev.bpm)
|
||||
measure_fumen.offset_start -= (240000 / measure_fumen.bpm)
|
||||
|
||||
# Compute the millisecond offset for the end of each measure
|
||||
measure_fumen.offset_end = (measure_fumen.offset_start +
|
||||
measure_fumen.duration)
|
||||
# Compute the millisecnd offsets for the start/end of each measure
|
||||
measure_fumen.set_ms_offsets(
|
||||
song_offset=tja.offset,
|
||||
delay=measure_tja.delay,
|
||||
prev_measure=(fumen.measures[idx_m-1] if idx_m else None),
|
||||
first_measure=(idx_m == 0)
|
||||
)
|
||||
|
||||
# Handle whether barline should be hidden:
|
||||
# 1. Measures where #BARLINEOFF has been set
|
||||
# 2. Sub-measures that don't fall on the barline
|
||||
barline_off = measure_tja.barline is False
|
||||
is_submeasure = (measure_ratio != 1.0 and
|
||||
is_submeasure = (measure_length < measure_tja.subdivisions and
|
||||
measure_tja.pos_start != 0)
|
||||
if barline_off or is_submeasure:
|
||||
measure_fumen.barline = False
|
||||
@ -230,55 +207,12 @@ def convert_tja_to_fumen(tja):
|
||||
|
||||
# Check to see if the measure contains a branching condition
|
||||
if branch_condition:
|
||||
# Handle branch conditions for percentage accuracy
|
||||
# There are three cases for interpreting #BRANCHSTART p:
|
||||
# 1. Percentage is between 0% and 100%
|
||||
# 2. Percentage is above 100% (guaranteed level down)
|
||||
# 3. Percentage is 0% (guaranteed level up)
|
||||
if branch_condition[0] == 'p':
|
||||
vals = []
|
||||
for percent in branch_condition[1:]:
|
||||
if 0 < percent <= 1:
|
||||
vals.append(int(branch_points_total * percent))
|
||||
elif percent > 1:
|
||||
vals.append(999)
|
||||
else:
|
||||
vals.append(0)
|
||||
if current_branch == 'normal':
|
||||
measure_fumen.branch_info[0:2] = vals
|
||||
elif current_branch == 'professional':
|
||||
measure_fumen.branch_info[2:4] = vals
|
||||
elif current_branch == 'master':
|
||||
measure_fumen.branch_info[4:6] = vals
|
||||
|
||||
# Handle branch conditions for drumroll accuracy
|
||||
# There are three cases for interpreting #BRANCHSTART r:
|
||||
# 1. It's the first branching condition.
|
||||
# 2. It's not the first branching condition, but it
|
||||
# has a #SECTION command to reset the accuracy.
|
||||
# 3. It's not the first branching condition, and it
|
||||
# doesn't have a #SECTION command.
|
||||
# For the first two cases, the branching conditions are the
|
||||
# same no matter what branch you're currently on, so we just
|
||||
# use the values as-is: [c1, c2, c1, c2, c1, c2]
|
||||
# But, for the third case, since there is no #SECTION, the
|
||||
# accuracy is not reset. This results in the following
|
||||
# condition: [999, 999, c1, c2, c2, c2]
|
||||
# - Normal can't advance to professional/master
|
||||
# - Professional can stay, or advance to master.
|
||||
# - Master can only stay in master.
|
||||
elif branch_condition[0] == 'r':
|
||||
is_first_branch_condition = not branch_conditions
|
||||
has_section = bool(measure_tja.section)
|
||||
if is_first_branch_condition or has_section:
|
||||
measure_fumen.branch_info = branch_condition[1:] * 3
|
||||
else:
|
||||
measure_fumen.branch_info = (
|
||||
[999, 999] +
|
||||
[branch_condition[1]] +
|
||||
[branch_condition[2]] * 3
|
||||
)
|
||||
|
||||
# Update the branch_info values for the measure
|
||||
measure_fumen.set_branch_info(
|
||||
branch_condition, branch_points_total, current_branch,
|
||||
first_branch_condition=(not branch_conditions),
|
||||
has_section=bool(measure_tja.section)
|
||||
)
|
||||
# Reset the points to prepare for the next #BRANCHSTART p
|
||||
branch_points_total = 0
|
||||
# Keep track of the branch conditions (to later determine how
|
||||
@ -299,8 +233,8 @@ def convert_tja_to_fumen(tja):
|
||||
branch_points_measure = 0
|
||||
for idx_d, data in enumerate(measure_tja.data):
|
||||
# Compute the ms position of the note
|
||||
pos_ratio = ((data.pos - measure_tja.pos_start)
|
||||
/ measure_length)
|
||||
pos_ratio = ((data.pos - measure_tja.pos_start) /
|
||||
(measure_tja.pos_end - measure_tja.pos_start))
|
||||
note_pos = (measure_fumen.duration * pos_ratio)
|
||||
|
||||
# Handle '8' notes (end of a drumroll/balloon)
|
||||
|
@ -108,6 +108,91 @@ class FumenMeasure:
|
||||
self.branches = {b: FumenBranch() for b in BRANCH_NAMES}
|
||||
self.padding1 = padding1
|
||||
self.padding2 = padding2
|
||||
|
||||
def set_duration(self, time_sig, measure_length, subdivisions):
|
||||
"""Compute the millisecond duration of the measure."""
|
||||
# First, we compute the duration for a full 4/4 measure.
|
||||
full_duration = (4 * 60_000 / self.bpm)
|
||||
# Next, we adjust this duration based on both:
|
||||
# 1. The *actual* measure size (e.g. #MEASURE 1/8, 5/4, etc.)
|
||||
# 2. Whether this is a "submeasure" (i.e. whether it contains
|
||||
# mid-measure commands, which split up the measure)
|
||||
# - If this is a submeasure, then `measure_length` will be
|
||||
# less than the total number of subdivisions.
|
||||
# - In other words, `measure_ratio` will be less than 1.0.
|
||||
measure_size = time_sig[0] / time_sig[1]
|
||||
measure_ratio = (
|
||||
1.0 if subdivisions == 0.0 # Avoid DivisionByZeroErrors
|
||||
else (measure_length / subdivisions)
|
||||
)
|
||||
self.duration = (full_duration * measure_size * measure_ratio)
|
||||
|
||||
def set_ms_offsets(self, song_offset, delay, prev_measure, first_measure):
|
||||
"""Compute the millisecond offsets for the start/end of the measure."""
|
||||
if first_measure:
|
||||
self.offset_start = (song_offset * -1000) - (4 * 60_000 / self.bpm)
|
||||
else:
|
||||
# First, start with sing the end timing of the previous measure
|
||||
self.offset_start = prev_measure.offset_end
|
||||
# Add any #DELAY commands
|
||||
self.offset_start += delay
|
||||
# Adjust the start timing to account for #BPMCHANGE commands
|
||||
# (!!! Discovered by tana :3 !!!)
|
||||
self.offset_start += (4 * 60_000 / prev_measure.bpm)
|
||||
self.offset_start -= (4 * 60_000 / self.bpm)
|
||||
|
||||
# Compute the end offset by adding the duration to the start offset
|
||||
self.offset_end = self.offset_start + self.duration
|
||||
|
||||
def set_branch_info(self, branch_condition, branch_points_total,
|
||||
current_branch, first_branch_condition, has_section):
|
||||
"""Compute the values that represent branching/diverge conditions."""
|
||||
# Handle branch conditions for percentage accuracy
|
||||
# There are three cases for interpreting #BRANCHSTART p:
|
||||
# 1. Percentage is between 0% and 100%
|
||||
# 2. Percentage is above 100% (guaranteed level down)
|
||||
# 3. Percentage is 0% (guaranteed level up)
|
||||
if branch_condition[0] == 'p':
|
||||
vals = []
|
||||
for percent in branch_condition[1:]:
|
||||
if 0 < percent <= 1:
|
||||
vals.append(int(branch_points_total * percent))
|
||||
elif percent > 1:
|
||||
vals.append(999)
|
||||
else:
|
||||
vals.append(0)
|
||||
if current_branch == 'normal':
|
||||
self.branch_info[0:2] = vals
|
||||
elif current_branch == 'professional':
|
||||
self.branch_info[2:4] = vals
|
||||
elif current_branch == 'master':
|
||||
self.branch_info[4:6] = vals
|
||||
|
||||
# Handle branch conditions for drumroll accuracy
|
||||
# There are three cases for interpreting #BRANCHSTART r:
|
||||
# 1. It's the first branching condition.
|
||||
# 2. It's not the first branching condition, but it
|
||||
# has a #SECTION command to reset the accuracy.
|
||||
# 3. It's not the first branching condition, and it
|
||||
# doesn't have a #SECTION command.
|
||||
# For the first two cases, the branching conditions are the
|
||||
# same no matter what branch you're currently on, so we just
|
||||
# use the values as-is: [c1, c2, c1, c2, c1, c2]
|
||||
# But, for the third case, since there is no #SECTION, the
|
||||
# accuracy is not reset. This results in the following
|
||||
# condition: [999, 999, c1, c2, c2, c2]
|
||||
# - Normal can't advance to professional/master
|
||||
# - Professional can stay, or advance to master.
|
||||
# - Master can only stay in master.
|
||||
elif branch_condition[0] == 'r':
|
||||
if first_branch_condition or has_section:
|
||||
self.branch_info = branch_condition[1:] * 3
|
||||
else:
|
||||
self.branch_info = (
|
||||
[999, 999] +
|
||||
[branch_condition[1]] +
|
||||
[branch_condition[2]] * 3
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.__dict__)
|
||||
|
Loading…
Reference in New Issue
Block a user