diff --git a/UVR.py b/UVR.py index 1b23ec0..ecc083a 100644 --- a/UVR.py +++ b/UVR.py @@ -266,6 +266,7 @@ DEFAULT_DATA = { 'inst_only_b': False, 'lastDir': None, 'margin': 44100, + 'margin_d': 44100, 'mdx_ensem': 'MDX-Net: UVR-MDX-NET Main', 'mdx_ensem_b': 'No Model', 'mdx_only_ensem_a': 'MDX-Net: UVR-MDX-NET Main', @@ -285,6 +286,8 @@ DEFAULT_DATA = { 'ModelParams': 'Auto', 'mp3bit': '320k', 'n_fft_scale': 6144, + 'no_chunk': False, + 'no_chunk_d': False, 'noise_pro_select': 'Auto Select', 'noise_reduc': True, 'noisereduc_s': '3', @@ -298,7 +301,7 @@ DEFAULT_DATA = { 'save': True, 'saveFormat': 'Wav', 'selectdownload': 'VR Arc', - 'segment': 'None', + 'segment': 'Default', 'settest': False, 'shifts': 2, 'shifts_b': 2, @@ -432,26 +435,51 @@ class ThreadSafeConsole(tk.Text): """ Text Widget which is thread safe for tkinter """ + def __init__(self, master, **options): tk.Text.__init__(self, master, **options) self.queue = queue.Queue() self.update_me() + def write(self, line): self.queue.put(line) + + def percentage(self, line): + line = f"percentage_value_{line}" + self.queue.put(line) + + def remove(self, line): + line = f"remove_line_{line}" + self.queue.put(line) def clear(self): self.queue.put(None) def update_me(self): self.configure(state=tk.NORMAL) + try: while 1: line = self.queue.get_nowait() + if line is None: self.delete(1.0, tk.END) else: - self.insert(tk.END, str(line)) + if "percentage_value_" in str(line): + line = str(line) + line = line.replace("percentage_value_", "") + string_len = len(str(line)) + self.delete(f"end-{string_len + 1}c", tk.END) + self.insert(tk.END, f"\n{line}") + elif "remove_line_" in str(line): + line = str(line) + line = line.replace("remove_line_", "") + string_len = len(str(line)) + self.delete(f"end-{string_len}c", tk.END) + else: + self.insert(tk.END, str(line)) + self.see(tk.END) self.update_idletasks() except queue.Empty: @@ -711,6 +739,10 @@ class MainWindow(TkinterDnD.Tk): self.margin_var = tk.StringVar(value=data['margin']) except: self.margin_var = tk.StringVar(value=data_alt['margin']) + try: + self.margin_d_var = tk.StringVar(value=data['margin_d']) + except: + self.margin_d_var = tk.StringVar(value=data_alt['margin_d']) try: self.mdx_only_ensem_a_var = tk.StringVar(value=data['mdx_only_ensem_a']) except: @@ -783,6 +815,14 @@ class MainWindow(TkinterDnD.Tk): self.n_fft_scale_var = tk.StringVar(value=data['n_fft_scale']) except: self.n_fft_scale_var = tk.StringVar(value=data_alt['n_fft_scale']) + try: + self.no_chunk_var = tk.BooleanVar(value=data['no_chunk']) + except: + self.no_chunk_var = tk.BooleanVar(value=data_alt['no_chunk']) + try: + self.no_chunk_d_var = tk.BooleanVar(value=data['no_chunk_d']) + except: + self.no_chunk_d_var = tk.BooleanVar(value=data_alt['no_chunk_d']) try: self.noise_pro_select_var = tk.StringVar(value=data['noise_pro_select']) except: @@ -1014,6 +1054,9 @@ class MainWindow(TkinterDnD.Tk): self.stop_Button = ttk.Button(master=self, image=self.stop_img, command=self.stop_inf) + self.mdx_stop_Button = ttk.Button(master=self, + image=self.stop_img, + command=self.stop_inf) self.settings_Button = ttk.Button(master=self, image=self.help_img, command=self.settings) @@ -1253,7 +1296,7 @@ class MainWindow(TkinterDnD.Tk): background='#0e0e0f', font=self.font, foreground='#13a4c9') self.options_segment_Optionmenu = ttk.OptionMenu(self.options_Frame, self.segment_var, - None, 'None', '1', '5', '10', '15', '20', + None, 'Default', '1', '5', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55', '60', '65', '70', '75', '80', '85', '90', '95', '100') @@ -1583,6 +1626,15 @@ class MainWindow(TkinterDnD.Tk): self.chunks_var.trace_add('write', lambda *args: self.update_states()) + self.chunks_d_var.trace_add('write', + lambda *args: self.update_states()) + + self.margin_d_var.trace_add('write', + lambda *args: self.update_states()) + + self.no_chunk_d_var.trace_add('write', + lambda *args: self.update_states()) + self.autocompensate_var.trace_add('write', lambda *args: self.update_states()) @@ -1669,6 +1721,10 @@ class MainWindow(TkinterDnD.Tk): Start the conversion for all the given mp3 and wav files """ + global stop_inf + + stop_inf = self.stop_inf_mdx + # -Get all variables- export_path = self.exportPath_var.get() input_paths = self.inputPaths @@ -1769,6 +1825,7 @@ class MainWindow(TkinterDnD.Tk): 'inst_only_b': self.inst_only_b_var.get(), 'instrumentalModel': instrumentalModel_path, 'margin': self.margin_var.get(), + 'margin_d': self.margin_d_var.get(), 'mdx_ensem': self.mdxensemchoose_var.get(), 'mdx_ensem_b': self.mdxensemchoose_b_var.get(), 'mdx_only_ensem_a': self.mdx_only_ensem_a_var.get(), @@ -1783,6 +1840,8 @@ class MainWindow(TkinterDnD.Tk): 'ModelParams': self.ModelParams_var.get(), 'mp3bit': self.mp3bit_var.get(), 'n_fft_scale': self.n_fft_scale_var.get(), + 'no_chunk': self.no_chunk_var.get(), + 'no_chunk_d': self.no_chunk_d_var.get(), 'noise_pro_select': self.noise_pro_select_var.get(), 'noise_reduc': self.noisereduc_var.get(), 'noisereduc_s': noisereduc_s, @@ -1829,6 +1888,7 @@ class MainWindow(TkinterDnD.Tk): 'wavtype': self.wavtype_var.get(), 'window': self, 'window_size': window_size, + 'stop_thread': stop_inf, }, daemon=True ) @@ -1839,16 +1899,7 @@ class MainWindow(TkinterDnD.Tk): confirm = tk.messagebox.askyesno(title='Confirmation', message='You are about to stop all active processes.\n\nAre you sure you wish to continue?') - - # if self.aiModel_var.get() == 'VR Architecture': - # inference = inference_v5 - # elif self.aiModel_var.get() == 'Ensemble Mode': - # inference = inference_v5_ensemble - # elif self.aiModel_var.get() == 'MDX-Net': - # inference = inference_MDX - # elif self.aiModel_var.get() == 'Demucs v3': - # inference = inference_demucs - + if confirm: inf.kill() button_widget = self.conversion_Button @@ -1864,6 +1915,18 @@ class MainWindow(TkinterDnD.Tk): else: pass + def stop_inf_mdx(self): + inf.kill() + button_widget = self.conversion_Button + button_widget.configure(state=tk.NORMAL) + #text = self.command_Text + #text.write('\n\nProcess stopped by user.') + torch.cuda.empty_cache() + importlib.reload(inference_v5) + importlib.reload(inference_v5_ensemble) + importlib.reload(inference_MDX) + importlib.reload(inference_demucs) + # Models def update_inputPaths(self): """Update the music file entry""" @@ -1980,6 +2043,14 @@ class MainWindow(TkinterDnD.Tk): j = ["UVR_MDXNET_Main"] for char in j: file_name_1 = file_name_1.replace(char, "UVR-MDX-NET Main") + + k = ["UVR_MDXNET_Inst_1"] + for char in k: + file_name_1 = file_name_1.replace(char, "UVR-MDX-NET Inst 1") + + l = ["UVR_MDXNET_Inst_2"] + for char in l: + file_name_1 = file_name_1.replace(char, "UVR-MDX-NET Inst 2") self.options_mdxnetModel_Optionmenu['menu'].add_radiobutton(label=file_name_1, command=tk._setit(self.mdxnetModel_var, file_name_1)) @@ -2948,6 +3019,19 @@ class MainWindow(TkinterDnD.Tk): self.downloadmodelOptions_mdx.configure(state=tk.DISABLED) except: pass + + if self.no_chunk_d_var.get() == False: + try: + self.chunk_d_entry.configure(state=tk.DISABLED) + self.margin_d_entry.configure(state=tk.DISABLED) + except: + pass + elif self.no_chunk_d_var.get() == True: + try: + self.chunk_d_entry.configure(state=tk.NORMAL) + self.margin_d_entry.configure(state=tk.NORMAL) + except: + pass if self.demucs_only_var.get() == True: self.demucsmodel_var.set(True) @@ -3114,6 +3198,7 @@ class MainWindow(TkinterDnD.Tk): self.inst_only_var.set(False) self.inst_only_b_var.set(False) self.margin_var.set(44100) + self.margin_d_var.set(44100) self.mdxensemchoose_var.set('MDX-Net: UVR-MDX-NET Main') self.mdxensemchoose_b_var.set('No Model') self.mdx_only_ensem_a_var.set('MDX-Net: UVR-MDX-NET Main') @@ -3129,6 +3214,8 @@ class MainWindow(TkinterDnD.Tk): self.ModelParams_var.set('Auto') self.mp3bit_var.set('320k') self.n_fft_scale_var.set(6144) + self.no_chunk_var.set(False) + self.no_chunk_d_var.set(True) self.noise_pro_select_var.set('Auto Select') self.noisereduc_var.set(True) self.noisereduc_s_var.set(3) @@ -3169,41 +3256,44 @@ class MainWindow(TkinterDnD.Tk): self.vr_basic_USER_model_param_5.set('Auto') self.wavtype_var.set('PCM_16') self.winSize_var.set('512') - - - + def advanced_vr_options(self): """ Open Advanced VR Options """ - top=Toplevel(self) + vr_opt=Toplevel(root) window_height = 630 window_width = 500 - top.title("Advanced VR Options") - - top.resizable(False, False) # This code helps to disable windows from resizing - - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = vr_opt.winfo_screenwidth() + screen_height = vr_opt.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + vr_opt.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) - top.attributes("-topmost", True) + vr_opt.resizable(False, False) # This code helps to disable windows from resizing + + x = root.winfo_x() + y = root.winfo_y() + vr_opt.geometry("+%d+%d" %(x+57,y+110)) + vr_opt.wm_transient(root) + + vr_opt.title("Advanced VR Options") + + #vr_opt.attributes("-topmost", True) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + vr_opt.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + vr_opt.destroy() self.settings() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(vr_opt) tab1 = ttk.Frame(tabControl) tab2 = ttk.Frame(tabControl) @@ -3269,7 +3359,7 @@ class MainWindow(TkinterDnD.Tk): l0.grid(row=12,column=0,padx=0,pady=5) def close_win_self(): - top.destroy() + vr_opt.destroy() l0=ttk.Button(frame0,text='Close Window', command=close_win_self) l0.grid(row=13,column=0,padx=0,pady=5) @@ -3307,131 +3397,143 @@ class MainWindow(TkinterDnD.Tk): l0=ttk.Checkbutton(frame0, text='Split Mode', variable=self.split_mode_var) l0.grid(row=9,column=0,padx=0,pady=5) - self.update_states() + #self.update_states() def advanced_demucs_options(self): """ Open Advanced Demucs Options """ - top= Toplevel(self) + demuc_opt= Toplevel(root) window_height = 750 window_width = 500 - top.title("Advanced Demucs Options") + demuc_opt.title("Advanced Demucs Options") - top.resizable(False, False) # This code helps to disable windows from resizing + demuc_opt.resizable(False, False) # This code helps to disable windows from resizing - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = demuc_opt.winfo_screenwidth() + screen_height = demuc_opt.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + demuc_opt.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) - top.attributes("-topmost", True) + #demuc_opt.attributes("-topmost", True) + + x = root.winfo_x() + y = root.winfo_y() + demuc_opt.geometry("+%d+%d" %(x+57,y+45)) + demuc_opt.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + demuc_opt.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + demuc_opt.destroy() self.settings() - tabControl = ttk.Notebook(top) - - tab1 = ttk.Frame(tabControl) - - tabControl.add(tab1, text ='Advanced Settings') - + tabControl = ttk.Notebook(demuc_opt) + tabControl.pack(expand = 1, fill ="both") - tab1.grid_rowconfigure(0, weight=1) - tab1.grid_columnconfigure(0, weight=1) + tabControl.grid_rowconfigure(0, weight=1) + tabControl.grid_columnconfigure(0, weight=1) - frame0=Frame(tab1, highlightbackground='red',highlightthicknes=0) + frame0=Frame(tabControl, highlightbackground='red',highlightthicknes=0) frame0.grid(row=0,column=0,padx=0,pady=30) - l0=tk.Label(frame0,text="Advanced Demucs Options",font=("Century Gothic", "13", "underline"), justify="center", fg="#13a4c9") + l0=tk.Label(frame0,text="Advanced Demucs Options",font=("Century Gothic", "13", "underline"), justify="center", fg="#13a4c9", width=50) l0.grid(row=0,column=0,padx=0,pady=10) - l0=tk.Label(frame0, text='Chunks (Set Manually)', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Shifts\n(Higher values use more resources and increase processing times)', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=1,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.chunks_d_var, justify='center') + l0=ttk.Entry(frame0, textvariable=self.shifts_b_var, justify='center') l0.grid(row=2,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Chunk Margin', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Overlap', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=3,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.margin_var, justify='center') + l0=ttk.Entry(frame0, textvariable=self.overlap_b_var, justify='center') l0.grid(row=4,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Shifts\n(Higher values use more resources and increase processing times)', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Segment', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=5,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.shifts_b_var, justify='center') + l0=ttk.Entry(frame0, textvariable=self.segment_var, justify='center') l0.grid(row=6,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Overlap', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Chunks (Set Manually)', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=7,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.overlap_b_var, justify='center') - l0.grid(row=8,column=0,padx=0,pady=0) + self.chunk_d_entry=ttk.Entry(frame0, textvariable=self.chunks_d_var, justify='center') + self.chunk_d_entry.grid(row=8,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Segment', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Chunk Margin', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=9,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.segment_var, justify='center') - l0.grid(row=10,column=0,padx=0,pady=0) + self.margin_d_entry=ttk.Entry(frame0, textvariable=self.margin_d_var, justify='center') + self.margin_d_entry.grid(row=10,column=0,padx=0,pady=0) + + l0=ttk.Checkbutton(frame0, text='Enable Chunks', variable=self.no_chunk_d_var) + l0.grid(row=11,column=0,padx=0,pady=10) l0=ttk.Checkbutton(frame0, text='Save Stems to Model & Track Name Directory', variable=self.audfile_var) - l0.grid(row=11,column=0,padx=0,pady=5) - - l0=ttk.Button(frame0,text='Open Demucs Model Folder', command=self.open_Modelfolder_de) l0.grid(row=12,column=0,padx=0,pady=0) - l0=ttk.Button(frame0,text='Back to Main Menu', command=close_win) + l0=ttk.Button(frame0,text='Open Demucs Model Folder', command=self.open_Modelfolder_de) l0.grid(row=13,column=0,padx=0,pady=10) + l0=ttk.Button(frame0,text='Back to Main Menu', command=close_win) + l0.grid(row=14,column=0,padx=0,pady=0) + def close_win_self(): - top.destroy() + demuc_opt.destroy() l0=ttk.Button(frame0,text='Close Window', command=close_win_self) - l0.grid(row=14,column=0,padx=0,pady=0) + l0.grid(row=15,column=0,padx=0,pady=10) + + l0=ttk.Label(frame0,text='\n') + l0.grid(row=16,column=0,padx=0,pady=50) + + self.update_states() def advanced_mdx_options(self): """ Open Advanced MDX Options """ - top= Toplevel(self) + mdx_net_opt= Toplevel(root) window_height = 740 window_width = 550 - top.title("Advanced MDX-Net Options") + mdx_net_opt.title("Advanced MDX-Net Options") - top.resizable(False, False) # This code helps to disable windows from resizing + mdx_net_opt.resizable(False, False) # This code helps to disable windows from resizing - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = mdx_net_opt.winfo_screenwidth() + screen_height = mdx_net_opt.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + mdx_net_opt.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) - top.attributes("-topmost", True) + x = root.winfo_x() + y = root.winfo_y() + mdx_net_opt.geometry("+%d+%d" %(x+35,y+45)) + mdx_net_opt.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + mdx_net_opt.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + mdx_net_opt.destroy() self.settings() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(mdx_net_opt) tab1 = ttk.Frame(tabControl) tab2 = ttk.Frame(tabControl) @@ -3501,7 +3603,7 @@ class MainWindow(TkinterDnD.Tk): l0.grid(row=14,column=0,padx=0,pady=0) def close_win_self(): - top.destroy() + mdx_net_opt.destroy() l0=ttk.Button(frame0,text='Close Window', command=close_win_self) l0.grid(row=15,column=0,padx=0,pady=10) @@ -3526,21 +3628,30 @@ class MainWindow(TkinterDnD.Tk): l0=ttk.OptionMenu(frame0, self.mixing_var, None, 'Default', 'Min_Mag', 'Max_Mag', 'Invert_p') l0.grid(row=4,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Shifts\n(Higher values use more resources and increase processing times)', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Segments\n(Higher values use more resources and increase processing times)', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=5,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.shifts_var, justify='center') + l0=ttk.Entry(frame0, textvariable=self.segment_var, justify='center') l0.grid(row=6,column=0,padx=0,pady=0) - l0=tk.Label(frame0, text='Overlap', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text='Shifts\n(Higher values use more resources and increase processing times)', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=7,column=0,padx=0,pady=10) - l0=ttk.Entry(frame0, textvariable=self.overlap_var, justify='center') + l0=ttk.Entry(frame0, textvariable=self.shifts_var, justify='center') l0.grid(row=8,column=0,padx=0,pady=0) - l0=ttk.Checkbutton(frame0, text='Split Mode', variable=self.split_mode_var) + l0=tk.Label(frame0, text='Overlap', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=9,column=0,padx=0,pady=10) + l0=ttk.Entry(frame0, textvariable=self.overlap_var, justify='center') + l0.grid(row=10,column=0,padx=0,pady=0) + + l0=ttk.Checkbutton(frame0, text='Split Mode', variable=self.split_mode_var) + l0.grid(row=11,column=0,padx=0,pady=10) + + l0=ttk.Checkbutton(frame0, text='Enable Chunks', variable=self.no_chunk_var) + l0.grid(row=12,column=0,padx=0,pady=0) + self.update_states() frame0=Frame(tab3, highlightbackground='red',highlightthicknes=0) @@ -3589,7 +3700,7 @@ class MainWindow(TkinterDnD.Tk): def clear_cache(): - cachedir = "lib_v5/filelists/hashes/mdx_model_cache" + cachedir = "lib_v5/filelists/model_cache/mdx_model_cache" for basename in os.listdir(cachedir): if basename.endswith('.json'): @@ -3610,33 +3721,41 @@ class MainWindow(TkinterDnD.Tk): """ Open Ensemble Custom """ - top= Toplevel(self) + custom_ens_opt= Toplevel(root) window_height = 680 window_width = 900 - top.title("Customize Ensemble") + custom_ens_opt.title("Customize Ensemble") - top.resizable(False, False) # This code helps to disable windows from resizing + custom_ens_opt.resizable(False, False) # This code helps to disable windows from resizing - top.attributes("-topmost", True) + x = root.winfo_x() + y = root.winfo_y() + custom_ens_opt.geometry("+%d+%d" %(x+57,y+100)) + custom_ens_opt.wm_transient(root) - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = custom_ens_opt.winfo_screenwidth() + screen_height = custom_ens_opt.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + custom_ens_opt.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + + x = root.winfo_x() + y = root.winfo_y() + custom_ens_opt.geometry("+%d+%d" %(x-140,y+70)) + custom_ens_opt.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + custom_ens_opt.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + custom_ens_opt.destroy() self.settings() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(custom_ens_opt) tab1 = ttk.Frame(tabControl) tab2 = ttk.Frame(tabControl) @@ -3791,7 +3910,7 @@ class MainWindow(TkinterDnD.Tk): l0.grid(row=11,column=2,padx=0,pady=0) def close_win_self(): - top.destroy() + custom_ens_opt.destroy() l0=ttk.Button(frame0,text='Close Window', command=close_win_self) l0.grid(row=13,column=1,padx=20,pady=0) @@ -3959,7 +4078,7 @@ class MainWindow(TkinterDnD.Tk): """ Open Help Guide """ - top= Toplevel(self) + help_guide_opt = Toplevel(self) if GetSystemMetrics(1) >= 900: window_height = 810 window_width = 1080 @@ -3969,28 +4088,32 @@ class MainWindow(TkinterDnD.Tk): else: window_height = 670 window_width = 930 - top.title("UVR Help Guide") + help_guide_opt.title("UVR Help Guide") - top.resizable(False, False) # This code helps to disable windows from resizing + help_guide_opt.resizable(False, False) # This code helps to disable windows from resizing - top.attributes("-topmost", True) - - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = help_guide_opt.winfo_screenwidth() + screen_height = help_guide_opt.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + help_guide_opt.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + + if GetSystemMetrics(1) >= 900: + x = root.winfo_x() + y = root.winfo_y() + help_guide_opt.geometry("+%d+%d" %(x-220,y+5)) + help_guide_opt.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + help_guide_opt.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + help_guide_opt.destroy() self.settings() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(help_guide_opt) tab1 = ttk.Frame(tabControl) tab2 = ttk.Frame(tabControl) @@ -4289,34 +4412,37 @@ class MainWindow(TkinterDnD.Tk): update_button_var = tk.StringVar(value='Check for Updates') update_set_var = tk.StringVar(value='UVR Version Current') - top= Toplevel(self) + settings_menu = Toplevel(self) window_height = 780 window_width = 500 - top.title("Settings Guide") + settings_menu.title("Settings Guide") - top.resizable(False, False) # This code helps to disable windows from resizing + settings_menu.resizable(False, False) # This code helps to disable windows from resizing - top.attributes("-topmost", True) - - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = settings_menu.winfo_screenwidth() + screen_height = settings_menu.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + settings_menu.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + + x = root.winfo_x() + y = root.winfo_y() + settings_menu.geometry("+%d+%d" %(x+57,y+15)) + settings_menu.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + settings_menu.iconbitmap('img\\UVR-Icon-v2.ico') def askyesorno(): """ Ask to Update """ - top_dialoge= Toplevel() + top_dialoge = Toplevel() window_height = 250 window_width = 370 @@ -4329,7 +4455,7 @@ class MainWindow(TkinterDnD.Tk): top_dialoge.attributes("-topmost", True) - top.attributes("-topmost", False) + settings_menu.attributes("-topmost", False) screen_width = top_dialoge.winfo_screenwidth() screen_height = top_dialoge.winfo_screenheight() @@ -4350,12 +4476,12 @@ class MainWindow(TkinterDnD.Tk): tabControl.grid_columnconfigure(0, weight=1) def no(): - top.attributes("-topmost", True) + settings_menu.attributes("-topmost", True) top_dialoge.destroy() def yes(): download_update() - top.attributes("-topmost", True) + settings_menu.attributes("-topmost", True) top_dialoge.destroy() frame0=Frame(tabControl,highlightbackground='red',highlightthicknes=0) @@ -4385,7 +4511,7 @@ class MainWindow(TkinterDnD.Tk): top_code.destroy() except: pass - top.destroy() + settings_menu.destroy() def close_win_custom_ensemble(): change_event() @@ -4415,10 +4541,10 @@ class MainWindow(TkinterDnD.Tk): change_event() def restart(): - top.destroy() + settings_menu.destroy() self.restart() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(settings_menu) tab1 = ttk.Frame(tabControl) tab2 = ttk.Frame(tabControl) @@ -4533,7 +4659,7 @@ class MainWindow(TkinterDnD.Tk): rlg.start() def open_bmac_m(): - top.attributes("-topmost", False) + settings_menu.attributes("-topmost", False) callback("https://www.buymeacoffee.com/uvr5") l0=ttk.Button(frame0,text=update_button_var.get(), command=start_check_updates) @@ -4598,7 +4724,7 @@ class MainWindow(TkinterDnD.Tk): global top_code - top_code= Toplevel() + top_code = Toplevel(settings_menu) window_height = 480 window_width = 320 @@ -4607,9 +4733,9 @@ class MainWindow(TkinterDnD.Tk): top_code.resizable(False, False) # This code helps to disable windows from resizing - top_code.attributes("-topmost", True) + # top_code.attributes("-topmost", True) - top.attributes("-topmost", False) + # settings_menu.attributes("-topmost", False) screen_width = top_code.winfo_screenwidth() screen_height = top_code.winfo_screenheight() @@ -4619,6 +4745,11 @@ class MainWindow(TkinterDnD.Tk): top_code.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + x = settings_menu.winfo_x() + y = settings_menu.winfo_y() + top_code.geometry("+%d+%d" %(x+90,y+135)) + top_code.wm_transient(settings_menu) + # change title bar icon top_code.iconbitmap('img\\UVR-Icon-v2.ico') @@ -4656,7 +4787,7 @@ class MainWindow(TkinterDnD.Tk): callback("https://www.buymeacoffee.com/uvr5") def quit(): - top.attributes("-topmost", True) + settings_menu.attributes("-topmost", True) top_code.destroy() l0=tk.Label(frame0, text=f'User Download Codes', font=("Century Gothic", "11", "underline"), foreground='#13a4c9') @@ -4719,10 +4850,6 @@ class MainWindow(TkinterDnD.Tk): top_code.resizable(False, False) # This code helps to disable windows from resizing - top_code.attributes("-topmost", True) - - top.attributes("-topmost", False) - screen_width = top_code.winfo_screenwidth() screen_height = top_code.winfo_screenheight() @@ -4731,6 +4858,11 @@ class MainWindow(TkinterDnD.Tk): top_code.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + x = settings_menu.winfo_x() + y = settings_menu.winfo_y() + top_code.geometry("+%d+%d" %(x+43,y+220)) + top_code.wm_transient(settings_menu) + # change title bar icon top_code.iconbitmap('img\\UVR-Icon-v2.ico') @@ -4752,7 +4884,7 @@ class MainWindow(TkinterDnD.Tk): top_code.destroy() def quit(): - top.attributes("-topmost", True) + settings_menu.attributes("-topmost", True) top_code.destroy() l0=tk.Label(frame0, text=f'Invalid Download Code', font=("Century Gothic", "11", "underline"), foreground='#13a4c9') @@ -5692,7 +5824,7 @@ class MainWindow(TkinterDnD.Tk): links = lib_v5.filelist.get_download_links(links, downloads='app_patch') url_link = f"{links}{pack_name}.exe" #print(url_link) - top.attributes("-topmost", False) + settings_menu.attributes("-topmost", False) try: if os.path.isfile(f"{cwd_path}/{pack_name}.exe"): self.download_progress_var.set('File already exists') @@ -5814,7 +5946,7 @@ class MainWindow(TkinterDnD.Tk): wget.download(url_7, download_links_file_temp, bar=download_progress_bar) move_lists_from_temp() self.download_progress_bar_var.set('Download list\'s refreshed!') - top.destroy() + settings_menu.destroy() self.settings(choose=True) except Exception as e: short_error = f'{e}' @@ -5851,7 +5983,7 @@ class MainWindow(TkinterDnD.Tk): wget.download(url_7, download_links_file_temp, bar=download_progress_bar) move_lists_from_temp() self.download_progress_bar_var.set('VIP: Download list\'s refreshed!') - top.destroy() + settings_menu.destroy() self.settings(choose=True) except Exception as e: short_error = f'{e}' @@ -5892,7 +6024,7 @@ class MainWindow(TkinterDnD.Tk): wget.download(url_7, download_links_file_temp, bar=download_progress_bar) move_lists_from_temp() self.download_progress_bar_var.set('Developer: Download list\'s refreshed!') - top.destroy() + settings_menu.destroy() self.settings(choose=True) except Exception as e: short_error = f'{e}' @@ -5983,7 +6115,7 @@ class MainWindow(TkinterDnD.Tk): self.download_progress_var.set('') self.download_stop_var.set(space_small) - top.protocol("WM_DELETE_WINDOW", change_event) + settings_menu.protocol("WM_DELETE_WINDOW", change_event) self.update_states() @@ -5991,7 +6123,8 @@ class MainWindow(TkinterDnD.Tk): """ Open Error Log """ - top= Toplevel(self) + error_log_screen= Toplevel(root) + if GetSystemMetrics(1) >= 900: window_height = 810 window_width = 1080 @@ -6002,31 +6135,42 @@ class MainWindow(TkinterDnD.Tk): window_height = 670 window_width = 930 - top.title("UVR Help Guide") + error_log_screen.title("UVR Help Guide") - top.resizable(False, False) # This code helps to disable windows from resizing + error_log_screen.resizable(False, False) # This code helps to disable windows from resizing - top.attributes("-topmost", True) + #error_log_screen.attributes("-topmost", True) - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = error_log_screen.winfo_screenwidth() + screen_height = error_log_screen.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + error_log_screen.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + + if GetSystemMetrics(1) >= 900: + x = root.winfo_x() + y = root.winfo_y() + error_log_screen.geometry("+%d+%d" %(x-220,y+5)) + error_log_screen.wm_transient(root) + + # x = root.winfo_x() + # y = root.winfo_y() + # error_log_screen.geometry("+%d+%d" %(x+43,y+220)) + # error_log_screen.wm_transient(root) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + error_log_screen.iconbitmap('img\\UVR-Icon-v2.ico') def close_win(): - top.destroy() + error_log_screen.destroy() self.settings() def close_win_self(): - top.destroy() + error_log_screen.destroy() - tabControl = ttk.Notebook(top) + tabControl = ttk.Notebook(error_log_screen) tab1 = ttk.Frame(tabControl) @@ -6059,7 +6203,6 @@ class MainWindow(TkinterDnD.Tk): l0=ttk.Button(frame0,text='Close Window', command=close_win_self) l0.grid(row=6,column=0,padx=20,pady=0) - def copy_clip(self): copy_t = open("errorlog.txt", "r").read() pyperclip.copy(copy_t) @@ -6157,6 +6300,7 @@ class MainWindow(TkinterDnD.Tk): 'inst_only_b': self.inst_only_b_var.get(), 'lastDir': self.lastDir, 'margin': self.margin_var.get(), + 'margin_d': self.margin_d_var.get(), 'mdx_ensem': self.mdxensemchoose_var.get(), 'mdx_ensem_b': self.mdxensemchoose_b_var.get(), 'mdx_only_ensem_a': self.mdx_only_ensem_a_var.get(), @@ -6176,6 +6320,8 @@ class MainWindow(TkinterDnD.Tk): 'ModelParams': self.ModelParams_var.get(), 'mp3bit': self.mp3bit_var.get(), 'n_fft_scale': self.n_fft_scale_var.get(), + 'no_chunk': self.no_chunk_var.get(), + 'no_chunk_d': self.no_chunk_d_var.get(), 'noise_pro_select': self.noise_pro_select_var.get(), 'noise_reduc': self.noisereduc_var.get(), 'noisereduc_s': noisereduc_s, diff --git a/demucs/__pycache__/__init__.cpython-39.pyc b/demucs/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..0b52817 Binary files /dev/null and b/demucs/__pycache__/__init__.cpython-39.pyc differ diff --git a/demucs/__pycache__/apply.cpython-39.pyc b/demucs/__pycache__/apply.cpython-39.pyc new file mode 100644 index 0000000..d473a8d Binary files /dev/null and b/demucs/__pycache__/apply.cpython-39.pyc differ diff --git a/demucs/__pycache__/demucs.cpython-39.pyc b/demucs/__pycache__/demucs.cpython-39.pyc new file mode 100644 index 0000000..b8e73d3 Binary files /dev/null and b/demucs/__pycache__/demucs.cpython-39.pyc differ diff --git a/demucs/__pycache__/hdemucs.cpython-39.pyc b/demucs/__pycache__/hdemucs.cpython-39.pyc new file mode 100644 index 0000000..78b3fcb Binary files /dev/null and b/demucs/__pycache__/hdemucs.cpython-39.pyc differ diff --git a/demucs/__pycache__/model.cpython-39.pyc b/demucs/__pycache__/model.cpython-39.pyc new file mode 100644 index 0000000..1b2f95a Binary files /dev/null and b/demucs/__pycache__/model.cpython-39.pyc differ diff --git a/demucs/__pycache__/model_v2.cpython-39.pyc b/demucs/__pycache__/model_v2.cpython-39.pyc new file mode 100644 index 0000000..7db8dd6 Binary files /dev/null and b/demucs/__pycache__/model_v2.cpython-39.pyc differ diff --git a/demucs/__pycache__/pretrained.cpython-39.pyc b/demucs/__pycache__/pretrained.cpython-39.pyc new file mode 100644 index 0000000..e9214e9 Binary files /dev/null and b/demucs/__pycache__/pretrained.cpython-39.pyc differ diff --git a/demucs/__pycache__/repo.cpython-39.pyc b/demucs/__pycache__/repo.cpython-39.pyc new file mode 100644 index 0000000..982ebb1 Binary files /dev/null and b/demucs/__pycache__/repo.cpython-39.pyc differ diff --git a/demucs/__pycache__/spec.cpython-39.pyc b/demucs/__pycache__/spec.cpython-39.pyc new file mode 100644 index 0000000..85b11eb Binary files /dev/null and b/demucs/__pycache__/spec.cpython-39.pyc differ diff --git a/demucs/__pycache__/states.cpython-39.pyc b/demucs/__pycache__/states.cpython-39.pyc new file mode 100644 index 0000000..e560681 Binary files /dev/null and b/demucs/__pycache__/states.cpython-39.pyc differ diff --git a/demucs/__pycache__/tasnet_v2.cpython-39.pyc b/demucs/__pycache__/tasnet_v2.cpython-39.pyc new file mode 100644 index 0000000..651e9d0 Binary files /dev/null and b/demucs/__pycache__/tasnet_v2.cpython-39.pyc differ diff --git a/demucs/__pycache__/utils.cpython-39.pyc b/demucs/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000..c91d721 Binary files /dev/null and b/demucs/__pycache__/utils.cpython-39.pyc differ diff --git a/demucs/apply.py b/demucs/apply.py index fdb096c..7920ad5 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -10,11 +10,13 @@ inteprolation between chunks, as well as the "shift trick". from concurrent.futures import ThreadPoolExecutor import random import typing as tp +from multiprocessing import Process,Queue,Pipe import torch as th from torch import nn from torch.nn import functional as F import tqdm +import tkinter as tk from .demucs import Demucs from .hdemucs import HDemucs @@ -22,6 +24,7 @@ from .utils import center_trim, DummyPoolExecutor Model = tp.Union[Demucs, HDemucs] +progress_bar_num = 0 class BagOfModels(nn.Module): def __init__(self, models: tp.List[Model], @@ -107,7 +110,6 @@ class TensorChunk: assert out.shape[-1] == target_length return out - def tensor_chunk(tensor_or_chunk): if isinstance(tensor_or_chunk, TensorChunk): return tensor_or_chunk @@ -115,10 +117,9 @@ def tensor_chunk(tensor_or_chunk): assert isinstance(tensor_or_chunk, th.Tensor) return TensorChunk(tensor_or_chunk) - -def apply_model(model, mix, shifts=1, split=True, - overlap=0.25, transition_power=1., progress=False, device=None, - num_workers=0, pool=None): +def apply_model(model, mix, gui_progress_bar: tk.Variable, widget_text: tk.Text, update_prog, total_files, file_num, inference_type, shifts=1, split=True, + overlap=0.25, transition_power=1., progress=True, device=None, + num_workers=0, pool=None, segmen=False): """ Apply model to a given mixture. @@ -136,6 +137,12 @@ def apply_model(model, mix, shifts=1, split=True, When `device` is different from `mix.device`, only local computations will be on `device`, while the entire tracks will be stored on `mix.device`. """ + + base_text = 'File {file_num}/{total_files} '.format(file_num=file_num, + total_files=total_files) + + global fut_length + if device is None: device = mix.device else: @@ -145,7 +152,12 @@ def apply_model(model, mix, shifts=1, split=True, pool = ThreadPoolExecutor(num_workers) else: pool = DummyPoolExecutor() + kwargs = { + 'gui_progress_bar': gui_progress_bar, + 'widget_text': widget_text, + 'update_prog': update_prog, + 'segmen': segmen, 'shifts': shifts, 'split': split, 'overlap': overlap, @@ -153,17 +165,35 @@ def apply_model(model, mix, shifts=1, split=True, 'progress': progress, 'device': device, 'pool': pool, + 'total_files': total_files, + 'file_num': file_num, + 'inference_type': inference_type } + if isinstance(model, BagOfModels): # Special treatment for bag of model. # We explicitely apply multiple times `apply_model` so that the random shifts # are different for each model. + global bag_num + global current_model + global progress_bar + global prog_bar + #global percent_prog_del + + #percent_prog_del = gui_progress_bar.get() + + progress_bar = 0 + prog_bar = 0 estimates = 0 totals = [0] * len(model.sources) + bag_num = len(model.models) + fut_length = 0 + current_model = 0 #(bag_num + 1) for sub_model, weight in zip(model.models, model.weights): original_model_device = next(iter(sub_model.parameters())).device sub_model.to(device) - + fut_length += fut_length + current_model += 1 out = apply_model(sub_model, mix, **kwargs) sub_model.to(original_model_device) for k, inst_weight in enumerate(weight): @@ -179,6 +209,7 @@ def apply_model(model, mix, shifts=1, split=True, model.to(device) assert transition_power >= 1, "transition_power < 1 leads to weird behavior." batch, channels, length = mix.shape + if split: kwargs['split'] = False out = th.zeros(batch, len(model.sources), channels, length, device=mix.device) @@ -202,9 +233,26 @@ def apply_model(model, mix, shifts=1, split=True, future = pool.submit(apply_model, model, chunk, **kwargs) futures.append((future, offset)) offset += segment - if progress: - futures = tqdm.tqdm(futures, unit_scale=scale, ncols=120, unit='seconds') for future, offset in futures: + if segmen: + fut_length = len(futures) + full_fut_length = (fut_length * bag_num) + send_back = full_fut_length * 2 + progress_bar += 100 + prog_bar += 1 + full_step = (progress_bar / full_fut_length) + percent_prog = f"{base_text}Demucs Inference Progress: {prog_bar}/{full_fut_length} | {round(full_step)}%" + if inference_type == 'demucs_only': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.1 + (1.7/send_back * prog_bar))) + elif inference_type == 'inference_mdx': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.35 + (1.05/send_back * prog_bar))) + elif inference_type == 'inference_vr': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.6 + (0.7/send_back * prog_bar))) + widget_text.percentage(percent_prog) + #gui_progress_bar.set(step) chunk_out = future.result() chunk_length = chunk_out.shape[-1] out[..., offset:offset + segment] += (weight[:chunk_length] * chunk_out).to(mix.device) diff --git a/demucs/utils.py b/demucs/utils.py index 22f3b9a..f09bc18 100644 --- a/demucs/utils.py +++ b/demucs/utils.py @@ -22,6 +22,7 @@ import socket import tempfile import warnings import zlib +import tkinter as tk from diffq import UniformQuantizer, DiffQuantizer import torch as th @@ -228,7 +229,7 @@ def tensor_chunk(tensor_or_chunk): return TensorChunk(tensor_or_chunk) -def apply_model_v1(model, mix, shifts=None, split=False, progress=False): +def apply_model_v1(model, mix, gui_progress_bar: tk.Variable, widget_text: tk.Text, update_prog, total_files, file_num, inference_type, shifts=None, split=False, progress=False, segmen=True): """ Apply model to a given mixture. @@ -242,6 +243,10 @@ def apply_model_v1(model, mix, shifts=None, split=False, progress=False): Useful for model with large memory footprint like Tasnet. progress (bool): if True, show a progress bar (requires split=True) """ + + base_text = 'File {file_num}/{total_files} '.format(file_num=file_num, + total_files=total_files) + channels, length = mix.size() device = mix.device if split: @@ -249,11 +254,31 @@ def apply_model_v1(model, mix, shifts=None, split=False, progress=False): shift = model.samplerate * 10 offsets = range(0, length, shift) scale = 10 + progress_bar = 0 + prog_bar = 0 if progress: offsets = tqdm.tqdm(offsets, unit_scale=scale, ncols=120, unit='seconds') for offset in offsets: + if segmen: + fut_length = len(offsets) + send_back = fut_length * 2 + progress_bar += 100 + prog_bar += 1 + if inference_type == 'demucs_only': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.1 + (1.7/send_back * prog_bar))) + elif inference_type == 'inference_mdx': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.35 + (1.05/send_back * prog_bar))) + elif inference_type == 'inference_vr': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.6 + (0.7/send_back * prog_bar))) + step = (progress_bar / fut_length) + percent_prog = f"{base_text}Demucs v1 Inference Progress: {prog_bar}/{fut_length} | {round(step)}%" + widget_text.percentage(percent_prog) + #gui_progress_bar.set(step) chunk = mix[..., offset:offset + shift] - chunk_out = apply_model_v1(model, chunk, shifts=shifts) + chunk_out = apply_model_v1(model, chunk, gui_progress_bar, widget_text, update_prog, total_files, file_num, inference_type, shifts=shifts) out[..., offset:offset + shift] = chunk_out offset += shift return out @@ -265,7 +290,7 @@ def apply_model_v1(model, mix, shifts=None, split=False, progress=False): out = 0 for offset in offsets[:shifts]: shifted = mix[..., offset:offset + length + max_shift] - shifted_out = apply_model_v1(model, shifted) + shifted_out = apply_model_v1(model, shifted, gui_progress_bar, widget_text, update_prog, total_files, file_num, inference_type) out += shifted_out[..., max_shift - offset:max_shift - offset + length] out /= shifts return out @@ -277,8 +302,8 @@ def apply_model_v1(model, mix, shifts=None, split=False, progress=False): out = model(padded.unsqueeze(0))[0] return center_trim(out, mix) -def apply_model_v2(model, mix, shifts=None, split=False, - overlap=0.25, transition_power=1., progress=False): +def apply_model_v2(model, mix, gui_progress_bar: tk.Variable, widget_text: tk.Text, update_prog, total_files, file_num, inference_type, shifts=None, split=False, + overlap=0.25, transition_power=1., progress=False, segmen=True): """ Apply model to a given mixture. @@ -292,6 +317,16 @@ def apply_model_v2(model, mix, shifts=None, split=False, Useful for model with large memory footprint like Tasnet. progress (bool): if True, show a progress bar (requires split=True) """ + + global prog_space + global percent_prog + + percent_prog = 0 + + base_text = 'File {file_num}/{total_files} '.format(file_num=file_num, + total_files=total_files) + + #widget_text.remove(percent_prog) assert transition_power >= 1, "transition_power < 1 leads to weird behavior." device = mix.device channels, length = mix.shape @@ -313,9 +348,30 @@ def apply_model_v2(model, mix, shifts=None, split=False, # If the overlap < 50%, this will translate to linear transition when # transition_power is 1. weight = (weight / weight.max())**transition_power + progress_bar = 0 + prog_bar = 0 for offset in offsets: + if segmen: + fut_length = len(offsets) + send_back = fut_length * 2 + progress_bar += 100 + prog_bar += 1 + if inference_type == 'demucs_only': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.1 + (1.7/send_back * prog_bar))) + elif inference_type == 'inference_mdx': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.35 + (1.05/send_back * prog_bar))) + elif inference_type == 'inference_vr': + update_prog(gui_progress_bar, total_files, file_num, + step=(0.6 + (0.7/send_back * prog_bar))) + step = (progress_bar / fut_length) + percent_prog = f"{base_text}Demucs v2 Inference Progress: {prog_bar}/{fut_length} | {round(step)}%" + prog_space = len(percent_prog) + prog_space = prog_bar*prog_space + widget_text.percentage(percent_prog) chunk = TensorChunk(mix, offset, segment) - chunk_out = apply_model_v2(model, chunk, shifts=shifts) + chunk_out = apply_model_v2(model, chunk, gui_progress_bar, widget_text, update_prog, total_files, file_num, inference_type, shifts=shifts) chunk_length = chunk_out.shape[-1] out[..., offset:offset + segment] += weight[:chunk_length] * chunk_out sum_weight[offset:offset + segment] += weight[:chunk_length] @@ -331,7 +387,7 @@ def apply_model_v2(model, mix, shifts=None, split=False, for _ in range(shifts): offset = random.randint(0, max_shift) shifted = TensorChunk(padded_mix, offset, length + max_shift - offset) - shifted_out = apply_model_v2(model, shifted) + shifted_out = apply_model_v2(model, shifted, gui_progress_bar, widget_text, update_prog, total_files, file_num, inference_type) out += shifted_out[..., max_shift - offset:] out /= shifts return out diff --git a/inference_MDX.py b/inference_MDX.py index 56b3486..79c736c 100644 --- a/inference_MDX.py +++ b/inference_MDX.py @@ -35,6 +35,7 @@ import pydub import shutil import soundfile as sf import subprocess +from UVR import MainWindow import sys import time import time # Timer @@ -61,45 +62,50 @@ class Predictor(): self.noise_pro_select_set_var = tk.StringVar(value='MDX-NET_Noise_Profile_14_kHz') self.compensate_v_var = tk.StringVar(value=1.03597672895) - top= Toplevel() + mdx_model_set = Toplevel() - top.geometry("740x550") - window_height = 740 - window_width = 550 + mdx_model_set.geometry("490x515") + window_height = 490 + window_width = 515 - top.title("Specify Parameters") + mdx_model_set.title("Specify Parameters") - top.resizable(False, False) # This code helps to disable windows from resizing + mdx_model_set.resizable(False, False) # This code helps to disable windows from resizing - top.attributes("-topmost", True) + mdx_model_set.attributes("-topmost", True) - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = mdx_model_set.winfo_screenwidth() + screen_height = mdx_model_set.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + mdx_model_set.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + + x = main_window.winfo_x() + y = main_window.winfo_y() + mdx_model_set.geometry("+%d+%d" %(x+50,y+150)) + mdx_model_set.wm_transient(main_window) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + mdx_model_set.iconbitmap('img\\UVR-Icon-v2.ico') - tabControl = ttk.Notebook(top) + mdx_model_set_window = ttk.Notebook(mdx_model_set) - tabControl.pack(expand = 1, fill ="both") + mdx_model_set_window.pack(expand = 1, fill ="both") - tabControl.grid_rowconfigure(0, weight=1) - tabControl.grid_columnconfigure(0, weight=1) + mdx_model_set_window.grid_rowconfigure(0, weight=1) + mdx_model_set_window.grid_columnconfigure(0, weight=1) - frame0=Frame(tabControl,highlightbackground='red',highlightthicknes=0) + frame0=Frame(mdx_model_set_window,highlightbackground='red',highlightthicknes=0) frame0.grid(row=0,column=0,padx=0,pady=0) - frame0.tkraise(frame0) + #frame0.tkraise(frame0) space_small = ' '*20 space_small_1 = ' '*10 - l0=tk.Label(frame0, text=f'{space_small}Stem Type{space_small}', font=("Century Gothic", "9"), foreground='#13a4c9') + l0=tk.Label(frame0, text=f'\n{space_small}Stem Type{space_small}', font=("Century Gothic", "9"), foreground='#13a4c9') l0.grid(row=3,column=0,padx=0,pady=5) l0=ttk.OptionMenu(frame0, self.mdxnetModeltype_var, None, 'Vocals', 'Instrumental', 'Other', 'Bass', 'Drums') @@ -160,18 +166,15 @@ class Predictor(): torch.cuda.empty_cache() gui_progress_bar.set(0) widget_button.configure(state=tk.NORMAL) # Enable Button - top.destroy() + self.okVar.set(1) + stop_button() + mdx_model_set.destroy() return l0=ttk.Button(frame0,text="Stop Process", command=stop) l0.grid(row=13,column=1,padx=0,pady=30) - def change_event(): - self.okVar.set(1) - #top.destroy() - pass - - top.protocol("WM_DELETE_WINDOW", change_event) + mdx_model_set.protocol("WM_DELETE_WINDOW", stop) frame0.wait_variable(self.okVar) @@ -217,13 +220,13 @@ class Predictor(): stem_text_b = 'Vocals' elif stemset_n == '(Other)': stem_text_a = 'Other' - stem_text_b = 'the no \"Other\" track' + stem_text_b = 'mixture without selected stem' elif stemset_n == '(Drums)': stem_text_a = 'Drums' - stem_text_b = 'no \"Drums\" track' + stem_text_b = 'mixture without selected stem' elif stemset_n == '(Bass)': stem_text_a = 'Bass' - stem_text_b = 'No \"Bass\" track' + stem_text_b = 'mixture without selected stem' else: stem_text_a = 'Vocals' stem_text_b = 'Instrumental' @@ -263,7 +266,7 @@ class Predictor(): widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') demucs_model_set = 'UVR_Demucs_Model_1' - top.destroy() + mdx_model_set.destroy() def prediction_setup(self): @@ -287,6 +290,10 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(state) widget_text.write('Done!\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Segments is only available in Demucs v3. Please use \"Chunks\" instead.\n') + else: + pass if demucs_model_version == 'v2': if '48' in demucs_model_set: @@ -306,6 +313,10 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(torch.load("models/Demucs_Models/"f"{demucs_model_set}")) widget_text.write('Done!\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Segments is only available in Demucs v3. Please use \"Chunks\" instead.\n') + else: + pass self.demucs.eval() if demucs_model_version == 'v3': @@ -324,6 +335,37 @@ class Predictor(): widget_text.write('Done!\n') if isinstance(self.demucs, BagOfModels): widget_text.write(base_text + f"Selected Demucs model is a bag of {len(self.demucs.models)} model(s).\n") + + if data['segment'] == 'Default': + segment = None + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment + else: + try: + segment = int(data['segment']) + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment + if split_mode: + widget_text.write(base_text + "Segments set to "f"{segment}.\n") + except: + segment = None + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment self.onnx_models = {} c = 0 @@ -394,13 +436,13 @@ class Predictor(): if data['modelFolder']: vocal_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}',) vocal_path_mp3 = '{save_path}/{file_name}.mp3'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}',) vocal_path_flac = '{save_path}/{file_name}.flac'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}',) else: vocal_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, @@ -428,13 +470,13 @@ class Predictor(): if data['modelFolder']: Instrumental_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}',) Instrumental_path_mp3 = '{save_path}/{file_name}.mp3'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}',) Instrumental_path_flac = '{save_path}/{file_name}.flac'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}',) else: Instrumental_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, @@ -461,13 +503,13 @@ class Predictor(): if data['modelFolder']: non_reduced_vocal_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}_No_Reduction',) non_reduced_vocal_path_mp3 = '{save_path}/{file_name}.mp3'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}_No_Reduction',) non_reduced_vocal_path_flac = '{save_path}/{file_name}.flac'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{vocal_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{vocal_name}_{mdx_model_name}_No_Reduction',) else: non_reduced_vocal_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, @@ -482,13 +524,13 @@ class Predictor(): if data['modelFolder']: non_reduced_Instrumental_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}_No_Reduction',) non_reduced_Instrumental_path_mp3 = '{save_path}/{file_name}.mp3'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}_No_Reduction',) non_reduced_Instrumental_path_flac = '{save_path}/{file_name}.flac'.format( save_path=save_path, - file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{model_set_name}_No_Reduction',) + file_name = f'{os.path.basename(_basename)}_{Instrumental_name}_{mdx_model_name}_No_Reduction',) else: non_reduced_Instrumental_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, @@ -918,19 +960,21 @@ class Predictor(): widget_text.write(base_text + 'Completed Separation!\n') def demix(self, mix): + global chunk_set + # 1 = demucs only # 0 = onnx only if data['chunks'] == 'Full': chunk_set = 0 - else: - chunk_set = data['chunks'] - - if data['chunks'] == 'Auto': + widget_text.write(base_text + "Chunk size user-set to \"Full\"... \n") + elif data['chunks'] == 'Auto': if data['gpu'] == 0: try: gpu_mem = round(torch.cuda.get_device_properties(0).total_memory/1.074e+9) except: widget_text.write(base_text + 'NVIDIA GPU Required for conversion!\n') + data['gpu'] = -1 + pass if int(gpu_mem) <= int(6): chunk_set = int(5) widget_text.write(base_text + 'Chunk size auto-set to 5... \n') @@ -954,9 +998,9 @@ class Predictor(): if int(sys_mem) >= int(17): chunk_set = int(60) widget_text.write(base_text + 'Chunk size auto-set to 60... \n') - elif data['chunks'] == 'Full': + elif data['chunks'] == '0': chunk_set = 0 - widget_text.write(base_text + "Chunk size set to full... \n") + widget_text.write(base_text + "Chunk size user-set to \"Full\"... \n") else: chunk_set = int(data['chunks']) widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") @@ -986,29 +1030,33 @@ class Predictor(): segmented_mix[skip] = mix[:,start:end].copy() if end == samples: break - if not data['demucsmodel']: sources = self.demix_base(segmented_mix, margin_size=margin) elif data['demucs_only']: - if split_mode == True: + if no_chunk_demucs == False: sources = self.demix_demucs_split(mix) - if split_mode == False: + if no_chunk_demucs == True: sources = self.demix_demucs(segmented_mix, margin_size=margin) else: # both, apply spec effects base_out = self.demix_base(segmented_mix, margin_size=margin) - #print(split_mode) - if demucs_model_version == 'v1': - demucs_out = self.demix_demucs_v1(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + demucs_out = self.demix_demucs_v1_split(mix) + if no_chunk_demucs == True: + demucs_out = self.demix_demucs_v1(segmented_mix, margin_size=margin) if demucs_model_version == 'v2': - demucs_out = self.demix_demucs_v2(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + demucs_out = self.demix_demucs_v2_split(mix) + if no_chunk_demucs == True: + demucs_out = self.demix_demucs_v2(segmented_mix, margin_size=margin) if demucs_model_version == 'v3': - if split_mode == True: + if no_chunk_demucs == False: demucs_out = self.demix_demucs_split(mix) - if split_mode == False: + if no_chunk_demucs == True: demucs_out = self.demix_demucs(segmented_mix, margin_size=margin) + nan_count = np.count_nonzero(np.isnan(demucs_out)) + np.count_nonzero(np.isnan(base_out)) if nan_count > 0: print('Warning: there are {} nan values in the array(s).'.format(nan_count)) @@ -1040,10 +1088,15 @@ class Predictor(): onnxitera = len(mixes) onnxitera_calc = onnxitera * 2 gui_progress_bar_onnx = 0 - widget_text.write(base_text + "Running ONNX Inference...\n") - widget_text.write(base_text + "Processing "f"{onnxitera} slices... ") + progress_bar = 0 + print(' Running ONNX Inference...') + if onnxitera == 1: + widget_text.write(base_text + f"Running ONNX Inference... ") + else: + widget_text.write(base_text + f"Running ONNX Inference...{space}\n") + for mix in mixes: gui_progress_bar_onnx += 1 if data['demucsmodel']: @@ -1053,6 +1106,15 @@ class Predictor(): update_progress(**progress_kwargs, step=(0.1 + (0.9/onnxitera * gui_progress_bar_onnx))) + progress_bar += 100 + step = (progress_bar / onnxitera) + + if onnxitera == 1: + pass + else: + percent_prog = f"{base_text}MDX-Net Inference Progress: {gui_progress_bar_onnx}/{onnxitera} | {round(step)}%" + widget_text.percentage(percent_prog) + cmix = mixes[mix] sources = [] n_sample = cmix.shape[1] @@ -1088,21 +1150,35 @@ class Predictor(): chunked_sources.append(sources) _sources = np.concatenate(chunked_sources, axis=-1) del self.onnx_models - widget_text.write('Done!\n') + + if onnxitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + return _sources def demix_demucs(self, mix, margin_size): - #print('shift_set ', shift_set) processed = {} demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Split Mode is off. (Chunks enabled for Demucs Model)\n") - widget_text.write(base_text + "Running Demucs Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs Inference... ") + else: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") + print(' Running Demucs Inference...') for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -1110,8 +1186,17 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - #print(split_mode) - sources = apply_model(self.demucs, cmix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] + sources = apply_model(self.demucs, cmix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=False, + **progress_demucs_kwargs)[0] sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -1123,17 +1208,21 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') #print('the demucs model is done running') return sources def demix_demucs_split(self, mix): - - #print('shift_set ', shift_set) - widget_text.write(base_text + "Split Mode is on. (Chunks disabled for Demucs Model)\n") - widget_text.write(base_text + "Running Demucs Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + + if split_mode: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs Inference... ") print(' Running Demucs Inference...') mix = torch.tensor(mix, dtype=torch.float32) @@ -1141,14 +1230,26 @@ class Predictor(): mix = (mix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model(self.demucs, mix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] + sources = apply_model(self.demucs, + mix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=True, + **progress_demucs_kwargs)[0] - widget_text.write('Done!\n') + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] - - #print('the demucs model is done running') return sources @@ -1157,11 +1258,21 @@ class Predictor(): demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v1 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + progress_bar = 0 print(' Running Demucs Inference...') + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v1 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -1169,7 +1280,15 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model_v1(self.demucs, cmix.to(device), split=split_mode, shifts=shift_set) + sources = apply_model_v1(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + shifts=shift_set, + **progress_demucs_kwargs) sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -1181,7 +1300,44 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + + return sources + + def demix_demucs_v1_split(self, mix): + + print(' Running Demucs Inference...') + if split_mode: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + + with torch.no_grad(): + sources = apply_model_v1(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + shifts=shift_set, + **progress_demucs_kwargs) + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + return sources def demix_demucs_v2(self, mix, margin_size): @@ -1189,11 +1345,22 @@ class Predictor(): demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v2 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") - print(' Running Demucs Inference...') + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v2 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) + update_progress(**progress_kwargs, step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -1201,7 +1368,16 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model_v2(self.demucs, cmix.to(device), split=split_mode, overlap=overlap_set, shifts=shift_set) + sources = apply_model_v2(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -1213,9 +1389,46 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + return sources + def demix_demucs_v2_split(self, mix): + print(' Running Demucs Inference...') + + if split_mode: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + with torch.no_grad(): + sources = apply_model_v2(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) + + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + + return sources data = { @@ -1240,6 +1453,7 @@ data = { 'modelFolder': False, 'mp3bit': '320k', 'n_fft_scale': 6144, + 'no_chunk': False, 'noise_pro_select': 'Auto Select', 'noisereduc_s': 3, 'non_red': False, @@ -1247,6 +1461,7 @@ data = { 'normalize': False, 'overlap': 0.5, 'saveFormat': 'Wav', + 'segment': 'Default', 'shifts': 0, 'split_mode': False, 'voc_only': False, @@ -1286,6 +1501,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress_var: tk.Variable, + stop_thread, **kwargs: dict): global widget_text @@ -1299,8 +1515,10 @@ def main(window: tk.Wm, global n_fft_scale_set global dim_f_set global progress_kwargs + global progress_demucs_kwargs global base_text global model_set_name + global mdx_model_name global stemset_n global stem_text_a global stem_text_b @@ -1325,17 +1543,20 @@ def main(window: tk.Wm, global stime global model_hash global demucs_switch + global no_chunk_demucs global inst_only global voc_only + global space + global main_window + global stop_button - - # Update default settings - default_chunks = data['chunks'] - default_noisereduc_s = data['noisereduc_s'] + stop_button = stop_thread widget_text = text_widget gui_progress_bar = progress_var widget_button = button_widget + main_window = window + #Error Handling @@ -1361,6 +1582,15 @@ def main(window: tk.Wm, data.update(kwargs) + global update_prog + + # Update default settings + update_prog = update_progress + default_chunks = data['chunks'] + default_noisereduc_s = data['noisereduc_s'] + no_chunk_demucs = data['no_chunk'] + space = ' '*90 + if data['DemucsModel_MDX'] == "Tasnet v1": demucs_model_set_name = 'tasnet.th' demucs_model_version = 'v1' @@ -1436,6 +1666,10 @@ def main(window: tk.Wm, mdx_model_name = 'UVR_MDXNET_KARA' elif model_set_name == 'UVR-MDX-NET Main': mdx_model_name = 'UVR_MDXNET_Main' + elif model_set_name == 'UVR-MDX-NET Inst 1': + mdx_model_name = 'UVR_MDXNET_Inst_1' + elif model_set_name == 'UVR-MDX-NET Inst 2': + mdx_model_name = 'UVR_MDXNET_Inst_2' else: mdx_model_name = data['mdxnetModel'] @@ -1583,12 +1817,18 @@ def main(window: tk.Wm, _basename = f'{data["export_path"]}/{str(randomnum)}_{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}' else: _basename = f'{data["export_path"]}/{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}' + + + inference_type = 'inference_mdx' + # -Get text and update progress- base_text = get_baseText(total_files=len(data['input_paths']), file_num=file_num) progress_kwargs = {'progress_var': progress_var, 'total_files': len(data['input_paths']), 'file_num': file_num} + progress_demucs_kwargs = {'total_files': len(data['input_paths']), + 'file_num': file_num, 'inference_type': inference_type} if 'UVR' in demucs_model_set: @@ -1603,10 +1843,11 @@ def main(window: tk.Wm, if stemset_n == '(Instrumental)': if not 'UVR' in demucs_model_set: - widget_text.write(base_text + 'The selected Demucs model cannot be used with this model.\n') - widget_text.write(base_text + 'Only 2 stem Demucs models are compatible with this model.\n') - widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') - demucs_model_set = 'UVR_Demucs_Model_1' + if data['demucsmodel']: + widget_text.write(base_text + 'The selected Demucs model cannot be used with this model.\n') + widget_text.write(base_text + 'Only 2 stem Demucs models are compatible with this model.\n') + widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') + demucs_model_set = 'UVR_Demucs_Model_1' try: if float(data['noisereduc_s']) >= 11: @@ -1904,7 +2145,7 @@ def main(window: tk.Wm, text_widget.write(f'\nError Received:\n\n') text_widget.write(f'Could not write audio file.\n') text_widget.write(f'This could be due to low storage on target device or a system permissions issue.\n') - text_widget.write(f"\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write(f"\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write(f'\nIf the error persists, please contact the developers.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') try: @@ -2013,7 +2254,7 @@ def main(window: tk.Wm, text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') text_widget.write(f'\nError Received:\n') - text_widget.write("\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write("\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write("\n" + f'Please address the error and try again.' + "\n") text_widget.write(f'If this error persists, please contact the developers with the error details.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') diff --git a/inference_demucs.py b/inference_demucs.py index 085be79..825d731 100644 --- a/inference_demucs.py +++ b/inference_demucs.py @@ -6,6 +6,7 @@ from demucs.pretrained import get_model as _gm from demucs.tasnet_v2 import ConvTasNet from demucs.utils import apply_model_v1 from demucs.utils import apply_model_v2 +import demucs.apply from diffq import DiffQuantizer from lib_v5 import spec_utils from lib_v5.model_param_init import ModelParameters @@ -30,6 +31,7 @@ import tkinter as tk import torch import torch.hub import traceback # Error Message Recent Calls +import threading import warnings import zlib @@ -58,8 +60,8 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(state) widget_text.write('Done!\n') - if not data['segment'] == 'None': - widget_text.write(base_text + 'Segments is only available in Demucs v3. Please use \"Chunks\" instead.\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Note: Segments only available for Demucs v3\n') else: pass @@ -81,8 +83,8 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(torch.load("models/Demucs_Models/"f"{demucs_model_set_name}")) widget_text.write('Done!\n') - if not data['segment'] == 'None': - widget_text.write(base_text + 'Segments is only available in Demucs v3. Please use \"Chunks\" instead.\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Note: Segments only available for Demucs v3\n') else: pass self.demucs.eval() @@ -101,7 +103,7 @@ class Predictor(): if isinstance(self.demucs, BagOfModels): widget_text.write(base_text + f"Selected model is a bag of {len(self.demucs.models)} models.\n") - if data['segment'] == 'None': + if data['segment'] == 'Default': segment = None if isinstance(self.demucs, BagOfModels): if segment is not None: @@ -120,7 +122,8 @@ class Predictor(): else: if segment is not None: sub.segment = segment - widget_text.write(base_text + "Segments set to "f"{segment}.\n") + if split_mode: + widget_text.write(base_text + "Segments set to "f"{segment}.\n") except: segment = None if isinstance(self.demucs, BagOfModels): @@ -145,7 +148,7 @@ class Predictor(): mix = mix.T sources = self.demix(mix.T) - widget_text.write(base_text + 'Inferences complete!\n') + widget_text.write(base_text + 'Inference complete!\n') #Main Save Path save_path = os.path.dirname(_basename) @@ -155,6 +158,25 @@ class Predictor(): drums_name = '(Drums)' bass_name = '(Bass)' + if stemset_n == '(Vocals)': + stem_text_a = 'Vocals' + stem_text_b = 'Instrumental' + elif stemset_n == '(Instrumental)': + stem_text_a = 'Instrumental' + stem_text_b = 'Vocals' + elif stemset_n == '(Other)': + stem_text_a = 'Other' + stem_text_b = 'mixture without selected stem' + elif stemset_n == '(Drums)': + stem_text_a = 'Drums' + stem_text_b = 'mixture without selected stem' + elif stemset_n == '(Bass)': + stem_text_a = 'Bass' + stem_text_b = 'mixture without selected stem' + else: + stem_text_a = 'Vocals' + stem_text_b = 'Instrumental' + vocals_path = '{save_path}/{file_name}.wav'.format( save_path=save_path, file_name = f'{os.path.basename(_basename)}_{vocals_name}',) @@ -202,8 +224,6 @@ class Predictor(): save_path=save_path, file_name = f'{os.path.basename(_basename)}_{bass_name}',) - - #If not 'All Stems' if stemset_n == '(Vocals)': @@ -273,7 +293,7 @@ class Predictor(): if not data['demucs_stems'] == 'All Stems': if data['inst_only_b']: - widget_text.write(base_text + 'Preparing mixture without selected stem...') + widget_text.write(base_text + 'Preparing mixture without selected stem... ') else: widget_text.write(base_text + 'Saving Stem(s)... ') else: @@ -415,7 +435,7 @@ class Predictor(): widget_text.write('Done!\n') update_progress(**progress_kwargs, - step=(0.9)) + step=(1)) if data['demucs_stems'] == 'All Stems': pass @@ -430,7 +450,7 @@ class Predictor(): 'files':[str(music_file), vocal_path], } ] - widget_text.write(base_text + 'Saving Instrumental... ') + widget_text.write(base_text + f'Saving {stem_text_b}... ') for i, e in tqdm(enumerate(finalfiles)): wave, specs = {}, {} @@ -469,7 +489,6 @@ class Predictor(): step=(1)) sf.write(Instrumental_path, normalization_set(spec_utils.cmb_spectrogram_to_wave(-v_spec, mp)), mp.param['sr'], subtype=wav_type_set) - if data['inst_only_b']: if file_exists_v == 'there': @@ -482,7 +501,6 @@ class Predictor(): widget_text.write('Done!\n') - if not data['demucs_stems'] == 'All Stems': if data['saveFormat'] == 'Mp3': @@ -604,76 +622,65 @@ class Predictor(): widget_text.write(base_text + 'Completed Separation!\n') def demix(self, mix): - + global chunk_set # 1 = demucs only # 0 = onnx only + if data['chunks_d'] == 'Full': - if split_mode == True: - chunk_set = 0 - else: - widget_text.write(base_text + "Chunk size set to full... \n") - chunk_set = 0 - else: - chunk_set = data['chunks'] - - if data['chunks_d'] == 'Auto': - if split_mode == True: - widget_text.write(base_text + "Split Mode is on (Chunks disabled).\n") - chunk_set = 0 - else: - widget_text.write(base_text + "Split Mode is off (Chunks enabled).\n") - if data['gpu'] == 0: - try: - gpu_mem = round(torch.cuda.get_device_properties(0).total_memory/1.074e+9) - except: - widget_text.write(base_text + 'NVIDIA GPU Required for conversion!\n') - if int(gpu_mem) <= int(6): - chunk_set = int(10) - widget_text.write(base_text + 'Chunk size auto-set to 10... \n') - if gpu_mem in [7, 8, 9]: - chunk_set = int(30) - widget_text.write(base_text + 'Chunk size auto-set to 30... \n') - if gpu_mem in [10, 11, 12, 13, 14, 15]: - chunk_set = int(50) - widget_text.write(base_text + 'Chunk size auto-set to 50... \n') - if int(gpu_mem) >= int(16): - chunk_set = int(0) - widget_text.write(base_text + 'Chunk size auto-set to Full... \n') - if data['gpu'] == -1: - sys_mem = psutil.virtual_memory().total >> 30 - if int(sys_mem) <= int(4): - chunk_set = int(5) + chunk_set = 0 + elif data['chunks_d'] == 'Auto': + if data['gpu'] == 0: + try: + gpu_mem = round(torch.cuda.get_device_properties(0).total_memory/1.074e+9) + except: + widget_text.write(base_text + 'NVIDIA GPU Required for conversion!\n') + if int(gpu_mem) <= int(6): + chunk_set = int(5) + if no_chunk_demucs: widget_text.write(base_text + 'Chunk size auto-set to 5... \n') - if sys_mem in [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]: - chunk_set = int(10) + if gpu_mem in [7, 8, 9, 10, 11, 12, 13, 14, 15]: + chunk_set = int(10) + if no_chunk_demucs: widget_text.write(base_text + 'Chunk size auto-set to 10... \n') - if sys_mem in [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32]: - chunk_set = int(40) + if int(gpu_mem) >= int(16): + chunk_set = int(40) + if no_chunk_demucs: widget_text.write(base_text + 'Chunk size auto-set to 40... \n') - if int(sys_mem) >= int(33): - chunk_set = int(0) - widget_text.write(base_text + 'Chunk size auto-set to Full... \n') + if data['gpu'] == -1: + sys_mem = psutil.virtual_memory().total >> 30 + if int(sys_mem) <= int(4): + chunk_set = int(1) + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 1... \n') + if sys_mem in [5, 6, 7, 8]: + chunk_set = int(10) + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 10... \n') + if sys_mem in [9, 10, 11, 12, 13, 14, 15, 16]: + chunk_set = int(25) + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 25... \n') + if int(sys_mem) >= int(17): + chunk_set = int(60) + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 60... \n') + elif data['chunks_d'] == str(0): + chunk_set = 0 + if no_chunk_demucs: + widget_text.write(base_text + "Chunk size set to full... \n") else: - if split_mode == True: - widget_text.write(base_text + "Split Mode is on (Chunks disabled).\n") - chunk_set = 0 - else: - widget_text.write(base_text + "Split Mode is off (Chunks enabled).\n") - if data['chunks_d'] == 'Full': - chunk_set = int(0) - widget_text.write(base_text + "Chunk size set to full... \n") - else: - chunk_set = data['chunks_d'] - widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") + chunk_set = int(data['chunks_d']) + if no_chunk_demucs: + widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") samples = mix.shape[-1] margin = margin_set chunk_size = chunk_set*44100 assert not margin == 0, 'margin cannot be zero!' + if margin > chunk_size: margin = chunk_size - segmented_mix = {} if chunk_set == 0 or samples < chunk_size: @@ -692,27 +699,45 @@ class Predictor(): if end == samples: break - if demucs_model_version == 'v1': - sources = self.demix_demucs_v1(segmented_mix, margin_size=margin) - if demucs_model_version == 'v2': - sources = self.demix_demucs_v2(segmented_mix, margin_size=margin) + if demucs_model_version == 'v1': + if no_chunk_demucs == False: + sources = self.demix_demucs_v1_split(mix) + if no_chunk_demucs == True: + sources = self.demix_demucs_v1(segmented_mix, margin_size=margin) + if demucs_model_version == 'v2': + if no_chunk_demucs == False: + sources = self.demix_demucs_v2_split(mix) + if no_chunk_demucs == True: + sources = self.demix_demucs_v2(segmented_mix, margin_size=margin) if demucs_model_version == 'v3': - sources = self.demix_demucs(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + sources = self.demix_demucs_split(mix) + if no_chunk_demucs == True: + sources = self.demix_demucs(segmented_mix, margin_size=margin) - return sources - - def demix_demucs(self, mix, margin_size): + return sources + def demix_demucs(self, mix, margin_size): processed = {} demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs Inference... ") + else: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") - widget_text.write(base_text + "Running Demucs Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") print(' Running Demucs Inference...') for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, step=(0.1 + (1.7/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -720,7 +745,17 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model(self.demucs, cmix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] + sources = apply_model(self.demucs, cmix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=False, + **progress_demucs_kwargs)[0] sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -732,7 +767,49 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + #print('the demucs model is done running') + + return sources + + def demix_demucs_split(self, mix): + + if split_mode: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs Inference... ") + print(' Running Demucs Inference...') + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + + with torch.no_grad(): + sources = apply_model(self.demucs, + mix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=True, + **progress_demucs_kwargs)[0] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + return sources def demix_demucs_v1(self, mix, margin_size): @@ -740,19 +817,37 @@ class Predictor(): demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v1 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + progress_bar = 0 print(' Running Demucs Inference...') + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v1 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, - step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) + step=(0.1 + (1.7/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] cmix = torch.tensor(cmix, dtype=torch.float32) ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model_v1(self.demucs, cmix.to(device), split=split_mode, shifts=shift_set) + sources = apply_model_v1(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + shifts=shift_set, + **progress_demucs_kwargs) sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -764,7 +859,44 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + + return sources + + def demix_demucs_v1_split(self, mix): + + print(' Running Demucs Inference...') + if split_mode: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + + with torch.no_grad(): + sources = apply_model_v1(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + shifts=shift_set, + **progress_demucs_kwargs) + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + return sources def demix_demucs_v2(self, mix, margin_size): @@ -772,20 +904,39 @@ class Predictor(): demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v2 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") - print(' Running Demucs Inference...') + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v2 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) + update_progress(**progress_kwargs, - step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) + step=(0.1 + (1.7/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] cmix = torch.tensor(cmix, dtype=torch.float32) ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() - shift_set = 0 with torch.no_grad(): - sources = apply_model_v2(self.demucs, cmix.to(device), split=split_mode, overlap=overlap_set, shifts=shift_set) + sources = apply_model_v2(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -797,8 +948,47 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + return sources + + def demix_demucs_v2_split(self, mix): + print(' Running Demucs Inference...') + + if split_mode: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + with torch.no_grad(): + sources = apply_model_v2(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) + + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + + return sources + data = { 'audfile': True, @@ -811,19 +1001,19 @@ data = { 'gpu': -1, 'input_paths': None, 'inst_only_b': False, - 'margin': 44100, + 'margin_d': 44100, 'mp3bit': '320k', + 'no_chunk_d': False, 'normalize': False, 'overlap_b': 0.25, 'saveFormat': 'Wav', - 'segment': 'None', + 'segment': 'Default', 'settest': False, 'shifts_b': 2, 'split_mode': False, 'voc_only_b': False, 'wavtype': 'PCM_16', } -default_chunks = data['chunks_d'] def update_progress(progress_var, total_files, file_num, step: float = 1): """Calculate the progress for the progress widget in the GUI""" @@ -850,7 +1040,7 @@ def hide_opt(): yield finally: sys.stdout = old_stdout - + def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress_var: tk.Variable, **kwargs: dict): @@ -861,6 +1051,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global _basename global _mixture global progress_kwargs + global progress_demucs_kwargs global base_text global model_set_name global stemset_n @@ -872,11 +1063,15 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global split_mode global demucs_model_set_name global demucs_model_version - global wav_type_set + global no_chunk_demucs + global space global flac_type_set global mp3_bit_set global normalization_set + global update_prog + + update_prog = update_progress wav_type_set = data['wavtype'] @@ -899,6 +1094,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress ffmp_err = """audioread\__init__.py", line 116, in audio_open""" sf_write_err = "sf.write" model_adv_set_err = "Got invalid dimensions for input" + demucs_model_missing_err = "is neither a single pre-trained model or a bag of models." try: with open('errorlog.txt', 'w') as f: @@ -911,7 +1107,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress randomnum = randrange(100000, 1000000) data.update(kwargs) - + if data['wavtype'] == '32-bit Float': wav_type_set = 'FLOAT' elif data['wavtype'] == '64-bit Float': @@ -921,6 +1117,9 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress flac_type_set = data['flactype'] mp3_bit_set = data['mp3bit'] + default_chunks = data['chunks_d'] + no_chunk_demucs = data['no_chunk_d'] + if data['normalize'] == True: normalization_set = spec_utils.normalize @@ -1057,10 +1256,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress overlap_set = float(data['overlap_b']) channel_set = int(data['channel']) - margin_set = int(data['margin']) + margin_set = int(data['margin_d']) shift_set = int(data['shifts_b']) - split_mode = data['split_mode'] + space = ' '*90 #print('Split? ', split_mode) @@ -1133,6 +1332,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress #if ('models/MDX_Net_Models/' + model_set + '.onnx') + inference_type = 'demucs_only' # -Get text and update progress- base_text = get_baseText(total_files=len(data['input_paths']), @@ -1140,6 +1340,8 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress progress_kwargs = {'progress_var': progress_var, 'total_files': len(data['input_paths']), 'file_num': file_num} + progress_demucs_kwargs = {'total_files': len(data['input_paths']), + 'file_num': file_num, 'inference_type': inference_type} try: @@ -1389,7 +1591,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(f'\nError Received:\n\n') text_widget.write(f'Could not write audio file.\n') text_widget.write(f'This could be due to low storage on target device or a system permissions issue.\n') - text_widget.write(f"\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write(f"\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write(f'\nIf the error persists, please contact the developers.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') try: @@ -1456,6 +1658,50 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress button_widget.configure(state=tk.NORMAL) # Enable Button return + if model_adv_set_err in message: + text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') + text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') + text_widget.write(f'\nError Received:\n\n') + text_widget.write(f'The current ONNX model settings are not compatible with the selected \nmodel.\n\n') + text_widget.write(f'Please re-configure the advanced ONNX model settings accordingly and try \nagain.\n\n') + text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') + try: + with open('errorlog.txt', 'w') as f: + f.write(f'Last Error Received:\n\n' + + f'Error Received while processing "{os.path.basename(music_file)}":\n' + + f'Process Method: Demucs v3\n\n' + + f'The current ONNX model settings are not compatible with the selected model.\n\n' + + f'Please re-configure the advanced ONNX model settings accordingly and try again.\n\n' + + message + f'\nError Time Stamp [{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}]\n') + except: + pass + torch.cuda.empty_cache() + progress_var.set(0) + button_widget.configure(state=tk.NORMAL) # Enable Button + return + + if demucs_model_missing_err in message: + text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') + text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') + text_widget.write(f'\nError Received:\n\n') + text_widget.write(f'The selected Demucs model is missing.\n\n') + text_widget.write(f'Please download the model or make sure it is in the correct directory.\n\n') + text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') + try: + with open('errorlog.txt', 'w') as f: + f.write(f'Last Error Received:\n\n' + + f'Error Received while processing "{os.path.basename(music_file)}":\n' + + f'Process Method: Demucs v3\n\n' + + f'The selected Demucs model is missing.\n\n' + + f'Please download the model or make sure it is in the correct directory.\n\n' + + message + f'\nError Time Stamp [{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}]\n') + except: + pass + torch.cuda.empty_cache() + progress_var.set(0) + button_widget.configure(state=tk.NORMAL) # Enable Button + return + print(traceback_text) print(type(e).__name__, e) @@ -1476,7 +1722,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') text_widget.write(f'\nError Received:\n') - text_widget.write("\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write("\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write("\n" + f'Please address the error and try again.' + "\n") text_widget.write(f'If this error persists, please contact the developers with the error details.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') @@ -1500,3 +1746,17 @@ if __name__ == '__main__': main() print("Successfully completed music demixing.");print('Total time: {0:.{1}f}s'.format(time.time() - start_time, 1)) +## Grave yard + + # def prog_val(): + # def thread(): + # global source + # source = apply_model(self.demucs, cmix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=True, )[0] + # th = threading.Thread(target=thread) + # th.start() + # print('wait') + # val = demucs.apply.progress_bar_num + # th.join() + # print('continue') + + # return source \ No newline at end of file diff --git a/inference_v5.py b/inference_v5.py index e6af489..54acd40 100644 --- a/inference_v5.py +++ b/inference_v5.py @@ -103,7 +103,7 @@ def determineModelFolderName(): def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress_var: tk.Variable, **kwargs: dict): - global model_params_d + global gui_progress_bar global nn_arch_sizes global nn_architecture @@ -115,9 +115,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global flac_type_set global mp3_bit_set + global space wav_type_set = data['wavtype'] - + gui_progress_bar = progress_var #Error Handling runtimeerr = "CUDNN error executing cudnnSetTensorNdDescriptor" @@ -127,6 +128,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress file_err = "FileNotFoundError" ffmp_err = """audioread\__init__.py", line 116, in audio_open""" sf_write_err = "sf.write" + demucs_model_missing_err = "is neither a single pre-trained model or a bag of models." try: with open('errorlog.txt', 'w') as f: @@ -382,8 +384,12 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global default_window_size global default_agg global normalization_set + global update_prog + + update_prog = update_progress default_window_size = data['window_size'] default_agg = data['agg'] + space = ' '*90 stime = time.perf_counter() progress_var.set(0) @@ -432,6 +438,9 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress else: base_name = f'{data["export_path"]}/{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}' + global inference_type + + inference_type = 'inference_vr' model_name = os.path.basename(data[f'{data["useModel"]}Model']) model = vocal_remover.models[data['useModel']] device = vocal_remover.devices[data['useModel']] @@ -441,6 +450,8 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress progress_kwargs = {'progress_var': progress_var, 'total_files': len(data['input_paths']), 'file_num': file_num} + progress_demucs_kwargs = {'total_files': len(data['input_paths']), + 'file_num': file_num, 'inference_type': inference_type} update_progress(**progress_kwargs, step=0) @@ -503,7 +514,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress model_hash = hashlib.md5(open(ModelName,'rb').read()).hexdigest() model_params = [] model_params = lib_v5.filelist.provide_model_param_hash(model_hash) - print(model_params) + #print(model_params) if model_params[0] == 'Not Found Using Hash': model_params = [] model_params = lib_v5.filelist.provide_model_param_name(ModelName) @@ -622,8 +633,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(base_text + 'Loading the stft of audio source...') text_widget.write(' Done!\n') - - text_widget.write(base_text + "Please Wait...\n") X_spec_m = spec_utils.combine_spectrograms(X_spec_s, mp) @@ -631,22 +640,47 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress def inference(X_spec, device, model, aggressiveness): - def _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness): + def _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness, tta=False): model.eval() + global active_iterations + global progress_value + with torch.no_grad(): preds = [] iterations = [n_window] - total_iterations = sum(iterations) - - text_widget.write(base_text + "Processing "f"{total_iterations} Slices... ") + if data['tta']: + total_iterations = sum(iterations) + total_iterations = total_iterations*2 + else: + total_iterations = sum(iterations) + + if tta: + active_iterations = sum(iterations) + active_iterations = active_iterations - 2 + total_iterations = total_iterations - 2 + else: + active_iterations = 0 - for i in tqdm(range(n_window)): - update_progress(**progress_kwargs, - step=(0.1 + (0.8/n_window * i))) + progress_bar = 0 + for i in range(n_window): + active_iterations += 1 + if data['demucsmodelVR']: + update_progress(**progress_kwargs, + step=(0.1 + (0.5/total_iterations * active_iterations))) + else: + update_progress(**progress_kwargs, + step=(0.1 + (0.8/total_iterations * active_iterations))) start = i * roi_size + progress_bar += 100 + progress_value = progress_bar + active_iterations_step = active_iterations*100 + step = (active_iterations_step / total_iterations) + + percent_prog = f"{base_text}Inference Progress: {active_iterations}/{total_iterations} | {round(step)}%" + text_widget.percentage(percent_prog) X_mag_window = X_mag_pad[None, :, :, start:start + data['window_size']] X_mag_window = torch.from_numpy(X_mag_window).to(device) @@ -656,7 +690,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress preds.append(pred[0]) pred = np.concatenate(preds, axis=2) - text_widget.write('Done!\n') return pred def preprocess(X_spec): @@ -691,7 +724,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress X_mag_pre, ((0, 0), (0, 0), (pad_l, pad_r)), mode='constant') pred_tta = _execute(X_mag_pad, roi_size, n_window, - device, model, aggressiveness) + device, model, aggressiveness, tta=True) pred_tta = pred_tta[:, :, roi_size // 2:] pred_tta = pred_tta[:, :, :n_frame] @@ -702,17 +735,16 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress aggressiveness = {'value': aggresive_set, 'split_bin': mp.param['band'][1]['crop_stop']} if data['tta']: - text_widget.write(base_text + "Running Inferences (TTA)...\n") + text_widget.write(base_text + f"Running Inferences (TTA)... {space}\n") else: - text_widget.write(base_text + "Running Inference...\n") + text_widget.write(base_text + f"Running Inference... {space}\n") pred, X_mag, X_phase = inference(X_spec_m, device, model, aggressiveness) - update_progress(**progress_kwargs, - step=0.9) - # Postprocess + text_widget.write('\n') + if data['postprocess']: try: text_widget.write(base_text + 'Post processing...') @@ -743,19 +775,38 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress v_spec_m = X_spec_m - y_spec_m def demix_demucs(mix): - #print('shift_set ', shift_set) - text_widget.write(base_text + "Running Demucs Inference...\n") - text_widget.write(base_text + "Processing... ") + print(' Running Demucs Inference...') + if split_mode: + text_widget.write(base_text + f'Running Demucs Inference... {space}') + else: + text_widget.write(base_text + f'Running Demucs Inference... ') + mix = torch.tensor(mix, dtype=torch.float32) ref = mix.mean(0) mix = (mix - ref.mean()) / ref.std() - + widget_text = text_widget with torch.no_grad(): - sources = apply_model(demucs, mix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] - - text_widget.write('Done!\n') + sources = apply_model(demucs, + mix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=True, + **progress_demucs_kwargs)[0] + + if split_mode: + text_widget.write('\n') + else: + update_progress(**progress_kwargs, + step=0.9) + text_widget.write('Done!\n') sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -774,15 +825,9 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress if data['demucsmodelVR']: demucs = HDemucs(sources=["other", "vocals"]) - text_widget.write(base_text + 'Loading Demucs model... ') - update_progress(**progress_kwargs, - step=0.95) path_d = Path('models/Demucs_Models/v3_repo') #print('What Demucs model was chosen? ', demucs_model_set) demucs = _gm(name=demucs_model_set, repo=path_d) - text_widget.write('Done!\n') - - #print('segment: ', data['segment']) if data['segment'] == 'None': segment = None @@ -803,7 +848,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress else: if segment is not None: sub.segment = segment - text_widget.write(base_text + "Segments set to "f"{segment}.\n") + #text_widget.write(base_text + "Segments set to "f"{segment}.\n") except: segment = None if isinstance(demucs, BagOfModels): @@ -814,8 +859,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress if segment is not None: sub.segment = segment - #print('segment port-process: ', segment) - demucs.cpu() demucs.eval() @@ -1039,7 +1082,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(f'\nError Received:\n\n') text_widget.write(f'Could not write audio file.\n') text_widget.write(f'This could be due to low storage on target device or a system permissions issue.\n') - text_widget.write(f"\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write(f"\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write(f'\nIf the error persists, please contact the developers.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') try: @@ -1084,6 +1127,28 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress button_widget.configure(state=tk.NORMAL) # Enable Button return + if demucs_model_missing_err in message: + text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') + text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') + text_widget.write(f'\nError Received:\n\n') + text_widget.write(f'The selected Demucs model is missing.\n\n') + text_widget.write(f'Please download the model or make sure it is in the correct directory.\n\n') + text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') + try: + with open('errorlog.txt', 'w') as f: + f.write(f'Last Error Received:\n\n' + + f'Error Received while processing "{os.path.basename(music_file)}":\n' + + f'Process Method: VR Architecture\n\n' + + f'The selected Demucs model is missing.\n\n' + + f'Please download the model or make sure it is in the correct directory.\n\n' + + message + f'\nError Time Stamp [{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}]\n') + except: + pass + torch.cuda.empty_cache() + progress_var.set(0) + button_widget.configure(state=tk.NORMAL) # Enable Button + return + print(traceback_text) print(type(e).__name__, e) print(message) @@ -1103,7 +1168,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') text_widget.write(f'\nError Received:\n') - text_widget.write("\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write("\Go to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write("\n" + f'Please address the error and try again.' + "\n") text_widget.write(f'If this error persists, please contact the developers with the error details.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') diff --git a/inference_v5_ensemble.py b/inference_v5_ensemble.py index a32df73..85a0f40 100644 --- a/inference_v5_ensemble.py +++ b/inference_v5_ensemble.py @@ -70,35 +70,35 @@ class Predictor(): self.noise_pro_select_set_var = tk.StringVar(value='MDX-NET_Noise_Profile_14_kHz') self.compensate_v_var = tk.StringVar(value=1.03597672895) - top= Toplevel() + mdx_model_set = Toplevel() - top.geometry("740x550") - window_height = 740 - window_width = 550 + mdx_model_set.geometry("490x515") + window_height = 490 + window_width = 515 - top.title("Specify Parameters") + mdx_model_set.title("Specify Parameters") - top.resizable(False, False) # This code helps to disable windows from resizing + mdx_model_set.resizable(False, False) # This code helps to disable windows from resizing - screen_width = top.winfo_screenwidth() - screen_height = top.winfo_screenheight() + screen_width = mdx_model_set.winfo_screenwidth() + screen_height = mdx_model_set.winfo_screenheight() x_cordinate = int((screen_width/2) - (window_width/2)) y_cordinate = int((screen_height/2) - (window_height/2)) - top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) + mdx_model_set.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) # change title bar icon - top.iconbitmap('img\\UVR-Icon-v2.ico') + mdx_model_set.iconbitmap('img\\UVR-Icon-v2.ico') - tabControl = ttk.Notebook(top) + mdx_model_set_window = ttk.Notebook(mdx_model_set) - tabControl.pack(expand = 1, fill ="both") + mdx_model_set_window.pack(expand = 1, fill ="both") - tabControl.grid_rowconfigure(0, weight=1) - tabControl.grid_columnconfigure(0, weight=1) + mdx_model_set_window.grid_rowconfigure(0, weight=1) + mdx_model_set_window.grid_columnconfigure(0, weight=1) - frame0=Frame(tabControl,highlightbackground='red',highlightthicknes=0) + frame0=Frame(mdx_model_set_window,highlightbackground='red',highlightthicknes=0) frame0.grid(row=0,column=0,padx=0,pady=0) frame0.tkraise(frame0) @@ -167,22 +167,19 @@ class Predictor(): torch.cuda.empty_cache() gui_progress_bar.set(0) widget_button.configure(state=tk.NORMAL) # Enable Button - top.destroy() + self.okVar.set(1) + stop_button() + mdx_model_set.destroy() return l0=ttk.Button(frame0,text="Stop Process", command=stop) l0.grid(row=13,column=1,padx=0,pady=30) - #print('print from top ', model_hash) + #print('print from mdx_model_set ', model_hash) #source_val = 0 - def change_event(): - self.okVar.set(1) - #top.destroy() - pass - - top.protocol("WM_DELETE_WINDOW", change_event) + mdx_model_set.protocol("WM_DELETE_WINDOW", stop) frame0.wait_variable(self.okVar) @@ -237,16 +234,16 @@ class Predictor(): with open(f"lib_v5/filelists/model_cache/mdx_model_cache/{model_hash}.json", "w") as outfile: outfile.write(mdx_model_params_r) - if stemset_n == '(Instrumental)': if not 'UVR' in demucs_model_set: - widget_text.write(base_text + 'The selected Demucs model cannot be used with this model.\n') - widget_text.write(base_text + 'Only 2 stem Demucs models are compatible with this model.\n') - widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') - demucs_model_set = 'UVR_Demucs_Model_1' + if demucs_switch == 'on': + widget_text.write(base_text + 'The selected Demucs model cannot be used with this model.\n') + widget_text.write(base_text + 'Only 2 stem Demucs models are compatible with this model.\n') + widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') + demucs_model_set = 'UVR_Demucs_Model_1' - top.destroy() + mdx_model_set.destroy() def prediction_setup(self): @@ -278,6 +275,10 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(state) widget_text.write('Done!\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Note: Segments only available for Demucs v3\n') + else: + pass elif 'tasnet-beb46fac.th' in demucs_model_set or 'tasnet_extra-df3777b2.th' in demucs_model_set or \ 'demucs48_hq-28a1282c.th' in demucs_model_set or'demucs-e07c671f.th' in demucs_model_set or \ @@ -300,6 +301,10 @@ class Predictor(): self.demucs.to(device) self.demucs.load_state_dict(torch.load("models/Demucs_Models/"f"{demucs_model_set}")) widget_text.write('Done!\n') + if not data['segment'] == 'Default': + widget_text.write(base_text + 'Note: Segments only available for Demucs v3\n') + else: + pass self.demucs.eval() else: @@ -318,6 +323,37 @@ class Predictor(): widget_text.write('Done!\n') if isinstance(self.demucs, BagOfModels): widget_text.write(base_text + f"Selected Demucs model is a bag of {len(self.demucs.models)} model(s).\n") + + if data['segment'] == 'Default': + segment = None + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment + else: + try: + segment = int(data['segment']) + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment + if split_mode: + widget_text.write(base_text + "Segments set to "f"{segment}.\n") + except: + segment = None + if isinstance(self.demucs, BagOfModels): + if segment is not None: + for sub in self.demucs.models: + sub.segment = segment + else: + if segment is not None: + sub.segment = segment self.onnx_models = {} c = 0 @@ -595,48 +631,84 @@ class Predictor(): widget_text.write(base_text + 'Completed Separation!\n\n') def demix(self, mix): - # 1 = demucs only - # 0 = onnx only + if data['chunks'] == 'Full': chunk_set = 0 - else: - chunk_set = data['chunks'] - - if data['chunks'] == 'Auto': + widget_text.write(base_text + "Chunk size user-set to \"Full\"... \n") + elif data['chunks'] == 'Auto': if data['gpu'] == 0: try: gpu_mem = round(torch.cuda.get_device_properties(0).total_memory/1.074e+9) except: widget_text.write(base_text + 'NVIDIA GPU Required for conversion!\n') + data['gpu'] = -1 + pass if int(gpu_mem) <= int(6): chunk_set = int(5) - widget_text.write(base_text + 'Chunk size auto-set to 5... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 5... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 5... \n') if gpu_mem in [7, 8, 9, 10, 11, 12, 13, 14, 15]: chunk_set = int(10) - widget_text.write(base_text + 'Chunk size auto-set to 10... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 10... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 10... \n') if int(gpu_mem) >= int(16): chunk_set = int(40) - widget_text.write(base_text + 'Chunk size auto-set to 40... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 40... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 40... \n') if data['gpu'] == -1: sys_mem = psutil.virtual_memory().total >> 30 if int(sys_mem) <= int(4): chunk_set = int(1) - widget_text.write(base_text + 'Chunk size auto-set to 1... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 1... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 1... \n') if sys_mem in [5, 6, 7, 8]: chunk_set = int(10) - widget_text.write(base_text + 'Chunk size auto-set to 10... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 10... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 10... \n') if sys_mem in [9, 10, 11, 12, 13, 14, 15, 16]: chunk_set = int(25) - widget_text.write(base_text + 'Chunk size auto-set to 25... \n') + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 25... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 25... \n') + if int(sys_mem) >= int(17): chunk_set = int(60) - widget_text.write(base_text + 'Chunk size auto-set to 60... \n') - elif data['chunks'] == 'Full': + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + 'Chunk size auto-set to 60... \n') + else: + widget_text.write(base_text + 'Chunk size auto-set to 60... \n') + elif data['chunks'] == '0': chunk_set = 0 - widget_text.write(base_text + "Chunk size set to full... \n") + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + "Chunk size user-set to \"Full\"... \n") + else: + widget_text.write(base_text + "Chunk size user-set to \"Full\"... \n") else: chunk_set = int(data['chunks']) - widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") + if demucs_only == 'on': + if no_chunk_demucs: + widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") + else: + widget_text.write(base_text + "Chunk size user-set to "f"{chunk_set}... \n") samples = mix.shape[-1] margin = margin_set @@ -673,16 +745,22 @@ class Predictor(): 'demucs.th' in demucs_model_set or \ 'demucs_extra.th' in demucs_model_set or 'light.th' in demucs_model_set or \ 'light_extra.th' in demucs_model_set or 'v1' in demucs_model_set or '.gz' in demucs_model_set: - sources = self.demix_demucs_v1(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + sources = self.demix_demucs_v1_split(mix) + if no_chunk_demucs == True: + sources = self.demix_demucs_v1(segmented_mix, margin_size=margin) elif 'tasnet-beb46fac.th' in demucs_model_set or 'tasnet_extra-df3777b2.th' in demucs_model_set or \ 'demucs48_hq-28a1282c.th' in demucs_model_set or'demucs-e07c671f.th' in demucs_model_set or \ 'demucs_extra-3646af93.th' in demucs_model_set or 'demucs_unittest-09ebc15f.th' in demucs_model_set or \ 'v2' in demucs_model_set: - sources = self.demix_demucs_v2(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + sources = self.demix_demucs_v2_split(mix) + if no_chunk_demucs == True: + sources = self.demix_demucs_v2(segmented_mix, margin_size=margin) else: - if split_mode == True: + if no_chunk_demucs == False: sources = self.demix_demucs_split(mix) - if split_mode == False: + if no_chunk_demucs == True: sources = self.demix_demucs(segmented_mix, margin_size=margin) else: # both, apply spec effects base_out = self.demix_base(segmented_mix, margin_size=margin) @@ -690,16 +768,22 @@ class Predictor(): 'demucs.th' in demucs_model_set or \ 'demucs_extra.th' in demucs_model_set or 'light.th' in demucs_model_set or \ 'light_extra.th' in demucs_model_set or 'v1' in demucs_model_set or '.gz' in demucs_model_set: - demucs_out = self.demix_demucs_v1(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + demucs_out = self.demix_demucs_v1_split(mix) + if no_chunk_demucs == True: + demucs_out = self.demix_demucs_v1(segmented_mix, margin_size=margin) elif 'tasnet-beb46fac.th' in demucs_model_set or 'tasnet_extra-df3777b2.th' in demucs_model_set or \ 'demucs48_hq-28a1282c.th' in demucs_model_set or'demucs-e07c671f.th' in demucs_model_set or \ 'demucs_extra-3646af93.th' in demucs_model_set or 'demucs_unittest-09ebc15f.th' in demucs_model_set or \ 'v2' in demucs_model_set: - demucs_out = self.demix_demucs_v2(segmented_mix, margin_size=margin) + if no_chunk_demucs == False: + demucs_out = self.demix_demucs_v2_split(mix) + if no_chunk_demucs == True: + demucs_out = self.demix_demucs_v2(segmented_mix, margin_size=margin) else: - if split_mode == True: + if no_chunk_demucs == False: demucs_out = self.demix_demucs_split(mix) - if split_mode == False: + if no_chunk_demucs == True: demucs_out = self.demix_demucs(segmented_mix, margin_size=margin) nan_count = np.count_nonzero(np.isnan(demucs_out)) + np.count_nonzero(np.isnan(base_out)) if nan_count > 0: @@ -731,17 +815,33 @@ class Predictor(): onnxitera = len(mixes) onnxitera_calc = onnxitera * 2 gui_progress_bar_onnx = 0 - widget_text.write(base_text + "Running ONNX Inference...\n") - widget_text.write(base_text + "Processing "f"{onnxitera} slices... ") + progress_bar = 0 + print(' Running ONNX Inference...') + + if onnxitera == 1: + widget_text.write(base_text + f"Running ONNX Inference... ") + else: + widget_text.write(base_text + f"Running ONNX Inference...{space}\n") + for mix in mixes: gui_progress_bar_onnx += 1 - if demucs_switch == 'on': + if data['demucsmodel']: update_progress(**progress_kwargs, step=(0.1 + (0.5/onnxitera_calc * gui_progress_bar_onnx))) else: update_progress(**progress_kwargs, - step=(0.1 + (0.9/onnxitera * gui_progress_bar_onnx))) + step=(0.1 + (0.8/onnxitera * gui_progress_bar_onnx))) + + progress_bar += 100 + step = (progress_bar / onnxitera) + + if onnxitera == 1: + pass + else: + percent_prog = f"{base_text}MDX-Net Inference Progress: {gui_progress_bar_onnx}/{onnxitera} | {round(step)}%" + widget_text.percentage(percent_prog) + cmix = mixes[mix] sources = [] n_sample = cmix.shape[1] @@ -774,28 +874,38 @@ class Predictor(): end = None sources.append(tar_signal[:,start:end]) - chunked_sources.append(sources) _sources = np.concatenate(chunked_sources, axis=-1) del self.onnx_models - widget_text.write('Done!\n') + + if onnxitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + return _sources def demix_demucs(self, mix, margin_size): - #print('shift_set ', shift_set) processed = {} demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs Inference... ") + else: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") - widget_text.write(base_text + "Split Mode is off. (Chunks enabled for Demucs Model)\n") - - widget_text.write(base_text + "Running Demucs Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") - print('Running Demucs Inference...') - + print(' Running Demucs Inference...') for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -803,7 +913,17 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model(self.demucs, cmix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] + sources = apply_model(self.demucs, cmix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=False, + **progress_demucs_kwargs)[0] sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -815,60 +935,49 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') - return sources + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + #print('the demucs model is done running') + + return sources + def demix_demucs_split(self, mix): - - #print('shift_set ', shift_set) - widget_text.write(base_text + "Split Mode is on. (Chunks disabled for Demucs Model)\n") - widget_text.write(base_text + "Running Demucs Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + + if split_mode: + widget_text.write(base_text + f"Running Demucs Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs Inference... ") print(' Running Demucs Inference...') - + mix = torch.tensor(mix, dtype=torch.float32) ref = mix.mean(0) mix = (mix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model(self.demucs, mix[None], split=split_mode, device=device, overlap=overlap_set, shifts=shift_set, progress=False)[0] + sources = apply_model(self.demucs, + mix[None], + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + device=device, + overlap=overlap_set, + shifts=shift_set, + progress=False, + segmen=True, + **progress_demucs_kwargs)[0] - widget_text.write('Done!\n') + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] - return sources - - def demix_demucs_v2(self, mix, margin_size): - processed = {} - demucsitera = len(mix) - demucsitera_calc = demucsitera * 2 - gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v2 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") - print(' Running Demucs Inference...') - for nmix in mix: - gui_progress_bar_demucs += 1 - update_progress(**progress_kwargs, - step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) - cmix = mix[nmix] - cmix = torch.tensor(cmix, dtype=torch.float32) - ref = cmix.mean(0) - cmix = (cmix - ref.mean()) / ref.std() - with torch.no_grad(): - sources = apply_model_v2(self.demucs, cmix.to(device), split=split_mode, overlap=overlap_set, shifts=shift_set) - sources = (sources * ref.std() + ref.mean()).cpu().numpy() - sources[[0,1]] = sources[[1,0]] - - start = 0 if nmix == 0 else margin_size - end = None if nmix == list(mix.keys())[::-1][0] else -margin_size - if margin_size == 0: - end = None - processed[nmix] = sources[:,:,start:end].copy() - - sources = list(processed.values()) - sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + return sources def demix_demucs_v1(self, mix, margin_size): @@ -876,11 +985,21 @@ class Predictor(): demucsitera = len(mix) demucsitera_calc = demucsitera * 2 gui_progress_bar_demucs = 0 - widget_text.write(base_text + "Running Demucs v1 Inference...\n") - widget_text.write(base_text + "Processing "f"{len(mix)} slices... ") + progress_bar = 0 print(' Running Demucs Inference...') + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") for nmix in mix: gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v1 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) update_progress(**progress_kwargs, step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) cmix = mix[nmix] @@ -888,7 +1007,15 @@ class Predictor(): ref = cmix.mean(0) cmix = (cmix - ref.mean()) / ref.std() with torch.no_grad(): - sources = apply_model_v1(self.demucs, cmix.to(device), split=split_mode, shifts=shift_set) + sources = apply_model_v1(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + shifts=shift_set, + **progress_demucs_kwargs) sources = (sources * ref.std() + ref.mean()).cpu().numpy() sources[[0,1]] = sources[[1,0]] @@ -900,22 +1027,135 @@ class Predictor(): sources = list(processed.values()) sources = np.concatenate(sources, axis=-1) - widget_text.write('Done!\n') + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + return sources + + def demix_demucs_v1_split(self, mix): -def update_progress(progress_var, total_files, file_num, step: float = 1): - """Calculate the progress for the progress widget in the GUI""" - base = (100 / total_files) - progress = base * (file_num - 1) - progress += base * step + print(' Running Demucs Inference...') + if split_mode: + widget_text.write(base_text + f"Running Demucs v1 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v1 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() - progress_var.set(progress) + with torch.no_grad(): + sources = apply_model_v1(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + shifts=shift_set, + **progress_demucs_kwargs) + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] -def get_baseText(total_files, file_num): - """Create the base text for the command widget""" - text = 'File {file_num}/{total_files} '.format(file_num=file_num, - total_files=total_files) - return text + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + + return sources + + def demix_demucs_v2(self, mix, margin_size): + processed = {} + demucsitera = len(mix) + demucsitera_calc = demucsitera * 2 + gui_progress_bar_demucs = 0 + progress_bar = 0 + if demucsitera == 1: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + + for nmix in mix: + gui_progress_bar_demucs += 1 + progress_bar += 100 + step = (progress_bar / demucsitera) + if demucsitera == 1: + pass + else: + percent_prog = f"{base_text}Demucs v2 Inference Progress: {gui_progress_bar_demucs}/{demucsitera} | {round(step)}%" + widget_text.percentage(percent_prog) + + update_progress(**progress_kwargs, + step=(0.35 + (1.05/demucsitera_calc * gui_progress_bar_demucs))) + cmix = mix[nmix] + cmix = torch.tensor(cmix, dtype=torch.float32) + ref = cmix.mean(0) + cmix = (cmix - ref.mean()) / ref.std() + with torch.no_grad(): + sources = apply_model_v2(self.demucs, + cmix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=False, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + start = 0 if nmix == 0 else margin_size + end = None if nmix == list(mix.keys())[::-1][0] else -margin_size + if margin_size == 0: + end = None + processed[nmix] = sources[:,:,start:end].copy() + + sources = list(processed.values()) + sources = np.concatenate(sources, axis=-1) + + if demucsitera == 1: + widget_text.write('Done!\n') + else: + widget_text.write('\n') + + return sources + + def demix_demucs_v2_split(self, mix): + print(' Running Demucs Inference...') + + if split_mode: + widget_text.write(base_text + f"Running Demucs v2 Inference...{space}\n") + else: + widget_text.write(base_text + f"Running Demucs v2 Inference... ") + + mix = torch.tensor(mix, dtype=torch.float32) + ref = mix.mean(0) + mix = (mix - ref.mean()) / ref.std() + with torch.no_grad(): + sources = apply_model_v2(self.demucs, + mix.to(device), + gui_progress_bar, + widget_text, + update_prog, + split=split_mode, + segmen=True, + overlap=overlap_set, + shifts=shift_set, + **progress_demucs_kwargs) + + sources = (sources * ref.std() + ref.mean()).cpu().numpy() + sources[[0,1]] = sources[[1,0]] + + if split_mode: + widget_text.write('\n') + else: + widget_text.write('Done!\n') + + return sources warnings.filterwarnings("ignore") cpu = torch.device('cpu') @@ -938,22 +1178,6 @@ class VocalRemover(object): self.models = defaultdict(lambda: None) self.devices = defaultdict(lambda: None) # self.offset = model.offset - - - -def update_progress(progress_var, total_files, file_num, step: float = 1): - """Calculate the progress for the progress widget in the GUI""" - base = (100 / total_files) - progress = base * (file_num - 1) - progress += base * step - - progress_var.set(progress) - -def get_baseText(total_files, file_num): - """Create the base text for the command widget""" - text = 'File {file_num}/{total_files} '.format(file_num=file_num, - total_files=total_files) - return text def determineModelFolderName(): """ @@ -1008,6 +1232,7 @@ data = { 'mdx_only_ensem_e': 'No Model', 'mixing': 'Default', 'mp3bit': '320k', + 'no_chunk': False, 'noise_pro_select': 'Auto Select', 'noisereduc_s': 3, 'non_red': False, @@ -1016,6 +1241,7 @@ data = { 'overlap': 0.5, 'postprocess': True, 'saveFormat': 'wav', + 'segment': 'Default', 'shifts': 0, 'split_mode': False, 'tta': True, @@ -1051,8 +1277,10 @@ default_noisereduc_s = data['noisereduc_s'] def update_progress(progress_var, total_files, file_num, step: float = 1): """Calculate the progress for the progress widget in the GUI""" - base = (100 / total_files) - progress = base * (file_num - 1) + + total_count = model_count * total_files + base = (100 / total_count) + progress = base * current_model_bar - base progress += base * step progress_var.set(progress) @@ -1061,9 +1289,14 @@ def get_baseText(total_files, file_num): """Create the base text for the command widget""" text = 'File {file_num}/{total_files} '.format(file_num=file_num, total_files=total_files) + return text -def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress_var: tk.Variable, +def main(window: tk.Wm, + text_widget: tk.Text, + button_widget: tk.Button, + progress_var: tk.Variable, + stop_thread, **kwargs: dict): global widget_text @@ -1071,6 +1304,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global music_file global default_chunks global default_noisereduc_s + global gui_progress_bar global base_name global progress_kwargs global base_text @@ -1081,6 +1315,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global compensate global autocompensate global demucs_model_set + global progress_demucs_kwargs global channel_set global margin_set global overlap_set @@ -1091,14 +1326,17 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress global split_mode global demucs_switch global demucs_only + global no_chunk_demucs global wav_type_set global flac_type_set global mp3_bit_set global model_hash + global space global stime global stemset_n global source_val global widget_button + global stop_button wav_type_set = data['wavtype'] @@ -1107,9 +1345,11 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress default_noisereduc_s = data['noisereduc_s'] autocompensate = data['autocompensate'] + stop_button = stop_thread widget_text = text_widget gui_progress_bar = progress_var widget_button = button_widget + space = ' '*90 #Error Handling @@ -1202,6 +1442,12 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress normalization_set(wav_vocals), mp.param['sr'], subtype=wav_type_set) data.update(kwargs) + + global update_prog + + update_prog = update_progress + no_chunk_demucs = data['no_chunk'] + space = ' '*90 if data['DemucsModel_MDX'] == "Tasnet v1": demucs_model_set_name = 'tasnet.th' @@ -1694,7 +1940,11 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress vr_ensem_e_name = data['vr_ensem_e'] vr_ensem_e = f'models/Main_Models/{vr_ensem_e_name}.pth' vr_param_ens_e = data['vr_basic_USER_model_param_5'] - + + basic_vr_ensemble_list = [vr_ensem_a_name, vr_ensem_b_name, vr_ensem_c_name, vr_ensem_d_name, vr_ensem_e_name] + no_models = basic_vr_ensemble_list.count('No Model') + vr_ensem_count = 5 - no_models + if data['vr_ensem_c'] == 'No Model' and data['vr_ensem_d'] == 'No Model' and data['vr_ensem_e'] == 'No Model': Basic_Ensem = [ { @@ -1970,6 +2220,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_ensem = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_ensem = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_ensem = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_ensem = 'UVR_MDXNET_Inst_2' else: mdx_ensem = mdx_net_model_name @@ -2008,6 +2262,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_ensem_b = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_ensem_b = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_ensem_b = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_ensem_b = 'UVR_MDXNET_Inst_2' else: mdx_ensem_b = mdx_net_model_name @@ -2020,6 +2278,9 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_ensem_b = 'pass' mdx_model_run_mul_b = 'pass' + multi_ai_ensemble_list = [vr_ensem_name, vr_ensem_mdx_a_name, vr_ensem_mdx_b_name, vr_ensem_mdx_c_name, data['mdx_ensem'], data['mdx_ensem_b']] + no_multi_models = multi_ai_ensemble_list.count('No Model') + multi_ensem_count = 6 - no_multi_models if data['vr_ensem'] == 'No Model' and data['vr_ensem_mdx_a'] == 'No Model' and data['vr_ensem_mdx_b'] == 'No Model' and data['vr_ensem_mdx_c'] == 'No Model': mdx_vr = [ @@ -2287,6 +2548,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_only_ensem_a = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_only_ensem_a = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_only_ensem_a = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_only_ensem_a = 'UVR_MDXNET_Inst_2' else: mdx_only_ensem_a = mdx_net_model_name @@ -2325,6 +2590,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_only_ensem_b = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_only_ensem_b = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_only_ensem_b = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_only_ensem_b = 'UVR_MDXNET_Inst_2' else: mdx_only_ensem_b = mdx_net_model_name @@ -2363,6 +2632,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_only_ensem_c = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_only_ensem_c = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_only_ensem_c = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_only_ensem_c = 'UVR_MDXNET_Inst_2' else: mdx_only_ensem_c = mdx_net_model_name @@ -2401,6 +2674,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_only_ensem_d = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_only_ensem_d = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_only_ensem_d = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_only_ensem_d = 'UVR_MDXNET_Inst_2' else: mdx_only_ensem_d = mdx_net_model_name @@ -2439,6 +2716,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_only_ensem_e = 'UVR_MDXNET_KARA' elif mdx_net_model_name == 'UVR-MDX-NET Main': mdx_only_ensem_e = 'UVR_MDXNET_Main' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 1': + mdx_only_ensem_e = 'UVR_MDXNET_Inst_1' + elif mdx_net_model_name == 'UVR-MDX-NET Inst 2': + mdx_only_ensem_e = 'UVR_MDXNET_Inst_2' else: mdx_only_ensem_e = mdx_net_model_name @@ -2701,31 +2982,46 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress } ] + basic_md_ensemble_list = [data['mdx_only_ensem_a'], data['mdx_only_ensem_b'], data['mdx_only_ensem_c'], data['mdx_only_ensem_d'], data['mdx_only_ensem_e']] + no_basic_md_models = basic_md_ensemble_list.count('No Model') + basic_md_ensem_count = 5 - no_basic_md_models + + global model_count + if data['ensChoose'] == 'Multi-AI Ensemble': loops = mdx_vr ensefolder = 'Multi_AI_Ensemble_Outputs' ensemode = 'Multi_AI_Ensemble' + model_count = multi_ensem_count if data['ensChoose'] == 'Basic VR Ensemble': loops = Basic_Ensem ensefolder = 'Basic_VR_Outputs' ensemode = 'Multi_VR_Ensemble' + model_count = vr_ensem_count if data['ensChoose'] == 'Basic MD Ensemble': loops = mdx_demuc_only ensefolder = 'Basic_MDX_Net_Demucs_Ensemble' ensemode = 'Basic_MDX_Net_Demucs_Ensemble' + model_count = basic_md_ensem_count + + global current_model_bar + + current_model_bar = 0 #Prepare Audiofile(s) for file_num, music_file in enumerate(data['input_paths'], start=1): # -Get text and update progress- + + current_model = 1 + + base_text = get_baseText(total_files=len(data['input_paths']), file_num=file_num) progress_kwargs = {'progress_var': progress_var, 'total_files': len(data['input_paths']), - 'file_num': file_num} - update_progress(**progress_kwargs, - step=0) + 'file_num': file_num} try: @@ -2757,7 +3053,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write('Detected Free Space: ' + str(free_space) + ' GB' + '\n\n') except: pass - + #Prepare to loop models for i, c in tqdm(enumerate(loops), disable=True, desc='Iterations..'): @@ -2833,10 +3129,16 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress if c['model_location'] == 'pass': pass else: + model_name = c['model_name'] + text_widget.write(f'Ensemble Mode - {model_name} - Model {current_model}/{model_count}\n\n') + current_model += 1 + current_model_bar += 1 + update_progress(**progress_kwargs, + step=0) presentmodel = Path(c['model_location']) if presentmodel.is_file(): - print(f'The file {presentmodel} exists') + pass else: if data['ensChoose'] == 'Multi-AI Ensemble': text_widget.write(base_text + 'Model "' + c['model_name'] + '.pth" is missing.\n') @@ -2851,14 +3153,11 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(base_text + 'Model "' + c['model_name'] + '.pth" is missing.\n') text_widget.write(base_text + 'Installation of v5 Model Expansion Pack required to use this model.\n\n') continue - - text_widget.write(c['loop_name'] + '\n\n') - text_widget.write(base_text + 'Loading ' + c['model_name_c'] + '... ') + text_widget.write(base_text + 'Loading VR model... ') aggresive_set = float(data['agg']/100) - model_size = math.ceil(os.stat(c['model_location']).st_size / 1024) nn_architecture = '{}KB'.format(min(nn_arch_sizes, key=lambda x:abs(x-model_size))) @@ -2917,9 +3216,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress ModelName_1=(c['model_name']) - #print('model param function output ', model_params) - - print('Model Parameters:', model_params[0]) + #print('Model Parameters:', model_params[0]) text_widget.write(base_text + 'Loading assigned model parameters ' + '\"' + model_params[1] + '\"... ') mp = ModelParameters(model_params[0]) @@ -2974,7 +3271,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(base_text + 'Loading the stft of audio source... ') text_widget.write('Done!\n') - text_widget.write(base_text + "Please Wait...\n") X_spec_m = spec_utils.combine_spectrograms(X_spec_s, mp) @@ -2982,22 +3278,47 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress def inference(X_spec, device, model, aggressiveness): - def _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness): + def _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness, tta=False): model.eval() + global active_iterations + global progress_value + with torch.no_grad(): preds = [] iterations = [n_window] - total_iterations = sum(iterations) - - text_widget.write(base_text + "Processing "f"{total_iterations} Slices... ") + if data['tta']: + total_iterations = sum(iterations) + total_iterations = total_iterations*2 + else: + total_iterations = sum(iterations) + + if tta: + active_iterations = sum(iterations) + active_iterations = active_iterations - 2 + total_iterations = total_iterations - 2 + else: + active_iterations = 0 - for i in tqdm(range(n_window)): - update_progress(**progress_kwargs, - step=(0.1 + (0.8/n_window * i))) + progress_bar = 0 + for i in range(n_window): + active_iterations += 1 + if data['demucsmodelVR']: + update_progress(**progress_kwargs, + step=(0.1 + (0.5/total_iterations * active_iterations))) + else: + update_progress(**progress_kwargs, + step=(0.1 + (0.8/total_iterations * active_iterations))) start = i * roi_size + progress_bar += 100 + progress_value = progress_bar + active_iterations_step = active_iterations*100 + step = (active_iterations_step / total_iterations) + + percent_prog = f"{base_text}Inference Progress: {active_iterations}/{total_iterations} | {round(step)}%" + text_widget.percentage(percent_prog) X_mag_window = X_mag_pad[None, :, :, start:start + data['window_size']] X_mag_window = torch.from_numpy(X_mag_window).to(device) @@ -3007,8 +3328,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress preds.append(pred[0]) pred = np.concatenate(preds, axis=2) - - text_widget.write('Done!\n') return pred def preprocess(X_spec): @@ -3043,29 +3362,28 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress X_mag_pre, ((0, 0), (0, 0), (pad_l, pad_r)), mode='constant') pred_tta = _execute(X_mag_pad, roi_size, n_window, - device, model, aggressiveness) + device, model, aggressiveness, tta=True) pred_tta = pred_tta[:, :, roi_size // 2:] pred_tta = pred_tta[:, :, :n_frame] return (pred + pred_tta) * 0.5 * coef, X_mag, np.exp(1.j * X_phase) else: return pred * coef, X_mag, np.exp(1.j * X_phase) - + aggressiveness = {'value': aggresive_set, 'split_bin': mp.param['band'][1]['crop_stop']} if data['tta']: - text_widget.write(base_text + "Running Inferences (TTA)... \n") + text_widget.write(base_text + f"Running Inferences (TTA)... {space}\n") else: - text_widget.write(base_text + "Running Inference... \n") + text_widget.write(base_text + f"Running Inference... {space}\n") pred, X_mag, X_phase = inference(X_spec_m, device, model, aggressiveness) + + text_widget.write('\n') - # update_progress(**progress_kwargs, - # step=0.8) - - # Postprocess + if data['postprocess']: try: text_widget.write(base_text + 'Post processing...') @@ -3154,6 +3472,8 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress ################################### if data['ensChoose'] == 'Multi-AI Ensemble' or data['ensChoose'] == 'Basic MD Ensemble': + + if data['demucsmodel']: demucs_switch = 'on' else: @@ -3194,11 +3514,11 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress mdx_name = c['mdx_model_name'] - if c['mdx_model_name'] == 'pass': pass else: - text_widget.write('Ensemble Mode - Running Model - ' + post_mdx_name + '\n\n') + text_widget.write(f'Ensemble Mode - {post_mdx_name} - Model {current_model}/{model_count}\n\n') + #text_widget.write('Ensemble Mode - Running Model - ' + post_mdx_name + '\n\n') if c['mdx_model_run'] == 'no': if 'UVR' in mdx_name: @@ -3244,15 +3564,20 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress #print(model_params_mdx) - print('demucs_only? ', demucs_only) + #print('demucs_only? ', demucs_only) + + if demucs_only == 'on': + inference_type = 'demucs_only' + else: + inference_type = 'inference_mdx' + progress_demucs_kwargs = {'total_files': len(data['input_paths']), + 'file_num': file_num, 'inference_type': inference_type} + if data['noise_pro_select'] == 'Auto Select': noise_pro_set = noise_pro else: noise_pro_set = data['noise_pro_select'] - - update_progress(**progress_kwargs, - step=0) if data['noisereduc_s'] == 'None': pass @@ -3266,6 +3591,12 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress e = os.path.join(data["export_path"]) + current_model += 1 + current_model_bar += 1 + + update_progress(**progress_kwargs, + step=0) + pred = Predictor() if c['mdx_model_run'] == 'yes': @@ -3273,6 +3604,13 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress widget_text.write(base_text + 'Only vocal and instrumental MDX-Net models are supported in \nensemble mode.\n') widget_text.write(base_text + 'Moving on to next model...\n\n') continue + if stemset_n == '(Instrumental)': + if not 'UVR' in demucs_model_set: + if data['demucsmodel']: + widget_text.write(base_text + 'The selected Demucs model cannot be used with this model.\n') + widget_text.write(base_text + 'Only 2 stem Demucs models are compatible with this model.\n') + widget_text.write(base_text + 'Setting Demucs model to \"UVR_Demucs_Model_1\".\n\n') + demucs_model_set = 'UVR_Demucs_Model_1' if modeltype == 'Not Set' or \ noise_pro == 'Not Set' or \ stemset_n == 'Not Set' or \ @@ -4203,7 +4541,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write(f'\nError Received:\n\n') text_widget.write(f'Could not write audio file.\n') text_widget.write(f'This could be due to low storage on target device or a system permissions issue.\n') - text_widget.write(f"\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write(f"\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write(f'\nIf the error persists, please contact the developers.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') try: @@ -4313,7 +4651,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress text_widget.write("\n" + base_text + f'Separation failed for the following audio file:\n') text_widget.write(base_text + f'"{os.path.basename(music_file)}"\n') text_widget.write(f'\nError Received:\n') - text_widget.write("\nFor raw error details, go to the Error Log tab in the Help Guide.\n") + text_widget.write("\nGo to the Settings Menu and click \"Open Error Log\" for raw error details.\n") text_widget.write("\n" + f'Please address the error and try again.' + "\n") text_widget.write(f'If this error persists, please contact the developers with the error details.\n\n') text_widget.write(f'Time Elapsed: {time.strftime("%H:%M:%S", time.gmtime(int(time.perf_counter() - stime)))}') @@ -4324,7 +4662,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress update_progress(**progress_kwargs, step=1) - print('Done!') + #print('Done!') progress_var.set(0) if not data['ensChoose'] == 'Manual Ensemble':