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.mzph.cn/news/667566.shtml

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

相关文章

2024机械工程师面试题

1.常用的机械画图软件有哪些 SolidWorks、Pro/e、CATIA、UG、Creo、CAD、inventor。CAXA电子图板. 2.第一视角是___&#xff0c;第三视角是___&#xff1b; 只要区别是&#xff1a;物体所处的位置不同。一般中国都使用第一视角的。 3.气缸属于_____执行元件&#xff0c;电磁…

Scrapy:Python中强大的网络爬虫框架

Scrapy&#xff1a;Python中强大的网络爬虫框架 在当今信息爆炸的时代&#xff0c;从互联网上获取数据已经成为许多应用程序的核心需求。Scrapy是一款基于Python的强大网络爬虫框架&#xff0c;它提供了一种灵活且高效的方式来提取、处理和存储互联网上的数据。本文将介绍Scrap…

【Django开发】美多商城项目第3篇:用户注册和图片验证码开发(附代码,文档已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

小白水平理解面试经典题目LeetCode 21. Merge Two Sorted Lists【Linked List类】

21. 将两个有序列表融合 Linked List 数据结构也在面试中经常出现&#xff0c;作为很好处理客户信息存储的结构很方便&#xff0c;也是重点必会项目之一&#xff0c;看看我们如何教懂白月光&#xff0c;成功邀约看电影吧。 小白渣翻译 你将获得两个排序链表 list1 和 list2 …

ElementUI鼠标拖动没列宽度

其实 element ui 表格Table有提供给我们一个resizable属性 按官方文档上描述 它就是控制是否允许拖拽表格列大小的属性 而且 它的默认值就是 true 但是依旧很多人会反应拖拽不了 首先 表格要有边框 如果没有变宽 确实是拖拽不了 给 el-table加上 border属性 运行结果如下 但…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之MenuItem组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之MenuItem组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、MenuItem组件 TextClock组件通过文本将当前系统时间显示在设备上。支持不同时…

详解洛谷P2912 [USACO08OCT] Pasture Walking G(牧场行走)(lca模板题)

题目 思路 一道模板题&#xff0c;没啥好说的&#xff0c;直接见代码 代码 #include <bits/stdc.h> using namespace std; int n,q,a,to[100001][22],b,deep[100001],c,t[1000001]; struct ff {int id,len; }; vector<ff> vec[100001]; void dfs(int x,int fa,i…

C#实现坐标系转换

已知坐标系的向量线段AB&#xff0c;旋转指定角度后平移到达坐标AB 获取旋转角度以及新的其他坐标转换。 新建窗体应用程序CoordinateTransDemo&#xff0c;将默认的Form1重命名为FormCoordinateTrans&#xff0c;窗体设计如图&#xff1a; 窗体设计代码如下&#xff1a; 部分…

C++ 之LeetCode刷题记录(二十八)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 目标&#xff1a;执行用时击败90%以上使用 C 的用户。 144. 二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍…

Facebook群控:利用IP代理提高聊单效率

在当今社交媒体竞争激烈的环境中&#xff0c;Facebook已经成为广告营销和推广的重要平台&#xff0c;为了更好地利用Facebook进行推广活动&#xff0c;群控技术应运而生。 本文将深入探讨Facebook群控的定义、作用以及如何利用IP代理来提升群控效率&#xff0c;为你提供全面的…

Adobe Camera Raw for Mac v16.1.0中文激活版

Adobe Camera Raw for Mac是一款强大的RAW格式图像编辑工具&#xff0c;它能够处理和编辑来自各种数码相机的原始图像。以下是关于Adobe Camera Raw for Mac的一些主要特点和功能&#xff1a; 软件下载&#xff1a;Adobe Camera Raw for Mac v16.1.0中文激活版 RAW格式支持&…

微信小程序使用ucharts折线图,有负数显示0刻度线

当数据有负数和正数的时候默认不会显示0刻度线&#xff0c;不方便看出正负对比 实现思路&#xff1a;显示的刻度线是根据数据的最大值和最小值自动分配到刻度线上面&#xff0c;把最大值和最小值设置为一样&#xff0c;然后平均分配给五个刻度线中间的刻度线就会为0就实现了显…

安卓平台valgrind交叉编译

背景 通过上次的文章valgrind跨平台调试及其问题分析,为同事们在大部分平台下进行内存问题分析提供了帮助。但是也遇到了阻塞情况&#xff1a;android 平台&#xff0c;无法交叉编译通过。大家对于编译这件事&#xff0c;似乎天然有一种排斥&#xff0c;本能的拒绝&#xff0c…

qt5入门-事件

参考&#xff1a; Qt 事件(event)_w3cschool https://www.w3cschool.cn/learnroadqt/xvme1j4c.html 本地环境&#xff1a; win10专业版&#xff0c;64位 事件的概念 将事件抽象为一个对象&#xff0c;当用户发起一个行为&#xff0c;就把对应的事件加入事件队列&#xff0c;对…

云计算、Docker、K8S问题

1 云计算 云计算作为一种新兴技术&#xff0c;已经在现代社会中得到了广泛应用。它以其高效、灵活和可扩展特性&#xff0c;成为了许多企业和组织在数据处理和存储方面的首选方案。 1.1 什么是云计算&#xff1f;它有哪些特点&#xff1f; 云计算是一种通过网络提供计算资源…

大型软件编程实例分享,诊所门诊处方笺管理系统多台电脑同时使用的软件教程

大型软件编程实例分享&#xff0c;诊所门诊处方笺管理系统多台电脑同时使用的软件教程 一、前言 以下教程以 佳易王诊所门诊电子处方管理系统V17.2 为例说明 软件资源可以点击最下方官网卡片了解详情 软件左侧为导航栏 1、系统参数设置&#xff1a;可以设置打印等参数 2、…

【数据分享】1929-2023年全球站点的逐年降雪深度数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 之前我们分享过1929-2023年全球气象站点的逐年平均气温数据、逐年最高气温数据…

Debian系统显示中文

开发板上的debian默认不显示中文。 安装字体 sudo apt install fonts-wqy-zenhei 安装locals sudo apt install locales &#xff08;无必要&#xff09;设置/etc/locale.gen、设置/etc/locale.conf 运行dpkg-reconfigure locales dpkg-reconfigure locales 可以选择UT…

Sqli靶场23-->30

不知不觉鸽了几天了&#xff0c;没办法去旅游摸鱼是这样的了&#xff0c;抓紧时间来小更一下 23.过滤注释符号 先手工注入一下&#xff0c;就能发现两个单引号不报错&#xff0c;但是一旦上到注释符号的话就会报错&#xff0c;可以猜测出是对注释符号进行了过滤&#xff0c;我…

【Crypto | CTF】BUUCTF 萌萌哒的八戒

天命&#xff1a;这年头连猪都有密码&#xff0c;真是奇葩&#xff0c;怪不得我一点头绪都没有 拿到软件&#xff0c;发现是.zip的压缩包&#xff0c;打不开&#xff0c;改成7z后缀名&#xff0c;打开了 发现是一张图片 也只有下面这行东西是感觉是密码了&#xff0c;又不可能…