Spaces:
Running on Zero
Running on Zero
Quarter-Peach commited on
Commit ·
6eae822
1
Parent(s): d3a4010
UI
Browse files- audio_visualizer.py +209 -162
audio_visualizer.py
CHANGED
|
@@ -5,218 +5,265 @@ import librosa
|
|
| 5 |
import librosa.display
|
| 6 |
import matplotlib.pyplot as plt
|
| 7 |
import numpy as np
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# 设置页面配置
|
| 10 |
-
st.set_page_config(page_title="音频对比与频谱可视化工具 (
|
| 11 |
|
| 12 |
# --- 辅助函数 ---
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
return
|
| 22 |
-
|
| 23 |
-
def
|
| 24 |
-
|
| 25 |
if not os.path.isdir(folder_path):
|
| 26 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
# 获取通用 ID
|
| 33 |
-
file_id = clean_filename_for_id(base, prefixes)
|
| 34 |
-
# 存储通用 ID 到 文件的基础名称 (例如: '2' -> 'mixture_2')
|
| 35 |
-
file_ids[file_id] = base
|
| 36 |
-
return file_ids
|
| 37 |
|
| 38 |
@st.cache_data(show_spinner="正在加载音频并生成频谱图...")
|
| 39 |
def generate_spectrogram(audio_path, title):
|
| 40 |
-
|
| 41 |
try:
|
| 42 |
y, sr = librosa.load(audio_path, sr=None)
|
| 43 |
S = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=2048, hop_length=512)
|
| 44 |
S_dB = librosa.power_to_db(S, ref=np.max)
|
| 45 |
|
| 46 |
fig, ax = plt.subplots(figsize=(10, 4))
|
| 47 |
-
# 使用'viridis'作为默认色图,如果'magma'在某些环境下不适用
|
| 48 |
img = librosa.display.specshow(S_dB, sr=sr, x_axis='time', y_axis='mel', ax=ax, cmap='viridis')
|
| 49 |
-
ax.set(title=f'Mel
|
|
|
|
| 50 |
|
| 51 |
return fig
|
| 52 |
except FileNotFoundError:
|
| 53 |
return None
|
| 54 |
-
except Exception:
|
|
|
|
| 55 |
return None
|
| 56 |
|
| 57 |
# --- Streamlit 状态初始化 ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
if 'mixture_path' not in st.session_state:
|
| 60 |
-
st.session_state.mixture_path = "/inspire/hdd/global_user/chenxie-25019/HaoQiu/music-source-restoration/msr_test_set/Bass" # 预设您的路径
|
| 61 |
-
if 'target_path' not in st.session_state:
|
| 62 |
-
st.session_state.target_path = "/inspire/hdd/global_user/chenxie-25019/HaoQiu/music-source-restoration/Result/Bass_gan_35k" # 预设您的路径
|
| 63 |
-
if 'mix_prefixes' not in st.session_state:
|
| 64 |
-
st.session_state.mix_prefixes = "mixture_,source_" # 混合文件常见前缀
|
| 65 |
-
if 'tar_prefixes' not in st.session_state:
|
| 66 |
-
st.session_state.tar_prefixes = "restored_,pred_,target_" # 目标文件常见前缀
|
| 67 |
-
if 'matched_ids' not in st.session_state:
|
| 68 |
-
st.session_state.matched_ids = {} # 存储匹配的通用ID -> (mix_base, tar_base)
|
| 69 |
-
if 'available_keys' not in st.session_state:
|
| 70 |
-
st.session_state.available_keys = [] # 存储通用ID列表
|
| 71 |
-
if 'selected_key' not in st.session_state:
|
| 72 |
-
st.session_state.selected_key = None
|
| 73 |
|
| 74 |
# --- 主体 UI ---
|
| 75 |
|
| 76 |
-
st.title("🎼 音频对比与频谱可视化工具 (
|
| 77 |
-
st.markdown("此版本包含**智能文件名匹配**功能,用于音源分离/恢复数据的可视化。")
|
| 78 |
|
| 79 |
# 1. 文件夹输入
|
| 80 |
st.header("1. 输入文件夹路径")
|
| 81 |
-
col_mix, col_tar = st.columns(
|
| 82 |
-
with
|
| 83 |
-
|
| 84 |
-
with col_tar:
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
st.
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
with
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
st.cache_data.clear()
|
| 107 |
-
|
| 108 |
-
st.session_state.selected_key = None
|
| 109 |
st.rerun()
|
| 110 |
|
| 111 |
-
# 3. 文件列表加载逻辑
|
| 112 |
-
if st.session_state.mixture_path and st.session_state.target_path:
|
| 113 |
-
|
| 114 |
-
# 将逗号分隔的前缀字符串转换为列表
|
| 115 |
-
mix_prefixes_list = [p.strip() for p in st.session_state.mix_prefixes.split(',') if p.strip()]
|
| 116 |
-
tar_prefixes_list = [p.strip() for p in st.session_state.tar_prefixes.split(',') if p.strip()]
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
tar_id_to_base = get_audio_files_v2(st.session_state.target_path, tar_prefixes_list)
|
| 121 |
|
| 122 |
-
#
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
-
st.session_state.
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
st.session_state.available_keys = sorted(list(matched_ids))
|
| 130 |
-
|
| 131 |
-
if not st.session_state.available_keys:
|
| 132 |
-
st.warning("在两个文件夹中未找到匹配的音频对。请检查路径、文件格式或**前缀配置**。")
|
| 133 |
else:
|
| 134 |
-
st.success(f"成功找到 {len(st.session_state.
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
st.session_state.selected_key = st.session_state.available_keys[0] if st.session_state.available_keys else None
|
| 138 |
|
| 139 |
|
| 140 |
-
#
|
| 141 |
-
st.
|
| 142 |
-
|
| 143 |
-
|
| 144 |
col_select, col_random = st.columns([3, 1])
|
| 145 |
|
| 146 |
with col_select:
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
st.session_state.
|
| 151 |
-
index=st.session_state.available_keys.index(st.session_state.selected_key) if st.session_state.selected_key in st.session_state.available_keys else 0
|
| 152 |
)
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
st.
|
| 156 |
|
| 157 |
with col_random:
|
| 158 |
-
|
| 159 |
-
st.
|
| 160 |
-
|
| 161 |
-
st.session_state.selected_key = random.choice(st.session_state.available_keys)
|
| 162 |
st.rerun()
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
st.subheader("Target (恢复/结果)")
|
| 208 |
-
st.markdown(f"**路径:** `{tar_file_path}`")
|
| 209 |
|
| 210 |
try:
|
| 211 |
-
|
|
|
|
|
|
|
| 212 |
except Exception as e:
|
| 213 |
-
st.error(f"
|
| 214 |
|
| 215 |
-
|
| 216 |
-
if
|
| 217 |
-
st.pyplot(
|
| 218 |
-
plt.close(
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import librosa.display
|
| 6 |
import matplotlib.pyplot as plt
|
| 7 |
import numpy as np
|
| 8 |
+
import re
|
| 9 |
+
from collections import defaultdict
|
| 10 |
|
| 11 |
# 设置页面配置
|
| 12 |
+
st.set_page_config(page_title="多音频对比与频谱可视化工具 (v5.2 - 最终稳定版)", layout="wide")
|
| 13 |
|
| 14 |
# --- 辅助函数 ---
|
| 15 |
|
| 16 |
+
def get_safe_prefix(prefix_input):
|
| 17 |
+
"""从逗号分隔的字符串中安全地获取第一个有效前缀,否则返回空字符串。"""
|
| 18 |
+
prefix_list = [p.strip() for p in prefix_input.split(',') if p.strip()]
|
| 19 |
+
return prefix_list[0] if prefix_list else ""
|
| 20 |
+
|
| 21 |
+
def get_prefix_list(prefix_input):
|
| 22 |
+
"""从逗号分隔的字符串中获取所有有效前缀的列表。"""
|
| 23 |
+
return [p.strip() for p in prefix_input.split(',') if p.strip()]
|
| 24 |
+
|
| 25 |
+
def get_universal_ids_regex(folder_path, prefix_list, regex_pattern, extensions=['.wav', '.mp3', '.flac']):
|
| 26 |
+
# ... (此函数体保持不变) ...
|
| 27 |
if not os.path.isdir(folder_path):
|
| 28 |
+
return set()
|
| 29 |
+
|
| 30 |
+
ids = set()
|
| 31 |
+
try:
|
| 32 |
+
compiled_regex = re.compile(regex_pattern)
|
| 33 |
+
|
| 34 |
+
for filename in os.listdir(folder_path):
|
| 35 |
+
base, ext = os.path.splitext(filename)
|
| 36 |
+
if ext.lower() in extensions:
|
| 37 |
+
|
| 38 |
+
current_base = base
|
| 39 |
+
for prefix in prefix_list:
|
| 40 |
+
if current_base.startswith(prefix):
|
| 41 |
+
current_base = current_base[len(prefix):]
|
| 42 |
+
break
|
| 43 |
+
|
| 44 |
+
match = compiled_regex.match(current_base)
|
| 45 |
+
|
| 46 |
+
if match and 'x' in match.groupdict():
|
| 47 |
+
file_id_x = match.group('x')
|
| 48 |
+
if file_id_x:
|
| 49 |
+
ids.add(file_id_x)
|
| 50 |
+
|
| 51 |
+
except re.error as e:
|
| 52 |
+
return set()
|
| 53 |
+
except Exception:
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
return ids
|
| 57 |
+
|
| 58 |
+
@st.cache_data(show_spinner="正在扫描文件并匹配通用 ID (x)...")
|
| 59 |
+
def find_matched_ids(output_path, mix_path, tar_path, out_pfx_str, mix_pfx_str, tar_pfx_str, out_pat, mix_pat, tar_pat):
|
| 60 |
+
"""获取三个文件夹中所有通用 ID (x) 的交集,并缓存结果。"""
|
| 61 |
+
|
| 62 |
+
out_prefixes_list = get_prefix_list(out_pfx_str)
|
| 63 |
+
mix_prefixes_list = get_prefix_list(mix_pfx_str)
|
| 64 |
+
tar_prefixes_list = get_prefix_list(tar_pfx_str)
|
| 65 |
+
|
| 66 |
+
# 获取三个集合的通用 ID (x)
|
| 67 |
+
out_ids = get_universal_ids_regex(output_path, out_prefixes_list, out_pat)
|
| 68 |
+
mix_ids = get_universal_ids_regex(mix_path, mix_prefixes_list, mix_pat)
|
| 69 |
+
tar_ids = get_universal_ids_regex(tar_path, tar_prefixes_list, tar_pat)
|
| 70 |
|
| 71 |
+
# 找到三个集合的交集
|
| 72 |
+
matched_x_ids = sorted(list(out_ids & mix_ids & tar_ids))
|
| 73 |
+
return matched_x_ids
|
| 74 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
@st.cache_data(show_spinner="正在加载音频并生成频谱图...")
|
| 77 |
def generate_spectrogram(audio_path, title):
|
| 78 |
+
# --- 修复点 2: 移除中文标题 (中文乱码问题) ---
|
| 79 |
try:
|
| 80 |
y, sr = librosa.load(audio_path, sr=None)
|
| 81 |
S = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=2048, hop_length=512)
|
| 82 |
S_dB = librosa.power_to_db(S, ref=np.max)
|
| 83 |
|
| 84 |
fig, ax = plt.subplots(figsize=(10, 4))
|
|
|
|
| 85 |
img = librosa.display.specshow(S_dB, sr=sr, x_axis='time', y_axis='mel', ax=ax, cmap='viridis')
|
| 86 |
+
ax.set(title=f'Mel Spectrogram: {title}', xlabel='Time', ylabel='Mel Freq') # <-- 标题改为英文
|
| 87 |
+
ax.tick_params(labelsize=8)
|
| 88 |
|
| 89 |
return fig
|
| 90 |
except FileNotFoundError:
|
| 91 |
return None
|
| 92 |
+
except Exception as e:
|
| 93 |
+
st.error(f"处理音频文件失败: {str(e)}")
|
| 94 |
return None
|
| 95 |
|
| 96 |
# --- Streamlit 状态初始化 ---
|
| 97 |
+
if 'output_path' not in st.session_state: st.session_state.output_path = ""
|
| 98 |
+
if 'mixture_path' not in st.session_state: st.session_state.mixture_path = ""
|
| 99 |
+
if 'target_path' not in st.session_state: st.session_state.target_path = ""
|
| 100 |
+
if 'output_pattern' not in st.session_state: st.session_state.output_pattern = r"(?P<x>\d+)_DT(?P<y>\d+)"
|
| 101 |
+
if 'mix_pattern' not in st.session_state: st.session_state.mix_pattern = r"(?P<x>\d+)_DT(?P<y>\d+)"
|
| 102 |
+
if 'tar_pattern' not in st.session_state: st.session_state.tar_pattern = r"(?P<x>\d+)"
|
| 103 |
+
if 'output_prefixes' not in st.session_state: st.session_state.output_prefixes = ""
|
| 104 |
+
if 'mix_prefixes' not in st.session_state: st.session_state.mix_prefixes = ""
|
| 105 |
+
if 'tar_prefixes' not in st.session_state: st.session_state.tar_prefixes = ""
|
| 106 |
+
if 'separator' not in st.session_state: st.session_state.separator = "_DT" # <--- 新增状态
|
| 107 |
+
if 'selected_y' not in st.session_state: st.session_state.selected_y = "0"
|
| 108 |
+
if 'available_x_ids' not in st.session_state: st.session_state.available_x_ids = []
|
| 109 |
+
if 'selected_x_id' not in st.session_state: st.session_state.selected_x_id = None
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
# --- 主体 UI ---
|
| 113 |
|
| 114 |
+
st.title("🎼 多音频对比与频谱可视化工具 (v5.2 - 最终稳定版)")
|
|
|
|
| 115 |
|
| 116 |
# 1. 文件夹输入
|
| 117 |
st.header("1. 输入文件夹路径")
|
| 118 |
+
col_out, col_mix, col_tar = st.columns(3)
|
| 119 |
+
with col_out: st.session_state.output_path = st.text_input("Output 文件夹路径", st.session_state.output_path)
|
| 120 |
+
with col_mix: st.session_state.mixture_path = st.text_input("Mixture 文件夹路径", st.session_state.mixture_path)
|
| 121 |
+
with col_tar: st.session_state.target_path = st.text_input("Target 文件夹路径", st.session_state.target_path)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
# 2. 模式和前缀配置
|
| 125 |
+
st.header("2. 配置文件名匹配模式")
|
| 126 |
+
st.markdown("请为每种文件类型配置**独立**的模式和前缀。模式必须包含 `(?P<x>...)`。")
|
| 127 |
+
|
| 128 |
+
# --- Output 配置 ---
|
| 129 |
+
col_out_p, col_out_r = st.columns(2)
|
| 130 |
+
with col_out_p: st.session_state.output_prefixes = st.text_input("Output 前缀", st.session_state.output_prefixes)
|
| 131 |
+
with col_out_r: st.session_state.output_pattern = st.text_input("Output 模式 (提取 x)", st.session_state.output_pattern, key='out_pat', help="例如: `(?P<x>\d+)_DT\d+`")
|
| 132 |
+
|
| 133 |
+
# --- Mixture 配置 ---
|
| 134 |
+
col_mix_p, col_mix_r = st.columns(2)
|
| 135 |
+
with col_mix_p: st.session_state.mix_prefixes = st.text_input("Mixture 前缀", st.session_state.mix_prefixes)
|
| 136 |
+
with col_mix_r: st.session_state.mix_pattern = st.text_input("Mixture 模式 (提取 x)", st.session_state.mix_pattern, key='mix_pat', help="例如: `(?P<x>\d+)_DT\d+`")
|
| 137 |
+
|
| 138 |
+
# --- Target 配置 (修复点 1: 确保 Target 模式在这里配置) ---
|
| 139 |
+
col_tar_p, col_tar_r = st.columns(2)
|
| 140 |
+
with col_tar_p: st.session_state.tar_prefixes = st.text_input("Target 前缀", st.session_state.tar_prefixes)
|
| 141 |
+
with col_tar_r: st.session_state.tar_pattern = st.text_input("Target 模式 (提取 x)", st.session_state.tar_pattern, key='tar_pat', help="例如: `(?P<x>\d+)`")
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
# 3. 核心版本号配置 (UI 结构调整,避免重复标题)
|
| 145 |
+
st.header("3. 核心版本号与 ID 加载")
|
| 146 |
+
col_sep, col_y, col_btn_y = st.columns([1, 1, 2])
|
| 147 |
+
with col_sep:
|
| 148 |
+
st.session_state.separator = st.text_input("X和Y之间的分隔符", st.session_state.separator, help="例如:`_DT`,`_V_` 等。**注意:修改此项后必须相应修改第2部分的模式!**")
|
| 149 |
+
with col_y:
|
| 150 |
+
st.session_state.selected_y = st.text_input("目标小版本号 (y)", st.session_state.selected_y)
|
| 151 |
+
|
| 152 |
+
with col_btn_y:
|
| 153 |
+
st.write(" ")
|
| 154 |
+
if st.button("加载/刷新通用ID列表 (x)", help="清除缓存,根据新的模式和前缀重新匹配通用大序号 (x)"):
|
| 155 |
st.cache_data.clear()
|
| 156 |
+
st.session_state.selected_x_id = None
|
|
|
|
| 157 |
st.rerun()
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
+
# 4. 文件列表加载逻辑 (通用 ID x)
|
| 161 |
+
if st.session_state.output_path and st.session_state.mixture_path and st.session_state.target_path:
|
|
|
|
| 162 |
|
| 163 |
+
# 核心:调用缓存函数,如果参数不变,它会立即返回结果
|
| 164 |
+
matched_x_ids = find_matched_ids(
|
| 165 |
+
st.session_state.output_path, st.session_state.mixture_path, st.session_state.target_path,
|
| 166 |
+
st.session_state.output_prefixes, st.session_state.mix_prefixes, st.session_state.tar_prefixes,
|
| 167 |
+
st.session_state.output_pattern, st.session_state.mix_pattern, st.session_state.tar_pattern
|
| 168 |
+
)
|
| 169 |
|
| 170 |
+
st.session_state.available_x_ids = matched_x_ids
|
| 171 |
+
|
| 172 |
+
if not st.session_state.available_x_ids:
|
| 173 |
+
st.warning("在三个文件夹中未找到匹配的通用音频 ID (x)。请检查路径、文件格式或正则表达式模式。")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
else:
|
| 175 |
+
st.success(f"成功找到 {len(st.session_state.available_x_ids)} 个匹配的通用 ID (x)。")
|
| 176 |
+
if st.session_state.selected_x_id not in st.session_state.available_x_ids:
|
| 177 |
+
st.session_state.selected_x_id = st.session_state.available_x_ids[0] if st.session_state.available_x_ids else None
|
|
|
|
| 178 |
|
| 179 |
|
| 180 |
+
# 5. 选择音频对 (通用 ID x)
|
| 181 |
+
if st.session_state.available_x_ids:
|
| 182 |
+
st.header("4. 选择音频 ID (x)")
|
|
|
|
| 183 |
col_select, col_random = st.columns([3, 1])
|
| 184 |
|
| 185 |
with col_select:
|
| 186 |
+
new_selected_x_id = st.selectbox(
|
| 187 |
+
"手动选择一个通用音频 ID (x)",
|
| 188 |
+
st.session_state.available_x_ids,
|
| 189 |
+
index=st.session_state.available_x_ids.index(st.session_state.selected_x_id) if st.session_state.selected_x_id in st.session_state.available_x_ids else 0
|
|
|
|
| 190 |
)
|
| 191 |
+
if new_selected_x_id and new_selected_x_id != st.session_state.selected_x_id:
|
| 192 |
+
st.session_state.selected_x_id = new_selected_x_id
|
| 193 |
+
st.rerun()
|
| 194 |
|
| 195 |
with col_random:
|
| 196 |
+
st.write(" ")
|
| 197 |
+
if st.button("随机抽取 ID (x)"):
|
| 198 |
+
st.session_state.selected_x_id = random.choice(st.session_state.available_x_ids)
|
|
|
|
| 199 |
st.rerun()
|
| 200 |
|
| 201 |
+
|
| 202 |
+
# 6. 展示结果
|
| 203 |
+
if st.session_state.selected_x_id:
|
| 204 |
+
selected_x_id = st.session_state.selected_x_id
|
| 205 |
+
selected_y = st.session_state.selected_y
|
| 206 |
+
separator = st.session_state.separator
|
| 207 |
|
| 208 |
+
st.header(f"5. 展示结果:ID(x) - {selected_x_id}, 版本(y) - {selected_y}")
|
| 209 |
+
|
| 210 |
+
# --- 文件路径构造逻辑 ---
|
| 211 |
+
|
| 212 |
+
def find_full_path(folder, base_name):
|
| 213 |
+
"""尝试查找常用扩展名,返回完整路径"""
|
| 214 |
+
for ext in ['.wav', '.flac', '.mp3']:
|
| 215 |
+
full_path = os.path.join(folder, base_name + ext)
|
| 216 |
+
if os.path.exists(full_path):
|
| 217 |
+
return full_path
|
| 218 |
+
return None
|
| 219 |
+
|
| 220 |
+
# ✅ 安全获取前缀
|
| 221 |
+
out_prefix = get_safe_prefix(st.session_state.output_prefixes)
|
| 222 |
+
mix_prefix = get_safe_prefix(st.session_state.mix_prefixes)
|
| 223 |
+
tar_prefix = get_safe_prefix(st.session_state.tar_prefixes)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
# 1. Output 文件:[Output Prefix]x[SEPARATOR]y
|
| 227 |
+
output_base_name = f"{out_prefix}{selected_x_id}{separator}{selected_y}"
|
| 228 |
+
output_file_path = find_full_path(st.session_state.output_path, output_base_name)
|
| 229 |
+
|
| 230 |
+
# 2. Mixture 文件:[Mixture Prefix]x[SEPARATOR]y
|
| 231 |
+
mixture_base_name = f"{mix_prefix}{selected_x_id}{separator}{selected_y}"
|
| 232 |
+
mixture_file_path = find_full_path(st.session_state.mixture_path, mixture_base_name)
|
| 233 |
+
|
| 234 |
+
# 3. Target 文件:[Target Prefix]x (简化的命名)
|
| 235 |
+
target_base_name = f"{tar_prefix}{selected_x_id}"
|
| 236 |
+
target_file_path = find_full_path(st.session_state.target_path, target_base_name)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
# --- 三列展示 ---
|
| 240 |
+
col_out, col_mix, col_tar = st.columns(3)
|
| 241 |
+
|
| 242 |
+
def display_audio_col(col, title, file_path, base_name):
|
| 243 |
+
with col:
|
| 244 |
+
st.subheader(title)
|
| 245 |
+
if file_path:
|
| 246 |
+
st.markdown(f"**File:** `{os.path.basename(file_path)}`")
|
|
|
|
|
|
|
| 247 |
|
| 248 |
try:
|
| 249 |
+
# 尝试用 flac 格式播放,如果文件是 flac
|
| 250 |
+
ext = os.path.splitext(file_path)[1].lower()
|
| 251 |
+
st.audio(file_path, format=f'audio/{ext.strip(".")}' if ext else 'audio/wav')
|
| 252 |
except Exception as e:
|
| 253 |
+
st.error(f"Playback failed for {title}: {str(e)}")
|
| 254 |
|
| 255 |
+
fig = generate_spectrogram(file_path, title)
|
| 256 |
+
if fig:
|
| 257 |
+
st.pyplot(fig)
|
| 258 |
+
plt.close(fig)
|
| 259 |
+
else:
|
| 260 |
+
st.warning(f"File not found. Attempted base name: `{base_name}`")
|
| 261 |
+
|
| 262 |
+
# 1. Output 列
|
| 263 |
+
display_audio_col(col_out, "Output (Result)", output_file_path, output_base_name)
|
| 264 |
+
|
| 265 |
+
# 2. Mixture 列
|
| 266 |
+
display_audio_col(col_mix, "Mixture (Original)", mixture_file_path, mixture_base_name)
|
| 267 |
+
|
| 268 |
+
# 3. Target 列
|
| 269 |
+
display_audio_col(col_tar, "Target (Ground Truth)", target_file_path, target_base_name)
|