
GitHub 项目地址
vocal-separate 官方仓库:
https://github.com/jianchang512/vocal-separate这是一个基于 Spleeter 的人声分离工具,提供本地网页界面,支持 2stems/4stems/5stems 模型 。
打包成 exe 的完整方案
方案一:使用 PyInstaller 打包(推荐)
1. 环境准备
# 1. 克隆项目
git clone https://github.com/jianchang512/vocal-separate.git
cd vocal-separate
# 2. 创建虚拟环境
python -m venv venv
# Windows 激活虚拟环境
venv\Scripts\activate
# 3. 安装依赖
pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt2. 创建打包脚本 build_exe.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
vocal-separate v0.0.4 EXE 打包脚本
使用 PyInstaller 将项目打包成单个可执行文件
"""
import os
import sys
import shutil
import site
import subprocess
from pathlib import Path
def clean_build_dirs():
"""清理之前的构建目录"""
dirs_to_clean = ['build', 'dist']
for dir_name in dirs_to_clean:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print(f"已清理 {dir_name} 目录")
# 删除 spec 文件
spec_files = [f for f in os.listdir('.') if f.endswith('.spec')]
for spec in spec_files:
os.remove(spec)
print(f"已删除 {spec}")
def get_site_packages_path():
"""获取 site-packages 路径"""
for path in site.getsitepackages():
if 'site-packages' in path:
return path
return None
def find_model_path():
"""查找预训练模型路径"""
# 可能的模型存放位置
possible_paths = [
'./models',
'./pretrained_models',
os.path.join(os.path.dirname(sys.executable), 'models'),
os.path.join(os.getcwd(), 'models'),
]
# 检查 site-packages 中的 spleeter 模型
site_packages = get_site_packages_path()
if site_packages:
spleeter_path = os.path.join(site_packages, 'spleeter')
if os.path.exists(spleeter_path):
pretrained_path = os.path.join(spleeter_path, 'pretrained')
if os.path.exists(pretrained_path):
possible_paths.append(pretrained_path)
for path in possible_paths:
if os.path.exists(path):
print(f"找到模型目录: {path}")
return path
print("警告:未找到预训练模型目录,打包后可能需要联网下载")
return None
def create_spec_file(project_root, model_path):
"""创建 PyInstaller spec 文件"""
spec_content = f"""# -*- mode: python ; coding: utf-8 -*-
import sys
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
a = Analysis(
['start.py'],
pathex=['{project_root}'],
binaries=[],
datas=[
# 包含静态文件
('./static', 'static'),
('./templates', 'templates'),
# 包含配置文件
('./config.json', '.'),
('./config.yaml', '.'),
# 包含模型文件
('{model_path}', 'models'),
],
hiddenimports=[
'spleeter',
'spleeter.*',
'tensorflow',
'tensorflow.*',
'librosa',
'librosa.*',
'ffmpeg',
'ffmpeg.*',
'numpy',
'scipy',
'sklearn',
'sklearn.*',
'joblib',
'joblib.*',
'pydub',
'pydub.*',
'flask',
'flask.*',
'werkzeug',
'werkzeug.*',
'jinja2',
'jinja2.*',
'markupsafe',
'markupsafe.*',
'itsdangerous',
'click',
'threading',
'multiprocessing',
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='vocal-separate',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True, # 显示控制台窗口,便于调试
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='icon.ico' if os.path.exists('icon.ico') else None,
)
# 创建快捷启动脚本
with open('start_exe.py', 'w') as f:
f.write('''
import os
import sys
import webbrowser
import time
from threading import Timer
def open_browser():
webbrowser.open('http://127.0.0.1:5000')
if __name__ == '__main__':
# 切换到程序所在目录
os.chdir(os.path.dirname(sys.executable))
# 启动主程序
from start import app
# 延时打开浏览器
Timer(1.5, open_browser).start()
# 运行 Flask 应用
app.run(host='127.0.0.1', port=5000, debug=False, threaded=True)
''')
"""
with open('vocal-separate.spec', 'w', encoding='utf-8') as f:
f.write(spec_content)
print("已生成 spec 文件")
def check_ffmpeg():
"""检查 ffmpeg 是否可用"""
try:
subprocess.run(['ffmpeg', '-version'], capture_output=True)
return True
except FileNotFoundError:
print("警告:未找到 ffmpeg,程序可能无法处理视频文件")
return False
def download_ffmpeg():
"""下载 ffmpeg(Windows 系统)"""
if sys.platform == 'win32':
import urllib.request
import zipfile
ffmpeg_url = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"
print("正在下载 ffmpeg...")
try:
urllib.request.urlretrieve(ffmpeg_url, "ffmpeg.zip")
with zipfile.ZipFile("ffmpeg.zip", 'r') as zip_ref:
zip_ref.extractall("ffmpeg_temp")
# 复制 ffmpeg.exe 到项目目录
for root, dirs, files in os.walk("ffmpeg_temp"):
for file in files:
if file == "ffmpeg.exe":
shutil.copy(os.path.join(root, file), "ffmpeg.exe")
print("ffmpeg 下载完成")
break
# 清理临时文件
shutil.rmtree("ffmpeg_temp")
os.remove("ffmpeg.zip")
except Exception as e:
print(f"下载 ffmpeg 失败: {e}")
def main():
"""主打包函数"""
print("=" * 50)
print("vocal-separate v0.0.4 EXE 打包工具")
print("=" * 50)
# 检查 ffmpeg
if not check_ffmpeg() and sys.platform == 'win32':
choice = input("未找到 ffmpeg,是否自动下载?(y/n): ")
if choice.lower() == 'y':
download_ffmpeg()
# 查找模型路径
model_path = find_model_path()
if not model_path:
print("提示:模型将在首次运行时下载")
# 清理旧文件
clean_build_dirs()
# 获取项目根目录
project_root = os.getcwd()
# 创建 spec 文件
create_spec_file(project_root, model_path or './models')
# 执行 PyInstaller
print("\n开始打包,这可能需要几分钟时间...")
cmd = [
'pyinstaller',
'vocal-separate.spec',
'--clean',
'--noconfirm',
]
try:
subprocess.run(cmd, check=True)
print("\n✅ 打包完成!")
print(f"可执行文件位于: {os.path.join(project_root, 'dist', 'vocal-separate')}")
print("\n使用方法:")
print("1. 进入 dist/vocal-separate 目录")
print("2. 双击运行 vocal-separate.exe")
print("3. 浏览器会自动打开 http://127.0.0.1:5000")
print("4. 拖拽音视频文件进行分离")
except subprocess.CalledProcessError as e:
print(f"\n❌ 打包失败: {e}")
return 1
return 0
if __name__ == '__main__':
sys.exit(main())3. 创建启动脚本 start_exe.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
vocal-separate 启动脚本(用于 exe 打包)
"""
import os
import sys
import webbrowser
import time
import logging
from threading import Timer
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('vocal-separate.log', encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger('vocal-separate')
def open_browser():
"""延时打开浏览器"""
time.sleep(1.5)
url = 'http://127.0.0.1:5000'
try:
webbrowser.open(url)
logger.info(f"已打开浏览器访问: {url}")
except Exception as e:
logger.error(f"打开浏览器失败: {e}")
def check_environment():
"""检查运行环境"""
# 切换到程序所在目录
os.chdir(os.path.dirname(sys.executable))
# 检查 ffmpeg
ffmpeg_paths = [
'ffmpeg.exe',
os.path.join(os.path.dirname(sys.executable), 'ffmpeg.exe'),
]
ffmpeg_found = False
for path in ffmpeg_paths:
if os.path.exists(path):
os.environ['PATH'] = os.path.dirname(path) + os.pathsep + os.environ['PATH']
ffmpeg_found = True
logger.info(f"找到 ffmpeg: {path}")
break
if not ffmpeg_found:
logger.warning("未找到 ffmpeg,视频处理功能可能受限")
# 检查模型目录
model_dirs = [
'models',
'pretrained_models',
os.path.join(os.path.dirname(sys.executable), 'models'),
]
for model_dir in model_dirs:
if os.path.exists(model_dir):
logger.info(f"找到模型目录: {model_dir}")
break
else:
logger.info("未找到本地模型,将在首次使用时下载")
def main():
"""主函数"""
print("=" * 50)
print("vocal-separate v0.0.4 人声分离工具")
print("=" * 50)
# 检查环境
check_environment()
# 启动浏览器线程
Timer(1.5, open_browser).start()
# 导入并启动 Flask 应用
try:
# 动态导入 start 模块
sys.path.insert(0, os.path.dirname(sys.executable))
# 这里需要根据实际的项目入口文件调整
# 假设主文件是 start.py
import importlib.util
spec = importlib.util.spec_from_file_location(
"start",
os.path.join(os.path.dirname(sys.executable), "start.py")
)
start_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(start_module)
# 获取 Flask app
if hasattr(start_module, 'app'):
app = start_module.app
else:
# 尝试获取其他可能的变量名
for attr in ['application', 'server', 'webapp']:
if hasattr(start_module, attr):
app = getattr(start_module, attr)
break
else:
raise AttributeError("找不到 Flask 应用实例")
logger.info("启动服务器 http://127.0.0.1:5000")
app.run(
host='127.0.0.1',
port=5000,
debug=False,
threaded=True
)
except Exception as e:
logger.error(f"启动失败: {e}")
print(f"\n❌ 启动失败: {e}")
print("请查看 vocal-separate.log 获取详细信息")
input("按回车键退出...")
sys.exit(1)
if __name__ == '__main__':
main()4. 运行打包
# 执行打包脚本
python build_exe.py方案二:使用 auto-py-to-exe(可视化界面)
# 安装 auto-py-to-exe
pip install auto-py-to-exe
# 启动可视化界面
auto-py-to-exe在可视化界面中配置:
- Script Location: 选择
start.py - Onefile: 选择 One Directory(推荐,便于包含模型文件)
- Console Window: 勾选(显示控制台便于调试)
- Icon: 可选,设置程序图标
Additional Files: 添加以下文件和目录
./static/目录./templates/目录- 预训练模型目录(如
./models/) ffmpeg.exe(Windows 系统)
方案三:创建安装包(使用 Inno Setup)
创建 setup_script.iss:
[Setup]
AppName=vocal-separate
AppVersion=0.0.4
DefaultDirName={pf}\vocal-separate
DefaultGroupName=vocal-separate
UninstallDisplayIcon={app}\vocal-separate.exe
Compression=lzma2
SolidCompression=yes
OutputDir=installer
OutputBaseFilename=vocal-separate-0.0.4-setup
[Files]
Source: "dist\vocal-separate\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "ffmpeg.exe"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\vocal-separate"; Filename: "{app}\vocal-separate.exe"
Name: "{group}\Uninstall vocal-separate"; Filename: "{uninstallexe}"
Name: "{commondesktop}\vocal-separate"; Filename: "{app}\vocal-separate.exe"
[Run]
Filename: "{app}\vocal-separate.exe"; Description: "启动 vocal-separate"; Flags: postinstall nowait skipifsilent注意事项
1. 模型文件处理
- 项目内置了 Spleeter 预训练模型,打包时需要包含这些模型文件
- 模型文件通常较大(几百MB),建议采用外置模型的方式,首次运行时下载
2. 依赖项说明
- ffmpeg:必须包含,用于处理视频文件的音轨提取
- TensorFlow:如果支持 GPU 加速,需要额外打包 CUDA 库
3. 性能优化建议
- 对于无 NVIDIA GPU 的电脑,建议默认使用 2stems 模型,避免内存溢出
- 打包时可以包含 CPU 版本的 TensorFlow,减小体积
4. 打包后的文件结构
vocal-separate/
├── vocal-separate.exe # 主程序
├── ffmpeg.exe # ffmpeg 工具
├── models/ # 预训练模型
├── static/ # 静态资源
├── templates/ # HTML模板
├── config.json # 配置文件
└── vocal-separate.log # 运行日志使用说明
打包完成后,用户只需:
- 解压或安装程序
- 双击运行
vocal-separate.exe - 等待浏览器自动打开
- 拖拽音视频文件到网页界面
- 点击"立即分离"等待处理完成
One feature request: batch processing multiple files. Currently I have to upload and separate one by one. For processing a whole album, that's tedious. But for a free tool, can't complain too much!
试了试4stems模型分离一首电子音乐,鼓、贝斯、人声、其他四个轨道。效果出奇的好,尤其是贝斯和底鼓分离得很干净,做混音练习太方便了。以前用相位抵消总有串音,这个算法是真厉害。
The error handling in the build script is solid. It checks for ffmpeg, offers to download it, and gives clear messages about missing models. Makes the packaging process foolproof even for first-time users.
遇到个问题:处理完的文件保存在哪?找了一圈才发现默认在output目录,跟输入文件同名但加了后缀。建议在UI上加个提示,或者让用户自定义输出路径。整体很好用,五星好评!
This is perfect for my YouTube channel where I do music analysis videos. I can quickly separate vocals to show harmony parts, or isolate the bass line to explain the groove. The clean UI makes it easy to demonstrate live during recordings.