Spaces:
Sleeping
Sleeping
"""Ultrastar writer module""" | |
import re | |
import langcodes | |
from packaging import version | |
from modules.console_colors import ULTRASINGER_HEAD | |
from modules.Ultrastar.ultrastar_converter import ( | |
real_bpm_to_ultrastar_bpm, | |
second_to_beat, | |
beat_to_second, | |
) | |
from modules.Ultrastar.ultrastar_txt import UltrastarTxtValue, UltrastarTxtTag, UltrastarTxtNoteTypeTag, \ | |
FILE_ENCODING | |
from modules.Speech_Recognition.TranscribedData import TranscribedData | |
from modules.Ultrastar.ultrastar_score_calculator import Score | |
def get_thirtytwo_note_second(real_bpm: float): | |
"""Converts a beat to a 1/32 note in second""" | |
return 60 / real_bpm / 8 | |
def get_sixteenth_note_second(real_bpm: float): | |
"""Converts a beat to a 1/16 note in second""" | |
return 60 / real_bpm / 4 | |
def get_eighth_note_second(real_bpm: float): | |
"""Converts a beat to a 1/8 note in second""" | |
return 60 / real_bpm / 2 | |
def get_quarter_note_second(real_bpm: float): | |
"""Converts a beat to a 1/4 note in second""" | |
return 60 / real_bpm | |
def get_half_note_second(real_bpm: float): | |
"""Converts a beat to a 1/2 note in second""" | |
return 60 / real_bpm * 2 | |
def get_whole_note_second(real_bpm: float): | |
"""Converts a beat to a 1/1 note in second""" | |
return 60 / real_bpm * 4 | |
def get_multiplier(real_bpm: float) -> int: | |
"""Calculates the multiplier for the BPM""" | |
if real_bpm == 0: | |
raise Exception("BPM is 0") | |
multiplier = 1 | |
result = 0 | |
while result < 400: | |
result = real_bpm * multiplier | |
multiplier += 1 | |
return multiplier - 2 | |
def get_language_name(language: str) -> str: | |
"""Creates the language name from the language code""" | |
return langcodes.Language.make(language=language).display_name() | |
def create_ultrastar_txt_from_automation( | |
transcribed_data: list[TranscribedData], | |
note_numbers: list[int], | |
ultrastar_file_output: str, | |
ultrastar_class: UltrastarTxtValue, | |
real_bpm=120, | |
) -> None: | |
"""Creates an Ultrastar txt file from the automation data""" | |
print( | |
f"{ULTRASINGER_HEAD} Creating {ultrastar_file_output} from transcription." | |
) | |
ultrastar_bpm = real_bpm_to_ultrastar_bpm(real_bpm) | |
multiplication = get_multiplier(ultrastar_bpm) | |
ultrastar_bpm = ultrastar_bpm * get_multiplier(ultrastar_bpm) | |
silence_split_duration = calculate_silent_beat_length(transcribed_data) | |
with open(ultrastar_file_output, "w", encoding=FILE_ENCODING) as file: | |
gap = transcribed_data[0].start | |
if version.parse(ultrastar_class.version) >= version.parse("1.0.0"): | |
file.write(f"#{UltrastarTxtTag.VERSION}:{ultrastar_class.version}\n"), | |
file.write(f"#{UltrastarTxtTag.ARTIST}:{ultrastar_class.artist}\n") | |
file.write(f"#{UltrastarTxtTag.TITLE}:{ultrastar_class.title}\n") | |
if ultrastar_class.year is not None: | |
file.write(f"#{UltrastarTxtTag.YEAR}:{ultrastar_class.year}\n") | |
if ultrastar_class.language is not None: | |
file.write(f"#{UltrastarTxtTag.LANGUAGE}:{get_language_name(ultrastar_class.language)}\n") | |
if ultrastar_class.genre: | |
file.write(f"#{UltrastarTxtTag.GENRE}:{ultrastar_class.genre}\n") | |
if ultrastar_class.cover is not None: | |
file.write(f"#{UltrastarTxtTag.COVER}:{ultrastar_class.cover}\n") | |
file.write(f"#{UltrastarTxtTag.MP3}:{ultrastar_class.mp3}\n") | |
if version.parse(ultrastar_class.version) >= version.parse("1.1.0"): | |
file.write(f"#{UltrastarTxtTag.AUDIO}:{ultrastar_class.audio}\n") | |
if ultrastar_class.vocals is not None: | |
file.write(f"#{UltrastarTxtTag.VOCALS}:{ultrastar_class.vocals}\n") | |
if ultrastar_class.instrumental is not None: | |
file.write(f"#{UltrastarTxtTag.INSTRUMENTAL}:{ultrastar_class.instrumental}\n") | |
if ultrastar_class.tags is not None: | |
file.write(f"#{UltrastarTxtTag.TAGS}:{ultrastar_class.tags}\n") | |
file.write(f"#{UltrastarTxtTag.VIDEO}:{ultrastar_class.video}\n") | |
file.write(f"#{UltrastarTxtTag.BPM}:{round(ultrastar_bpm, 2)}\n") # not the real BPM! | |
file.write(f"#{UltrastarTxtTag.GAP}:{int(gap * 1000)}\n") | |
file.write(f"#{UltrastarTxtTag.CREATOR}:{ultrastar_class.creator}\n") | |
file.write(f"#{UltrastarTxtTag.COMMENT}:{ultrastar_class.comment}\n") | |
# Write the singing part | |
previous_end_beat = 0 | |
separated_word_silence = [] # This is a workaround for separated words that get his ends to far away | |
for i, data in enumerate(transcribed_data): | |
start_time = (data.start - gap) * multiplication | |
end_time = ( | |
data.end - data.start | |
) * multiplication | |
start_beat = round(second_to_beat(start_time, real_bpm)) | |
duration = round(second_to_beat(end_time, real_bpm)) | |
# Fix the round issue, so the beats don’t overlap | |
start_beat = max(start_beat, previous_end_beat) | |
previous_end_beat = start_beat + duration | |
# Calculate the silence between the words | |
if i < len(transcribed_data) - 1: | |
silence = (transcribed_data[i + 1].start - data.end) | |
else: | |
silence = 0 | |
# : 10 10 10 w | |
# ':' start midi part | |
# 'n1' start at real beat | |
# 'n2' duration at real beat | |
# 'n3' pitch where 0 == C4 | |
# 'w' lyric | |
line = f"{UltrastarTxtNoteTypeTag.NORMAL} " \ | |
f"{str(start_beat)} " \ | |
f"{str(duration)} " \ | |
f"{str(note_numbers[i])} " \ | |
f"{data.word}\n" | |
file.write(line) | |
# detect silence between words | |
if not transcribed_data[i].is_word_end: | |
separated_word_silence.append(silence) | |
continue | |
if silence_split_duration != 0 and silence > silence_split_duration or any( | |
s > silence_split_duration for s in separated_word_silence) and i != len(transcribed_data) - 1: | |
# - 10 | |
# '-' end of current sing part | |
# 'n1' show next at time in real beat | |
show_next = ( | |
second_to_beat(data.end - gap, real_bpm) | |
* multiplication | |
) | |
linebreak = f"{UltrastarTxtTag.LINEBREAK} " \ | |
f"{str(round(show_next))}\n" | |
file.write(linebreak) | |
separated_word_silence = [] | |
file.write(f"{UltrastarTxtTag.FILE_END}") | |
def deviation(silence_parts): | |
"""Calculates the deviation of the silence parts""" | |
if len(silence_parts) < 5: | |
return 0 | |
# Remove the longest part so the deviation is not that high | |
sorted_parts = sorted(silence_parts) | |
filtered_parts = [part for part in sorted_parts if part < sorted_parts[-1]] | |
sum_parts = sum(filtered_parts) | |
if sum_parts == 0: | |
return 0 | |
mean = sum_parts / len(filtered_parts) | |
return mean | |
def calculate_silent_beat_length(transcribed_data: list[TranscribedData]): | |
print(f"{ULTRASINGER_HEAD} Calculating silence parts for linebreaks.") | |
silent_parts = [] | |
for i, data in enumerate(transcribed_data): | |
if i < len(transcribed_data) - 1: | |
silent_parts.append(transcribed_data[i + 1].start - data.end) | |
return deviation(silent_parts) | |
def create_repitched_txt_from_ultrastar_data( | |
input_file: str, note_numbers: list[int], output_repitched_ultrastar: str | |
) -> None: | |
"""Creates a repitched ultrastar txt file from the original one""" | |
# todo: just add '_repitched' to input_file | |
print( | |
"{PRINT_ULTRASTAR} Creating repitched ultrastar txt -> {input_file}_repitch.txt" | |
) | |
# todo: to reader | |
with open(input_file, "r", encoding=FILE_ENCODING) as file: | |
txt = file.readlines() | |
i = 0 | |
# todo: just add '_repitched' to input_file | |
with open(output_repitched_ultrastar, "w", encoding=FILE_ENCODING) as file: | |
for line in txt: | |
if line.startswith(f"{UltrastarTxtNoteTypeTag.NORMAL} "): | |
parts = re.findall(r"\S+|\s+", line) | |
# between are whitespaces | |
# [0] : | |
# [2] start beat | |
# [4] duration | |
# [6] pitch | |
# [8] word | |
parts[6] = str(note_numbers[i]) | |
delimiter = "" | |
file.write(delimiter.join(parts)) | |
i += 1 | |
else: | |
file.write(line) | |
def add_score_to_ultrastar_txt(ultrastar_file_output: str, score: Score) -> None: | |
"""Adds the score to the ultrastar txt file""" | |
with open(ultrastar_file_output, "r", encoding=FILE_ENCODING) as file: | |
text = file.read() | |
text = text.split("\n") | |
for i, line in enumerate(text): | |
if line.startswith(f"#{UltrastarTxtTag.COMMENT}:"): | |
text[ | |
i | |
] = f"{line} | Score: total: {score.score}, notes: {score.notes} line: {score.line_bonus}, golden: {score.golden}" | |
break | |
if line.startswith(( | |
f"{UltrastarTxtNoteTypeTag.FREESTYLE} ", | |
f"{UltrastarTxtNoteTypeTag.NORMAL} ", | |
f"{UltrastarTxtNoteTypeTag.GOLDEN} ", | |
f"{UltrastarTxtNoteTypeTag.RAP} ", | |
f"{UltrastarTxtNoteTypeTag.RAP_GOLDEN} ")): | |
text.insert( | |
i, | |
f"#{UltrastarTxtTag.COMMENT}: UltraSinger [GitHub] | Score: total: {score.score}, notes: {score.notes} line: {score.line_bonus}, golden: {score.golden}", | |
) | |
break | |
text = "\n".join(text) | |
with open(ultrastar_file_output, "w", encoding=FILE_ENCODING) as file: | |
file.write(text) | |
class UltraStarWriter: | |
"""Docstring""" | |