Qt文档阅读笔记-Audio Example解析

x33g5p2x  于2022-07-26 转载在 其他  
字(3.7k)|赞(0)|评价(0)|浏览(470)

这个例子很有意思,今天分析了下,在此记录下笔记,方便下次查阅。

这个例子展示了画动态数据(麦克风输入的数据)

界面如下:

关键文件如下:

这里官方给出的解释就没了,后面我自己补充下。

首先要学习两个类,分别是:QAudioInput和QAudioFormat

具体可以查本人的这篇博文-Qt文档阅读笔记-QAudioInput&QAudioFormat解析与实例。

首先在main.cpp中的关键代码:

const QAudioDeviceInfo inputDevice = QAudioDeviceInfo::defaultInputDevice();
if (inputDevice.isNull()) {
    QMessageBox::warning(nullptr, "audio",
                         "There is no audio input device available.");
    return -1;
}

这里查询了当前系统中是否设置了麦克风,如果没有,就直接退出了。

在Widget.cpp中配置了QAudioFormat相关属性,如下:

QAudioFormat formatAudio;
formatAudio.setSampleRate(8000);
formatAudio.setChannelCount(1);
formatAudio.setSampleSize(8);
formatAudio.setCodec("audio/pcm");
formatAudio.setByteOrder(QAudioFormat::LittleEndian);
formatAudio.setSampleType(QAudioFormat::UnSignedInt);

| 参数 | 描述 |
| Sample Rate | 每秒音频的Hertz |
| Number of channels | 单声道还是双声道 |
| Sample size | 数据按8位存还是16位存 |
| Sample type | 存储的类型是int还是unsigned int还是float |
| Byte order | 存成大端还是小端 |

最后说下最有意思的类XYSeriesIODevice。对应的

xyseriesiodevice.h:

#ifndef XYSERIESIODEVICE_H
#define XYSERIESIODEVICE_H

#include <QtCore/QIODevice>
#include <QtCore/QPointF>
#include <QtCore/QVector>
#include <QtCharts/QChartGlobal>

QT_CHARTS_BEGIN_NAMESPACE
class QXYSeries;
QT_CHARTS_END_NAMESPACE

QT_CHARTS_USE_NAMESPACE

class XYSeriesIODevice : public QIODevice
{
    Q_OBJECT
public:
    explicit XYSeriesIODevice(QXYSeries *series, QObject *parent = nullptr);

    static const int sampleCount = 2000;

protected:
    qint64 readData(char *data, qint64 maxSize) override;
    qint64 writeData(const char *data, qint64 maxSize) override;

private:
    QXYSeries *m_series;
    QVector<QPointF> m_buffer;
};

#endif // XYSERIESIODEVICE_H

xyseriesiodevice.cpp:

#include "xyseriesiodevice.h"

#include <QtCharts/QXYSeries>
#include <QDebug>

XYSeriesIODevice::XYSeriesIODevice(QXYSeries *series, QObject *parent) :
    QIODevice(parent),
    m_series(series)
{
}

qint64 XYSeriesIODevice::readData(char *data, qint64 maxSize)
{
    Q_UNUSED(data)
    Q_UNUSED(maxSize)
    return -1;
}

qint64 XYSeriesIODevice::writeData(const char *data, qint64 maxSize)
{
    static const int resolution = 4;
    if (m_buffer.isEmpty()) {
        m_buffer.reserve(sampleCount);
        for (int i = 0; i < sampleCount; ++i)
            m_buffer.append(QPointF(i, 0));
    }

    int start = 0;
    const int availableSamples = int(maxSize) / resolution;

    if (availableSamples < sampleCount) {
        start = sampleCount - availableSamples;
        for (int s = 0; s < start; ++s)
            m_buffer[s].setY(m_buffer.at(s + availableSamples).y());
    }

    for (int s = start; s < sampleCount; ++s, data += resolution)
        m_buffer[s].setY(qreal(uchar(*data) - 128) / qreal(128));

    m_series->replace(m_buffer);
    return (sampleCount - start) * resolution;
}

这里最关键的代码是这个:

qint64 XYSeriesIODevice::writeData(const char *data, qint64 maxSize)
{
    static const int resolution = 4;
    if (m_buffer.isEmpty()) {
        m_buffer.reserve(sampleCount);
        for (int i = 0; i < sampleCount; ++i)
            m_buffer.append(QPointF(i, 0));
    }

    int start = 0;
    const int availableSamples = int(maxSize) / resolution;

    if (availableSamples < sampleCount) {
        start = sampleCount - availableSamples;
        for (int s = 0; s < start; ++s)
            m_buffer[s].setY(m_buffer.at(s + availableSamples).y());
    }

    for (int s = start; s < sampleCount; ++s, data += resolution)
        m_buffer[s].setY(qreal(uchar(*data) - 128) / qreal(128));

    m_series->replace(m_buffer);
    return (sampleCount - start) * resolution;
}

整个曲线x轴长度是:

static const int sampleCount = 2000;

y轴长度:

axisY->setRange(-1, 1);

音频大小是:

formatAudio.setSampleType(QAudioFormat::UnSignedInt);

这个函数data每次会传320个字节过来,换成UnsignedInt就是80个UnsignedInt。

下面这一段是对x轴数据都初始化:

if (m_buffer.isEmpty()) {
    m_buffer.reserve(sampleCount);
    for (int i = 0; i < sampleCount; ++i)
        m_buffer.append(QPointF(i, 0));
}

下面是将数据网x轴左方向移动1920个坐标:

if (availableSamples < sampleCount) {
    start = sampleCount - availableSamples;
    for (int s = 0; s < start; ++s)
        m_buffer[s].setY(m_buffer.at(s + availableSamples).y());
}

将刚刚采集的音频放到最后80坐标里面(1920~2000)

for (int s = start; s < sampleCount; ++s, data += resolution)
    m_buffer[s].setY(qreal(uchar(*data) - 128) / qreal(128));

这里-128是为了使得y轴有负数,除以128是因为。

axisY->setRange(-1, 1);

Y轴坐标是-1,到1之间。

相关文章