mvsepless_plugins / ensembless_v2.py
noblebarkrr's picture
Обновление плагина для создания ансамблей Ensembless.
1b94eeb verified
Raw
History Blame Contribute Delete
39.4 kB
import gradio as gr
import json
import pandas as pd
import tempfile
import os
from separator.ensemble import ensemble_audio_files
from pydub.utils import mediainfo
from pydub import AudioSegment
import numpy as np
import librosa
import librosa.display
import soundfile as sf
from separator.audio_writer import write_audio_file
from multi_inference import MVSEPLESS
from pydub.exceptions import CouldntDecodeError
mvsepless = MVSEPLESS()
TRANSLATIONS = {
"ru": {
"app_title": "EnsembLess",
"auto_ensemble": "Авто-ансамбль",
"invert_ensemble": "Инвертировать ансамбль",
"give_name_preset": "Дайте имя пресету",
"export": "Экспорт",
"import": "Импорт",
"manual_ensemble": "Ручной ансамбль",
"inverter": "Инвертер",
"model_selection": "Выберите модель для добавления в ансамбль",
"model_type": "Тип модели",
"model_name": "Имя модели",
"stem_selection": "Стем, который будет использован в ансамбле",
"weight": "Весы",
"invert_weights": "Использовать перевернутые весы для инвертированного стема",
"add_button": "➕ Добавить",
"current_ensemble": "Текущий ансамбль",
"remove_index": "Индекс модели, который хотите удалить (начинается с 1)",
"remove_button": "❌ Удалить",
"clear_button": "Очистить",
"input_audio": "Входное аудио",
"settings": "Настройки",
"method": "Метод",
"output_format": "Формат вывода",
"run_button": "Создать ансамбль",
"results": "Результаты",
"inverted_result": "Инвертированный результат",
"invert_method": "Метод инвертирования",
"invert_button": "Инвертировать",
"audio_files": "Аудио файлы",
"weights_input": "Весы",
"main_audio": "Основное аудио",
"audio_to_remove": "Аудио для удаления",
"processing_method": "Метод обработки",
"analyze_title": "РЕЗУЛЬТАТЫ АНАЛИЗА:",
"all_same_rate": "✅ ВСЕ ФАЙЛЫ имеют одинаковую частоту дискретизации: {rate} Hz",
"different_rates": "⚠️ Файлы имеют РАЗНУЮ частоту дискретизации",
"resample_warning": "К загруженному аудио автоматически применён ресэмплинг для лучшего инвертирования",
"error_no_files": "Ошибка: файлы не загружены",
"error_unsupported_format": "не поддерживаемый формат",
"error_general": "ошибка ({error})",
"error_no_models": "Добавьте хотя бы одну модель для создания ансамбля",
"error_no_audio": "Сначала загрузите аудио",
"error_both_audio": "Пожалуйста, загрузите оба аудиофайла",
"language": "Язык",
"batch_processing": "Пакетная обработка",
"batch_info": "Позволяет загрузить сразу несколько файлов",
"separation_info": "Информация о разделении",
"vocal_separation": "Разделение вокалы",
"stereo_mode": "Стерео режим",
"stem": "Стем",
"p_stem": "Основной стем",
"s_stem": "Инвертированный стем",
"vocal_multi_separation": "Мульти-вокал",
"ensemble": "Ансамбль",
"transform": "Преобразование",
"algorithm": "Алгоритм: {model_fullname}",
"output_format_info": "Формат выходных данных: {output_format}",
"process1": "Начало обработки",
"process2": "Модель",
"process3": "Автоматическое выравнивание длин аудио",
"process4": "Создание ансамбля",
"result_source": "Промежуточные файлы",
"local_path": "Указать путь к аудио локально",
"resample": "Ресэмпл"
},
"en": {
"app_title": "EnsembLess",
"auto_ensemble": "Auto-Ensemble",
"invert_ensemble": "Invert ensemble",
"give_name_preset": "Give name of preset",
"export": "Export",
"import": "Import",
"manual_ensemble": "Manual Ensemble",
"inverter": "Inverter",
"model_selection": "Select a model to add to the ensemble",
"model_type": "Model Type",
"model_name": "Model Name",
"stem_selection": "Stem to use in the ensemble",
"weight": "Weights",
"invert_weights": "Use inverted weights for inverted stem",
"add_button": "➕ Add",
"current_ensemble": "Current Ensemble",
"remove_index": "Index of model to remove (starts from 1)",
"remove_button": "❌ Remove",
"clear_button": "Clear",
"input_audio": "Input Audio",
"settings": "Settings",
"method": "Method",
"output_format": "Output Format",
"run_button": "Create Ensemble",
"results": "Results",
"inverted_result": "Inverted Result",
"invert_method": "Inversion Method",
"invert_button": "Invert",
"audio_files": "Audio Files",
"weights_input": "Weights",
"main_audio": "Main Audio",
"audio_to_remove": "Audio to Remove",
"processing_method": "Processing Method",
"analyze_title": "ANALYSIS RESULTS:",
"all_same_rate": "✅ ALL FILES have the same sample rate: {rate} Hz",
"different_rates": "⚠️ Files have DIFFERENT sample rates",
"resample_warning": "Resampling applied automatically for better inversion",
"error_no_files": "Error: no files uploaded",
"error_unsupported_format": "unsupported format",
"error_general": "error ({error})",
"error_no_models": "Add at least one model to create an ensemble",
"error_no_audio": "Please upload audio first",
"error_both_audio": "Please upload both audio files",
"language": "Language",
"batch_processing": "Batch Processing",
"batch_info": "Allows uploading multiple files at once",
"separation_info": "Separation Info",
"vocal_separation": "Vocal Separation",
"stereo_mode": "Stereo Mode",
"stem": "Stem",
"p_stem": "Primary stem",
"s_stem": "Secondary stem",
"vocal_multi_separation": "Multi-Vocal",
"ensemble": "Ensemble",
"transform": "Transform",
"algorithm": "Algorithm: {model_fullname}",
"output_format_info": "Output format: {output_format}",
"process1": "Start process",
"process2": "Model",
"process3": "Auto post-padding audios",
"process4": "Build ensemble",
"result_source": "Intermediate files",
"local_path": "Specify path to audio locally",
"resample": "Resample"
}
}
INVERT_METHODS = {
"min_fft": "max_fft",
"max_fft": "min_fft",
"min_wave": "max_wave",
"max_wave": "min_wave",
"median_fft": "median_fft",
"median_wave": "median_wave",
"avg_fft": "avg_fft",
"avg_wave": "avg_wave"
}
# Глобальная переменная для текущего языка
CURRENT_LANG = "ru"
def set_language(lang):
global CURRENT_LANG
CURRENT_LANG = lang
def t(key, **kwargs):
"""Функция для получения перевода с подстановкой значений"""
translation = TRANSLATIONS[CURRENT_LANG].get(key, key)
return translation.format(**kwargs) if kwargs else translation
# Фиксированные параметры для STFT
N_FFT = 2048
WIN_LENGTH = 2048
HOP_LENGTH = WIN_LENGTH // 4
class Inverter:
def __init__(self):
self.test = "test"
def load_audio(self, filepath):
"""Загрузка аудиофайла с помощью librosa"""
if filepath is None:
return None, None
try:
return librosa.load(filepath, sr=None, mono=False)
except Exception as e:
print(f"Ошибка загрузки аудио: {e}")
return None, None
def process_channel(self, y1_ch, y2_ch, sr, method):
"""Обработка одного аудиоканала"""
if method == "waveform":
return y1_ch - y2_ch
elif method == "spectrogram":
# Вычисляем спектрограммы
S1 = librosa.stft(y1_ch, n_fft=N_FFT, hop_length=HOP_LENGTH, win_length=WIN_LENGTH)
S2 = librosa.stft(y2_ch, n_fft=N_FFT, hop_length=HOP_LENGTH, win_length=WIN_LENGTH)
# Амплитудные спектрограммы
mag1 = np.abs(S1)
mag2 = np.abs(S2)
# Спектральное вычитание
mag_result = np.maximum(mag1 - mag2, 0)
# Сохраняем фазовую информацию исходного сигнала
phase = np.angle(S1)
# Комбинируем амплитуду результата с фазой
S_result = mag_result * np.exp(1j * phase)
# Обратное преобразование
return librosa.istft(
S_result,
n_fft=N_FFT,
hop_length=HOP_LENGTH,
win_length=WIN_LENGTH,
length=len(y1_ch)
)
def process_audio(self, audio1_path, audio2_path, out_format, method):
# Загрузка аудиофайлов
y1, sr1 = self.load_audio(audio1_path)
y2, sr2 = self.load_audio(audio2_path)
if sr1 is None or sr2 is None:
raise gr.Error(t("error_both_audio"))
# Определяем количество каналов
channels1 = 1 if y1.ndim == 1 else y1.shape[0]
channels2 = 1 if y2.ndim == 1 else y2.shape[0]
# Преобразование в форму (samples, channels)
if channels1 > 1:
y1 = y1.T # (channels, samples) -> (samples, channels)
else:
y1 = y1.reshape(-1, 1)
if channels2 > 1:
y2 = y2.T # (channels, samples) -> (samples, channels)
else:
y2 = y2.reshape(-1, 1)
# Ресемплинг до одинаковой частоты дискретизации
if sr1 != sr2:
if channels2 > 1:
# Ресемплинг для каждого канала отдельно
y2_resampled = np.zeros((len(y2), channels2), dtype=np.float32)
for c in range(channels2):
y2_resampled[:, c] = librosa.resample(
y2[:, c],
orig_sr=sr2,
target_sr=sr1
)
y2 = y2_resampled
else:
y2 = librosa.resample(y2[:, 0], orig_sr=sr2, target_sr=sr1)
y2 = y2.reshape(-1, 1)
sr2 = sr1
# Приводим к одинаковой длине
min_len = min(len(y1), len(y2))
y1 = y1[:min_len]
y2 = y2[:min_len]
# Обрабатываем каждый канал отдельно
result_channels = []
# Если основной сигнал моно, а удаляемый стерео - преобразуем удаляемый в моно
if channels1 == 1 and channels2 > 1:
y2 = y2.mean(axis=1, keepdims=True)
channels2 = 1
for c in range(channels1):
# Выбираем канал для основного сигнала
y1_ch = y1[:, c]
# Выбираем канал для удаляемого сигнала
if channels2 == 1:
y2_ch = y2[:, 0]
else:
# Если каналов удаляемого сигнала больше, используем соответствующий канал
y2_ch = y2[:, min(c, channels2-1)]
# Обрабатываем канал
result_ch = self.process_channel(y1_ch, y2_ch, sr1, method)
result_channels.append(result_ch)
# Собираем каналы в один массив
if len(result_channels) > 1:
result = np.column_stack(result_channels)
else:
result = np.array(result_channels[0])
# Нормализация (предотвращение клиппинга)
if result.ndim > 1:
# Для многоканального аудио нормализуем каждый канал отдельно
for c in range(result.shape[1]):
channel = result[:, c]
max_val = np.max(np.abs(channel))
if max_val > 0:
result[:, c] = channel * 0.9 / max_val
else:
max_val = np.max(np.abs(result))
if max_val > 0:
result = result * 0.9 / max_val
folder_path = os.path.dirname(audio2_path)
inverted_wav = os.path.join(folder_path, "inverted.wav")
sf.write(inverted_wav, result, sr1)
inverted = os.path.join(folder_path, f"inverted_ensemble.{out_format}")
write_audio_file(inverted, result.T, sr1, out_format, "320k")
return inverted, inverted_wav
class EnsembLess:
def __init__(self):
self.test = "test"
def get_model_types(self):
return mvsepless.get_mt()
def get_models_by_type(self, model_type):
return mvsepless.get_mn(model_type)
def get_stems_by_model(self, model_type, model_name):
stems = mvsepless.get_stems(model_type, model_name)
if set(stems) == {"bass", "drums", "vocals", "other"} or set(stems) == {"bass", "drums", "vocals", "other", "piano", "guitar"} and not mvsepless.get_tgt_inst(model_type, model_name):
stems.append("instrumental +")
stems.append("instrumental -")
return stems
def get_invert_stems_by_model(self, model_type, model_name, primary_stem):
invert_stems = []
stems = mvsepless.get_stems(model_type, model_name)
for stem in stems:
if stem != primary_stem:
invert_stems.append(stem)
if not mvsepless.get_tgt_inst(model_type, model_name) and model_type not in ["vr", "mdx"]:
invert_stems.append("inverted +")
invert_stems.append("inverted -")
return invert_stems
def invert_weights(self, weights):
total_weight = sum(weights)
return [total_weight - w for w in weights]
def analyze_sample_rate(self, files):
"""
Анализирует частоту дискретизации для списка аудиофайлов
Возвращает форматированную строку с результатами
"""
if not files:
return t("error_no_files")
results = []
common_rate = None
all_same = True
for file_info in files:
try:
# Создаем аудиосегмент из файла
audio = AudioSegment.from_file(file_info.name)
rate = audio.frame_rate
# Проверяем единообразие частоты
if common_rate is None:
common_rate = rate
elif common_rate != rate:
all_same = False
results.append(f"{file_info.name.split('/')[-1]}: {rate} Hz")
except CouldntDecodeError:
results.append(f"{file_info.name.split('/')[-1]}: {t('error_unsupported_format')}")
except Exception as e:
results.append(f"{file_info.name.split('/')[-1]}: {t('error_general', error=str(e))}")
# Форматируем итоговый результат
header = t("analyze_title") + "\n" + "-" * 50 + "\n"
body = "\n".join(results)
footer = "\n" + "-" * 50 + "\n"
if all_same and common_rate is not None:
footer += f"\n{t('all_same_rate', rate=common_rate)}"
elif common_rate is not None:
footer += f"\n{t('different_rates')}"
return header + body + footer
def resample_audio(self, audio_path):
if not audio_path or not os.path.isfile(audio_path):
gr.Warning(t("error_no_audio"))
return None
original_name = os.path.splitext(os.path.basename(audio_path))[0]
folder_path = os.path.dirname(audio_path)
resampled_path = os.path.join(folder_path, f"resampled_{original_name}.wav")
target_sr = 44100
# Загрузка аудио через librosa с сохранением оригинальной структуры каналов
y, orig_sr = librosa.load(audio_path, sr=None, mono=False)
# Определение типа аудио (моно/стерео)
if y.ndim == 1:
channels = 1
y = y.reshape(-1, 1)
else:
channels = y.shape[0]
y = y.T
# Ресемплинг только если необходима смена частоты
if orig_sr != target_sr:
resampled_channels = []
for channel in range(channels):
channel_data = y[:, channel]
resampled = librosa.resample(
y=channel_data,
orig_sr=orig_sr,
target_sr=target_sr,
res_type="kaiser_best" # Высококачественный метод
)
resampled_channels.append(resampled)
# Синхронизация длины каналов
min_length = min(len(c) for c in resampled_channels)
resampled_data = np.vstack([c[:min_length] for c in resampled_channels]).T
else:
resampled_data = y
# Сохранение результата в формате WAV (16-bit PCM)
sf.write(
resampled_path,
resampled_data,
target_sr,
subtype="PCM_16"
)
gr.Warning(message=t("resample_warning"))
return resampled_path
def maximize_length_audio(self, output):
padded_files = []
audio_data = []
max_length = 0
for file in output:
data, sr = librosa.load(file, sr=None, mono=False)
if data.ndim == 1:
data = np.stack([data, data])
elif data.shape[0] != 2:
data = data.T
audio_data.append([file, data])
max_length = max(max_length, data.shape[1])
for i, [file, data] in enumerate(audio_data):
if data.shape[1] < max_length:
pad_width = ((0, 0), (0, max_length - data.shape[1]))
padded_data = np.pad(data, pad_width, mode='constant')
else:
padded_data = data
sf.write(file, padded_data.T, sr)
padded_files.append(file)
return padded_files
def maximize_length_audio_wav(self, output):
padded_files = []
audio_data = []
max_length = 0
for file in output:
data, sr = sf.read(file)
if data.ndim == 1:
data = np.stack([data, data])
elif data.shape[0] != 2:
data = data.T
audio_data.append([file, data])
max_length = max(max_length, data.shape[1])
for i, [file, data] in enumerate(audio_data):
if data.shape[1] < max_length:
pad_width = ((0, 0), (0, max_length - data.shape[1]))
padded_data = np.pad(data, pad_width, mode='constant')
else:
padded_data = data
sf.write(file, padded_data.T, sr)
padded_files.append(file)
return padded_files
def manual_ensemble(self, input_audios, method, weights, out_format):
temp_dir = tempfile.mkdtemp()
weights = [float(x) for x in weights.split(",")]
# padded_files = self.maximize_length_audio(input_audios)
a1, a2 = ensemble_audio_files(input_audios, output=os.path.join(temp_dir, f"ensemble_{method}"), ensemble_type=method, weights=weights, out_format=out_format)
return a1, a2
def auto_ensemble(self, input_audio, input_settings, type, out_format, invert_weights, invert_ensemble):
progress = gr.Progress()
progress(0, desc=f"{t('process1')}...")
base_name = os.path.splitext(os.path.basename(input_audio))[0]
temp_dir = tempfile.mkdtemp()
source_files = []
output_p_files = []
output_s_files = []
output_p_weights = []
block_count = len(input_settings)
for i, (input_model, weight, p_stem, s_stem) in enumerate(input_settings):
output_s_files.append(None)
progress(i / block_count, desc=f"{t('process2')} {i+1}/{block_count}")
model_type, model_name = input_model.split(" / ")
output_dir_p = os.path.join(temp_dir, f"{model_type}_{model_name}_p_stems")
output_p = mvsepless.separator(input_file=input_audio, output_dir=output_dir_p, model_type=model_type, model_name=model_name, ext_inst=True, vr_aggr=10, output_format="wav", template="MODEL_STEM", call_method="cli")
for stem, file in output_p:
source_files.append(file)
if stem == p_stem:
output_p_files.append(file)
output_p_weights.append(weight)
elif invert_ensemble:
if stem == s_stem:
output_s_files[i] = file
if invert_ensemble:
if not output_s_files[i]:
output_dir_s = os.path.join(temp_dir, f"{model_type}_{model_name}_s_stems")
output_s = mvsepless.separator(input_file=input_audio, output_dir=output_dir_s, model_type=model_type, model_name=model_name, ext_inst=True, vr_aggr=10, output_format="wav", template="MODEL_STEM", call_method="cli", selected_stems=[p_stem if not mvsepless.get_tgt_inst(model_type, model_name) else "both"])
for stem, file in output_s:
source_files.append(file)
if stem == s_stem:
output_s_files[i] = file
source_files.append(file)
progress(0.9, desc=f"{t('process3')}...")
# output_p_files = self.maximize_length_audio_wav(output_p_files)
if invert_ensemble:
# output_s_files = self.maximize_length_audio_wav(output_s_files)
pass
progress(0.95, desc=f"{t('process4')}...")
if invert_ensemble:
if invert_weights:
output_s_weights = self.invert_weights(output_p_weights)
else:
output_s_weights = output_p_weights
output_s, output_wav_s = ensemble_audio_files(files=output_s_files, output=os.path.join(temp_dir, f"ensemble_invert_{base_name}_{type}"), ensemble_type=INVERT_METHODS[type], weights=output_s_weights, out_format=out_format)
else:
output_s, output_wav_s = None, None
output_p, output_wav_p = ensemble_audio_files(files=output_p_files, output=os.path.join(temp_dir, f"ensemble_{base_name}_{type}"), ensemble_type=type, weights=output_p_weights, out_format=out_format)
return output_p, output_wav_p, output_s, output_wav_s, source_files
class EnsembleManager:
def __init__(self):
self.models = []
self.presets_dir = os.path.join(os.getcwd(), "presets")
os.makedirs(self.presets_dir, exist_ok=True)
def export_preset(self, name):
if not name:
name = "ensembless_preset"
filepath = os.path.join(self.presets_dir, f"{name}.json")
with open(filepath, 'w') as f:
json.dump(self.models, f)
return filepath
def import_preset(self, filepath):
with open(filepath, 'r') as f:
self.models = json.load(f)
return self.get_df()
def add_model(self, model_type, model_name, p_stem, s_stem, weight):
model_info = {
'type': model_type,
'name': model_name,
'p_stem': p_stem,
's_stem': s_stem,
'weight': float(weight)
}
self.models.append(model_info)
return self.get_df()
def remove_model(self, index):
if 0 <= index < len(self.models):
del self.models[index]
return self.get_df()
def clear_models(self):
self.models = []
return self.get_df()
def get_df(self):
if not self.models:
columns = ["#", t("model_type"), t("model_name"), t("p_stem"), t("s_stem"), t("weight")]
return pd.DataFrame(columns=columns)
data = []
for i, model in enumerate(self.models):
data.append([
f"{i+1}",
model['type'],
model['name'],
model['p_stem'],
model['s_stem'],
model['weight']
])
columns = ["#", t("model_type"), t("model_name"), t("p_stem"), t("s_stem"), t("weight")]
return pd.DataFrame(data, columns=columns)
def get_settings(self):
return [(f"{m['type']} / {m['name']}", m['weight'], m['p_stem'], m['s_stem']) for m in self.models]
inverter = Inverter()
manager = EnsembleManager()
ensembless = EnsembLess()
class EnsembLess_ui_updates:
def update_model_dropdown(self, model_type):
models = ensembless.get_models_by_type(model_type)
return gr.Dropdown(choices=models, value=models[0] if models else None)
def update_stem_dropdown(self, model_type, model_name):
stems = ensembless.get_stems_by_model(model_type, model_name)
return gr.Dropdown(choices=stems, value=stems[0] if stems else None)
def update_invert_stem_dropdown(self, model_type, model_name, primary_stem):
stems = ensembless.get_invert_stems_by_model(model_type, model_name, primary_stem)
return gr.Dropdown(choices=stems, value=stems[0] if stems else None)
def add_model(self, model_type, model_name, p_stem, s_stem, weight):
return manager.add_model(model_type, model_name, p_stem, s_stem, weight)
def remove_model(self, index):
if index >= 0:
return manager.remove_model(index-1) # Пользователь вводит начиная с 1, а индекс с 0
return manager.get_df()
def clear_all_models(self):
return manager.clear_models()
def run_ensemble(self, input_audio, ensemble_type, output_format, invert_weights, invert_ensemble):
if not manager.models:
raise gr.Error(t("error_no_models"))
if not input_audio:
raise gr.Error(t("error_no_audio"))
input_settings = manager.get_settings()
o, o_wav, i, i_wav, result_source = ensembless.auto_ensemble(
input_audio=input_audio,
input_settings=input_settings,
type=ensemble_type,
out_format=output_format,
invert_weights=invert_weights,
invert_ensemble=invert_ensemble,
)
return o, o_wav, i, i_wav, result_source
ensembless_ui = EnsembLess_ui_updates()
def ensembless_plugin_name():
return "EnsembLess"
# Создаем интерфейс
def ensembless_plugin(lang):
set_language(lang)
with gr.Tabs():
with gr.Tab(t("auto_ensemble")):
with gr.Row():
with gr.Column(scale=1):
# Секция добавления моделей
gr.Markdown(f"### {t('model_selection')}")
model_type = gr.Dropdown(
choices=ensembless.get_model_types(),
label=t("model_type"),
value=ensembless.get_model_types()[0] if ensembless.get_model_types() else None,
filterable=False
)
model_name = gr.Dropdown(
choices=ensembless.get_models_by_type(ensembless.get_model_types()[0]),
label=t("model_name"),
interactive=True,
value=ensembless.get_models_by_type(ensembless.get_model_types()[0])[0],
filterable=False
)
stem = gr.Dropdown(
choices=ensembless.get_stems_by_model(ensembless.get_model_types()[0], ensembless.get_models_by_type(ensembless.get_model_types()[0])[0]),
label=t("p_stem"),
interactive=True,
filterable=False
)
invert_stem = gr.Dropdown(
choices=ensembless.get_invert_stems_by_model(ensembless.get_model_types()[0], ensembless.get_models_by_type(ensembless.get_model_types()[0])[0], "vocals"),
label=t("s_stem"),
interactive=True,
filterable=False
)
weight = gr.Slider(
label=t("weight"),
value=1.0,
minimum=0.1,
maximum=10.0,
step=0.1
)
add_btn = gr.Button(t("add_button"), variant="primary")
with gr.Column(scale=2):
# Секция управления ансамблем
gr.Markdown(f"### {t('current_ensemble')}")
ensemble_df = gr.Dataframe(
value=manager.get_df(),
headers=["#", t("model_type"), t("model_name"), t("p_stem"), t("s_stem"), t("weight")],
datatype=["str", "str", "str", "str", "str", "number"],
interactive=False
)
with gr.Row(equal_height=True):
export_preset_name = gr.Textbox(label=t("give_name_preset"), interactive=True, value="ensembless_preset")
with gr.Column():
export_btn = gr.DownloadButton(t("export"), variant="secondary")
import_btn = gr.UploadButton(t("import"), file_types=[".json"], file_count="single")
with gr.Row(equal_height=True):
remove_idx = gr.Number(
label=t("remove_index"),
precision=0,
minimum=1,
interactive=True
)
with gr.Column():
remove_btn = gr.Button(t("remove_button"), variant="stop")
clear_btn = gr.Button(t("clear_button"), variant="stop")
# Секция запуска обработки
with gr.Row():
with gr.Column():
gr.Markdown(f"### {t('input_audio')}")
input_audio = gr.Audio(type="filepath", show_label=False)
input_audio_resampled = gr.Text(visible=False)
gr.Markdown(f"### {t('settings')}")
ensemble_type = gr.Dropdown(
choices=['avg_wave', 'median_wave', 'min_wave', 'max_wave',
'avg_fft', 'median_fft', 'min_fft', 'max_fft'],
value='avg_fft',
label=t("method"),
filterable=False
)
invert_ensem = gr.Checkbox(label=t("invert_ensemble"))
invert_weights = gr.Checkbox(label=t("invert_weights"))
output_format = gr.Dropdown(
choices=["wav", "mp3", "flac", "m4a", "aac", "ogg", "opus", "aiff"],
value="mp3",
label=t("output_format"),
filterable=False
)
run_btn = gr.Button(t("run_button"), variant="primary")
with gr.Column():
with gr.Tab(t('results')):
with gr.Column():
output_audio = gr.Audio(label=t("results"), type="filepath", interactive=False, show_download_button=True)
output_wav = gr.Text(label="Результат в WAV", interactive=False, visible=False)
gr.Markdown(f"###### {t('inverted_result')}")
invert_method = gr.Radio(
choices=["waveform", "spectrogram"],
label=t("invert_method"),
value="waveform"
)
invert_btn = gr.Button(t("invert_button"))
inverted_output_audio = gr.Audio(label=t("inverted_result"), type="filepath", interactive=False, show_download_button=True)
inverted_wav = gr.Text(label="Инвертированный результат в WAV", interactive=False, visible=False)
with gr.Tab(t('result_source')):
result_source = gr.Files(interactive=False, label=t('result_source'))
stem.change(ensembless_ui.update_invert_stem_dropdown, inputs=[model_type, model_name, stem], outputs=invert_stem)
model_type.change(
ensembless_ui.update_model_dropdown,
inputs=model_type,
outputs=model_name
)
model_name.change(
ensembless_ui.update_stem_dropdown,
inputs=[model_type, model_name],
outputs=stem
)
ensemble_df.change(
manager.export_preset,
inputs=export_preset_name,
outputs=export_btn
)
export_preset_name.change(
manager.export_preset,
inputs=export_preset_name,
outputs=export_btn
)
import_btn.upload(
manager.import_preset,
inputs=import_btn,
outputs=ensemble_df
)
invert_btn.click(
inverter.process_audio,
inputs=[input_audio_resampled, output_wav, output_format, invert_method],
outputs=[inverted_output_audio, inverted_wav]
)
input_audio.upload(
ensembless.resample_audio,
inputs=input_audio,
outputs=input_audio_resampled
)
add_btn.click(
ensembless_ui.add_model,
inputs=[model_type, model_name, stem, invert_stem, weight],
outputs=ensemble_df
)
remove_btn.click(
ensembless_ui.remove_model,
inputs=remove_idx,
outputs=ensemble_df
)
clear_btn.click(
ensembless_ui.clear_all_models,
outputs=ensemble_df
)
run_btn.click(
ensembless_ui.run_ensemble,
inputs=[input_audio_resampled, ensemble_type, output_format, invert_weights, invert_ensem],
outputs=[output_audio, output_wav, inverted_output_audio, inverted_wav, result_source]
)
with gr.Tab(t("manual_ensemble")):
with gr.Row(equal_height=True):
input_files = gr.Files(show_label=False, type="filepath", file_types=[".wav", ".mp3", ".flac", ".m4a", ".aac", ".ogg", ".opus", ".aiff"])
with gr.Column():
info_audios = gr.Textbox(label="", interactive=False)
man_method = gr.Dropdown(
choices=['avg_wave', 'median_wave', 'min_wave', 'max_wave',
'avg_fft', 'median_fft', 'min_fft', 'max_fft'],
value='avg_fft',
label=t("method"),
filterable=False
)
weights_input = gr.Textbox(label=t("weights_input"), value="1.0,1.0")
output_man_format = gr.Dropdown(
choices=["wav", "mp3", "flac", "m4a", "aac", "ogg", "opus", "aiff"],
value="mp3",
label=t("output_format"),
filterable=False
)
run_man_btn = gr.Button(t("run_button"), variant="primary")
output_man_audio = gr.Audio(label=t("results"), type="filepath", interactive=False, show_download_button=True)
output_man_wav = gr.Text(label="Результат в WAV", interactive=False, visible=False)
input_files.upload(
fn=ensembless.analyze_sample_rate,
inputs=input_files,
outputs=info_audios
)
run_man_btn.click(
ensembless.manual_ensemble,
inputs=[input_files, man_method, weights_input, output_man_format],
outputs=[output_man_audio, output_man_wav]
)