免费背景音人声分离解决方案MVSEP-MDX23,足以和Spleeter分庭抗礼

在音视频领域,把已经发布的混音歌曲或者音频文件逆向分离一直是世界性的课题。音波混合的物理特性导致在没有原始工程文件的情况下,将其还原和分离是一件很有难度的事情。
言及背景音人声分离技术,就不能不提Spleeter,它是一种用于音频源分离(音乐分离)的开源深度学习算法,由Deezer研究团队开发。使用的是一个性能取向的音源分离算法,并且为用户提供了已经预训练好的模型,能够开箱即用,这也是Spleeter泛用性高的原因之一,关于Spleeter,请移步:人工智能AI库Spleeter免费人声和背景音乐分离实践(Python3.10),这里不再赘述。
MVSEP-MDX23背景音人声分离技术由Demucs研发,Demucs来自Facebook Research团队,它的发源晚于Spleeter,早于MDX-Net,并且经历过4个大版本的迭代,每一代的模型结构都被大改。Demucs的生成质量从v3开始大幅质变,一度领先行业平均水平,v4是现在最强的开源乐器分离单模型,v1和v2的网络模型被用作MDX-net其中的一部分。
本次我们基于MVSEP-MDX23来对音频的背景音和人声进行分离。
本地分离人声和背景音
如果本地离线运行MVSEP-MDX23,首先克隆代码:
git clone https://github.com/jarredou/MVSEP-MDX23-Colab_v2.git
随后进入项目并安装依赖:
cd MVSEP-MDX23-Colab_v2
pip3 install -r requirements.txt
随后直接进推理即可:
python3 inference.py --input_audio test.wav --output_folder ./results/
这里将test.wav进行人声分离,分离后的文件在results文件夹生成。
注意推理过程中会将分离模型下载到项目的models目录,极其巨大。
同时推理过程相当缓慢。
这里可以添加--single_onnx参数来提高推理速度,但音质上有一定的损失。
如果本地设备具备12G以上的显存,也可以添加--large_gpu参数来提高推理的速度。
如果本地没有N卡或者显存实在捉襟见肘,也可以通过--cpu参数来使用cpu进行推理,但是并不推荐这样做,因为本来就慢,用cpu就更慢了。
令人暖心的是,官方还利用Pyqt写了一个小的gui界面来提高操作友好度:
__author__ = 'Roman Solovyev (ZFTurbo), IPPM RAS'
if __name__ == '__main__':
import os
gpu_use = "0"
print('GPU use: {}'.format(gpu_use))
os.environ["CUDA_VISIBLE_DEVICES"] = "{}".format(gpu_use)
import time
import os
import numpy as np
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
import sys
from inference import predict_with_model
root = dict()
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
def __init__(self, options):
super().__init__()
self.options = options
def run(self):
global root
# Here we pass the update_progress (uncalled!)
self.options['update_percent_func'] = self.update_progress
predict_with_model(self.options)
root['button_start'].setDisabled(False)
root['button_finish'].setDisabled(True)
root['start_proc'] = False
self.finished.emit()
def update_progress(self, percent):
self.progress.emit(percent)
class Ui_Dialog(object):
def setupUi(self, Dialog):
global root
Dialog.setObjectName("Settings")
Dialog.resize(370, 180)
self.checkbox_cpu = QCheckBox("Use CPU instead of GPU?", Dialog)
self.checkbox_cpu.move(30, 10)
self.checkbox_cpu.resize(320, 40)
if root['cpu']:
self.checkbox_cpu.setChecked(True)
self.checkbox_single_onnx = QCheckBox("Use single ONNX?", Dialog)
self.checkbox_single_onnx.move(30, 40)
self.checkbox_single_onnx.resize(320, 40)
if root['single_onnx']:
self.checkbox_single_onnx.setChecked(True)
self.pushButton_save = QPushButton(Dialog)
self.pushButton_save.setObjectName("pushButton_save")
self.pushButton_save.move(30, 120)
self.pushButton_save.resize(150, 35)
self.pushButton_cancel = QPushButton(Dialog)
self.pushButton_cancel.setObjectName("pushButton_cancel")
self.pushButton_cancel.move(190, 120)
self.pushButton_cancel.resize(150, 35)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.Dialog = Dialog
# connect the two functions
self.pushButton_save.clicked.connect(self.return_save)
self.pushButton_cancel.clicked.connect(self.return_cancel)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Settings", "Settings"))
self.pushButton_cancel.setText(_translate("Settings", "Cancel"))
self.pushButton_save.setText(_translate("Settings", "Save settings"))
def return_save(self):
global root
# print("save")
root['cpu'] = self.checkbox_cpu.isChecked()
root['single_onnx'] = self.checkbox_single_onnx.isChecked()
self.Dialog.close()
def return_cancel(self):
global root
# print("cancel")
self.Dialog.close()
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(560, 360)
self.move(300, 300)
self.setWindowTitle('MVSEP music separation model')
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
global root
files = [u.toLocalFile() for u in event.mimeData().urls()]
txt = ''
root['input_files'] = []
for f in files:
root['input_files'].append(f)
txt += f + '\n'
root['input_files_list_text_area'].insertPlainText(txt)
root['progress_bar'].setValue(0)
def execute_long_task(self):
global root
if len(root['input_files']) == 0 and 1:
QMessageBox.about(root['w'], "Error", "No input files specified!")
return
root['progress_bar'].show()
root['button_start'].setDisabled(True)
root['button_finish'].setDisabled(False)
root['start_proc'] = True
options = {
'input_audio': root['input_files'],
'output_folder': root['output_folder'],
'cpu': root['cpu'],
'single_onnx': root['single_onnx'],
'overlap_large': 0.6,
'overlap_small': 0.5,
}
self.update_progress(0)
self.thread = QThread()
self.worker = Worker(options)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.update_progress)
self.thread.start()
def stop_separation(self):
global root
self.thread.terminate()
root['button_start'].setDisabled(False)
root['button_finish'].setDisabled(True)
root['start_proc'] = False
root['progress_bar'].hide()
def update_progress(self, progress):
global root
root['progress_bar'].setValue(progress)
def open_settings(self):
global root
dialog = QDialog()
dialog.ui = Ui_Dialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
def dialog_select_input_files():
global root
files, _ = QFileDialog.getOpenFileNames(
None,
"QFileDialog.getOpenFileNames()",
"",
"All Files (*);;Audio Files (*.wav, *.mp3, *.flac)",
)
if files:
txt = ''
root['input_files'] = []
for f in files:
root['input_files'].append(f)
txt += f + '\n'
root['input_files_list_text_area'].insertPlainText(txt)
root['progress_bar'].setValue(0)
return files
def dialog_select_output_folder():
global root
foldername = QFileDialog.getExistingDirectory(
None,
"Select Directory"
)
root['output_folder'] = foldername + '/'
root['output_folder_line_edit'].setText(root['output_folder'])
return foldername
def create_dialog():
global root
app = QApplication(sys.argv)
w = MyWidget()
root['input_files'] = []
root['output_folder'] = os.path.dirname(os.path.abspath(__file__)) + '/results/'
root['cpu'] = False
root['single_onnx'] = False
button_select_input_files = QPushButton(w)
button_select_input_files.setText("Input audio files")
button_select_input_files.clicked.connect(dialog_select_input_files)
button_select_input_files.setFixedHeight(35)
button_select_input_files.setFixedWidth(150)
button_select_input_files.move(30, 20)
input_files_list_text_area = QTextEdit(w)
input_files_list_text_area.setReadOnly(True)
input_files_list_text_area.setLineWrapMode(QTextEdit.NoWrap)
font = input_files_list_text_area.font()
font.setFamily("Courier")
font.setPointSize(10)
input_files_list_text_area.move(30, 60)
input_files_list_text_area.resize(500, 100)
button_select_output_folder = QPushButton(w)
button_select_output_folder.setText("Output folder")
button_select_output_folder.setFixedHeight(35)
button_select_output_folder.setFixedWidth(150)
button_select_output_folder.clicked.connect(dialog_select_output_folder)
button_select_output_folder.move(30, 180)
output_folder_line_edit = QLineEdit(w)
output_folder_line_edit.setReadOnly(True)
font = output_folder_line_edit.font()
font.setFamily("Courier")
font.setPointSize(10)
output_folder_line_edit.move(30, 220)
output_folder_line_edit.setFixedWidth(500)
output_folder_line_edit.setText(root['output_folder'])
progress_bar = QProgressBar(w)
# progress_bar.move(30, 310)
progress_bar.setValue(0)
progress_bar.setGeometry(30, 310, 500, 35)
progress_bar.setAlignment(QtCore.Qt.AlignCenter)
progress_bar.hide()
root['progress_bar'] = progress_bar
button_start = QPushButton('Start separation', w)
button_start.clicked.connect(w.execute_long_task)
button_start.setFixedHeight(35)
button_start.setFixedWidth(150)
button_start.move(30, 270)
button_finish = QPushButton('Stop separation', w)
button_finish.clicked.connect(w.stop_separation)
button_finish.setFixedHeight(35)
button_finish.setFixedWidth(150)
button_finish.move(200, 270)
button_finish.setDisabled(True)
button_settings = QPushButton('⚙', w)
button_settings.clicked.connect(w.open_settings)
button_settings.setFixedHeight(35)
button_settings.setFixedWidth(35)
button_settings.move(495, 270)
button_settings.setDisabled(False)
mvsep_link = QLabel(w)
mvsep_link.setOpenExternalLinks(True)
font = mvsep_link.font()
font.setFamily("Courier")
font.setPointSize(10)
mvsep_link.move(415, 30)
mvsep_link.setText('Powered by <a href="https://mvsep.com">MVSep.com</a>')
root['w'] = w
root['input_files_list_text_area'] = input_files_list_text_area
root['output_folder_line_edit'] = output_folder_line_edit
root['button_start'] = button_start
root['button_finish'] = button_finish
root['button_settings'] = button_settings
# w.showMaximized()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
create_dialog()
效果如下:

