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

喜歡的可以可以關注我,一起學習,讓學習更加快樂。

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