Blane187's picture
Update app.py
8a0132d verified
import gradio as gr
import json
import logging
import random
from dataclasses import dataclass
from typing import Union
from do_not_delete_or_move_this import midi3 as mid3, midi2 as mid2
# Define constants
DISABLE_PROMPTS = True
SWAP_BF_EN = False
@dataclass(frozen=True)
class Preferences:
jack_mode: int
note_tolerance: int
def process_notes(channel_data, spb: float, prefs: Preferences):
chart_notes = []
prev_note = [0, 60, 100, 0.0]
prev_pitch = prev_note[1]
cur_arrow = random.randint(0, 3)
for note in channel_data:
cur_pitch = note[1]
diff = cur_pitch - prev_pitch
if cur_pitch < prev_pitch:
cur_arrow = (cur_arrow - one_or_two_seed(diff)) % 4
elif cur_pitch > prev_pitch:
cur_arrow = (cur_arrow + one_or_two_seed(diff)) % 4
elif random.randint(1, 3) <= prefs.jack_mode:
cur_arrow = (cur_arrow + random.randint(-2, 2)) % 4
sus_length = note[3] * 0.85 * 1000 if note[2] < 60 or note[3] > (spb / 2) + 0.0001 else 0
chart_notes.append([note[0] * 1000, cur_arrow, sus_length])
prev_pitch = cur_pitch
return chart_notes
def one_or_two_seed(seed: int) -> int:
seed = abs(seed)
seed_map = {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 3}
chance = seed_map.get(seed, 3)
rand = random.randint(0, 6)
return 2 if chance >= rand else 1
def split_into_sections(notes: list, spb: float):
section_length = spb * 4
full_note_data = {}
for note in notes:
note_section = ((note[0] / 1000) + 0.0001) // section_length
full_note_data.setdefault(note_section, []).append(note)
highest_channel = max(int(mm) for mm in full_note_data.keys()) + 1
isolated_section_list = [[] for _ in range(0, highest_channel)]
for key, item in full_note_data.items():
isolated_section_list[int(key)] = item
return isolated_section_list
def compare_sections(en_section: list, bf_section: list, prev_musthit: bool, prefs: Preferences):
percent_bf = (len(bf_section) / len(en_section)) * 100 if len(en_section) != 0 else (100 if len(bf_section) else 50)
must_hit = percent_bf >= prefs.note_tolerance or (prefs.note_tolerance >= percent_bf >= (100 - prefs.note_tolerance) and not prev_musthit)
unsorted_combined_sections = []
if must_hit:
unsorted_combined_sections.extend([[en_note[0], en_note[1] + 4, en_note[2]] for en_note in en_section])
unsorted_combined_sections.extend([[bf_note[0], bf_note[1], bf_note[2]] for bf_note in bf_section])
else:
unsorted_combined_sections.extend([[en_note[0], en_note[1], en_note[2]] for en_note in en_section])
unsorted_combined_sections.extend([[bf_note[0], bf_note[1] + 4, bf_note[2]] for bf_note in bf_section])
sorted_combined_sections = sorted(unsorted_combined_sections, key=lambda x: (x[0], x[1]))
return sorted_combined_sections, must_hit
def main(path_to: str, prefs: Preferences):
pm = mid2.process_midi(path_to)
spb = mid2.obtain_spb(path_to)
bpm = round(60 / spb, 3)
full_mid_data = mid3.main(pm)
full_note_list_en = process_notes(full_mid_data[0], spb, prefs)
full_note_list_bf = process_notes(full_mid_data[1], spb, prefs)
sectioned_en_list = split_into_sections(full_note_list_en, spb)
sectioned_bf_list = split_into_sections(full_note_list_bf, spb)
sectioned_bf_list, sectioned_en_list = sectioned_en_list, sectioned_bf_list if SWAP_BF_EN else (sectioned_bf_list, sectioned_en_list)
len_diff = abs(len(sectioned_bf_list) - len(sectioned_en_list))
if len(sectioned_en_list) < len(sectioned_bf_list):
sectioned_en_list.extend([[] for _ in range(0, len_diff)])
if len(sectioned_en_list) > len(sectioned_bf_list):
sectioned_bf_list.extend([[] for _ in range(0, len_diff)])
musthit = True
json_notes = []
for en_section, bf_section in zip(sectioned_en_list, sectioned_bf_list):
sec_notes, musthit = compare_sections(en_section, bf_section, musthit, prefs)
json_notes.append({"sectionNotes": sec_notes, "lengthInSteps": 16, "mustHitSection": musthit})
return json.dumps({"song": {"player1": "bf", "player2": "dad", "gfVersion": "gf",
"notes": json_notes, "stage": "", "needsVoices": True,
"validScore": True, "bpm": bpm, "speed": 2.4, "song": "tempSong"}}, indent=4)
def gradio_interface(midi_file, jack_mode, note_tolerance):
prefs = Preferences(jack_mode=jack_mode, note_tolerance=note_tolerance)
return main(midi_file.name, prefs)
theme = gr.themes.Soft(
primary_hue="sky",
secondary_hue="violet",
neutral_hue="gray",
font=[gr.themes.GoogleFont('orbitron')]
)
# Create Gradio interface
mainweb = gr.Interface(
fn=gradio_interface,
inputs=[
gr.File(file_count="single", label="MIDI File"),
gr.Slider(minimum=0, maximum=3, step=1, value=0, label="Jack Mode"),
gr.Slider(minimum=0, maximum=100, step=5, value=75, label="Note Tolerance")
],
outputs=gr.Textbox(label="JSON Output"),
)
with gr.Blocks() as information:
with open("anotherday.md", "r", encoding="utf8") as f:
info = f.read()
gr.Markdown(value=info)
with gr.Blocks(theme=theme) as demo:
gr.Markdown("# Funkin Chart Generator")
gr.Markdown("Automatically generate an FnF chart only using MIDI files")
gr.TabbedInterface([mainweb, information], ["main settings", "information"])
demo.launch()