qt录取实时音频数据,并画出对应图形

话不多说,上例子图:

 记得要加上这两个模块:

QT       += charts

QT += multimedia

对应的h文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include    <QMainWindow>
#include    <QtCharts>
#include    <QAudioDeviceInfo>
#include    <QAudioInput>
//#include    <QIODevice>
#include    "qmydisplaydevice.h"


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    const qint64  displayPointsCount=4000;

    QLineSeries *lineSeries;//曲线序列

    QList<QAudioDeviceInfo> deviceList;  //音频录入设备列表

    QAudioDeviceInfo curDevice;//当前输入设备

    QmyDisplayDevice    *displayDevice; //用于显示的IODevice

    QAudioInput         *audioInput;//音频输入设备

    QString SampleTypeString(QAudioFormat::SampleType sampleType);

    QString ByteOrderString(QAudioFormat::Endian endian);
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
//自定义槽函数
    void    on_IODevice_UpdateBlockSize(qint64 blockSize);

//
    void on_comboDevices_currentIndexChanged(int index);

    void on_actStart_triggered();

    void on_actStop_triggered();

    void on_actDeviceTest_triggered();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

       这里定义了较多的私有变量,其中 QmyDisplayDevice 是一个自定义的从 QIODevice 继承的类 ,用于读取音频输入缓冲区的数据 , 并在图表上显示,其具体实现在后面介绍。
 

MainWindow 的构造函数代码如下 :

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setCentralWidget(ui->splitter);

//创建显示图表
    QChart *chart = new QChart;
    chart->setTitle("音频输入原始信号");
    ui->chartView->setChart(chart);
//    ui->chartView->setRenderHint(QPainter::Antialiasing);
    lineSeries= new QLineSeries(); //序列
    chart->addSeries(lineSeries);

    QValueAxis *axisX = new QValueAxis;  //座标轴
    axisX->setRange(0, displayPointsCount); //chart显示4000个采样点数据
    axisX->setLabelFormat("%g");
    axisX->setTitleText("Samples");

    QValueAxis *axisY = new QValueAxis;  //座标轴
    axisY->setRange(0, 256); // UnSingedInt采样,数据范围0-255
//    axisY->setRange(-1, 1);
    axisY->setTitleText("Audio level");

    chart->setAxisX(axisX, lineSeries);
    chart->setAxisY(axisY, lineSeries);
    chart->legend()->hide();

//
    ui->comboDevices->clear();
//    QList<QAudioDeviceInfo> deviceList;
    deviceList=QAudioDeviceInfo::availableDevices(QAudio::AudioInput);//输入设备列表
    for(int i=0;i<deviceList.count();i++)
    {
       QAudioDeviceInfo device=deviceList.at(i);
       ui->comboDevices->addItem(device.deviceName());
     }

    if (deviceList.size()>0)
    {
      ui->comboDevices->setCurrentIndex(0); //触发comboDevices的信号currentIndexChanged()
      curDevice =deviceList.at(0);//
    }
    else
    {
        ui->actStart->setEnabled(false);
        ui->actDeviceTest->setEnabled(false);
        ui->groupBoxDevice->setTitle("支持的音频输入设置(无设备)");
    }
}

       构造函数创建了用于图表显示的 QChart 对象 cha此,创建了 QLineSeries 类型的序列 lineSeries,创建了 X和 Y 座标轴;其 X轴的范围等于 0 到显示的数据点的总数 4000, y 轴的范围是 0 至 256,采用 8 位无符号整数,采样数据范围是 0 至 255 。
         QAudioDevicelnfo: :a vai lableDevices(QAudio: :Audiolnput)可以获取音频输入设备列表,设备名称被添加到窗口上的 comboDevices 下拉列表框里。

音频输入设备支持的格式

   在主窗口的构造函数中,向 comboDevices 下拉列表框中添加 了 系统所有 的音频输入设备 。 在下拉列表框里选择一个设备时, 发射 currentindexChanged(int index)信号 ,在其槽函数里获取设备支持的各种音频输入参数,包括支持的音频编码、采样率、通道数 、 采样点类型和采样点大小等 ,以此更新窗口 上的组件显示。
 