界面虽然朴素,但相当实用,Spleeter可没给我们提供这个待遇。
Colab云端分离人声和背景音
托Google的福,我们也可以在Colab云端使用MVSEP-MDX23:
https://colab.research.google.com/github/jarredou/MVSEP-MDX23-Colab_v2/blob/v2.3/MVSep-MDX23-Colab.ipynb#scrollTo=uWX5WOqjU0QC
首先安装MVSEP-MDX23:
#@markdown #Installation
#@markdown *Run this cell to install MVSep-MDX23*
print('Installing... This will take 1 minute...')
%cd /content
from google.colab import drive
drive.mount('/content/drive')
!git clone https://github.com/jarredou/MVSEP-MDX23-Colab_v2.git &> /dev/null
%cd /content/MVSEP-MDX23-Colab_v2
!pip install -r requirements.txt &> /dev/null
# onnxruntime-gpu nightly fix for cuda12.2
!python -m pip install ort-nightly-gpu --index-url=https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/ort-cuda-12-nightly/pypi/simple/
print('Installation done !')
随后编写推理代码:
#@markdown #Separation
from pathlib import Path
import glob
%cd /content/MVSEP-MDX23-Colab_v2
input = '/content/drive/MyDrive' #@param {type:"string"}
output_folder = '/content/drive/MyDrive/output' #@param {type:"string"}
#@markdown ---
#@markdown *Bigshifts=1 to disable that feature*
BigShifts = 7 #@param {type:"slider", min:1, max:41, step:1}
#@markdown ---
overlap_InstVoc = 1 #@param {type:"slider", min:1, max:40, step:1}
overlap_VitLarge = 1 #@param {type:"slider", min:1, max:40, step:1}
#@markdown ---
weight_InstVoc = 8 #@param {type:"slider", min:0, max:10, step:1}
weight_VitLarge = 5 #@param {type:"slider", min:0, max:10, step:1}
#@markdown ---
use_VOCFT = False #@param {type:"boolean"}
overlap_VOCFT = 0.1 #@param {type:"slider", min:0, max:0.95, step:0.05}
weight_VOCFT = 2 #@param {type:"slider", min:0, max:10, step:1}
#@markdown ---
vocals_instru_only = True #@param {type:"boolean"}
overlap_demucs = 0.6 #@param {type:"slider", min:0, max:0.95, step:0.05}
#@markdown ---
output_format = 'PCM_16' #@param ["PCM_16", "FLOAT"]
if vocals_instru_only:
vocals_only = '--vocals_only true'
else:
vocals_only = ''
if use_VOCFT:
use_VOCFT = '--use_VOCFT true'
else:
use_VOCFT = ''
if Path(input).is_file():
file_path = input
Path(output_folder).mkdir(parents=True, exist_ok=True)
!python inference.py \
--large_gpu \
--weight_InstVoc {weight_InstVoc} \
--weight_VOCFT {weight_VOCFT} \
--weight_VitLarge {weight_VitLarge} \
--input_audio "{file_path}" \
--overlap_demucs {overlap_demucs} \
--overlap_VOCFT {overlap_VOCFT} \
--overlap_InstVoc {overlap_InstVoc} \
--overlap_VitLarge {overlap_VitLarge} \
--output_format {output_format} \
--BigShifts {BigShifts} \
--output_folder "{output_folder}" \
{vocals_only} \
{use_VOCFT}
else:
file_paths = sorted([f'"{glob.escape(path)}"' for path in glob.glob(input + "/*")])[:]
input_audio_args = ' '.join(file_paths)
Path(output_folder).mkdir(parents=True, exist_ok=True)
!python inference.py \
--large_gpu \
--weight_InstVoc {weight_InstVoc} \
--weight_VOCFT {weight_VOCFT} \
--weight_VitLarge {weight_VitLarge} \
--input_audio {input_audio_args} \
--overlap_demucs {overlap_demucs} \
--overlap_VOCFT {overlap_VOCFT} \
--overlap_InstVoc {int(overlap_InstVoc)} \
--overlap_VitLarge {int(overlap_VitLarge)} \
--output_format {output_format} \
--BigShifts {BigShifts} \
--output_folder "{output_folder}" \
{vocals_only} \
{use_VOCFT}
这里默认使用google云盘的目录,也可以修改为当前服务器的目录地址。
结语
MVSEP-MDX23 和 Spleeter 都是音频人声背景音分离软件,作为用户,我们到底应该怎么选择?
MVSEP-MDX23 基于 Demucs4 和 MDX 神经网络架构,可以将音乐分离成“bass”、“drums”、“vocals”和“other”四个部分。MVSEP-MDX23 在 2023 年的音乐分离挑战中获得了第三名,并且在 MultiSong 数据集上的质量比较中表现出色。它提供了 Python 命令行工具和 GUI 界面,支持 CPU 和 GPU 加速,可以在本地运行。
Spleeter 是由 Deezer 开发的开源音频分离库,它使用深度学习模型将音频分离成不同的音轨,如人声、伴奏等。Spleeter 提供了预训练的模型,可以在命令行或作为 Python 库使用。它的优势在于易用性和灵活性,可以根据需要分离不同数量的音轨。
总的来说,MVSEP-MDX23 在音频分离的性能和精度上表现出色,尤其适合需要高质量音频分离的专业用户。而 Spleeter 则更适合普通用户和开发者,因为它易于使用,并且具有更多的定制选项。
免费背景音人声分离解决方案MVSEP-MDX23,足以和Spleeter分庭抗礼的更多相关文章
- Sylius – 100% 免费和开源的电子商务解决方案
Sylius 项目提供了一个完整的电子商务解决方案.您将学习如何掌握它,帮助你在下一个项目中能够更快速的开发.Sylius 提供了一个完整的在线商店演示:demo.sylius.com. 您可能感兴趣 ...
- [RK3399] ES8316+NS4150 播放视频只有背景音,播放歌曲有的有声音,有的无声音
CPU:RK3399 系统:Android 音频IC:ES8316 功放IC:NS4150 以前也在 RK3399 上调试过 ES8316,功能都正常,新主板只是更换了功放IC,就出现无声音的问题(仔 ...
- EF core 实现读写分离解决方案
我们公司2019年web开发已迁移至.NET core,目前有部分平台随着用户量增加,单一数据库部署已经无法满足我们的业务需求,一直在寻找EF CORE读写分离解决方案,目前在各大技术论坛上还没找到很 ...
- efcore在Saas系统下多租户零脚本分表分库读写分离解决方案
efcore在Saas系统下多租户零脚本分表分库读写分离解决方案 ## 介绍 本文ShardinfCore版本x.6.0.20+ 本期主角: - [`ShardingCore`](https://gi ...
- 使用PyQt(Python+Qt)+moviepy开发的视频截取、音视频分离、MP4转GIF动图工具免费下载分享
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 在因博文素材需要将软件操作制作成动画时,发现网上相关绿色使用工具都需要 ...
- IE6中 PNG 背景透明的最佳解决方案
为什么要使用 PNG 图片? 简 单来说,使用 PNG 格式比起 GIF 来表现色彩更丰富,特别是表现渐变以及背景透明的渐变要比GIF格式出色很多.目前,最新的浏览器基本上都支持PNG格式.唯独有万恶 ...
- [开源] angularjs + Asp.net 前后端分离解决方案
本文版权归 博客园 萧秦 所有,此处为技术收藏,如有再转,请于篇头明显位置标明原创作者及出处,以示尊重! 作者:萧秦 原文:http://www.cnblogs.com/xqin/p/4862849. ...
- FFmpeg--如何同步音视频的解决方案
如何同步视频 PTS和DTS 幸运的是,音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面.音频流有采样,视频流有每秒的帧率.然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视频 ...
- 基于语音识别、音文同步、图像OCR的字幕解决方案HtwMedia介绍
背景介绍 俗话说,“好记性不如乱笔头”,这充分说明了文字归档的重要性.如今随着微信.抖音等移动端app的使用越来越广,人们生产音.视频内容也越来越便捷.而相比语音和视频而言,文字具有易存档.易检索.易 ...
- 全网首发! Odoo 订单分解执行及供应链管理免费开源解决方案
引言 前一篇介绍了佛山王朝家具的案例背景.佛山王朝家具公司在全国有30多家门店,三个生产工厂.王朝家具有六大痛点问题: 订单迫切需要实现电子化管理及在线签名 总部分单工作量大,供应链效率低 配送和售后 ...
随机推荐
- 图解 LeetCode 算法汇总——回溯
本文首发公众号:小码A梦 回溯算法是一种常见的算法,常见用于解决排列组合.排列问题.搜索问题等算法,在一个搜索空间中寻找所有的可能的解.通过向分支不断尝试获取所有的解,然后找到合适的解,找完一个分支后 ...
- IOS苹果应用IPA重签名软件手机版(苹果重签名,企业签名,安卓苹果平台,时间控制)
软件简介 IOS苹果应用IPA重签名软件手机版,可以在安卓或者苹果手机上,苹果应用IPA文件重新签名,无需MAC苹果电脑和配置XCODE开发环境,便可以直接对IPA文件进行签名,签名在本地进行,不消耗 ...
- Netty+WebSocket整合STOMP协议
1.STOMP协议简介 常用的WebSocket协议定义了两种传输信息类型:文本信息和二进制信息.类型虽然被确定,但是他们的传输体是没有规定的,也就是说传输体可以自定义成什么样的数据格式都行,只要客户 ...
- 图解 LeetCode 算法汇总——双指针
双指针算法是一种比较常用于搜索链表或数组相关的问题,很多算法的基本的解题思路就是使用暴力搜索法.而双指针是对暴力搜索的一种优化,通过双指针可以减少数据的遍历次数.通常双指针是有两个指针,叫做 ligh ...
- SQL函数count(),sum()理解
①准备-创建测试表: create table test ( id SMALLINT, name varchar(10) ); 插入数据: insert into test values(0,'张三' ...
- TopCoder 15903 EllysNim
TopCoder 15903 EllysNim(https://vjudge.net/problem/TopCoder-15903) \(n\)看似有点东西,实际上就只是一个贪心... 设\(i\)表 ...
- MySQL索引、事务与存储引擎
MySQL索引.事务与存储引擎 索引介绍 1.索引的概念 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址). 使用 ...
- Ubuntu16.04 设置jar开机自启脚本
1.编写脚本 后缀 ".sh" #! /bin/sh ### BEGIN INIT INFO # Provides: start-adb-connect-manager # Re ...
- 整理unity资料
https://www.cnblogs.com/fly-100/p/3910515.html 协同的概念介绍
- Qt源码解析——一切从QObject说起
关键词:Qt 源码 QObject 元对象 属性 事件 信号 槽 状态机 概述 原系列文章地址 学习和理解任何框架或库,官方文档可能都是最权威.最有效的信息.Qt也不例外,https://doc.qt ...