实时采集音频数据并使用Qt + DeepFilter进行AI实时降噪

前言

最近在做一些有关DeepFilter的开发,写了份代码,这里简单说说代码,怎么使用Qt + DeepFilter进行实时的AI音频降噪,并获得耳返

环境

Windows11 + VisualStudio + Qt 5.14.2

流程

我们大致需要两个组件,一个是AudioDevice_,用于充当QIODevice来从QAudioInpu中源源不断获取当前默认写入设备的数据流,一个是AudioOutputThread,则在不影响AudioDevice的情况下将读取到的音频数据以耳返的方式播放出来。

代码如下:

Audio.h

cpp 复制代码
#ifndef AUDIODEVICE_H
#define AUDIODEVICE_H

#include <QAudioBuffer>
#include <QApplication>
#include <QMediaPlayer>
#include <QAudioOutput>
#include <QIODevice>
#include <QAudioFormat>
#include <QTimer>
#include <QAudioInput>
#include <QAudioDeviceInfo>
#include <QQueue>
#include <QDateTime>
#include <QDebug>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include "./include/df/deep_filter.h"

class AudioOutputThread : public QThread {
	Q_OBJECT

public:
	AudioOutputThread(QMutex* mutex, QWaitCondition* condition, QObject* parent = nullptr)
		: QThread(parent), mutex(mutex), condition(condition) {
		// 初始化音频格式
		QAudioFormat format;
		format.setSampleRate(48000);
		format.setChannelCount(1);  // 单声道
		format.setSampleSize(16);
		format.setCodec("audio/pcm");
		format.setByteOrder(QAudioFormat::LittleEndian);
		format.setSampleType(QAudioFormat::SignedInt);

		// 初始化音频输出设备
		QAudioDeviceInfo outputDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
		audioOutput = new QAudioOutput(outputDeviceInfo, format);
		outputIODevice = audioOutput->start();
	}

	void run() override {
		while (true) {
			mutex->lock();
			while (buffer.size() < 4800) {
				condition->wait(mutex);
			}

			short output[4800];
			for (int i = 0; i < 4800; ++i) {
				output[i] = buffer.dequeue();
			}
			mutex->unlock();

			outputIODevice->write(reinterpret_cast<const char*>(output), sizeof(output));
		}
	}

	void enqueueData(const short* data, int size) {
		mutex->lock();
		for (int i = 0; i < size; ++i) {
			buffer.enqueue(data[i]);
		}
		condition->wakeAll();
		mutex->unlock();
	}

private:
	QQueue<short> buffer;
	QMutex* mutex;
	QWaitCondition* condition;
	QAudioOutput* audioOutput;
	QIODevice* outputIODevice;
};
#include "qdir.h"
class AudioDevice_ : public QIODevice {
	Q_OBJECT

public:
	AudioDevice_(QObject* parent = nullptr) : QIODevice(parent) {
		// 获取当前可执行文件的目录
		QString executableDir = QCoreApplication::applicationDirPath();

		// 使用QDir拼接路径
		QDir dir(executableDir);
		QString modelPath = dir.filePath("model/DeepFilterNet3_onnx.tar.gz");

		// 转换为const char*类型
		QByteArray modelPathBytes = modelPath.toLocal8Bit();
		const char* model_path = modelPathBytes.constData();
		this->df_state = df_create(model_path, 100.);

		// 初始化音频输出线程
		outputThread = new AudioOutputThread(&mutex, &condition);
		outputThread->start();
	}

	~AudioDevice_() {
		outputThread->quit();
		outputThread->wait();
		delete outputThread;
	}

	bool open(OpenMode mode) override {
		return QIODevice::open(mode | QIODevice::ReadWrite); // 使用ReadWrite模式打开
	}

	qint64 readData(char* data, qint64 maxlen) override {
		// 不需要实现,因为我们只使用writeData
		Q_UNUSED(data);
		Q_UNUSED(maxlen);
		return 0;
	}

	qint64 writeData(const char* data, qint64 len) override {
		// 将接收到的数据存入缓冲区
		const short* samples = reinterpret_cast<const short*>(data);
		qint64 sampleCount = len / sizeof(short);
		for (qint64 i = 0; i < sampleCount; ++i) {
			buffer.enqueue(samples[i]);
			if (buffer.size() >= 480) {
				// 缓冲区已满,处理数据
				short input[480];
				short output[480];
				for (int j = 0; j < 480; ++j) {
					input[j] = buffer.dequeue();
				}
				qint64 first_time = QDateTime::currentDateTime().toMSecsSinceEpoch();
				if (blnDF) {
					float snr = df_process_frame_i16(df_state, input, output);
					qint64 lastTime = QDateTime::currentDateTime().toMSecsSinceEpoch();

					// 处理后的数据可以在这里输出或传递到其他地方
					qDebug() << "Processed frame with Time " << lastTime - first_time << " ms, SNR:" << snr;
				}
				else {
					// 使用 memcpy 进行内存拷贝
					memcpy(output, input, 480 * sizeof(short));
				}
				

				// 将处理后的数据放入输出缓冲区
				if (this->blnReturn)
					outputThread->enqueueData(output, 480);
			}
		}
		return len;  // 返回写入的数据长度
	}
	/// <summary>
	/// 设置耳返
	/// </summary>
	/// <param name="blnReturn"></param>
	void setReturn(bool blnReturn) {
		this->blnReturn = blnReturn;
	}
	void setDF(bool blnDF) {
		this->blnDF = blnDF;
	}
private:
	DFState* df_state;
	QQueue<short> buffer;
	AudioOutputThread* outputThread;
	QMutex mutex;
	QWaitCondition condition;
	bool blnReturn;
	bool blnDF;
};

#endif // AUDIODEVICE_H

调用示例:mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 获取默认音频输入设备
    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();

    // 设置音频格式
    QAudioFormat format;
    format.setSampleRate(48000);
    format.setChannelCount(1);  // 使用单声道,方便处理
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    // 检查设备是否支持所设置的格式
    if (!info.isFormatSupported(format)) {
        qWarning() << "Default format not supported, trying to use the nearest.";
        format = info.nearestFormat(format);
    }

    // 创建音频输入对象
    audioInput = new QAudioInput(info, format, this);

    // 创建自定义的QIODevice来处理音频数据
    audioDevice = new AudioDevice_(this);
    audioDevice->open(QIODevice::ReadWrite);

    // 初始化 DeepFilterNet
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_2_clicked()
{
    // 停止录音
    audioInput->stop();
}

void MainWindow::on_cbx_return_clicked(bool blnchecked)
{
    audioDevice->setReturn(blnchecked);
}

void MainWindow::on_cbx_df_clicked(bool blnchecked)
{
    audioDevice->setDF(blnchecked);
}

void MainWindow::on_pushButton_clicked()
{
    // 开始录音
    audioInput->start(audioDevice);
}
相关推荐
Envyᥫᩣ2 分钟前
C#语言:从入门到精通
开发语言·c#
童先生23 分钟前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
lulu_gh_yu24 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
huanggang98224 分钟前
在Ubuntu22.04上使用Qt Creator开发ROS2项目
qt·ros2
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
老秦包你会1 小时前
Qt第三课 ----------容器类控件
开发语言·qt
凤枭香1 小时前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
ULTRA??1 小时前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
远望清一色1 小时前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself1 小时前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言