├── Auto_trans_GUI.py ├── GUI.py ├── README.md ├── Sil-adder-GUI.py ├── cannedbread_genon2db_GUI.py ├── config.yaml ├── convert-phonemes-seg-GUI.py ├── convert_kana_GUI.py ├── hiragana.json ├── kiritan_script_GUI.py ├── lab2seg_GUI.py ├── phoneme_grabber_GUI.py ├── phonemes.json ├── requirements.txt └── trans_convert_GUI.py /Auto_trans_GUI.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | import subprocess 4 | import glob 5 | import json 6 | from tkinter import filedialog # Import filedialog 7 | from os import path 8 | 9 | # Create a tkinter window 10 | root = tk.Tk() 11 | root.title("Auto-trans") 12 | 13 | # Import the tcl file for the Forest theme 14 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 15 | 16 | # Set the theme with the theme_use method 17 | style = ttk.Style(root) 18 | style.theme_use("forest-dark") 19 | 20 | # Create a BooleanVar with global scope 21 | trans_check_var = tk.BooleanVar() 22 | 23 | # Function to run the Auto-trans script 24 | def run_auto_trans(): 25 | # Your Auto-trans script code here 26 | jsonfile = open("hiragana.json", encoding='UTF-8', errors='ignore') 27 | pDict = json.load(jsonfile) 28 | jsonfile.close() 29 | 30 | wav_path = wav_entry.get() 31 | wav_path = wav_path + "\\" 32 | wav_files = glob.glob(f'{wav_path}*.wav') 33 | 34 | creating_ask = trans_check_var.get() 35 | auto_creating = False 36 | if creating_ask: 37 | auto_creating = True 38 | 39 | for filepath in wav_files: 40 | name = path.basename(filepath) 41 | name = path.splitext(name)[0] 42 | 43 | filepath = filepath.replace(".wav", ".trans") 44 | trans_file = open(filepath, "w+") 45 | if auto_creating: 46 | nameLen = len(name) 47 | phoneme = "Sil " 48 | findCheck = False 49 | for i in range(0, nameLen): 50 | findCheck = False 51 | if i + 1 < nameLen: 52 | for obj in pDict: 53 | if obj['kana'] == (name[i] + name[i + 1]): 54 | phoneme += obj['phoneme'] + " " 55 | findCheck = True 56 | break 57 | if not findCheck: 58 | for obj in pDict: 59 | if obj['kana'] == (name[i]): 60 | phoneme += obj['phoneme'] + " " 61 | break 62 | else: 63 | for obj in pDict: 64 | if obj['kana'] == (name[i]): 65 | phoneme += obj['phoneme'] + " " 66 | break 67 | 68 | phoneme += "Sil" 69 | 70 | trans_file.write(phoneme) 71 | phonlist = phoneme.split() 72 | phonLen = len(phonlist) 73 | for i in range(0, phonLen): 74 | if i < phonLen - 1: 75 | trans_file.write("\n[" + phonlist[i] + " " + phonlist[i + 1] + "]") 76 | else: 77 | break 78 | 79 | trans_file.close() 80 | 81 | result_text.config(state=tk.NORMAL) 82 | result_text.delete("1.0", tk.END) 83 | result_text.insert(tk.END, "Process Completed!") 84 | result_text.config(state=tk.DISABLED) 85 | 86 | def browse_wav_directory(): 87 | wav_directory = filedialog.askdirectory() # Open directory dialog 88 | wav_entry.delete(0, tk.END) # Clear any previous input 89 | wav_entry.insert(0, wav_directory) # Insert selected directory path 90 | 91 | # Create a frame for the Auto-trans page 92 | auto_trans_page = tk.Frame(root) 93 | auto_trans_page.pack(padx=20, pady=20) 94 | 95 | # Create a label and entry for the wav directory 96 | wav_label = tk.Label(auto_trans_page, text="Enter wav directory:") 97 | wav_label.pack() 98 | wav_entry = tk.Entry(auto_trans_page) 99 | wav_entry.pack() 100 | browse_button = ttk.Button(auto_trans_page, text="Browse", command=browse_wav_directory) 101 | browse_button.pack() 102 | 103 | # Create a checkbox for transcription writing 104 | trans_check = ttk.Checkbutton(auto_trans_page, text="Write transcriptions automatically", variable=trans_check_var) 105 | trans_check.pack() 106 | 107 | # Create a "Run" button 108 | run_button = ttk.Button(auto_trans_page, text="Run Auto-trans Script", command=run_auto_trans) 109 | run_button.pack() 110 | 111 | # Create a text widget for the script output 112 | result_text = tk.Text(auto_trans_page, height=5, width=40, state=tk.DISABLED) 113 | result_text.pack() 114 | 115 | # Start the tkinter main loop 116 | root.mainloop() 117 | -------------------------------------------------------------------------------- /GUI.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | import subprocess 4 | 5 | def run_script(script_path): 6 | try: 7 | process = subprocess.Popen(script_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True) 8 | stdout, stderr = process.communicate() 9 | return stdout 10 | except Exception as e: 11 | return str(e) 12 | 13 | # Create the main application window 14 | root = tk.Tk() 15 | root.title("Canned_Bread's VOCALOIDDBTOOL Swiss Army Knife") 16 | root.geometry("800x600") # Set the initial window size 17 | 18 | # Make the app responsive 19 | for i in range(3): 20 | root.columnconfigure(index=i, weight=1) 21 | for i in range(3): 22 | root.rowconfigure(index=i, weight=1) 23 | 24 | # Create a style 25 | style = ttk.Style(root) 26 | 27 | # Import the tcl file 28 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 29 | 30 | # Set the theme with the theme_use method 31 | style.theme_use("forest-dark") 32 | 33 | # Create a notebook for multiple script pages 34 | notebook = ttk.Notebook(root) 35 | 36 | # Define the "Trans Tools" page 37 | trans_tools_page = ttk.Frame(notebook) 38 | 39 | # Create a Frame for buttons on the "Trans Tools" page 40 | trans_tools_button_frame = ttk.Frame(trans_tools_page) 41 | trans_tools_button_frame.grid(row=0, column=1, padx=10, pady=(30, 10)) 42 | 43 | # Create a custom style for buttons 44 | ttk.Style().configure('Custom.TButton', background='blue', foreground='white') 45 | 46 | # Run button for "Auto_Trans" 47 | def run_auto_trans(): 48 | script_path = "Auto_trans_GUI.py" # Replace with your script path 49 | output = run_script(script_path) 50 | trans_tools_output_text.config(state=tk.NORMAL) 51 | trans_tools_output_text.delete("1.0", tk.END) 52 | trans_tools_output_text.insert(tk.END, output) 53 | trans_tools_output_text.config(state=tk.DISABLED) 54 | 55 | run_button_auto_trans = ttk.Button(trans_tools_button_frame, text="Run Auto_Trans", style='Custom.TButton', command=run_auto_trans) 56 | run_button_auto_trans.pack(pady=10) 57 | 58 | 59 | def run_trans_convert(): 60 | script_path = "trans_convert_GUI.py" 61 | output = run_script(script_path) 62 | trans_tools_output_text.config(state=tk.NORMAL) 63 | trans_tools_output_text.delete("1.0", tk.END) 64 | trans_tools_output_text.insert(tk.END, output) 65 | trans_tools_output_text.config(state=tk.DISABLED) 66 | 67 | run_button_auto_trans = ttk.Button(trans_tools_button_frame, text="Run Trans Converter", style='Custom.TButton', command=run_trans_convert) 68 | run_button_auto_trans.pack(pady=10) 69 | 70 | # Create a Label widget with the desired text 71 | trans_tools_description = ttk.Label( 72 | trans_tools_page, 73 | text="The first step in Vocaloid creation is the .trans files collection. Here you can run Auto_Trans to generate those for you (based off of WAV filename). \nTrans_converter is a tool that converts the Articulation .trans you get with auto_trans and makes them into Stationary .trans files (yes they're different).", 74 | wraplength=400 # Adjust this value as needed to fit the text within the desired width 75 | ) 76 | trans_tools_description.grid(row=2, column=1, pady=(0, 10), columnspan=3) 77 | 78 | # Output text widget for "Trans Tools" page 79 | trans_tools_output_text = tk.Text(trans_tools_page, height=10, width=40, state=tk.DISABLED) 80 | trans_tools_output_text.grid(row=1, column=1) 81 | 82 | # Define the "oto.ini _> seg file tools" page 83 | oto_ini_page = ttk.Frame(notebook) 84 | 85 | # Create a Frame for buttons on the "oto.ini _> seg file tools" page 86 | oto_ini_button_frame = ttk.Frame(oto_ini_page) 87 | oto_ini_button_frame.grid(row=0, column=1, padx=10, pady=(30, 10)) 88 | 89 | # Run button for "Your Script 1" 90 | def run_your_script_1(): 91 | script_path = "cannedbread_genon2db_GUI.py" 92 | output = run_script(script_path) 93 | oto_ini_output_text.config(state=tk.NORMAL) 94 | oto_ini_output_text.delete("1.0", tk.END) 95 | oto_ini_output_text.insert(tk.END, output) 96 | oto_ini_output_text.config(state=tk.DISABLED) 97 | 98 | run_button_your_script_1 = ttk.Button(oto_ini_button_frame, text="Run genon2db", style='Custom.TButton', command=run_your_script_1) 99 | run_button_your_script_1.pack(pady=10) 100 | 101 | # Run button for "Your Script 2" 102 | def run_your_script_2(): 103 | script_path = "lab2seg_GUI.py" 104 | output = run_script(script_path) 105 | oto_ini_output_text.config(state=tk.NORMAL) 106 | oto_ini_output_text.delete("1.0", tk.END) 107 | oto_ini_output_text.insert(tk.END, output) 108 | oto_ini_output_text.config(state=tk.DISABLED) 109 | 110 | run_button_your_script_2 = ttk.Button(oto_ini_button_frame, text="Run lab2seg", style='Custom.TButton', command=run_your_script_2) 111 | run_button_your_script_2.pack(pady=10) 112 | 113 | # Output text widget for "oto.ini _> seg file tools" page 114 | oto_ini_output_text = tk.Text(oto_ini_page, height=10, width=40, state=tk.DISABLED) 115 | oto_ini_output_text.grid(row=1, column=1) 116 | 117 | # Create a Label widget with the desired text 118 | oto_ini_description = ttk.Label( 119 | oto_ini_page, 120 | text="Genon2DB is a tool in which you can convert your oto.ini to .lab files for the next step. \nLab2Seg is where you take lab files and convert them into seg files for articulation .trans files (I need to make one for stationary because they're different too).", 121 | wraplength=400 # Adjust this value as needed to fit the text within the desired width 122 | ) 123 | oto_ini_description.grid(row=2, column=1, pady=(0, 10), columnspan=3) 124 | 125 | 126 | # Define the "Misc. Tools" page 127 | misc_tools_page = ttk.Frame(notebook) 128 | 129 | # Create a Frame for buttons on the "Misc. Tools" page 130 | misc_tools_button_frame = ttk.Frame(misc_tools_page) 131 | misc_tools_button_frame.grid(row=0, column=1, padx=10, pady=(30, 10)) 132 | 133 | # Run button for "Phoneme Grabber" 134 | def run_phoneme_grabber(): 135 | script_path = "phoneme_grabber_GUI.py" 136 | output = run_script(script_path) 137 | trans_tools_output_text.config(state=tk.NORMAL) 138 | trans_tools_output_text.delete("1.0", tk.END) 139 | trans_tools_output_text.insert(tk.END, output) 140 | trans_tools_output_text.config(state=tk.DISABLED) 141 | 142 | run_button_phoneme_grabber = ttk.Button(misc_tools_button_frame, text="Run Phoneme Grabber", style='Custom.TButton', command=run_phoneme_grabber) 143 | run_button_phoneme_grabber.pack(pady=10) 144 | 145 | # Run button for "Your Script 3" (Miscellaneous script 1) 146 | def run_misc_script_1(): 147 | script_path = "convert_kana_GUI.py" 148 | output = run_script(script_path) 149 | misc_tools_output_text.config(state=tk.NORMAL) 150 | misc_tools_output_text.delete("1.0", tk.END) 151 | misc_tools_output_text.insert(tk.END, output) 152 | misc_tools_output_text.config(state=tk.DISABLED) 153 | 154 | run_button_misc_script_1 = ttk.Button(misc_tools_button_frame, text="Run Convert Kana (lab)", style='Custom.TButton', command=run_misc_script_1) 155 | run_button_misc_script_1.pack(pady=10) 156 | 157 | # Run button for "Your Script 4" (Miscellaneous script 2) 158 | def run_misc_script_2(): 159 | script_path = "kiritan_script_GUI.py" 160 | output = run_script(script_path) 161 | misc_tools_output_text.config(state=tk.NORMAL) 162 | misc_tools_output_text.delete("1.0", tk.END) 163 | misc_tools_output_text.insert(tk.END, output) 164 | misc_tools_output_text.config(state=tk.DISABLED) 165 | 166 | run_button_misc_script_2 = ttk.Button(misc_tools_button_frame, text="Run Phoneme Transfer (wonky)", style='Custom.TButton', command=run_misc_script_2) 167 | run_button_misc_script_2.pack(pady=10) 168 | 169 | # Run button for "Your Script 5" (Miscellaneous script 3) 170 | def run_misc_script_3(): 171 | script_path = "Sil-adder-GUI.py" 172 | output = run_script(script_path) 173 | misc_tools_output_text.config(state=tk.NORMAL) 174 | misc_tools_output_text.delete("1.0", tk.END) 175 | misc_tools_output_text.insert(tk.END, output) 176 | misc_tools_output_text.config(state=tk.DISABLED) 177 | 178 | run_button_misc_script_3 = ttk.Button(misc_tools_button_frame, text="Run Sil adder", style='Custom.TButton', command=run_misc_script_3) 179 | run_button_misc_script_3.pack(pady=10) 180 | 181 | # Run button for "Your Script 6" (Miscellaneous script 4) 182 | def run_misc_script_4(): 183 | script_path = "convert-phonemes-seg-GUI.py" 184 | output = run_script(script_path) 185 | misc_tools_output_text.config(state=tk.NORMAL) 186 | misc_tools_output_text.delete("1.0", tk.END) 187 | misc_tools_output_text.insert(tk.END, output) 188 | misc_tools_output_text.config(state=tk.DISABLED) 189 | 190 | run_button_misc_script_4 = ttk.Button(misc_tools_button_frame, text="Run Phoneme converter (seg)", style='Custom.TButton', command=run_misc_script_4) 191 | run_button_misc_script_4.pack(pady=10) 192 | 193 | # Output text widget for "Misc. Tools" page 194 | misc_tools_output_text = tk.Text(misc_tools_page, height=10, width=40, state=tk.DISABLED) 195 | misc_tools_output_text.grid(row=1, column=1) 196 | 197 | # Create a Label widget with the desired text 198 | misc_tools_description = ttk.Label( 199 | misc_tools_page, 200 | text="Phoneme Grabber is a tool that takes all of your phonemes from an oto.ini file and it generates a text file with all of the phonemes in it. It's useful for editing hiragana.json or phonemes.json.\nConvert Kana (lab) is a tool that converts kana in your labs to phonemes for the seg creation.\nPhoneme transfer is an experimental script that transfers phonemes from one seg file to another (for when you have a voicebank with many pitches and you want the timings to be different but the phonemes to be the same in a set of seg files\nSil adder is a tool to add Sil phonemes to the beginning of your .seg files.\nPhoneme comverter (seg) converts phonemes in your seg files.", wraplength=400 # Adjust this value as needed to fit the text within the desired width 201 | ) 202 | misc_tools_description.grid(row=2, column=1, pady=(0, 10), columnspan=3) 203 | 204 | 205 | # Add the pages to the notebook 206 | notebook.add(trans_tools_page, text="Trans Tools") 207 | notebook.add(oto_ini_page, text="oto.ini -> seg file tools") 208 | notebook.add(misc_tools_page, text="Misc. Tools") 209 | 210 | # Pack the notebook 211 | notebook.grid(row=0, column=1, padx=10, pady=(30, 10)) 212 | 213 | # Center the window and set minsize 214 | root.update() 215 | root.minsize(root.winfo_width(), root.winfo_height()) 216 | x_cordinate = int((root.winfo_screenwidth()/2) - (root.winfo_width()/2)) 217 | y_cordinate = int((root.winfo_screenheight()/2) - (root.winfo_height()/2)) 218 | root.geometry("+{}+{}".format(x_cordinate, y_cordinate)) 219 | 220 | # Start the main loop 221 | root.mainloop() 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Canned-Bread's VOCALOIDDBTOOL Swiss Army Knife 2 | 3 | # UPDATE ON THE TOOL! 4 | 5 | Since other tools are better and more useful, I decided to archive this tool since I'm not working in the vocal synth space anymore... Sorry... 6 | 7 | Canned-Bread's VOCALOIDDBTOOL Swiss Army Knife, which allows you to do some pretty cool things for HOMEBREW VOCALOID development! 8 | 9 | Uses a modded Genon2NNSVS from https://github.com/oatsu-gh/genon2nnsvs 10 | 11 | Also uses a modded Auto-Trans from https://github.com/EgorV4/auto-trans 12 | 13 | Uses https://github.com/rdbende/Forest-ttk-theme 14 | 15 | # HOW TO RUN 16 | 17 | Have python 3.10 installed https://www.python.org/downloads/release/python-3100/ 18 | 19 | Have requirements pydub==0.25.1 and utaupy==1.18.3 installed 20 | 21 | 22 | `pip install pydub==0.25.1 utaupy==1.18.3` 23 | 24 | Run GUI.py and select what you'd like to do 25 | 26 | 27 | Please let me know if anything needs changes! 28 | 29 | DOWNLOAD FROM RELEASES IT HAS ALL FILES NEEDED! 30 | 31 | # HOW TO UPDATE 32 | 33 | Download the code in a zip and replace the python files 34 | 35 | Here's a screenshot: 36 | ![image](https://github.com/bread-in-a-can/Canned-Bread-s-VOCALOIDDBTOOL-Swiss-Army-Knife/assets/124006393/7b2edccf-2bfe-47c6-b1e7-723d076b7661) 37 | 38 | 39 | 40 | 41 | Yamaha plz don't kill me 42 | -------------------------------------------------------------------------------- /Sil-adder-GUI.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import tkinter as tk 4 | from tkinter import ttk 5 | from tkinter import filedialog 6 | 7 | 8 | def add_sil_phonemes(input_directory, output_directory, add_sil_at_beginning, add_sil_at_end): 9 | # Create the output directory if it doesn't exist 10 | if not os.path.exists(output_directory): 11 | os.makedirs(output_directory) 12 | 13 | # Process each seg file in the input directory 14 | for seg_file_name in os.listdir(input_directory): 15 | if seg_file_name.endswith(".seg"): 16 | input_seg_file = os.path.join(input_directory, seg_file_name) 17 | output_seg_file = os.path.join(output_directory, seg_file_name) 18 | 19 | # Read the original .seg file 20 | with open(input_seg_file, 'r') as infile: 21 | lines = infile.readlines() 22 | 23 | # Find the line index where the "=====" line appears 24 | separator_line_index = lines.index("=================================================\n") 25 | 26 | # Extract the timing information from the first non-empty phoneme line 27 | first_non_empty_phoneme_start = None 28 | first_non_empty_phoneme_end = None 29 | 30 | for line in lines[separator_line_index + 1:]: 31 | phoneme_values = line.strip().split('\t') 32 | if len(phoneme_values) >= 3: 33 | first_non_empty_phoneme_start = phoneme_values[1] 34 | first_non_empty_phoneme_end = phoneme_values[2] 35 | break 36 | 37 | if add_sil_at_beginning: 38 | # Insert "Sil" at the top with the calculated end time 39 | new_sil_line = f"Sil\t\t0.000000\t\t{first_non_empty_phoneme_end}\n" 40 | lines.insert(separator_line_index + 1, new_sil_line) 41 | 42 | if add_sil_at_end: 43 | # Calculate the number of phonemes 44 | num_phonemes = len(lines) - separator_line_index 45 | 46 | # Update the "nPhonemes" line with the calculated count 47 | for i, line in enumerate(lines): 48 | if line.startswith("nPhonemes"): 49 | current_value = int(line.split()[-1]) 50 | new_value = current_value + 1 51 | lines[i] = f"nPhonemes {new_value}\n" 52 | break 53 | 54 | # Find the last non-empty phoneme's end time 55 | last_non_empty_phoneme_end = first_non_empty_phoneme_end 56 | for line in reversed(lines[separator_line_index + 1:]): 57 | phoneme_values = line.strip().split('\t') 58 | if len(phoneme_values) >= 3: 59 | last_non_empty_phoneme_end = phoneme_values[2] 60 | break 61 | 62 | # Find the largest end time in the .seg file 63 | largest_end_time = max(float(phoneme_values[2]) for line in lines[separator_line_index + 1:]) 64 | 65 | # Format the largest_end_time to include all decimals and zero if it's a whole number 66 | if largest_end_time.is_integer(): 67 | largest_end_time = f"{largest_end_time:.6f}" 68 | else: 69 | largest_end_time = str(largest_end_time) 70 | 71 | # Set the "Sil" at the end with the largest end time 72 | lines[-1] = f"Sil\t\t{last_non_empty_phoneme_end}\t\t{last_non_empty_phoneme_end}\n" 73 | 74 | 75 | # Open the output .seg file for writing 76 | with open(output_seg_file, 'w') as outfile: 77 | # Write the entire modified list back to the file 78 | outfile.writelines(lines) 79 | 80 | print("Sil phonemes added to", seg_file_name) 81 | 82 | print("All modifications complete.") 83 | 84 | def browse_input_directory(): 85 | input_directory = filedialog.askdirectory() 86 | input_directory_entry.delete(0, tk.END) 87 | input_directory_entry.insert(0, input_directory) 88 | 89 | def browse_output_directory(): 90 | output_directory = filedialog.askdirectory() 91 | output_directory_entry.delete(0, tk.END) 92 | output_directory_entry.insert(0, output_directory) 93 | 94 | def execute_script(): 95 | input_directory = input_directory_entry.get() 96 | output_directory = output_directory_entry.get() 97 | add_sil_at_beginning = add_sil_at_beginning_var.get() 98 | add_sil_at_end = add_sil_at_end_var.get() 99 | add_sil_phonemes(input_directory, output_directory, add_sil_at_beginning, add_sil_at_end) 100 | result_label.config(text="Modifications complete.") 101 | 102 | # Create the main window 103 | root = tk.Tk() 104 | root.title("Add 'Sil' Phonemes") 105 | 106 | # Import the tcl file 107 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 108 | 109 | # Set the theme with the theme_use method 110 | style = ttk.Style() 111 | style.theme_use("forest-dark") 112 | 113 | # Create and configure widgets 114 | input_directory_label = ttk.Label(root, text="Input Directory:") 115 | input_directory_entry = ttk.Entry(root) 116 | browse_input_button = ttk.Button(root, text="Browse", command=browse_input_directory) 117 | 118 | output_directory_label = ttk.Label(root, text="Output Directory:") 119 | output_directory_entry = ttk.Entry(root) 120 | browse_output_button = ttk.Button(root, text="Browse", command=browse_output_directory) 121 | 122 | add_sil_at_beginning_var = tk.BooleanVar() 123 | add_sil_at_beginning_checkbox = ttk.Checkbutton(root, text="Add 'Sil' at the beginning", variable=add_sil_at_beginning_var) 124 | 125 | add_sil_at_end_var = tk.BooleanVar() 126 | add_sil_at_end_checkbox = ttk.Checkbutton(root, text="Add 'Sil' at the end (EXPERIMENTAL/BROKEN)", variable=add_sil_at_end_var) 127 | 128 | execute_button = ttk.Button(root, text="Execute", command=execute_script) 129 | result_label = ttk.Label(root, text="") 130 | 131 | # Layout widgets 132 | input_directory_label.grid(row=0, column=0, padx=10, pady=5) 133 | input_directory_entry.grid(row=0, column=1, padx=10, pady=5) 134 | browse_input_button.grid(row=0, column=2, padx=10, pady=5) 135 | 136 | output_directory_label.grid(row=1, column=0, padx=10, pady=5) 137 | output_directory_entry.grid(row=1, column=1, padx=10, pady=5) 138 | browse_output_button.grid(row=1, column=2, padx=10, pady=5) 139 | 140 | add_sil_at_beginning_checkbox.grid(row=2, column=1, padx=10, pady=5) 141 | add_sil_at_end_checkbox.grid(row=3, column=1, padx=10, pady=5) 142 | 143 | execute_button.grid(row=4, column=1, padx=10, pady=10) 144 | result_label.grid(row=5, column=1, padx=10, pady=5) 145 | 146 | root.mainloop() 147 | -------------------------------------------------------------------------------- /cannedbread_genon2db_GUI.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk, filedialog 3 | import utaupy as up 4 | from os.path import abspath, basename, dirname, isdir, join 5 | from shutil import copy 6 | from pydub import AudioSegment 7 | from operator import attrgetter 8 | from glob import glob 9 | from os import makedirs 10 | 11 | NOTENAME_TO_NOTENUM_DICT = { 12 | "C3": 48, "D3": 50, "E3": 52, "F3": 53, "G3": 55, "A3": 57, "B3": 59, 13 | "C4": 60, "D4": 62, "E4": 64, "F4": 65, "G4": 67, "A4": 69, "B4": 71, 14 | "C5": 72 15 | } 16 | 17 | class USTGeneratorApp: 18 | def __init__(self, root): 19 | self.root = root 20 | self.root.title("Genon2DB") 21 | self.style = ttk.Style(self.root) 22 | 23 | # Import the tcl file for the Forest theme 24 | self.root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 25 | 26 | # Set the theme with the theme_use method 27 | self.style.theme_use("forest-dark") 28 | 29 | self.frame = ttk.Frame(self.root, padding=(10, 10)) 30 | self.frame.grid(row=0, column=0) 31 | 32 | self.create_widgets() 33 | self.create_output_console() 34 | 35 | def create_output_console(self): 36 | self.output_console = tk.Text(self.frame, wrap=tk.WORD, width=60, height=20) 37 | self.output_console.grid(row=8, column=0, columnspan=3, pady=10) 38 | self.scrollbar = ttk.Scrollbar(self.frame, command=self.output_console.yview) 39 | self.output_console.config(yscrollcommand=self.scrollbar.set) 40 | self.scrollbar.grid(row=8, column=3, sticky='nsew') 41 | 42 | def update_output_console(self, message): 43 | self.output_console.insert(tk.INSERT, message) 44 | self.output_console.see(tk.INSERT) 45 | 46 | def create_widgets(self): 47 | self.label1 = ttk.Label(self.frame, text="Original Configuration File:") 48 | self.label1.grid(row=0, column=0, sticky="w") 49 | 50 | self.otoini_path = tk.StringVar() 51 | self.otoini_entry = ttk.Entry(self.frame, textvariable=self.otoini_path) 52 | self.otoini_entry.grid(row=0, column=1, padx=10) 53 | 54 | self.browse_otoini_button = ttk.Button(self.frame, text="Browse", command=self.browse_otoini) 55 | self.browse_otoini_button.grid(row=0, column=2) 56 | 57 | self.label4 = ttk.Label(self.frame, text="Table File:") 58 | self.label4.grid(row=1, column=0, sticky="w") 59 | 60 | self.table_path = tk.StringVar() 61 | self.table_entry = ttk.Entry(self.frame, textvariable=self.table_path) 62 | self.table_entry.grid(row=1, column=1, padx=10) 63 | 64 | self.browse_table_button = ttk.Button(self.frame, text="Browse", command=self.browse_table) 65 | self.browse_table_button.grid(row=1, column=2) 66 | 67 | self.label6 = ttk.Label(self.frame, text="Output Directory:") 68 | self.label6.grid(row=2, column=0, sticky="w") 69 | 70 | self.output_dir_path = tk.StringVar() 71 | self.output_dir_entry = ttk.Entry(self.frame, textvariable=self.output_dir_path) 72 | self.output_dir_entry.grid(row=2, column=1, padx=10) 73 | 74 | self.browse_output_dir_button = ttk.Button(self.frame, text="Browse", command=self.browse_output_dir) 75 | self.browse_output_dir_button.grid(row=2, column=2) 76 | 77 | self.label2 = ttk.Label(self.frame, text="Tempo:") 78 | self.label2.grid(row=3, column=0, sticky="w") 79 | 80 | self.tempo = tk.DoubleVar() 81 | self.tempo_entry = ttk.Entry(self.frame, textvariable=self.tempo) 82 | self.tempo_entry.grid(row=3, column=1, padx=10) 83 | 84 | self.label3 = ttk.Label(self.frame, text="Initial Pause Length in beats (put auto for auto estimation):") 85 | self.label3.grid(row=4, column=0, sticky="w") 86 | 87 | self.pause_length = tk.StringVar() 88 | self.pause_length_entry = ttk.Entry(self.frame, textvariable=self.pause_length) 89 | self.pause_length_entry.grid(row=4, column=1, padx=10) 90 | 91 | self.label5 = ttk.Label(self.frame, text="Note:") 92 | self.label5.grid(row=5, column=0, sticky="w") 93 | 94 | self.note = tk.StringVar() 95 | self.note_entry = ttk.Entry(self.frame, textvariable=self.note) 96 | self.note_entry.grid(row=5, column=1, padx=10) 97 | 98 | self.uta_vcv = tk.BooleanVar() 99 | self.uta_vcv_checkbox = ttk.Checkbutton(self.frame, text="Song Continuous Phoneme Recording (UTA VCV Mode)", 100 | variable=self.uta_vcv) 101 | self.uta_vcv_checkbox.grid(row=6, column=1) 102 | 103 | self.generate_button = ttk.Button(self.frame, text="Generate DB", command=self.generate_ust) 104 | self.generate_button.grid(row=7, column=0, columnspan=3) 105 | 106 | def browse_otoini(self): 107 | otoini_path = filedialog.askopenfilename(filetypes=[("oto.ini files", "*.ini")]) 108 | self.otoini_path.set(otoini_path) 109 | 110 | def browse_table(self): 111 | table_path = filedialog.askopenfilename(filetypes=[("Table files", "*.table")]) 112 | self.table_path.set(table_path) 113 | 114 | def browse_output_dir(self): 115 | output_dir = filedialog.askdirectory() 116 | self.output_dir_path.set(output_dir) 117 | 118 | def force_otoinifile_cutoff_negative(self, path_otoini_in, path_otoini_out): 119 | otoini = up.otoini.load(path_otoini_in) 120 | voice_dir = dirname(path_otoini_in) 121 | if any([oto.cutoff > 0 for oto in otoini]): 122 | for oto in otoini: 123 | path_wav = join(voice_dir, oto.filename) 124 | sound = AudioSegment.from_file(path_wav, 'wav') 125 | duration_ms = 1000 * sound.duration_seconds 126 | absolute_cutoff_position = duration_ms - oto.cutoff 127 | oto.cutoff = -(absolute_cutoff_position - oto.offset) 128 | otoini.write(path_otoini_out) 129 | 130 | def prepare_otoini(self, otoini): 131 | otoini.data = [ 132 | oto for oto in otoini if all( 133 | [' ' in oto.alias, '息' not in oto.alias, 'を' not in oto.alias] 134 | ) 135 | ] 136 | for oto in otoini: 137 | oto.alias = oto.alias.split()[-1].replace('-', 'R') 138 | otoini.data = sorted(otoini.data, key=attrgetter('filename', 'offset')) 139 | 140 | def split_otoini(self, otoini): 141 | l_2d = [] 142 | filename = '' 143 | for oto in otoini: 144 | if filename != oto.filename: 145 | filename = oto.filename 146 | temp_otoini = up.otoini.OtoIni() 147 | l_2d.append(temp_otoini) 148 | temp_otoini.append(oto) 149 | return l_2d 150 | 151 | def generate_ustobj(self, otoini, notenum, tempo, pause_length_by_beat): 152 | ust = up.ust.Ust() 153 | ust.version = 1.20 154 | if not otoini: 155 | return ust 156 | 157 | note = up.ust.Note() 158 | note.lyric = 'R' 159 | note.tempo = tempo 160 | note.notenum = notenum 161 | duration_ms = (otoini[0].offset + otoini[0].preutterance) 162 | if pause_length_by_beat == 'auto': 163 | note.length = 60 * round(duration_ms * tempo / 7500) 164 | else: 165 | try: 166 | pause_length_by_beat = int(pause_length_by_beat) 167 | note.length = int(pause_length_by_beat * 480) 168 | except ValueError: 169 | print("Invalid pause length. Please enter a valid number or 'auto'.") 170 | ust.notes.append(note) 171 | 172 | for oto in otoini: 173 | note = up.ust.Note() 174 | note.lyric = oto.alias 175 | note.tempo = tempo 176 | note.notenum = notenum 177 | note.length = 480 178 | ust.notes.append(note) 179 | 180 | if len(otoini) >= 2: 181 | duration_ms = ( 182 | (otoini[-1].offset + otoini[-1].preutterance) - 183 | (otoini[-2].offset + otoini[-2].preutterance) 184 | ) 185 | ust.notes[-2].length = 60 * round(duration_ms * tempo / 7500) 186 | duration_ms = (- otoini[-1].cutoff) - otoini[-1].preutterance 187 | ust.notes[-1].length = 60 * round(duration_ms * tempo / 7500) 188 | 189 | return ust 190 | 191 | def configure_notenum_for_uta_vcv(self, ust): 192 | for i, note in enumerate(ust.notes[1:-1], 1): 193 | if i % 2 == 0: 194 | note.notenum += 1 195 | else: 196 | note.notenum -= 1 197 | ust.notes[0].notenum = ust.notes[1].notenum 198 | ust.notes[-1].notenum = ust.notes[-2].notenum 199 | 200 | def generate_labelobj(self, otoini, d_table): 201 | for oto in otoini: 202 | oto.alias = ' '.join(d_table.get(oto.alias, [oto.alias])) 203 | label = up.convert.otoini2label(otoini, mode='romaji_cv') 204 | for phoneme in label: 205 | if phoneme.symbol == 'sil': 206 | phoneme.symbol = 'pau' 207 | return label 208 | 209 | def generate_labfile(self, path_otoini, path_table, out_dir, tempo, notename, uta_vcv_mode, pause_length): 210 | self.force_otoinifile_cutoff_negative(path_otoini, path_otoini) 211 | otoini = up.otoini.load(path_otoini) 212 | self.prepare_otoini(otoini) 213 | otoini_2d = self.split_otoini(otoini) 214 | makedirs(join(out_dir, 'lab'), exist_ok=True) 215 | makedirs(join(out_dir, 'ust'), exist_ok=True) 216 | makedirs(join(out_dir, 'wav'), exist_ok=True) 217 | 218 | # Load the .table file for note mapping 219 | table = self.load_table_file(path_table) 220 | 221 | for otoini in otoini_2d: 222 | name = otoini[0].filename.replace('.wav', '') 223 | ust = self.generate_ustobj(otoini, NOTENAME_TO_NOTENUM_DICT.get(notename, 60), tempo, pause_length) 224 | 225 | if uta_vcv_mode: 226 | self.configure_notenum_for_uta_vcv(ust) 227 | 228 | ust.write(join(out_dir, 'ust', f'{name}.ust')) 229 | mono_label = self.generate_labelobj(otoini, table) 230 | mono_label.write(join(out_dir, 'lab', f'{name}.lab')) 231 | copy(join(dirname(path_otoini), f'{name}.wav'), join(out_dir, 'wav', f'{name}.wav')) 232 | 233 | print('UST and label files generated successfully.') 234 | 235 | def load_table_file(self, path_table): 236 | table = {} 237 | with open(path_table, 'r', encoding='utf-8') as file: 238 | for line in file: 239 | parts = line.strip().split('\t') 240 | if len(parts) == 2: 241 | key, value = parts 242 | table[key] = value 243 | return table 244 | 245 | def mono2full_and_round(self, mono_align_dir, full_score_dir, prefix): 246 | mono_label_files = glob(f'{mono_align_dir}/{prefix}*.lab') 247 | for path_mono in mono_label_files: 248 | path_full = join(full_score_dir, basename(path_mono)) 249 | mono_label = up.label.load(path_mono) 250 | full_label = up.label.load(path_full) 251 | if len(mono_label) != len(full_label): 252 | print(f"Error: Mismatch in the number of phonemes in {basename(path_mono)}") 253 | continue 254 | for mono_phoneme, full_phoneme in zip(mono_label, full_label): 255 | mono_phoneme.symbol = full_phoneme.symbol 256 | mono_label.round(50000) 257 | mono_label.data = [phoneme for phoneme in mono_label.data if phoneme.symbol != 'sil' and phoneme.symbol.strip()] 258 | mono_label.write(path_mono) 259 | 260 | print('Mono-label files converted to full-label files and rounded.') 261 | 262 | def guess_notename_from_prefix(self, prefix, d_notename2notenum): 263 | notenames = d_notename2notenum.keys() 264 | for notename in notenames: 265 | if prefix in notename: 266 | return notename 267 | return None 268 | 269 | def generate_ust(self): 270 | out_dir = self.output_dir_path.get() 271 | path_otoini = self.otoini_path.get() 272 | path_table = self.table_path.get() 273 | tempo = self.tempo.get() 274 | pause_length = self.pause_length.get() 275 | prefix = basename(dirname(path_otoini)) 276 | notename = self.note.get() 277 | uta_vcv_mode = self.uta_vcv.get() 278 | 279 | self.update_output_console('Converting oto.ini to label files and UST files and copying WAV files.\n') 280 | self.generate_labfile(path_otoini, path_table, out_dir, tempo, notename, uta_vcv_mode, pause_length) 281 | 282 | self.update_output_console('Converting mono-label files to full-label files and rounding them.\n') 283 | self.mono2full_and_round(join(out_dir, 'lab'), out_dir, prefix) 284 | 285 | self.update_output_console(f'All files were successfully saved to {abspath(out_dir)}\n') 286 | 287 | if __name__ == '__main__': 288 | root = tk.Tk() 289 | app = USTGeneratorApp(root) 290 | root.mainloop() 291 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | ### General settings. 2 | ## The name of singer 3 | spk: raina_cute 4 | 5 | ## exp tag(for managing experiments) 6 | tag: jp_qst_crazy_mono_007_enunu_173D 7 | 8 | ## Output directory 9 | # 学習に使うデータを置く場所。genon_db_dir からここにファイルをコピーしてから学習する。 10 | # All the generated labels, intermediate files, and segmented wav files 11 | # will be saved in the following directory 12 | out_dir: ./data 13 | 14 | ## Songs to be excluded from trainig. 15 | # NOTE: ここ未実装 16 | exclude_songs: [] 17 | 18 | ## table file for translating lyrics to phonemes. 19 | # NOTE: ラベル作るときに手動指定するので必要ない気がする。 20 | # TODO: 母音と母音の区別をつけるためのconfファイルも使うようにする。 21 | sinsy_dic: ./dic 22 | table_path: ./dic/intunist_jp_compatibility.table 23 | 24 | ## HTS-style question used for extracting musical/linguistic context from musicxml files 25 | question_path: ./hed/jp_qst_crazy_mono_007_enunu_173D.hed 26 | 27 | # Audio sampling rate 28 | # CAUTION: Changing sample_rate may affect the dimension number of acoustic features. 29 | # DO NOT CHANGE this unless you know the relationship between the dim of bap and sample_rate. 30 | sample_rate: 44100 31 | 32 | ########################################################### 33 | # FEATURE EXTRACTION SETTING # 34 | ########################################################### 35 | 36 | timelag_features: defaults 37 | duration_features: defaults 38 | acoustic_features: static_deltadelta 39 | 40 | ########################################################### 41 | # TRAINING SETTING # 42 | ########################################################### 43 | 44 | # Models 45 | # To customize, put your config or change ones in 46 | # conf/train/{timelag,duration,acoustic}/ and 47 | # specify the config name below 48 | # NOTE: *_model: model definition, *_train: general train configs, 49 | # *_data: data configs (e.g., batch size) 50 | 51 | timelag_model: timelag_ffn 52 | timelag_train: myconfig 53 | timelag_data: myconfig 54 | 55 | duration_model: duration_lstm 56 | duration_train: myconfig 57 | duration_data: myconfig 58 | 59 | acoustic_model: acoustic_conv 60 | acoustic_train: myconfig 61 | acoustic_data: myconfig 62 | 63 | # Pretrained model dir (leave empty to disable) 64 | pretrained_expdir: 65 | 66 | ########################################################### 67 | # SYNTHESIS SETTING # 68 | ########################################################### 69 | timelag_synthesis: defaults 70 | duration_synthesis: defaults 71 | acoustic_synthesis: defaults 72 | 73 | # latest.pth or best.pth 74 | timelag_eval_checkpoint: best_loss.pth 75 | duration_eval_checkpoint: best_loss.pth 76 | acoustic_eval_checkpoint: best_loss.pth 77 | -------------------------------------------------------------------------------- /convert-phonemes-seg-GUI.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import tkinter as tk 4 | from tkinter import ttk 5 | from tkinter import filedialog 6 | from tkinter import messagebox 7 | 8 | # Load the phoneme JSON data 9 | script_directory = os.path.dirname(os.path.abspath(__file__)) 10 | phonemes_json_path = os.path.join(script_directory, 'phonemes.json') 11 | 12 | with open(phonemes_json_path, 'r') as file: 13 | phonemes_data = json.load(file) 14 | 15 | # Create a dictionary for easy lookup 16 | phonemes_dict = {entry['kana']: entry['phoneme'] for entry in phonemes_data} 17 | 18 | # Function to convert kana sequence to phonemes 19 | def convert_kana_sequence_to_phonemes(kana_sequence): 20 | words = kana_sequence.split() 21 | phonemes = [] 22 | for word in words: 23 | phoneme = phonemes_dict.get(word, word) 24 | phonemes.append(phoneme) 25 | return ' '.join(phonemes) 26 | 27 | # Function to process a single .seg file 28 | def process_seg_file(input_path): 29 | with open(input_path, 'r', encoding='utf-8') as seg_file: 30 | seg_lines = seg_file.read().split('\n') 31 | 32 | converted_lines = [] 33 | convert_lines = False 34 | header_lines = [] 35 | 36 | for line in seg_lines: 37 | if line.startswith('='): 38 | convert_lines = True 39 | header_lines.append(line) 40 | elif convert_lines: 41 | parts = line.split() 42 | if len(parts) == 3: 43 | kana_sequence, start_time, end_time = parts 44 | phoneme_sequence = convert_kana_sequence_to_phonemes(kana_sequence) 45 | converted_lines.append(f"{phoneme_sequence}\t\t{start_time}\t\t{end_time}") 46 | else: 47 | converted_lines.append(line) 48 | 49 | n_phonemes = len(converted_lines) # Subtract 1 to exclude the "=====" line 50 | header_lines.insert(0, f'nPhonemes {n_phonemes}') 51 | header_lines.insert(1, 'articulationsAreStationaries 0') 52 | header_lines.insert(2, 'phoneme BeginTime EndTime') 53 | 54 | converted_content = '\n'.join(header_lines + converted_lines) 55 | 56 | with open(input_path, 'w', encoding='utf-8') as seg_file: 57 | seg_file.write(converted_content) 58 | 59 | def browse_directory(): 60 | directory = filedialog.askdirectory() 61 | if directory: 62 | directory_entry.delete(0, tk.END) 63 | directory_entry.insert(0, directory) 64 | 65 | def convert_files(): 66 | directory = directory_entry.get() 67 | 68 | if not directory: 69 | messagebox.showerror("Error", "Please select a directory.") 70 | return 71 | 72 | for filename in os.listdir(directory): 73 | if filename.endswith(".seg"): 74 | input_path = os.path.join(directory, filename) 75 | process_seg_file(input_path) 76 | 77 | messagebox.showinfo("Success", "Conversion complete.") 78 | 79 | # Create the main application window 80 | root = tk.Tk() 81 | root.title("SEG File Conversion") 82 | 83 | # Create a style 84 | style = ttk.Style(root) 85 | 86 | # Import the tcl file 87 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 88 | 89 | # Set the theme with the theme_use method 90 | style.theme_use("forest-dark") 91 | 92 | # Create a frame for the input fields 93 | input_frame = ttk.Frame(root, padding=10) 94 | input_frame.grid(row=0, column=0, padx=10, pady=10, sticky='w') 95 | 96 | # Directory selection 97 | directory_label = ttk.Label(input_frame, text='Select Directory:') 98 | directory_label.grid(row=0, column=0, sticky='w') 99 | 100 | directory_entry = ttk.Entry(input_frame, width=40) 101 | directory_entry.grid(row=0, column=1, padx=(5, 0), sticky='w') 102 | 103 | directory_button = ttk.Button(input_frame, text='Browse', command=browse_directory) 104 | directory_button.grid(row=0, column=2, padx=(5, 0), sticky='w') 105 | 106 | # Convert button 107 | convert_button = ttk.Button(root, text='Convert SEG Files', command=convert_files) 108 | convert_button.grid(row=1, column=0, padx=10, pady=10) 109 | 110 | # Start the GUI application 111 | root.mainloop() 112 | -------------------------------------------------------------------------------- /convert_kana_GUI.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import re 4 | import argparse 5 | import tkinter as tk 6 | from tkinter import ttk 7 | from tkinter import filedialog 8 | from tkinter import messagebox 9 | 10 | # Load the hiragana JSON data from the same directory as the script 11 | script_directory = os.path.dirname(os.path.abspath(__file__)) 12 | hiragana_json_path = os.path.join(script_directory, 'hiragana.json') 13 | 14 | with open(hiragana_json_path, 'r', encoding='utf-8') as file: 15 | hiragana_data = json.load(file) 16 | 17 | # Create a dictionary for easy lookup 18 | hiragana_dict = {entry['kana']: entry['phoneme'] for entry in hiragana_data} 19 | 20 | # Create a set to store unknown kana characters 21 | unknown_kana = set() 22 | 23 | # Function to convert kana sequence to phonemes 24 | def convert_kana_sequence_to_phonemes(kana_sequence): 25 | words = kana_sequence.split() 26 | phonemes = [] 27 | for word in words: 28 | phoneme = hiragana_dict.get(word, word) 29 | if phoneme == word: 30 | unknown_kana.add(word) # Add unknown kana characters to the set 31 | phonemes.append(phoneme) 32 | return ' '.join(phonemes) 33 | 34 | # Function to process a single .lab file 35 | def process_lab_file(input_path): 36 | with open(input_path, 'r', encoding='utf-8') as lab_file: 37 | lab_content = lab_file.read() 38 | 39 | lines = lab_content.split('\n') 40 | converted_lines = [] 41 | 42 | for line in lines: 43 | if not line: 44 | continue 45 | parts = line.split() 46 | if len(parts) == 3: 47 | start_time, end_time, kana_sequence = parts 48 | phoneme_sequence = convert_kana_sequence_to_phonemes(kana_sequence) 49 | converted_lines.append(f"{start_time} {end_time} {phoneme_sequence}") 50 | 51 | converted_lab_content = '\n'.join(converted_lines) 52 | 53 | with open(input_path, 'w', encoding='utf-8') as lab_file: 54 | lab_file.write(converted_lab_content) 55 | 56 | def browse_labs_directory(): 57 | directory = filedialog.askdirectory() 58 | if directory: 59 | labs_directory_entry.delete(0, tk.END) 60 | labs_directory_entry.insert(0, directory) 61 | 62 | def convert_labs(): 63 | labs_directory = labs_directory_entry.get() 64 | 65 | if not os.path.exists(labs_directory): 66 | messagebox.showerror("Error", "LABs directory does not exist.") 67 | return 68 | 69 | for filename in os.listdir(labs_directory): 70 | if filename.endswith(".lab"): 71 | input_path = os.path.join(labs_directory, filename) 72 | process_lab_file(input_path) 73 | 74 | # Create a text file to store unknown kana characters 75 | unknown_kana_file_path = os.path.join(script_directory, 'unknown_kana.txt') 76 | with open(unknown_kana_file_path, 'w', encoding='utf-8') as unknown_kana_file: 77 | for kana in unknown_kana: 78 | unknown_kana_file.write(kana + '\n') 79 | 80 | messagebox.showinfo("Success", "Conversion complete. Unknown kana characters saved to 'unknown_kana.txt'.") 81 | 82 | # Create the main application window 83 | root = tk.Tk() 84 | root.title("Kana to Phonemes converter (lab)") 85 | 86 | # Import the tcl file 87 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 88 | 89 | # Set the theme with the theme_use method 90 | style = ttk.Style() 91 | style.theme_use("forest-dark") 92 | 93 | # Create a frame for the input fields 94 | input_frame = ttk.Frame(root, padding=10) 95 | input_frame.grid(row=0, column=0, padx=10, pady=10, sticky='w') 96 | 97 | # LABs directory 98 | labs_directory_label = ttk.Label(input_frame, text='LABs Directory:') 99 | labs_directory_label.grid(row=0, column=0, sticky='w') 100 | 101 | labs_directory_entry = ttk.Entry(input_frame, width=40) 102 | labs_directory_entry.grid(row=0, column=1, padx=(5, 0), sticky='w') 103 | 104 | labs_directory_button = ttk.Button(input_frame, text='Browse', command=browse_labs_directory) 105 | labs_directory_button.grid(row=0, column=2, padx=(5, 0), sticky='w') 106 | 107 | # Convert button 108 | convert_button = ttk.Button(root, text='Convert LABs', command=convert_labs) 109 | convert_button.grid(row=1, column=0, padx=10, pady=10) 110 | 111 | # Start the GUI application 112 | root.mainloop() 113 | -------------------------------------------------------------------------------- /hiragana.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kana": "あ", 4 | "romaji": "a", 5 | "phoneme": "a", 6 | "type": "gojuuon" 7 | }, 8 | { 9 | "kana": "い", 10 | "romaji": "i", 11 | "phoneme": "i", 12 | "type": "gojuuon" 13 | }, 14 | { 15 | "kana": "う", 16 | "romaji": "u", 17 | "phoneme": "M", 18 | "type": "gojuuon" 19 | }, 20 | { 21 | "kana": "うぃ", 22 | "romaji": "wi", 23 | "phoneme": "w i", 24 | "type": "gojuuon" 25 | }, 26 | { 27 | "kana": "うぉ", 28 | "romaji": "wo", 29 | "phoneme": "w o", 30 | "type": "gojuuon" 31 | }, 32 | { 33 | "kana": "うぇ", 34 | "romaji": "we", 35 | "phoneme": "w e", 36 | "type": "gojuuon" 37 | }, 38 | { 39 | "kana": "え", 40 | "romaji": "e", 41 | "phoneme": "e", 42 | "type": "gojuuon" 43 | }, 44 | { 45 | "kana": "お", 46 | "romaji": "o", 47 | "phoneme": "o", 48 | "type": "gojuuon" 49 | }, 50 | { 51 | "kana": "か", 52 | "romaji": "ka", 53 | "phoneme": "k a", 54 | "type": "gojuuon" 55 | }, 56 | { 57 | "kana": "き", 58 | "romaji": "ki", 59 | "phoneme": "k' i", 60 | "type": "gojuuon" 61 | }, 62 | { 63 | "kana": "く", 64 | "romaji": "ku", 65 | "phoneme": "k M", 66 | "type": "gojuuon" 67 | }, 68 | { 69 | "kana": "け", 70 | "romaji": "ke", 71 | "phoneme": "k e", 72 | "type": "gojuuon" 73 | }, 74 | { 75 | "kana": "こ", 76 | "romaji": "ko", 77 | "phoneme": "k o", 78 | "type": "gojuuon" 79 | }, 80 | { 81 | "kana": "さ", 82 | "romaji": "sa", 83 | "phoneme": "s a", 84 | "type": "gojuuon" 85 | }, 86 | { 87 | "kana": "し", 88 | "romaji": "shi", 89 | "phoneme": "S i", 90 | "type": "gojuuon" 91 | }, 92 | { 93 | "kana": "す", 94 | "romaji": "su", 95 | "phoneme": "s M", 96 | "type": "gojuuon" 97 | }, 98 | { 99 | "kana": "せ", 100 | "romaji": "se", 101 | "phoneme": "s e", 102 | "type": "gojuuon" 103 | }, 104 | { 105 | "kana": "そ", 106 | "romaji": "so", 107 | "phoneme": "s o", 108 | "type": "gojuuon" 109 | }, 110 | { 111 | "kana": "た", 112 | "romaji": "ta", 113 | "phoneme": "t a", 114 | "type": "gojuuon" 115 | }, 116 | { 117 | "kana": "ち", 118 | "romaji": "chi", 119 | "phoneme": "tS i", 120 | "type": "gojuuon" 121 | }, 122 | { 123 | "kana": "つ", 124 | "romaji": "tsu", 125 | "phoneme": "tS M", 126 | "type": "gojuuon" 127 | }, 128 | { 129 | "kana": "て", 130 | "romaji": "te", 131 | "phoneme": "t e", 132 | "type": "gojuuon" 133 | }, 134 | { 135 | "kana": "と", 136 | "romaji": "to", 137 | "phoneme": "t o", 138 | "type": "gojuuon" 139 | }, 140 | { 141 | "kana": "な", 142 | "romaji": "na", 143 | "phoneme": "n a", 144 | "type": "gojuuon" 145 | }, 146 | { 147 | "kana": "に", 148 | "romaji": "ni", 149 | "phoneme": "J i", 150 | "type": "gojuuon" 151 | }, 152 | { 153 | "kana": "ぬ", 154 | "romaji": "nu", 155 | "phoneme": "n M", 156 | "type": "gojuuon" 157 | }, 158 | { 159 | "kana": "ね", 160 | "romaji": "ne", 161 | "phoneme": "n e", 162 | "type": "gojuuon" 163 | }, 164 | { 165 | "kana": "の", 166 | "romaji": "no", 167 | "phoneme": "n o", 168 | "type": "gojuuon" 169 | }, 170 | { 171 | "kana": "は", 172 | "romaji": "ha", 173 | "phoneme": "h a", 174 | "type": "gojuuon" 175 | }, 176 | { 177 | "kana": "ひ", 178 | "romaji": "hi", 179 | "phoneme": "C i", 180 | "type": "gojuuon" 181 | }, 182 | { 183 | "kana": "ふ", 184 | "romaji": "fu", 185 | "phoneme": "p\\ M", 186 | "type": "gojuuon" 187 | }, 188 | { 189 | "kana": "ふぃ", 190 | "romaji": "fi", 191 | "phoneme": "p\\' i", 192 | "type": "gojuuon" 193 | }, 194 | { 195 | "kana": "ふぁ", 196 | "romaji": "fa", 197 | "phoneme": "p\\ a", 198 | "type": "gojuuon" 199 | }, 200 | { 201 | "kana": "ふぇ", 202 | "romaji": "fe", 203 | "phoneme": "p\\ e", 204 | "type": "gojuuon" 205 | }, 206 | { 207 | "kana": "ふゃ", 208 | "romaji": "fya", 209 | "phoneme": "p\\' a", 210 | "type": "gojuuon" 211 | }, 212 | { 213 | "kana": "ふゅ", 214 | "romaji": "fyu", 215 | "phoneme": "p\\' M", 216 | "type": "gojuuon" 217 | }, 218 | { 219 | "kana": "ふょ", 220 | "romaji": "fyo", 221 | "phoneme": "p\\' o", 222 | "type": "gojuuon" 223 | }, 224 | { 225 | "kana": "へ", 226 | "romaji": "he", 227 | "phoneme": "h e", 228 | "type": "gojuuon" 229 | }, 230 | { 231 | "kana": "ほ", 232 | "romaji": "ho", 233 | "phoneme": "h o", 234 | "type": "gojuuon" 235 | }, 236 | { 237 | "kana": "ま", 238 | "romaji": "ma", 239 | "phoneme": "m a", 240 | "type": "gojuuon" 241 | }, 242 | { 243 | "kana": "み", 244 | "romaji": "mi", 245 | "phoneme": "m' i", 246 | "type": "gojuuon" 247 | }, 248 | { 249 | "kana": "む", 250 | "romaji": "mu", 251 | "phoneme": "m M", 252 | "type": "gojuuon" 253 | }, 254 | { 255 | "kana": "め", 256 | "romaji": "me", 257 | "phoneme": "m e", 258 | "type": "gojuuon" 259 | }, 260 | { 261 | "kana": "も", 262 | "romaji": "mo", 263 | "phoneme": "m o", 264 | "type": "gojuuon" 265 | }, 266 | { 267 | "kana": "や", 268 | "romaji": "ya", 269 | "phoneme": "j a", 270 | "type": "gojuuon" 271 | }, 272 | { 273 | "kana": "ゆ", 274 | "romaji": "yu", 275 | "phoneme": "j M", 276 | "type": "gojuuon" 277 | }, 278 | { 279 | "kana": "よ", 280 | "romaji": "yo", 281 | "phoneme": "j o", 282 | "type": "gojuuon" 283 | }, 284 | { 285 | "kana": "ら", 286 | "romaji": "ra", 287 | "phoneme": "4 a", 288 | "type": "gojuuon" 289 | }, 290 | { 291 | "kana": "り", 292 | "romaji": "ri", 293 | "phoneme": "4' i", 294 | "type": "gojuuon" 295 | }, 296 | { 297 | "kana": "る", 298 | "romaji": "ru", 299 | "phoneme": "4 M", 300 | "type": "gojuuon" 301 | }, 302 | { 303 | "kana": "れ", 304 | "romaji": "re", 305 | "phoneme": "4 e", 306 | "type": "gojuuon" 307 | }, 308 | { 309 | "kana": "ろ", 310 | "romaji": "ro", 311 | "phoneme": "4 o", 312 | "type": "gojuuon" 313 | }, 314 | { 315 | "kana": "わ", 316 | "romaji": "wa", 317 | "phoneme": "w a", 318 | "type": "gojuuon" 319 | }, 320 | { 321 | "kana": "を", 322 | "romaji": "wo", 323 | "phoneme": "w o", 324 | "type": "gojuuon" 325 | }, 326 | { 327 | "kana": "ん", 328 | "romaji": "n", 329 | "phoneme": "N", 330 | "type": "gojuuon" 331 | }, 332 | { 333 | "kana": "が", 334 | "romaji": "ga", 335 | "phoneme": "g a", 336 | "type": "dakuon" 337 | }, 338 | { 339 | "kana": "ぎ", 340 | "romaji": "gi", 341 | "phoneme": "g' i", 342 | "type": "dakuon" 343 | }, 344 | { 345 | "kana": "ぐ", 346 | "romaji": "gu", 347 | "phoneme": "g M", 348 | "type": "dakuon" 349 | }, 350 | { 351 | "kana": "げ", 352 | "romaji": "ge", 353 | "phoneme": "g e", 354 | "type": "dakuon" 355 | }, 356 | { 357 | "kana": "ご", 358 | "romaji": "go", 359 | "phoneme": "g o", 360 | "type": "dakuon" 361 | }, 362 | { 363 | "kana": "ざ", 364 | "romaji": "za", 365 | "phoneme": "dz a", 366 | "type": "dakuon" 367 | }, 368 | { 369 | "kana": "じ", 370 | "romaji": "ji", 371 | "phoneme": "dZ i", 372 | "type": "dakuon" 373 | }, 374 | { 375 | "kana": "ず", 376 | "romaji": "zu", 377 | "phoneme": "dz M", 378 | "type": "dakuon" 379 | }, 380 | { 381 | "kana": "ぜ", 382 | "romaji": "ze", 383 | "phoneme": "dz e", 384 | "type": "dakuon" 385 | }, 386 | { 387 | "kana": "ぞ", 388 | "romaji": "zo", 389 | "phoneme": "dz o", 390 | "type": "dakuon" 391 | }, 392 | { 393 | "kana": "だ", 394 | "romaji": "da", 395 | "phoneme": "d a", 396 | "type": "dakuon" 397 | }, 398 | { 399 | "kana": "ぢ", 400 | "romaji": "ji", 401 | "phoneme": "dZ i", 402 | "type": "dakuon" 403 | }, 404 | { 405 | "kana": "づ", 406 | "romaji": "zu", 407 | "phoneme": "dz M", 408 | "type": "dakuon" 409 | }, 410 | { 411 | "kana": "で", 412 | "romaji": "de", 413 | "phoneme": "d e", 414 | "type": "dakuon" 415 | }, 416 | { 417 | "kana": "ど", 418 | "romaji": "do", 419 | "phoneme": "d o", 420 | "type": "dakuon" 421 | }, 422 | { 423 | "kana": "ば", 424 | "romaji": "ba", 425 | "phoneme": "b a", 426 | "type": "dakuon" 427 | }, 428 | { 429 | "kana": "び", 430 | "romaji": "bi", 431 | "phoneme": "b' i", 432 | "type": "dakuon" 433 | }, 434 | { 435 | "kana": "ぶ", 436 | "romaji": "bu", 437 | "phoneme": "n M", 438 | "type": "dakuon" 439 | }, 440 | { 441 | "kana": "べ", 442 | "romaji": "be", 443 | "phoneme": "b e", 444 | "type": "dakuon" 445 | }, 446 | { 447 | "kana": "ぼ", 448 | "romaji": "bo", 449 | "phoneme": "b o", 450 | "type": "dakuon" 451 | }, 452 | { 453 | "kana": "ぱ", 454 | "romaji": "pa", 455 | "phoneme": "p a", 456 | "type": "handakuon" 457 | }, 458 | { 459 | "kana": "ぴ", 460 | "romaji": "pi", 461 | "phoneme": "p' i", 462 | "type": "handakuon" 463 | }, 464 | { 465 | "kana": "ぷ", 466 | "romaji": "pu", 467 | "phoneme": "p M", 468 | "type": "handakuon" 469 | }, 470 | { 471 | "kana": "ぺ", 472 | "romaji": "pe", 473 | "phoneme": "p e", 474 | "type": "handakuon" 475 | }, 476 | { 477 | "kana": "ぽ", 478 | "romaji": "po", 479 | "phoneme": "p o", 480 | "type": "handakuon" 481 | }, 482 | { 483 | "kana": "いぇ", 484 | "romaji": "ye", 485 | "phoneme": "j e", 486 | "type": "handakuon" 487 | }, 488 | { 489 | "kana": "c", 490 | "romaji": "(pause)", 491 | "phoneme": "Sil", 492 | "type": "sokuon" 493 | }, 494 | { 495 | "kana": "きゃ", 496 | "romaji": "kya", 497 | "phoneme": "k' a", 498 | "type": "youon" 499 | }, 500 | { 501 | "kana": "きゅ", 502 | "romaji": "kyu", 503 | "phoneme": "k' M", 504 | "type": "youon" 505 | }, 506 | { 507 | "kana": "きょ", 508 | "romaji": "kyo", 509 | "phoneme": "k' o", 510 | "type": "youon" 511 | }, 512 | { 513 | "kana": "きぇ", 514 | "romaji": "kye", 515 | "phoneme": "k' e", 516 | "type": "youon" 517 | }, 518 | { 519 | "kana": "しゃ", 520 | "romaji": "sha", 521 | "phoneme": "S a", 522 | "type": "youon" 523 | }, 524 | { 525 | "kana": "しゅ", 526 | "romaji": "shu", 527 | "phoneme": "S M", 528 | "type": "youon" 529 | }, 530 | { 531 | "kana": "しょ", 532 | "romaji": "sho", 533 | "phoneme": "S o", 534 | "type": "youon" 535 | }, 536 | { 537 | "kana": "ちゃ", 538 | "romaji": "cha", 539 | "phoneme": "tS a", 540 | "type": "youon" 541 | }, 542 | { 543 | "kana": "ちゅ", 544 | "romaji": "chu", 545 | "phoneme": "tS M", 546 | "type": "youon" 547 | }, 548 | { 549 | "kana": "ちょ", 550 | "romaji": "cho", 551 | "phoneme": "tS o", 552 | "type": "youon" 553 | }, 554 | { 555 | "kana": "にゃ", 556 | "romaji": "nya", 557 | "phoneme": "J a", 558 | "type": "youon" 559 | }, 560 | { 561 | "kana": "にゅ", 562 | "romaji": "nyu", 563 | "phoneme": "J M", 564 | "type": "youon" 565 | }, 566 | { 567 | "kana": "にょ", 568 | "romaji": "nyo", 569 | "phoneme": "J o", 570 | "type": "youon" 571 | }, 572 | { 573 | "kana": "ひゃ", 574 | "romaji": "hya", 575 | "phoneme": "C a", 576 | "type": "youon" 577 | }, 578 | { 579 | "kana": "ひゅ", 580 | "romaji": "hyu", 581 | "phoneme": "C M", 582 | "type": "youon" 583 | }, 584 | { 585 | "kana": "ひょ", 586 | "romaji": "hyo", 587 | "phoneme": "C o", 588 | "type": "youon" 589 | }, 590 | { 591 | "kana": "みゃ", 592 | "romaji": "mya", 593 | "phoneme": "m' a", 594 | "type": "youon" 595 | }, 596 | { 597 | "kana": "みゅ", 598 | "romaji": "myu", 599 | "phoneme": "m' M", 600 | "type": "youon" 601 | }, 602 | { 603 | "kana": "みょ", 604 | "romaji": "myo", 605 | "phoneme": "m' o", 606 | "type": "youon" 607 | }, 608 | { 609 | "kana": "りゃ", 610 | "romaji": "rya", 611 | "phoneme": "4' a", 612 | "type": "youon" 613 | }, 614 | { 615 | "kana": "りゅ", 616 | "romaji": "ryu", 617 | "phoneme": "4' M", 618 | "type": "youon" 619 | }, 620 | { 621 | "kana": "りょ", 622 | "romaji": "ryo", 623 | "phoneme": "4' o", 624 | "type": "youon" 625 | }, 626 | { 627 | "kana": "りぇ", 628 | "romaji": "rye", 629 | "phoneme": "4' e", 630 | "type": "youon" 631 | }, 632 | 633 | { 634 | "kana": "ぎゃ", 635 | "romaji": "gya", 636 | "phoneme": "g' a", 637 | "type": "youon" 638 | }, 639 | { 640 | "kana": "ぎゅ", 641 | "romaji": "gyu", 642 | "phoneme": "g' M", 643 | "type": "youon" 644 | }, 645 | { 646 | "kana": "ぎょ", 647 | "romaji": "gyo", 648 | "phoneme": "g' o", 649 | "type": "youon" 650 | }, 651 | { 652 | "kana": "じゃ", 653 | "romaji": "ja", 654 | "phoneme": "dZ a", 655 | "type": "youon" 656 | }, 657 | { 658 | "kana": "じゅ", 659 | "romaji": "ju", 660 | "phoneme": "dZ M", 661 | "type": "youon" 662 | }, 663 | { 664 | "kana": "じょ", 665 | "romaji": "jo", 666 | "phoneme": "dZ o", 667 | "type": "youon" 668 | }, 669 | { 670 | "kana": "びゃ", 671 | "romaji": "bya", 672 | "phoneme": "b' a", 673 | "type": "youon" 674 | }, 675 | { 676 | "kana": "びゅ", 677 | "romaji": "byu", 678 | "phoneme": "b' M", 679 | "type": "youon" 680 | }, 681 | { 682 | "kana": "びょ", 683 | "romaji": "byo", 684 | "phoneme": "b' o", 685 | "type": "youon" 686 | }, 687 | { 688 | "kana": "ぴゃ", 689 | "romaji": "pya", 690 | "phoneme": "p' a", 691 | "type": "youon" 692 | }, 693 | { 694 | "kana": "ぴゅ", 695 | "romaji": "pyu", 696 | "phoneme": "p' M", 697 | "type": "youon" 698 | }, 699 | { 700 | "kana": "ぴょ", 701 | "romaji": "pyo", 702 | "phoneme": "p' o", 703 | "type": "youon" 704 | }, 705 | { 706 | "kana": "ヴぁ", 707 | "romaji": "va", 708 | "phoneme": "v' a", 709 | "type": "youon" 710 | }, 711 | { 712 | "kana": "ヴぃ", 713 | "romaji": "vi", 714 | "phoneme": "v' i", 715 | "type": "youon" 716 | }, 717 | { 718 | "kana": "ヴ", 719 | "romaji": "vu", 720 | "phoneme": "v M", 721 | "type": "youon" 722 | }, 723 | { 724 | "kana": "ヴぇ", 725 | "romaji": "ve", 726 | "phoneme": "v' e", 727 | "type": "youon" 728 | }, 729 | { 730 | "kana": "ヴぉ", 731 | "romaji": "vo", 732 | "phoneme": "v' o", 733 | "type": "youon" 734 | }, 735 | { 736 | "kana": "てぃ", 737 | "romaji": "ti", 738 | "phoneme": "t' i", 739 | "type": "youon" 740 | }, 741 | { 742 | "kana": "とぅ", 743 | "romaji": "tu", 744 | "phoneme": "t' M", 745 | "type": "youon" 746 | }, 747 | { 748 | "kana": "ファ", 749 | "romaji": "fa", 750 | "phoneme": "p\\ a", 751 | "type": "youon" 752 | }, 753 | { 754 | "kana": "フィ", 755 | "romaji": "fi", 756 | "phoneme": "p\\' i", 757 | "type": "youon" 758 | }, 759 | { 760 | "kana": "フェ", 761 | "romaji": "fe", 762 | "phoneme": "p\\ e", 763 | "type": "youon" 764 | }, 765 | { 766 | "kana": "フォ", 767 | "romaji": "fo", 768 | "phoneme": "p\\ o", 769 | "type": "youon" 770 | }, 771 | { 772 | "kana": "テュ", 773 | "romaji": "tyu", 774 | "phoneme": "t' M", 775 | "type": "youon" 776 | }, 777 | { 778 | "kana": "トァ", 779 | "romaji": "twa", 780 | "phoneme": "t w a", 781 | "type": "youon" 782 | }, 783 | { 784 | "kana": "トィ", 785 | "romaji": "twi", 786 | "phoneme": "t w' i", 787 | "type": "youon" 788 | }, 789 | { 790 | "kana": "トゥ", 791 | "romaji": "twu", 792 | "phoneme": "t w' M", 793 | "type": "youon" 794 | }, 795 | { 796 | "kana": "トェ", 797 | "romaji": "twe", 798 | "phoneme": "t w' e", 799 | "type": "youon" 800 | }, 801 | { 802 | "kana": "トォ", 803 | "romaji": "two", 804 | "phoneme": "t w' o", 805 | "type": "youon" 806 | }, 807 | { 808 | "kana": "でゅ", 809 | "romaji": "dyu", 810 | "phoneme": "d' M", 811 | "type": "youon" 812 | }, 813 | { 814 | "kana": "てゅ", 815 | "romaji": "tyu", 816 | "phoneme": "t' M", 817 | "type": "youon" 818 | }, 819 | { 820 | "kana": "でぃ", 821 | "romaji": "di", 822 | "phoneme": "d' i", 823 | "type": "youon" 824 | }, 825 | { 826 | "kana": "どぅ", 827 | "romaji": "du", 828 | "phoneme": "d' M", 829 | "type": "youon" 830 | }, 831 | { 832 | "kana": "ずぃ", 833 | "romaji": "zi", 834 | "phoneme": "z' i", 835 | "type": "youon" 836 | }, 837 | { 838 | "kana": "みぇ", 839 | "romaji": "mye", 840 | "phoneme": "m' e", 841 | "type": "youon" 842 | }, 843 | { 844 | "kana": "つぉ", 845 | "romaji": "tso", 846 | "phoneme": "ts o", 847 | "type": "youon" 848 | }, 849 | { 850 | "kana": "しぇ", 851 | "romaji": "she", 852 | "phoneme": "S e", 853 | "type": "youon" 854 | }, 855 | { 856 | "kana": "つぇ", 857 | "romaji": "tse", 858 | "phoneme": "ts e", 859 | "type": "youon" 860 | }, 861 | { 862 | "kana": "ぎぇ", 863 | "romaji": "gye", 864 | "phoneme": "g' e", 865 | "type": "youon" 866 | }, 867 | { 868 | "kana": "ふぉ", 869 | "romaji": "fo", 870 | "phoneme": "p\\ o", 871 | "type": "youon" 872 | }, 873 | { 874 | "kana": "つぃ", 875 | "romaji": "tsi", 876 | "phoneme": "ts' i", 877 | "type": "youon" 878 | }, 879 | { 880 | "kana": "ひぇ", 881 | "romaji": "hye", 882 | "phoneme": "C e", 883 | "type": "youon" 884 | }, 885 | { 886 | "kana": "ちぇ", 887 | "romaji": "che", 888 | "phoneme": "tS e", 889 | "type": "youon" 890 | }, 891 | { 892 | "kana": "びぇ", 893 | "romaji": "bye", 894 | "phoneme": "b' e", 895 | "type": "youon" 896 | }, 897 | { 898 | "kana": "つぁ", 899 | "romaji": "tsa", 900 | "phoneme": "ts a", 901 | "type": "youon" 902 | }, 903 | { 904 | "kana": "ぴぇ", 905 | "romaji": "pye", 906 | "phoneme": "p' e", 907 | "type": "youon" 908 | }, 909 | { 910 | "kana": "すぃ", 911 | "romaji": "si", 912 | "phoneme": "s' i", 913 | "type": "youon" 914 | }, 915 | { 916 | "kana": "にぇ", 917 | "romaji": "nye", 918 | "phoneme": "J e", 919 | "type": "youon" 920 | }, 921 | { 922 | "kana": "じぇ", 923 | "romaji": "je", 924 | "phoneme": "dZ e", 925 | "type": "youon" 926 | }, 927 | { 928 | "kana": "hy", 929 | "romaji": "hy", 930 | "phoneme": "C I", 931 | "type": "youon" 932 | }, 933 | { 934 | "kana": "でゃ", 935 | "romaji": "dya", 936 | "phoneme": "d' a", 937 | "type": "youon" 938 | }, 939 | { 940 | "kana": "ン", 941 | "romaji": "n", 942 | "phoneme": "n", 943 | "type": "basic" 944 | }, 945 | { 946 | "kana": "k", 947 | "romaji": "k", 948 | "phoneme": "k", 949 | "type": "basic" 950 | }, 951 | { 952 | "kana": "びぃ", 953 | "romaji": "byi", 954 | "phoneme": "b' i", 955 | "type": "youon" 956 | }, 957 | { 958 | "kana": "てょ", 959 | "romaji": "tyo", 960 | "phoneme": "t' o", 961 | "type": "youon" 962 | }, 963 | { 964 | "kana": "h", 965 | "romaji": "h", 966 | "phoneme": "C", 967 | "type": "basic" 968 | }, 969 | { 970 | "kana": "n", 971 | "romaji": "n", 972 | "phoneme": "n", 973 | "type": "basic" 974 | }, 975 | { 976 | "kana": "ん2", 977 | "romaji": "n", 978 | "phoneme": "n", 979 | "type": "basic" 980 | }, 981 | { 982 | "kana": "f", 983 | "romaji": "f", 984 | "phoneme": "p\\", 985 | "type": "basic" 986 | }, 987 | { 988 | "kana": "j", 989 | "romaji": "j", 990 | "phoneme": "dZ", 991 | "type": "basic" 992 | }, 993 | { 994 | "kana": "ry", 995 | "romaji": "ry", 996 | "phoneme": "J I", 997 | "type": "youon" 998 | }, 999 | { 1000 | "kana": "m", 1001 | "romaji": "m", 1002 | "phoneme": "m", 1003 | "type": "basic" 1004 | }, 1005 | { 1006 | "kana": "dh", 1007 | "romaji": "dh", 1008 | "phoneme": "D", 1009 | "type": "basic" 1010 | }, 1011 | { 1012 | "kana": "い2", 1013 | "romaji": "i", 1014 | "phoneme": "i", 1015 | "type": "basic" 1016 | }, 1017 | { 1018 | "kana": "w", 1019 | "romaji": "w", 1020 | "phoneme": "w", 1021 | "type": "basic" 1022 | }, 1023 | { 1024 | "kana": "v", 1025 | "romaji": "v", 1026 | "phoneme": "v", 1027 | "type": "basic" 1028 | }, 1029 | { 1030 | "kana": "お2", 1031 | "romaji": "o", 1032 | "phoneme": "o", 1033 | "type": "basic" 1034 | }, 1035 | { 1036 | "kana": "sh", 1037 | "romaji": "sh", 1038 | "phoneme": "S", 1039 | "type": "basic" 1040 | }, 1041 | { 1042 | "kana": "s", 1043 | "romaji": "s", 1044 | "phoneme": "s", 1045 | "type": "basic" 1046 | }, 1047 | { 1048 | "kana": "by", 1049 | "romaji": "by", 1050 | "phoneme": "b I", 1051 | "type": "youon" 1052 | }, 1053 | { 1054 | "kana": "ts", 1055 | "romaji": "ts", 1056 | "phoneme": "ts", 1057 | "type": "basic" 1058 | }, 1059 | { 1060 | "kana": "my", 1061 | "romaji": "my", 1062 | "phoneme": "m I", 1063 | "type": "youon" 1064 | }, 1065 | { 1066 | "kana": "t", 1067 | "romaji": "t", 1068 | "phoneme": "t", 1069 | "type": "basic" 1070 | }, 1071 | { 1072 | "kana": "r", 1073 | "romaji": "r", 1074 | "phoneme": "4", 1075 | "type": "basic" 1076 | }, 1077 | { 1078 | "kana": "ty", 1079 | "romaji": "ty", 1080 | "phoneme": "tS", 1081 | "type": "basic" 1082 | }, 1083 | { 1084 | "kana": "あ2", 1085 | "romaji": "a", 1086 | "phoneme": "a", 1087 | "type": "basic" 1088 | }, 1089 | { 1090 | "kana": "b", 1091 | "romaji": "b", 1092 | "phoneme": "b", 1093 | "type": "basic" 1094 | }, 1095 | { 1096 | "kana": "ny", 1097 | "romaji": "ny", 1098 | "phoneme": "J", 1099 | "type": "basic" 1100 | }, 1101 | { 1102 | "kana": "ヴゅ", 1103 | "romaji": "vu", 1104 | "phoneme": "v M", 1105 | "type": "youon" 1106 | }, 1107 | { 1108 | "kana": "ky", 1109 | "romaji": "ky", 1110 | "phoneme": "k I", 1111 | "type": "youon" 1112 | }, 1113 | { 1114 | "kana": "t2", 1115 | "romaji": "t", 1116 | "phoneme": "t", 1117 | "type": "basic" 1118 | }, 1119 | { 1120 | "kana": "でょ", 1121 | "romaji": "dyo", 1122 | "phoneme": "d' o", 1123 | "type": "youon" 1124 | }, 1125 | { 1126 | "kana": "py", 1127 | "romaji": "py", 1128 | "phoneme": "p'", 1129 | "type": "youon" 1130 | }, 1131 | { 1132 | "kana": "ch", 1133 | "romaji": "ch", 1134 | "phoneme": "tS", 1135 | "type": "basic" 1136 | }, 1137 | { 1138 | "kana": "吸", 1139 | "romaji": "kyu", 1140 | "phoneme": "Asp", 1141 | "type": "youon" 1142 | }, 1143 | { 1144 | "kana": "p", 1145 | "romaji": "p", 1146 | "phoneme": "p", 1147 | "type": "basic" 1148 | }, 1149 | { 1150 | "kana": "y", 1151 | "romaji": "y", 1152 | "phoneme": "j", 1153 | "type": "basic" 1154 | }, 1155 | { 1156 | "kana": "z", 1157 | "romaji": "z", 1158 | "phoneme": "z", 1159 | "type": "basic" 1160 | }, 1161 | { 1162 | "kana": "d", 1163 | "romaji": "d", 1164 | "phoneme": "d", 1165 | "type": "basic" 1166 | }, 1167 | { 1168 | "kana": "gy", 1169 | "romaji": "gy", 1170 | "phoneme": "g'", 1171 | "type": "youon" 1172 | }, 1173 | { 1174 | "kana": "・", 1175 | "romaji": "?", 1176 | "phoneme": "?", 1177 | "type": "basic" 1178 | }, 1179 | { 1180 | "kana": "g", 1181 | "romaji": "g", 1182 | "phoneme": "g", 1183 | "type": "basic" 1184 | }, 1185 | { 1186 | "kana": "う2", 1187 | "romaji": "u", 1188 | "phoneme": "M", 1189 | "type": "basic" 1190 | }, 1191 | { 1192 | "kana": "え2", 1193 | "romaji": "e", 1194 | "phoneme": "e", 1195 | "type": "basic" 1196 | }, 1197 | { 1198 | "kana": "てゃ", 1199 | "romaji": "tya", 1200 | "phoneme": "t' a", 1201 | "type": "youon" 1202 | }, 1203 | { 1204 | "kana": "w", 1205 | "romaji": "w", 1206 | "phoneme": "w", 1207 | "type": "basic" 1208 | }, 1209 | { 1210 | "kana": "b", 1211 | "romaji": "b", 1212 | "phoneme": "b", 1213 | "type": "basic" 1214 | }, 1215 | { 1216 | "kana": "v", 1217 | "romaji": "v", 1218 | "phoneme": "v", 1219 | "type": "basic" 1220 | }, 1221 | { 1222 | "kana": "d", 1223 | "romaji": "d", 1224 | "phoneme": "d", 1225 | "type": "basic" 1226 | }, 1227 | { 1228 | "kana": "g", 1229 | "romaji": "g", 1230 | "phoneme": "g", 1231 | "type": "basic" 1232 | }, 1233 | { 1234 | "kana": "s", 1235 | "romaji": "s", 1236 | "phoneme": "s", 1237 | "type": "basic" 1238 | }, 1239 | { 1240 | "kana": "p", 1241 | "romaji": "p", 1242 | "phoneme": "p", 1243 | "type": "basic" 1244 | }, 1245 | { 1246 | "kana": "n", 1247 | "romaji": "n", 1248 | "phoneme": "n", 1249 | "type": "basic" 1250 | }, 1251 | { 1252 | "kana": "t", 1253 | "romaji": "t", 1254 | "phoneme": "t", 1255 | "type": "basic" 1256 | }, 1257 | { 1258 | "kana": "z", 1259 | "romaji": "z", 1260 | "phoneme": "z", 1261 | "type": "basic" 1262 | }, 1263 | { 1264 | "kana": "m", 1265 | "romaji": "m", 1266 | "phoneme": "m", 1267 | "type": "basic" 1268 | }, 1269 | { 1270 | "kana": "ts", 1271 | "romaji": "ts", 1272 | "phoneme": "ts", 1273 | "type": "basic" 1274 | }, 1275 | { 1276 | "kana": "k", 1277 | "romaji": "k", 1278 | "phoneme": "k", 1279 | "type": "basic" 1280 | } 1281 | ] -------------------------------------------------------------------------------- /kiritan_script_GUI.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import tkinter as tk 4 | from tkinter import ttk 5 | from tkinter import filedialog 6 | from tkinter import messagebox 7 | 8 | def load_segments(seg_file): 9 | segments = [] 10 | in_segment_section = False 11 | 12 | with open(seg_file, 'r') as file: 13 | for line in file: 14 | if line.strip() == '===' or line.strip() == '=================================================': 15 | in_segment_section = True 16 | continue 17 | 18 | if in_segment_section: 19 | parts = line.strip().split() 20 | if len(parts) >= 3: 21 | phoneme, begin_time, end_time = parts[0], float(parts[1]), float(parts[2]) 22 | segments.append((phoneme, begin_time, end_time)) 23 | 24 | return segments 25 | 26 | def transfer_phonemes(seg_folder1, seg_folder2, out_folder): 27 | os.makedirs(out_folder, exist_ok=True) 28 | 29 | seg_files1 = {} 30 | seg_files2 = {} 31 | 32 | for seg_filename1 in os.listdir(seg_folder1): 33 | if seg_filename1.endswith('.seg'): 34 | base_name = os.path.splitext(seg_filename1)[0] 35 | seg_files1[base_name] = os.path.join(seg_folder1, seg_filename1) 36 | 37 | for seg_filename2 in os.listdir(seg_folder2): 38 | if seg_filename2.endswith('.seg'): 39 | base_name = os.path.splitext(seg_filename2)[0] 40 | seg_files2[base_name] = os.path.join(seg_folder2, seg_filename2) 41 | 42 | common_base_names = set(seg_files1.keys()).intersection(seg_files2.keys()) 43 | 44 | for base_name in common_base_names: 45 | seg_file1 = seg_files1[base_name] 46 | seg_file2 = seg_files2[base_name] 47 | out_file = os.path.join(out_folder, base_name + '.seg') 48 | 49 | segments1 = load_segments(seg_file1) 50 | segments2 = load_segments(seg_file2) 51 | 52 | with open(out_file, 'w') as outfile: 53 | outfile.write("nPhonemes {}\n".format(len(segments2))) 54 | outfile.write("articulationsAreStationaries 0\n") 55 | outfile.write("phoneme\tBeginTime\tEndTime\n") 56 | outfile.write("=================================================\n") 57 | 58 | for i, segment2 in enumerate(segments2): 59 | if i < len(segments1): 60 | phoneme1, begin_time1, end_time1 = segments1[i] 61 | phoneme2, begin_time2, end_time2 = segment2 62 | if phoneme1.strip(): 63 | outfile.write(f"{phoneme1}\t{begin_time2:.6f}\t{end_time2:.6f}\n") 64 | else: 65 | phoneme2, begin_time2, end_time2 = segment2 66 | outfile.write(f"{phoneme2}\t{begin_time2:.6f}\t{end_time2:.6f}\n") 67 | 68 | print(f"Transferred phonemes from {seg_file1} to {seg_file2} and saved in {out_file}") 69 | 70 | def browse_folder1(): 71 | directory = filedialog.askdirectory() 72 | if directory: 73 | folder1_entry.delete(0, tk.END) 74 | folder1_entry.insert(0, directory) 75 | 76 | def browse_folder2(): 77 | directory = filedialog.askdirectory() 78 | if directory: 79 | folder2_entry.delete(0, tk.END) 80 | folder2_entry.insert(0, directory) 81 | 82 | def browse_output_folder(): 83 | directory = filedialog.askdirectory() 84 | if directory: 85 | output_folder_entry.delete(0, tk.END) 86 | output_folder_entry.insert(0, directory) 87 | 88 | def convert_phonemes(): 89 | folder1 = folder1_entry.get() 90 | folder2 = folder2_entry.get() 91 | output_folder = output_folder_entry.get() 92 | 93 | if not os.path.exists(folder1) or not os.path.exists(folder2): 94 | messagebox.showerror("Error", "Input folders do not exist.") 95 | return 96 | 97 | if not os.path.exists(output_folder): 98 | os.makedirs(output_folder) 99 | 100 | transfer_phonemes(folder1, folder2, output_folder) 101 | messagebox.showinfo("Success", "Conversion complete.") 102 | 103 | # Create the main application window 104 | root = tk.Tk() 105 | root.title("Phoneme Transfer") 106 | 107 | # Import the tcl file 108 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 109 | 110 | # Set the theme with the theme_use method 111 | style = ttk.Style() 112 | style.theme_use("forest-dark") 113 | 114 | # Create a frame for the input fields 115 | input_frame = ttk.Frame(root, padding=10) 116 | input_frame.grid(row=0, column=0, padx=10, pady=10, sticky='w') 117 | 118 | # Folder 1 119 | folder1_label = ttk.Label(input_frame, text='Folder 1 (Donor):') 120 | folder1_label.grid(row=0, column=0, sticky='w') 121 | 122 | folder1_entry = ttk.Entry(input_frame, width=40) 123 | folder1_entry.grid(row=0, column=1, padx=(5, 0), sticky='w') 124 | 125 | folder1_button = ttk.Button(input_frame, text='Browse', command=browse_folder1) 126 | folder1_button.grid(row=0, column=2, padx=(5, 0), sticky='w') 127 | 128 | # Folder 2 129 | folder2_label = ttk.Label(input_frame, text='Folder 2 (Receiver):') 130 | folder2_label.grid(row=1, column=0, sticky='w') 131 | 132 | folder2_entry = ttk.Entry(input_frame, width=40) 133 | folder2_entry.grid(row=1, column=1, padx=(5, 0), sticky='w') 134 | 135 | folder2_button = ttk.Button(input_frame, text='Browse', command=browse_folder2) 136 | folder2_button.grid(row=1, column=2, padx=(5, 0), sticky='w') 137 | 138 | # Output folder 139 | output_folder_label = ttk.Label(input_frame, text='Output Folder:') 140 | output_folder_label.grid(row=2, column=0, sticky='w') 141 | 142 | output_folder_entry = ttk.Entry(input_frame, width=40) 143 | output_folder_entry.grid(row=2, column=1, padx=(5, 0), sticky='w') 144 | 145 | output_folder_button = ttk.Button(input_frame, text='Browse', command=browse_output_folder) 146 | output_folder_button.grid(row=2, column=2, padx=(5, 0), sticky='w') 147 | 148 | # Convert button 149 | convert_button = ttk.Button(root, text='Transfer Phonemes', command=convert_phonemes) 150 | convert_button.grid(row=1, column=0, padx=10, pady=10) 151 | 152 | # Start the GUI application 153 | root.mainloop() 154 | -------------------------------------------------------------------------------- /lab2seg_GUI.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tkinter as tk 3 | from tkinter import ttk 4 | from tkinter import filedialog 5 | from tkinter import messagebox 6 | 7 | import argparse 8 | import re 9 | 10 | def convert_lab_to_seg(input_directory, output_directory): 11 | # Create the output directory if it doesn't exist 12 | if not os.path.exists(output_directory): 13 | os.makedirs(output_directory) 14 | 15 | # Process each lab file 16 | for lab_file_name in os.listdir(input_directory): 17 | if lab_file_name.endswith(".lab"): 18 | input_lab_file = os.path.join(input_directory, lab_file_name) 19 | output_seg_file = os.path.join(output_directory, lab_file_name.replace(".lab", ".seg")) 20 | 21 | # Initialize variables 22 | phoneme_list = [] 23 | 24 | # Open the input lab for reading 25 | with open(input_lab_file, 'r') as infile: 26 | lines = infile.readlines() 27 | 28 | for line in lines: 29 | line = line.strip() 30 | # Use regular expressions to extract phonemes, start times, and end times 31 | match = re.match(r'(\d+)\s+(\d+)\s+(.+)', line) 32 | if match: 33 | start_time = float(match.group(1)) / 1e7 # Convert time to seconds 34 | end_time = float(match.group(2)) / 1e7 35 | phoneme = match.group(3) 36 | 37 | # Remove trailing numbers from phonemes (you might remove this) 38 | phoneme = ''.join([c for c in phoneme if not c.isdigit()]) 39 | 40 | # Replace "R" and "pau" with "Sil" (mostly for arpabet) 41 | if phoneme == "R" or phoneme == "pau": 42 | phoneme = "Sil" 43 | 44 | phoneme_list.append((phoneme, start_time, end_time)) 45 | 46 | if phoneme_list: 47 | # .seg writing 48 | with open(output_seg_file, 'w') as outfile: 49 | outfile.write("nPhonemes {}\n".format(len(phoneme_list))) 50 | outfile.write("articulationsAreStationaries 0\n") 51 | outfile.write("phoneme\t\tBeginTime\t\tEndTime\n") 52 | outfile.write("=" * 49 + "\n") 53 | for phoneme, start_time, end_time in phoneme_list: 54 | outfile.write("{}\t\t{:.6f}\t\t{:.6f}\n".format(phoneme, start_time, end_time)) 55 | 56 | print("Conversion complete for", lab_file_name) 57 | else: 58 | print("Skipping empty file:", lab_file_name) 59 | 60 | print("All conversions complete.") 61 | 62 | def browse_input_directory(): 63 | directory = filedialog.askdirectory() 64 | if directory: 65 | input_directory_entry.delete(0, tk.END) 66 | input_directory_entry.insert(0, directory) 67 | 68 | def browse_output_directory(): 69 | directory = filedialog.askdirectory() 70 | if directory: 71 | output_directory_entry.delete(0, tk.END) 72 | output_directory_entry.insert(0, directory) 73 | 74 | def convert_lab_files(): 75 | input_directory = input_directory_entry.get() 76 | output_directory = output_directory_entry.get() 77 | 78 | if not os.path.exists(input_directory): 79 | messagebox.showerror("Error", "Input directory does not exist.") 80 | return 81 | 82 | if not os.path.exists(output_directory): 83 | os.makedirs(output_directory) 84 | 85 | convert_lab_to_seg(input_directory, output_directory) 86 | messagebox.showinfo("Success", "Conversion complete.") 87 | 88 | # Create the main application window 89 | root = tk.Tk() 90 | root.title("Lab to Seg Converter") 91 | 92 | # Import the tcl file 93 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 94 | 95 | # Set the theme with the theme_use method 96 | style = ttk.Style() 97 | style.theme_use("forest-dark") 98 | 99 | # Create a frame for the input fields 100 | input_frame = ttk.Frame(root, padding=10) 101 | input_frame.grid(row=0, column=0, padx=10, pady=10, sticky='w') 102 | 103 | # Input directory 104 | input_directory_label = ttk.Label(input_frame, text='Input Directory:') 105 | input_directory_label.grid(row=0, column=0, sticky='w') 106 | 107 | input_directory_entry = ttk.Entry(input_frame, width=40) 108 | input_directory_entry.grid(row=0, column=1, padx=(5, 0), sticky='w') 109 | 110 | input_directory_button = ttk.Button(input_frame, text='Browse', command=browse_input_directory) 111 | input_directory_button.grid(row=0, column=2, padx=(5, 0), sticky='w') 112 | 113 | # Output directory 114 | output_directory_label = ttk.Label(input_frame, text='Output Directory:') 115 | output_directory_label.grid(row=1, column=0, sticky='w') 116 | 117 | output_directory_entry = ttk.Entry(input_frame, width=40) 118 | output_directory_entry.grid(row=1, column=1, padx=(5, 0), sticky='w') 119 | 120 | output_directory_button = ttk.Button(input_frame, text='Browse', command=browse_output_directory) 121 | output_directory_button.grid(row=1, column=2, padx=(5, 0), sticky='w') 122 | 123 | # Convert button 124 | convert_button = ttk.Button(root, text='Convert', command=convert_lab_files) 125 | convert_button.grid(row=1, column=0, padx=10, pady=10) 126 | 127 | # Start the GUI application 128 | root.mainloop() 129 | -------------------------------------------------------------------------------- /phoneme_grabber_GUI.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | from tkinter import filedialog 4 | from tkinter import messagebox 5 | import subprocess 6 | 7 | # Function to parse an oto.ini file and extract phonemes 8 | def parse_oto_ini(oto_file): 9 | phonemes = set() 10 | with open(oto_file, 'rb') as file: 11 | for line in file: 12 | try: 13 | line = line.decode('shift-jis') 14 | except UnicodeDecodeError: 15 | continue 16 | parts = line.strip().split('=') 17 | if len(parts) == 2: 18 | phoneme_data = parts[1].split(',')[0].strip() 19 | phoneme_data = phoneme_data.replace(' ', '\n') # Replace space with newline 20 | phonemes.update(phoneme_data.split('\n')) # Add individual phonemes to the set 21 | return phonemes 22 | 23 | # Function to run the phoneme grabber script 24 | def run_phoneme_grabber(): 25 | oto_file = oto_file_entry.get() 26 | output_folder = output_folder_entry.get() 27 | 28 | try: 29 | phonemes = parse_oto_ini(oto_file) 30 | 31 | output_file = f'{output_folder}/phonemes.txt' 32 | with open(output_file, 'w', encoding='utf-8') as file: 33 | for phoneme in sorted(phonemes): 34 | file.write(phoneme + '\n') 35 | 36 | messagebox.showinfo('Success', f'Phonemes extracted and saved to {output_file}') 37 | except Exception as e: 38 | messagebox.showerror('Error', f'An error occurred: {str(e)}') 39 | 40 | # Create the main application window 41 | root = tk.Tk() 42 | root.title('Phoneme Grabber') 43 | 44 | # Import the tcl file for the Forest theme 45 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 46 | 47 | # Set the theme with the theme_use method 48 | style = ttk.Style(root) 49 | style.theme_use("forest-dark") 50 | 51 | # Create a frame for the input fields 52 | input_frame = ttk.Frame(root, padding=10) 53 | input_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky='w') 54 | 55 | # Oto.ini file input 56 | oto_file_label = ttk.Label(input_frame, text='oto.ini File:') 57 | oto_file_label.grid(row=0, column=0, sticky='w') 58 | 59 | oto_file_entry = ttk.Entry(input_frame, width=40) 60 | oto_file_entry.grid(row=0, column=1, padx=(5, 0), sticky='w') 61 | 62 | oto_file_button = ttk.Button(input_frame, text='Browse', command=lambda: browse_file(oto_file_entry)) 63 | oto_file_button.grid(row=0, column=2, padx=(5, 0), sticky='w') 64 | 65 | # Output folder input 66 | output_folder_label = ttk.Label(input_frame, text='Output Folder:') 67 | output_folder_label.grid(row=1, column=0, sticky='w') 68 | 69 | output_folder_entry = ttk.Entry(input_frame, width=40) 70 | output_folder_entry.grid(row=1, column=1, padx=(5, 0), sticky='w') 71 | 72 | output_folder_button = ttk.Button(input_frame, text='Browse', command=lambda: browse_directory(output_folder_entry)) 73 | output_folder_button.grid(row=1, column=2, padx=(5, 0), sticky='w') 74 | 75 | # Run button 76 | run_button = ttk.Button(root, text='Run Phoneme Grabber', command=run_phoneme_grabber) 77 | run_button.grid(row=1, column=0, columnspan=2, padx=10, pady=10) 78 | 79 | # Function to open a file dialog and populate an entry field 80 | def browse_file(entry_field): 81 | file_path = filedialog.askopenfilename(filetypes=[('oto.ini files', '*.ini')]) 82 | if file_path: 83 | entry_field.delete(0, tk.END) 84 | entry_field.insert(0, file_path) 85 | 86 | # Function to open a directory dialog and populate an entry field 87 | def browse_directory(entry_field): 88 | folder_path = filedialog.askdirectory() 89 | if folder_path: 90 | entry_field.delete(0, tk.END) 91 | entry_field.insert(0, folder_path) 92 | 93 | # Start the GUI application 94 | root.mainloop() 95 | -------------------------------------------------------------------------------- /phonemes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kana": "-", 4 | "romaji": "Sil", 5 | "phoneme": "Sil", 6 | "type": "basic" 7 | }, 8 | { 9 | "kana": "a", 10 | "romaji": "a", 11 | "phoneme": "a", 12 | "type": "basic" 13 | }, 14 | { 15 | "kana": "b", 16 | "romaji": "b", 17 | "phoneme": "b", 18 | "type": "basic" 19 | }, 20 | { 21 | "kana": "by", 22 | "romaji": "by", 23 | "phoneme": "b'", 24 | "type": "youon" 25 | }, 26 | { 27 | "kana": "ch", 28 | "romaji": "ch", 29 | "phoneme": "tS", 30 | "type": "basic" 31 | }, 32 | { 33 | "kana": "d", 34 | "romaji": "d", 35 | "phoneme": "d", 36 | "type": "basic" 37 | }, 38 | { 39 | "kana": "e", 40 | "romaji": "e", 41 | "phoneme": "e", 42 | "type": "basic" 43 | }, 44 | { 45 | "kana": "g", 46 | "romaji": "g", 47 | "phoneme": "g", 48 | "type": "basic" 49 | }, 50 | { 51 | "kana": "gy", 52 | "romaji": "gy", 53 | "phoneme": "g'", 54 | "type": "youon" 55 | }, 56 | { 57 | "kana": "h", 58 | "romaji": "h", 59 | "phoneme": "h", 60 | "type": "basic" 61 | }, 62 | { 63 | "kana": "hy", 64 | "romaji": "hy", 65 | "phoneme": "C", 66 | "type": "youon" 67 | }, 68 | { 69 | "kana": "i", 70 | "romaji": "i", 71 | "phoneme": "i", 72 | "type": "basic" 73 | }, 74 | { 75 | "kana": "j", 76 | "romaji": "j", 77 | "phoneme": "dZ", 78 | "type": "basic" 79 | }, 80 | { 81 | "kana": "k", 82 | "romaji": "k", 83 | "phoneme": "k", 84 | "type": "basic" 85 | }, 86 | { 87 | "kana": "ky", 88 | "romaji": "ky", 89 | "phoneme": "k'", 90 | "type": "youon" 91 | }, 92 | { 93 | "kana": "m", 94 | "romaji": "m", 95 | "phoneme": "m", 96 | "type": "basic" 97 | }, 98 | { 99 | "kana": "my", 100 | "romaji": "my", 101 | "phoneme": "m'", 102 | "type": "youon" 103 | }, 104 | { 105 | "kana": "n", 106 | "romaji": "n", 107 | "phoneme": "n", 108 | "type": "basic" 109 | }, 110 | { 111 | "kana": "ny", 112 | "romaji": "ny", 113 | "phoneme": "J", 114 | "type": "basic" 115 | }, 116 | { 117 | "kana": "o", 118 | "romaji": "o", 119 | "phoneme": "o", 120 | "type": "basic" 121 | }, 122 | { 123 | "kana": "p", 124 | "romaji": "p", 125 | "phoneme": "p", 126 | "type": "basic" 127 | }, 128 | { 129 | "kana": "py", 130 | "romaji": "py", 131 | "phoneme": "p I", 132 | "type": "youon" 133 | }, 134 | { 135 | "kana": "r", 136 | "romaji": "r", 137 | "phoneme": "4", 138 | "type": "basic" 139 | }, 140 | { 141 | "kana": "ry", 142 | "romaji": "ry", 143 | "phoneme": "J I", 144 | "type": "youon" 145 | }, 146 | { 147 | "kana": "s", 148 | "romaji": "s", 149 | "phoneme": "s", 150 | "type": "basic" 151 | }, 152 | { 153 | "kana": "sh", 154 | "romaji": "sh", 155 | "phoneme": "S", 156 | "type": "basic" 157 | }, 158 | { 159 | "kana": "t", 160 | "romaji": "t", 161 | "phoneme": "t", 162 | "type": "basic" 163 | }, 164 | { 165 | "kana": "ts", 166 | "romaji": "ts", 167 | "phoneme": "ts", 168 | "type": "basic" 169 | }, 170 | { 171 | "kana": "u", 172 | "romaji": "u", 173 | "phoneme": "M", 174 | "type": "basic" 175 | }, 176 | { 177 | "kana": "w", 178 | "romaji": "w", 179 | "phoneme": "w", 180 | "type": "basic" 181 | }, 182 | { 183 | "kana": "y", 184 | "romaji": "y", 185 | "phoneme": "j", 186 | "type": "basic" 187 | }, 188 | { 189 | "kana": "z", 190 | "romaji": "z", 191 | "phoneme": "z", 192 | "type": "basic" 193 | }, 194 | { 195 | "kana": " ", 196 | "romaji": "iki", 197 | "phoneme": "br", 198 | "type": "youon" 199 | } 200 | ] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pydub==0.25.1 2 | utaupy==1.18.3 3 | -------------------------------------------------------------------------------- /trans_convert_GUI.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | from tkinter import filedialog 4 | import os 5 | import subprocess 6 | 7 | def convert_trans_file(input_file, output_file): 8 | with open(input_file, "r") as infile: 9 | lines = infile.readlines() 10 | 11 | output_lines = [] 12 | top_line = lines[0].strip() # Keep the top line as is 13 | output_lines.append(top_line + "\n") 14 | 15 | for line in lines[1:-1]: # Exclude the first and last lines 16 | if '[' in line: 17 | tokens = line.strip().split(' ') 18 | for token in tokens[1:]: 19 | output_lines.append("[" + token) 20 | output_lines.append("\n") 21 | else: 22 | tokens = line.strip().split('][') 23 | for token in tokens: 24 | phonemes = token.split(' ') 25 | if len(phonemes) > 1: 26 | output_lines.append("[" + phonemes[1] + ' ') 27 | output_lines.append("\n") 28 | 29 | with open(output_file, "w") as outfile: 30 | outfile.writelines(output_lines) 31 | 32 | def convert_trans_files(): 33 | input_dir = input_dir_entry.get() 34 | output_dir = output_dir_entry.get() 35 | 36 | if not os.path.exists(output_dir): 37 | os.makedirs(output_dir) 38 | 39 | converted_files = [] 40 | 41 | for filename in os.listdir(input_dir): 42 | if filename.endswith(".trans"): 43 | input_file = os.path.join(input_dir, filename) 44 | output_file = os.path.join(output_dir, filename) 45 | convert_trans_file(input_file, output_file) 46 | converted_files.append(filename) 47 | 48 | terminal_output.config(state=tk.NORMAL) 49 | if converted_files: 50 | terminal_output.insert(tk.END, "Conversion completed for the following files:\n") 51 | for file in converted_files: 52 | terminal_output.insert(tk.END, file + "\n") 53 | else: 54 | terminal_output.insert(tk.END, "No .trans files found for conversion.\n") 55 | terminal_output.config(state=tk.DISABLED) 56 | 57 | # Create the main application window 58 | root = tk.Tk() 59 | root.title("Trans File Converter") 60 | 61 | # Import the tcl file 62 | root.tk.call("source", "./Forest-ttk-theme-1.0/forest-dark.tcl") 63 | 64 | # Set the theme with the theme_use method 65 | style = ttk.Style() 66 | style.theme_use("forest-dark") 67 | 68 | # Make the app responsive 69 | for i in range(2): 70 | root.columnconfigure(index=i, weight=1) 71 | for i in range(4): 72 | root.rowconfigure(index=i, weight=1) 73 | 74 | # Create a Frame for inputs and buttons 75 | input_frame = ttk.Frame(root) 76 | input_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") 77 | 78 | # Input for .trans files directory 79 | input_dir_label = ttk.Label(input_frame, text="Articulation .trans Input Directory:") 80 | input_dir_label.grid(row=0, column=0, sticky="w") 81 | input_dir_entry = ttk.Entry(input_frame, width=40) 82 | input_dir_entry.grid(row=0, column=1, padx=(0, 5)) 83 | input_dir_browse = ttk.Button(input_frame, text="Browse", command=lambda: browse_directory(input_dir_entry)) 84 | input_dir_browse.grid(row=0, column=2) 85 | 86 | # Input for output directory 87 | output_dir_label = ttk.Label(input_frame, text="Stationary .trans Output Directory:") 88 | output_dir_label.grid(row=1, column=0, sticky="w") 89 | output_dir_entry = ttk.Entry(input_frame, width=40) 90 | output_dir_entry.grid(row=1, column=1, padx=(0, 5)) 91 | output_dir_browse = ttk.Button(input_frame, text="Browse", command=lambda: browse_directory(output_dir_entry)) 92 | output_dir_browse.grid(row=1, column=2) 93 | 94 | # Convert button 95 | convert_button = ttk.Button(input_frame, text="Convert .trans Files", command=convert_trans_files) 96 | convert_button.grid(row=2, column=0, columnspan=3, pady=10) 97 | 98 | # Create a Frame for the terminal output 99 | output_frame = ttk.Frame(root) 100 | output_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 10), sticky="nsew") 101 | 102 | # Terminal output 103 | terminal_output = tk.Text(output_frame, height=10, width=60, state=tk.DISABLED) 104 | terminal_output.grid(row=0, column=0, sticky="nsew") 105 | 106 | # Function to browse for a directory and update the corresponding entry 107 | def browse_directory(entry_widget): 108 | directory = filedialog.askdirectory() 109 | if directory: 110 | entry_widget.delete(0, tk.END) 111 | entry_widget.insert(0, directory) 112 | 113 | # Start the main loop 114 | root.mainloop() 115 | --------------------------------------------------------------------------------