QT6调用音频输入输出(超详细)

 

目录

 一、QT6音频调用与QT5的区别

1.QAudioSource代替QAudioInput类

2.QAudioSink代替QAudioOutput类

二、音频操作中Push和Pull的区别

三、依托于Websocket实现实时对讲机

1.AudioIputDevices类

2.AudioOutputDevices类

3.实现的AudioHandler类完整内容


 本人实际是要完成一个类似于对讲机的通话小Demo,并且支持安卓,当然QT就是跨平台的,安卓的内容就不在这里叙述,后面可能会记录,功能就是两台客户端,通过网络websocket传递音频数据,做到实时通话。需要使用到QT的音频输入输出。但是网络上对QT6的音频输入输出不详细,故写此篇。

想寻找QT5实现的可以参考这些文章:

http://t.csdnimg.cn/00ABs

QT应用编程: 基于Qt设计的跨平台录音机功能 - 知乎 (zhihu.com)

 一、QT6音频调用与QT5的区别

QT5的音频输入输出调用网络上还是蛮多介绍的,这里详细介绍以及实战一下QT6的音频调用输入输出,网络上很少提到,问一些AI它们也都只会QT5的调用方法,于是还是通过自己查找资料和看官方文档,慢慢整理出来并且实现一个对讲机功能的应用,下面就先看看官方控制输出与输入的类的变化,以及范例。

1.QAudioSource代替QAudioInput类

QAudioSource Class

QAudioSource类提供了一个接口,用于从音频输入设备接收音频数据。

Header:#include <QAudioSource>
CMake:find_package(Qt6 REQUIRED COMPONENTS Multimedia)
target_link_libraries(mytarget PRIVATE Qt6::Multimedia)
qmake:QT += multimedia
Inherits:QObject

公共函数

QAudioSource(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr)
QAudioSource(const QAudioDevice &audioDevice, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr)
virtual~QAudioSource()
qsizetypebufferSize() const
qsizetypebytesAvailable() const
qint64elapsedUSecs() const
QAudio::Errorerror() const
QAudioFormatformat() const
boolisNull() const
qint64processedUSecs() const
voidreset()
voidresume()
voidsetBufferSize(qsizetype value)
voidsetVolume(qreal volume)
voidstart(QIODevice *device)
QIODevice *start()
QAudio::Statestate() const
voidstop()
voidsuspend()
qrealvolume() const

信号

voidstateChanged(QAudio::State state)

详细说明:

您可以使用系统的默认音频输入设备构建音频输入。也可以使用特定的QAudioDevice创建QAudioSource。创建音频输入时,还应发送用于录制的QAudioFormat(有关详细信息,请参阅QAudioFormat类描述)。

 

要录制到文件,请执行以下操作:

 

QAudioSource允许您使用音频输入设备录制音频。此类的默认构造函数将使用系统默认音频设备,但您也可以为特定设备指定QAudioDevice。您还需要传入要录制的QAudioFormat。启动QAudioSource只需在打开QIODevice的情况下调用start()

​
QFile destinationFile;   // Class member
QAudioSource* audio; // Class member
{destinationFile.setFileName("/tmp/test.raw");destinationFile.open( QIODevice::WriteOnly | QIODevice::Truncate );QAudioFormat format;// Set up the desired format, for example:format.setSampleRate(8000);format.setChannelCount(1);format.setSampleFormat(QAudioFormat::UInt8);QAudioDevice info = QMediaDevices::defaultAudioInput();if (!info.isFormatSupported(format)) {qWarning() << "Default format not supported, trying to use the nearest.";}audio = new QAudioSource(format, this);connect(audio, &QAudioSource::stateChanged, this, &AudioInputExample::handleStateChanged);QTimer::singleShot(3000, this, &AudioInputExample::stopRecording);audio->start(&destinationFile);// Records audio for 3000ms
}
​