void MainWindow::on_comboDevices_currentIndexChanged(int index)
{//选择音频输入设备
    curDevice =deviceList.at(index);//当前音频设备

    ui->comboCodec->clear(); //支持的音频编码
    QStringList codecs = curDevice.supportedCodecs();
    for (int i = 0; i < codecs.size(); ++i)
        ui->comboCodec->addItem(codecs.at(i));

    ui->comboSampleRate->clear(); //支持的采样率
    QList<int> sampleRate = curDevice.supportedSampleRates();
    for (int i = 0; i < sampleRate.size(); ++i)
        ui->comboSampleRate->addItem(QString("%1").arg(sampleRate.at(i)));

    ui->comboChannels->clear();//支持的通道数
    QList<int> Channels = curDevice.supportedChannelCounts();
    for (int i = 0; i < Channels.size(); ++i)
        ui->comboChannels->addItem(QString("%1").arg(Channels.at(i)));

    ui->comboSampleTypes->clear(); //支持的采样点类型
    QList<QAudioFormat::SampleType> sampleTypes = curDevice.supportedSampleTypes();
    for (int i = 0; i < sampleTypes.size(); ++i)
        ui->comboSampleTypes->addItem(SampleTypeString(sampleTypes.at(i)),
                                      QVariant(sampleTypes.at(i)));

    ui->comboSampleSizes->clear();//采样点大小
    QList<int> sampleSizes = curDevice.supportedSampleSizes();
    for (int i = 0; i < sampleSizes.size(); ++i)
        ui->comboSampleSizes->addItem(QString("%1").arg(sampleSizes.at(i)));

    ui->comboByteOrder->clear();//字节序
    QList<QAudioFormat::Endian> endians = curDevice.supportedByteOrders();
    for (int i = 0; i < endians.size(); ++i)
        ui->comboByteOrder->addItem(ByteOrderString(endians.at(i)));
}
QString MainWindow::SampleTypeString(QAudioFormat::SampleType sampleType)
{//将QAudioFormat::SampleType类型转换为字符串
    QString result("Unknown");
    switch (sampleType) {
    case QAudioFormat::SignedInt:
        result = "SignedInt";
        break;
    case QAudioFormat::UnSignedInt:
        result = "UnSignedInt";
        break;
    case QAudioFormat::Float:
        result = "Float";
        break;
    case QAudioFormat::Unknown:
        result = "Unknown";
    }
    return result;
}

QString MainWindow::ByteOrderString(QAudioFormat::Endian endian)
{ //将QAudioFormat::Endian  转换为字符串
  if (endian==QAudioFormat::LittleEndian)
    return "LittleEndian";
  else if (endian==QAudioFormat::BigEndian)
    return "BigEndian";
  else
    return "Unknown";
}

       创建一个 QAudiolnput 对象时需要传递一个 QAudioformat 类型作为参数,用于指定音频输入配置,而音频设备是否支持这些配置需要进行测试。窗口上的“测试音频设置”按钮可 以进行测试,代码如下 :

void MainWindow::on_actDeviceTest_triggered()
{//测试音频输入设备是否支持选择的设置
  QAudioFormat settings;

  settings.setCodec(ui->comboCodec->currentText());
  settings.setSampleRate(ui->comboSampleRate->currentText().toInt());
  settings.setChannelCount(ui->comboChannels->currentText().toInt());

  settings.setSampleType(QAudioFormat::SampleType(ui->comboSampleTypes->currentData().toInt()));

  settings.setSampleSize(ui->comboSampleSizes->currentText().toInt());

//  不能采用下面的语句,   QAudioFormat::Endian的取值与 QSysInfo::Endian对应,正好相反
//    testSettings.setByteOrder(QAudioFormat::Endian(ui->comboByteOrder->currentData().toInt()));
  if (ui->comboByteOrder->currentText()=="LittleEndian")
    settings.setByteOrder(QAudioFormat::LittleEndian);
  else
    settings.setByteOrder(QAudioFormat::BigEndian);

  if (curDevice.isFormatSupported(settings))
    QMessageBox::information(this,"音频输入设置测试","测试成功,输入设备支持此设置");
  else
    QMessageBox::critical(this,"音频输入设置测试","测试失败,输入设备不支持此设置");
}

开始音频输入

void MainWindow::on_actStart_triggered()
{//开始音频输入
    QAudioFormat defaultAudioFormat; //缺省格式
    defaultAudioFormat.setSampleRate(8000);
    defaultAudioFormat.setChannelCount(1);
    defaultAudioFormat.setSampleSize(8);
    defaultAudioFormat.setCodec("audio/pcm");
    defaultAudioFormat.setByteOrder(QAudioFormat::LittleEndian);
    defaultAudioFormat.setSampleType(QAudioFormat::UnSignedInt);

//    curDevice = QAudioDeviceInfo::defaultInputDevice(); // 选择缺省设备
    if (!curDevice.isFormatSupported(defaultAudioFormat))
    {
        QMessageBox::critical(this,"音频输入设置测试","测试失败,输入设备不支持此设置");
        return;
    }

    audioInput = new QAudioInput(curDevice,defaultAudioFormat, this);
    audioInput->setBufferSize(displayPointsCount);

//    Returns the audio buffer size in bytes.
//    If called before start(), returns platform default value.
//    If called before start() but setBufferSize() was called prior,
//    returns value set by setBufferSize(). If called after start(),
//    returns the actual buffer size being used.
//    This may not be what was set previously by setBufferSize().

//    ui->LabBufferSize->setText(QString::asprintf("QAudioInput::bufferSize()=%d",
//                                                 audioInput->bufferSize()));

// 接收音频输入数据的流设备
    displayDevice = new QmyDisplayDevice(lineSeries, displayPointsCount,this);

    connect(displayDevice,SIGNAL(updateBlockSize(qint64)),
            this,SLOT(on_IODevice_UpdateBlockSize(qint64)));

    displayDevice->open(QIODevice::WriteOnly); //必须以写方式打开

    audioInput->start(displayDevice); //以流设备作为参数,开始录入音频输入数据

//    ioDevice=new QmyAudioIODevice(lineSeries,this);//
//    ioDevice->start();
//    audioInput->start(ioDevice);

//    destinationFile.setFileName("/tmp/test.raw");
//    destinationFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
//    audioInput->start(&destinationFile);

    ui->actStart->setEnabled(false);
    ui->actStop->setEnabled(true);
}

