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

前言

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

环境

Windows11 + VisualStudio + Qt 5.14.2

流程

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

代码如下:

Audio.h

#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

#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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值