话不多说,上例子图:
记得要加上这两个模块:
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
喜欢的可以可以关注我,一起学习,让学习更加快乐。