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

这篇具有很好参考价值的文章主要介绍了QT6调用音频输入输出(超详细)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 

目录

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

1.QAudioSource代替QAudioInput类

2.QAudioSink代替QAudioOutput类

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

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

1.AudioIputDevices类

2.AudioOutputDevices类

3.实现的AudioHandler类完整内容


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

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

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()
qsizetype bufferSize() const
qsizetype bytesAvailable() const
qint64 elapsedUSecs() const
QAudio::Error error() const
QAudioFormat format() const
bool isNull() const
qint64 processedUSecs() const
void reset()
void resume()
void setBufferSize(qsizetype value)
void setVolume(qreal volume)
void start(QIODevice *device)
QIODevice * start()
QAudio::State state() const
void stop()
void suspend()
qreal volume() const

信号

void stateChanged(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 device
            break;

        default:
            // ... other cases as appropriate
            break;
    }
}

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()
qsizetype bufferSize() const
qsizetype bytesFree() const
qint64 elapsedUSecs() const
QAudio::Error error() const
QAudioFormat format() const
bool isNull() const
qint64 processedUSecs() const
void reset()
void resume()
void setBufferSize(qsizetype value)
void setVolume(qreal volume)
void start(QIODevice *device)
QIODevice * start()
QAudio::State state() const
void stop()
void suspend()
qreal volume() const

信号

void stateChanged(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 reasons
            if (audio->error() != QAudio::NoError) {
                // Error handling
            }
            break;

        default:
            // ... other cases as appropriate
            break;
    }
}

同样可以看到这两个在上面两个类中的运用,可以自行去看看 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实现实时对讲机

效果图:

qt6 qaudiooutput,QT,音视频,qt,安卓,windows

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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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 builds

    m_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 2
    qDebug("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 it
    if (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。文章来源地址https://www.toymoban.com/news/detail-828185.html

到了这里,关于QT6调用音频输入输出(超详细)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QT6 for android 安装教程记录(版本Qt6.5.2)

    本文记录首次安装QT for andriod的详细记录。 网上的信息和资料非常多,收集和整理以及遇到的问题也各异,对新手首次接触相关开发和部署环境并不是清晰,因此,特将相关详细配置记录。 首先,开发QT for andriod 不建议使用QT5.15的版本,因为该版本不能区分相关的CPU架构,而

    2024年02月03日
    浏览(52)
  • 【QT教程】QT6物联网应用

    QT6物联网应用 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C++扩展开发视频课程 免费QT视频课程 您可以看免费1000+个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免费看 免费QT视

    2024年04月25日
    浏览(39)
  • Qt6中使用Qt Charts

     官方文档:Qt Charts 6.5.2           如果你是使用  CMake 构建的,则应在  CMakeLists.txt  中添加如下两行代码:         其中  mytarget 为你的项目名称。一共2处改动,如下截图:         改完之后,你在 .cpp 文件导入库名,就可以开始画图了。         关于怎么画出一个可

    2024年02月09日
    浏览(41)
  • 【Qt6】QStringList

    2023年10月31日,周二上午 QStringList 是 Qt 中的一个类,用于存储一组字符串。它提供了一些方便的方法来操作和管理字符串列表。 QStringList 可以用于存储任意数量的字符串,并提供了一些常用的操作,例如添加、删除、查找、排序等。它是基于 QStringList 类的 QVectorQString 的封装

    2024年02月06日
    浏览(36)
  • Qt5和Qt6的区别

    Qt4和Qt5的区别 之前有做过将项目从QT4迁移到QT5的操作,所以写了一篇QT4和Qt5的区别。在最近这一年接触过Qt6后,所以想要介绍一下Qt6和Qt5的区别,通过自己的试用感受和结合网上各路大佬的点拨,有了一个大概的了解。 各稳定版本: Qt4: 4.8.7 4.X 系列终结版本 Qt5 : 5.6 LTS 长期

    2024年01月18日
    浏览(55)
  • 【Qt6】嵌套 QWindow

    在上个世纪的文章中,老周简单介绍了 QWindow 类的基本使用——包括从 QWindow 类派生和从 QRasterWindow 类派生。 其实,QWindow 类并不是只能充当主窗口用,它也可以嵌套到父级窗口中,变成子级对象。咱们一般称之为【控件】。F 话不多讲,下面咱们用实际案例来说明。 这个例

    2024年02月02日
    浏览(36)
  • Qt6教程之一 Qt介绍及准备工作

    在正式开始之前,需要在自己电脑上面搭建好Qt的开发环境,本教程使用的Qt开发环境为Qt6.2 。 那话不多说,咋们开始做准备工作吧! 第一步:查看电脑硬件配置及操作系统 如果有一台较好配置的电脑,那么无疑用起来是最舒心的,推荐的最佳电脑配置: 由于Qt是为跨平台而

    2024年02月09日
    浏览(39)
  • 一.QT QT6.5快速安装(Windows)

    本文仅适用于初学者参考!!! 如有错误和疑问请指出  点开 下载 打开命令行: 快捷键win+r 输入cmd 输入D:qt-unified-windows-x64-4.5.1-online.exe --mirror https://mirrors.ustc.edu.cn/qtproject(因为我的安装器下在D盘所以开头是D:,其他盘相同)                               

    2023年04月25日
    浏览(48)
  • Qt6.2教程——3.Qt信号和槽

    信号和槽是Qt中一个强大的特性,用于处理对象之间的通信。它们是一种事件处理机制,允许一个对象在某个事件发生时通知另一个对象。 定义 : 信号是一个QObject的成员函数,当某个特定事件发生时,它被自动调用。它可以与一个或多个槽关联。 声明 : 在Qt类的声明中,信号

    2024年02月10日
    浏览(52)
  • Qt6 c++教程2 Qt Creator简介

    Qt Creator是Qt自带的集成开发环境(IDE),用于跨平台应用程序开发。在本章中,您将学习Qt Creator集成开发环境的基础知识,并了解集成开发环境的用户界面 (UI)。我们还将了解如何在 Qt Creator中创建和管理项目。本Qt 模块包括使用Qt Creator开发一个简单的Qt应用程序、快捷方式

    2024年02月05日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包