diff --git a/CRC.py b/CRC.py index 545266f..8497e6b 100644 --- a/CRC.py +++ b/CRC.py @@ -105,5 +105,7 @@ def qbkey_hex(text, endian = "big"): if __name__ == "__main__": - test = "Solo 1" - print(QBKey_qs(test)) \ No newline at end of file + x = 0 + while x != -1: + x = input("Enter string: ") + print(QBKey(x)) diff --git a/GH Toolkit.py b/GH Toolkit.py index ed3de50..04357e6 100644 --- a/GH Toolkit.py +++ b/GH Toolkit.py @@ -16,6 +16,7 @@ menu_options = [ "compile_wt_song - Compile a Guitar Hero: World Tour song pak file from audio and a MIDI file", "convert_ska_file - Convert a SKA file from one game to another (for conversions)", + # "convert_5_to_wt - Convert a song from Guitar Hero 5 or Warriors of Rock to the World Tour format", "convert_to_5 - Convert a WT song to Guitar Hero 5 format", "convert_to_gh3 - Convert a GH:A song to GH3 (removing rhythm anims/special mocap calls, porting lights and cameras)", "convert_to_gha - Convert a GH3 song to GH:A (adding rhythm anims, porting lights and cameras)", @@ -96,19 +97,22 @@ def manual_input(): print("Choose your singer: ") print("1.) Default\n2.) Steven Tyler\n3.) Run DMC\n4.) Joe Perry") singer = singer_dict[input("Type in the number corresponding to your singer: ")] - song_name, song_pak = convert_to_gha(midqb_file, output, lipsync_dict[singer]) + song_name, song_pak = convert_to_gha(midqb_file, output, singer) if midqb_file.lower().endswith(".mid"): pak_name = f'\\{song_name}_song.pak.xen' else: pak_name = f'\\{song_name}_song_GHA.pak.xen' with open(output + pak_name, 'wb') as f: f.write(song_pak) + input("Convert complete! Press Enter to continue. ") elif main_menu == "convert_to_gh3": midqb_file = input("Drag in your song PAK file: ").replace("\"", "") output = f'{os.path.dirname(midqb_file)}' song_name, song_pak = convert_to_gh3(midqb_file, output) with open(output + f'\\{song_name}_song_GH3.pak.xen', 'wb') as f: f.write(song_pak) + input("Convert complete! Press Enter to continue. ") + elif main_menu == "convert_to_5" or main_menu == "convert_to_ghwor": compile_args = [] if main_menu == "convert_to_ghwor": @@ -240,6 +244,25 @@ def manual_input(): input("\nPress any key to exit.") input("Complete! Press any key to exit.") + elif main_menu == "convert_5_to_wt": + midqb_file = input("Drag in your song PAK file: ").replace("\"", "") + output = f'{os.path.dirname(midqb_file)}' + midname = os.path.basename(midqb_file)[:os.path.basename(midqb_file).find(".")] + if re.search(r'^[a-c]dlc', midname, flags=re.IGNORECASE): + midname = midname[1:] + print("""\nYou can add a performance override file to be added to the song.\nFor example, you can add tapping animation events or for WoR songs, add PlayIdle events.""".replace("\t","")) + perf_override = input("Drag in your perf override file (or leave this blank) and press Enter to continue: ").replace("\"", "") + music_override = input("Drag in the folder containing your audio files from the PS3 version.\nLeave blank to skip: ").replace("\"", "") + if music_override: + try: + audio_functions.strip_mp3(music_override) + except Exception as E: + raise E + print("Could not convert audio.") + print(midname) + wt_pak = convert_5_to_wt(midqb_file, perf_override) + with open(f"{output}\\a{midname}.pak.xen".lower(), "wb") as f: + f.write(wt_pak) elif main_menu == 1337: input("Ha! Got ourselves a leet hacker over here ") @@ -438,6 +461,18 @@ def launch_gui(ghproj = ""): anim_string = f"qb_file = songs/{midname}_scripts.qb".lower() + "\n" + anim_string with open(f"{output}\\{midname}_scripts.txt", "w") as f: f.write(anim_string) + elif sys.argv[1] == "convert_5_to_wt": + if "output" not in locals(): + output = f'{os.path.dirname(input_file)}' + midname = os.path.basename(input_file)[:os.path.basename(input_file).find(".")] + if re.search(r'^[a-c]dlc', midname, flags=re.IGNORECASE): + midname = midname[1:] + compile_args = [] + if len(sys.argv) > 3: + compile_args.extend(sys.argv[3:]) + wt_pak = convert_5_to_wt(input_file, *compile_args) + with open(f"{output}\\a{midname}.pak.xen".lower(), "wb") as f: + f.write(wt_pak) elif sys.argv[1] == "make_wt_mid": qb_file = mid_qb.make_wt_qb(sys.argv[2]) elif sys.argv[1] == "extract_fsb": diff --git a/compile-folder.bat b/compile-folder.bat index f5cee13..0395658 100644 --- a/compile-folder.bat +++ b/compile-folder.bat @@ -1,9 +1,10 @@ -pyinstaller -p "D:\GitHub\Guitar-Hero-III-Tools\create_audio" ^ --p D:\GitHub\Guitar-Hero-III-Tools\midqb_gen ^ --p D:\GitHub\Guitar-Hero-III-Tools\pak_extract ^ --p D:\GitHub\Guitar-Hero-III-Tools\ska_converter ^ --p D:\GitHub\Guitar-Hero-III-Tools ^ --p "D:\GitHub\Guitar-Hero-III-Tools\gui" ^ +pyinstaller -p ".\create_audio" ^ +-p .\midqb_gen ^ +-p .\pak_extract ^ +-p .\ska_converter ^ +-p . ^ +-p ".\gui" ^ +--add-data "anim_loops.txt;." ^ --add-data "debug.txt;." ^ --add-data "conversion_files_prod;conversion_files" ^ --add-data "create_audio\default_audio\blank.mp3;create_audio\default_audio" ^ diff --git a/conversion_files/basic_loops.txt b/conversion_files/basic_loops.txt new file mode 100644 index 0000000..48e2b50 --- /dev/null +++ b/conversion_files/basic_loops.txt @@ -0,0 +1,112 @@ +car_female_anim_struct = { + guitar = { + pak = L_GUIT_Ginger_Bulls_anims + anim_set = L_GUIT_Ginger_Bulls_anims_set + finger_anims = guitarist_finger_anims_car_female + fret_anims = fret_anims_rocker + strum_anims = CAR_Female_Normal + facial_anims = facial_anims_female_rocker + } + Bass = { + pak = L_GUIT_Judita_Bulls_anims + anim_set = L_GUIT_Judita_Bulls_anims_set + finger_anims = guitarist_finger_anims_car_female + fret_anims = fret_anims_rocker + strum_anims = CAR_Female_Normal + facial_anims = facial_anims_female_rocker + } + drum = { + pak = L_DRUM_Loops_Standard_anims + anim_set = l_drum_loops_standard_anims_set + facial_anims = facial_anims_female_rocker + } + vocals = { + pak = L_SING_Amanda_Bulls_anims + anim_set = L_SING_Amanda_Bulls_anims_set + facial_anims = facial_anims_female_rocker + } +} +car_male_anim_struct = { + guitar = { + pak = L_GUIT_Matt_Bulls_anims + anim_set = L_GUIT_Matt_Bulls_anims_set + finger_anims = guitarist_finger_anims_CAR_Male + fret_anims = fret_anims_rocker + strum_anims = CAR_Male_Normal + facial_anims = facial_anims_male_rocker + } + Bass = { + pak = L_GUIT_Davidicus_Bulls_anims + anim_set = L_GUIT_Davidicus_Bulls_anims_set + finger_anims = guitarist_finger_anims_CAR_Male + fret_anims = fret_anims_rocker + strum_anims = CAR_Male_Normal + facial_anims = facial_anims_male_rocker + } + drum = { + pak = L_DRUM_Loops_Standard_anims + anim_set = l_drum_loops_standard_anims_set + facial_anims = facial_anims_male_rocker + } + vocals = { + pak = L_SING_Patrick_Bulls_anims + anim_set = L_SING_Patrick_Bulls_anims_set + facial_anims = facial_anims_male_rocker + } +} +car_female_alt_anim_struct = { + guitar = { + pak = L_GUIT_Ginger_Bulls_anims + anim_set = L_GUIT_Ginger_Bulls_anims_set + finger_anims = guitarist_finger_anims_car_female + fret_anims = fret_anims_rocker + strum_anims = CAR_Female_Normal + facial_anims = facial_anims_female_rocker + } + Bass = { + pak = L_GUIT_Judita_Bulls_anims + anim_set = L_GUIT_Judita_Bulls_anims_set + finger_anims = guitarist_finger_anims_car_female + fret_anims = fret_anims_rocker + strum_anims = CAR_Female_Normal + facial_anims = facial_anims_female_rocker + } + drum = { + pak = L_DRUM_Loops_Standard_anims + anim_set = l_drum_loops_standard_anims_set + facial_anims = facial_anims_female_rocker + } + vocals = { + pak = L_SING_Amanda_Bulls_anims + anim_set = L_SING_Amanda_Bulls_anims_set + facial_anims = facial_anims_female_rocker + } +} +car_male_alt_anim_struct = { + guitar = { + pak = L_GUIT_Matt_Bulls_anims + anim_set = L_GUIT_Matt_Bulls_anims_set + finger_anims = guitarist_finger_anims_CAR_Male + fret_anims = fret_anims_rocker + strum_anims = CAR_Male_Normal + facial_anims = facial_anims_male_rocker + } + Bass = { + pak = L_GUIT_Davidicus_Bulls_anims + anim_set = L_GUIT_Davidicus_Bulls_anims_set + finger_anims = guitarist_finger_anims_CAR_Male + fret_anims = fret_anims_rocker + strum_anims = CAR_Male_Normal + facial_anims = facial_anims_male_rocker + } + drum = { + pak = L_DRUM_Loops_Standard_anims + anim_set = l_drum_loops_standard_anims_set + facial_anims = facial_anims_male_rocker + } + vocals = { + pak = L_SING_Patrick_Bulls_anims + anim_set = L_SING_Patrick_Bulls_anims_set + facial_anims = facial_anims_male_rocker + } +} diff --git a/convert_5_to_wt.py b/convert_5_to_wt.py new file mode 100644 index 0000000..e57d395 --- /dev/null +++ b/convert_5_to_wt.py @@ -0,0 +1,10 @@ +import subprocess +import os +import shutil + +pak_name = os.path.basename(input("Drag in your pak file: ")).replace("\"","") +root_folder = os.path.realpath(os.path.dirname(__file__)) +perf = "_performance.txt" if os.path.isfile("_performance.txt") else "" +scripts = "_song_scripts_anim_loops.txt" if os.path.isfile("_song_scripts_anim_loops.txt") else "" +ska_files = "SKA Files" if os.path.isdir("SKA Files") else "" +subprocess.run(["python",f"{root_folder}\\GHToolkit.py", "convert_5_to_wt", pak_name, perf]) \ No newline at end of file diff --git a/create_audio/audio_functions.py b/create_audio/audio_functions.py index dcbb2b2..40744b0 100644 --- a/create_audio/audio_functions.py +++ b/create_audio/audio_functions.py @@ -7,6 +7,7 @@ import sox import json +import strip_mp3_padding as pad_strip from crypt_keys import ghwor_keys, ghwor_cipher from struct import pack as floatPack, unpack as f_up @@ -43,16 +44,6 @@ def pad_wav_file_sox(input_file, target_length, file_num = 0): # Calculate the required padding padding = target_length - duration - # Set up SoX transform parameters - tfm = sox.Transformer() - transform_args = ["mp3"] - if sample_rate != 48000: - transform_args.append(48000) - tfm.set_output_format(*transform_args) - - # Add the padding to the input file and convert it to an MP3 - if padding > 0: - tfm.pad(end_duration=padding) # Run the sox command, save temp file, and re-read temp_dir = ".\\temp" temp_out = temp_dir + f"\\temp_{file_num}.mp3" @@ -62,8 +53,13 @@ def pad_wav_file_sox(input_file, target_length, file_num = 0): except: pass + sox_command = ["sox", input_file, "-c", "2", "-C", "128", "-r", "48000", temp_out] + # Add the padding to the input file + if padding > 0: + sox_command.extend(["pad", "0", str(padding)]) try: - tfm.build_file(input_filepath=input_file, output_filepath=temp_out) + subprocess.run(sox_command) + #tfm.build_file(input_filepath=input_file, output_filepath=temp_out) except Exception as E: raise E @@ -72,7 +68,7 @@ def pad_wav_file_sox(input_file, target_length, file_num = 0): return padded_mp3_data -def make_preview_sox(start_time, end_time): +def make_preview_sox(start_time, end_time, *args): # Run the sox command, save temp file, and re-read temp_dir = ".\\temp" @@ -82,24 +78,34 @@ def make_preview_sox(start_time, end_time): os.remove(temp_out) audio_list = [] - for file in os.listdir(temp_dir): - audio_list.append(temp_dir + "\\" + file) + if "rendered_preview" in args: + print("Converting custom preview audio") + audio_list.append(args[args.index("rendered_preview") + 1]) + + else: + for file in os.listdir(temp_dir): + audio_list.append(temp_dir + "\\" + file) extra_args = [] + # Create SoX Combiner - if len(audio_list) == 1: + if "rendered_preview" not in args: + if len(audio_list) == 1: + preview = sox.Transformer() + preview.set_input_format("mp3") + audio_list = audio_list[0] + else: + preview = sox.Combiner() + preview.set_input_format(["mp3"] * len(audio_list)) + extra_args.append("mix") + + preview.trim(start_time, end_time) + preview.fade(1.0, 1.0) + preview.vol(-7.0, "db") + else: preview = sox.Transformer() - preview.set_input_format("mp3") audio_list = audio_list[0] - else: - preview = sox.Combiner() - preview.set_input_format(["mp3"] * len(audio_list)) - extra_args.append("mix") preview.set_output_format("mp3", 48000) - preview.trim(start_time, end_time) - preview.fade(1.0, 1.0) - preview.vol(-7.0, "db") - try: preview.build(audio_list, temp_out, *extra_args) @@ -153,32 +159,16 @@ def pad_wav_file_ffmpeg(input_file, target_length, file_num=0): if padding > 0: # Use FFmpeg to add silence to the end of the audio - subprocess.run([ - 'ffmpeg', '-y', '-f', 'lavfi', '-i', f'anullsrc=r=48000:cl=stereo:d={padding}', - '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', silent_file - ], check=True) subprocess.run([ - 'ffmpeg', '-y', '-i', input_file, '-ar', '48000', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out + 'ffmpeg', '-y', '-i', input_file, '-ar', '48000', '-ac', '2', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out ], check=True) - with open(f"{temp_dir}\\temp_concat.txt", "w") as f: - f.write(f"file temp_{file_num}.mp3\n") - f.write(f"file silent.mp3\n") - - # Concatenate the input audio file and the silent audio file - subprocess.run([ - 'ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', f"{temp_dir}\\temp_concat.txt", '-c', 'copy', '-map_metadata', '-1', - temp_out - ], check=True) - - os.remove(f"{temp_dir}\\temp_concat.txt") - os.remove(f"{temp_dir}\\silent.mp3") else: # Convert audio to mp3 with 48000 sample rate subprocess.run([ - 'ffmpeg', '-y', '-i', input_file, '-ar', '48000', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out + 'ffmpeg', '-y', '-i', input_file, '-ar', '48000', '-ac', '2', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out ], check=True) # Read the padded mp3 file @@ -187,7 +177,7 @@ def pad_wav_file_ffmpeg(input_file, target_length, file_num=0): return padded_mp3_data -def make_preview_ffmpeg(start_time, end_time): +def make_preview_ffmpeg(start_time, end_time, *args): # Set temp directory and output file temp_dir = ".\\temp" temp_out = temp_dir + "\\temp.mp3" @@ -196,17 +186,11 @@ def make_preview_ffmpeg(start_time, end_time): os.remove(temp_out) # Get a list of all audio files in temp directory - audio_list = [os.path.join(temp_dir, file) for file in os.listdir(temp_dir)] - - trim_duration = end_time - start_time - fade_duration = 1.0 - - # Build filtergraph for mixing and trimming audio - mix_filter = ''.join([f'[{i}:0]' for i in range(len(audio_list))]) + f'amix=inputs={len(audio_list)}:duration=first:dropout_transition=2:normalize=0[mixout]' - # trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration}[final]' - trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration},afade=t=in:st={start_time}:d=1,afade=t=out:st={start_time+trim_duration - 1}:d=1,volume=-7.0dB[final]' - #trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration},afade=t=in:ss=0:d={fade_duration},afade=t=out:st={trim_duration-fade_duration}:d={fade_duration}[final]' - filtergraph = mix_filter + ';' + trim_filter + if "rendered_preview" in args: + print("Using custom preview audio") + audio_list = [args[args.index("rendered_preview") + 1]] + else: + audio_list = [os.path.join(temp_dir, file) for file in os.listdir(temp_dir)] command = ['ffmpeg'] @@ -214,11 +198,27 @@ def make_preview_ffmpeg(start_time, end_time): for audio_file in audio_list: command.extend(['-i', audio_file]) - # Add the rest of the command - command.extend([ - '-filter_complex', filtergraph, '-map', '[final]', - '-ar', '48000', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out - ]) + if "rendered_preview" not in args: + trim_duration = end_time - start_time + fade_duration = 1.0 + + # Build filtergraph for mixing and trimming audio + mix_filter = ''.join([f'[{i}:0]' for i in range(len(audio_list))]) + f'amix=inputs={len(audio_list)}:duration=first:dropout_transition=2:normalize=0[mixout]' + # trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration}[final]' + trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration},afade=t=in:st={start_time}:d=1,afade=t=out:st={start_time+trim_duration - 1}:d=1,volume=-7.0dB[final]' + #trim_filter = f'[mixout]atrim=start={start_time}:duration={trim_duration},afade=t=in:ss=0:d={fade_duration},afade=t=out:st={trim_duration-fade_duration}:d={fade_duration}[final]' + filtergraph = mix_filter + ';' + trim_filter + + # Add the rest of the command + command.extend([ + '-filter_complex', filtergraph, '-map', '[final]', + '-ar', '48000', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out + ]) + else: + # Add the rest of the command without filters + command.extend([ + '-ac', '2', '-ar', '48000', '-acodec', 'libmp3lame', '-b:a', '128k', '-map_metadata', '-1', temp_out + ]) try: # Run FFmpeg command @@ -237,58 +237,70 @@ def make_preview_ffmpeg(start_time, end_time): def is_program_in_path(program_name): return shutil.which(program_name) is not None -def get_padded_audio(all_audio, shortname, start_time = 30, end_time = 60, *args): +def get_padded_audio(all_audio, start_time = 30, end_time = 60, *args): # Get the maximum length - time_0 = time.time() - encrypt = False - if "encrypt" in args: - encrypt = True max_length = 0 if not "audio_len" in args: print("Converting all files to MP3 and padding to the longest") - if is_program_in_path("sox") and "ffmpeg" not in args: - for input_file in all_audio: - duration = get_audio_duration_sox(input_file) - max_length = max(max_length, duration) - if "audio_len" in args: - return max_length - print("Using SoX to convert") - # Pad each input file and store the output in a list - print(f"Padding all files to match the longest file ({strftime('%M:%S', gmtime(max_length))})") - # Pad each input file and store the output in a list + if "no_convert" in args: padded_mp3_data_list = [] for enum, input_file in enumerate(all_audio): - if input_file.endswith("default_audio/blank.mp3"): - with open(input_file, 'rb') as f: - padded_mp3_data_list.append(f.read()) - else: - padded_mp3_data_list.append(pad_wav_file_sox(input_file, max_length, enum)) - preview = make_preview_sox(start_time, end_time) - elif is_program_in_path("ffmpeg"): - for input_file in all_audio: - duration = get_audio_duration_ffmpeg(input_file) - max_length = max(max_length, duration) - if "audio_len" in args: - return max_length - print("Using FFmpeg to convert") - # Pad each input file and store the output in a list - padded_mp3_data_list = [] - for enum, input_file in enumerate(all_audio): - if input_file.endswith("default_audio/blank.mp3"): - with open(input_file, 'rb') as f: - padded_mp3_data_list.append(f.read()) - else: - padded_mp3_data_list.append(pad_wav_file_ffmpeg(input_file, max_length, enum)) - preview = make_preview_ffmpeg(start_time, end_time) - elif "ffmpeg" in args: - print("FFmpeg was asked to use, but cannot find it in PATH") - return 0 + with open(input_file, 'rb') as f: + padded_mp3_data_list.append(f.read()) + preview = padded_mp3_data_list[-1] + padded_mp3_data_list.pop() else: - print("Could not find ffmpeg or SoX in PATH") - return 0 + if is_program_in_path("sox") and "ffmpeg" not in args: + for input_file in all_audio: + duration = get_audio_duration_sox(input_file) + max_length = max(max_length, duration) + if "audio_len" in args: + return max_length + print("Using SoX to convert") + # Pad each input file and store the output in a list + print(f"Padding all files to match the longest file ({strftime('%M:%S', gmtime(max_length))})") + # Pad each input file and store the output in a list + padded_mp3_data_list = [] + for enum, input_file in enumerate(all_audio): + if input_file.endswith("default_audio/blank.mp3"): + with open(input_file, 'rb') as f: + padded_mp3_data_list.append(f.read()) + else: + padded_mp3_data_list.append(pad_wav_file_sox(input_file, max_length, enum)) + preview = make_preview_sox(start_time, end_time, *args) + elif is_program_in_path("ffmpeg"): + for input_file in all_audio: + duration = get_audio_duration_ffmpeg(input_file) + max_length = max(max_length, duration) + if "audio_len" in args: + return max_length + print("Using FFmpeg to convert") + # Pad each input file and store the output in a list + padded_mp3_data_list = [] + for enum, input_file in enumerate(all_audio): + if input_file.endswith("default_audio/blank.mp3"): + with open(input_file, 'rb') as f: + padded_mp3_data_list.append(f.read()) + else: + padded_mp3_data_list.append(pad_wav_file_ffmpeg(input_file, max_length, enum)) + preview = make_preview_ffmpeg(start_time, end_time, *args) + elif "ffmpeg" in args: + print("FFmpeg was asked to use, but cannot find it in PATH") + return 0 + else: + print("Could not find ffmpeg or SoX in PATH") + return 0 stream_size = 0 for x in padded_mp3_data_list: stream_size = max(len(pullMP3Frames(x)[0]), stream_size) + return padded_mp3_data_list, preview, stream_size + +def compile_wt_audio(all_audio, shortname, start_time, end_time, *args): + time_0 = time.time() + encrypt = False + if "encrypt" in args: + encrypt = True + padded_mp3_data_list, preview, stream_size = get_padded_audio(all_audio, start_time, end_time, *args) extra_args = ["-stream_size", stream_size] print("Creating Drum Audio") drum_files = createFSB4(padded_mp3_data_list[:4], f"{shortname}_1", encrypt, *extra_args) @@ -303,6 +315,23 @@ def get_padded_audio(all_audio, shortname, start_time = 30, end_time = 60, *args print(f"Audio generation took {time_1-time_0} seconds") return drum_files, inst_files, song_files, preview +def compile_gh3_audio(all_audio, shortname, start_time, end_time, *args): + time_0 = time.time() + audio_names = [] + audio_paths = [] + for key,value in all_audio.items(): + audio_names.append(key) + audio_paths.append(value) + padded_mp3_data_list, preview, stream_size = get_padded_audio(audio_paths, start_time, end_time, *args) + padded_mp3_data_list.append(preview) + audio_names.append("preview") + extra_args = ["-stream_size", stream_size, "compiler"] + print("Creating Audio") + fsb_file, fsb_dat = createFSB3(padded_mp3_data_list, f"{shortname}", *extra_args, audio_names = audio_names) + time_1 = time.time() + + print(f"Audio generation took {time_1 - time_0} seconds") + return fsb_file, fsb_dat def flipBits(audio): return bytes(br[x] for x in audio) @@ -571,10 +600,10 @@ def pullMP3Frames(audio): position += to_pull return frames, first_frame -def FSBentry(data, filename): +def FSBentry(data, filename, *args): filesize = len(data) frameSize = 1152 - frames = len(pullMP3Frames(data)) + frames = len(pullMP3Frames(data)[0]) file_entry_len = 80 fsb_file = bytes(filename if len(filename) <= 30 else filename[:30], "latin-1") while len(fsb_file) < 30: @@ -583,7 +612,10 @@ def FSBentry(data, filename): loop_start = 0 loop_end = samples_length - 1 mode = 576 - sample_rate = 44100 + if "compiler" in args: + sample_rate = 48000 + else: + sample_rate = 44100 volume, priority = 255, 255 pan = 128 channels = 2 @@ -610,17 +642,27 @@ def FSBentry(data, filename): return fsb_entry -def createFSB3(file, shortname): +def createFSB3(files, shortname, *args, **kwargs): headers = [] audio = bytearray() dat_entries = [] - for x in file: - audio_name = f"{shortname}_{os.path.basename(x)}" - with open(x, 'rb') as f: - audio_data = f.read() - headers.append(FSBentry(audio_data, audio_name)[0]) - dat_entries.append(os.path.splitext(audio_name)[0]) - audio += audio_data + if type(files) == list: + for y, x in enumerate(files): + if type(x) == str: + audio_name = f"{shortname}_{os.path.basename(x)}" + with open(x, 'rb') as f: + audio_data = f.read() + else: + stream_frames, temp_frame = pullMP3Frames(x) + audio_name = f"{shortname}_{kwargs['audio_names'][y]}" + if "-stream_size" in args and not audio_name.endswith("preview"): + stream_length = int(args[args.index("-stream_size") + 1]) + if len(stream_frames) != stream_length: + stream_frames.extend([blank_48k_mp3] * (stream_length - len(stream_frames))) + audio_data = b''.join(stream_frames) + audio += audio_data + headers.append(FSBentry(audio_data, audio_name, *args)) + dat_entries.append(os.path.splitext(audio_name)[0]) print(dat_entries) fsb_file = bytearray() fsb_file += b'FSB3' # FSB3 header @@ -643,12 +685,15 @@ def createFSB3(file, shortname): fsb_dat += toBytes(y, 4, "big") fsb_dat += toBytes(0, 12) - raise Exception - with open(f"{shortname}.fsb.xen", 'wb') as f: + # print(binascii.hexlify(fsb_dat, ' ', 1)) + return fsb_file, fsb_dat + +def writeFSB3(fsb_file, fsb_dat, filepath): + print("Encrypting FSB file.") + with open(f"{filepath}.fsb.xen", 'wb') as f: f.write(encrypt_fsb3(fsb_file)) - with open(f"{shortname}.dat.xen", 'wb') as f: + with open(f"{filepath}.dat.xen", 'wb') as f: f.write(fsb_dat) - # print(binascii.hexlify(fsb_dat, ' ', 1)) return def splitFSBFrames(audio): @@ -766,39 +811,43 @@ def file_renamer(file_name): file_name = file_name[1:] return file_name - +def crypt_files(dirin, filename, gh3 = False): + t0 = time.process_time() + with open(f"{dirin}\\{filename}", 'rb') as f: + audio = f.read() + if filename.lower().endswith(".fsb.xen") or filename.lower().endswith(".fsb.ps3"): + crypted = decrypt_file(audio, filename) + elif filename.endswith(".fsb"): + if gh3: + crypted = encrypt_fsb3(audio) + else: + no_ext = file_renamer(os.path.basename(filename[:-4]).lower()) + key = generate_fsb_key(no_ext) + crypted = encrypt_fsb4(audio, key) + else: + print(f"Unknown file {filename} found, skipping...") + return 0, 0 + t1 = time.process_time() + fileout = (f"{filename}.xen" if filename.endswith(".fsb") else filename[:-4]) + print(filename, t1 - t0) + return crypted, fileout def main(gh3 = False): dirin = f".\\input" dirout = f".\\output" for filename in os.listdir(dirin): - t0 = time.process_time() - with open(f"{dirin}\\{filename}", 'rb') as f: - audio = f.read() - if filename.lower().endswith(".fsb.xen") or filename.lower().endswith(".fsb.ps3"): - crypted = decrypt_file(audio, filename) - elif filename.endswith(".fsb"): - if gh3: - crypted = encrypt_fsb3(audio) - else: - no_ext = file_renamer(os.path.basename(filename[:-4]).lower()) - key = generate_fsb_key(no_ext) - crypted = encrypt_fsb4(audio, key) - else: - print(f"Unknown file {filename} found, skipping...") - continue - t1 = time.process_time() - fileout = (f"{filename}.xen" if filename.endswith(".fsb") else filename[:-4]) - with open(f"{dirout}\\{fileout}", 'wb') as f: + crypted, fileout = crypt_files(dirin, filename, gh3) + with open(f"{dirout}\\{fileout}", 'wb') as f: f.write(crypted) - print(filename, t1 - t0) - return +def strip_mp3(in_folder): + pad_strip.main(in_folder) + def test_make(): dirin = f".\\input" dirout = f".\\output" - song_name = "" + song_name = "aqualung" files = [] songs = {} for filename in os.listdir(dirin): @@ -817,14 +866,16 @@ def test_make(): f.write(fsb_file) return -def test_combine(): - dirin = f".\\input" - dirout = f".\\output" - song_name = "test" +def test_combine(song_name = "output", dirin = ".\\input", dirout = ".\\output"): + song_name = song_name files = [] for filename in os.listdir(dirin): - files.append(f"{os.path.join(dirin,filename)}") - drum, inst, other, preview = get_padded_audio(files, song_name) + if filename.endswith(".mp3"): + files.append(f"{os.path.join(dirin,filename)}") + if not len(files) == 10: + print(f"Not enough files found. Found {len(files)}, expected 10.") + return + drum, inst, other, preview = compile_wt_audio(files, song_name, 0, 0, "no_convert") for enum, x in enumerate([drum, inst, other, preview]): if enum != 3: with open(f"{dirout}\\{song_name}_{enum+1}.fsb.xen", 'wb') as f: @@ -837,7 +888,21 @@ def test_combine(): # Playable: # Preview: sox -m D:\RB\Songs\Pleymo\Sept\GH3\Guitar.wav D:\RB\Songs\Pleymo\Sept\GH3\Bass.wav D:\RB\Songs\Pleymo\Sept\GH3\Backing.wav -C 128 output.mp3 trim 0:35 1:05 if __name__ == "__main__": - main() + if len(sys.argv) > 1: + if sys.argv[1] == "combine": + song_name = input("Enter the checksum for this song: ") + test_combine(song_name) + elif sys.argv[1] == "make": + test_make() + elif sys.argv[1] == "header": + with open(sys.argv[2], 'rb') as f: + headerFile = f.read()[:4] + header = parseMP3header("{:08b}".format(int(headerFile.hex(), 16))) + + for k, v in header.items(): + print(k, v) + else: + main() #test_combine() #test_make() diff --git a/create_audio/combine_fsb.bat b/create_audio/combine_fsb.bat new file mode 100644 index 0000000..58816a5 --- /dev/null +++ b/create_audio/combine_fsb.bat @@ -0,0 +1,3 @@ +@echo off + +audio_functions.py combine \ No newline at end of file diff --git a/create_audio/strip_mp3_padding.py b/create_audio/strip_mp3_padding.py new file mode 100644 index 0000000..2ffd676 --- /dev/null +++ b/create_audio/strip_mp3_padding.py @@ -0,0 +1,85 @@ +import subprocess +import os +import shutil +import audio_functions as af + +def delete_files_in_directory(directory_path): + try: + files = os.listdir(directory_path) + for file in files: + file_path = os.path.join(directory_path, file) + if os.path.isfile(file_path): + os.remove(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + print("All files deleted successfully.") + except OSError: + print("Error occurred while deleting files.") + +def main(in_folder = "input"): + if not shutil.which("fsbext"): + input("fsbext is not found in your computer's PATH.\nPress Enter to continue...") + return 0 + if in_folder == "input": + out_folder = "output" + else: + if not os.path.isdir(in_folder): + print("Invalid folder. Aborting audio function.") + return 0 + out_folder = os.path.join(os.path.dirname(in_folder),"audio_stripped") + try: + os.mkdir(out_folder) + except: + pass + try: + if in_folder == "input": + delete_files_in_directory("output") + subprocess.run(["python","audio_functions.py"]) + else: + for filename in os.listdir(in_folder): + crypted, fileout = af.crypt_files(in_folder, filename) + with open(f"{out_folder}\\{fileout}", 'wb') as f: + f.write(crypted) + for filename in os.listdir(out_folder): + print(filename) + subprocess.run(["fsbext", "-M","-d",out_folder, f"{out_folder}\\{filename}"]) + if filename.endswith("_1.FSB"): + new_folder = "drums" + elif filename.endswith("_2.FSB"): + new_folder = "playable" + elif filename.endswith("_3.FSB"): + new_folder = "non-playable" + elif filename.lower().endswith("_preview.fsb"): + new_folder = "preview" + else: + continue + os.rename(f"{out_folder}\\multichannel sound.mp3_channels", f"{out_folder}\\{new_folder}") + for filename in os.listdir(out_folder): + if filename == "playable": + modifier = 4 + elif filename == "non-playable": + modifier = 7 + elif filename == "preview": + modifier = 9 + else: + continue + for file in os.listdir(f"{out_folder}\\{filename}"): + audio_name = int(os.path.splitext(file)[0]) + audio_name += modifier + shutil.copy(f"{out_folder}\\{filename}\\{file}",f"{out_folder}\\drums\\{audio_name}.mp3") + for filename in os.listdir(f"{out_folder}\\drums"): + shutil.copy(f"{out_folder}\\drums\\{filename}", f"{in_folder}\\{filename}") + delete_files_in_directory(f"{out_folder}") + if in_folder == "input": + subprocess.run(["combine_fsb.bat"]) + else: + song_name = input("Enter the checksum for this song: ") + af.test_combine(song_name, in_folder, out_folder) + except Exception as e: + raise e + print(f"Error: {e}") + os.system('pause') + return 1 + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ghtoolkit.py b/ghtoolkit.py new file mode 100644 index 0000000..cd5f57b --- /dev/null +++ b/ghtoolkit.py @@ -0,0 +1,9 @@ +import subprocess +import sys +import os +import shutil + + +root_folder = os.path.realpath(os.path.dirname(__file__)) +args = sys.argv +subprocess.run(["python",f"{root_folder}\\GH Toolkit.py", *sys.argv[1:]]) \ No newline at end of file diff --git a/gui/gui_initialize.py b/gui/gui_initialize.py index 8e2a352..0448363 100644 --- a/gui/gui_initialize.py +++ b/gui/gui_initialize.py @@ -35,6 +35,13 @@ from toolkit_functions import convert_to_5 +def wrap_string(to_wrap, wide=False): + new_string = f'"{to_wrap}"' + if wide: + new_string = "w" + new_string + return new_string + + class main_window(QWidget): def __init__(self): super().__init__() @@ -52,14 +59,7 @@ def __init__(self, ghproj=""): self.gh3_audio_fields() self.gh3_song_data_fields() self.compile_fields() - - self.ghwt_checksum = "" - self.ghwt_genre = "" - self.ghwor_checksum = "" - self.ghwor_genre = "" - - self.checksum_input.textEdited.connect(self.set_checksum_variable) - self.genre_select.activated.connect(self.set_game_genre) + self.changing_fields() self.first_boot() @@ -95,7 +95,7 @@ def compile_pak(self): return game = self.game_select_group.checkedButton().objectName() if game == "gh3": - self.compile_gh3() + self.compile_gh3("skip_audio") elif game == "ghwt": self.compile_ghwt("skip_audio") elif game == "gh5": @@ -185,6 +185,8 @@ def save_project(self): "ghwor_checksum": self.ghwor_checksum, "ghwt_genre": self.ghwt_genre, "ghwor_genre": self.ghwor_genre, + "ghwt_drumkit": self.ghwt_drumkit, + "ghwor_drumkit": self.ghwor_drumkit, # WT Data "kick_input": self.kick_input.text(), @@ -199,6 +201,9 @@ def save_project(self): "backing_input": self.backing_input.text(), "crowd_input": self.crowd_input.text(), + "ghwt_preview_audio_input": self.ghwt_preview_audio_input.text(), + "ghwt_rendered_preview_check": self.ghwt_rendered_preview_check.isChecked(), + "encrypt_audio": self.encrypt_audio.isChecked(), "preview_minutes": self.preview_minutes.value(), @@ -219,7 +224,9 @@ def save_project(self): "ghwt_drumkit_select": self.ghwt_drumkit_select.currentText(), "ghwt_vocal_gender_select": self.ghwt_vocal_gender_select.currentText(), "vocal_scroll_speed_input": self.vocal_scroll_speed_input.value(), + "ghwt_vocal_cents": self.ghwt_vocal_cents.value(), "ghwt_band_vol": self.ghwt_band_vol.value(), + "ghwt_whammy_cutoff": self.ghwt_whammy_cutoff.value(), "band_tier_value": self.band_tier_value.value(), "drums_tier_value": self.drums_tier_value.value(), @@ -253,6 +260,9 @@ def save_project(self): "coop_backing_input_gh3": self.coop_backing_input_gh3.text(), "crowd_input_gh3": self.crowd_input_gh3.text(), + + "gh3_preview_audio_input": self.gh3_preview_audio_input.text(), + "gh3_rendered_preview_check": self.gh3_rendered_preview_check.isChecked(), "preview_minutes_gh3": self.preview_minutes_gh3.value(), "preview_seconds_gh3": self.preview_seconds_gh3.value(), @@ -264,6 +274,8 @@ def save_project(self): "gh3_midi_file_input": self.gh3_midi_file_input.text(), "gh3_perf_override_input": self.gh3_perf_override_input.text(), + "gh3_ska_files_input": self.gh3_ska_files_input.text(), + "gh3_song_script_input": self.gh3_song_script_input.text(), "gh3_countoff_select": self.gh3_countoff_select.currentText(), "gh3_vocal_gender_select": self.gh3_vocal_gender_select.currentText(), "gh3_bassist_select": self.gh3_bassist_select.currentText(), @@ -310,7 +322,7 @@ def load_project_file(self, ghproj=""): i.setChecked(True) break self.set_game_fields() - platform_index = self.platform_button_group.findChildren(QRadioButton) + platform_index = self.platform_button_group.buttons() for i in platform_index: if i.objectName() == load_vars["platform"]: i.setChecked(True) @@ -318,7 +330,8 @@ def load_project_file(self, ghproj=""): check_boxes = ["cover_checkbox", "p2_rhythm_check", "coop_audio_check", "beatlines_check", "encrypt_audio", "ghwt_set_end", - "guitar_mic_check", "bass_mic_check", "use_new_clips_check", "force_ffmpeg_check"] + "guitar_mic_check", "bass_mic_check", "use_new_clips_check", "force_ffmpeg_check", + "gh3_rendered_preview_check", "ghwt_rendered_preview_check"] for x in check_boxes: try: @@ -362,12 +375,20 @@ def set_checksum_variable(self): elif game == "ghwor": self.ghwor_checksum = self.checksum_input.text() - def set_game_genre(self): + def set_genre(self): game = self.game_select_group.checkedButton().objectName() if game == "ghwt": self.ghwt_genre = self.genre_select.currentText() elif game == "ghwor": self.ghwor_genre = self.genre_select.currentText() + + def set_drumkit(self): + game = self.game_select_group.checkedButton().objectName() + if game == "ghwt": + self.ghwt_drumkit = self.ghwt_drumkit_select.currentText() + elif game == "ghwor": + self.ghwor_drumkit = self.ghwt_drumkit_select.currentText() + def set_game_fields(self): self.setUpdatesEnabled(False) game = self.game_select_group.checkedButton().objectName() @@ -400,6 +421,8 @@ def set_game_fields(self): self.deactivate_layout(self.setlist_settings_layout) self.deactivate_layout(self.skeleton_types_layout) + self.platform_360.setEnabled(False) + self.platform_pc.setEnabled(False) if any([game == "gh3", game == "gha"]): self.genre_select.setDisabled(True) elif game == "ghwt": @@ -407,6 +430,7 @@ def set_game_fields(self): self.ghwt_drumkit_select.addItems(sorted(drum_kit_wt)) self.checksum_input.setText(self.ghwt_checksum) self.genre_select.setCurrentText(self.ghwt_genre) + self.ghwt_drumkit_select.setCurrentText(self.ghwt_drumkit) self.other_settings_label.setText("World Tour Definitive Edition Settings") self.activate_layout(self.setlist_settings_layout) self.activate_layout(self.skeleton_types_layout) @@ -418,6 +442,7 @@ def set_game_fields(self): self.ghwt_drumkit_select.addItems(sorted(drum_kit_gh5)) self.checksum_input.setText(self.ghwor_checksum) self.genre_select.setCurrentText(self.ghwor_genre) + self.ghwt_drumkit_select.setCurrentText(self.ghwor_drumkit) self.ghwor_stfs_input.setEnabled(True) self.ghwor_stfs_select.setEnabled(True) self.encrypt_audio.setDisabled(True) @@ -434,6 +459,8 @@ def set_game_fields(self): self.crowd_input_gh3.setDisabled(True) self.crowd_select_gh3.setDisabled(True) self.cover_checkbox.setDisabled(True) + self.platform_360.setEnabled(True) + self.platform_pc.setEnabled(True) else: self.crowd_input_gh3.setDisabled(False) self.crowd_select_gh3.setDisabled(False) @@ -462,6 +489,9 @@ def wt_audio_fields(self): self.backing_select.clicked.connect(partial(self.open_file_dialog_audio, self.backing_input)) self.crowd_select.clicked.connect(partial(self.open_file_dialog_audio, self.crowd_input)) + self.ghwt_preview_audio_select.clicked.connect( + partial(self.open_file_dialog_audio, self.ghwt_preview_audio_input)) + self.ghwt_set_end.toggled.connect(self.set_end_time) def wt_song_data_fields(self): @@ -484,11 +514,15 @@ def gh3_audio_fields(self): self.coop_rhythm_select_gh3.clicked.connect(partial(self.open_file_dialog_audio, self.coop_rhythm_input_gh3)) self.coop_backing_select_gh3.clicked.connect(partial(self.open_file_dialog_audio, self.coop_backing_input_gh3)) + self.gh3_preview_audio_select.clicked.connect(partial(self.open_file_dialog_audio, self.gh3_preview_audio_input)) + self.p2_rhythm_check.toggled.connect(self.p2_rhythm_toggle) def gh3_song_data_fields(self): self.gh3_midi_file_select.clicked.connect(partial(self.open_file_dialog_midi, self.gh3_midi_file_input)) self.gh3_perf_override_select.clicked.connect(partial(self.open_file_dialog_qb, self.gh3_perf_override_input)) + self.gh3_ska_files_select.clicked.connect(partial(self.open_file_dialog_folder, self.gh3_ska_files_input)) + self.gh3_song_script_select.clicked.connect(partial(self.open_file_dialog_qb, self.gh3_song_script_input)) bassists = ["Default"] + ["Axel", "Casey", "Izzy", "Judy", "Johnny", "Lars", "Midori", "Xavier", "Slash", "Tom Morello", "Lou", "God of Rock", "Grim Ripper"] self.gh3_bassist_select.addItems(bassists) @@ -505,6 +539,18 @@ def compile_fields(self): self.compile_button.clicked.connect(self.compile_song_package) self.compile_pak_button.clicked.connect(self.compile_pak) + def changing_fields(self): + self.ghwt_checksum = "" + self.ghwt_genre = "" + self.ghwor_checksum = "" + self.ghwor_genre = "" + self.ghwt_drumkit = "" + self.ghwor_drumkit = "" + + self.checksum_input.textEdited.connect(self.set_checksum_variable) + self.genre_select.activated.connect(self.set_genre) + self.ghwt_drumkit_select.activated.connect(self.set_drumkit) + def update_ns_value(self): self.ns_hopo_value.setText(str(round(self.ns_value(), 5))) @@ -538,14 +584,14 @@ def combo_copy(self, source, destination): item_text = source.itemText(i) destination.addItem(item_text) - def midi_check(self): - if not self.ghwt_midi_file_input.text(): + def midi_check(self, midi_path): + if not midi_path: print("No MIDI file selected! Cancelling compilation") return 0 - elif not os.path.isfile(self.ghwt_midi_file_input.text()): + elif not os.path.isfile(midi_path): print("MIDI file not found! Cancelling compilation") return 0 - return self.ghwt_midi_file_input.text() + return midi_path def wor_checksum(self): text, ok = QInputDialog.getText(None, "Proper DLC ID Required", @@ -585,7 +631,7 @@ def ghwt_ini_file(self): ini.optionxform = str ini["ModInfo"] = { "Name": self.checksum_input.text(), - "Description": "Created with Addy's Toolkit", + "Description": "Created with the Addy GH Toolkit", "Author": self.author_input.text(), "Version": "1" } @@ -624,6 +670,15 @@ def ghwt_ini_file(self): ini["SongInfo"]["MicForBassist"] = "1" if self.guitar_mic_check.isChecked(): ini["SongInfo"]["MicForGuitarist"] = "1" + if self.beatlines_check.isChecked(): + ini["SongInfo"]["Low8Bars"] = str(self.beat_8th_low_input.value()) + ini["SongInfo"]["High8Bars"] = str(self.beat_8th_high_input.value()) + ini["SongInfo"]["Low16Bars"] = str(self.beat_16th_low_input.value()) + ini["SongInfo"]["High16Bars"] = str(self.beat_16th_high_input.value()) + if self.ghwt_vocal_cents.value() != 0: + ini["SongInfo"]["Cents"] = str(self.ghwt_vocal_cents.value()) + if self.ghwt_whammy_cutoff.value() != 0.5: + ini["SongInfo"]["WhammyCutoff"] = str(self.ghwt_whammy_cutoff.value()) return ini def ghwor_songlist_info(self, *args): @@ -636,6 +691,8 @@ def ghwor_songlist_info(self, *args): artist_text = "$artist_text_as_made_famous_by" orig_artist = 0 + cents = self.ghwt_vocal_cents.value() + if self.beatlines_check.isChecked(): low_8 = self.beat_8th_low_input.text() high_8 = self.beat_8th_high_input.text() @@ -696,13 +753,78 @@ def ghwor_songlist_info(self, *args): "cymbal": f'"{drumkit}"', "drum_kit": f'"{drumkit}"', "countoff": f'"{self.ghwt_countoff_select.currentText().lower()}"', - "overall_song_volume": self.ghwt_band_vol.value() + "overall_song_volume": self.ghwt_band_vol.value(), + "vocals_pitch_score_shift": "{ " + f"cents = {cents} " + "}" } if not parts_with_mic: songlist_info.pop("parts_with_mic") + if not cents: + songlist_info.pop("vocals_pitch_score_shift") return songlist_info, qs_keys + def gh3_songlist_info(self): + artist_text = self.artist_text_select.currentText() + orig_artist = 1 + if artist_text == "By": + artist_text = "$artist_text_by" + elif artist_text == "Other": + artist_text = wrap_string(self.artist_text_other.text(), True) + else: + artist_text = "$artist_text_as_made_famous_by" + orig_artist = 0 + if self.p2_rhythm_check.isChecked(): + rhythm_track = 1 + else: + rhythm_track = 0 + + songlist_info = { + "checksum": self.checksum_input.text(), + "name": wrap_string(self.checksum_input.text()), + "title": wrap_string(self.title_input.text(), True), + "artist": wrap_string(self.artist_input.text(), True), + "year": wrap_string(f", {self.year_input.text()}", True), + "artist_text": artist_text, + "original_artist": orig_artist, + "version": "gh3", + "leaderboard": 1, + "gem_offset": 0, + "input_offset": 0, + "singer": self.gh3_vocal_gender_select.currentText(), + "keyboard": "False", + "band_playback_volume": self.gh3_gtr_vol.value(), + "guitar_playback_volume": self.gh3_band_vol.value(), + "countoff": wrap_string(self.gh3_countoff_select.currentText()), + "rhythm_track": rhythm_track + } + if self.coop_audio_check.isChecked(): + songlist_info["no_id"] = "use_coop_notetracks" + return songlist_info + + def gh3_audio_gen(self, song_name, start_time, end_time, compile_args): + all_audio = { + "guitar": self.guitar_input_gh3.text(), + "rhythm": self.rhythm_input_gh3.text(), + "song": self.backing_input_gh3.text() + } + if self.coop_audio_check.isChecked(): + all_audio["coop_guitar"] = self.coop_guitar_input_gh3.text() + all_audio["coop_rhythm"] = self.coop_rhythm_input_gh3.text() + all_audio["coop_song"] = self.coop_backing_input_gh3.text() + if self.game_select_group.checkedButton().objectName() == "gha": + all_audio["crowd"] = self.crowd_input_gh3.text() + + for key, value in all_audio.items(): + if not os.path.isfile(value): + print(f"File for {key} does not exist. Using blank audio") + all_audio[key] = f"{audio_folder}/default_audio/blank.mp3" + elif value.endswith("default_audio/blank.wav"): + value = f"{audio_folder}/default_audio/blank.mp3" + all_audio[key] = value + fsb_file, fsb_dat = af.compile_gh3_audio(all_audio, song_name, start_time, end_time, *compile_args) + + return fsb_file, fsb_dat + def ghwt_audio_gen(self, song_name, start_time, end_time, compile_args): all_audio = [self.kick_input, self.snare_input, self.cymbals_input, self.toms_input, self.guitar_input, self.bass_input, self.vocals_input, self.backing_input, self.crowd_input] @@ -716,7 +838,7 @@ def ghwt_audio_gen(self, song_name, start_time, end_time, compile_args): files.setText(f"{audio_folder}/default_audio/blank.mp3") all_audio_path.append(files.text()) try: - drum, inst, other, preview = af.get_padded_audio(all_audio_path, song_name, start_time, end_time, + drum, inst, other, preview = af.compile_wt_audio(all_audio_path, song_name, start_time, end_time, *compile_args) except Exception as E: traceback.print_exc() @@ -737,10 +859,38 @@ def get_audio_duration(self): duration = int(af.get_padded_audio(all_audio_path, 0, 0, 0, *compile_args)) return duration + def gen_gh3_songlist(self, *args): + songlist_info = self.gh3_songlist_info() + qb_text = "" + checksum = songlist_info['checksum'] + qb_text += f"{checksum} = " + "{\n" + for k, v in songlist_info.items(): + qb_text += f"{k} = {v}\n" + qb_text += "}" + platform = self.platform_button_group.checkedButton().objectName() + if platform == "platform_360": + dl_qb = """qb_file = 0xbf0730f0 + GH3_Download_Songs = { + prefix = "download" + num_tiers = 1 + tier1 = { + Title = w"Downloaded songs" + songs = [%s] + no_id = unlockall + level = load_z_artdeco + } + } + download_songlist = [%s] + download_songlist_props = { + """.replace("%s", checksum) + qb_text = dl_qb + qb_text + " }" + qb_text = mid_gen.t2q_main(qb_text, game = "GH3") + return qb_text + def gen_wor_songlist(self, *args): songlist_info, qs_keys = self.ghwor_songlist_info(*args) - qb_text_name = f"{hex(int(QBKey(self.checksum_input.text()),16)+2)}" + qb_text_name = f"{hex(int(QBKey(self.checksum_input.text()), 16) + 2)}" '''.download_songlist.qb''' gh6_dlc_songlist = f"gh6_dlc_songlist = [{songlist_info['checksum']}]\n" @@ -799,7 +949,18 @@ def gen_wor_manifest(self): return cmanifest_qb, qb_file_name, cmanifest def set_compile_args(self, *args): - midi_file = self.midi_check() + if "gh3" in args: + midi_file = self.midi_check(self.gh3_midi_file_input.text()) + perf = self.gh3_perf_override_input.text() + ska_files = self.gh3_ska_files_input.text() + song_script = self.gh3_song_script_input.text() + rendered_audio = self.gh3_rendered_preview_check.isChecked() + else: + midi_file = self.midi_check(self.ghwt_midi_file_input.text()) + perf = self.ghwt_perf_override_input.text() + ska_files = self.ghwt_ska_files_input.text() + song_script = self.ghwt_song_script_input.text() + rendered_audio = self.ghwt_rendered_preview_check.isChecked() if not midi_file: return 0 @@ -808,14 +969,21 @@ def set_compile_args(self, *args): if self.encrypt_audio.isChecked(): compile_args += ["encrypt"] - perf = self.ghwt_perf_override_input.text() + if perf and os.path.isfile(perf): compile_args += ["replace_perf", perf] - ska_files = self.ghwt_ska_files_input.text() if ska_files and os.path.isdir(ska_files): compile_args += ["add_ska", ska_files] + if rendered_audio: + if "gh3" in args and os.path.exists(self.gh3_preview_audio_input.text()): + compile_args += ["rendered_preview", self.gh3_preview_audio_input.text()] + elif "ghwt" in args and os.path.exists(self.ghwt_preview_audio_input.text()): + compile_args += ["rendered_preview", self.ghwt_preview_audio_input.text()] + else: + print("Rendered preview audio selected, but file is not found. Creating preview based on time.") + hopo_mode = self.hopo_mode_select.currentText() if hopo_mode == "Guitar Hero 3": compile_args += ["gh3_mode"] @@ -824,7 +992,6 @@ def set_compile_args(self, *args): elif hopo_mode == "Guitar Hero World Tour+": compile_args += ["force_only"] - song_script = self.ghwt_song_script_input.text() if song_script and os.path.isfile(song_script): compile_args += ["song_script", song_script] @@ -832,7 +999,37 @@ def set_compile_args(self, *args): compile_args += ["ffmpeg"] return compile_args - def compile_gh3(self): + def compile_gh3(self, *args): + start_time = self.conv_to_secs("start") + end_time = self.conv_to_secs("end") + if not self.ghwt_set_end.isChecked(): + end_time += start_time + + song_name = self.checksum_input.text() + compile_args = self.set_compile_args(*["gh3"]) + project_folder = os.path.dirname(self.project_file_path.text()) + save_folder = f"{project_folder}\\gh3_compile" + try: + os.mkdir(save_folder) + except: + pass + if not compile_args: + return + + songlist = self.gen_gh3_songlist() + try: + song_pak = mid_gen.make_mid(*compile_args)[0] + except Exception as E: + # raise E + traceback.print_exc() + return + if not "skip_audio" in args: + audio, dat = self.gh3_audio_gen(song_name, start_time, end_time, compile_args) + audio_path = f"{save_folder}\\{song_name}" + af.writeFSB3(audio, dat, audio_path) + with open(f"{save_folder}\\{self.checksum_input.text()}_song.pak.xen", "wb") as f: + f.write(song_pak) + print("Compile complete!") return def compile_ghwt(self, *args): @@ -860,15 +1057,16 @@ def compile_ghwt(self, *args): with open(f"{song_folder}/song.ini", "w") as f: ini.write(f, space_around_delimiters=False) - compile_args = self.set_compile_args(*["ghwt", "wtde"]) + compile_args = self.set_compile_args(*["ghwt", "wtde", "gh5_mode"]) if not compile_args: return try: - song_pak = mid_gen.make_mid(*compile_args)[0] + song_pak, xplus_pak = mid_gen.make_mid(*compile_args) except Exception as E: traceback.print_exc() + # raise E return with open(f"{song_folder}\\Content\\a{song_name}_song.pak.xen", "wb") as f: f.write(song_pak) @@ -908,7 +1106,8 @@ def compile_ghwor(self, *args): traceback.print_exc() return - wor_pak_files = convert_to_5(song_pak, self.checksum_input.text(), *compile_args, song_name = self.checksum_input.text(), decomp_ska = True) + wor_pak_files = convert_to_5(song_pak, self.checksum_input.text(), *compile_args, + song_name=self.checksum_input.text(), decomp_ska=True) songlist_args = [] if "double_kick" in wor_pak_files: songlist_args += ["double_kick"] @@ -917,21 +1116,23 @@ def compile_ghwor(self, *args): songlist_qb, qs_bytes, empty_qs = self.gen_wor_songlist(*songlist_args) manifest_pak = mid_gen.pakMaker([[cmanifest_qb, f"{man_file_name}.0xb2a7df81.qb"]]) - cdl_pak = mid_gen.pakMaker([[b'\x00\x00\x00\x00\x00\x00\x00\x1C\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - "0xb1392214.0x179eac5.qb"]]) + cdl_pak = mid_gen.pakMaker([[ + b'\x00\x00\x00\x00\x00\x00\x00\x1C\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + "0xb1392214.0x179eac5.qb"]]) cdl_text_files = [] langs = ["de", "en", "fr", "it", "es"] for enum, blanks in enumerate(["0x5a7a551", "0x8b6e4d98", "0xb4424214", "0xdab9fbee", "0xe8682141"]): cdl_text_files.append([empty_qs, f"{blanks}.0x179eac5.qs.{langs[enum]}"]) - dl_qb = hex(int(QBKey(self.checksum_input.text()),16)) + dl_qb = hex(int(QBKey(self.checksum_input.text()), 16)) for lang in langs: cdl_text_files.append([qs_bytes, f"{dl_qb}.download_songlist.qs.{lang}"]) - cdl_text_files.append([songlist_qb, f"{hex(int(QBKey(self.checksum_input.text()), 16)+2)}.download_songlist.qb"]) + cdl_text_files.append( + [songlist_qb, f"{hex(int(QBKey(self.checksum_input.text()), 16) + 2)}.download_songlist.qb"]) cdl_text_pak = mid_gen.pakMaker(cdl_text_files) - - wor_pak = mid_gen.pakMaker([[x["file_data"], x["file_name"]] for x in wor_pak_files], self.checksum_input.text()) + wor_pak = mid_gen.pakMaker([[x["file_data"], x["file_name"]] for x in wor_pak_files], + self.checksum_input.text()) project_folder = os.path.dirname(self.project_file_path.text()) save_folder = f"{project_folder}\\{self.checksum_input.text()}_WoR_Files" @@ -943,7 +1144,8 @@ def compile_ghwor(self, *args): end_time = self.conv_to_secs("end") if not self.ghwt_set_end.isChecked(): end_time += start_time - drum, inst, other, preview = self.ghwt_audio_gen(self.checksum_input.text(), start_time, end_time, compile_args) + drum, inst, other, preview = self.ghwt_audio_gen(self.checksum_input.text(), start_time, end_time, + compile_args) if not drum: return for enum, x in enumerate([drum, inst, other, preview]): diff --git a/gui/project_files/compile_package.py b/gui/project_files/compile_package.py index 7345573..f21ecd0 100644 --- a/gui/project_files/compile_package.py +++ b/gui/project_files/compile_package.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'compile_package.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.5.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -25,7 +25,7 @@ class Ui_Form(object): def setupUi(self, Form): if not Form.objectName(): Form.setObjectName(u"Form") - Form.resize(562, 659) + Form.resize(562, 712) self.verticalLayout_11 = QVBoxLayout(Form) self.verticalLayout_11.setObjectName(u"verticalLayout_11") self.verticalLayout_4 = QVBoxLayout() @@ -70,7 +70,7 @@ def setupUi(self, Form): self.game_select_group.setObjectName(u"game_select_group") self.game_select_group.addButton(self.gh3) self.gh3.setObjectName(u"gh3") - self.gh3.setEnabled(False) + self.gh3.setEnabled(True) self.gh3.setText(u"GH3") self.gh3.setChecked(False) @@ -79,7 +79,7 @@ def setupUi(self, Form): self.gha = QRadioButton(self.game_select) self.game_select_group.addButton(self.gha) self.gha.setObjectName(u"gha") - self.gha.setEnabled(False) + self.gha.setEnabled(True) self.horizontalLayout.addWidget(self.gha) @@ -269,11 +269,23 @@ def setupUi(self, Form): self.ghwt_stems_layout = QGridLayout() self.ghwt_stems_layout.setObjectName(u"ghwt_stems_layout") self.ghwt_stems_layout.setContentsMargins(0, 9, -1, 9) - self.toms_input = QLineEdit(self.wt_audio_widget) - self.toms_input.setObjectName(u"toms_input") - self.toms_input.setEnabled(True) + self.crowd_select = QToolButton(self.wt_audio_widget) + self.crowd_select.setObjectName(u"crowd_select") + self.crowd_select.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.toms_input, 4, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.crowd_select, 14, 3, 1, 1) + + self.vocals_input = QLineEdit(self.wt_audio_widget) + self.vocals_input.setObjectName(u"vocals_input") + self.vocals_input.setEnabled(True) + + self.ghwt_stems_layout.addWidget(self.vocals_input, 10, 2, 1, 1) + + self.toms_label = QLabel(self.wt_audio_widget) + self.toms_label.setObjectName(u"toms_label") + self.toms_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.ghwt_stems_layout.addWidget(self.toms_label, 4, 1, 1, 1) self.backing_select = QToolButton(self.wt_audio_widget) self.backing_select.setObjectName(u"backing_select") @@ -281,11 +293,11 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.backing_select, 12, 3, 1, 1) - self.cymbals_select = QToolButton(self.wt_audio_widget) - self.cymbals_select.setObjectName(u"cymbals_select") - self.cymbals_select.setEnabled(True) + self.backing_label = QLabel(self.wt_audio_widget) + self.backing_label.setObjectName(u"backing_label") + self.backing_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.ghwt_stems_layout.addWidget(self.cymbals_select, 3, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.backing_label, 12, 1, 1, 1) self.line_4 = QFrame(self.wt_audio_widget) self.line_4.setObjectName(u"line_4") @@ -294,11 +306,11 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.line_4, 7, 0, 1, 4) - self.bass_label = QLabel(self.wt_audio_widget) - self.bass_label.setObjectName(u"bass_label") - self.bass_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.cymbals_input = QLineEdit(self.wt_audio_widget) + self.cymbals_input.setObjectName(u"cymbals_input") + self.cymbals_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.bass_label, 8, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.cymbals_input, 3, 2, 1, 1) self.snare_select = QToolButton(self.wt_audio_widget) self.snare_select.setObjectName(u"snare_select") @@ -306,36 +318,56 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.snare_select, 2, 3, 1, 1) - self.kick_label = QLabel(self.wt_audio_widget) - self.kick_label.setObjectName(u"kick_label") - self.kick_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.line_3 = QFrame(self.wt_audio_widget) + self.line_3.setObjectName(u"line_3") + self.line_3.setFrameShape(QFrame.HLine) + self.line_3.setFrameShadow(QFrame.Sunken) - self.ghwt_stems_layout.addWidget(self.kick_label, 1, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.line_3, 5, 0, 1, 4) - self.bass_input = QLineEdit(self.wt_audio_widget) - self.bass_input.setObjectName(u"bass_input") - self.bass_input.setEnabled(True) + self.line_6 = QFrame(self.wt_audio_widget) + self.line_6.setObjectName(u"line_6") + self.line_6.setFrameShape(QFrame.HLine) + self.line_6.setFrameShadow(QFrame.Sunken) - self.ghwt_stems_layout.addWidget(self.bass_input, 8, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.line_6, 11, 0, 1, 4) - self.crowd_label = QLabel(self.wt_audio_widget) - self.crowd_label.setObjectName(u"crowd_label") - self.crowd_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.kick_input = QLineEdit(self.wt_audio_widget) + self.kick_input.setObjectName(u"kick_input") + self.kick_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.crowd_label, 14, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.kick_input, 1, 2, 1, 1) - self.snare_label = QLabel(self.wt_audio_widget) - self.snare_label.setObjectName(u"snare_label") - self.snare_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.guitar_select = QToolButton(self.wt_audio_widget) + self.guitar_select.setObjectName(u"guitar_select") + self.guitar_select.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.snare_label, 2, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.guitar_select, 6, 3, 1, 1) - self.line_8 = QFrame(self.wt_audio_widget) - self.line_8.setObjectName(u"line_8") - self.line_8.setFrameShape(QFrame.HLine) - self.line_8.setFrameShadow(QFrame.Sunken) + self.vocals_select = QToolButton(self.wt_audio_widget) + self.vocals_select.setObjectName(u"vocals_select") + self.vocals_select.setEnabled(True) + + self.ghwt_stems_layout.addWidget(self.vocals_select, 10, 3, 1, 1) + + self.snare_input = QLineEdit(self.wt_audio_widget) + self.snare_input.setObjectName(u"snare_input") + self.snare_input.setEnabled(True) + + self.ghwt_stems_layout.addWidget(self.snare_input, 2, 2, 1, 1) + + self.line_5 = QFrame(self.wt_audio_widget) + self.line_5.setObjectName(u"line_5") + self.line_5.setFrameShape(QFrame.HLine) + self.line_5.setFrameShadow(QFrame.Sunken) - self.ghwt_stems_layout.addWidget(self.line_8, 15, 0, 1, 4) + self.ghwt_stems_layout.addWidget(self.line_5, 9, 0, 1, 4) + + self.bass_label = QLabel(self.wt_audio_widget) + self.bass_label.setObjectName(u"bass_label") + self.bass_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.ghwt_stems_layout.addWidget(self.bass_label, 8, 1, 1, 1) self.bass_select = QToolButton(self.wt_audio_widget) self.bass_select.setObjectName(u"bass_select") @@ -343,30 +375,24 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.bass_select, 8, 3, 1, 1) - self.line_3 = QFrame(self.wt_audio_widget) - self.line_3.setObjectName(u"line_3") - self.line_3.setFrameShape(QFrame.HLine) - self.line_3.setFrameShadow(QFrame.Sunken) - - self.ghwt_stems_layout.addWidget(self.line_3, 5, 0, 1, 4) - - self.vocals_input = QLineEdit(self.wt_audio_widget) - self.vocals_input.setObjectName(u"vocals_input") - self.vocals_input.setEnabled(True) + self.crowd_input = QLineEdit(self.wt_audio_widget) + self.crowd_input.setObjectName(u"crowd_input") + self.crowd_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.vocals_input, 10, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.crowd_input, 14, 2, 1, 1) - self.guitar_select = QToolButton(self.wt_audio_widget) - self.guitar_select.setObjectName(u"guitar_select") - self.guitar_select.setEnabled(True) + self.backing_input = QLineEdit(self.wt_audio_widget) + self.backing_input.setObjectName(u"backing_input") + self.backing_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.guitar_select, 6, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.backing_input, 12, 2, 1, 1) - self.vocals_label = QLabel(self.wt_audio_widget) - self.vocals_label.setObjectName(u"vocals_label") - self.vocals_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.line_8 = QFrame(self.wt_audio_widget) + self.line_8.setObjectName(u"line_8") + self.line_8.setFrameShape(QFrame.HLine) + self.line_8.setFrameShadow(QFrame.Sunken) - self.ghwt_stems_layout.addWidget(self.vocals_label, 10, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.line_8, 17, 0, 1, 4) self.line_7 = QFrame(self.wt_audio_widget) self.line_7.setObjectName(u"line_7") @@ -375,23 +401,11 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.line_7, 13, 0, 1, 4) - self.toms_label = QLabel(self.wt_audio_widget) - self.toms_label.setObjectName(u"toms_label") - self.toms_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - - self.ghwt_stems_layout.addWidget(self.toms_label, 4, 1, 1, 1) - - self.backing_input = QLineEdit(self.wt_audio_widget) - self.backing_input.setObjectName(u"backing_input") - self.backing_input.setEnabled(True) - - self.ghwt_stems_layout.addWidget(self.backing_input, 12, 2, 1, 1) - - self.toms_select = QToolButton(self.wt_audio_widget) - self.toms_select.setObjectName(u"toms_select") - self.toms_select.setEnabled(True) + self.cymbals_select = QToolButton(self.wt_audio_widget) + self.cymbals_select.setObjectName(u"cymbals_select") + self.cymbals_select.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.toms_select, 4, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.cymbals_select, 3, 3, 1, 1) self.guitar_label = QLabel(self.wt_audio_widget) self.guitar_label.setObjectName(u"guitar_label") @@ -399,18 +413,29 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.guitar_label, 6, 1, 1, 1) - self.crowd_select = QToolButton(self.wt_audio_widget) - self.crowd_select.setObjectName(u"crowd_select") - self.crowd_select.setEnabled(True) + self.toms_input = QLineEdit(self.wt_audio_widget) + self.toms_input.setObjectName(u"toms_input") + self.toms_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.crowd_select, 14, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.toms_input, 4, 2, 1, 1) - self.line_6 = QFrame(self.wt_audio_widget) - self.line_6.setObjectName(u"line_6") - self.line_6.setFrameShape(QFrame.HLine) - self.line_6.setFrameShadow(QFrame.Sunken) + self.vocals_label = QLabel(self.wt_audio_widget) + self.vocals_label.setObjectName(u"vocals_label") + self.vocals_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.ghwt_stems_layout.addWidget(self.line_6, 11, 0, 1, 4) + self.ghwt_stems_layout.addWidget(self.vocals_label, 10, 1, 1, 1) + + self.ghwt_preview_audio_input = QLineEdit(self.wt_audio_widget) + self.ghwt_preview_audio_input.setObjectName(u"ghwt_preview_audio_input") + self.ghwt_preview_audio_input.setEnabled(False) + + self.ghwt_stems_layout.addWidget(self.ghwt_preview_audio_input, 16, 2, 1, 1) + + self.snare_label = QLabel(self.wt_audio_widget) + self.snare_label.setObjectName(u"snare_label") + self.snare_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.ghwt_stems_layout.addWidget(self.snare_label, 2, 1, 1, 1) self.cymbals_label = QLabel(self.wt_audio_widget) self.cymbals_label.setObjectName(u"cymbals_label") @@ -426,49 +451,54 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.label, 0, 2, 1, 1) - self.track_label = QLabel(self.wt_audio_widget) - self.track_label.setObjectName(u"track_label") - sizePolicy2.setHeightForWidth(self.track_label.sizePolicy().hasHeightForWidth()) - self.track_label.setSizePolicy(sizePolicy2) - self.track_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.kick_select = QToolButton(self.wt_audio_widget) + self.kick_select.setObjectName(u"kick_select") + self.kick_select.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.track_label, 0, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.kick_select, 1, 3, 1, 1) - self.crowd_input = QLineEdit(self.wt_audio_widget) - self.crowd_input.setObjectName(u"crowd_input") - self.crowd_input.setEnabled(True) + self.ghwt_preview_label = QLabel(self.wt_audio_widget) + self.ghwt_preview_label.setObjectName(u"ghwt_preview_label") - self.ghwt_stems_layout.addWidget(self.crowd_input, 14, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.ghwt_preview_label, 16, 1, 1, 1, Qt.AlignRight) - self.vocals_select = QToolButton(self.wt_audio_widget) - self.vocals_select.setObjectName(u"vocals_select") - self.vocals_select.setEnabled(True) + self.kick_label = QLabel(self.wt_audio_widget) + self.kick_label.setObjectName(u"kick_label") + self.kick_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.ghwt_stems_layout.addWidget(self.vocals_select, 10, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.kick_label, 1, 1, 1, 1) - self.cymbals_input = QLineEdit(self.wt_audio_widget) - self.cymbals_input.setObjectName(u"cymbals_input") - self.cymbals_input.setEnabled(True) + self.toms_select = QToolButton(self.wt_audio_widget) + self.toms_select.setObjectName(u"toms_select") + self.toms_select.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.cymbals_input, 3, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.toms_select, 4, 3, 1, 1) - self.kick_select = QToolButton(self.wt_audio_widget) - self.kick_select.setObjectName(u"kick_select") - self.kick_select.setEnabled(True) + self.bass_input = QLineEdit(self.wt_audio_widget) + self.bass_input.setObjectName(u"bass_input") + self.bass_input.setEnabled(True) - self.ghwt_stems_layout.addWidget(self.kick_select, 1, 3, 1, 1) + self.ghwt_stems_layout.addWidget(self.bass_input, 8, 2, 1, 1) - self.snare_input = QLineEdit(self.wt_audio_widget) - self.snare_input.setObjectName(u"snare_input") - self.snare_input.setEnabled(True) + self.track_label = QLabel(self.wt_audio_widget) + self.track_label.setObjectName(u"track_label") + sizePolicy2.setHeightForWidth(self.track_label.sizePolicy().hasHeightForWidth()) + self.track_label.setSizePolicy(sizePolicy2) + self.track_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.ghwt_stems_layout.addWidget(self.snare_input, 2, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.track_label, 0, 1, 1, 1) - self.backing_label = QLabel(self.wt_audio_widget) - self.backing_label.setObjectName(u"backing_label") - self.backing_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.ghwt_preview_audio_select = QToolButton(self.wt_audio_widget) + self.ghwt_preview_audio_select.setObjectName(u"ghwt_preview_audio_select") + self.ghwt_preview_audio_select.setEnabled(False) - self.ghwt_stems_layout.addWidget(self.backing_label, 12, 1, 1, 1) + self.ghwt_stems_layout.addWidget(self.ghwt_preview_audio_select, 16, 3, 1, 1) + + self.crowd_label = QLabel(self.wt_audio_widget) + self.crowd_label.setObjectName(u"crowd_label") + self.crowd_label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.ghwt_stems_layout.addWidget(self.crowd_label, 14, 1, 1, 1) self.guitar_input = QLineEdit(self.wt_audio_widget) self.guitar_input.setObjectName(u"guitar_input") @@ -476,32 +506,16 @@ def setupUi(self, Form): self.ghwt_stems_layout.addWidget(self.guitar_input, 6, 2, 1, 1) - self.line_5 = QFrame(self.wt_audio_widget) - self.line_5.setObjectName(u"line_5") - self.line_5.setFrameShape(QFrame.HLine) - self.line_5.setFrameShadow(QFrame.Sunken) - - self.ghwt_stems_layout.addWidget(self.line_5, 9, 0, 1, 4) - - self.kick_input = QLineEdit(self.wt_audio_widget) - self.kick_input.setObjectName(u"kick_input") - self.kick_input.setEnabled(True) + self.line_11 = QFrame(self.wt_audio_widget) + self.line_11.setObjectName(u"line_11") + self.line_11.setFrameShape(QFrame.HLine) + self.line_11.setFrameShadow(QFrame.Sunken) - self.ghwt_stems_layout.addWidget(self.kick_input, 1, 2, 1, 1) + self.ghwt_stems_layout.addWidget(self.line_11, 15, 0, 1, 4) self.verticalLayout_9.addLayout(self.ghwt_stems_layout) - self.horizontalLayout_13 = QHBoxLayout() - self.horizontalLayout_13.setObjectName(u"horizontalLayout_13") - self.encrypt_audio = QCheckBox(self.wt_audio_widget) - self.encrypt_audio.setObjectName(u"encrypt_audio") - - self.horizontalLayout_13.addWidget(self.encrypt_audio, 0, Qt.AlignRight) - - - self.verticalLayout_9.addLayout(self.horizontalLayout_13) - self.horizontalLayout_2 = QHBoxLayout() self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.preview_label = QLabel(self.wt_audio_widget) @@ -557,6 +571,21 @@ def setupUi(self, Form): self.verticalLayout_9.addLayout(self.horizontalLayout_2) + self.horizontalLayout_13 = QHBoxLayout() + self.horizontalLayout_13.setObjectName(u"horizontalLayout_13") + self.encrypt_audio = QCheckBox(self.wt_audio_widget) + self.encrypt_audio.setObjectName(u"encrypt_audio") + + self.horizontalLayout_13.addWidget(self.encrypt_audio, 0, Qt.AlignLeft) + + self.ghwt_rendered_preview_check = QCheckBox(self.wt_audio_widget) + self.ghwt_rendered_preview_check.setObjectName(u"ghwt_rendered_preview_check") + + self.horizontalLayout_13.addWidget(self.ghwt_rendered_preview_check) + + + self.verticalLayout_9.addLayout(self.horizontalLayout_13) + self.verticalLayout_3.addWidget(self.wt_audio_widget) @@ -576,19 +605,24 @@ def setupUi(self, Form): self.verticalLayout_10.setContentsMargins(0, 0, 0, 0) self.gh3_stems_layout = QGridLayout() self.gh3_stems_layout.setObjectName(u"gh3_stems_layout") - self.line_9 = QFrame(self.gh3_audio_widget) - self.line_9.setObjectName(u"line_9") - self.line_9.setFrameShape(QFrame.HLine) - self.line_9.setFrameShadow(QFrame.Sunken) - - self.gh3_stems_layout.addWidget(self.line_9, 11, 0, 1, 3) - self.guitar_label_gh3 = QLabel(self.gh3_audio_widget) self.guitar_label_gh3.setObjectName(u"guitar_label_gh3") self.guitar_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.gh3_stems_layout.addWidget(self.guitar_label_gh3, 1, 0, 1, 1) + self.backing_label_gh3 = QLabel(self.gh3_audio_widget) + self.backing_label_gh3.setObjectName(u"backing_label_gh3") + self.backing_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.gh3_stems_layout.addWidget(self.backing_label_gh3, 3, 0, 1, 1) + + self.crowd_label_gh3 = QLabel(self.gh3_audio_widget) + self.crowd_label_gh3.setObjectName(u"crowd_label_gh3") + self.crowd_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.gh3_stems_layout.addWidget(self.crowd_label_gh3, 12, 0, 1, 1) + self.track_label_2 = QLabel(self.gh3_audio_widget) self.track_label_2.setObjectName(u"track_label_2") sizePolicy2.setHeightForWidth(self.track_label_2.sizePolicy().hasHeightForWidth()) @@ -597,29 +631,45 @@ def setupUi(self, Form): self.gh3_stems_layout.addWidget(self.track_label_2, 0, 0, 1, 1) - self.guitar_select_gh3 = QToolButton(self.gh3_audio_widget) - self.guitar_select_gh3.setObjectName(u"guitar_select_gh3") - self.guitar_select_gh3.setEnabled(True) + self.gh3_audio_options = QHBoxLayout() + self.gh3_audio_options.setObjectName(u"gh3_audio_options") + self.p2_rhythm_check = QCheckBox(self.gh3_audio_widget) + self.p2_rhythm_check.setObjectName(u"p2_rhythm_check") - self.gh3_stems_layout.addWidget(self.guitar_select_gh3, 1, 2, 1, 1) + self.gh3_audio_options.addWidget(self.p2_rhythm_check) - self.coop_guitar_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.coop_guitar_input_gh3.setObjectName(u"coop_guitar_input_gh3") - self.coop_guitar_input_gh3.setEnabled(False) + self.coop_audio_check = QCheckBox(self.gh3_audio_widget) + self.coop_audio_check.setObjectName(u"coop_audio_check") + self.coop_audio_check.setEnabled(False) - self.gh3_stems_layout.addWidget(self.coop_guitar_input_gh3, 7, 1, 1, 1) + self.gh3_audio_options.addWidget(self.coop_audio_check) - self.rhythm_label_gh3 = QLabel(self.gh3_audio_widget) - self.rhythm_label_gh3.setObjectName(u"rhythm_label_gh3") - self.rhythm_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.gh3_stems_layout.addWidget(self.rhythm_label_gh3, 2, 0, 1, 1) + self.gh3_stems_layout.addLayout(self.gh3_audio_options, 5, 1, 1, 1) - self.crowd_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.crowd_input_gh3.setObjectName(u"crowd_input_gh3") - self.crowd_input_gh3.setEnabled(True) + self.coop_backing_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.coop_backing_input_gh3.setObjectName(u"coop_backing_input_gh3") + self.coop_backing_input_gh3.setEnabled(False) - self.gh3_stems_layout.addWidget(self.crowd_input_gh3, 12, 1, 1, 1) + self.gh3_stems_layout.addWidget(self.coop_backing_input_gh3, 10, 1, 1, 1) + + self.coop_rhythm_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.coop_rhythm_input_gh3.setObjectName(u"coop_rhythm_input_gh3") + self.coop_rhythm_input_gh3.setEnabled(False) + + self.gh3_stems_layout.addWidget(self.coop_rhythm_input_gh3, 9, 1, 1, 1) + + self.coop_guitar_select_gh3 = QToolButton(self.gh3_audio_widget) + self.coop_guitar_select_gh3.setObjectName(u"coop_guitar_select_gh3") + self.coop_guitar_select_gh3.setEnabled(False) + + self.gh3_stems_layout.addWidget(self.coop_guitar_select_gh3, 7, 2, 1, 1) + + self.coop_rhythm_label_gh3 = QLabel(self.gh3_audio_widget) + self.coop_rhythm_label_gh3.setObjectName(u"coop_rhythm_label_gh3") + self.coop_rhythm_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + + self.gh3_stems_layout.addWidget(self.coop_rhythm_label_gh3, 9, 0, 1, 1) self.backing_select_gh3 = QToolButton(self.gh3_audio_widget) self.backing_select_gh3.setObjectName(u"backing_select_gh3") @@ -627,12 +677,6 @@ def setupUi(self, Form): self.gh3_stems_layout.addWidget(self.backing_select_gh3, 3, 2, 1, 1) - self.coop_backing_select_gh3 = QToolButton(self.gh3_audio_widget) - self.coop_backing_select_gh3.setObjectName(u"coop_backing_select_gh3") - self.coop_backing_select_gh3.setEnabled(False) - - self.gh3_stems_layout.addWidget(self.coop_backing_select_gh3, 10, 2, 1, 1) - self.gh3_file_path_label = QLabel(self.gh3_audio_widget) self.gh3_file_path_label.setObjectName(u"gh3_file_path_label") sizePolicy2.setHeightForWidth(self.gh3_file_path_label.sizePolicy().hasHeightForWidth()) @@ -641,41 +685,29 @@ def setupUi(self, Form): self.gh3_stems_layout.addWidget(self.gh3_file_path_label, 0, 1, 1, 1) - self.guitar_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.guitar_input_gh3.setObjectName(u"guitar_input_gh3") - self.guitar_input_gh3.setEnabled(True) - - self.gh3_stems_layout.addWidget(self.guitar_input_gh3, 1, 1, 1, 1) - - self.backing_label_gh3 = QLabel(self.gh3_audio_widget) - self.backing_label_gh3.setObjectName(u"backing_label_gh3") - self.backing_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - - self.gh3_stems_layout.addWidget(self.backing_label_gh3, 3, 0, 1, 1) - self.crowd_select_gh3 = QToolButton(self.gh3_audio_widget) self.crowd_select_gh3.setObjectName(u"crowd_select_gh3") self.crowd_select_gh3.setEnabled(True) self.gh3_stems_layout.addWidget(self.crowd_select_gh3, 12, 2, 1, 1) - self.rhythm_select_gh3 = QToolButton(self.gh3_audio_widget) - self.rhythm_select_gh3.setObjectName(u"rhythm_select_gh3") - self.rhythm_select_gh3.setEnabled(True) + self.rhythm_label_gh3 = QLabel(self.gh3_audio_widget) + self.rhythm_label_gh3.setObjectName(u"rhythm_label_gh3") + self.rhythm_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.gh3_stems_layout.addWidget(self.rhythm_select_gh3, 2, 2, 1, 1) + self.gh3_stems_layout.addWidget(self.rhythm_label_gh3, 2, 0, 1, 1) - self.coop_backing_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.coop_backing_input_gh3.setObjectName(u"coop_backing_input_gh3") - self.coop_backing_input_gh3.setEnabled(False) + self.guitar_select_gh3 = QToolButton(self.gh3_audio_widget) + self.guitar_select_gh3.setObjectName(u"guitar_select_gh3") + self.guitar_select_gh3.setEnabled(True) - self.gh3_stems_layout.addWidget(self.coop_backing_input_gh3, 10, 1, 1, 1) + self.gh3_stems_layout.addWidget(self.guitar_select_gh3, 1, 2, 1, 1) - self.backing_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.backing_input_gh3.setObjectName(u"backing_input_gh3") - self.backing_input_gh3.setEnabled(True) + self.coop_backing_label_gh3 = QLabel(self.gh3_audio_widget) + self.coop_backing_label_gh3.setObjectName(u"coop_backing_label_gh3") + self.coop_backing_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.gh3_stems_layout.addWidget(self.backing_input_gh3, 3, 1, 1, 1) + self.gh3_stems_layout.addWidget(self.coop_backing_label_gh3, 10, 0, 1, 1) self.coop_guitar_label_gh3 = QLabel(self.gh3_audio_widget) self.coop_guitar_label_gh3.setObjectName(u"coop_guitar_label_gh3") @@ -689,23 +721,42 @@ def setupUi(self, Form): self.gh3_stems_layout.addWidget(self.rhythm_input_gh3, 2, 1, 1, 1) - self.coop_rhythm_label_gh3 = QLabel(self.gh3_audio_widget) - self.coop_rhythm_label_gh3.setObjectName(u"coop_rhythm_label_gh3") - self.coop_rhythm_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.coop_guitar_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.coop_guitar_input_gh3.setObjectName(u"coop_guitar_input_gh3") + self.coop_guitar_input_gh3.setEnabled(False) - self.gh3_stems_layout.addWidget(self.coop_rhythm_label_gh3, 9, 0, 1, 1) + self.gh3_stems_layout.addWidget(self.coop_guitar_input_gh3, 7, 1, 1, 1) - self.coop_rhythm_input_gh3 = QLineEdit(self.gh3_audio_widget) - self.coop_rhythm_input_gh3.setObjectName(u"coop_rhythm_input_gh3") - self.coop_rhythm_input_gh3.setEnabled(False) + self.rhythm_select_gh3 = QToolButton(self.gh3_audio_widget) + self.rhythm_select_gh3.setObjectName(u"rhythm_select_gh3") + self.rhythm_select_gh3.setEnabled(True) - self.gh3_stems_layout.addWidget(self.coop_rhythm_input_gh3, 9, 1, 1, 1) + self.gh3_stems_layout.addWidget(self.rhythm_select_gh3, 2, 2, 1, 1) - self.crowd_label_gh3 = QLabel(self.gh3_audio_widget) - self.crowd_label_gh3.setObjectName(u"crowd_label_gh3") - self.crowd_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.coop_backing_select_gh3 = QToolButton(self.gh3_audio_widget) + self.coop_backing_select_gh3.setObjectName(u"coop_backing_select_gh3") + self.coop_backing_select_gh3.setEnabled(False) - self.gh3_stems_layout.addWidget(self.crowd_label_gh3, 12, 0, 1, 1) + self.gh3_stems_layout.addWidget(self.coop_backing_select_gh3, 10, 2, 1, 1) + + self.line_9 = QFrame(self.gh3_audio_widget) + self.line_9.setObjectName(u"line_9") + self.line_9.setFrameShape(QFrame.HLine) + self.line_9.setFrameShadow(QFrame.Sunken) + + self.gh3_stems_layout.addWidget(self.line_9, 11, 0, 1, 3) + + self.backing_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.backing_input_gh3.setObjectName(u"backing_input_gh3") + self.backing_input_gh3.setEnabled(True) + + self.gh3_stems_layout.addWidget(self.backing_input_gh3, 3, 1, 1, 1) + + self.crowd_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.crowd_input_gh3.setObjectName(u"crowd_input_gh3") + self.crowd_input_gh3.setEnabled(True) + + self.gh3_stems_layout.addWidget(self.crowd_input_gh3, 12, 1, 1, 1) self.coop_rhythm_select_gh3 = QToolButton(self.gh3_audio_widget) self.coop_rhythm_select_gh3.setObjectName(u"coop_rhythm_select_gh3") @@ -713,37 +764,37 @@ def setupUi(self, Form): self.gh3_stems_layout.addWidget(self.coop_rhythm_select_gh3, 9, 2, 1, 1) - self.coop_guitar_select_gh3 = QToolButton(self.gh3_audio_widget) - self.coop_guitar_select_gh3.setObjectName(u"coop_guitar_select_gh3") - self.coop_guitar_select_gh3.setEnabled(False) - - self.gh3_stems_layout.addWidget(self.coop_guitar_select_gh3, 7, 2, 1, 1) - - self.coop_backing_label_gh3 = QLabel(self.gh3_audio_widget) - self.coop_backing_label_gh3.setObjectName(u"coop_backing_label_gh3") - self.coop_backing_label_gh3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.guitar_input_gh3 = QLineEdit(self.gh3_audio_widget) + self.guitar_input_gh3.setObjectName(u"guitar_input_gh3") + self.guitar_input_gh3.setEnabled(True) - self.gh3_stems_layout.addWidget(self.coop_backing_label_gh3, 10, 0, 1, 1) + self.gh3_stems_layout.addWidget(self.guitar_input_gh3, 1, 1, 1, 1) - self.gh3_audio_options = QHBoxLayout() - self.gh3_audio_options.setObjectName(u"gh3_audio_options") - self.p2_rhythm_check = QCheckBox(self.gh3_audio_widget) - self.p2_rhythm_check.setObjectName(u"p2_rhythm_check") + self.gh3_preview_audio_label = QLabel(self.gh3_audio_widget) + self.gh3_preview_audio_label.setObjectName(u"gh3_preview_audio_label") - self.gh3_audio_options.addWidget(self.p2_rhythm_check) + self.gh3_stems_layout.addWidget(self.gh3_preview_audio_label, 13, 0, 1, 1) - self.coop_audio_check = QCheckBox(self.gh3_audio_widget) - self.coop_audio_check.setObjectName(u"coop_audio_check") - self.coop_audio_check.setEnabled(False) + self.gh3_preview_audio_input = QLineEdit(self.gh3_audio_widget) + self.gh3_preview_audio_input.setObjectName(u"gh3_preview_audio_input") + self.gh3_preview_audio_input.setEnabled(False) - self.gh3_audio_options.addWidget(self.coop_audio_check) + self.gh3_stems_layout.addWidget(self.gh3_preview_audio_input, 13, 1, 1, 1) + self.gh3_preview_audio_select = QToolButton(self.gh3_audio_widget) + self.gh3_preview_audio_select.setObjectName(u"gh3_preview_audio_select") + self.gh3_preview_audio_select.setEnabled(False) - self.gh3_stems_layout.addLayout(self.gh3_audio_options, 5, 1, 1, 1) + self.gh3_stems_layout.addWidget(self.gh3_preview_audio_select, 13, 2, 1, 1) self.verticalLayout_10.addLayout(self.gh3_stems_layout) + self.gh3_rendered_preview_check = QCheckBox(self.gh3_audio_widget) + self.gh3_rendered_preview_check.setObjectName(u"gh3_rendered_preview_check") + + self.verticalLayout_10.addWidget(self.gh3_rendered_preview_check) + self.gh3_preview_layout = QHBoxLayout() self.gh3_preview_layout.setObjectName(u"gh3_preview_layout") self.preview_label_2 = QLabel(self.gh3_audio_widget) @@ -791,6 +842,11 @@ def setupUi(self, Form): self.gh3_preview_layout.addWidget(self.length_mills_gh3) + self.gh3_set_end = QCheckBox(self.gh3_audio_widget) + self.gh3_set_end.setObjectName(u"gh3_set_end") + + self.gh3_preview_layout.addWidget(self.gh3_set_end) + self.verticalLayout_10.addLayout(self.gh3_preview_layout) @@ -809,83 +865,99 @@ def setupUi(self, Form): self.verticalLayout_13.setContentsMargins(0, 0, 0, 0) self.gridLayout_3 = QGridLayout() self.gridLayout_3.setObjectName(u"gridLayout_3") - self.ghwt_song_script_input = QLineEdit(self.wt_song_data_widget) - self.ghwt_song_script_input.setObjectName(u"ghwt_song_script_input") + self.setlist_settings_layout = QHBoxLayout() + self.setlist_settings_layout.setObjectName(u"setlist_settings_layout") + self.game_icon_label = QLabel(self.wt_song_data_widget) + self.game_icon_label.setObjectName(u"game_icon_label") - self.gridLayout_3.addWidget(self.ghwt_song_script_input, 3, 1, 1, 1) + self.setlist_settings_layout.addWidget(self.game_icon_label) - self.ghwt_band_vol = QDoubleSpinBox(self.wt_song_data_widget) - self.ghwt_band_vol.setObjectName(u"ghwt_band_vol") - self.ghwt_band_vol.setMinimum(-10.000000000000000) - self.ghwt_band_vol.setMaximum(5.000000000000000) - self.ghwt_band_vol.setSingleStep(0.500000000000000) + self.game_icon_input = QLineEdit(self.wt_song_data_widget) + self.game_icon_input.setObjectName(u"game_icon_input") - self.gridLayout_3.addWidget(self.ghwt_band_vol, 10, 1, 1, 1) + self.setlist_settings_layout.addWidget(self.game_icon_input) - self.ghwor_stfs_label = QLabel(self.wt_song_data_widget) - self.ghwor_stfs_label.setObjectName(u"ghwor_stfs_label") + self.game_category_label = QLabel(self.wt_song_data_widget) + self.game_category_label.setObjectName(u"game_category_label") - self.gridLayout_3.addWidget(self.ghwor_stfs_label, 4, 0, 1, 1) + self.setlist_settings_layout.addWidget(self.game_category_label) - self.ghwt_drumkit_label = QLabel(self.wt_song_data_widget) - self.ghwt_drumkit_label.setObjectName(u"ghwt_drumkit_label") + self.game_category_input = QLineEdit(self.wt_song_data_widget) + self.game_category_input.setObjectName(u"game_category_input") - self.gridLayout_3.addWidget(self.ghwt_drumkit_label, 7, 0, 1, 1) + self.setlist_settings_layout.addWidget(self.game_category_input) - self.line_10 = QFrame(self.wt_song_data_widget) - self.line_10.setObjectName(u"line_10") - self.line_10.setFrameShape(QFrame.HLine) - self.line_10.setFrameShadow(QFrame.Sunken) + self.band_label = QLabel(self.wt_song_data_widget) + self.band_label.setObjectName(u"band_label") - self.gridLayout_3.addWidget(self.line_10, 12, 0, 1, 3) + self.setlist_settings_layout.addWidget(self.band_label) - self.ghwt_midi_file_select = QToolButton(self.wt_song_data_widget) - self.ghwt_midi_file_select.setObjectName(u"ghwt_midi_file_select") + self.band_input = QLineEdit(self.wt_song_data_widget) + self.band_input.setObjectName(u"band_input") - self.gridLayout_3.addWidget(self.ghwt_midi_file_select, 0, 2, 1, 1) + self.setlist_settings_layout.addWidget(self.band_input) - self.ghwt_ska_files_input = QLineEdit(self.wt_song_data_widget) - self.ghwt_ska_files_input.setObjectName(u"ghwt_ska_files_input") - self.gridLayout_3.addWidget(self.ghwt_ska_files_input, 2, 1, 1, 1) + self.gridLayout_3.addLayout(self.setlist_settings_layout, 17, 1, 1, 2) - self.ghwt_vocal_gender_select = QComboBox(self.wt_song_data_widget) - self.ghwt_vocal_gender_select.addItem("") - self.ghwt_vocal_gender_select.addItem("") - self.ghwt_vocal_gender_select.addItem("") - self.ghwt_vocal_gender_select.setObjectName(u"ghwt_vocal_gender_select") + self.ghwt_perf_override_input = QLineEdit(self.wt_song_data_widget) + self.ghwt_perf_override_input.setObjectName(u"ghwt_perf_override_input") - self.gridLayout_3.addWidget(self.ghwt_vocal_gender_select, 8, 1, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_perf_override_input, 1, 1, 1, 1) - self.ghwt_song_script_select = QToolButton(self.wt_song_data_widget) - self.ghwt_song_script_select.setObjectName(u"ghwt_song_script_select") + self.ghwt_band_vol = QDoubleSpinBox(self.wt_song_data_widget) + self.ghwt_band_vol.setObjectName(u"ghwt_band_vol") + self.ghwt_band_vol.setMinimum(-10.000000000000000) + self.ghwt_band_vol.setMaximum(5.000000000000000) + self.ghwt_band_vol.setSingleStep(0.500000000000000) - self.gridLayout_3.addWidget(self.ghwt_song_script_select, 3, 2, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_band_vol, 11, 1, 1, 1) - self.ghwt_vocal_gender_label = QLabel(self.wt_song_data_widget) - self.ghwt_vocal_gender_label.setObjectName(u"ghwt_vocal_gender_label") + self.ghwt_drumkit_label = QLabel(self.wt_song_data_widget) + self.ghwt_drumkit_label.setObjectName(u"ghwt_drumkit_label") - self.gridLayout_3.addWidget(self.ghwt_vocal_gender_label, 8, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_drumkit_label, 7, 0, 1, 1) - self.ghwt_song_script_label = QLabel(self.wt_song_data_widget) - self.ghwt_song_script_label.setObjectName(u"ghwt_song_script_label") + self.ghwt_midi_file_input = QLineEdit(self.wt_song_data_widget) + self.ghwt_midi_file_input.setObjectName(u"ghwt_midi_file_input") - self.gridLayout_3.addWidget(self.ghwt_song_script_label, 3, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_midi_file_input, 0, 1, 1, 1) - self.ghwt_ska_files_label = QLabel(self.wt_song_data_widget) - self.ghwt_ska_files_label.setObjectName(u"ghwt_ska_files_label") + self.ghwt_countoff_label = QLabel(self.wt_song_data_widget) + self.ghwt_countoff_label.setObjectName(u"ghwt_countoff_label") - self.gridLayout_3.addWidget(self.ghwt_ska_files_label, 2, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_countoff_label, 5, 0, 1, 1) + + self.other_settings_label = QLabel(self.wt_song_data_widget) + self.other_settings_label.setObjectName(u"other_settings_label") + + self.gridLayout_3.addWidget(self.other_settings_label, 15, 0, 1, 3, Qt.AlignHCenter) + + self.ghwor_stfs_select = QToolButton(self.wt_song_data_widget) + self.ghwor_stfs_select.setObjectName(u"ghwor_stfs_select") + + self.gridLayout_3.addWidget(self.ghwor_stfs_select, 4, 2, 1, 1) self.ghwor_stfs_input = QLineEdit(self.wt_song_data_widget) self.ghwor_stfs_input.setObjectName(u"ghwor_stfs_input") self.gridLayout_3.addWidget(self.ghwor_stfs_input, 4, 1, 1, 1) - self.ghwt_perf_override_select = QToolButton(self.wt_song_data_widget) - self.ghwt_perf_override_select.setObjectName(u"ghwt_perf_override_select") + self.ghwt_song_script_label = QLabel(self.wt_song_data_widget) + self.ghwt_song_script_label.setObjectName(u"ghwt_song_script_label") - self.gridLayout_3.addWidget(self.ghwt_perf_override_select, 1, 2, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_song_script_label, 3, 0, 1, 1) + + self.ghwt_countoff_select = QComboBox(self.wt_song_data_widget) + self.ghwt_countoff_select.addItem(u"Hihat01") + self.ghwt_countoff_select.addItem(u"Hihat02") + self.ghwt_countoff_select.addItem(u"Hihat03") + self.ghwt_countoff_select.addItem(u"Sticks_Huge") + self.ghwt_countoff_select.addItem(u"Sticks_Normal") + self.ghwt_countoff_select.addItem(u"Sticks_Tiny") + self.ghwt_countoff_select.setObjectName(u"ghwt_countoff_select") + + self.gridLayout_3.addWidget(self.ghwt_countoff_select, 5, 1, 1, 1) self.tiers_layout = QGridLayout() self.tiers_layout.setObjectName(u"tiers_layout") @@ -963,78 +1035,23 @@ def setupUi(self, Form): self.tiers_layout.addWidget(self.band_tier_value, 4, 1, 1, 1) - self.gridLayout_3.addLayout(self.tiers_layout, 11, 1, 1, 1) - - self.ghwt_countoff_select = QComboBox(self.wt_song_data_widget) - self.ghwt_countoff_select.addItem(u"Hihat01") - self.ghwt_countoff_select.addItem(u"Hihat02") - self.ghwt_countoff_select.addItem(u"Hihat03") - self.ghwt_countoff_select.addItem(u"Sticks_Huge") - self.ghwt_countoff_select.addItem(u"Sticks_Normal") - self.ghwt_countoff_select.addItem(u"Sticks_Tiny") - self.ghwt_countoff_select.setObjectName(u"ghwt_countoff_select") + self.gridLayout_3.addLayout(self.tiers_layout, 13, 1, 1, 1) - self.gridLayout_3.addWidget(self.ghwt_countoff_select, 5, 1, 1, 1) - - self.ghwt_ska_files_select = QToolButton(self.wt_song_data_widget) - self.ghwt_ska_files_select.setObjectName(u"ghwt_ska_files_select") - - self.gridLayout_3.addWidget(self.ghwt_ska_files_select, 2, 2, 1, 1) - - self.ghwt_countoff_label = QLabel(self.wt_song_data_widget) - self.ghwt_countoff_label.setObjectName(u"ghwt_countoff_label") + self.vocal_scroll_speed_input = QDoubleSpinBox(self.wt_song_data_widget) + self.vocal_scroll_speed_input.setObjectName(u"vocal_scroll_speed_input") + self.vocal_scroll_speed_input.setValue(1.000000000000000) - self.gridLayout_3.addWidget(self.ghwt_countoff_label, 5, 0, 1, 1) + self.gridLayout_3.addWidget(self.vocal_scroll_speed_input, 9, 1, 1, 1) self.tiers_label = QLabel(self.wt_song_data_widget) self.tiers_label.setObjectName(u"tiers_label") - self.gridLayout_3.addWidget(self.tiers_label, 11, 0, 1, 1) - - self.setlist_settings_layout = QHBoxLayout() - self.setlist_settings_layout.setObjectName(u"setlist_settings_layout") - self.game_icon_label = QLabel(self.wt_song_data_widget) - self.game_icon_label.setObjectName(u"game_icon_label") - - self.setlist_settings_layout.addWidget(self.game_icon_label) - - self.game_icon_input = QLineEdit(self.wt_song_data_widget) - self.game_icon_input.setObjectName(u"game_icon_input") - - self.setlist_settings_layout.addWidget(self.game_icon_input) - - self.game_category_label = QLabel(self.wt_song_data_widget) - self.game_category_label.setObjectName(u"game_category_label") - - self.setlist_settings_layout.addWidget(self.game_category_label) - - self.game_category_input = QLineEdit(self.wt_song_data_widget) - self.game_category_input.setObjectName(u"game_category_input") - - self.setlist_settings_layout.addWidget(self.game_category_input) - - self.band_label = QLabel(self.wt_song_data_widget) - self.band_label.setObjectName(u"band_label") - - self.setlist_settings_layout.addWidget(self.band_label) - - self.band_input = QLineEdit(self.wt_song_data_widget) - self.band_input.setObjectName(u"band_input") - - self.setlist_settings_layout.addWidget(self.band_input) - - - self.gridLayout_3.addLayout(self.setlist_settings_layout, 15, 1, 1, 2) + self.gridLayout_3.addWidget(self.tiers_label, 13, 0, 1, 1) - self.other_settings_label = QLabel(self.wt_song_data_widget) - self.other_settings_label.setObjectName(u"other_settings_label") - - self.gridLayout_3.addWidget(self.other_settings_label, 13, 0, 1, 3, Qt.AlignHCenter) - - self.label_3 = QLabel(self.wt_song_data_widget) - self.label_3.setObjectName(u"label_3") + self.vocal_speed_label = QLabel(self.wt_song_data_widget) + self.vocal_speed_label.setObjectName(u"vocal_speed_label") - self.gridLayout_3.addWidget(self.label_3, 15, 0, 1, 1) + self.gridLayout_3.addWidget(self.vocal_speed_label, 9, 0, 1, 1) self.skeleton_types_layout = QHBoxLayout() self.skeleton_types_layout.setObjectName(u"skeleton_types_layout") @@ -1086,83 +1103,145 @@ def setupUi(self, Form): self.skeleton_types_layout.addWidget(self.skeleton_type_v_select) - self.gridLayout_3.addLayout(self.skeleton_types_layout, 16, 1, 1, 2) + self.gridLayout_3.addLayout(self.skeleton_types_layout, 18, 1, 1, 2) - self.vocal_speed_label = QLabel(self.wt_song_data_widget) - self.vocal_speed_label.setObjectName(u"vocal_speed_label") + self.ghwt_band_vol_label = QLabel(self.wt_song_data_widget) + self.ghwt_band_vol_label.setObjectName(u"ghwt_band_vol_label") - self.gridLayout_3.addWidget(self.vocal_speed_label, 9, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_band_vol_label, 11, 0, 1, 1) - self.ghwor_stfs_select = QToolButton(self.wt_song_data_widget) - self.ghwor_stfs_select.setObjectName(u"ghwor_stfs_select") + self.horizontalLayout_9 = QHBoxLayout() + self.horizontalLayout_9.setObjectName(u"horizontalLayout_9") + self.guitar_mic_check = QCheckBox(self.wt_song_data_widget) + self.guitar_mic_check.setObjectName(u"guitar_mic_check") - self.gridLayout_3.addWidget(self.ghwor_stfs_select, 4, 2, 1, 1) + self.horizontalLayout_9.addWidget(self.guitar_mic_check) - self.skeleton_types_label = QLabel(self.wt_song_data_widget) - self.skeleton_types_label.setObjectName(u"skeleton_types_label") + self.bass_mic_check = QCheckBox(self.wt_song_data_widget) + self.bass_mic_check.setObjectName(u"bass_mic_check") + + self.horizontalLayout_9.addWidget(self.bass_mic_check) + + self.use_new_clips_check = QCheckBox(self.wt_song_data_widget) + self.use_new_clips_check.setObjectName(u"use_new_clips_check") + + self.horizontalLayout_9.addWidget(self.use_new_clips_check) + + + self.gridLayout_3.addLayout(self.horizontalLayout_9, 16, 1, 1, 2) + + self.ghwt_vocal_cents_label = QLabel(self.wt_song_data_widget) + self.ghwt_vocal_cents_label.setObjectName(u"ghwt_vocal_cents_label") + + self.gridLayout_3.addWidget(self.ghwt_vocal_cents_label, 10, 0, 1, 1) + + self.ghwt_ska_files_input = QLineEdit(self.wt_song_data_widget) + self.ghwt_ska_files_input.setObjectName(u"ghwt_ska_files_input") + + self.gridLayout_3.addWidget(self.ghwt_ska_files_input, 2, 1, 1, 1) + + self.ghwt_perf_override_select = QToolButton(self.wt_song_data_widget) + self.ghwt_perf_override_select.setObjectName(u"ghwt_perf_override_select") + + self.gridLayout_3.addWidget(self.ghwt_perf_override_select, 1, 2, 1, 1) + + self.ghwt_ska_files_select = QToolButton(self.wt_song_data_widget) + self.ghwt_ska_files_select.setObjectName(u"ghwt_ska_files_select") + + self.gridLayout_3.addWidget(self.ghwt_ska_files_select, 2, 2, 1, 1) + + self.ghwt_ska_files_label = QLabel(self.wt_song_data_widget) + self.ghwt_ska_files_label.setObjectName(u"ghwt_ska_files_label") + + self.gridLayout_3.addWidget(self.ghwt_ska_files_label, 2, 0, 1, 1) + + self.label_3 = QLabel(self.wt_song_data_widget) + self.label_3.setObjectName(u"label_3") + + self.gridLayout_3.addWidget(self.label_3, 17, 0, 1, 1) + + self.ghwt_song_script_select = QToolButton(self.wt_song_data_widget) + self.ghwt_song_script_select.setObjectName(u"ghwt_song_script_select") + + self.gridLayout_3.addWidget(self.ghwt_song_script_select, 3, 2, 1, 1) + + self.label_7 = QLabel(self.wt_song_data_widget) + self.label_7.setObjectName(u"label_7") + + self.gridLayout_3.addWidget(self.label_7, 16, 0, 1, 1) + + self.ghwt_song_script_input = QLineEdit(self.wt_song_data_widget) + self.ghwt_song_script_input.setObjectName(u"ghwt_song_script_input") + + self.gridLayout_3.addWidget(self.ghwt_song_script_input, 3, 1, 1, 1) + + self.line_10 = QFrame(self.wt_song_data_widget) + self.line_10.setObjectName(u"line_10") + self.line_10.setFrameShape(QFrame.HLine) + self.line_10.setFrameShadow(QFrame.Sunken) - self.gridLayout_3.addWidget(self.skeleton_types_label, 16, 0, 1, 1) + self.gridLayout_3.addWidget(self.line_10, 14, 0, 1, 3) self.ghwt_drumkit_select = QComboBox(self.wt_song_data_widget) self.ghwt_drumkit_select.setObjectName(u"ghwt_drumkit_select") self.gridLayout_3.addWidget(self.ghwt_drumkit_select, 7, 1, 1, 1) - self.ghwt_perf_override_label = QLabel(self.wt_song_data_widget) - self.ghwt_perf_override_label.setObjectName(u"ghwt_perf_override_label") - - self.gridLayout_3.addWidget(self.ghwt_perf_override_label, 1, 0, 1, 1) - - self.ghwt_perf_override_input = QLineEdit(self.wt_song_data_widget) - self.ghwt_perf_override_input.setObjectName(u"ghwt_perf_override_input") + self.ghwt_vocal_gender_label = QLabel(self.wt_song_data_widget) + self.ghwt_vocal_gender_label.setObjectName(u"ghwt_vocal_gender_label") - self.gridLayout_3.addWidget(self.ghwt_perf_override_input, 1, 1, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_vocal_gender_label, 8, 0, 1, 1) - self.ghwt_band_vol_label = QLabel(self.wt_song_data_widget) - self.ghwt_band_vol_label.setObjectName(u"ghwt_band_vol_label") + self.ghwt_midi_file_label = QLabel(self.wt_song_data_widget) + self.ghwt_midi_file_label.setObjectName(u"ghwt_midi_file_label") - self.gridLayout_3.addWidget(self.ghwt_band_vol_label, 10, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_midi_file_label, 0, 0, 1, 1) - self.vocal_scroll_speed_input = QDoubleSpinBox(self.wt_song_data_widget) - self.vocal_scroll_speed_input.setObjectName(u"vocal_scroll_speed_input") - self.vocal_scroll_speed_input.setValue(1.000000000000000) + self.ghwt_perf_override_label = QLabel(self.wt_song_data_widget) + self.ghwt_perf_override_label.setObjectName(u"ghwt_perf_override_label") - self.gridLayout_3.addWidget(self.vocal_scroll_speed_input, 9, 1, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_perf_override_label, 1, 0, 1, 1) - self.ghwt_midi_file_input = QLineEdit(self.wt_song_data_widget) - self.ghwt_midi_file_input.setObjectName(u"ghwt_midi_file_input") + self.ghwor_stfs_label = QLabel(self.wt_song_data_widget) + self.ghwor_stfs_label.setObjectName(u"ghwor_stfs_label") - self.gridLayout_3.addWidget(self.ghwt_midi_file_input, 0, 1, 1, 1) + self.gridLayout_3.addWidget(self.ghwor_stfs_label, 4, 0, 1, 1) - self.ghwt_midi_file_label = QLabel(self.wt_song_data_widget) - self.ghwt_midi_file_label.setObjectName(u"ghwt_midi_file_label") + self.skeleton_types_label = QLabel(self.wt_song_data_widget) + self.skeleton_types_label.setObjectName(u"skeleton_types_label") - self.gridLayout_3.addWidget(self.ghwt_midi_file_label, 0, 0, 1, 1) + self.gridLayout_3.addWidget(self.skeleton_types_label, 18, 0, 1, 1) - self.label_7 = QLabel(self.wt_song_data_widget) - self.label_7.setObjectName(u"label_7") + self.ghwt_vocal_gender_select = QComboBox(self.wt_song_data_widget) + self.ghwt_vocal_gender_select.addItem("") + self.ghwt_vocal_gender_select.addItem("") + self.ghwt_vocal_gender_select.addItem("") + self.ghwt_vocal_gender_select.setObjectName(u"ghwt_vocal_gender_select") - self.gridLayout_3.addWidget(self.label_7, 14, 0, 1, 1) + self.gridLayout_3.addWidget(self.ghwt_vocal_gender_select, 8, 1, 1, 1) - self.horizontalLayout_9 = QHBoxLayout() - self.horizontalLayout_9.setObjectName(u"horizontalLayout_9") - self.guitar_mic_check = QCheckBox(self.wt_song_data_widget) - self.guitar_mic_check.setObjectName(u"guitar_mic_check") + self.ghwt_vocal_cents = QSpinBox(self.wt_song_data_widget) + self.ghwt_vocal_cents.setObjectName(u"ghwt_vocal_cents") + self.ghwt_vocal_cents.setMinimum(-50) + self.ghwt_vocal_cents.setMaximum(50) - self.horizontalLayout_9.addWidget(self.guitar_mic_check) + self.gridLayout_3.addWidget(self.ghwt_vocal_cents, 10, 1, 1, 1) - self.bass_mic_check = QCheckBox(self.wt_song_data_widget) - self.bass_mic_check.setObjectName(u"bass_mic_check") + self.ghwt_midi_file_select = QToolButton(self.wt_song_data_widget) + self.ghwt_midi_file_select.setObjectName(u"ghwt_midi_file_select") - self.horizontalLayout_9.addWidget(self.bass_mic_check) + self.gridLayout_3.addWidget(self.ghwt_midi_file_select, 0, 2, 1, 1) - self.use_new_clips_check = QCheckBox(self.wt_song_data_widget) - self.use_new_clips_check.setObjectName(u"use_new_clips_check") + self.ghwt_whammy_cutoff_label = QLabel(self.wt_song_data_widget) + self.ghwt_whammy_cutoff_label.setObjectName(u"ghwt_whammy_cutoff_label") - self.horizontalLayout_9.addWidget(self.use_new_clips_check) + self.gridLayout_3.addWidget(self.ghwt_whammy_cutoff_label, 12, 0, 1, 1) + self.ghwt_whammy_cutoff = QDoubleSpinBox(self.wt_song_data_widget) + self.ghwt_whammy_cutoff.setObjectName(u"ghwt_whammy_cutoff") + self.ghwt_whammy_cutoff.setValue(0.500000000000000) - self.gridLayout_3.addLayout(self.horizontalLayout_9, 14, 1, 1, 2) + self.gridLayout_3.addWidget(self.ghwt_whammy_cutoff, 12, 1, 1, 1) self.verticalLayout_13.addLayout(self.gridLayout_3) @@ -1182,6 +1261,25 @@ def setupUi(self, Form): self.verticalLayout_7.setContentsMargins(0, 0, 0, 0) self.gridLayout_5 = QGridLayout() self.gridLayout_5.setObjectName(u"gridLayout_5") + self.gh3_ska_files_select = QToolButton(self.gh3_song_data_widget) + self.gh3_ska_files_select.setObjectName(u"gh3_ska_files_select") + + self.gridLayout_5.addWidget(self.gh3_ska_files_select, 2, 2, 1, 1) + + self.gh3_ska_files_label = QLabel(self.gh3_song_data_widget) + self.gh3_ska_files_label.setObjectName(u"gh3_ska_files_label") + + self.gridLayout_5.addWidget(self.gh3_ska_files_label, 2, 0, 1, 1) + + self.gh3_vocal_gender_select = QComboBox(self.gh3_song_data_widget) + self.gh3_vocal_gender_select.addItem("") + self.gh3_vocal_gender_select.addItem("") + self.gh3_vocal_gender_select.addItem("") + self.gh3_vocal_gender_select.addItem("") + self.gh3_vocal_gender_select.setObjectName(u"gh3_vocal_gender_select") + + self.gridLayout_5.addWidget(self.gh3_vocal_gender_select, 7, 1, 1, 1) + self.horizontalLayout_7 = QHBoxLayout() self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") self.gh3_gtr_vol_label = QLabel(self.gh3_song_data_widget) @@ -1213,17 +1311,34 @@ def setupUi(self, Form): self.horizontalLayout_7.addWidget(self.gh3_band_vol) - self.gridLayout_5.addLayout(self.horizontalLayout_7, 7, 1, 1, 1) + self.gridLayout_5.addLayout(self.horizontalLayout_7, 10, 1, 1, 1) - self.gh3_perf_override_label = QLabel(self.gh3_song_data_widget) - self.gh3_perf_override_label.setObjectName(u"gh3_perf_override_label") + self.gh3_perf_override_input = QLineEdit(self.gh3_song_data_widget) + self.gh3_perf_override_input.setObjectName(u"gh3_perf_override_input") - self.gridLayout_5.addWidget(self.gh3_perf_override_label, 1, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_perf_override_input, 1, 1, 1, 1) self.gh3_countoff_label = QLabel(self.gh3_song_data_widget) self.gh3_countoff_label.setObjectName(u"gh3_countoff_label") - self.gridLayout_5.addWidget(self.gh3_countoff_label, 2, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_countoff_label, 5, 0, 1, 1) + + self.line = QFrame(self.gh3_song_data_widget) + self.line.setObjectName(u"line") + self.line.setFrameShape(QFrame.HLine) + self.line.setFrameShadow(QFrame.Sunken) + + self.gridLayout_5.addWidget(self.line, 9, 0, 1, 3) + + self.gh3_midi_file_input = QLineEdit(self.gh3_song_data_widget) + self.gh3_midi_file_input.setObjectName(u"gh3_midi_file_input") + + self.gridLayout_5.addWidget(self.gh3_midi_file_input, 0, 1, 1, 1) + + self.gh3_perf_override_label = QLabel(self.gh3_song_data_widget) + self.gh3_perf_override_label.setObjectName(u"gh3_perf_override_label") + + self.gridLayout_5.addWidget(self.gh3_perf_override_label, 1, 0, 1, 1) self.gh3_countoff_select = QComboBox(self.gh3_song_data_widget) self.gh3_countoff_select.addItem(u"Hihat01") @@ -1234,68 +1349,67 @@ def setupUi(self, Form): self.gh3_countoff_select.addItem(u"Sticks_Tiny") self.gh3_countoff_select.setObjectName(u"gh3_countoff_select") - self.gridLayout_5.addWidget(self.gh3_countoff_select, 2, 1, 1, 1) + self.gridLayout_5.addWidget(self.gh3_countoff_select, 5, 1, 1, 1) - self.gh3_vocal_gender_select = QComboBox(self.gh3_song_data_widget) - self.gh3_vocal_gender_select.addItem("") - self.gh3_vocal_gender_select.addItem("") - self.gh3_vocal_gender_select.addItem("") - self.gh3_vocal_gender_select.addItem("") - self.gh3_vocal_gender_select.setObjectName(u"gh3_vocal_gender_select") + self.gh3_bassist_select = QComboBox(self.gh3_song_data_widget) + self.gh3_bassist_select.setObjectName(u"gh3_bassist_select") + + self.gridLayout_5.addWidget(self.gh3_bassist_select, 8, 1, 1, 1) + + self.gh3_volume_label = QLabel(self.gh3_song_data_widget) + self.gh3_volume_label.setObjectName(u"gh3_volume_label") + + self.gridLayout_5.addWidget(self.gh3_volume_label, 10, 0, 1, 1) + + self.gh3_song_script_label = QLabel(self.gh3_song_data_widget) + self.gh3_song_script_label.setObjectName(u"gh3_song_script_label") - self.gridLayout_5.addWidget(self.gh3_vocal_gender_select, 4, 1, 1, 1) + self.gridLayout_5.addWidget(self.gh3_song_script_label, 4, 0, 1, 1) + + self.gh3_midi_file_select = QToolButton(self.gh3_song_data_widget) + self.gh3_midi_file_select.setObjectName(u"gh3_midi_file_select") + + self.gridLayout_5.addWidget(self.gh3_midi_file_select, 0, 2, 1, 1) self.gh3_bassist_select_label = QLabel(self.gh3_song_data_widget) self.gh3_bassist_select_label.setObjectName(u"gh3_bassist_select_label") - self.gridLayout_5.addWidget(self.gh3_bassist_select_label, 5, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_bassist_select_label, 8, 0, 1, 1) - self.gh3_midi_file_input = QLineEdit(self.gh3_song_data_widget) - self.gh3_midi_file_input.setObjectName(u"gh3_midi_file_input") + self.gh3_song_script_select = QToolButton(self.gh3_song_data_widget) + self.gh3_song_script_select.setObjectName(u"gh3_song_script_select") - self.gridLayout_5.addWidget(self.gh3_midi_file_input, 0, 1, 1, 1) + self.gridLayout_5.addWidget(self.gh3_song_script_select, 4, 2, 1, 1) - self.gh3_midi_file_label = QLabel(self.gh3_song_data_widget) - self.gh3_midi_file_label.setObjectName(u"gh3_midi_file_label") + self.gh3_song_script_input = QLineEdit(self.gh3_song_data_widget) + self.gh3_song_script_input.setObjectName(u"gh3_song_script_input") - self.gridLayout_5.addWidget(self.gh3_midi_file_label, 0, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_song_script_input, 4, 1, 1, 1) self.gh3_perf_override_select = QToolButton(self.gh3_song_data_widget) self.gh3_perf_override_select.setObjectName(u"gh3_perf_override_select") self.gridLayout_5.addWidget(self.gh3_perf_override_select, 1, 2, 1, 1) - self.gh3_bassist_select = QComboBox(self.gh3_song_data_widget) - self.gh3_bassist_select.setObjectName(u"gh3_bassist_select") - - self.gridLayout_5.addWidget(self.gh3_bassist_select, 5, 1, 1, 1) - - self.gh3_midi_file_select = QToolButton(self.gh3_song_data_widget) - self.gh3_midi_file_select.setObjectName(u"gh3_midi_file_select") - - self.gridLayout_5.addWidget(self.gh3_midi_file_select, 0, 2, 1, 1) - - self.gh3_perf_override_input = QLineEdit(self.gh3_song_data_widget) - self.gh3_perf_override_input.setObjectName(u"gh3_perf_override_input") + self.gh3_vocal_gender_label = QLabel(self.gh3_song_data_widget) + self.gh3_vocal_gender_label.setObjectName(u"gh3_vocal_gender_label") - self.gridLayout_5.addWidget(self.gh3_perf_override_input, 1, 1, 1, 1) + self.gridLayout_5.addWidget(self.gh3_vocal_gender_label, 7, 0, 1, 1) - self.gh3_volume_label = QLabel(self.gh3_song_data_widget) - self.gh3_volume_label.setObjectName(u"gh3_volume_label") + self.gh3_ska_files_input = QLineEdit(self.gh3_song_data_widget) + self.gh3_ska_files_input.setObjectName(u"gh3_ska_files_input") - self.gridLayout_5.addWidget(self.gh3_volume_label, 7, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_ska_files_input, 2, 1, 1, 1) - self.gh3_vocal_gender_label = QLabel(self.gh3_song_data_widget) - self.gh3_vocal_gender_label.setObjectName(u"gh3_vocal_gender_label") + self.gh3_midi_file_label = QLabel(self.gh3_song_data_widget) + self.gh3_midi_file_label.setObjectName(u"gh3_midi_file_label") - self.gridLayout_5.addWidget(self.gh3_vocal_gender_label, 4, 0, 1, 1) + self.gridLayout_5.addWidget(self.gh3_midi_file_label, 0, 0, 1, 1) - self.line = QFrame(self.gh3_song_data_widget) - self.line.setObjectName(u"line") - self.line.setFrameShape(QFrame.HLine) - self.line.setFrameShadow(QFrame.Sunken) + self.gh3_ska_file_convert_check = QCheckBox(self.gh3_song_data_widget) + self.gh3_ska_file_convert_check.setObjectName(u"gh3_ska_file_convert_check") - self.gridLayout_5.addWidget(self.line, 6, 0, 1, 3) + self.gridLayout_5.addWidget(self.gh3_ska_file_convert_check, 3, 1, 1, 1) self.verticalLayout_7.addLayout(self.gridLayout_5) @@ -1440,12 +1554,12 @@ def setupUi(self, Form): self.verticalLayout_15.addLayout(self.gridLayout_6) - self.horizontalLayout_5 = QHBoxLayout() - self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.platform_select = QHBoxLayout() + self.platform_select.setObjectName(u"platform_select") self.platform_label = QLabel(self.compile_settings) self.platform_label.setObjectName(u"platform_label") - self.horizontalLayout_5.addWidget(self.platform_label) + self.platform_select.addWidget(self.platform_label) self.platform_pc = QRadioButton(self.compile_settings) self.platform_button_group = QButtonGroup(Form) @@ -1454,17 +1568,17 @@ def setupUi(self, Form): self.platform_pc.setObjectName(u"platform_pc") self.platform_pc.setChecked(True) - self.horizontalLayout_5.addWidget(self.platform_pc) + self.platform_select.addWidget(self.platform_pc) self.platform_360 = QRadioButton(self.compile_settings) self.platform_button_group.addButton(self.platform_360) self.platform_360.setObjectName(u"platform_360") self.platform_360.setEnabled(False) - self.horizontalLayout_5.addWidget(self.platform_360) + self.platform_select.addWidget(self.platform_360) - self.verticalLayout_15.addLayout(self.horizontalLayout_5) + self.verticalLayout_15.addLayout(self.platform_select) self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") @@ -1574,8 +1688,19 @@ def setupUi(self, Form): self.beatlines_check.toggled.connect(self.beat_16th_low_input.setEnabled) self.beatlines_check.toggled.connect(self.beat_16th_high_input.setEnabled) self.p2_rhythm_check.toggled.connect(self.coop_audio_check.setEnabled) + self.ghwt_rendered_preview_check.toggled.connect(self.ghwt_preview_audio_input.setEnabled) + self.ghwt_rendered_preview_check.toggled.connect(self.ghwt_preview_audio_select.setEnabled) + self.ghwt_rendered_preview_check.toggled.connect(self.preview_minutes.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.preview_seconds.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.preview_mills.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.length_minutes.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.length_seconds.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.length_mills.setDisabled) + self.ghwt_rendered_preview_check.toggled.connect(self.ghwt_set_end.setDisabled) + self.gh3_rendered_preview_check.toggled.connect(self.gh3_preview_audio_input.setEnabled) + self.gh3_rendered_preview_check.toggled.connect(self.gh3_preview_audio_select.setEnabled) - self.compile_tabs.setCurrentIndex(3) + self.compile_tabs.setCurrentIndex(5) QMetaObject.connectSlotsByName(Form) @@ -1603,81 +1728,78 @@ def retranslateUi(self, Form): self.artist_label.setText(QCoreApplication.translate("Form", u"Artist:", None)) self.cover_artist_label.setText(QCoreApplication.translate("Form", u"Cover Artist:", None)) self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.metadata_tab), QCoreApplication.translate("Form", u"Metadata", None)) + self.crowd_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.toms_label.setText(QCoreApplication.translate("Form", u"Toms", None)) self.backing_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.cymbals_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.bass_label.setText(QCoreApplication.translate("Form", u"Bass", None)) + self.backing_label.setText(QCoreApplication.translate("Form", u"Backing", None)) self.snare_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.kick_label.setText(QCoreApplication.translate("Form", u"Kick", None)) - self.crowd_label.setText(QCoreApplication.translate("Form", u"Crowd", None)) - self.snare_label.setText(QCoreApplication.translate("Form", u"Snare", None)) - self.bass_select.setText(QCoreApplication.translate("Form", u"...", None)) self.guitar_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.vocals_label.setText(QCoreApplication.translate("Form", u"Vocals", None)) - self.toms_label.setText(QCoreApplication.translate("Form", u"Toms", None)) - self.toms_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.vocals_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.bass_label.setText(QCoreApplication.translate("Form", u"Bass", None)) + self.bass_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.cymbals_select.setText(QCoreApplication.translate("Form", u"...", None)) self.guitar_label.setText(QCoreApplication.translate("Form", u"Guitar", None)) - self.crowd_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.vocals_label.setText(QCoreApplication.translate("Form", u"Vocals", None)) + self.snare_label.setText(QCoreApplication.translate("Form", u"Snare", None)) self.cymbals_label.setText(QCoreApplication.translate("Form", u"Cymbals", None)) self.label.setText(QCoreApplication.translate("Form", u"Audio File Path", None)) - self.track_label.setText(QCoreApplication.translate("Form", u"Track", None)) - self.vocals_select.setText(QCoreApplication.translate("Form", u"...", None)) self.kick_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.backing_label.setText(QCoreApplication.translate("Form", u"Backing", None)) - self.encrypt_audio.setText(QCoreApplication.translate("Form", u"Encrypt Audio", None)) + self.ghwt_preview_label.setText(QCoreApplication.translate("Form", u"Preview", None)) + self.kick_label.setText(QCoreApplication.translate("Form", u"Kick", None)) + self.toms_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.track_label.setText(QCoreApplication.translate("Form", u"Track", None)) + self.ghwt_preview_audio_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.crowd_label.setText(QCoreApplication.translate("Form", u"Crowd", None)) self.preview_label.setText(QCoreApplication.translate("Form", u"Preview Start:", None)) self.length_label.setText(QCoreApplication.translate("Form", u"Length:", None)) self.ghwt_set_end.setText(QCoreApplication.translate("Form", u"Set End Time", None)) + self.encrypt_audio.setText(QCoreApplication.translate("Form", u"Encrypt Audio", None)) + self.ghwt_rendered_preview_check.setText(QCoreApplication.translate("Form", u"Use Rendered Preview Audio", None)) self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.audio_tab_wt), QCoreApplication.translate("Form", u"Audio (WT)", None)) self.guitar_label_gh3.setText(QCoreApplication.translate("Form", u"Guitar", None)) + self.backing_label_gh3.setText(QCoreApplication.translate("Form", u"Backing", None)) + self.crowd_label_gh3.setText(QCoreApplication.translate("Form", u"Crowd", None)) self.track_label_2.setText(QCoreApplication.translate("Form", u"Track", None)) - self.guitar_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) - self.rhythm_label_gh3.setText(QCoreApplication.translate("Form", u"Bass", None)) + self.p2_rhythm_check.setText(QCoreApplication.translate("Form", u"P2 is Rhythm", None)) + self.coop_audio_check.setText(QCoreApplication.translate("Form", u"Separate Co-op Audio", None)) + self.coop_guitar_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) + self.coop_rhythm_label_gh3.setText(QCoreApplication.translate("Form", u"Rhythm", None)) self.backing_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) - self.coop_backing_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) self.gh3_file_path_label.setText(QCoreApplication.translate("Form", u"Audio File Path", None)) - self.backing_label_gh3.setText(QCoreApplication.translate("Form", u"Backing", None)) self.crowd_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) - self.rhythm_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) + self.rhythm_label_gh3.setText(QCoreApplication.translate("Form", u"Bass", None)) + self.guitar_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) + self.coop_backing_label_gh3.setText(QCoreApplication.translate("Form", u"Backing", None)) self.coop_guitar_label_gh3.setText(QCoreApplication.translate("Form", u"Guitar", None)) - self.coop_rhythm_label_gh3.setText(QCoreApplication.translate("Form", u"Rhythm", None)) - self.crowd_label_gh3.setText(QCoreApplication.translate("Form", u"Crowd", None)) + self.rhythm_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) + self.coop_backing_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) self.coop_rhythm_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) - self.coop_guitar_select_gh3.setText(QCoreApplication.translate("Form", u"...", None)) - self.coop_backing_label_gh3.setText(QCoreApplication.translate("Form", u"Backing", None)) - self.p2_rhythm_check.setText(QCoreApplication.translate("Form", u"P2 is Rhythm", None)) - self.coop_audio_check.setText(QCoreApplication.translate("Form", u"Separate Co-op Audio", None)) + self.gh3_preview_audio_label.setText(QCoreApplication.translate("Form", u"Preview", None)) + self.gh3_preview_audio_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.gh3_rendered_preview_check.setText(QCoreApplication.translate("Form", u"Use Rendered Preview", None)) self.preview_label_2.setText(QCoreApplication.translate("Form", u"Song Preview:", None)) self.length_label_2.setText(QCoreApplication.translate("Form", u"Length:", None)) + self.gh3_set_end.setText(QCoreApplication.translate("Form", u"Set End Time", None)) self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.audio_tab_gh3), QCoreApplication.translate("Form", u"Audio (GH3)", None)) - self.ghwor_stfs_label.setText(QCoreApplication.translate("Form", u"WoR STFS File", None)) - self.ghwt_drumkit_label.setText(QCoreApplication.translate("Form", u"Drum Kit:", None)) - self.ghwt_midi_file_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.game_icon_label.setText(QCoreApplication.translate("Form", u"Game Icon", None)) + self.game_category_label.setText(QCoreApplication.translate("Form", u"Game Category", None)) + self.band_label.setText(QCoreApplication.translate("Form", u"Band", None)) #if QT_CONFIG(tooltip) - self.ghwt_ska_files_input.setToolTip(QCoreApplication.translate("Form", u"

A folder containing all SKA files used. Gets ignored if it's blank or doesn't exist

", None)) + self.ghwt_perf_override_input.setToolTip(QCoreApplication.translate("Form", u"

Select a file with a performance array to override the generated performance array.

Can be a text file compatible with the toolkit, or an already compiled qb file.

", None)) #endif // QT_CONFIG(tooltip) - self.ghwt_vocal_gender_select.setItemText(0, QCoreApplication.translate("Form", u"Male", None)) - self.ghwt_vocal_gender_select.setItemText(1, QCoreApplication.translate("Form", u"Female", None)) - self.ghwt_vocal_gender_select.setItemText(2, QCoreApplication.translate("Form", u"None", None)) - - self.ghwt_song_script_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.ghwt_vocal_gender_label.setText(QCoreApplication.translate("Form", u"Vocal Gender:", None)) + self.ghwt_drumkit_label.setText(QCoreApplication.translate("Form", u"Drum Kit:", None)) + self.ghwt_countoff_label.setText(QCoreApplication.translate("Form", u"Count Off:", None)) + self.other_settings_label.setText(QCoreApplication.translate("Form", u"World Tour Definitive Edition Settings", None)) + self.ghwor_stfs_select.setText(QCoreApplication.translate("Form", u"...", None)) self.ghwt_song_script_label.setText(QCoreApplication.translate("Form", u"Song Script:", None)) - self.ghwt_ska_files_label.setText(QCoreApplication.translate("Form", u"SKA Files:", None)) - self.ghwt_perf_override_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.band_tier_label.setText(QCoreApplication.translate("Form", u"Band", None)) self.vocal_tier_label.setText(QCoreApplication.translate("Form", u"Vocals", None)) self.drums_tier_label.setText(QCoreApplication.translate("Form", u"Drums", None)) self.guitar_tier_label.setText(QCoreApplication.translate("Form", u"Guitar", None)) self.bass_tier_label.setText(QCoreApplication.translate("Form", u"Bass", None)) - - self.ghwt_ska_files_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.ghwt_countoff_label.setText(QCoreApplication.translate("Form", u"Count Off:", None)) self.tiers_label.setText(QCoreApplication.translate("Form", u"Tiers", None)) - self.game_icon_label.setText(QCoreApplication.translate("Form", u"Game Icon", None)) - self.game_category_label.setText(QCoreApplication.translate("Form", u"Game Category", None)) - self.band_label.setText(QCoreApplication.translate("Form", u"Band", None)) - self.other_settings_label.setText(QCoreApplication.translate("Form", u"World Tour Definitive Edition Settings", None)) - self.label_3.setText(QCoreApplication.translate("Form", u"Setlist Settings", None)) + self.vocal_speed_label.setText(QCoreApplication.translate("Form", u"Vocal Scroll Speed:", None)) self.skeleton_type_g_label.setText(QCoreApplication.translate("Form", u"G", None)) self.skeleton_type_g_select.setItemText(0, QCoreApplication.translate("Form", u"Default", None)) self.skeleton_type_g_select.setItemText(1, QCoreApplication.translate("Form", u"Alex", None)) @@ -1690,39 +1812,59 @@ def retranslateUi(self, Form): self.skeleton_type_b_label.setText(QCoreApplication.translate("Form", u"B", None)) self.skeleton_type_d_label.setText(QCoreApplication.translate("Form", u"D", None)) self.skeleton_type_v_label.setText(QCoreApplication.translate("Form", u"V", None)) - self.vocal_speed_label.setText(QCoreApplication.translate("Form", u"Vocal Scroll Speed:", None)) - self.ghwor_stfs_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.skeleton_types_label.setText(QCoreApplication.translate("Form", u"Skeleton Types", None)) - self.ghwt_perf_override_label.setText(QCoreApplication.translate("Form", u"Perf Override:", None)) -#if QT_CONFIG(tooltip) - self.ghwt_perf_override_input.setToolTip(QCoreApplication.translate("Form", u"

Select a file with a performance array to override the generated performance array.

Can be a text file compatible with the toolkit, or an already compiled qb file.

", None)) -#endif // QT_CONFIG(tooltip) self.ghwt_band_vol_label.setText(QCoreApplication.translate("Form", u"Overall Volume:", None)) - self.ghwt_midi_file_label.setText(QCoreApplication.translate("Form", u"MIDI File:", None)) - self.label_7.setText(QCoreApplication.translate("Form", u"In-game Settings", None)) self.guitar_mic_check.setText(QCoreApplication.translate("Form", u"Guitar Mic", None)) self.bass_mic_check.setText(QCoreApplication.translate("Form", u"Bass Mic", None)) self.use_new_clips_check.setText(QCoreApplication.translate("Form", u"Use New Clips", None)) - self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.song_data_tab_wt), QCoreApplication.translate("Form", u"Song Data (WT)", None)) - self.gh3_gtr_vol_label.setText(QCoreApplication.translate("Form", u"Guitar:", None)) - self.gh3_band_vol_label.setText(QCoreApplication.translate("Form", u"Band:", None)) - self.gh3_perf_override_label.setText(QCoreApplication.translate("Form", u"Perf Override", None)) - self.gh3_countoff_label.setText(QCoreApplication.translate("Form", u"Count Off:", None)) + self.ghwt_vocal_cents_label.setText(QCoreApplication.translate("Form", u"Vocal Tuning Cents:", None)) +#if QT_CONFIG(tooltip) + self.ghwt_ska_files_input.setToolTip(QCoreApplication.translate("Form", u"

A folder containing all SKA files used. Gets ignored if it's blank or doesn't exist

", None)) +#endif // QT_CONFIG(tooltip) + self.ghwt_perf_override_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.ghwt_ska_files_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.ghwt_ska_files_label.setText(QCoreApplication.translate("Form", u"SKA Files:", None)) + self.label_3.setText(QCoreApplication.translate("Form", u"Setlist Settings", None)) + self.ghwt_song_script_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.label_7.setText(QCoreApplication.translate("Form", u"In-game Settings", None)) + self.ghwt_vocal_gender_label.setText(QCoreApplication.translate("Form", u"Vocal Gender:", None)) + self.ghwt_midi_file_label.setText(QCoreApplication.translate("Form", u"MIDI File:", None)) + self.ghwt_perf_override_label.setText(QCoreApplication.translate("Form", u"Perf Override:", None)) + self.ghwor_stfs_label.setText(QCoreApplication.translate("Form", u"WoR STFS File", None)) + self.skeleton_types_label.setText(QCoreApplication.translate("Form", u"Skeleton Types", None)) + self.ghwt_vocal_gender_select.setItemText(0, QCoreApplication.translate("Form", u"Male", None)) + self.ghwt_vocal_gender_select.setItemText(1, QCoreApplication.translate("Form", u"Female", None)) + self.ghwt_vocal_gender_select.setItemText(2, QCoreApplication.translate("Form", u"None", None)) + self.ghwt_midi_file_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.ghwt_whammy_cutoff_label.setText(QCoreApplication.translate("Form", u"Sustain Threshold:", None)) +#if QT_CONFIG(tooltip) + self.ghwt_whammy_cutoff.setToolTip(QCoreApplication.translate("Form", u"

The threshold at which sustains appear. In World Tour, the default is 0.50 and it can be read as "If a note is longer than 50% of the current beat's length, it is a sustain."


In GH5 and up, this value is 0.45, meaning that a note only needs to be 45% the length of the distance between 2 beats.


Make the number smaller to have more sustains appear (for lower BPMs), and make it larger to have fewer sustains appear (for higher BPMs).

", None)) +#endif // QT_CONFIG(tooltip) + self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.song_data_tab_wt), QCoreApplication.translate("Form", u"Song Data (WT)", None)) + self.gh3_ska_files_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.gh3_ska_files_label.setText(QCoreApplication.translate("Form", u"SKA Files:", None)) self.gh3_vocal_gender_select.setItemText(0, QCoreApplication.translate("Form", u"Male", None)) self.gh3_vocal_gender_select.setItemText(1, QCoreApplication.translate("Form", u"Female", None)) self.gh3_vocal_gender_select.setItemText(2, QCoreApplication.translate("Form", u"Bret Michaels", None)) self.gh3_vocal_gender_select.setItemText(3, QCoreApplication.translate("Form", u"None", None)) - self.gh3_bassist_select_label.setText(QCoreApplication.translate("Form", u"Bassist:", None)) - self.gh3_midi_file_label.setText(QCoreApplication.translate("Form", u"MIDI File", None)) - self.gh3_perf_override_select.setText(QCoreApplication.translate("Form", u"...", None)) - self.gh3_midi_file_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.gh3_gtr_vol_label.setText(QCoreApplication.translate("Form", u"Guitar:", None)) + self.gh3_band_vol_label.setText(QCoreApplication.translate("Form", u"Band:", None)) #if QT_CONFIG(tooltip) self.gh3_perf_override_input.setToolTip(QCoreApplication.translate("Form", u"Select a qb file with a performance array to override the generated performance array.", None)) #endif // QT_CONFIG(tooltip) + self.gh3_countoff_label.setText(QCoreApplication.translate("Form", u"Count Off:", None)) + self.gh3_perf_override_label.setText(QCoreApplication.translate("Form", u"Perf Override:", None)) + self.gh3_volume_label.setText(QCoreApplication.translate("Form", u"Volume:", None)) + self.gh3_song_script_label.setText(QCoreApplication.translate("Form", u"Song Script:", None)) + self.gh3_midi_file_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.gh3_bassist_select_label.setText(QCoreApplication.translate("Form", u"Bassist:", None)) + self.gh3_song_script_select.setText(QCoreApplication.translate("Form", u"...", None)) + self.gh3_perf_override_select.setText(QCoreApplication.translate("Form", u"...", None)) self.gh3_vocal_gender_label.setText(QCoreApplication.translate("Form", u"Vocal Gender:", None)) + self.gh3_midi_file_label.setText(QCoreApplication.translate("Form", u"MIDI File:", None)) + self.gh3_ska_file_convert_check.setText(QCoreApplication.translate("Form", u"Do not convert SKA Files", None)) self.compile_tabs.setTabText(self.compile_tabs.indexOf(self.song_data_tab_gh3), QCoreApplication.translate("Form", u"Song Data (GH3)", None)) self.compile_settings.setTitle(QCoreApplication.translate("Form", u"Compile Settings", None)) self.beat_8th_high_label.setText(QCoreApplication.translate("Form", u"High:", None)) @@ -1737,7 +1879,7 @@ def retranslateUi(self, Form): self.beat_8th_low_label.setText(QCoreApplication.translate("Form", u"Low:", None)) self.hopo_mode_label.setText(QCoreApplication.translate("Form", u"HOPO Mode", None)) self.hopo_mode_select.setItemText(0, QCoreApplication.translate("Form", u"Rock Band", None)) - self.hopo_mode_select.setItemText(1, QCoreApplication.translate("Form", u"HMX/NS Hybrid", None)) + self.hopo_mode_select.setItemText(1, QCoreApplication.translate("Form", u"Moonscraper", None)) self.hopo_mode_select.setItemText(2, QCoreApplication.translate("Form", u"Guitar Hero 3", None)) self.hopo_mode_select.setItemText(3, QCoreApplication.translate("Form", u"Guitar Hero World Tour+", None)) diff --git a/gui/project_files/compile_package.ui b/gui/project_files/compile_package.ui index f88f840..8e3238c 100644 --- a/gui/project_files/compile_package.ui +++ b/gui/project_files/compile_package.ui @@ -7,7 +7,7 @@ 0 0 562 - 659 + 712 @@ -31,7 +31,7 @@ - 4 + 5 true @@ -86,7 +86,7 @@ - false + true GH3 @@ -102,7 +102,7 @@ - false + true GHA @@ -391,15 +391,8 @@ 9 - - - - true - - - - - + + true @@ -408,35 +401,25 @@ - - + + true - - ... - - - - - - - Qt::Horizontal - - - + + - Bass + Toms Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + true @@ -445,52 +428,32 @@ - - - - Kick - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - - + + - Crowd + Backing Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Snare - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Qt::Horizontal - - - - Qt::Horizontal + + + + true - - + + true @@ -506,8 +469,15 @@ - - + + + + Qt::Horizontal + + + + + true @@ -523,33 +493,57 @@ - - + + + + true + - Vocals + ... - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + true - - + + Qt::Horizontal - - + + - Toms + Bass Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + true + + + ... + + + + + + + true + + + @@ -557,8 +551,22 @@ - - + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + true @@ -577,20 +585,37 @@ - - + + true + + + + - ... + Vocals + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::Horizontal + + + + false + + + + + + + Snare + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -620,31 +645,35 @@ - - - - - 0 - 0 - + + + + true - Track + ... - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + Preview - - - - true + + + + Kick + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + true @@ -653,34 +682,43 @@ - - + + true - - - - true + + + + + 0 + 0 + - ... + Track + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - true + false + + + ... - - + + - Backing + Crowd Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -694,31 +732,13 @@ - - + + Qt::Horizontal - - - - true - - - - - - - - - - - Encrypt Audio - - - @@ -783,6 +803,24 @@ + + + + + + Encrypt Audio + + + + + + + Use Rendered Preview Audio + + + + + @@ -822,13 +860,6 @@ - - - - Qt::Horizontal - - - @@ -839,6 +870,26 @@ + + + + Backing + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Crowd + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -855,40 +906,61 @@ - - + + + + + + P2 is Rhythm + + + + + + + false + + + Separate Co-op Audio + + + + + + + - true + false - - ... + + + + + + false - - + + false + + ... + - - + + - Bass + Rhythm Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - true - - - @@ -899,16 +971,6 @@ - - - - false - - - ... - - - @@ -925,25 +987,28 @@ - - + + true + + ... + - - + + - Backing + Bass Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + true @@ -952,27 +1017,13 @@ - - - - true - + + - ... - - - - - - - false + Backing - - - - - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -993,30 +1044,51 @@ - - - - Rhythm + + + + false - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + true + + + ... - - + + false + + ... + - - - - Crowd + + + + Qt::Horizontal - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + true + + + + + + + true @@ -1030,8 +1102,29 @@ - - + + + + true + + + + + + + Preview + + + + + + + false + + + + + false @@ -1040,45 +1133,21 @@ - - + + + + + + Use Rendered Preview + + + + + + + - Backing - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - P2 is Rhythm - - - - - - - false - - - Separate Co-op Audio - - - - - - - - - - - - - Song Preview: + Song Preview: @@ -1126,6 +1195,13 @@ + + + + Set End Time + + + @@ -1155,10 +1231,48 @@ - - + + + + + + Game Icon + + + + + + + + + + Game Category + + + + + + + + + + Band + + + + + + + - + + + + <html><head/><body><p>Select a file with a performance array to override the generated performance array.</p><p>Can be a text file compatible with the toolkit, or an already compiled qb file.</p></body></html> + + + + -10.000000000000000 @@ -1171,99 +1285,82 @@ - - + + - WoR STFS File + Drum Kit: - - + + + + + - Drum Kit: + Count Off: - - - - Qt::Horizontal + + + + World Tour Definitive Edition Settings - - + + ... - - - - <html><head/><body><p>A folder containing all SKA files used. Gets ignored if it's blank or doesn't exist</p></body></html> + + + + + + + Song Script: - - + + - Male + Hihat01 - Female + Hihat02 - None + Hihat03 + + + + + Sticks_Huge + + + + + Sticks_Normal + + + + + Sticks_Tiny - - - - ... - - - - - - - Vocal Gender: - - - - - - - Song Script: - - - - - - - SKA Files: - - - - - - - - - - ... - - - - + @@ -1382,110 +1479,28 @@ - - - - - Hihat01 - - - - - Hihat02 - - - - - Hihat03 - - - - - Sticks_Huge - - - - - Sticks_Normal - - - - - Sticks_Tiny - - - - - - - - ... - - - - - - - Count Off: + + + + 1.000000000000000 - + Tiers - - - - - - Game Icon - - - - - - - - - - Game Category - - - - - - - - - - Band - - - - - - - - - - - - World Tour Definitive Edition Settings - - - - - + + - Setlist Settings + Vocal Scroll Speed: - + @@ -1565,30 +1580,121 @@ - - + + - Vocal Scroll Speed: + Overall Volume: - - + + + + + + Guitar Mic + + + + + + + Bass Mic + + + + + + + Use New Clips + + + + + + + + + Vocal Tuning Cents: + + + + + + + <html><head/><body><p>A folder containing all SKA files used. Gets ignored if it's blank or doesn't exist</p></body></html> + + + + + + + ... + + + + + + + ... + + + + + + + SKA Files: + + + + + + + Setlist Settings + + + + + ... - + - Skeleton Types + In-game Settings + + + + + + + + + + Qt::Horizontal + + + + Vocal Gender: + + + + + + + MIDI File: + + + @@ -1596,99 +1702,141 @@ - - - - <html><head/><body><p>Select a file with a performance array to override the generated performance array.</p><p>Can be a text file compatible with the toolkit, or an already compiled qb file.</p></body></html> + + + + WoR STFS File - - + + - Overall Volume: + Skeleton Types - - - - 1.000000000000000 + + + + + Male + + + + + Female + + + + + None + + + + + + + + -50 + + + 50 - - + + + + ... + + - - + + - MIDI File: + Sustain Threshold: - - + + + + <html><head/><body><p>The threshold at which sustains appear. In World Tour, the default is 0.50 and it can be read as &quot;If a note is longer than 50% of the current beat's length, it is a sustain.&quot;</p><p><br/></p><p>In GH5 and up, this value is 0.45, meaning that a note only needs to be 45% the length of the distance between 2 beats.</p><p><br/></p><p>Make the number smaller to have more sustains appear (for lower BPMs), and make it larger to have fewer sustains appear (for higher BPMs).</p></body></html> + + + 0.500000000000000 + + + + + + + + + + + + + Song Data (GH3) + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + - In-game Settings + ... - - + + + + SKA Files: + + + + + - - - Guitar Mic - - + + Male + - - - Bass Mic - - + + Female + - - - Use New Clips - - + + Bret Michaels + + + + + None + - + - - - - - - - - - - Song Data (GH3) - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - + @@ -1738,21 +1886,38 @@ - - - - Perf Override + + + + Select a qb file with a performance array to override the generated performance array. - + Count Off: - + + + + Qt::Horizontal + + + + + + + + + + Perf Override: + + + + @@ -1786,89 +1951,75 @@ - - - - - Male - - - - - Female - - - - - Bret Michaels - - - - - None - - - + + - - + + - Bassist: + Volume: - - - - - + + - MIDI File + Song Script: - - + + ... - - + + + + Bassist: + + - - + + ... - - - - Select a qb file with a performance array to override the generated performance array. + + + + + + + ... - + - Volume: + Vocal Gender: - - + + + + + - Vocal Gender: + MIDI File: - - - - Qt::Horizontal + + + + Do not convert SKA Files @@ -2081,7 +2232,7 @@ - HMX/NS Hybrid + Moonscraper @@ -2099,7 +2250,7 @@ - + @@ -2250,11 +2401,11 @@ setEnabled(bool) - 240 - 238 + 330 + 261 - 391 + 541 239 @@ -2270,8 +2421,8 @@ 170 - 96 - 187 + 155 + 213 @@ -2282,12 +2433,12 @@ setEnabled(bool) - 229 - 163 + 512 + 170 - 395 - 186 + 541 + 199 @@ -2298,12 +2449,12 @@ setEnabled(bool) - 238 - 167 + 512 + 170 - 77 - 216 + 136 + 243 @@ -2314,12 +2465,12 @@ setEnabled(bool) - 232 - 161 + 512 + 170 - 396 - 216 + 541 + 227 @@ -2334,8 +2485,8 @@ 170 - 122 - 240 + 181 + 273 @@ -2346,12 +2497,12 @@ setEnabled(bool) - 239 - 163 + 512 + 170 - 393 - 244 + 541 + 255 @@ -2362,12 +2513,12 @@ setEnabled(bool) - 186 - 229 + 330 + 261 - 188 - 250 + 305 + 293 @@ -2378,12 +2529,12 @@ setEnabled(bool) - 84 - 143 + 107 + 180 - 300 - 130 + 376 + 149 @@ -2394,12 +2545,12 @@ setEnabled(bool) - 70 - 143 + 93 + 180 - 391 - 130 + 529 + 166 @@ -2410,12 +2561,12 @@ setEnabled(bool) - 97 - 143 + 120 + 180 - 300 - 158 + 376 + 177 @@ -2426,12 +2577,12 @@ setEnabled(bool) - 105 - 143 + 128 + 180 - 391 - 158 + 529 + 197 @@ -2442,18 +2593,194 @@ setEnabled(bool) - 76 - 158 + 136 + 182 - 240 - 166 + 512 + 170 + + + + + ghwt_rendered_preview_check + toggled(bool) + ghwt_preview_audio_input + setEnabled(bool) + + + 541 + 620 + + + 329 + 425 + + + + + ghwt_rendered_preview_check + toggled(bool) + ghwt_preview_audio_select + setEnabled(bool) + + + 541 + 620 + + + 541 + 424 + + + + + ghwt_rendered_preview_check + toggled(bool) + preview_minutes + setDisabled(bool) + + + 541 + 620 + + + 139 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + preview_seconds + setDisabled(bool) + + + 541 + 620 + + + 188 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + preview_mills + setDisabled(bool) + + + 541 + 620 + + + 241 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + length_minutes + setDisabled(bool) + + + 541 + 620 + + + 339 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + length_seconds + setDisabled(bool) + + + 541 + 620 + + + 388 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + length_mills + setDisabled(bool) + + + 541 + 620 + + + 441 + 513 + + + + + ghwt_rendered_preview_check + toggled(bool) + ghwt_set_end + setDisabled(bool) + + + 541 + 620 + + + 541 + 511 + + + + + gh3_rendered_preview_check + toggled(bool) + gh3_preview_audio_input + setEnabled(bool) + + + 88 + 362 + + + 115 + 335 + + + + + gh3_rendered_preview_check + toggled(bool) + gh3_preview_audio_select + setEnabled(bool) + + + 453 + 355 + + + 526 + 330 - + diff --git a/midqb_gen/MidQbGen.py b/midqb_gen/MidQbGen.py index 09dcbbd..dc58f92 100644 --- a/midqb_gen/MidQbGen.py +++ b/midqb_gen/MidQbGen.py @@ -8,11 +8,15 @@ sys.path.append("..\\pak_extract") - +sys.path.append("..\\ska_converter") +root_folder = os.path.realpath(os.path.dirname(__file__)) from pak_extract.pak_functions import createHeaderDict from pak_extract.QB2Text import convert_qb_file, qb_bytes, print_qb_text_file from pak_extract.Text2QB import main as t2q_main +from ska_converter.ska_classes import ska_bytes, lipsync_dict +from ska_converter.ska_functions import make_modern_ska, make_gh3_ska + tbp = 480 @@ -27,104 +31,167 @@ def make_mid(midfile, hopo, filename = "", *args, **kwargs): headerDict = createHeaderDict(filename) # print(headerDict) + xplus = 0 if "ghwt" in args: qb_dict = parse_wt_qb(mid, hopo, *args) - if "replace_perf" in args: - perf_file = args[args.index("replace_perf") + 1] - - if perf_file.endswith(".txt"): - with open(perf_file, "r") as f: - perf_qb = f.read().replace("song_performance", f"{filename}_performance") - if "qb_file = " not in perf_qb: - perf_qb = f"qb_file = songs/{filename}.mid.qb\n" + perf_qb - perf_qb = t2q_main(perf_qb, game = "GHWT") - else: - with open(perf_file, "rb") as f: - perf_qb = f.read() - qb_sections = convert_qb_file(qb_bytes(perf_qb), filename, headerDict, - "PC") - for x in qb_sections: - if "_performance" in x.section_id and not "notes" in x.section_id: - qb_dict["performance"] = x - '''if qb_dict["performance_mid"]: - sorted_section = {} - for y in qb_dict["performance"].section_data: - sorted_section[y.data_value[0].data_value] = y - qb_dict["performance"].section_data += qb_dict["performance_mid"]''' - break - QBSections, midQS = create_wt_qb_sections(qb_dict, filename, *args) - midQB = create_wt_qb(QBSections, filename) - if "performance" in qb_dict: - midQB = convert_qb_file(qb_bytes(midQB), filename, headerDict, "PC") - for x in midQB: - if "_performance" in x.section_id and not "notes" in x.section_id: - if x.section_data == [0, 0]: - x.array_node_type = qb_dict["performance"].array_node_type - x.section_data = qb_dict["performance"].section_data - else: - x.section_data += qb_dict["performance"].section_data - cam_list = list(qb_dict["anim"]["CAMERAS"].keys()) - sorted_section = {} - for y in x.section_data: - event_time = y.data_value[0].data_value - if re.search(r'Band_PlayLoop', y.data_value[1].data_value, flags = re.IGNORECASE): - closest_value = min(cam_list, key=lambda x: abs(y.data_value[0].data_value - x)) - if event_time != closest_value and abs(event_time - closest_value) < 1000: - event_time = closest_value - y.data_value[0].data_value = event_time - if event_time in sorted_section: - sorted_section[event_time].append(y) - else: - sorted_section[event_time] = [y] - sorted_section = dict(sorted(sorted_section.items())) - new_perf = [] - for z in sorted_section: - new_perf.extend(sorted_section[z]) - x.section_data = new_perf - break - result = StringIO() - orig_stdout = sys.stdout - sys.stdout = result - print_qb_text_file(midQB) - sys.stdout = orig_stdout - qb_text = result.getvalue() - midQB = t2q_main(qb_text, game = "GHWT") - # print() + midQB, midQS = make_wt_files(headerDict, qb_dict,filename, *args) else: - midParsed = parseGH3QB(mid, hopo) - midQB = makeMidQB(midParsed, filename, headerDict, consoleType) - """with open(f"{filename}_song.mid.qb", 'wb') as f: - f.write(midQB)""" + # midParsed = parseGH3QB(mid, hopo) + qb_dict = parse_gh3_qb(mid, hopo, *args) + if "replace_perf" in args: + override_perf(filename, headerDict, qb_dict, *args) + qb_sections = create_gh3_sections(qb_dict, filename, headerDict, consoleType) + midQB = create_game_qb(qb_sections, filename, "GH3") + if "performance" in qb_dict: + midQB = add_perf_to_qb(midQB, filename, headerDict, qb_dict, *args) if "qb" in args: pakFile = {"qb": midQB, "qs": midQS} else: - to_pak = [[midQB, f"songs\\{filename}.mid.qb"]] - if midQS: - to_pak.append([midQS, f"songs\\{filename}.mid.qs"]) - if "add_ska" in args: - for files in os.listdir(args[args.index("add_ska") + 1]): - to_pak.append([open(f"{args[args.index('add_ska') + 1]}\\{files}", 'rb').read(), files]) - if "song_script" in args: - song_script_file = args[args.index("song_script") + 1] + pakFile = create_pak_file(midQB, filename, midQS, *args) + + if xplus: + x_filename = f"{filename}xplus" + xplusQB = make_wt_files(headerDict, xplus, x_filename, *args)[0] + if "performance" in xplus: + xplusQB = add_perf_to_qb(xplusQB, x_filename, headerDict, xplus, *args) + xplusPAK = create_pak_file(xplusQB, x_filename, midQS, *args) + return pakFile, xplusPAK + else: + return pakFile, filename + +def make_wt_files(headerDict, qb_dict, filename, *args): + if "replace_perf" in args: + override_perf(filename, headerDict, qb_dict, "GHWT", *args) + QBSections, midQS = create_wt_qb_sections(qb_dict, filename, *args) + midQB = create_game_qb(QBSections, filename) + return midQB, midQS + +def add_perf_to_qb(midQB, filename, headerDict, qb_dict, *args): + midQB = convert_qb_file(qb_bytes(midQB), filename, headerDict, "PC") + for x in midQB: + if "_performance" in x.section_id and not "notes" in x.section_id: + if x.section_data == [0, 0]: + x.array_node_type = qb_dict["performance"].array_node_type + x.section_data = qb_dict["performance"].section_data + else: + x.section_data += qb_dict["performance"].section_data + cam_list = list(qb_dict["anim"]["CAMERAS"].keys()) + sorted_section = {} + for y in x.section_data: + event_time = y.data_value[0].data_value + if re.search(r'Band_PlayLoop', y.data_value[1].data_value, flags=re.IGNORECASE): + closest_value = min(cam_list, key=lambda x: abs(y.data_value[0].data_value - x)) + if event_time != closest_value and abs(event_time - closest_value) < 1000: + event_time = closest_value + y.data_value[0].data_value = event_time + if event_time in sorted_section: + sorted_section[event_time].append(y) + else: + sorted_section[event_time] = [y] + sorted_section = dict(sorted(sorted_section.items())) + new_perf = [] + for z in sorted_section: + new_perf.extend(sorted_section[z]) + x.section_data = new_perf + break + result = StringIO() + orig_stdout = sys.stdout + sys.stdout = result + print_qb_text_file(midQB) + sys.stdout = orig_stdout + qb_text = result.getvalue() + if "ghwt" in args: + game = "GHWT" + else: + game = "GH3" + midQB = t2q_main(qb_text, game=game) + return midQB + +def song_script_check(song_script_raw, filename): + song_script = "" + if not "qb_file = " in song_script_raw: + song_script = f"qb_file = songs/{filename}_song_scripts.qb\n" + if not "_song_startup = " in song_script_raw: + song_script += f'script {filename}_song_startup = "29 b6 24 06 00 00 00 09 00 00 00 09 01 16 f8 2d 15 1a 2c 01 24"\n' + song_script += song_script_raw + if not f"_anim_struct_{filename}" in song_script: + song_script = song_script.replace("anim_struct", f"anim_struct_{filename}") + song_script = t2q_main(song_script, game="GHWT") + return song_script + +def create_pak_file(midQB, filename, midQS = "", *args): + to_pak = [[midQB, f"songs\\{filename}.mid.qb"]] + if midQS: + to_pak.append([midQS, f"songs\\{filename}.mid.qs"]) + if "song_script" in args: + song_script_file = args[args.index("song_script") + 1] + if os.path.exists(song_script_file): if song_script_file.endswith(".txt"): - song_script = "" with open(song_script_file, "r") as f: - song_script_raw = f.read() - if not "qb_file = " in song_script_raw: - song_script = f"qb_file = songs/{filename}_song_scripts.qb\n" - if not "_song_startup = " in song_script_raw: - song_script += f'script {filename}_song_startup = "29 b6 24 06 00 00 00 09 00 00 00 09 01 16 f8 2d 15 1a 2c 01 24"\n' - song_script += song_script_raw - if not f"_anim_struct_{filename}" in song_script: - song_script = song_script.replace("anim_struct", f"anim_struct_{filename}") - song_script = t2q_main(song_script, game = "GHWT") + song_script = song_script_check(f.read(), filename) else: with open(song_script_file, "rb") as f: song_script = f.read() - to_pak.append([song_script, f"songs\\{filename}_song_scripts.qb"]) - pakFile = pakMaker(to_pak) + else: + try: + song_script = song_script_check(song_script_file, filename) + except Exception as e: + raise e + to_pak.append([song_script, f"songs\\{filename}_song_scripts.qb"]) + if "add_ska" in args: + ska_files = args[args.index("add_ska") + 1] + if type(ska_files) == list: + to_pak.extend(ska_files) + elif os.path.exists(ska_files): + for files in os.listdir(ska_files): + with open(f"{args[args.index('add_ska') + 1]}\\{files}", 'rb') as f: + ska_file = f.read() + if "gh3" in args: + ska_file = ska_bytes(ska_file) + ska_file = make_modern_ska(ska_file) + ska_file = ska_bytes(ska_file) + if "gha" in args: + to_ska = "gha_singer" + else: + to_ska = "gh3_singer" + ska_file = make_gh3_ska(ska_file, quats_mult=0.5, ska_switch=to_ska) + to_pak.append([ska_file, files]) - return pakFile, filename + else: + raise Exception("Could not find SKA files.") + if "add_loops" in args: + for file in args[args.index("add_loops") + 1]: + with open(f"{root_folder}\\..\\conversion_files\\anim_loops_wt\\{file}.ska.xen", 'rb') as f: + to_pak.append([f.read(), f"{file}.ska"]) + pakFile = pakMaker(to_pak) + return pakFile + +def override_perf(filename, headerDict, qb_dict, game, *args): + perf_file = args[args.index("replace_perf") + 1] + if os.path.isfile(perf_file): + if perf_file.endswith(".txt"): + with open(perf_file, "r") as f: + perf_qb = f.read().replace("song_performance", f"{filename}_performance") + if "qb_file = " not in perf_qb: + perf_qb = f"qb_file = songs/{filename}.mid.qb\n" + perf_qb + perf_qb = t2q_main(perf_qb, game=game) + else: + with open(perf_file, "rb") as f: + perf_qb = f.read() + else: + try: + perf_qb = perf_file.replace("song_performance", f"{filename}_performance") + if "qb_file = " not in perf_qb: + perf_qb = f"qb_file = songs/{filename}.mid.qb\n" + perf_qb + perf_qb = t2q_main(perf_qb, game=game) + except Exception as e: + raise e + qb_sections = convert_qb_file(qb_bytes(perf_qb), filename, headerDict, + "PC") + for x in qb_sections: + if "_performance" in x.section_id and not "notes" in x.section_id: + qb_dict["performance"] = x + break if __name__ == "__main__": consoleType = 1 # 0 for Wii, 1 for PC, 2 for 360, 3 for XBX, 4 for PS2, 5 for WPC diff --git a/midqb_gen/midqb_classes.py b/midqb_gen/midqb_classes.py index 536b0ad..25049a5 100644 --- a/midqb_gen/midqb_classes.py +++ b/midqb_gen/midqb_classes.py @@ -100,20 +100,22 @@ def __init__(self, time, prevNote, currTempChange, colour): setattr(self, colour, 1) class NoteChart: - def __init__(self, part, diff): + def __init__(self, part, diff, gh3 = False): self.part = part self.diff = diff self.notes = [] + self.gh3 = gh3 def __str__(self): - return f"Part {self.part if self.part != '' else 'Guitar'}, {self.diff}, {int(len(self.notes)/2)} in chart" + divisor = 3 if self.gh3 else 2 + return f"Part {self.part if self.part != '' else 'Guitar'}, {self.diff}, {int(len(self.notes)/divisor)} in chart" class AnimNote: - def __init__(self, time, note): + def __init__(self, time, note, length = 1): self.time = time self.note = note - self.length = 1 + self.length = length def __str__(self): return f"Anim note {self.note} at {self.time} lasting {self.length}" diff --git a/midqb_gen/midqb_definitions.py b/midqb_gen/midqb_definitions.py index cf20c3f..4bbad31 100644 --- a/midqb_gen/midqb_definitions.py +++ b/midqb_gen/midqb_definitions.py @@ -55,6 +55,12 @@ percussion_start = ["[clap_start]", "[cowbell_start]", "[tambourine_start]"] percussion_end = ["[clap_end]", "[cowbell_end]", "[tambourine_end]"] +singer_stances = ["Stance_A", "Stance_B"] +singer_anims = ["special", "jump", "kick", "release", "long_note"] +guit_stances = singer_stances + ["Stance_C"] +guit_anims = ["jump", "kick", "solo", "special"] +gha_guit_stances = guit_stances + ["Stance_D"] + venue_states = ["[verse]", "[chorus]", "[solo]"] valid_camera_notes_gh3 = [77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, @@ -67,27 +73,31 @@ valid_camera_notes_wt = [0, 1, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 28, 29, 30, 31, 33, 34, 35, 36, 38, 40, 41, 43, 45, 46, 48] -valid_camera_notes_wor = [50, 51, 52, 53, 54, 55, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 74, 75, 90, 91, +valid_camera_notes_wor = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] -valid_camera_notes_wtde = [50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 74, 75, 81, +moment_cams = [3, 4, 5, 6, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] + +valid_camera_notes_wtde = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 81, 82, 83, 84, 85, 86, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126] valid_lightshow_notes_gh3 = [39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 56, 57, 58, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76] + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 107] valid_lightshow_notes_wt = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 87, 88, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105] + 98, 99, 100, 101, 102, 103, 104, 105, 107] -valid_lightshow_notes_wor = [19, 107] +valid_lightshow_notes_wor = [19] valid_crowd_gh3 = [72, 73, 74, 75, 76, 77, 78, 79, 80] valid_crowd_wt = [82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95, 96] +venue_track = "LIGHTSHOWCAMERASCROWD" + gh5_camera_dict = { # This dict is to grab camera cuts to add to a gh5-style perf file 33: 8, 34: 13, @@ -96,7 +106,8 @@ } anims_guitar = [] # MIDI Note 127-118 is Lead left hand anims, 110-101 is Bass/Rhythm left hand notes -stances = ["Stance_A", "Stance_B", "Stance_C", "jump", "special", "Solo", "kick"] + + drumKeyMapGH3 = { # More FYI, but may use this dict in later code 36: "Kick_R", # Budokan and Hell venues only 37: "Tom_3_L", @@ -144,12 +155,13 @@ 34: 44, 32: 50, 31: 53, - 30: 41, + 30: 42, 29: 52, 28: 40, 27: 52, 26: 40, 24: 48, + 23: 36, 22: 70 } @@ -221,33 +233,35 @@ 59: 97, 58: 102, 57: 101, - 48: 79, + 48: 87, 46: 113, - 45: 112, - 43: 79, + 45: 111, + 43: 91, 38: 79, + 36: 97, + 35: 89, 34: 104, - 33: 90, - 31: 79, + 33: 81, + 31: 87, 30: 85, 29: 94, 28: 114, - 26: 105, - 25: 105, + 26: 89, + 25: 89, 24: 87, 23: 89, - 21: 105, - 20: 105, + 21: 97, + 20: 97, 19: 110, 18: 97, 16: 88, - 15: 101, + 15: 88, 14: 103, 13: 104, - 11: 105, - 10: 106, + 11: 79, + 10: 79, 9: 87, - 8: 95, + 8: 95 }, "gha": { @@ -324,6 +338,12 @@ 59: 84 } +anim_maps_gh3 = { + "Guitar": leftHandGtr_gh3, + "Bass": leftHandBass_gh3, + "Rhythm": leftHandMappingRhythm +} + note_mapping_gh3 = { 60: 'Easy_green', 61: 'Easy_red', @@ -531,7 +551,7 @@ sp_note = 116 fo_star_note = 107 -playable_range = range(36, 85) +playable_range = [26] + list(range(36, 85)) phrase_marker = [105, 106] freeform_marker = tap_note diff --git a/midqb_gen/midqb_functions.py b/midqb_gen/midqb_functions.py index ccb7289..87bac0c 100644 --- a/midqb_gen/midqb_functions.py +++ b/midqb_gen/midqb_functions.py @@ -12,7 +12,7 @@ sys.path.append("..\\pak_extract") from pak_extract.Text2QB import basic_data, struct_data, assign_data, assign_types, create_qb -from pak_extract.pak_functions import make_script_struct, round_time, round_cam_len +from pak_extract.pak_functions import make_script_struct, round_time, round_cam_len, new_stance_gh3 sys.path.append("..\\") import CRC @@ -32,9 +32,8 @@ def create_wt_qb_sections(qb_dict, filename, *args): if "wtde" in args: parsing.append(qb_dict["solo_markers"]) - - - qb_sections[f"{filename}_timesig"] = basic_data(f"{filename}_timesig", [[x.time, x.numerator, x.denominator] for x in qb_dict["timesigs"]]) + qb_sections[f"{filename}_timesig"] = basic_data(f"{filename}_timesig", + [[x.time, x.numerator, x.denominator] for x in qb_dict["timesigs"]]) qb_sections[f"{filename}_fretbars"] = basic_data(f"{filename}_fretbars", qb_dict["fretbars"]) no_diff = ["tap", "fo_star_note", "face_off"] @@ -134,7 +133,7 @@ def create_wt_qb_sections(qb_dict, filename, *args): bin_len = bin(anim_stuff.length)[2:].zfill(16) bin_mid_note = bin(anim_stuff.note)[2:].zfill(8) bin_velocity = bin(anim_stuff.velocity)[2:].zfill(8) - note_val = int(bin_velocity+bin_mid_note+bin_len, 2) + note_val = int(bin_velocity + bin_mid_note + bin_len, 2) anim_list.append(timing) anim_list.append(note_val) qb_sections[chart_name] = basic_data(chart_name, anim_list.copy()) @@ -145,7 +144,7 @@ def create_wt_qb_sections(qb_dict, filename, *args): qb_sections[chart_name] = basic_data(chart_name, anim_list) else: qb_sections[chart_name] = basic_data(chart_name, []) - #"performance", + # "performance", for temp in ["song_vocals", "vocals_freeform", "vocals_phrases", "vocals_note_range", "lyrics", "vocals_markers"]: chart_name = f"{filename}_{temp}" try: @@ -170,6 +169,9 @@ def create_wt_qb_sections(qb_dict, filename, *args): if "wor" in args and qb_dict["ghost_notes"]: chart_name = f"{filename}_ghost_notes" qb_sections[chart_name] = basic_data(chart_name, qb_dict["ghost_notes"]) + if "wtde" in args and qb_dict["xplus"]: + chart_name = f"{filename}_song_drums_expertplus" + qb_sections[chart_name] = basic_data(chart_name, qb_dict["xplus"].notes) if qb_dict["has_2x_kick"]: chart_name = f"{filename}_double_kick" @@ -185,10 +187,13 @@ def create_wt_qb_sections(qb_dict, filename, *args): return qb_sections, qs_file -def create_wt_qb(qb_sections, filename): - qb_list = [value for key, value in qb_sections.items()] - assign_types(qb_list, game="GHWT") - qb_file = create_qb(qb_list, "Section", f"songs/{filename}.mid.qb", game = "GHWT") +def create_game_qb(qb_sections, filename, game="GHWT"): + if type(qb_sections) == dict: + qb_list = [value for key, value in qb_sections.items()] + else: + qb_list = qb_sections + assign_types(qb_list, game=game) + qb_file = create_qb(qb_list, "Section", f"songs/{filename}.mid.qb", game=game) return qb_file @@ -281,11 +286,15 @@ def mod_notes(time_array, compare): return np.where((time_array >= compare[0]) & (time_array < compare[1])) -def make_bin_notes(gem): +def set_gems(gem): notes_bin = 0 for temp_colour in gem["colours"]: notes_bin += 1 << temp_colour + return notes_bin + +def make_bin_notes(gem): + notes_bin = set_gems(gem) return bin(notes_bin)[2:].zfill(7) @@ -316,7 +325,8 @@ def parse_fretbars(timeSigs, end_event_ticks, changes, ticksArray, mid): return fretbars -def active_note_check(x, active_notes, timeSec, midi_track, track): # midi_track is usually anim_notes + +def active_note_check(x, active_notes, timeSec, midi_track, track): # midi_track is usually anim_notes if x.velocity != 0: if x.note in active_notes: return -1 @@ -335,13 +345,59 @@ def active_note_check(x, active_notes, timeSec, midi_track, track): # midi_track raise Exception(f"Something went wrong parsing the {track.name} track.") return 0 + +def create_diff_dicts(): + play_notes = { + "Easy": [], + "Medium": [], + "Hard": [], + "Expert": [] + } + + active_notes = { + "Easy": {}, + "Medium": {}, + "Hard": {}, + "Expert": {} + } + forced_notes = { + "Easy": {"off": [], "on": []}, + "Medium": {"off": [], "on": []}, + "Hard": {"off": [], "on": []}, + "Expert": {"off": [], "on": []} + } + play_face_off = {"P1": [], "P2": []} + play_star = { + "Easy": [], + "Medium": [], + "Hard": [], + "Expert": [] + } + play_star_bm = { + "Easy": [], + "Medium": [], + "Hard": [], + "Expert": [] + } + play_star_fo = { + "Easy": [], + "Medium": [], + "Hard": [], + "Expert": [] + } + return play_notes, active_notes, forced_notes, play_face_off, play_star, play_star_bm, play_star_fo + + +def split_list(l_in): + return [l_in[i:i + 2] for i in range(0, len(l_in), 2)] + + def parse_wt_qb(mid, hopo, *args, **kwargs): changes, ticks = tempMap(mid) if "force_only" in args: time_hopos = False else: time_hopos = True - split_list = lambda l_in: [l_in[i:i + 2] for i in range(0, len(l_in), 2)] global sp_note if "star_power" in kwargs: @@ -356,6 +412,8 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): ticksArray = np.array(ticks) changesArray = np.array(changes) + xplus_chart = False + make_xplus = False ghost_notes = {} has_2x_kick = False @@ -433,14 +491,6 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): valid_anims["LIGHTSHOW"].extend(valid_lightshow_notes_wor) valid_anims["CAMERAS"].extend(valid_camera_notes_wtde) - - venue_track = "LIGHTSHOWCAMERASCROWD" - - leftHandAnims = { - "Guitar": [], - "Bass": [] - } - anim_notes = { "scripts_notes": {}, "left_hand": {}, @@ -459,8 +509,6 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): "performance": [] } - - fo_star_power = { "Guitar": [], "Bass": [], @@ -484,9 +532,10 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): vocals_freeform = { 0: [], 1: [] - } # 0 is freeform, 1 is hype + } # 0 is freeform, 1 is hype vocals_freeform_playable = [] - vocals_phrases = {105: [], 106: []} # 105 is star power, 106 is bm star power# Type 0 is blank, playable phrases alternate between 1 and 2 + vocals_phrases = {105: [], + 106: []} # 105 is star power, 106 is bm star power# Type 0 is blank, playable phrases alternate between 1 and 2 vocals_phrase_dict = {0: {}} # 3 exists too. Maybe to do with face-off? vocals_lyrics = {} @@ -518,50 +567,18 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): drums = False elif re.search(r'(drums)', instrument, flags=re.IGNORECASE): inst_map = note_mapping_gh3 | forceMapping | note_mapping_wt_drum + if "2x_check" in args: + inst_map.pop(96) + inst_map[95] = 'Expert_purple' + xplus_chart = True + drums = True else: inst_map = note_mapping_gh3 | forceMapping | note_mapping_wt_drum drums = False - play_vox = [] + play_notes, active_notes, forced_notes, play_face_off, play_star, play_star_bm, play_star_fo = create_diff_dicts() - play_notes = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - active_notes = { - "Easy": {}, - "Medium": {}, - "Hard": {}, - "Expert": {} - } - forced_notes = { - "Easy": {"off": [], "on": []}, - "Medium": {"off": [], "on": []}, - "Hard": {"off": [], "on": []}, - "Expert": {"off": [], "on": []} - } - play_face_off = {"P1": [], "P2": []} - play_star = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - play_star_bm = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - play_star_fo = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } play_tap = [] play_solo = [] @@ -581,27 +598,32 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): for y, x in enumerate(track): time += x.time currChange = changes[len(ticksArray[ticksArray <= time]) - 1] # time, tempo, avgTempo - timeSec = timeInSecs(currChange, mid, time) + time_sec = timeInSecs(currChange, mid, time) if track_num == 0: if x.type == "time_signature": - timeSigs.append(timeSigEvent(timeSec, x.numerator, x.denominator)) + timeSigs.append(timeSigEvent(time_sec, x.numerator, x.denominator)) elif track.name == "EVENTS": if x.type == 'text': - end_event_secs, end_event_ticks = process_text_event(x, markers, time, timeSec) + end_event_secs, end_event_ticks = process_text_event(x, markers, time, time_sec) if end_event_secs and end_event_ticks: continue - elif re.search(track.name, venue_track, flags=re.IGNORECASE): - track.name = track.name.upper() + elif re.search(track.name, rf'{venue_track}|cameras_wt', flags=re.IGNORECASE): + if track.name == "cameras_wt": + track.name = "CAMERAS" + else: + track.name = track.name.upper() valid_check = valid_anims[track.name] if x.type == "note_on" or x.type == "note_off": if x.note in valid_check: - if x.velocity != 0: + create_anim_note(x, active_notes, track.name, anim_notes, time_sec, + AnimNoteWT(time_sec, x.note, x.velocity)) + '''if x.velocity != 0: if x.note in active_notes: continue - active_notes[x.note] = AnimNoteWT(timeSec, x.note, x.velocity) + active_notes[x.note] = AnimNoteWT(time_sec, x.note, x.velocity) else: try: - active_notes[x.note].setLength(timeSec - active_notes[x.note].time) + active_notes[x.note].setLength(time_sec - active_notes[x.note].time) if active_notes[x.note].time in anim_notes[track.name]: anim_notes[track.name][active_notes[x.note].time].append(active_notes[x.note]) else: @@ -610,21 +632,22 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): except KeyError: pass except: - raise Exception(f"Something went wrong parsing the {track.name} track.") + raise Exception(f"Something went wrong parsing the {track.name} track.")''' elif x.type == "text": if re.search(r'^(SetBlendTime|LightShow_Set)', x.text, flags=re.IGNORECASE): - # if x.text.startswith("SetBlendTime") or x.text.startswith("LightShow_Set"): - blendtime = {"param": "time", "data": float(x.text.split(" ")[1]), "type": "Float"} # Blend time is in seconds - anim_notes["lightshow"].append(scriptsNode(timeSec, "LightShow_SetTime", [blendtime])) - elif re.search(r'(zoom_(in|out)_(quick|slow)_(small|large)|pulse[1-5]) [0-9]+', x.text, flags = re.IGNORECASE): + # if x.text.startswith("SetBlendTime") or x.text.startswith("LightShow_Set"): + blendtime = lightshow_script(x) + anim_notes["lightshow"].append(scriptsNode(time_sec, "LightShow_SetTime", [blendtime])) + elif re.search(r'(zoom_(in|out)_(quick|slow)_(small|large)|pulse[1-5]) [0-9]+', x.text, + flags=re.IGNORECASE): zoom_type = x.text.split(" ")[0] zoom_length = float(x.text.split(" ")[1]) if zoom_length.is_integer(): zoom_length = int(zoom_length) - param_type = {"param": "type", "data": zoom_type, "type": "QbKey"} - param_time = {"param": "time", "data": zoom_length, "type": "Integer" if type(zoom_length) == int else "Float"} - anim_notes["performance"].append(scriptsNode(timeSec, "CameraCutsEffect_FOVPulse", [param_type, param_time])) + param_type, param_time = camera_script(zoom_type, zoom_length) + anim_notes["performance"].append( + scriptsNode(time_sec, "CameraCutsEffect_FOVPulse", [param_type, param_time])) elif playable: if re.search(r'(guitar|bass|drums)', instrument, flags=re.IGNORECASE): if x.type == "note_on" or x.type == "note_off": @@ -632,7 +655,7 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): chart_diff = inst_map[x.note].split("_")[0] note_colour = inst_map[x.note].split("_")[1] if note_colour not in ["off", "on"]: - to_add = {"time_sec": timeSec, "time_tick": time, "curr_change": currChange, + to_add = {"time_sec": time_sec, "time_tick": time, "curr_change": currChange, "velocity": x.velocity} if note_colour in active_notes[chart_diff]: temp = active_notes[chart_diff] @@ -641,35 +664,34 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): active_notes[chart_diff][note_colour] = [to_add] if all([drums, x.note >= 97, x.velocity == 1]): ghost = (1 << note_bit_vals[note_colour]) - if timeSec in ghost_notes: - if not ghost & ghost_notes[timeSec]: - ghost_notes[timeSec] += ghost + if time_sec in ghost_notes: + if not ghost & ghost_notes[time_sec]: + ghost_notes[time_sec] += ghost else: - ghost_notes[timeSec] = ghost - + ghost_notes[time_sec] = ghost else: - forced_notes[chart_diff][note_colour].append(timeSec) + forced_notes[chart_diff][note_colour].append(time_sec) elif x.note == sp_note: - star_power[instrument].append(timeSec) + star_power[instrument].append(time_sec) elif x.note == bm_star_note: - bm_star_power[instrument].append(timeSec) + bm_star_power[instrument].append(time_sec) elif x.note == fo_star_note: - fo_star_power[instrument].append(timeSec) + fo_star_power[instrument].append(time_sec) elif x.note in faceOffMapping: - play_face_off[faceOffMapping[x.note]].append(timeSec) + play_face_off[faceOffMapping[x.note]].append(time_sec) elif x.note == tap_note: - play_tap.append(timeSec) + play_tap.append(time_sec) elif x.note == solo_note: - play_solo.append(timeSec) + play_solo.append(time_sec) elif x.note in leftHandGtr_wt and not drums: new_note = anim_maps[instrument][x.note] - if x.velocity != 0: + if x.velocity != 0 and x.type == "note_on": if new_note in active_notes: continue - active_notes[new_note] = AnimNoteWT(timeSec, new_note, x.velocity) + active_notes[new_note] = AnimNoteWT(time_sec, new_note, x.velocity) else: try: - active_notes[new_note].setLength(timeSec - active_notes[new_note].time) + active_notes[new_note].setLength(time_sec - active_notes[new_note].time) if active_notes[new_note].time in anim_notes["left_hand"]: anim_notes["left_hand"][active_notes[new_note].time].append( active_notes[new_note]) @@ -687,10 +709,11 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): if x.velocity != 0 and x.type == "note_on": if new_note in active_notes: continue - active_notes[new_note] = AnimNoteWT(timeSec, new_note, x.velocity) + active_notes[new_note] = AnimNoteWT(time_sec, new_note, x.velocity) else: try: - new_len = timeSec - active_notes[new_note].time + drum_anim_note(x, active_notes, new_note, anim_notes, time_sec, args) + '''new_len = time_sec - active_notes[new_note].time active_notes[new_note].setLength(new_len) if active_notes[new_note].time in anim_notes["drum_anims"]: anim_notes["drum_anims"][active_notes[new_note].time].append( @@ -708,10 +731,10 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): active_notes.pop(new_note) continue else: - practice_note = new_note-13 - temp.append(AnimNoteWT(timeSec, practice_note, temp[-1].velocity)) + practice_note = new_note - 13 + temp.append(AnimNoteWT(time_sec, practice_note, temp[-1].velocity)) temp[-1].setLength(new_len) - active_notes.pop(new_note) + active_notes.pop(new_note)''' except KeyError as E: # raise E pass @@ -720,125 +743,60 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): raise Exception(f"Something went wrong parsing the {track.name} track.") elif x.note == 25 and drums: - open_hh.append(timeSec) + open_hh.append(time_sec) + if all([x.note == 95, drums, "wtde" in args, not make_xplus]): + make_xplus = True # if x.note in faceOffMapping: elif x.type == "sysex": sys_head = x.data[:3] if not sys_head == (80, 83, 0): continue if x.data[5] == 4 and x.data[4] in [3, 127]: - sysex_taps.append(timeSec) + sysex_taps.append(time_sec) elif x.data[5] == 1 and x.data[4] != 127: try: - sysex_opens[x.data[4]].append(timeSec) + sysex_opens[x.data[4]].append(time_sec) if not sysex_opens_check: sysex_opens_check = 1 except: - print(f"{track.name}: Invalid SysEx event found at {timeSec}") + print(f"{track.name}: Invalid SysEx event found at {time_sec}") # print() - else: # vocals. Note range 36-84 inclusive probably + else: # vocals. Note range 36-84 inclusive probably if x.type == "lyrics": - vocals_lyrics[timeSec] = x.text + vocals_lyrics[time_sec] = x.text elif x.type == "text": if "[" not in x.text: - vocals_lyrics[timeSec] = x.text + vocals_lyrics[time_sec] = x.text elif x.type == "note_on" or x.type == "note_off": if x.note in playable_range: if x.note in vocals_notes: - vocals_notes[x.note].append(timeSec) + vocals_notes[x.note].append(time_sec) else: - vocals_notes[x.note] = [timeSec] + vocals_notes[x.note] = [time_sec] elif x.note in phrase_marker: - vocals_phrases[x.note].append(timeSec) + vocals_phrases[x.note].append(time_sec) elif x.note == freeform_marker: - vocals_freeform[x.channel].append(timeSec) + vocals_freeform[x.channel].append(time_sec) elif x.note == sp_note: - vocal_sp.append(timeSec) - + vocal_sp.append(time_sec) if time > last_event_tick: last_event_tick = time - last_event_secs = timeSec + last_event_secs = time_sec if playable and not re.search(r'(vocal)', instrument, flags=re.IGNORECASE): if sysex_taps and not play_tap: play_tap = sysex_taps star_power[instrument] = split_list(star_power[instrument]) bm_star_power[instrument] = split_list(bm_star_power[instrument]) fo_star_power[instrument] = split_list(fo_star_power[instrument]) - time_array_diffs = { - } for player, fo in play_face_off.items(): if fo: play_face_off[player] = split_list(fo) for diff, notes in active_notes.items(): - timestamps = {} - for colour, times in notes.items(): - if colour == "2x": - continue - note_bit = note_bit_vals[colour] - colour_time = [] - for count, playtime in enumerate(times): - if count % 2 == 0: - colour_time.append(playtime) - else: - if colour_time[-1]["time_sec"] in timestamps: - s_time = colour_time[-1]["time_sec"] - test_colour = timestamps[s_time] - t_time = colour_time[-1]["time_tick"] - test_colour["colours"].append(note_bit) - test_colour["colour_name"].append(colour) - temp_len = playtime["time_sec"] - s_time - temp_other_len = test_colour["length_sec"] - if drums: - if colour_time[-1]["velocity"] == 127: - timestamps[s_time]["accents"].append(note_bit) - if temp_len != test_colour["length_sec"]: - if colour == "purple": - test_colour["length_sec_kick"] = temp_len - elif 5 in test_colour["colours"]: - test_colour["length_sec_kick"] = test_colour["length_sec"] - test_colour["length_sec"] = temp_len - else: - temp_len_t = playtime["time_tick"] - t_time - other_temp_t = timestamps[s_time]["length_tick"] - timestamps[s_time]["length_sec"] = min(temp_len, temp_other_len) - timestamps[s_time]["length_tick"] = min(temp_len_t, other_temp_t) - del temp_len - del temp_other_len - del temp_len_t - del other_temp_t - else: - if temp_len != timestamps[s_time]["length_sec"]: - temp_len_t = playtime["time_tick"] - t_time - other_temp_t = timestamps[s_time]["length_tick"] - timestamps[s_time]["length_sec"] = min(temp_len, temp_other_len) - timestamps[s_time]["length_tick"] = min(temp_len_t, other_temp_t) - del temp_len - del temp_other_len - del temp_len_t - del other_temp_t - else: - s_time = colour_time[-1]["time_sec"] - t_time = colour_time[-1]["time_tick"] - len_sec = playtime["time_sec"] - s_time - if len_sec < 10: - len_sec = 10 - len_tick = playtime["time_tick"] - t_time - if not drums: - timestamps[s_time] = {"colours": [note_bit], "colour_name": [colour], - "length_sec": len_sec, - "length_tick": playtime["time_tick"] - t_time, - "extended": []} | colour_time[-1] - else: - timestamps[s_time] = {"colours": [note_bit], "colour_name": [colour], - "length_sec": len_sec, - "length_tick": len_tick, - "accents": [], "length_sec_kick": 0} | colour_time[-1] - if colour_time[-1]["velocity"] == 127: - timestamps[s_time]["accents"].append(note_bit) - # colour_time[-1]["length"] = playtime - colour_time[-1]["time"] + timestamps = create_playable_notes(notes, drums) + # colour_time[-1]["length"] = playtime - colour_time[-1]["time"] if all([drums, "2x_kick" in args, diff == "Expert", "2x" in notes]): has_2x_kick = True d_kick_list = [] @@ -851,7 +809,9 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): if x["time_sec"] in timestamps: timestamps[x["time_sec"]]["2x_kick"] = 1 else: - timestamps[x["time_sec"]] = {"colours": [], "colour_name": [], "2x_kick": 1, "length_sec": x["length_sec"], "velocity": x["velocity"], "accents": [], "length_sec_kick": 0} + timestamps[x["time_sec"]] = {"colours": [], "colour_name": [], "2x_kick": 1, + "length_sec": x["length_sec"], "velocity": x["velocity"], + "accents": [], "length_sec_kick": 0} timestamps = dict(sorted(timestamps.items())) if drums and diff == "Expert": drum_notes = dict(sorted(timestamps.items())) @@ -894,21 +854,18 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): curr_in["extended"].extend( q for q in extended if q not in curr_in["extended"] + curr_in["colours"]) # print() - + curr_colours = "".join(sorted(gem["colour_name"])) # Set hopos if not prev_time == 0: - curr_colours = "".join(sorted(gem["colour_name"])) if all([gem["time_tick"] - prev_time <= hopo, len(gem["colours"]) == 1, prev_colours != curr_colours, time_hopos]): gem["colours"].append(6) if "rb_mode" in args: if curr_colours in prev_colours: gem["colours"].remove(6) - prev_time = gem["time_tick"] - prev_colours = curr_colours - else: - prev_time = gem["time_tick"] - prev_colours = "".join(sorted(gem["colour_name"])) + + prev_time = gem["time_tick"] + prev_colours = curr_colours force_on = forced_notes[diff]["on"] if force_on: force_on = split_list(force_on) @@ -930,7 +887,7 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): else: timestamps[time_array[index]]["colours"].append(6) # print() - force_off = forced_notes[diff]["off"] + force_off = forced_notes[diff]["off"] if not "gh3_mode" in args else 0 if force_off: force_off = split_list(force_off) for forced in force_off: @@ -950,48 +907,10 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): playable_face_off[instrument] = play_face_off.copy() play_notes[diff] = NoteChart(instrument, diff) - for enum, gem in timestamps.items(): - play_notes[diff].notes.append(enum) - len_bin = bin(gem["length_sec"])[2:].zfill(16) - if not drums: - mod_bin = "11111" - if gem["extended"]: - mod_bin = int(mod_bin, 2) - for mod in gem["extended"]: - mod_bin -= 1 << mod - mod_bin = bin(mod_bin)[2:] - elif gem["accents"]: - mod_bin = int("11111", 2) - for mod in gem["colours"]: - if mod not in gem["accents"] and mod < 5: - mod_bin -= 1 << mod - mod_bin = bin(mod_bin)[2:] - else: - mod_bin = "00000" - - if drums and "2x_kick" in gem: - double_bits = "0010" - mod_bin = double_bits + mod_bin - mod_bin = mod_bin.zfill(9) - sep_kick = 0 - if not drums: - notes_bin = make_bin_notes(gem) - elif gem["length_sec_kick"]: - gem["colours"].remove(5) - notes_bin = make_bin_notes(gem) - kick_note = make_bin_notes({"colours": [5]}) - len_bin_kick = bin(gem["length_sec_kick"])[2:].zfill(16) - sep_kick = int(mod_bin + kick_note + len_bin_kick, 2) - mod_bin = "0000" + mod_bin[4:] - else: - notes_bin = make_bin_notes(gem) + if "ghwt" in args: + make_wt_bin_notes(timestamps, play_notes, diff, drums) - note_val = mod_bin + notes_bin + len_bin - play_notes[diff].notes.append(int(note_val, 2)) - if sep_kick: - play_notes[diff].notes.append(enum) - play_notes[diff].notes.append(sep_kick) playable_qb[instrument] = play_notes.copy() playable_star_power[instrument] = play_star.copy() playable_fo_star_power[instrument] = play_star_fo.copy() @@ -1008,12 +927,13 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): curr_taps = [] if not np.any(all_taps): print("No notes found under any tap note on Expert track.") - no_tap_notes = input("Type 'Continue' to compile without taps, or press enter to cancel compilation: ") + no_tap_notes = input( + "Type 'Continue' to compile without taps, or press enter to cancel compilation: ") if not no_tap_notes.lower() == "continue": raise Exception("Cancelled compilation by user") else: - break - for note_check in list(timestamps.values())[all_taps[0]:all_taps[-1]+1]: + break + for note_check in list(timestamps.values())[all_taps[0]:all_taps[-1] + 1]: if len(note_check['colour_name']) > 1: if curr_taps: curr_taps.append(note_check["time_sec"]) @@ -1086,12 +1006,12 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): t_diff = next_key - current_key if t_diff > phrase_dist: # difference is greater than 5 seconds num_keys_to_add = ceil(t_diff / phrase_dist) # number of keys to add - interval = t_diff // num_keys_to_add # interval between new keys + interval = t_diff // num_keys_to_add # interval between new keys for j in range(0, num_keys_to_add): new_key = current_key + j * (interval + 1) - master_phrases[new_key] = [new_key, next_key-1] + master_phrases[new_key] = [new_key, next_key - 1] master_phrases[values[-1][1]] = [values[-1][1], values[-1][1]] - master_phrases[timeSec] = [timeSec, timeSec] + master_phrases[time_sec] = [time_sec, time_sec] master_phrases = dict(sorted(master_phrases.items())) vocals_phrases = [phrase for phrase in master_phrases.values()] lyrics_array = np.array(list(vocals_lyrics.keys())) @@ -1119,7 +1039,7 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): f_split = split_list(freeform) for f2 in f_split: f_len = f2[1] - f2[0] - f_unk = 0 if f_type == 1 else f_len//6 + f_unk = 0 if f_type == 1 else f_len // 6 if f2[0] in vocals_phrase_dict: vocals_phrase_dict[f2[0]] = {"freeform_marker": "freeform" if f_type == 0 else "hype"} vocals_freeform_playable.append([f2[0], f_len, f_unk]) @@ -1127,11 +1047,12 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): for key in list(vocals_phrase_dict.keys())[::-1]: if key <= f2[0]: if not vocals_phrase_dict[key]: - vocals_phrase_dict[key]= {"freeform_marker": "freeform" if f_type == 0 else "hype"} + vocals_phrase_dict[key] = {"freeform_marker": "freeform" if f_type == 0 else "hype"} vocals_freeform_playable.append([key, f_len, f_unk]) break else: - print(f"Freeform marker at {f2[0]} does not coincide with a free phrase marker. Skipping...") + print( + f"Freeform marker at {f2[0]} does not coincide with a free phrase marker. Skipping...") playable_freeform_dict = {} for t in vocals_freeform_playable: playable_freeform_dict[t[0]] = t @@ -1181,12 +1102,34 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): if "text" in v_note: playable_vocals["vocals_markers"].append(markerNode(t, f"qbs(0x{CRC.QBKey_qs(v_note['text'])})")) elif "freeform_marker" in v_note: - playable_vocals["vocals_markers"].append(markerNode(t, f"$vocal_marker_{v_note['freeform_marker']}")) + playable_vocals["vocals_markers"].append( + markerNode(t, f"$vocal_marker_{v_note['freeform_marker']}")) for t, v_note in vocals_lyrics_playable.items(): playable_vocals["lyrics"].append(markerNode(t, f"qbs(0x{v_note})")) playable_vocals["qs_file"] = lyrics_qs_dict # print() + if anim_notes["CAMERAS"]: + prev_time = 0 + for k, v in anim_notes["CAMERAS"].items(): + if prev_time: + prev_note = anim_notes["CAMERAS"][prev_time][0] + prev_note.setLength(v[0].time - prev_note.time) + prev_time = v[0].time + if len(v) > 1: + to_keep = -1 + to_keep_2 = -1 + for enum, cut in enumerate(v): + if cut.note in moment_cams: + to_keep = enum + elif cut.note in range(33,37): + to_keep_2 = enum + if to_keep >= 0: + anim_notes["CAMERAS"][k] = [v[to_keep]] + elif to_keep_2 >= 0: + anim_notes["CAMERAS"][k] = [v[to_keep_2]] + else: + anim_notes["CAMERAS"][k] = [v[0]] temp_sp = [] for enum, sp in enumerate(vocal_sp): if enum % 2 == 0: @@ -1211,8 +1154,9 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): for index in np.nditer(to_open): # Loop through indexes in drum anims temp_anim = drum_anim[hh_array[index]] temp_notes = [x.note for x in temp_anim] - if 78 in temp_notes: - temp_anim[temp_notes.index(78)].note = 79 + for x in [78, 65, 39, 9]: + if x in temp_notes: + temp_anim[temp_notes.index(x)].note += 1 except: continue @@ -1230,27 +1174,125 @@ def parse_wt_qb(mid, hopo, *args, **kwargs): if ghost_notes: ghost_notes = split_list([elem for pair in ghost_notes.items() for elem in pair]) + to_return = {"playable_qb": playable_qb, "star_power": playable_star_power, "bm_star_power": playable_bm_star_power, + "tap": playable_tap, "fo_star_power": playable_fo_star_power, "face_off": playable_face_off, + "gtr_markers": markers, "drum_fills": playable_drum_fills, "anim": anim_notes, "timesigs": timeSigs, + "fretbars": fretbars, "vox_sp": vocal_sp, "ghost_notes": ghost_notes, + "solo_markers": playable_solo_markers, + "has_2x_kick": has_2x_kick, "xplus": 0 + } + xplus_track = 0 + '''if "wtde" in args and make_xplus and not "2x_check" in args: + x_args = list(args) + ["2x_check"] + xplus_track = parse_wt_qb(mid, hopo, *x_args) + + if xplus_track: + to_return["wtde_xplus"] = xplus_track''' + + return to_return + + +def make_open(temp_anim, temp_notes, from_note): + temp_anim[temp_notes.index(from_note)].note = from_note + 1 + + +def create_playable_notes(notes, drums=False): + timestamps = {} + for colour, times in notes.items(): + if colour == "2x": + continue + note_bit = note_bit_vals[colour] + colour_time = [] + for count, playtime in enumerate(times): + if count % 2 == 0: + colour_time.append(playtime) + else: + if colour_time[-1]["time_sec"] in timestamps: + s_time = colour_time[-1]["time_sec"] + test_colour = timestamps[s_time] + t_time = colour_time[-1]["time_tick"] + test_colour["colours"].append(note_bit) + test_colour["colour_name"].append(colour) + temp_len = playtime["time_sec"] - s_time + if temp_len < 13: + temp_len = 13 + temp_other_len = test_colour["length_sec"] + if drums: + if colour_time[-1]["velocity"] == 127: + timestamps[s_time]["accents"].append(note_bit) + if temp_len != test_colour["length_sec"]: + temp_len_t = playtime["time_tick"] - t_time + other_temp_t = timestamps[s_time]["length_tick"] + if temp_len_t <= 180 and other_temp_t <= 180: + timestamps[s_time]["length_sec"] = min(temp_len, temp_other_len) + timestamps[s_time]["length_tick"] = min(temp_len_t, other_temp_t) + del temp_len + del temp_other_len + del temp_len_t + del other_temp_t + elif colour == "purple": + test_colour["length_sec_kick"] = temp_len + elif 5 in test_colour["colours"]: + test_colour["length_sec_kick"] = test_colour["length_sec"] + test_colour["length_sec"] = temp_len + else: + timestamps[s_time]["length_sec"] = min(temp_len, temp_other_len) + timestamps[s_time]["length_tick"] = min(temp_len_t, other_temp_t) + del temp_len + del temp_other_len + del temp_len_t + del other_temp_t + else: + if temp_len != timestamps[s_time]["length_sec"]: + temp_len_t = playtime["time_tick"] - t_time + other_temp_t = timestamps[s_time]["length_tick"] + timestamps[s_time]["length_sec"] = min(temp_len, temp_other_len) + timestamps[s_time]["length_tick"] = min(temp_len_t, other_temp_t) + del temp_len + del temp_other_len + del temp_len_t + del other_temp_t + else: + s_time = colour_time[-1]["time_sec"] + t_time = colour_time[-1]["time_tick"] + len_sec = playtime["time_sec"] - s_time + if len_sec < 13: + len_sec = 13 + len_tick = playtime["time_tick"] - t_time + if not drums: + timestamps[s_time] = {"colours": [note_bit], "colour_name": [colour], + "length_sec": len_sec, + "length_tick": playtime["time_tick"] - t_time, + "extended": []} | colour_time[-1] + else: + timestamps[s_time] = {"colours": [note_bit], "colour_name": [colour], + "length_sec": len_sec, + "length_tick": len_tick, + "accents": [], "length_sec_kick": 0} | colour_time[-1] + if colour_time[-1]["velocity"] == 127: + timestamps[s_time]["accents"].append(note_bit) + return timestamps - return {"playable_qb": playable_qb, "star_power": playable_star_power, "bm_star_power": playable_bm_star_power, - "tap": playable_tap, "fo_star_power": playable_fo_star_power, "face_off": playable_face_off, - "gtr_markers": markers, "drum_fills": playable_drum_fills, "anim": anim_notes, "timesigs": timeSigs, - "fretbars": fretbars, "vox_sp": vocal_sp, "ghost_notes": ghost_notes, "solo_markers": playable_solo_markers, - "has_2x_kick": has_2x_kick - } -def auto_gen_camera(fretbars): +def auto_gen_camera(fretbars, gh3=False): cam_cycle = [10, 18, 23, 15, 43, 11, 19, 24, 16, 30] + if gh3: + cam_cycle = [wor_camera_converts["gh3"][x] for x in cam_cycle] cameras = {} for enum, fb in enumerate(fretbars): note_start = round_time(fb) try: - note_len = round_cam_len(round_time(fretbars[enum+1]) - note_start) + note_len = round_cam_len(round_time(fretbars[enum + 1]) - note_start) except IndexError: note_len = 20000 - cameras[note_start] = [AnimNoteWT(note_start, cam_cycle[enum % len(cam_cycle)], 96)] + if gh3: + cameras[note_start] = [AnimNote(note_start, cam_cycle[enum % len(cam_cycle)])] + else: + cameras[note_start] = [AnimNoteWT(note_start, cam_cycle[enum % len(cam_cycle)], 96)] cameras[note_start][0].setLength(note_len) return cameras + def auto_gen_drums(drum_notes): anim_lookup = { 0: 74, @@ -1271,10 +1313,23 @@ def auto_gen_drums(drum_notes): drum_anim[note_time] = anim_array return drum_anim -def auto_gen_lightshow(fretbars, markers): + +def auto_gen_lightshow(fretbars, markers, gh3=False): fb_array = np.array(fretbars) lightshow = {} - lightshow[0] = [AnimNoteWT(0, 39, 96, 25), AnimNoteWT(0, 84, 96, 25)] + if gh3: + lightshow[0] = [AnimNote(0, 39, 25), AnimNote(0, 76, 25)] + else: + lightshow[0] = [AnimNoteWT(0, 39, 96, 25), AnimNoteWT(0, 84, 96, 25)] + + gh3_lights = { + 74: 74, + 75: 74, + 76: 75, + 77: 74, + 78: 75, + 79: 78 + } for enum, marker in enumerate(markers): try: @@ -1282,10 +1337,10 @@ def auto_gen_lightshow(fretbars, markers): except IndexError: break if re.search(r'intro( [0-9]?[a-z]?)?', marker.marker, flags=re.IGNORECASE): - light = 79 # Prelude + light = 79 # Prelude light_steps = 0 elif re.search(r'(verse( [0-9]?a))|verse$', marker.marker, flags=re.IGNORECASE): - light = 78 # Exposition + light = 78 # Exposition light_steps = 4 elif re.search(r'((pre)-?(chorus)( [0-9]?a))|(pre)-?(chorus)$', marker.marker, flags=re.IGNORECASE): light = 74 # Falling Action @@ -1302,17 +1357,25 @@ def auto_gen_lightshow(fretbars, markers): else: light = 78 # Exposition light_steps = 4 - lightshow[marker.time] = [AnimNoteWT(marker.time, light, 96, 25)] + if gh3: + light = gh3_lights[light] + lightshow[marker.time] = [AnimNote(marker.time, light, 25)] + else: + lightshow[marker.time] = [AnimNoteWT(marker.time, light, 96, 25)] if light_steps != 0: curr_steps = 1 frets_in_range = fb_array[(fb_array > marker.time) & (fb_array < next_time)] for x in frets_in_range: if curr_steps % light_steps == 0: light_time = int(x) - lightshow[light_time] = [AnimNoteWT(light_time, 58, 96, 25)] + if gh3: + lightshow[light_time] = [AnimNote(light_time, 58, 25)] + else: + lightshow[light_time] = [AnimNoteWT(light_time, 58, 96, 25)] curr_steps += 1 return lightshow + def process_text_event(event, markers, time, time_sec): if event.text.startswith("[section"): if event.text.startswith("[section_"): @@ -1320,7 +1383,8 @@ def process_text_event(event, markers, time, time_sec): try: markers.append(markerNode(time_sec, sections[event.text.split(" ")[1][:-1]].title())) except: - new_marker = " ".join(w.capitalize() for w in event.text.replace("_", " ").replace("\"", "\'").split(" ")[1:]) + new_marker = " ".join( + w.capitalize() for w in event.text.replace("_", " ").replace("\"", "\'").split(" ")[1:]) if new_marker[-1] == "]": new_marker = new_marker[:-1] markers.append(markerNode(time_sec, new_marker)) @@ -1338,7 +1402,60 @@ def process_text_event(event, markers, time, time_sec): return 0, 0 -def parseGH3QB(mid, hopo, hmxmode=1, spNote=116): +def make_gh3_bin_notes(timestamps, play_notes, diff): + for enum, gem in timestamps.items(): + play_notes[diff].notes.append(enum) + return + + +def make_wt_bin_notes(timestamps, play_notes, diff, drums): + for enum, gem in timestamps.items(): + play_notes[diff].notes.append(enum) + len_bin = bin(gem["length_sec"])[2:].zfill(16) + if not drums: + mod_bin = "11111" + if gem["extended"]: + mod_bin = int(mod_bin, 2) + for mod in gem["extended"]: + mod_bin -= 1 << mod + mod_bin = bin(mod_bin)[2:] + elif gem["accents"]: + mod_bin = int("11111", 2) + for mod in gem["colours"]: + if mod not in gem["accents"] and mod < 5: + mod_bin -= 1 << mod + mod_bin = bin(mod_bin)[2:] + else: + mod_bin = "00000" + + if drums and "2x_kick" in gem: + double_bits = "0010" + mod_bin = double_bits + mod_bin + + mod_bin = mod_bin.zfill(9) + sep_kick = 0 + if not drums: + notes_bin = make_bin_notes(gem) + elif gem["length_sec_kick"]: + gem["colours"].remove(5) + notes_bin = make_bin_notes(gem) + kick_note = make_bin_notes({"colours": [5]}) + len_bin_kick = bin(gem["length_sec_kick"])[2:].zfill(16) + sep_kick = int(mod_bin + kick_note + len_bin_kick, 2) + mod_bin = "0000" + mod_bin[4:] + else: + notes_bin = make_bin_notes(gem) + + note_val = mod_bin + notes_bin + len_bin + play_notes[diff].notes.append(int(note_val, 2)) + if sep_kick: + play_notes[diff].notes.append(enum) + play_notes[diff].notes.append(sep_kick) + return + + +def parse_gh3_qb(mid, hopo, *args, **kwargs): + hmxmode = 1 changes, ticks = tempMap(mid) ticksArray = np.array(ticks) @@ -1351,8 +1468,6 @@ def parseGH3QB(mid, hopo, hmxmode=1, spNote=116): for x in mid.tracks: if x.name in GH2Tracks: spNote = 103 - elif x.name == "VENUE": - hmxvenue = x.copy() elif x.name == "GH3 VENUE": gh3venue = x.copy() @@ -1360,14 +1475,14 @@ def parseGH3QB(mid, hopo, hmxmode=1, spNote=116): """if hmxvenue and not gh3venue: gh3venue = convertVenue(mid, hmxvenue, changes, ticksArray)""" - playTracksDict = { + play_notes_dict = { "PART GUITAR": "Guitar", "PART BASS": "Bass", "PART GUITAR COOP": "Guitar_Coop", "PART RHYTHM": "Rhythm_Coop" } - playableQB = { + playable_qb = { "Guitar": {}, "Bass": {}, "Guitar_Coop": {}, @@ -1379,11 +1494,78 @@ def parseGH3QB(mid, hopo, hmxmode=1, spNote=116): "Bass": [] } - faceOffs = { + face_offs = { "P1": [], "P2": [] } + venue_track = "LIGHTSHOWCAMERASCROWD" + + valid_anims = { + "LIGHTSHOW": valid_lightshow_notes_gh3, + "CAMERAS": valid_camera_notes_gha if "gha" in args else valid_camera_notes_gh3, + "CROWD": valid_crowd_wt + } + + anim_notes = { + "scripts_notes": {}, + "left_hand": {}, + "triggers_notes": {}, + "CAMERAS": {}, + "LIGHTSHOW": {}, + "CROWD": {}, + "drum_anims": {}, + "scripts": {}, + "anim": {}, + "triggers": {}, + "cameras": [], + "lightshow": [], + "crowd": {}, + "drums": {}, + "performance": [] + } + + fo_star_power = { + "Guitar": [], + "Bass": [], + "Guitar_Coop": [], + "Rhythm_Coop": [] + } + + star_power = { + "Guitar": [], + "Bass": [], + "Guitar_Coop": [], + "Rhythm_Coop": [] + } + + bm_star_power = { + "Guitar": [], + "Bass": [], + "Guitar_Coop": [], + "Rhythm_Coop": [] + } + + playable_star_power = { + "Guitar": {}, + "Bass": {}, + "Guitar_Coop": {}, + "Rhythm_Coop": {} + } + + playable_bm_star_power = { + "Guitar": {}, + "Bass": {}, + "Guitar_Coop": {}, + "Rhythm_Coop": {} + } + + open_hh = [] + inst_map = note_mapping_gh3 | forceMapping + + note_len_mode = 0 # Convert WoR lighting to GH3 style blend events + drum_key_map = drumKeyMapRB_gh3 + cameraNotes = [] lightshowNotes = [] lightshowScripts = [] @@ -1392,562 +1574,548 @@ def parseGH3QB(mid, hopo, hmxmode=1, spNote=116): markers = [] fretbars = [] - for trackNum, track in enumerate(mid.tracks): - diffs = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - - starPowerList = [] - - starPowerDiffs = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - - starBM = { - "Easy": [], - "Medium": [], - "Hard": [], - "Expert": [] - } - - time = 0 - activeNote = { - "Easy": 0, - "Medium": 0, - "Hard": 0, - "Expert": 0 - } - - qbItems = [] - forcing = { - "Easy": {"force_on": 0, "force_off": 0}, - "Medium": {"force_on": 0, "force_off": 0}, - "Hard": {"force_on": 0, "force_off": 0}, - "Expert": {"force_on": 0, "force_off": 0} - } + last_event_secs = 0 + last_event_tick = 0 + end_event_secs, end_event_ticks = 0, 0 + for track_num, track in enumerate(mid.tracks): try: - instrument = playTracksDict[track.name] + instrument = play_notes_dict[track.name] except: print(f"Processing non-instrument track {track.name}") + playable = False else: print(f"Processing instrument track {track.name}") - for x in track: + playable = True + if playable: + play_notes, active_notes, forced_notes, play_face_off, play_star, play_star_bm, play_star_fo = create_diff_dicts() + + starPowerList = [] + + activeNote = { + "Easy": 0, + "Medium": 0, + "Hard": 0, + "Expert": 0 + } + + forcing = { + "Easy": {"force_on": 0, "force_off": 0}, + "Medium": {"force_on": 0, "force_off": 0}, + "Hard": {"force_on": 0, "force_off": 0}, + "Expert": {"force_on": 0, "force_off": 0} + } + + else: + active_notes = {} + time = 0 + for y, x in enumerate(track): time += x.time currChange = changes[len(ticksArray[ticksArray <= time]) - 1] # time, tempo, avgTempo - timeSec = timeInSecs(currChange, mid, time) - if trackNum == 0: + time_sec = timeInSecs(currChange, mid, time) + if track_num == 0: if x.type == "time_signature": - timeSigs.append(timeSigEvent(timeSec, x.numerator, x.denominator)) + timeSigs.append(timeSigEvent(time_sec, x.numerator, x.denominator)) elif track.name == "EVENTS": if x.type == 'text': - # print(msg, timeSec) - end_event_secs, end_event_ticks = process_text_event(x, markers, time, timeSec) + # print(msg, time_sec) + end_event_secs, end_event_ticks = process_text_event(x, markers, time, time_sec) if end_event_secs and end_event_ticks: continue - """if x.text.startswith("[section"): - if x.text.startswith("[section_"): - x.text = x.text.replace("_", " ", 1) - try: - markers.append(markerNode(timeSec, sections[x.text.split(" ")[1][:-1]].title())) - except: - markers.append(markerNode(timeSec, x.text.split(" ")[1].title())) - elif x.text.startswith("[prc_"): - try: - markers.append(markerNode(timeSec, sections[x.text[1:-1]].title())) - except: - markers.append(markerNode(timeSec, x.text[1:-1].title())) - elif x.text == '[end]': - end_event_secs = timeSec - end_event_ticks = time""" - - elif track.name == "GH3 VENUE": - if x.type == "note_on": - if x.note in valid_camera_notes_gh3: - if x.velocity != 0: - if len(cameraNotes) >= 1: - camera_length = timeSec - cameraNotes[-1].time - if camera_length > 0: - cameraNotes[-1].setLength(timeSec - cameraNotes[-1].time) - else: - continue - cameraNotes.append(AnimNote(timeSec, x.note)) - elif x.note in valid_lightshow_notes_gh3: - if x.velocity != 0: - lightshowNotes.append(AnimNote(timeSec, x.note)) + elif re.search(track.name, venue_track, flags=re.IGNORECASE): + track.name = track.name.upper() + valid_check = valid_anims[track.name] + if track.name == "CAMERAS" and not "gha" in args: + alt_check = wor_camera_converts["gh3"] + else: + alt_check = [] + if x.type == "note_on" or x.type == "note_off": + if x.note in valid_check: + if track.name == "LIGHTSHOW": + if x.note == 107: + note_len_mode = 1 + continue + if all([x.note in range(39, 54), note_len_mode]): + continue + create_anim_note(x, active_notes, track.name, anim_notes, time_sec, + AnimNote(time_sec, x.note)) + elif x.note in alt_check and "gha" not in args: + create_anim_note(x, active_notes, track.name, anim_notes, time_sec, + AnimNote(time_sec, alt_check[x.note])) elif x.type == "text": - if x.text.startswith("SetBlendTime"): - blendtime = float(x.text.split(" ")[1]) - lightshowScripts.append(scriptsNode(timeSec, "LightShow_SetTime", blendtime)) + if re.search(r'^(SetBlendTime|LightShow_Set)', x.text, flags=re.IGNORECASE) and not note_len_mode: + # if x.text.startswith("SetBlendTime") or x.text.startswith("LightShow_Set"): + blendtime = {"param": "time", "data": float(x.text.split(" ")[1]), + "type": "Float"} # Blend time is in seconds + anim_notes["lightshow"].append(scriptsNode(time_sec, "LightShow_SetTime", [blendtime])) elif track.name == "PART DRUMS": - if x.type == "note_on": - if x.note in drumKeyMapRB_gh3.keys(): + if x.type == "note_on" or x.type == "note_off": + if x.note in drum_key_map: + new_note = drum_key_map[x.note] + if x.velocity != 0 and x.type == "note_on": + if new_note in active_notes: + continue + active_notes[new_note] = AnimNote(time_sec, drumKeyMapRB_gh3[x.note]) + else: + try: + drum_anim_note(x, active_notes, new_note, anim_notes, time_sec, args) + except KeyError as E: + # raise E + pass + except: + raise Exception(f"Something went wrong parsing the {track.name} track.") + elif x.note == 25: + open_hh.append(time_sec) + elif track.name == "PART VOCALS": + if x.type == "text": + check_to_add_stance(x, time_sec, anim_notes, "Vocalist", singer_stances, singer_anims) + elif playable: + if x.type == "note_on" or x.type == "note_off": + if x.note in inst_map: + chart_diff = inst_map[x.note].split("_")[0] + note_colour = inst_map[x.note].split("_")[1] + if note_colour not in ["off", "on"]: + to_add = {"time_sec": time_sec, "time_tick": time, "curr_change": currChange, + "velocity": x.velocity} + if note_colour in active_notes[chart_diff]: + temp = active_notes[chart_diff] + temp[note_colour].append(to_add) + else: + active_notes[chart_diff][note_colour] = [to_add] + else: + forced_notes[chart_diff][note_colour].append(time_sec) + elif x.note == sp_note: + star_power[instrument].append(time_sec) + if track.name == "PART GUITAR": + if x.note == bm_star_note: + bm_star_power[instrument].append(time_sec) + elif x.note in faceOffMapping: + play_face_off[faceOffMapping[x.note]].append(time_sec) + if not "COOP" in track.name and x.note in leftHandGtr_gh3: + lh_name = instrument if instrument != "Rhythm_Coop" else "Bass" + new_note = anim_maps_gh3[lh_name][x.note] if x.velocity != 0: - drumNotes.append(AnimNote(timeSec, drumKeyMapRB_gh3[x.note])) - else: - if x.type == "note_on": - if track.name in playTracksDict: - if x.velocity == 0: - if x.note in note_mapping_gh3: - chartDiff = note_mapping_gh3[x.note].split("_")[0] - noteColour = note_mapping_gh3[x.note].split("_")[1] - if activeNote[chartDiff] == 1: - activeNote[chartDiff] = 0 - diffs[chartDiff][-1].setLength(timeSec - diffs[chartDiff][-1].time, - currChange.tempo, - mid.ticks_per_beat) - elif x.note in forceMapping: - chartDiff = forceMapping[x.note].split("_")[0] - forceType = "force_" + forceMapping[x.note].split("_")[1] - forcing[chartDiff][forceType] = 0 - if diffs[chartDiff]: - if timeSec == diffs[chartDiff][-1].time: - setattr(diffs[chartDiff][-1], forceType, 0) - elif x.note == spNote: - starPowerList[-1].setLength(timeSec - starPowerList[-1].time) - elif instrument == "Guitar": - if x.note in faceOffMapping: - faceOffs[faceOffMapping[x.note]][-1].setLength( - timeSec - faceOffs[faceOffMapping[x.note]][-1].time - 1) - elif instrument != "Guitar_Coop": - if x.note in leftHandGtr_gh3: - if instrument == "Rhythm_Coop": - leftIns = "Bass" - else: - leftIns = instrument - leftHandAnims[leftIns][-1].setLength(timeSec - leftHandAnims[leftIns][-1].time) + if new_note in active_notes: + continue + active_notes[new_note] = AnimNote(time_sec, new_note) else: - if x.note in note_mapping_gh3: - chartDiff = note_mapping_gh3[x.note].split("_")[0] - noteColour = note_mapping_gh3[x.note].split("_")[1] - - if not diffs[chartDiff]: # Check for empty note array and adds a note - diffs[chartDiff].append(Note(timeSec, 0, currChange)) - activeNote[chartDiff] = 1 - elif diffs[chartDiff][ - -1].time != timeSec: # Check if the note shows up at the same time as the previous note - if activeNote[ - chartDiff] == 1: # If a note has already been placed before a note_off event occured - if timeSec - diffs[chartDiff][ - -1].time <= 16: # Broken chord check. If the note is within 16ms (approx 1/60, i.e. one frame) assume it's part of the same note as before - setattr(diffs[chartDiff][-1], noteColour, 1) - else: # If not, it's assumed to be a new note. Old note gets cut off at the time stamp and new note is created - diffs[chartDiff][-1].setLength(timeSec - diffs[chartDiff][-1].time, - currChange.tempo, - mid.ticks_per_beat) - diffs[chartDiff].append(Note(timeSec, diffs[chartDiff][-1], currChange)) - activeNote[chartDiff] = 1 - else: # If no note is currently active at this note_on event, create one. - diffs[chartDiff].append(Note(timeSec, diffs[chartDiff][-1], currChange)) - activeNote[chartDiff] = 1 - if noteColour in colours: - setattr(diffs[chartDiff][-1], noteColour, 1) - if "forceType" in locals(): - if forcing[chartDiff][forceType] == 1: - setattr(diffs[chartDiff][-1], forceType, 1) - if x.note in forceMapping: - chartDiff = forceMapping[x.note].split("_")[0] - forceType = "force_" + forceMapping[x.note].split("_")[1] - forcing[chartDiff][forceType] = 1 - if diffs[chartDiff]: - if timeSec == diffs[chartDiff][-1].time: - setattr(diffs[chartDiff][-1], forceType, 1) - if x.note == spNote: - if not starPowerList: - starPowerList.append(StarPower(timeSec)) - elif starPowerList[-1].time != timeSec: - starPowerList.append(StarPower(timeSec)) - if instrument == "Guitar": - if x.note in leftHandGtr_gh3: - if not leftHandAnims[instrument]: - leftHandAnims[instrument].append(AnimNote(timeSec, leftHandGtr_gh3[x.note])) - elif leftHandAnims[instrument][-1].time != timeSec: - leftHandAnims[instrument].append(AnimNote(timeSec, leftHandGtr_gh3[x.note])) - if x.note in faceOffMapping: - if not faceOffs[faceOffMapping[x.note]]: - faceOffs[faceOffMapping[x.note]].append(FaceOffSection(timeSec)) - elif faceOffs[faceOffMapping[x.note]][-1].time != timeSec: - faceOffs[faceOffMapping[x.note]].append(FaceOffSection(timeSec)) - if instrument == "Bass" or instrument == "Rhythm_Coop": - if x.note in leftHandBass_gh3: - if not leftHandAnims["Bass"]: - leftHandAnims["Bass"].append(AnimNote(timeSec, leftHandBass_gh3[x.note])) - elif leftHandAnims["Bass"][-1].time != timeSec: - leftHandAnims["Bass"].append(AnimNote(timeSec, leftHandBass_gh3[x.note])) - - elif x.type == "note_off": - if track.name in playTracksDict: - if x.note in note_mapping_gh3: - chartDiff = note_mapping_gh3[x.note].split("_")[0] - noteColour = note_mapping_gh3[x.note].split("_")[1] - if activeNote[chartDiff] == 1: - activeNote[chartDiff] = 0 - diffs[chartDiff][-1].setLength(timeSec - diffs[chartDiff][-1].time, currChange.tempo, - mid.ticks_per_beat) - if x.note in forceMapping: - chartDiff = forceMapping[x.note].split("_")[0] - forceType = "force_" + forceMapping[x.note].split("_")[1] - forcing[chartDiff][forceType] = 0 - if diffs[chartDiff]: - if timeSec == diffs[chartDiff][-1].time: - setattr(diffs[chartDiff][-1], forceType, 0) - if x.note == spNote: - starPowerList[-1].setLength(timeSec - starPowerList[-1].time) - if instrument == "Guitar": - if x.note in faceOffMapping: - faceOffs[faceOffMapping[x.note]][-1].setLength( - timeSec - faceOffs[faceOffMapping[x.note]][-1].time - 1) - if instrument != "Guitar_Coop": - if x.note in leftHandGtr_gh3: - leftHandAnims[instrument][-1].setLength(timeSec - leftHandAnims[instrument][-1].time) - - for x in diffs: - spCounter = 0 - for y in diffs[x]: - if not starPowerDiffs[x]: - try: - starPowerDiffs[x].append( - StarPowerPhrase(starPowerList[spCounter].time, starPowerList[spCounter].length)) - except: - print(f"No Star Power found for track {track.name} on {x} difficulty") - break - if y.time >= starPowerList[spCounter].time and y.time <= starPowerList[spCounter].time + starPowerList[ - spCounter].length: - starPowerDiffs[x][-1].incNotes() - # print(spCounter, x, y, len(starPowerList)) - elif y.time > starPowerList[spCounter].time + starPowerList[spCounter].length: - if len(starPowerList) == spCounter + 1: - pass + try: + active_notes[new_note].setLength(time_sec - active_notes[new_note].time) + if active_notes[new_note].time in anim_notes["left_hand"]: + anim_notes["left_hand"][active_notes[new_note].time].append( + active_notes[new_note]) + else: + anim_notes["left_hand"][active_notes[new_note].time] = [active_notes[new_note]] + active_notes.pop(new_note) + except KeyError: + pass + except: + raise Exception(f"Something went wrong parsing the {track.name} track.") + elif x.type == "text": + if instrument == "Bass" or instrument == "Guitar": + player = "Bassist" if instrument == "Bass" else "Guitarist" + check_to_add_stance(x, time_sec, anim_notes, player, guit_stances, guit_anims) + if playable: + star_power[instrument] = split_list(star_power[instrument]) + bm_star_power[instrument] = split_list(bm_star_power[instrument]) + if track.name == "PART GUITAR": + for player, fo in play_face_off.items(): + if fo: + play_face_off[player] = split_list(fo) + for diff, notes in active_notes.items(): + timestamps = create_playable_notes(notes) + timestamps = dict(sorted(timestamps.items())) + time_array = np.array(list(timestamps.keys())) + + prev_time = 0 + prev_colours = "" + for enum, (timing, gem) in enumerate(timestamps.items()): + curr_colours = "".join(sorted(gem["colour_name"])) + if prev_time != 0: + if all([gem["time_tick"] - prev_time <= hopo, len(gem["colours"]) == 1, + prev_colours != curr_colours]): + gem["is_hopo"] = True + else: + gem["is_hopo"] = False + if all([gem["is_hopo"], "rb_mode" in args, curr_colours in prev_colours]): + gem["colours"].append(5) + elif all([gem["is_hopo"], "force_only" in args]): + gem["colours"].append(5) else: - while y.time > starPowerList[spCounter].time + starPowerList[spCounter].length: - spCounter += 1 - starPowerDiffs[x].append( - StarPowerPhrase(starPowerList[spCounter].time, starPowerList[spCounter].length)) - - if track.name in playTracksDict: - for x in diffs: - playableQB[instrument][x] = Difficulty(x, instrument, diffs[x], starPowerDiffs[x], starBM[x]) - for y in playableQB[instrument][x].song: - y.setForcing(hopo, mid.ticks_per_beat, hmxmode) - - # print(playableQB) + gem["is_hopo"] = False + + prev_time = gem["time_tick"] + prev_colours = curr_colours + # print() + force_on = forced_notes[diff]["on"] + if force_on: + force_on = split_list(force_on) + for forced in force_on: + if forced[0] == forced[1]: + forced[1] += 1 + # Pull all timestamps that need to be forced + to_force = mod_notes(time_array, forced) + for index in np.nditer(to_force): # Loop through indexes in timestamps + temp_colour = timestamps[time_array[index]]["colours"] + if index == 0: + continue + elif temp_colour == timestamps[time_array[index - 1]]["colours"]: + continue + elif len(temp_colour) > 1: + continue + elif timestamps[time_array[index]]["is_hopo"]: + if 5 in timestamps[time_array[index]]["colours"] and not "gh3_mode" in args: + timestamps[time_array[index]]["colours"].remove(5) + continue + else: + timestamps[time_array[index]]["colours"].append(5) + + force_off = forced_notes[diff]["off"] if not "gh3_mode" in args else 0 + if force_off: + force_off = split_list(force_off) + for forced in force_off: + if forced[0] == forced[1]: + forced[1] += 1 + to_force = mod_notes(time_array, forced) + for index in np.nditer(to_force): + if not timestamps[time_array[index]]["is_hopo"] and 5 in timestamps[time_array[index]][ + "colours"]: + timestamps[time_array[index]]["colours"].remove(5) + elif timestamps[time_array[index]]["is_hopo"]: + timestamps[time_array[index]]["colours"].append(5) + + if star_power[instrument]: + note_range_count(star_power[instrument], time_array, play_star[diff]) + if bm_star_power[instrument]: + note_range_count(bm_star_power[instrument], time_array, play_star_bm[diff]) + + play_notes[diff] = NoteChart(instrument, diff, True) + for enum, gem in timestamps.items(): + play_notes[diff].notes.append(enum) + play_notes[diff].notes.append(gem["length_sec"]) + play_notes[diff].notes.append(set_gems(gem)) + + playable_qb[instrument] = play_notes.copy() + playable_star_power[instrument] = play_star.copy() + playable_bm_star_power[instrument] = play_star_bm.copy() + + if note_len_mode: + blend_lookup = { + 1000: 53, + 900: 52, + 800: 51, + 700: 50, + 600: 49, + 500: 48, + 400: 47, + 300: 46, + 250: 45, + 200: 44, + 150: 43, + 100: 42, + 50: 41, + 0: 40 + } + strobe_mode = 0 + prev_len = -1 + new_lights = {} + for key, value in anim_notes["LIGHTSHOW"].items(): + blend_time = key - 20 + max_len = 0 + blended = False + for lights in value: + if lights.note in range(70, 77) or lights.note in range(57,59): + blended = True + if lights.note == 70: + strobe_mode = 1 + elif lights.note in range(71, 77): + strobe_mode = 0 + if lights.note == 56: + continue + max_len = max(max_len, lights.length) + if not blended: + continue + if not strobe_mode: + if max_len > 1020: + max_len = round((max_len) / 1000, 3) + blendtime = {"param": "time", "data": float(max_len), + "type": "Float"} # Blend time is in seconds + anim_notes["lightshow"].append(scriptsNode(blend_time, "LightShow_SetTime", [blendtime])) + elif max_len % 100 < 50: + max_len = round(max_len, -2) + blend_note = AnimNote(blend_time, blend_lookup[max_len]) + blend_note.setLength(20) + if max_len == prev_len: + continue + if blend_time in new_lights: + new_lights[blend_time].append(blend_note) + else: + new_lights[blend_time] = [blend_note] + elif max_len % 100 < 100 and max_len % 100 >= 50 and max_len < 300: + max_len = round(max_len) // 50 * 50 + blend_note = AnimNote(blend_time, blend_lookup[max_len]) + blend_note.setLength(20) + if max_len == prev_len: + continue + if blend_time in new_lights: + new_lights[blend_time].append(blend_note) + else: + new_lights[blend_time] = [blend_note] + else: + max_len = round((max_len) / 1000, 3) + blendtime = {"param": "time", "data": float(max_len), + "type": "Float"} # Blend time is in seconds + anim_notes["lightshow"].append(scriptsNode(blend_time, "LightShow_SetTime", [blendtime])) + else: + blend_note = AnimNote(blend_time, 40) + blend_note.setLength(20) + if blend_time in new_lights: + new_lights[blend_time].append(blend_note) + else: + new_lights[blend_time] = [blend_note] + prev_len = max_len + for key, value in new_lights.items(): + if key in anim_notes["LIGHTSHOW"]: + for note in value: + anim_notes["LIGHTSHOW"][key].append(note) + else: + anim_notes["LIGHTSHOW"][key] = value + anim_notes["LIGHTSHOW"] = dict(sorted((anim_notes["LIGHTSHOW"].items()))) + + if not "end_event_secs" in locals(): raise Exception("Invalid MIDI: No [end] event found. Cannot parse MIDI.") if cameraNotes: cameraNotes[-1].setLength(end_event_secs - cameraNotes[-1].time) fretbars = parse_fretbars(timeSigs, end_event_ticks, changes, ticksArray, mid) + if anim_notes["drum_anims"]: + drum_anim = anim_notes["drum_anims"] + open_hh = split_list(open_hh) + hh_array = np.array(list(drum_anim.keys())) + for hh in open_hh: + to_open = mod_notes(hh_array, hh)[0] + try: + for index in np.nditer(to_open): # Loop through indexes in drum anims + temp_anim = drum_anim[hh_array[index]] + temp_notes = [x.note for x in temp_anim] + for x in [65, 53, 41]: + if x in temp_notes: + temp_anim[temp_notes.index(x)].note += 1 + except: + continue - # print(fretbars[-1]) + if not anim_notes["CAMERAS"] and "gha" not in args: + print("Generating cameras") + anim_notes["CAMERAS"] = auto_gen_camera(fretbars[::8], True) - for x in playableQB: - for i, y in enumerate(playableQB[x]): - for j, z in enumerate(playableQB[x][y].song): - if j != 0: - prev = playableQB[x][y].song[j - 1] - if z.time == prev.time + prev.length: - prev.noNoteTouch() - # print(y, z.time, prev.time + prev.length) + if not anim_notes["LIGHTSHOW"]: + print("Generating lightshow") + anim_notes["LIGHTSHOW"] = auto_gen_lightshow(fretbars, markers, True) - return {"playableQB": playableQB, "drums_notes": drumNotes, "timesig": timeSigs, "markers": markers, - "fretbars": fretbars, "leftHandAnims": leftHandAnims, "faceOffs": faceOffs, "cameras_notes": cameraNotes, - "lightshow_notes": lightshowNotes, "lightshow": lightshowScripts} + return {"playable_qb": playable_qb, "star_power": playable_star_power, "bm_star_power": playable_bm_star_power, + "timesig": timeSigs, "markers": markers, + "fretbars": fretbars, "face_offs": play_face_off, "anim": anim_notes} -def makeMidQB(midQB, filename, headerDict, consoleType): +def create_gh3_sections(qb_dict, filename, headerDict, consoleType): QBItems = [] - qbFileHeader = b'\x1C\x08\x02\x04\x10\x04\x08\x0C\x0C\x08\x02\x04\x14\x02\x04\x0C\x10\x10\x0C\x00' # print(midQB) - for x in midQB["playableQB"]: + for x in qb_dict["playable_qb"]: QBChart = [] QBStar = [] QBStarBM = [] # print(x) - if not midQB["playableQB"][x]: + if not qb_dict["playable_qb"][x]: for y in difficulties: instrument = x.replace("_", "").lower() if x != "Bass" else "rhythm" - chartName = f"{filename}_song_{instrument}_{y}" - starName = f"{filename}_{instrument}_{y}_Star" - BMStarName = f"{filename}_{instrument}_{y}_StarBattleMode" - QBChart.append( - QBItem("ArrayInteger", chartName, headerDict[chartName], [], consoleType)) - QBStar.append(QBItem("ArrayArray", starName, headerDict[starName], [], consoleType)) - QBStarBM.append(QBItem("ArrayArray", BMStarName, headerDict[BMStarName], [], consoleType)) - - for i, y in enumerate(midQB["playableQB"][x]): - data = [midQB["playableQB"][x][y].song, midQB["playableQB"][x][y].star, midQB["playableQB"][x][y].starBM] - if x == "Guitar": - chartName = f"{filename}_song_{y}" - starName = f"{filename}_{y}_Star" - BMStarName = f"{filename}_{y}_StarBattleMode" - QBChart.append( - QBItem("ArrayInteger", chartName, headerDict[chartName], data[0], consoleType)) - QBStar.append(QBItem("ArrayArray", starName, headerDict[starName], data[1], consoleType)) - QBStarBM.append(QBItem("ArrayArray", BMStarName, headerDict[BMStarName], data[2], consoleType)) - else: - instrument = x.replace("_", "").lower() if x != "Bass" else "rhythm" - chartName = f"{filename}_song_{instrument}_{y}" + chart_name = f"{filename}_song_{instrument}_{y}" starName = f"{filename}_{instrument}_{y}_Star" BMStarName = f"{filename}_{instrument}_{y}_StarBattleMode" - QBChart.append( - QBItem("ArrayInteger", chartName, headerDict[chartName], data[0], consoleType)) - QBStar.append(QBItem("ArrayArray", starName, headerDict[starName], data[1], consoleType)) - QBStarBM.append(QBItem("ArrayArray", BMStarName, headerDict[BMStarName], data[2], consoleType)) + QBChart.append(basic_data(chart_name, [])) + QBStar.append(basic_data(chart_name, [])) + QBStarBM.append(basic_data(chart_name, [])) + else: + for i, y in enumerate(qb_dict["playable_qb"][x]): + song = qb_dict["playable_qb"][x][y].notes + star = qb_dict["star_power"][x][y] + star_bm = qb_dict["bm_star_power"][x][y] + if x == "Guitar": + chart_name = f"{filename}_song_{y}" + starName = f"{filename}_{y}_Star" + BMStarName = f"{filename}_{y}_StarBattleMode" + else: + instrument = x.replace("_", "").lower() if x != "Bass" else "rhythm" + chart_name = f"{filename}_song_{instrument}_{y}" + starName = f"{filename}_{instrument}_{y}_Star" + BMStarName = f"{filename}_{instrument}_{y}_StarBattleMode" + QBChart.append(basic_data(chart_name, song)) + QBStar.append(basic_data(starName, star)) + QBStarBM.append(basic_data(BMStarName, star_bm)) for y in [QBChart, QBStar, QBStarBM]: for z in y: QBItems.append(z) # print(midQB["faceOffs"]) - for x in midQB["faceOffs"]: - chartName = f"{filename}_FaceOff{x}" - if not midQB["faceOffs"][x]: - QBItems.append(QBItem("ArrayInteger", chartName, headerDict[chartName], [], consoleType)) + for x in qb_dict["face_offs"]: + chart_name = f"{filename}_FaceOff{x}" + if not qb_dict["face_offs"][x]: + QBItems.append(basic_data(chart_name, [])) else: - QBItems.append(QBItem("ArrayArray", chartName, headerDict[chartName], midQB["faceOffs"][x], consoleType)) + QBItems.append(basic_data(chart_name, [])) # Boss Battle Array - for x in midQB["faceOffs"]: - chartName = f"{filename}_BossBattle{x}" - QBItems.append(QBItem("ArrayArray", chartName, headerDict[chartName], [], consoleType)) + for x in qb_dict["face_offs"]: + chart_name = f"{filename}_BossBattle{x}" + QBItems.append(basic_data(chart_name, [])) # timesig (array), markers (struct), fretbars (integer) - chartName = f"{filename}_timesig" - QBItems.append(QBItem("ArrayArray", chartName, headerDict[chartName], midQB["timesig"], consoleType)) + chart_name = f"{filename}_timesig" + QBItems.append(basic_data(chart_name, [[x.time, x.numerator, x.denominator] for x in qb_dict["timesig"]])) + + chart_name = f"{filename}_fretbars" + QBItems.append(basic_data(chart_name, qb_dict["fretbars"])) + chart_name = f"{filename}_markers" + gtr_markers = [] + if qb_dict["markers"]: + for gtr in qb_dict["markers"]: + gtr_markers.append(struct_data()) + temp_struct = gtr_markers[-1] + temp_struct.add_data("time", str(gtr.time)) + temp_struct.add_data("marker", f"w\"{gtr.marker}\"") + QBItems.append(basic_data(chart_name, gtr_markers)) - chartName = f"{filename}_fretbars" - QBItems.append(QBItem("ArrayInteger", chartName, headerDict[chartName], midQB["fretbars"], consoleType)) - chartName = f"{filename}_markers" - QBItems.append(QBItem("ArrayStruct", chartName, headerDict[chartName], midQB["markers"], consoleType)) + for anim, value in qb_dict["anim"].items(): + if anim in anim_lookup: + chart_name = f"{filename}_{anim_lookup[anim]}" + else: + chart_name = f"{filename}_{anim}" + anim_list = [] + if value: + if type(value) == dict: + prev_time = 0 + for timing, notes in sorted(value.items()): + for anim_stuff in notes: + anim_list.append([timing, anim_stuff.note, anim_stuff.length]) + prev_time = timing + elif type(value) == list: + for anim_stuff in value: + anim_list.append(make_script_struct(anim_stuff, False)) + QBItems.append(basic_data(chart_name, anim_list.copy())) + + return QBItems - P1count = 0 - P2count = 0 - mergedAnim = [] + +def check_to_add_stance(x, time_sec, anim_notes, player, stances, anims): while True: - try: - if midQB["leftHandAnims"]["Guitar"][P1count].time < midQB["leftHandAnims"]["Bass"][P2count].time: - mergedAnim.append(midQB["leftHandAnims"]["Guitar"][P1count]) - P1count += 1 - else: - mergedAnim.append(midQB["leftHandAnims"]["Bass"][P2count]) - P2count += 1 - except: - if P1count == len(midQB["leftHandAnims"]["Guitar"]): - mergedAnim += midQB["leftHandAnims"]["Bass"][P2count:] - else: - mergedAnim += midQB["leftHandAnims"]["Guitar"][P1count:] + extra = [] + if re.search(r'(cycle)', x.text, re.IGNORECASE): + extra.append("cycle") + if re.search(r'(no_wait)', x.text, re.IGNORECASE): + extra.append("no_wait") + if re.search(r'( [1-9]+)$', x.text, re.IGNORECASE): + extra.append("repeat") + extra.append(re.search(r'( [1-9]+)$', x.text, re.IGNORECASE).group(0).strip()) + pattern = re.search(rf'({"|".join(stances)})', x.text, re.IGNORECASE) + if pattern: + new_stance = stance_script(time_sec, "Band_ChangeStance", player, pattern.group(0), *extra) break + pattern = re.search(rf'^({"|".join(anims)})', x.text, re.IGNORECASE) + if pattern: + new_stance = stance_script(time_sec, "Band_PlayAnim", player, pattern.group(0), *extra) + break + new_stance = 0 + break + if new_stance: + anim_notes["performance"].append(new_stance) - misc = ["scripts", "anim", "triggers", "cameras", "lightshow", "crowd", "drums", "performance"] - withNotes = ["drums", "lightshow", "cameras"] - withScripts = ["lightshow"] - QBNotes = [] - QBScripts = [] - - for x in misc: - xscript = f"{filename}_{x}" - xnote = f"{xscript}_notes" - notesqb = f"{x}_notes" - if x == "anim": - QBNotes.append(QBItem("ArrayArray", xnote, headerDict[xnote], mergedAnim, consoleType)) - elif x in withNotes: - if not midQB[notesqb]: - QBNotes.append(QBItem("ArrayInteger", xnote, headerDict[xnote], [], consoleType)) - else: - QBNotes.append(QBItem("ArrayArray", xnote, headerDict[xnote], midQB[notesqb], consoleType)) - else: - QBNotes.append(QBItem("ArrayInteger", xnote, headerDict[xnote], [], consoleType)) - if x in withScripts: - QBScripts.append(QBItem("ArrayStruct", xscript, headerDict[xscript], midQB[x], consoleType)) - # print(QBScripts[-1]) - else: - QBScripts.append(QBItem("ArrayInteger", xscript, headerDict[xscript], [], consoleType)) - for x in QBNotes: - QBItems.append(x) - for x in QBScripts: - QBItems.append(x) +def stance_script(time_sec, script, player, event, *args): + if script == "Band_ChangeStance": + params = new_stance_gh3(player, event, "stance", *args) + elif script == "Band_PlayAnim": + params = new_stance_gh3(player, event, "anim", *args) + else: + print(f"Script {script} at {time_sec} not recognized.") + return 0 + script_node = scriptsNode(time_sec, script, params) + return script_node - positionStart = 28 - qbbytes = bytearray() +def create_anim_note(x, active_notes, track_name, anim_notes, time_sec, anim_entry): + if x.velocity != 0 and x.type == "note_on": + if x.note in active_notes: + return + active_notes[x.note] = anim_entry + else: + try: + anim_len = time_sec - active_notes[x.note].time + if anim_len < 13 or anim_len > 2**32: + anim_len = 13 + active_notes[x.note].setLength(anim_len) + if active_notes[x.note].time in anim_notes[track_name]: + anim_notes[track_name][active_notes[x.note].time].append(active_notes[x.note]) + else: + anim_notes[track_name][active_notes[x.note].time] = [active_notes[x.note]] + active_notes.pop(x.note) + except KeyError: + pass + except: + raise Exception(f"Something went wrong parsing the {track_name} track.") + return - toBytes = lambda a, b=4: a.to_bytes(b, "big") +def camera_script(zoom_type, zoom_length): + param_type = {"param": "type", "data": zoom_type, "type": "QbKey"} + param_time = {"param": "time", "data": zoom_length, + "type": "Integer" if type(zoom_length) == int else "Float"} + return param_type, param_time - # binascii.hexlify(bytes("Intro Slow", "latin-1"), ' ', 1)) - # print(bytes("Intro Slow", "utf-8")) - packname = f"songs/{filename}.mid.qb" - for i, x in enumerate(QBItems): - # print(x) - x.processData(consoleType) # Convert data in classes to numbers for use - sectionbytes = bytearray() - - # QB Item Header - sectionbytes += toBytes(qbNodeHeaders["SectionArray"][consoleType]) - sectionbytes += toBytes(int(headerDict[x.name], 16)) # CRC of the header name - sectionbytes += toBytes(int(QBKey(packname), 16)) # CRC of the mid.qb name (e.g. "songs\slowride.mid.qb") - - position = positionStart + len(sectionbytes) - sectionbytes += toBytes(position + 8) # +8 to account for the next 8 bytes - sectionbytes += toBytes(0) # Next Item = 0 for mid.qb - - sectionbytes += toBytes(x.qbType) # Add the hex value of the qb type - if x.node == "ArrayInteger": - sectionbytes += toBytes(x.itemcount) - position = positionStart + len(sectionbytes) - sectionbytes += toBytes(position + 4) # List starts 4 bytes after where the offset is defined - for y in x.data: - if isinstance(y, Note): - sectionbytes += toBytes(y.time) - sectionbytes += toBytes(y.length) - sectionbytes += toBytes(int(y.binForm(), 2)) - else: - sectionbytes += toBytes(y) - elif x.node == "ArrayArray": - sectionbytes += toBytes(x.itemcount) - position = positionStart + len(sectionbytes) - liststart = position + 4 # To cover the 4 bytes that point to the start of list - sectionbytes += toBytes(liststart) - firstitem = position + 4 + (x.itemcount * 4) - # print(firstitem) - for j, y in enumerate(x.arraydata): # Add all starts of array entries to the mid - arraynodelength = 4 + 4 + 4 + (len(y) * 4) - # print(arraynodelength) - if len(x.arraydata) == 1: - pass - else: - sectionbytes += toBytes(firstitem + (arraynodelength * j)) - # print(y) - position = positionStart + len(sectionbytes) - # print(len(x.arraydata)) - for y in x.arraydata: - sectionbytes += toBytes(x.subarraytype) - sectionbytes += toBytes(len(y)) - position = positionStart + len(sectionbytes) + 4 - sectionbytes += toBytes(position) - for z in y: - sectionbytes += toBytes(z) - position = positionStart + len(sectionbytes) - elif x.node == "ArrayStruct": - sectionbytes += toBytes(x.itemcount) - position = positionStart + len(sectionbytes) - liststart = position + 4 # To cover the 4 bytes that point to the start of list - sectionbytes += toBytes(liststart) - # sectionbytes += toBytes(0) * x.itemcount - # print(firstitem) - if "markers" in x.name: - offsets = [] - markerBytes = bytearray() # 4 for header_marker, 4 for "first item" in struct - for y in x.arraydata: - # Struct Header - offsets.append(positionStart + len(sectionbytes) + 4 * x.itemcount + len(markerBytes)) - markerBytes += toBytes(qbNodeHeaders["StructHeader"][consoleType]) - - # time - position = positionStart + len(sectionbytes) + len(markerBytes) + 4 * x.itemcount - markerBytes += toBytes(position + 4) - markerBytes += toBytes(qbNodeHeaders["StructItemInteger"][consoleType]) - markerBytes += toBytes(int(QBKey("time"), 16)) - markerBytes += toBytes(y[0]) # Add the time of the marker - position = positionStart + len(sectionbytes) + len(markerBytes) + 4 * x.itemcount - markerBytes += toBytes(position + 4) # Position of next item - - # marker - markerBytes += toBytes(qbNodeHeaders["StructItemStringW"][consoleType]) - markerBytes += toBytes(int(QBKey("marker"), 16)) - position = positionStart + len(sectionbytes) + len(markerBytes) + 4 * x.itemcount + 8 - markerBytes += toBytes(position) - markerBytes += toBytes(0) # Next item is 0 as it's the final item in this struct - markername = y[1] - # print(markername, len(markername)) - markernamebytes = bytes(markername, "latin-1") - for z in markernamebytes: - markerBytes += toBytes(z, 2) - markerBytes += toBytes(0, 2) - if len(markername) % 2 == 0: - markerBytes += toBytes(0, 2) - for y in offsets: - sectionbytes += toBytes(y) - sectionbytes += markerBytes - if "lightshow" in x.name: - offsets = [] - lightshowBytes = bytearray() - if len(x.arraydata) == 1: - setattr(x, "itemcount", - 0) # If only one blend event, it shuffles back the position of the following items - for y in x.arraydata: - # Struct Header - offsets.append(positionStart + len(sectionbytes) + 4 * x.itemcount + len(lightshowBytes)) - lightshowBytes += toBytes(qbNodeHeaders["StructHeader"][consoleType]) - - # Time - position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount - lightshowBytes += toBytes(position + 4) - lightshowBytes += toBytes(qbNodeHeaders["StructItemInteger"][consoleType]) - lightshowBytes += toBytes(int(QBKey("time"), 16)) - lightshowBytes += toBytes(y[0]) # Add the time of the lightshow event - position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount - lightshowBytes += toBytes(position + 4) # Position of next item in struct - - # Event Type - lightshowBytes += toBytes(qbNodeHeaders["StructItemQbKey"][consoleType]) - lightshowBytes += toBytes(int(QBKey("scr"), 16)) - lightshowBytes += toBytes(int(QBKey(y[1]), 16)) # Add blend time to struct - position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount - lightshowBytes += toBytes(position + 4) # Position of next item in struct - - # Struct Item Struct - To feed the game the custom blend time - lightshowBytes += toBytes(qbNodeHeaders["StructItemStruct"][consoleType]) - lightshowBytes += toBytes(int(QBKey("params"), 16)) # Tell the game to change the params - position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount + 8 - lightshowBytes += toBytes(position) - lightshowBytes += toBytes(0) # Next item is 0 as it's the final item in this overall struct - if y[1] == "LightShow_SetTime": - lightshowBytes += toBytes(qbNodeHeaders["StructHeader"][consoleType]) - position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount + 4 - lightshowBytes += toBytes(position) - lightshowBytes += toBytes(qbNodeHeaders["StructItemFloat"][consoleType]) - lightshowBytes += toBytes(int(QBKey("time"), 16)) - lightshowBytes += struct.pack(">f", y[2]) # Pack in the float as bytes - lightshowBytes += toBytes(0) # Next item is 0 as it's the final item in this internal struct - - if len(offsets) > 1: - for y in offsets: - # print(y) - sectionbytes += toBytes(y) - - sectionbytes += lightshowBytes - - # position = positionStart + len(sectionbytes) + len(lightshowBytes) + 4 * x.itemcount + 8 - - # print(offsets) - - elif x.node == "Floats": - sectionbytes += toBytes(0) - sectionbytes += toBytes(0) - # print(position) - # print(position) - # print(x.name, x.itemcount) - # Add the section to the overall bytearray - qbbytes += sectionbytes - positionStart = 28 + len(qbbytes) - - # print(positionStart) - - # print(binascii.hexlify(qbbytes, ' ', 1)) - # exit() - filesize = len(qbbytes) + 28 - fullqb = bytearray() - fullqb += toBytes(0) + toBytes(filesize) + qbFileHeader + qbbytes - - return fullqb +def lightshow_script(x): + if type(x) == float or type(x) == int: + t = x + else: + t = x.text.split(" ")[1] + blendtime = {"param": "time", "data": float(t), + "type": "Float"} # Blend time is in seconds + return blendtime + +def drum_anim_note(x, active_notes, new_note, anim_notes, time_sec, args): + new_len = time_sec - active_notes[new_note].time + active_notes[new_note].setLength(new_len) + if active_notes[new_note].time in anim_notes["drum_anims"]: + anim_notes["drum_anims"][active_notes[new_note].time].append( + active_notes[new_note]) + else: + anim_notes["drum_anims"][active_notes[new_note].time] = [active_notes[new_note]] + if x.note not in practice_mode_wt: + temp = anim_notes["drum_anims"][active_notes[new_note].time] + if "wor" in args: + if new_note < 22: + practice_note = new_note + 86 + elif new_note < 105: + practice_note = new_note + 56 + else: + active_notes.pop(new_note) + return + elif "gh3" in args: + if new_note < 47: + practice_note = new_note + 24 + elif new_note < 59: + practice_note = new_note + 12 + else: + active_notes.pop(new_note) + return + else: + practice_note = new_note - 13 + if "gh3" in args: + temp.append(AnimNote(time_sec, practice_note)) + else: + temp.append(AnimNoteWT(time_sec, practice_note, temp[-1].velocity)) + temp[-1].setLength(new_len) + active_notes.pop(new_note) + return def make_wt_drum_anims(midi_track, tempo_data): @@ -1997,6 +2165,3 @@ def make_wt_qb(mid_file): if track.name == "drums": drum_anims = make_wt_drum_anims(track, tempo_data) return - - - diff --git a/pak_extract/PAKExtract.py b/pak_extract/PAKExtract.py index f730297..c2d9d7e 100644 --- a/pak_extract/PAKExtract.py +++ b/pak_extract/PAKExtract.py @@ -4,6 +4,7 @@ from dbg import checksum_dbg from pak_functions import * from CRC import QBKey +from io import BytesIO import os import zlib @@ -149,6 +150,55 @@ def main(pak, folder, endian = "big", wor_mode = 0, pak_header_size = 0, toolkit return files +def decompress_pab(comp, pak_file): + pak = BytesIO(pak_file) + del pak_file + last = 0x2CB3EF3B + files = [] + uint32 = lambda x: int.from_bytes(x.read(4), 'big') + ext = uint32(pak) + while ext != last: + entry = {"extension": ext} + entry["offset"] = uint32(pak) + entry["length"] = uint32(pak) + entry["pak_key"] = uint32(pak) + entry["full_name"] = uint32(pak) + entry["name_sum"] = uint32(pak) + entry["parent"] = uint32(pak) + entry["flags"] = uint32(pak) + try: + entry["file_name"] = f"{checksum_dbg[entry['full_name']]}{checksum_dbg[entry['extension']]}" + entry["full_name"] = checksum_dbg[entry['full_name']] + entry['extension'] = checksum_dbg[entry['extension']] + except: + entry["file_name"] = f"{entry['full_name']}.{entry['name_sum']}.{entry['extension']}" + files.append(entry) + ext = uint32(pak) + pak.close() + comp = BytesIO(comp) + for entry in files: + comp_bytes = comp.read(entry["length"]) + if entry["full_name"] == "SING_Adam_Dammit_100_01": + print() + if entry["flags"] == 512: + magic = str(comp_bytes[:4], encoding = "UTF-8") + if magic == "CHNK": + comp_bytes = comp_bytes[128:] + else: + raise Exception("Unknown magic value found. Contact me.") + file_bytes = zlib.decompress(comp_bytes, wbits=-15) + file_size = int.from_bytes(file_bytes[:4], "big") + #print() + elif entry["flags"] == 0: + file_size = int.from_bytes(comp_bytes[:4], "big") + file_bytes = comp_bytes[:file_size] + else: + raise Exception("Unknown Flag number found. Contact me.") + entry["file_bytes"] = file_bytes + #while magic == "CHNK": + + return files + def decompress_pak(comp, endian = "big"): comp = compressed_pak(comp, endian) decomp_file = b'' @@ -185,9 +235,13 @@ def compress_pak(decomp): return -def check_decomp(pak_file, file_path = "", output_decomp = True): - if pak_file[:4] == b'CHNK': # Check for xbox compressed file - pak_file = decompress_pak(pak_file) +def check_decomp(comp_file, file_path = "", output_decomp = True, pab = False, *args, **kwargs): + if comp_file[:4] == b'CHNK': # Check for xbox compressed file + if pab: + comp_file = decompress_pab(comp_file, kwargs["pak_file"]) + return comp_file + else: + comp_file = decompress_pak(comp_file) if output_decomp: pak_decomp_out = f'.\\Decompressed PAKs\\{file_path}' if not os.path.exists(pak_decomp_out): @@ -197,8 +251,8 @@ def check_decomp(pak_file, file_path = "", output_decomp = True): except: pass with open(pak_decomp_out, 'wb') as write_file: - write_file.write(pak_file) - return pak_file + write_file.write(comp_file) + return comp_file def extract_paks(): pabs = [] @@ -232,15 +286,26 @@ def extract_paks(): header_size = 0 curr_file = os.path.basename(filepaths[y]) print(f"Processing {curr_file}") + with open(filepaths[y], 'rb') as f: + pak_file = f.read() + if pak_file[:4] == b'CHNK': # Check for WoR style PAK + pak_file = check_decomp(pak_file, curr_file,) if x in pabs: with open(filepaths_pab[pabs.index(x)], 'rb') as f: - pab_file = check_decomp(f.read(), curr_file.replace(".pak.xen", ".pab.xen")) + pab_file = check_decomp(f.read(), curr_file.replace(".pak.xen", ".pab.xen"), False, True, pak_file= pak_file) + if type(pab_file) == list: + for z in pab_file: + output_file = f'.\\output\\PAK\\{x}\\{z["file_name"]}.xen' + dir_name = os.path.dirname(output_file) + try: + os.makedirs(dir_name) + except: + pass + with open(output_file, 'wb') as write_file: + write_file.write(z["file_bytes"]) + continue else: pab_file = b'' - with open(filepaths[y], 'rb') as f: - pak_file = f.read() - if pak_file[:4] == b'CHNK': # Check for WoR style PAK - pak_file = check_decomp(pak_file, curr_file) first_file = int.from_bytes(pak_file[4:8], "big") if first_file == 0: wor_mode = 1 diff --git a/pak_extract/QB2Text.py b/pak_extract/QB2Text.py index b3a0090..93a2701 100644 --- a/pak_extract/QB2Text.py +++ b/pak_extract/QB2Text.py @@ -1,12 +1,20 @@ from pak_definitions import * from pak_functions import * from pak_classes import * +from io import StringIO import os import sys orig_stdout = sys.stdout - +def print_to_var(text_qb): + result = StringIO() + orig_stdout = sys.stdout + sys.stdout = result + print_qb_text_file(text_qb) + sys.stdout = orig_stdout + qb_text = result.getvalue() + return qb_text def convert_qb_file(qb_file, file_name, file_headers, console = "PC"): endian = console_endian[console] consoleType = console_lookup[console] diff --git a/pak_extract/pak_definitions.py b/pak_extract/pak_definitions.py index 6ec5a7f..182826b 100644 --- a/pak_extract/pak_definitions.py +++ b/pak_extract/pak_definitions.py @@ -112,7 +112,7 @@ others = ["_BossBattleP1", "_BossBattleP2", "_timesig", "_fretbars", "_markers", "_scripts_notes", "_anim_notes", "_triggers_notes", "_cameras_notes", "_lightshow_notes", "_crowd_notes", "_drums_notes", "_performance_notes", "_scripts", "_anim", "_triggers", "_cameras", "_lightshow", "_crowd", - "_drums", "_performance"] + "_drums", "_performance", "_song_drums_expertplus"] markers_wt = ["_guitar_markers", "_rhythm_markers", "_drum_markers"] diff --git a/pak_extract/pak_functions.py b/pak_extract/pak_functions.py index 3d027d4..ccc8cdd 100644 --- a/pak_extract/pak_functions.py +++ b/pak_extract/pak_functions.py @@ -20,9 +20,11 @@ def round_time(entry): new_time = entry elif time_trunc == 99: new_time = entry + 1 - elif time_trunc < 33: + elif time_trunc == 1: + new_time = int(str(entry)[:-2] + "00") + elif time_trunc <= 34: new_time = int(str(entry)[:-2] + str(33)) - elif time_trunc < 67: + elif time_trunc <= 68: new_time = int(str(entry)[:-2] + str(67)) else: new_time = int(str(entry)[:-2] + str(99)) + 1 @@ -511,7 +513,7 @@ def print_array_data(array_data, array_type, sub_array="", id_string="", indent= for y, x in enumerate(array_data): array_string += f"{output_item_data(x, array_type)}" if y != len(array_data) - 1: - array_string += ", " + array_string += " " # print(f"{indent_val}{id_string}", f"= [{array_string}]") return array_string + "]" elif array_type.endswith("Array"): @@ -526,12 +528,12 @@ def print_array_data(array_data, array_type, sub_array="", id_string="", indent= print(f"{indent_val}\t\t" + "{") for items in struct.data_value: print_struct_item(items, indent + 3) - print(f"{indent_val}\t\t" + "}" + f"{',' if s_count != len(array_data[y]) - 1 else ''}") - print(f"{indent_val}\t]" + f"{',' if y != len(array_data) - 1 else ''}") + print(f"{indent_val}\t\t" + "}" + f"{'' if s_count != len(array_data[y]) - 1 else ''}") + print(f"{indent_val}\t]" + f"{'' if y != len(array_data) - 1 else ''}") # raise Exception else: print( - f"{print_array_data(x, sub_array[y], 1, '', indent + 1)}{',' if y != len(array_data) - 1 else ''}") + f"{print_array_data(x, sub_array[y], 1, '', indent + 1)}{'' if y != len(array_data) - 1 else ''}") print(f"{indent_val}]") elif array_type.endswith("Struct"): """if sub_array: @@ -545,21 +547,21 @@ def print_array_data(array_data, array_type, sub_array="", id_string="", indent= else: for z in x.data_value: print_struct_item(z, indent + 2) - print(f"{indent_val}\t" + "}" + f"{',' if y != len(array_data) - 1 else ''}") + print(f"{indent_val}\t" + "}" + f"{'' if y != len(array_data) - 1 else ''}") # raise Exception print(f"{indent_val}]") # raise Exception elif len(array_data) > 3: print(f"{indent_val}{id_string} = [") for y, x in enumerate(array_data): - print(f"{indent_val}\t{output_item_data(x, array_type)}{',' if y != len(array_data) - 1 else ''}") + print(f"{indent_val}\t{output_item_data(x, array_type)}{'' if y != len(array_data) - 1 else ''}") print(f"{indent_val}]") else: array_string = "" for y, x in enumerate(array_data): array_string += f"{output_item_data(x, array_type)}" if y != len(array_data) - 1: - array_string += ", " + array_string += " " print(f"{indent_val}{id_string}", f"= [{array_string}]") return @@ -713,11 +715,17 @@ def new_play_clip(time, clip, start, end = 0): return play_clip -def make_script_struct(script_data): +def make_script_struct(script_data, to_round = True): final_struct = struct_data() - time = basic_data("time", round_time(script_data.time)) + if to_round: + time = basic_data("time", round_time(script_data.time)) + else: + time = basic_data("time", script_data.time) time.set_type("Integer") - time.set_bin_data(struct.pack(">i", round_time(script_data.time))) + if to_round: + time.set_bin_data(struct.pack(">i", round_time(script_data.time))) + else: + time.set_bin_data(struct.pack(">i", script_data.time)) scr = basic_data("scr", script_data.type) scr.set_type("QbKey") scr.set_bin_data(bytes.fromhex(CRC.QBKey(script_data.type))) @@ -772,16 +780,16 @@ def new_band_clip_gh5(char_class): return char_array -def new_stance_gh3(time, name, stance): - params_list = [] - params_list.append(struct_item("StructItemQbKey", "name", name, 0)) - params_list.append(struct_item("StructItemQbKey", "stance", stance, 0)) - - time = struct_item("StructItemInteger", "time", time, 0) - scr = struct_item("StructItemQbKey", "scr", "Band_ChangeStance", 0) - params = struct_item("StructItemStruct", "params", params_list, 0) - - new_stance = struct_item("StructHeader", 0, [time, scr, params], 0) +def new_stance_gh3(name, stance, anim_type, *args): + param_name = {"param": "name", "data": name, "type": "QbKey"} + param_stance = {"param": anim_type, "data": stance, "type": "QbKey"} + new_stance = [param_name, param_stance] + if "cycle" in args: + new_stance.append({"param": "no_id", "data": "cycle", "type": "QbKey"}) + if "no_wait" in args: + new_stance.append({"param": "no_id", "data": "no_wait", "type": "QbKey"}) + if "repeat" in args: + new_stance.append({"param": "repeat_count", "data": int(args[args.index("repeat")+1]), "type": "Integer"}) return new_stance diff --git a/requirements/install_requirements.bat b/requirements/install_requirements.bat index e3c029b..1f83b31 100644 --- a/requirements/install_requirements.bat +++ b/requirements/install_requirements.bat @@ -1,3 +1,5 @@ @echo off -python -m pip install -r requirements.txt \ No newline at end of file +python -m pip install -r requirements.txt + +pause \ No newline at end of file diff --git a/ska_converter/read_ska.py b/ska_converter/read_ska.py index 06cda1b..fa87b6c 100644 --- a/ska_converter/read_ska.py +++ b/ska_converter/read_ska.py @@ -141,6 +141,7 @@ def main(func, write = False, **kwargs): out_dir = f"{root_folder}/out" with os.scandir(directory) as songs: for x in songs: + print(f"Processing {x}") with open(x, 'rb') as f: ska_orig = f.read() ska_file = ska_bytes(ska_orig) diff --git a/ska_converter/ska_classes.py b/ska_converter/ska_classes.py index 39a2407..94a68c2 100644 --- a/ska_converter/ska_classes.py +++ b/ska_converter/ska_classes.py @@ -373,7 +373,23 @@ def read_custom_keys(self): self.position = self.customkey_pos custom_keys = [] for x in range(self.custom_keys): - custom_keys.append([self.readFloat(), self.readBytes(), self.readBytes(), self.readFloat()]) + curr_key = [] + key_time = self.readFloat() + key_type = self.readBytes() + key_value = self.readBytes() + curr_key.append(key_time) + curr_key.append(key_type) + curr_key.append(key_value) + if key_type == 1: + key_mod = self.readFloat() + curr_key.append(key_mod) + elif key_type == 9: + pass + else: + raise Exception("Custom keys found in unsupported ska file. Contact me.") + custom_keys.append(curr_key) + + return custom_keys def read_pointer_block(self): diff --git a/ska_converter/ska_functions.py b/ska_converter/ska_functions.py index 3b7e539..892bed1 100644 --- a/ska_converter/ska_functions.py +++ b/ska_converter/ska_functions.py @@ -592,12 +592,26 @@ def make_modern_ska(ska, game = "GH5", *args, **kwargs): total_size = len(quat_data) + len(trans_data) + len(block_sizes) + len(partial_anim) + len(custom_keys) + 256 quat_pos = 256 - custom_key_pos = (total_size - len(custom_keys)) if custom_keys else NO_OFF - partial_anim_offset = (total_size - len(partial_anim)) if partial_anim else NO_OFF + to_move_back = 0 + if custom_keys: + custom_key_pos = total_size + if partial_anim: + custom_key_pos -= len(partial_anim) + custom_key_pos -= len(custom_keys) + to_move_back += len(custom_keys) + else: + custom_key_pos = NO_OFF + + if partial_anim: + partial_anim_offset = total_size - len(partial_anim) + to_move_back += len(partial_anim) + else: + partial_anim_offset = NO_OFF + if all([custom_key_pos == NO_OFF, partial_anim_offset == NO_OFF]): bone_start = total_size else: - bone_start = custom_key_pos if partial_anim_offset == NO_OFF else partial_anim_offset + bone_start = total_size - to_move_back if bone_pointers: bonepointer_offset = total_size bone_header = bytearray() @@ -630,5 +644,5 @@ def make_modern_ska(ska, game = "GH5", *args, **kwargs): for y in x: header_bytes += struct.pack(">f", y) header_bytes += b'\x00' * (256 - len(header_bytes)) - ska_file = header_bytes + quat_data + trans_data + block_sizes + partial_anim + custom_keys + bone_header + bone_pointers + ska_file = header_bytes + quat_data + trans_data + block_sizes + custom_keys + partial_anim + bone_header + bone_pointers return ska_file diff --git a/toolkit_functions.py b/toolkit_functions.py index ca53b93..2952d0e 100644 --- a/toolkit_functions.py +++ b/toolkit_functions.py @@ -228,7 +228,7 @@ def add_to_dict(p_dict, t, entry): return -def convert_to_gh3(pakmid, output=f'{os.getcwd()}', singer=lipsync_dict["gh3_singer"]): +def convert_to_gh3(pakmid, output=f'{os.getcwd()}', singer="gh3_singer"): if not "_song.pak" in pakmid: warning = input( "WARNING: File does not appear to be a validly named mid PAK file. Do you want to continue? (Y/N): ") @@ -405,11 +405,10 @@ def convert_to_gh3(pakmid, output=f'{os.getcwd()}', singer=lipsync_dict["gh3_sin song_pak = mid_qb.pakMaker(gh3_array) # raise Exception - return song_name, song_pak -def convert_to_gha(pakmid, output=f'{os.getcwd()}', singer=lipsync_dict["gha_singer"]): +def convert_to_gha(pakmid, output=f'{os.getcwd()}', singer="gha_singer"): if "_song.pak" in pakmid: song_name = pakmid[len(os.path.dirname(pakmid)) + 1:pakmid.find("_song")] elif ".mid" in pakmid: @@ -507,7 +506,7 @@ def convert_to_gha(pakmid, output=f'{os.getcwd()}', singer=lipsync_dict["gha_sin if re.search("[0-9][bB]\.ska", x['file_name'].lower()): # raise Exception - x["file_data"] = make_gh3_ska(ska_bytes(x["file_data"]), ska_switch=lipsync_dict["gha_guitarist"], + x["file_data"] = make_gh3_ska(ska_bytes(x["file_data"]), ska_switch="gha_guitarist", quats_mult=2) else: x["file_data"] = make_gh3_ska(ska_bytes(x["file_data"]), ska_switch=singer, quats_mult=2) @@ -1377,7 +1376,7 @@ def convert_to_5(pakmid, new_name, *args, **kwargs): if "vocals_note_range" in x: continue temp_sections[x] = y - override_sections = mid_qb.create_wt_qb(temp_sections, song_name) + override_sections = mid_qb.create_game_qb(temp_sections, song_name) override_sections = QB2Text.convert_qb_file(QB2Text.qb_bytes(override_sections), song_name, file_headers) del (temp_sections) @@ -2583,7 +2582,7 @@ def rename_track(avatar): return track -def read_gh3_note(sections_dict, tempo_data, tpb, game="GH3"): +def read_gh3_note(sections_dict, tempo_data, tpb, game="GH3", *args): if game == "GH3": gtr_anims = range(117, 128) bass_anims = range(100, 111) @@ -2657,6 +2656,7 @@ def read_gh3_note(sections_dict, tempo_data, tpb, game="GH3"): "anim": [], "triggers": [], "cameras": [], + "cameras_wt": [], "lightshow": [], "crowd": [], "drums": [], @@ -2794,8 +2794,10 @@ def read_gh3_note(sections_dict, tempo_data, tpb, game="GH3"): note_val -= 1 if note_val != 40 else 0 note_charts[track].append({"time": t_sec, "length": length, "note": note_val}) continue - # if misc_type == "drum": - + if misc_type == "cameras": + if "gha" not in args: + misc_charts["cameras_wt"].append({"time": t_sec, "length": length, "note": gh3_to_wt[note_val]}) + #print() misc_note.append({"time": t_sec, "length": length, "note": note_val}) misc_charts[misc_type] += misc_note.copy() elif re.search(r"(lightshow)", misc_type, flags=re.IGNORECASE): @@ -2940,7 +2942,7 @@ def read_gh3_note(sections_dict, tempo_data, tpb, game="GH3"): return mid -def read_gh5_note(note_bin, drum_mode=False): +def read_gh5_note(note_bin, drum_mode=False, *args): note_file = BytesIO(note_bin) read_int = lambda a=4, note=note_file: int.from_bytes(note.read(a), "big") dbg = lambda check: PAKExtract.pull_dbg_name(check) @@ -3010,21 +3012,50 @@ def read_gh5_note(note_bin, drum_mode=False): base_notes = drum_base_notes else: base_notes = other_base_notes + if "raw" in args: + note_file_dict[entry_id] = { + "normal": [], + "xplus": [] + } for entry in range(entry_count): entry_time = read_int() entry_length = read_int(2) drum_note = read_int(1) drum_accent = read_int(1) + drum_ghost = 0 if entry_type == "gh6_expert_drum_note": drum_ghost = read_int(1) + if "raw" in args: + len_bin = bin(entry_length)[2:].zfill(16) + acc_bin = bin(drum_accent)[2:].zfill(9) + if "drum" in entry_id: + if drum_ghost: + drum_note += drum_ghost + norm_bin = bin(drum_note)[2:].zfill(7) + xplus_bin = norm_bin + if drum_note & 1 << 6 and drum_note & 1 << 5: # 2x note + norm_bin = bin(drum_note)[3:].zfill(7) + xplus_bin = norm_bin + elif drum_note & 1 << 6: + norm_bin = bin(drum_note)[3:].zfill(7) + xplus_bin = f"1{bin(drum_note)[4:]}".zfill(7) + + if int(norm_bin, 2): + note_file_dict[entry_id]["normal"].extend([entry_time, int(acc_bin+norm_bin+len_bin,2)]) + + if diff == "expert": + note_file_dict[entry_id]["xplus"].extend( + [entry_time, int(acc_bin + xplus_bin + len_bin, 2)]) + else: + norm_bin = bin(drum_note)[2:].zfill(7) + note_file_dict[entry_id]["normal"].extend([entry_time, int(acc_bin + norm_bin + len_bin, 2)]) + else: entry_note = set_note_type(drum_note, drum_accent, drum_ghost) - else: - entry_note = set_note_type(drum_note, drum_accent) - for note in entry_note: - note_file_dict[entry_id].append({"time": entry_time, "length": entry_length, - "note": base_notes[note["colour"]] + (12 * note_mult[entry_diff]), - "velocity": note["velocity"]}) + for note in entry_note: + note_file_dict[entry_id].append({"time": entry_time, "length": entry_length, + "note": base_notes[note["colour"]] + (12 * note_mult[entry_diff]), + "velocity": note["velocity"]}) elif entry_type == "gh5_band_moment_note": for entry in range(entry_count): entry_time = read_int() @@ -3244,7 +3275,403 @@ def gh5_to_midi(notes, tempo_data, tpb, vox=False, anim=False, drums=False): return temp_tracks +def perf_override_check(perf_override, to_add): + if perf_override: + perf_override += f",\n {to_add}" + else: + perf_override = to_add + return perf_override + +def convert_5_to_wt(pakmid, perf_override = "", *args): + ska_override = [] + song_name = pakmid[len(os.path.dirname(pakmid)) + 1:pakmid.lower().find("_s")].lower() + if re.search(r'^[a-c]dlc', song_name, flags=re.IGNORECASE): + song_name = song_name[1:] + qb_sections, file_headers, file_headers_hex, song_files = pak2mid(pakmid, song_name) + sections_dict = get_section_dict(qb_sections, file_headers_hex) + game_check = ''.join(x for x in sections_dict.keys()) + if perf_override: + with open(perf_override) as f: + perf_override = "\n".join(f.read().split("\n")[1:-1]) + if not re.search(rf"{song_name}_song_easy", game_check, flags=re.IGNORECASE): + pass + else: + print("Not a valid GH5+ file.") + return + playable_qb = { + "Guitar": {}, + "Bass": {}, + "Drums": {}, + "Vocals": { + "song_vocals": [], + "vocals_freeform": [], + "vocals_phrases": [], + "vocals_note_range": [60, 60], + "lyrics": [], + "vocals_markers": [], + "qs_file": {} + } + } + + playable_face_off = { + "Guitar": {"P1": [], "P2": []}, + "Bass": {"P1": [], "P2": []}, + "Drums": {"P1": [], "P2": []} + } + + playable_fo_star_power = { + "Guitar": {}, + "Bass": {}, + "Drums": {} + } + + playable_star_power = { + "Guitar": {}, + "Bass": {}, + "Drums": {} + } + playable_bm_star_power = { + "Guitar": {}, + "Bass": {}, + "Drums": {} + } + + playable_tap = { + "Guitar": [], + "Bass": [] + } + + playable_solo_markers = { + "Guitar": [], + "Bass": [], + "Drums": [] + } + + playable_drum_fills = [] + anim_notes = { + "scripts_notes": {}, + "left_hand": {}, + "triggers_notes": {}, + "CAMERAS": {}, + "LIGHTSHOW": {}, + "CROWD": {}, + "drum_anims": {}, + "scripts": {}, + "anim": {}, + "triggers": {}, + "cameras": [], + "lightshow": [], + "crowd": {}, + "drums": {}, + "performance": [] + } + band_clips = [] + instruments = 0 + perf_clips = 0 + use_cams = 0 + pull_struct = 0 + struct_string = "" + anim_structs = [] + anim_loops = [] + qs_dict = 0 + lyrics_qs = [] + lyrics_qs_dict = {} + gtr_markers = [] + xplus = 0 + new_perf = "" + for files in song_files: + if re.search(fr"songs/{song_name}\.mid.qb$", files["file_name"], flags=re.IGNORECASE): + qb_mid_file = QB2Text.convert_qb_file(QB2Text.qb_bytes(files["file_data"]), song_name, file_headers) + qb_mid_text = QB2Text.print_to_var(qb_mid_file) + qb_mid_nohead = "\n".join(qb_mid_text.split("\n")[1:]) + lip_events_loc = qb_mid_nohead.find(f"{song_name}_facial") + lip_events_end = qb_mid_nohead.find("]", lip_events_loc) + lip_events = "\n".join(qb_mid_nohead[lip_events_loc:lip_events_end].strip().split("\n")[1:]) + perf_override = perf_override_check(perf_override, lip_events) + if re.search(fr"songs/{song_name}\.mid\.qs.en$", files["file_name"], flags=re.IGNORECASE): + qs_dict = get_qs_strings(files["file_data"]) + elif re.search(fr"songs/{song_name}\.note$", files["file_name"], flags=re.IGNORECASE): + instruments = read_gh5_note(files["file_data"], False, "raw") + elif re.search(fr"songs/{song_name}\.perf$", files["file_name"], flags=re.IGNORECASE): + cameras, anim_structs = read_gh5_perf(files["file_data"], song_name) + use_cams = 1 + pull_struct = 1 + elif re.search(fr"songs/{song_name}\.perf.xml.qb$", files["file_name"], flags=re.IGNORECASE): + perf_xml_file = QB2Text.convert_qb_file(QB2Text.qb_bytes(files["file_data"]), song_name, file_headers) + for x in perf_xml_file: + if x.section_id.endswith("scriptevents"): + for y in x.section_data: + if y.data_dict["scr"] == "Band_PlayClip": + clip_params = y.data_dict["params"] + clip_len = round((clip_params["endframe"] - clip_params["startframe"]) / 30 / clip_params[ + "timefactor"] * 1000) + band_clips.append( + [clip_params["clip"], y.data_dict["time"], clip_len + y.data_dict["time"]]) + elif y.data_dict["scr"] == "Band_PlayLoop": + anim_loops.append({"text": y.data_dict["params"]["name"], "time": y.data_dict["time"]}) + perf_xml_text = QB2Text.print_to_var(perf_xml_file) + perf_xml_nohead = "\n".join(perf_xml_text.split("\n")[1:]).strip() + perf_events_loc = perf_xml_nohead.find(f"{song_name}_scriptevents") + perf_clips = perf_xml_nohead[:perf_events_loc] + perf_events = "\n".join(perf_xml_nohead[perf_events_loc:].split("\n")[1:-1]) + if perf_events.endswith("]"): + perf_events = perf_events[:-1] + perf_override = perf_override_check(perf_override, perf_events) + elif re.search(r".ska$", files["file_name"], flags=re.IGNORECASE): + wt_ska = make_modern_ska(ska_bytes(files["file_data"]),"GHWT", quats_mult=1, ska_switch = "wt_rocker") + #wt_ska = files["file_data"] + ska_override.append([wt_ska, files["file_name"]]) + if perf_override: + perf_override = f"song_performance = [\n{perf_override}\n]" + fretbars = instruments["fretbar"] + instruments.pop("fretbar") + timesigs = [mid_qb.timeSigEvent(x[0],x[1],x[2]) for x in instruments["timesig"]] + instruments.pop("timesig") + temp_phrase = [] + temp_freeform = [] + to_pop = [] + for k, v in instruments.items(): + key_name = k + reg = re.search(r'^(drums|bass|guitar)', key_name) + if reg: + play = reg[0].title() + key_name = key_name[len(play):] + type_reg = re.search(r'(starpower|tapping|instrument|markers)$', key_name)[0] + key_name = key_name[:-len(type_reg)].title() + if type_reg == "markers": + gtr_markers = [] + for x in v: + marker = qs_dict[int(x["text"], 16)] + if marker.startswith("\\u[m]"): + marker = marker[5:] + gtr_markers.append(mid_qb.markerNode(x["time"], marker)) + elif type_reg == "instrument": + new_chart = mid_qb.NoteChart(play, key_name) + new_chart.notes = v["normal"] + if v["normal"] != v["xplus"] and play == "Drums" and key_name == "Expert": + xplus = mid_qb.NoteChart(play, key_name) + xplus.notes = v["xplus"] + + playable_qb[play][key_name] = new_chart + elif type_reg == "starpower": + playable_star_power[play][key_name] = [[x["time"], x["length"]] for x in v] + else: + if v and key_name == "Expert": + playable_tap[play] = [[x["time"], x["length"], 1] for x in v] + to_pop.append(k) + continue + reg = re.search(r'(drumfill)', key_name) + if reg: + to_pop.append(k) + if "expert" in k: + playable_drum_fills.extend([[x["time"], x["time"]+x["length"]] for x in v]) + continue + reg = re.search(r'(vocal)', key_name) + if reg: + curr = playable_qb["Vocals"] + #to_pop.append(k) + if k == "vocals": + low = 128 + high = 0 + for vox in v: + if vox["note"] > 27: + low = min(vox["note"], low) + high = max(vox["note"], high) + curr["song_vocals"].extend([vox["time"],vox["length"],vox["note"]]) + curr["vocals_note_range"] = [low, high] + elif "freeform" in k: + for vox in v: + temp_freeform.append(vox["time"]) + curr["vocals_freeform"].append([vox["time"], vox["length"], round(vox["length"]/6)]) + elif "lyrics" in k: + split_word = 0 + for vox in v: + raw_text = vox['text'] + if split_word: + split_word = 0 + raw_text = "=" + raw_text + if raw_text.endswith("-"): + split_word = 1 + raw_text = raw_text[:-1] + elif raw_text.endswith("="): + split_word = 1 + raw_text = raw_text[:-1] + "-" + lyric = f"\\L{raw_text}" + if lyric not in lyrics_qs: + lyrics_qs.append(lyric) + curr["lyrics"].append(mid_qb.markerNode(vox["time"], f"qbs(0x{CRC.QBKey_qs(lyric)})")) + + for vox in instruments["vocalsmarkers"]: + raw_text = vox['text'] + if not raw_text: + if vox["time"] in temp_freeform: + playable_qb["Vocals"]["vocals_markers"].append(mid_qb.markerNode(vox["time"], f"$vocal_marker_freeform")) + continue + lyric = f"\\L{raw_text}" + if lyric not in lyrics_qs: + lyrics_qs.append(lyric) + temp_phrase.append(vox["time"]) + playable_qb["Vocals"]["vocals_markers"].append(mid_qb.markerNode(vox["time"], f"qbs(0x{CRC.QBKey_qs(lyric)})")) + + for lyrics in sorted(lyrics_qs): + playable_qb["Vocals"]["qs_file"][lyrics] = CRC.QBKey_qs(lyrics) + + lyric_time = np.array(playable_qb["Vocals"]["song_vocals"][::3]) + phrase_time = [] + for enum, phrase in enumerate(instruments["vocalphrase"]): + if enum != 0: + phrase_time.append(phrase["time"]) + phrase_time.append(phrase["time"]) + if enum == len(instruments["vocalphrase"]) - 1: + if phrase["time"] <= lyric_time[-1]: + phrase_time.append(lyric_time[-1]+60) + else: + phrase_time.append(phrase["time"]+1) + phrase_mod = mid_qb.split_list(phrase_time) + player = 0 + for times in phrase_mod: + phrase_check = mid_qb.mod_notes(lyric_time, times)[0] + phrase_to = 0 + if len(phrase_check) > 0: + phrase_to = (player % 2) + 1 + player += 1 + elif times[0] in temp_freeform: + phrase_to = 3 + playable_qb["Vocals"]["vocals_phrases"].extend([times[0], phrase_to]) + + for x in to_pop: + instruments.pop(x) + # Calculate Star Power notes! + for inst, v in playable_star_power.items(): + inst_play = playable_qb[inst] + for diff, val in inst_play.items(): + diff_time = np.array(val.notes[::2]) + if diff == "Expert": + playable_face_off[inst]["P1"] = playable_face_off[inst]["P2"] = [int(diff_time[0]-50), int(diff_time[-1]-diff_time[0]+50)] + diff_split = v[diff] + for starpower in diff_split: + star_list = [starpower[0],starpower[0] + starpower[1]] + notes = mid_qb.mod_notes(diff_time, star_list)[0] + starpower.append(len(notes)) + for x in qb_sections: + if x.array_node_type == "Floats": + continue + elif x.section_id.endswith("_notes"): + if "anim" in x.section_id: + to_add = "left_hand" + elif "drums" in x.section_id: + to_add = "drum_anims" + elif "crowd" in x.section_id: + to_add = "CROWD" + elif "lightshow" in x.section_id: + to_add = "LIGHTSHOW" + elif "cameras" in x.section_id: + continue + else: + input(f"Unknown track {x.section_id} found. Enter to continue.") + continue + anim_time = x.section_data[::2] + anim_event = x.section_data[1::2] + drum_count = "" + for t, e in zip(anim_time, anim_event): + e_bin = bin(e)[2:].zfill(32) + e_len = int(e_bin[16:], 2) + e_note = int(e_bin[8:16], 2) + e_vel = int(e_bin[:8], 2) + if "drums" in x.section_id: + if e_note <= 59: + e_note = mid_qb.drumKeyMapRB_wt[mid_qb.wor_to_rb_drums[e_note]] + e_prac = mid_qb.AnimNoteWT(t, e_note-13, e_vel, e_len) + if t in anim_notes[to_add]: + anim_notes[to_add][t].append(e_prac) + else: + anim_notes[to_add][t] = [e_prac] + elif e_note in range(108,114): + e_prac = mid_qb.AnimNoteWT(t, 70, e_vel, e_len) + if e_note in range(108,111): + e_note = 83 + else: + e_note = 78 + if not drum_count: + if e_note == 83: + drum_count = f"Sticks" + else: + drum_count = f"Hihat" + print(drum_count) + if t in anim_notes[to_add]: + anim_notes[to_add][t].append(e_prac) + else: + anim_notes[to_add][t] = [e_prac] + elif e_note in range(60,84): + pass + else: + continue + e_anim = mid_qb.AnimNoteWT(t, e_note, e_vel, e_len) + if t in anim_notes[to_add]: + anim_notes[to_add][t].append(e_anim) + else: + anim_notes[to_add][t] = [e_anim] + elif "lightshow" in x.section_id: + for y in x.section_data: + blendtime = mid_qb.lightshow_script(y.data_dict['params']['time']) + anim_notes["lightshow"].append(mid_qb.scriptsNode(y.data_dict['time'], "LightShow_SetTime", [blendtime])) + + for cams in cameras["momentcameras"]: + anim_notes["CAMERAS"][cams["time"]] = [mid_qb.AnimNoteWT(cams["time"], cams["note"], 100, cams["length"])] + for cams in cameras["autocutcameras"]: + if not cams["time"] in anim_notes["CAMERAS"]: + anim_notes["CAMERAS"][cams["time"]] = [mid_qb.AnimNoteWT(cams["time"], cams["note"], 100, cams["length"])] + anim_notes["CAMERAS"] = dict(sorted(anim_notes["CAMERAS"].items())) + + playable_bm_star_power = playable_fo_star_power = playable_star_power + qb_dict = {"playable_qb": playable_qb, "star_power": playable_star_power, "bm_star_power": playable_bm_star_power, + "tap": playable_tap, "fo_star_power": playable_fo_star_power, "face_off": playable_face_off, + "gtr_markers": gtr_markers, "drum_fills": playable_drum_fills, "anim": anim_notes, "timesigs": timesigs, + "fretbars": fretbars, "vox_sp": 0, "ghost_notes": 0, + "solo_markers": playable_solo_markers, + "has_2x_kick": 0, "xplus": xplus + } + compile_args = [] + if perf_override: + compile_args.extend(["replace_perf", perf_override]) + if anim_structs['type'] == "gh6": + with open(f"{root_folder}\\conversion_files\\basic_loops.txt", "r") as f: + scripts_override = f.read() + if perf_clips: + scripts_override += f"\n{perf_clips}" + gh6_anims = [] + for gen in ["male", "female"]: + for k, v in anim_structs[f"car_{gen}_anim_struct_{song_name}"].items(): + for loops in v: + if loops not in gh6_anims: + gh6_anims.append(loops) + compile_args.extend(["add_loops", gh6_anims]) + else: + anim_structs.pop("type") + for x in anim_structs.keys(): + struct_string += f"{x}" + " = {\n" + for structs in anim_structs[x].keys(): + struct_string += "\t" + f"{structs}" + " = {\n" + for anims in anim_structs[x][structs].keys(): + struct_string += "\t\t" + f"{anims}" + f" = {anim_structs[x][structs][anims]}" + "\n" + struct_string += "\t}\n" + struct_string += "}\n" + scripts_override = struct_string + if perf_clips: + scripts_override += perf_clips + + if scripts_override: + compile_args.extend(["song_script", scripts_override]) + if ska_override: + compile_args.extend(["add_ska", ska_override]) + + compile_args.extend(["ghwt", "wtde"]) + midQB, midQS = mid_qb.make_wt_files(file_headers, qb_dict, song_name, *compile_args) + if "performance" in qb_dict: + midQB = mid_qb.add_perf_to_qb(midQB, song_name, file_headers, qb_dict, *compile_args) + wt_pak = mid_qb.create_pak_file(midQB, song_name, midQS, *compile_args) + return wt_pak def create_mid_from_qb(pakmid): song_name = pakmid[len(os.path.dirname(pakmid)) + 1:pakmid.lower().find("_s")].lower() if re.search(r'^[a-c]dlc', song_name, flags=re.IGNORECASE): @@ -3253,11 +3680,16 @@ def create_mid_from_qb(pakmid): sections_dict = get_section_dict(qb_sections, file_headers_hex) game_check = ''.join(x for x in sections_dict.keys()) gh3 = False + gha = False ghwt = False if not re.search(rf"{song_name}_song_easy", game_check, flags=re.IGNORECASE): print("GH5+ song found") elif not re.search(rf"{song_name}_drum_easy", game_check, flags=re.IGNORECASE): - print("GH3 song found") + if re.search(r"_song_aux", game_check, flags=re.IGNORECASE): + gha = True + print("GHA song found") + else: + print("GH3 song found") gh3 = True else: print("GHWT song found") @@ -3268,12 +3700,16 @@ def create_mid_from_qb(pakmid): pull_struct = 0 struct_string = "" anim_structs = [] + anim_loops = [] + qs_dict = 0 for files in song_files: if re.search(fr"songs/{song_name}\.mid\.qs$", files["file_name"], flags=re.IGNORECASE): qs_dict = get_qs_strings(files["file_data"]) note_file, qb_file, qs_file, cameras, marker_names = wt_to_5_file(sections_dict, qs_dict, song_name, convert="") instruments = read_gh5_note(convert_to_gh5_bin(note_file, "note", song_name)) + elif re.search(fr"songs/{song_name}\.mid\.qs.en$", files["file_name"], flags=re.IGNORECASE): + qs_dict = get_qs_strings(files["file_data"]) elif re.search(fr"songs/{song_name}\.note$", files["file_name"], flags=re.IGNORECASE): instruments = read_gh5_note(files["file_data"]) elif re.search(fr"songs/{song_name}\.perf$", files["file_name"], flags=re.IGNORECASE): @@ -3289,6 +3725,8 @@ def create_mid_from_qb(pakmid): clip_params = y.data_dict["params"] clip_len = round((clip_params["endframe"] - clip_params["startframe"]) / 30 / clip_params["timefactor"] * 1000) band_clips.append([clip_params["clip"],y.data_dict["time"], clip_len+y.data_dict["time"]]) + elif y.data_dict["scr"] == "Band_PlayLoop": + anim_loops.append({"text": y.data_dict["params"]["name"], "time": y.data_dict["time"]}) #print() try: timesig = sections_dict[f"{song_name}_timesig"].section_data @@ -3341,7 +3779,10 @@ def create_mid_from_qb(pakmid): tempo_data.set_seconds_array(np.array(tempo_data.songSeconds)) if gh3: - new_mid.tracks += read_gh3_note(sections_dict, tempo_data, tpb) + extra_args = [] + if gha: + extra_args.append("gha") + new_mid.tracks += read_gh3_note(sections_dict, tempo_data, tpb, "GH3", *extra_args) return new_mid, struct_string """elif ghwt: new_mid.tracks += read_gh3_note(sections_dict, tempo_data, tpb, "GHWT")""" @@ -3351,6 +3792,7 @@ def create_mid_from_qb(pakmid): gtr_events = {"name": "PART GUITAR"} bass_events = {"name": "PART BASS"} vox_events = {"name": "PART VOCALS"} + event_markers = {"name": "EVENTS"} to_pop = [] if instruments: @@ -3367,6 +3809,17 @@ def create_mid_from_qb(pakmid): elif "vocal" in x and not "marker" in x: vox_events[x] = instruments[x] to_pop.append(x) + elif "guitarmarker" in x: + event_markers[x] = instruments[x] + for marker in event_markers[x]: + qs_entry = int(marker["text"],16) + if qs_entry in qs_dict: + marker_text = qs_dict[qs_entry] + if "ENDOFSONG" in marker_text: + marker_text = "[end]" + elif marker_text.startswith("\\u[m]"): + marker_text = f"[section {marker_text[5:]}]" + marker["text"] = marker_text for x in to_pop: instruments.pop(x) for inst in [drum_events, gtr_events, bass_events, vox_events]: @@ -3384,8 +3837,13 @@ def create_mid_from_qb(pakmid): all_tracks.append(mido.merge_tracks(gh5_to_midi(inst[track], tempo_data, tpb))) elif "vocal" in track: all_tracks.append(mido.merge_tracks(gh5_to_midi(inst[track], tempo_data, tpb, vox=True))) - new_mid.tracks[-1] = mido.merge_tracks(all_tracks) + new_mid.tracks[-1] = mido.merge_tracks(all_tracks) + new_mid.add_track(event_markers["name"]) + if "guitarmarkers" in event_markers: + events = gh5_to_midi(event_markers["guitarmarkers"], tempo_data, tpb) + new_mid.tracks[-1] = mido.merge_tracks([new_mid.tracks[-1]] + events) + print() non_play = ["scripts", "anim", "triggers", "cameras", "lightshow", "crowd", "drums"] for x in non_play: try: @@ -3482,6 +3940,9 @@ def create_mid_from_qb(pakmid): band_midi[-1].append(Message("note_on", time=timeVal, note=clip_note, velocity=100)) band_midi[-1].append(Message("note_on", time=timeVal2, note=clip_note, velocity=0)) + if anim_loops: + anim_events = gh5_to_midi(anim_loops, tempo_data, tpb) + band_midi.extend(anim_events) band_midi = mido.merge_tracks(band_midi) band_midi.name = "Band_Clips" new_mid.tracks.append(band_midi) diff --git a/toolkit_variables.py b/toolkit_variables.py index 96ff2fb..50f1cc1 100644 --- a/toolkit_variables.py +++ b/toolkit_variables.py @@ -138,6 +138,50 @@ 91: 117, } +gh3_to_wt = { + 117: 34, + 116: 8, + 115: 18, + 114: 74, + 113: 46, + 112: 45, + 111: 45, + 110: 31, + 109: 41, + 108: 41, + 107: 40, + 106: 40, + 105: 40, + 104: 13, + 103: 14, + 102: 58, + 101: 57, + 100: 64, + 99: 63, + 98: 14, + 97: 20, + 96: 15, + 95: 8, + 94: 75, + 93: 74, + 92: 75, + 91: 74, + 90: 33, + 89: 25, + 88: 15, + 87: 31, + 86: 8, + 85: 29, + 84: 11, + 83: 45, + 82: 74, + 81: 33, + 79: 38, + 78: 1, + 77: 0 +} +gha_to_wt = {} + anim_struct = { "guitar": {}, "bass": {},