如果输入设备支持指定的格式,这将开始录制(您可以使用QAudioDevice::isFormatSupported()进行检查。如果出现任何问题,请使用error()函数检查出了什么问题。我们在stopRecording()插槽中停止录制。

void AudioInputExample::stopRecording()
{audio->stop();destinationFile.close();delete audio;
}

在任何时间点,QAudioSource都将处于四种状态之一:活动、挂起、停止或空闲。这些状态由QAudio::State枚举指定。您可以直接通过suspend()、resume(),stop(),reset()和start()请求状态更改。当前状态由state()报告。当状态发生变化时,QAudioSink也会向您发出信号(stateChanged())。
QAudioSource提供了几种测量录制开始()后经过的时间的方法。processedUSecs()函数返回以微秒为单位写入的流的长度,即,它忽略了音频输入暂停或空闲的时间。elapsedUSecs()函数返回自调用start()以来经过的时间,无论QAudioSource处于何种状态。
如果出现错误,可以使用error()获取其原因。可能的错误原因由QAudio::error枚举描述。遇到错误时,QAudioSource将进入StoppedState。连接到stateChanged()信号以处理错误:

void AudioInputExample::handleStateChanged(QAudio::State newState)
{switch (newState) {case QAudio::StoppedState:if (audio->error() != QAudio::NoError) {// Error handling} else {// Finished recording}break;case QAudio::ActiveState:// Started recording - read from IO devicebreak;default:// ... other cases as appropriatebreak;}
}

2.QAudioSink代替QAudioOutput类

QAudioSink Class

QAudioSink类提供了一个接口,用于将音频数据发送到音频输出设备。

Header:#include <QAudioSink>
CMake:find_package(Qt6 REQUIRED COMPONENTS Multimedia)
target_link_libraries(mytarget PRIVATE Qt6::Multimedia)
qmake:QT += multimedia
Inherits:QObject

公共函数

QAudioSink(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr)
QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr)
virtual~QAudioSink()
qsizetypebufferSize() const
qsizetypebytesFree() const
qint64elapsedUSecs() const
QAudio::Errorerror() const
QAudioFormatformat() const
boolisNull() const
qint64processedUSecs() const
voidreset()
voidresume()
voidsetBufferSize(qsizetype value)
voidsetVolume(qreal volume)
voidstart(QIODevice *device)
QIODevice *start()
QAudio::Statestate() const
voidstop()
voidsuspend()
qrealvolume() const

信号

voidstateChanged(QAudio::State state)

详细描述


您可以使用系统的默认音频输出设备构建音频输出。也可以使用特定的QAudioDevice创建QAudioSink。创建音频输出时,还应发送用于播放的QAudioFormat(有关详细信息,请参阅QAudioFormat类描述)。
播放文件:
开始播放音频流只需使用QIODevice调用start()即可。然后,QAudioSink将从io设备中获取所需的数据。因此,播放音频文件非常简单:

QFile sourceFile;   // class member.
QAudioSink* audio; // class member.
{sourceFile.setFileName("/tmp/test.raw");sourceFile.open(QIODevice::ReadOnly);QAudioFormat format;// Set up the format, eg.format.setSampleRate(8000);format.setChannelCount(1);format.setSampleFormat(QAudioFormat::UInt8);QAudioDevice info(QMediaDevices::defaultAudioOutput());if (!info.isFormatSupported(format)) {qWarning() << "Raw audio format not supported by backend, cannot play audio.";return;}audio = new QAudioSink(format, this);connect(audio, QAudioSink::stateChanged, this, &AudioInputExample::handleStateChanged);audio->start(&sourceFile);
}
​

​假设音频系统和输出设备支持该文件,则该文件将开始播放。如果运气不好,请检查error()函数的情况。
文件播放完毕后,我们需要停止设备:

void AudioOutputExample::stopAudioOutput()
{audio->stop();sourceFile.close();delete audio;
}

​在任何给定时间,QAudioSink都将处于四种状态之一:活动、暂停、停止或空闲。这些状态由QAudio::State枚举描述。状态变化通过stateChanged()信号报告。例如,您可以使用此信号来更新应用程序的GUI;这里常见的例子是更改播放/暂停按钮的状态。您可以使用suspend()、stop()、reset()、resume()和start()直接请求状态更改。
如果发生错误,可以使用error()函数获取错误类型。有关报告的可能错误的描述,请参阅QAudio::Error枚举。当遇到QAudio::UnderrunError时,状态将变为QAudio::IdleState,当遇到另一个错误时,状态变为QAaudio::StoppedState。您可以通过连接到stateChanged()信号来检查错误:

void AudioOutputExample::handleStateChanged(QAudio::State newState)
{switch (newState) {case QAudio::IdleState:// Finished playing (no more data)AudioOutputExample::stopAudioOutput();break;case QAudio::StoppedState:// Stopped for other reasonsif (audio->error() != QAudio::NoError) {// Error handling}break;default:// ... other cases as appropriatebreak;}
}

同样可以看到这两个在上面两个类中的运用,可以自行去看看 QAudioSource and QAudioDevice.

看到这里有些同志已经会了,上面的范例主要就是示范对于音频文件的输入输出,加载在设备上就有了录音和读文件的功能。但是我要实现的实时对讲机不是这样的,不需要记录为文件,所以我要生成pcm格式的二进制数据然后传入传出,这里如果想了解音频格式的,或者是对音频格式有要求的可以去了解一下这些方面。

QT生成的音频数据格式

QT播放音频文件

FFMPEG音频库的引入

FFMPEG库对音频数据的转码

二、音频操作中Push和Pull的区别

网络上很多博主都没有说清楚甚至没有说其实输入和输出都有两种方法,就是Push和Pull方式,要根据实际功能选择使用,而且不要弄混了,我在项目中使用的时候,输入是用的push方式,输出用的pull方式,实现的是实时对讲机,它们有以下区别:

在Qt的QIODevice及其派生类中,有两种常见的数据读取和写入方式:pushpull。这两种方式是用于描述数据流如何被传输的。

  1. Push 模式:

    • 概念: 在 Push 模式中,数据的生产者(producer)主动推送数据到消费者(consumer)。生产者生成数据并将其推送到消费者。
    • 例子: 一个网络套接字(QTcpSocket)可以使用 Push 模式,当有新数据到达时,套接字发射 readyRead 信号,告知应用程序有数据可读。
    • 使用场景: 当数据的生成速率相对较快或者生产者的数据产生是不规律的时候,Push 模式通常更为合适。
  2. Pull 模式:

    • 概念: 在 Pull 模式中,数据的消费者主动从数据源拉取(pull)数据。消费者主动发起请求以获取数据。
    • 例子: 文件I/O 操作通常是 Pull 模式,你需要调用 read 函数来从文件中拉取数据。
    • 使用场景: 当数据的生成速率相对较慢或者数据生成是规律的时候,Pull 模式通常更为合适。

在 Qt 中,QIODevicereadwrite 方法是 Pull 模式的典型例子,而 QIODevicereadyRead 信号则是 Push 模式的例子。QIODevice 实际上可以同时支持 Push 和 Pull 操作。

在 Qt 中,QIODevice 是一个抽象类,而具体的实现类如 QFileQTcpSocket 等,根据其用途,可能更倾向于其中一种方式。你在使用这些类时,可以根据具体的需求选择适当的模式。

三、依托于Websocket实现实时对讲机

效果图:

WebSocket部分主要就是通过QT自带的WebSocket然后利用网络服务器帮着传输音频数据,就不贴出来了,可以用其他任何方式替代,主要是对音频处理的代码我会贴出。

1.AudioIputDevices类

实现了对音频输入的设备数据控制。

#include <QAudioSource>
#include <QMediaDevices>#include <QComboBox>
#include <QPushButton>
#include <QSlider>
#include <QWidget>#include <QPixmap>#include <QByteArray>
#include <QScopedPointer>class AudioIputDevices : public QIODevice
{Q_OBJECTpublic:AudioIputDevices(const QAudioFormat &format);void start();void stop();qreal level() const { return m_level; }qint64 readData(char *data, qint64 maxlen) override;qint64 writeData(const char *data, qint64 len) override;qreal calculateLevel(const char *data, qint64 len) const;signals:void levelChanged(qreal level);void signalInputAudioBytearrayData(const char *data, qint64 len);
private:const QAudioFormat m_format;qreal m_level = 0.0; // 0.0 <= m_level <= 1.0
};#include <QAudioDevice>
#include <QAudioSource>
#include <QDateTime>
#include <QDebug>
#include <QLabel>
#include <QPainter>
#include <QVBoxLayout>
#include <QtEndian>#if QT_CONFIG(permissions)
#include <QCoreApplication>
#include <QPermission>
#endif#include <math.h>
#include <stdlib.h>AudioIputDevices::AudioIputDevices(const QAudioFormat &format) : m_format(format) { }void AudioIputDevices::start()
{open(QIODevice::WriteOnly);
}void AudioIputDevices::stop()
{close();
}qint64 AudioIputDevices::readData(char * /* data */, qint64 /* maxlen */)
{return 0;
}qreal AudioIputDevices::calculateLevel(const char *data, qint64 len) const
{const int channelBytes = m_format.bytesPerSample();const int sampleBytes = m_format.bytesPerFrame();const int numSamples = len / sampleBytes;float maxValue = 0;auto *ptr = reinterpret_cast<const unsigned char *>(data);for (int i = 0; i < numSamples; ++i) {for (int j = 0; j < m_format.channelCount(); ++j) {float value = m_format.normalizedSampleValue(ptr);maxValue = qMax(value, maxValue);ptr += channelBytes;}}return maxValue;
}qint64 AudioIputDevices::writeData(const char *data, qint64 len)
{m_level = calculateLevel(data, len);emit signalInputAudioBytearrayData(data, len);emit levelChanged(m_level);return len;
}

 音量实时显示条

class RenderArea : public QWidget
{Q_OBJECTpublic:explicit RenderArea(QWidget *parent = nullptr);
public slots:void setLevel(qreal value);protected:void paintEvent(QPaintEvent *event) override;private:qreal m_level = 0;
};RenderArea::RenderArea(QWidget *parent) : QWidget(parent)
{setBackgroundRole(QPalette::Base);setAutoFillBackground(true);setMinimumHeight(30);setMinimumWidth(200);
}void RenderArea::paintEvent(QPaintEvent * /* event */)
{QPainter painter(this);painter.setPen(Qt::black);const QRect frame = painter.viewport() - QMargins(10, 10, 10, 10);painter.drawRect(frame);if (m_level == 0.0)return;const int pos = qRound(qreal(frame.width() - 1) * m_level);painter.fillRect(frame.left() + 1, frame.top() + 1, pos, frame.height() - 1, Qt::red);
}void RenderArea::setLevel(qreal value)
{m_level = value;update();
}

2.AudioOutputDevices类

实现对音频输出的设备数据控制。

#include <QAudioSink>
#include <QByteArray>
#include <QComboBox>
#include <QIODevice>
#include <QLabel>
#include <QMainWindow>
#include <QMediaDevices>
#include <QObject>
#include <QPushButton>
#include <QScopedPointer>
#include <QSlider>
#include <QTimer>class AudioOutputDevices : public QIODevice
{Q_OBJECTpublic:AudioOutputDevices(const QAudioFormat &format, qint64 durationUs, int sampleRate);void start();void stop();qint64 readData(char *data, qint64 maxlen) override;qint64 writeData(const char *data, qint64 len) override;qint64 bytesAvailable() const override;qint64 size() const override { return m_buffer.size(); }private:void generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate);private:qint64 m_pos = 0;QByteArray m_buffer;
};#include <QAudioDevice>
#include <QAudioSink>
#include <QDebug>
#include <QVBoxLayout>
#include <QtEndian>
#include <QtMath>AudioOutputDevices::AudioOutputDevices(const QAudioFormat &format, qint64 durationUs, int sampleRate)
{if (format.isValid())generateData(format, durationUs, sampleRate);
}void AudioOutputDevices::start()
{open(QIODevice::ReadOnly);
}void AudioOutputDevices::stop()
{m_pos = 0;close();
}void AudioOutputDevices::generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate)
{const int channelBytes = format.bytesPerSample();const int sampleBytes = format.channelCount() * channelBytes;qint64 length = format.bytesForDuration(durationUs);Q_ASSERT(length % sampleBytes == 0);Q_UNUSED(sampleBytes); // suppress warning in release buildsm_buffer.resize(length);unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data());int sampleIndex = 0;while (length) {// Produces value (-1..1)const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex++ % format.sampleRate())/ format.sampleRate());for (int i = 0; i < format.channelCount(); ++i) {switch (format.sampleFormat()) {case QAudioFormat::UInt8:*reinterpret_cast<quint8 *>(ptr) = static_cast<quint8>((1.0 + x) / 2 * 255);break;case QAudioFormat::Int16:*reinterpret_cast<qint16 *>(ptr) = static_cast<qint16>(x * 32767);break;case QAudioFormat::Int32:*reinterpret_cast<qint32 *>(ptr) =static_cast<qint32>(x * std::numeric_limits<qint32>::max());break;case QAudioFormat::Float:*reinterpret_cast<float *>(ptr) = x;break;default:break;}ptr += channelBytes;length -= channelBytes;}}
}qint64 AudioOutputDevices::readData(char *data, qint64 len)
{qint64 total = 0;
//    if (!m_buffer.isEmpty()) {
//        // qDebug() << "!m_buffer.isEmpty()" << m_buffer ;
//        while (len - total > 0) {
//            const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total);
//            memcpy(data + total, m_buffer.constData() + m_pos, chunk);
//            m_pos = (m_pos + chunk) % m_buffer.size();
//            total += chunk;
//        }
//    }return total;
}qint64 AudioOutputDevices::writeData(const char *data, qint64 len)
{Q_UNUSED(data);Q_UNUSED(len);return 0;
}qint64 AudioOutputDevices::bytesAvailable() const
{return m_buffer.size() + QIODevice::bytesAvailable();
}

3.实现的AudioHandler类完整内容

这个类处理了开始讲话和停止讲话,接收WebSocket传来的音频数据

#ifndef AUDIOHANDLER_H
#define AUDIOHANDLER_H#include <QObject>
#include <QCoreApplication>
#include <QtWebSockets/QWebSocket>
#include <QBuffer>
#include <QAudio>     //这五个是QT处理音频的库
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include <QIODevice>
#include <QThread>
#include <QAudioSource>
#include <QAudioSink>
#include <QTimer>
#include <QFile>
#include <QMediaDevices>
#include "datahandle.h"
#include "audioiputdevices.h"
#include "audiooutputdevices.h"
class AudioHandler : public QObject
{Q_OBJECT
public:explicit AudioHandler(QObject *parent = nullptr);~AudioHandler();
public slots:void onStartTalking();void onStopTalking();// void deviceChanged(QAudioDevice device, int index);void onAudioDataformWebsocket(const QByteArray& audioOutputData);
private slots:void processAudioData(const QByteArray &data);void onInputNotify();void onOutputNotify();private:void initializeInputAudio(const QAudioDevice &deviceInfo);void initializeOutPutAudio(const QAudioDevice &deviceInfo);void startAudioInput();void stopAudioInput();void startAudioOutput();void stopAudioOutput();private:QScopedPointer<AudioIputDevices> inputDevice;QScopedPointer<AudioOutputDevices> outputDevice;QIODevice *m_output;QAudioFormat inputAudioFormat;QAudioFormat outputAudioFormat;QScopedPointer<QAudioSource> audioInputsource;QScopedPointer<QAudioSink> audioOutputsource;// QAudioDevice inputDevice;// QAudioDevice outputAudioDevice;QMediaDevices *m_inputMediaDevices;QMediaDevices *m_outputMediaDevices;QTimer *m_inputTimer;QTimer *m_outputTimer;QTimer *m_pushTimer;QBuffer *m_audioBuffer;DataHandle m_dataHandle;QByteArray m_audioByteArrayData;QByteArray m_audioOutputByteArryaData;
signals:void signalSendData(const QByteArray& data);void signalRequestTalk(CMDTYPE cmdtype);void signalAudioLevel(qreal value);
};#endif // AUDIOHANDLER_H
#include "audiohandler.h"
#include <QDebug>
AudioHandler::AudioHandler(QObject *parent):QObject(parent), audioInputsource(nullptr), audioOutputsource(nullptr),m_outputMediaDevices(new QMediaDevices(this)), m_inputTimer(new QTimer(this)), m_outputTimer(new QTimer(this)),m_pushTimer(new QTimer(this))
{for(int i = 0; i < QMediaDevices::audioInputs().count(); ++i){auto aa = QMediaDevices::audioInputs().at(i);qDebug() << "音频输入:" << aa.description();}for(int i = 0; i < QMediaDevices::audioOutputs().count(); ++i){auto aa = QMediaDevices::audioOutputs().at(i);qDebug() << "音频输出:" << aa.description();}initializeInputAudio(QMediaDevices::defaultAudioInput());//    initializeOutPutAudio(m_outputMediaDevices->audioOutputs().at(2));initializeOutPutAudio(m_outputMediaDevices->defaultAudioOutput());connect(m_inputTimer, &QTimer::timeout, this, &AudioHandler::onInputNotify);connect(inputDevice.data(), &AudioIputDevices::levelChanged, [=](qreal value){//        qDebug() << "emit signalAudioLevel(value);" << value;emit signalAudioLevel(value);});connect(m_outputTimer, &QTimer::timeout, this, &AudioHandler::onOutputNotify);onStopTalking();
}AudioHandler::~AudioHandler()
{if(m_outputTimer){m_outputTimer->deleteLater();m_outputTimer = nullptr;}if(m_inputTimer){m_inputTimer->deleteLater();m_inputTimer = nullptr;}
}void AudioHandler::initializeInputAudio(const QAudioDevice &deviceInfo)
{//设置录音的格式inputAudioFormat.setSampleRate(8000); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.inputAudioFormat.setChannelCount(1);   //将通道数设置为通道。// audioFormat.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/inputAudioFormat.setSampleFormat(QAudioFormat::Int16);// audioFormat.setCodec("audio/pcm"); //设置编码格式// audioFormat.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序// audioFormat.setSampleType(QAudioFormat::SignedInt); //样本类型// ChannelConfigStereo is 2, Int16 is 2qDebug("sampleRate: %d, channelCount: %d, sampleFormat: %d",inputAudioFormat.sampleRate(), inputAudioFormat.channelCount(), inputAudioFormat.sampleFormat());inputDevice.reset(new AudioIputDevices(inputAudioFormat));audioInputsource.reset(new QAudioSource(deviceInfo, inputAudioFormat));connect(inputDevice.data(), &AudioIputDevices::signalInputAudioBytearrayData, [=](const char *data, qint64 len){qDebug() << "m_audioByteArrayData start:" << "m_audioByteArrayData size:" << m_audioByteArrayData.size() ;QByteArray aa(data, len);m_audioByteArrayData.append(aa);qDebug() << "m_audioByteArrayData final:" << "m_audioByteArrayData size:" << m_audioByteArrayData.size() ;});}void AudioHandler::initializeOutPutAudio(const QAudioDevice &deviceInfo)
{qDebug() << "outputAudioDevice音频输出:" << deviceInfo.description();outputAudioFormat = deviceInfo.preferredFormat();outputAudioFormat.setSampleRate(8000); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.outputAudioFormat.setChannelCount(1);   //将通道数设置为通道。// audioFormat.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/outputAudioFormat.setSampleFormat(QAudioFormat::Int16);if(!deviceInfo.isFormatSupported(outputAudioFormat)){qWarning() << "not Support fromat";}qDebug("sampleRate: %d, channelCount: %d, sampleFormat: %d",outputAudioFormat.sampleRate(), outputAudioFormat.channelCount(), outputAudioFormat.sampleFormat());const int durationSeconds = 1;const int toneSampleRateHz = 600;outputDevice.reset(new AudioOutputDevices(outputAudioFormat, durationSeconds * 1000000, toneSampleRateHz));audioOutputsource.reset(new QAudioSink(deviceInfo, outputAudioFormat));}void AudioHandler::processAudioData(const QByteArray &data)
{emit signalSendData(data);
}void AudioHandler::onOutputNotify()
{if(m_audioOutputByteArryaData.size() == 0)return;qDebug() << "m_audioOutputByteArryaData大小:" << m_audioOutputByteArryaData.size();//        auto io = audioOutputsource->start();//        int len = audioOutputsource->bytesFree();//        qDebug() << "len:" << len;//        len = io->write(m_audioOutputByteArryaData.data(), m_audioOutputByteArryaData.size());int len = audioOutputsource->bytesFree();qDebug() << "len:" << len;len = m_output->write(m_audioOutputByteArryaData.data(), m_audioOutputByteArryaData.size());m_audioOutputByteArryaData.clear();}void AudioHandler::onInputNotify()
{// Read available audio input data and send itif (inputDevice){//        QByteArray audioData = inputDevice->readAll();// qDebug() << "audioData" << audioData;// QFile file("output.pcm");// if(!file.open(QIODevice::WriteOnly | QIODevice::Append))// {//     qDebug() << "unable to open file";// }//  qDebug() << "file.write";// file.write("aaaaaaaaa");// file.write(audioData.data(), audioData.size());// file.close();QByteArray adtsHeaders = m_dataHandle.createADTSHeader(m_audioByteArrayData.size() + 7);qDebug() << "onInputNotify" << "m_audioByteArrayData size:" << m_audioByteArrayData.size() ;processAudioData(m_audioByteArrayData);m_audioByteArrayData.clear();}}void AudioHandler::onStartTalking()
{qDebug() << "onStartTalking";stopAudioOutput();startAudioInput();emit signalRequestTalk(CMDTYPE::CMDTALK);}void AudioHandler::onStopTalking()
{qDebug() << "onStopTalking";stopAudioInput();startAudioOutput();emit signalRequestTalk(CMDTYPE::CMDSTOPTALK);// if(m_timer)// {//     m_timer->stop();//     delete m_timer;//     m_timer = nullptr;// }
}void AudioHandler::onAudioDataformWebsocket(const QByteArray &audioOutputData)
{m_audioOutputByteArryaData.append(audioOutputData);qDebug() << "onAudioDataformWebsocket" << "m_audioByteArrayData size:" << m_audioOutputByteArryaData.size() ;
}void AudioHandler::startAudioInput()
{if (audioInputsource){inputDevice->start();audioInputsource->start(inputDevice.data());m_inputTimer->start(10);// connect(inputDevice, &QIODevice::readyRead, this, &AudioHandler::onNotify);}
}void AudioHandler::stopAudioInput()
{if (audioInputsource){m_inputTimer->stop();audioInputsource->stop();}
}void AudioHandler::startAudioOutput()
{qDebug() << "onstartListening";outputDevice->start();m_output = audioOutputsource->start();m_outputTimer->start(10);//        QFile file("clip_0002.wav");//        audioOutputsource->start(&file);
}void AudioHandler::stopAudioOutput()
{qDebug() << "onstopListening";m_outputTimer->stop();outputDevice->stop();audioOutputsource->stop();
}// void AudioHandler::deviceChanged(QAudioDevice device, int index)
// {
//     outputDevice->stop();
//     audioOutputsource->stop();
//     audioOutputsource->disconnect(this);
//     initializeOutPutAudio(device);
// }

以上就是AudioHandler类通过调用AudioIputDevices和AudioOutputDevices两个类来完成音频输入和输出的核心类代码,这样大家应该就知道该如何在QT6中使用了。

若有疑问可留言评论。thanks。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Open CASCADE学习|分割曲线

1、通过参数进行分割 分别获得曲线的 FirstParameter 和 LastParameter &#xff0c;然后对参数进行分割&#xff0c;获得n个ui&#xff0c;并对每个ui调用D0&#xff08;获得这个点的坐标值&#xff09;或D1&#xff08;获得这个点的坐标值和切向量&#xff09;。这个方法的优…

海康IPC摄像机接入国标平台,发现一直不在线(离线)的处理方式

目 录 一、问题 二、问题分析 &#xff08;一&#xff09;常见设备离线问题的原因 &#xff08;二&#xff09;原因分析 三、问题查处 &#xff08;一&#xff09;设备端排查故障&#xff08;设备端自查&#xff09; 1、检查GB28181参数配置是否有误 2、…

【LeetCode: 292. Nim 游戏+ 博弈问题】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

算法——递归与搜索算法

1. 递归 ①什么是递归&#xff1f; 官方一点来说 递归指的是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法。 通俗一点来说&#xff0c;递归就是一个函数自己调用自己的过程 ②什么情况下会用到递归&#xff1f; 我们在遇见一个问题的时候&#xff0c;我怎…

ffmpeg操作实战001:视频+音频文件融合

一、功能需求 把视频文件video.mp4 和音频文件audio.wav融合在一起&#xff0c;输出视频文件output.mp4 二、操作指令 ffmpeg -i video.mp4 -i audio.wav -c:v copy -map 0:v:0 -map 1:a:0 output.mp4 三、参数说明 ffmpeg: 这是用于执行FFmpeg命令行工具的命令。-i video…

深度学习(生成式模型)—— Consistency Models

文章目录 前言预备知识&#xff1a;SDE与ODEMethod实验结果 前言 Diffusion model需要多次推断才能生成最终的图像&#xff0c;这将耗费大量的计算资源。前几篇博客我们已经介绍了加速Diffusion model生成图像速率的DDIM和Stable Diffusion&#xff0c;本节将介绍最近大火的Co…

HarmonyOS 鸿蒙应用开发(九、还是蓝海,如何贡献第三方库)

快来共享第三方库吧&#xff0c;不但可以通过分享自己的成果&#xff0c;可以获得来自全球开发者的技术反馈和建议&#xff0c;提升自身技术能力&#xff0c;还有助于提高个人或团队在开源社区中的知名度和影响力。在流量时代和粉丝经济时代&#xff0c;获得曝光度和流量密码。…

Maven工程的配置及使用

一、Maven章节 Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建和依赖管理支持的工具 1.1、maven的作用 1&#xff09;依赖管理&#xff1a; 方便快捷的管理项目依赖的资源包&#xff08;jar包&#xff09;避免版本冲突 2&#xff09;统一项目结构&…

初识C语言·编译与链接

1 翻译环境和运行环境 C语言标准ANSI C 实现C语言代码的时候 一般需要经过两种环境&#xff0c;一是翻译环境&#xff0c;二是运行环境&#xff0c;计算机能识别的是二进制的指令&#xff0c;人写完代码后通过翻译环境&#xff0c;使代码变成计算机能读懂的可执行的机器指令&a…

go-redis hash slot 之旅

搭建redis 集群 创建一个网桥 docker network create -d bridge --subnet192.168.148.0/24 --gateway192.168.148.1 -o parenteno1 redis-net通过docker 文件创建redis 集群&#xff0c; 这里注意要不要使用redis 7以上的版本&#xff0c;不然会出问题 version: "3&quo…

ArcGIS学习(三)数据可视化

ArcGIS学习(三)数据可视化 1.矢量数据可视化 需要提前说明的是,在ArcGIS中,所有的可视化选项设置都是在“图层属性”对话框里面的“符号系统”中实现的。 对于矢量数据的可视化,主要有四种可视化方式: 按“要素”可视化按“类别”可视化按“数量”可视化按“图表”可视…

最近宣布的NIST后量子密码学标准的3个关键要点

当今世界依赖于许多保护措施&#xff0c;即使您没有注意到这一点。从手机和智能技术到网站&#xff0c;从支付交易到城市基础设施&#xff0c;人们经常与之互动的一切&#xff0c;都通过保护和检查技术来保护。量子计算机能够快速轻松地打破这些保护措施&#xff0c;这是政府和…