void MainWindow::on_actStop_triggered()
{
    audioInput->stop();
    audioInput->deleteLater();
//    destinationFile.close();
//    delete audioInput;

//    disconnect(ioDevice,SIGNAL(readyRead()),
//           this,SLOT(on_IODevice_readyRead()));
    displayDevice->close();
    disconnect(displayDevice,SIGNAL(updateBlockSize(qint64)),
            this,SLOT(on_IODevice_UpdateBlockSize(qint64)));
    displayDevice->deleteLater();

    ui->actStart->setEnabled(true);
    ui->actStop->setEnabled(false);
}

 自定义槽函数 on_IODevice_UpdateBlockSize()用于显示缓冲区大小和数据块大小:

void MainWindow::on_IODevice_UpdateBlockSize(qint64 blockSize)
{//显示缓冲区大小和数据块大小
    ui->LabBufferSize->setText(QString::asprintf("QAudioInput::bufferSize()=%d",
                                                 audioInput->bufferSize()));

    ui->LabBlockSize->setText(
                QString("IODevice数据块字节数=%1").arg(blockSize));
}

流设备 QmyDisplayDevice 的功能实现:

对应的头文件:

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Charts module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QMYDISPLAYDEVICE_H
#define QMYDISPLAYDEVICE_H

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

#include  <QtCharts>
#include  <QIODevice>

//QT_CHARTS_BEGIN_NAMESPACE
//class QXYSeries;
//QT_CHARTS_END_NAMESPACE

//QT_CHARTS_USE_NAMESPACE

class QmyDisplayDevice : public QIODevice
{
    Q_OBJECT
public:
    explicit QmyDisplayDevice(QXYSeries * series, qint64 pointsCount,QObject *parent = 0);

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

private:
    QXYSeries *m_series;
    qint64  range=4000;

signals:
    void  updateBlockSize(qint64 blockSize);
};

#endif // QMYDISPLAYDEVICE_H

对应的cpp文件(这里可以学习下如何处理数据的):

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Charts module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmydisplaydevice.h"
#include <QtCharts/QXYSeries>

QmyDisplayDevice::QmyDisplayDevice(QXYSeries * series, qint64 pointsCount, QObject *parent) :
    QIODevice(parent)
//    m_series(series)
{
   m_series= series;
   range=pointsCount;
}

qint64 QmyDisplayDevice::readData(char * data, qint64 maxSize)
{// 流的读 操作不处理
    Q_UNUSED(data)
    Q_UNUSED(maxSize)
    return -1;
}

qint64 QmyDisplayDevice::writeData(const char * data, qint64 maxSize)
{ //读取数据块内的数据,更新到序列
    QVector<QPointF> oldPoints = m_series->pointsVector();
    QVector<QPointF> points;
//    int resolution = 4;//只是每4个取1个数
//    int resolution = 1;//应改为sampleSize=1 byte

    if (oldPoints.count() < range)
    { //m_series序列的数据未满4000点,
       points = m_series->pointsVector();
    }
    else
    {//将原来maxSize至4000的数据点前移,
       for (int i = maxSize; i < oldPoints.count(); i++)
          points.append(QPointF(i - maxSize, oldPoints.at(i).y()));
    }

    qint64 size = points.count();
    for (int k = 0; k < maxSize; k++) //数据块内的数据填充序列的尾部
        points.append(QPointF(k + size, (quint8)data[k]));

    m_series->replace(points); //最快的方式

    emit updateBlockSize(maxSize);
    return maxSize;
}

例子的连接:

链接:https://pan.baidu.com/s/1HSEAPyyxZqxj_CdE32ZrAA 
提取码:i21c

喜欢的可以可以关注我,一起学习,让学习更加快乐。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章