免费背景音人声分离解决方案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 则更适合普通用户和开发者,因为它易于使用,并且具有更多的定制选项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/277266.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue协同过滤算法的电影推荐系统

&#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;摘 要 “互联网”的战略实施后&a…

根据commitID删除某一次提交

1.查看提交历史 git log --prettyoneline2.找到需要删除的那个commit,然后找到上次提交的commitID 比如想要删除下面这一条 我们找到上次提交的commitID 3.执行rebase git rebase -i efa11da0a684977bf8ac047ebb803e2ded2063a4 进入编辑状态显示如下 将需要删除的那个提交前…

【小程序】如何获取特定页面的小程序码

一、进入到小程序管理后台&#xff0c;进入后点击上方的“工具”》“生成小程序码” 小程序管理后台 二、进入开发者工具&#xff0c;打开对应的小程序项目&#xff0c;复制底部小程序特定页面的路径 三、粘贴到对应位置的文本框&#xff0c;点击确定即可

three.js绘制网波浪

无图不欢&#xff0c;先上图 使用方法&#xff08;以vue3为例&#xff09; <template><div class"net" ref"net"></div> </template><script setup> import { ref, onMounted } from vue import NetAnimation from /util…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第四节 参数传递对堆栈的影响 1

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第四节 参数传递对堆栈的影响1 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理](http…

嵌入式开发——ADC开发

学习目标 了解ADC开发流程掌握采样方式能够使用ADC进行芯片内部通道进行采样能够使用ADC对外部电路进行采样学习内容 GD32F4的ADC 特点: 16个外部模拟输入通道;1个内部温度传感通道(VSENSE);1个内部参考电压输入通道(VREFINT);1个外部监测电池VBAT供电引脚输入通道。ADC开…

C++ BuilderXE10 关于Intraweb关于IWTemplateProcessorHTML1操作

1、端口设置,port参数修改端口号。 2、初始化设置成ciMultiThreaded。这样可以避免ADO组件的加载错误。 3、IWTemplateProcessorHTML1设置&#xff0c; IWForm1->LayoutMgr IWTemplateProcessorHTML1;//关联模板(IWForm1. html) IWTemplateProcessorHTML1->RenderStyles…

独立容器 Rancher Server 证书过期解决

问题 Rancher无法登录 容器报错X509&#xff1a;certificate has expired or is not ye valid 在某天需要发布新版本的时候&#xff0c;发现rancher无法登录&#xff0c;于是到服务器上查看rancher日志&#xff0c;发现以下内容&#xff1a; docker logs -f rancher --since10…

ClickHouse基础知识(五):ClickHouse的SQL 操作

基本上来说传统关系型数据库&#xff08;以 MySQL 为例&#xff09;的 SQL 语句&#xff0c;ClickHouse 基本都支持&#xff0c; 这里不会从头讲解 SQL 语法只介绍 ClickHouse 与标准 SQL&#xff08;MySQL&#xff09;不一致的地方。 1. Insert 基本与标准 SQL&#xff08;My…

探索 EndNote:卓越文献管理工具的功能与应用

引言 在当今科研与学术写作的领域&#xff0c;文献管理是每一位研究者都不可避免面对的挑战。为了有效地整理、引用和协作&#xff0c;研究者需要强大而灵活的文献管理工具。EndNote作为一款备受推崇的文献管理软件&#xff0c;在解决这一问题上发挥着关键作用。本文将深入探讨…

手写操作系统 - 汇编实现进入保护模式

前言 在了解段页门得基础上&#xff0c;理解如何从实时模式进入保护模式如何引入C语言得开发与调试 生成内核&#xff1a;汇编与C语言如何生成内核调试C语言汇编、C语言如何互调 手写64位操作系统内核规划图&#xff1a; boot程序起始0扇区&#xff0c;共占1个扇区 setup程…

数字化时代背景下服装表演创新研究

服装表演是一门独具魅力的艺术,它既高于生活,又来源于生活。这一艺术形式通过舞台上的服装、音乐、舞蹈和表演艺术家的表现力,将时尚与创意融为一体,向观众传递着独特的美感和情感。然而,如今,我们生活在一个飞速发展的数字化时代,这为服装表演的教育带来了前所未有的机遇和挑战…

NCNN环境部署及yolov5pt转ncnn模型转换推理

该内容还未完整&#xff0c;笔记内容&#xff0c;持续补充。 〇开发环境版本 vs2022 cmake3.21.1 ncnn20231027发行版 yolov5s v6.2 vunlkan1.2.198.1 Protobuf3.20.0 Opencv3.4.1 一、模型转换 yolov5s v6.2训练的pt模型&#xff0c;直接导出tourchscript&#xff0c…

帆软报表中定时调度中使用自己的短信平台,并且不使用官方商城里的模板

1 当我们在定时调度里中完成某些任务后,需要通过短信平台来发送一些短信,此时的配置界面是这样的: 此时需要帆软市场,并且短信模板只能使用帆软市场里配置的短信模板。 限制太多,使用起来非常不方便,如果我们想要使用自己的短信签名,并且使用自己的短信发送平台。那么可…

ebay会员收费吗,ebay会员活动的权益有哪些?-站斧浏览器

ebay会员收费吗&#xff1f; 是的&#xff0c;ebay会员是需要付费的。不过&#xff0c;ebay也提供了免费试用期&#xff0c;让你可以在成为正式会员之前先行体验各项权益。 试用期结束后&#xff0c;你可以选择是否继续付费成为正式会员。对于经常使用ebay购物的用户来说&…

前端使用高德api的AMap.Autocomplete无效,使用AMap.Autocomplete报错

今天需要一个坐标拾取器&#xff0c;需要一个输入框输入模糊地址能筛选的功能 查看官方文档&#xff0c;有一个api可以直接满足我们的需求 AMap.Autocomplete 上代码 AMapLoader.load({"key": "你的key", // 申请好的Web端开发者Key&#xff0c;首次调…

linux cuda环境搭建

1&#xff0c;检查驱动是否安装 运行nvidia-smi&#xff0c;如果出现如下界面&#xff0c;说明驱动已经安装 记住cuda版本号 2&#xff0c;安装cudatoolkit 上官网CUDA Toolkit Archive | NVIDIA Developer 根据操作系统选择对应的toolkit 如果已经安装了驱动&#xff0c;选…

win32 WM_MENUSELECT消息学习

之前写了一些win32的程序&#xff0c;处理菜单单击都是处理WM_COMMAND消息&#xff0c;通过 LOWORD(wParam) 获取菜单ID&#xff0c;判断单击的是哪个菜单项&#xff1b; 还有一些其他菜单消息&#xff1b; 当在菜单项中移动光标或鼠标&#xff0c;程序会收到许多WM_MENUSELEC…

Hive讲课笔记:内部表与外部表

文章目录 一、导言二、内部表1.1 什么是内部表1.1.1 内部表的定义1.1.2 内部表的关键特性 1.2 创建与操作内部表1.2.1 创建并查看数据库1.2.2 在park数据库里创建student表1.2.3 在student表插入一条记录1.2.4 通过HDFS WebUI查看数据库与表 三、外部表2.1 什么是外部表2.2 创建…

Arduino stm32 USB CDC虚拟串口使用示例

Arduino stm32 USB CDC虚拟串口使用示例 &#x1f4cd;相关篇《STM32F401RCT6基于Arduino框架点灯程序》&#x1f516;本开发环境基于VSCode PIO&#x1f33f;验证芯片&#xff1a;STM32F401RC⌛USB CDC引脚&#xff1a; PA11、 PA12&#x1f527;platformio.ini配置信息&…