Spaces:
Running
Running
File size: 5,535 Bytes
88f34f8 8a0132d 88f34f8 837da5d 88f34f8 41e3e01 88f34f8 41e3e01 837da5d 8a0132d 837da5d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
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()
|